diff --git a/app/Console/Commands/RunTasks.php b/app/Console/Commands/RunTasks.php index 44b7be9a6..351dabd68 100644 --- a/app/Console/Commands/RunTasks.php +++ b/app/Console/Commands/RunTasks.php @@ -67,7 +67,7 @@ class RunTasks extends Command */ public function handle() { - $tasks = Models\Task::where('queued', 0)->where('next_run', '<=', (Carbon::now())->toAtomString())->get(); + $tasks = Models\Task::where('queued', 0)->where('active', 1)->where('next_run', '<=', (Carbon::now())->toAtomString())->get(); $this->info(sprintf('Preparing to queue %d tasks.', count($tasks))); $bar = $this->output->createProgressBar(count($tasks)); diff --git a/app/Jobs/SendScheduledTask.php b/app/Jobs/SendScheduledTask.php index ac1dece3f..2fff42094 100644 --- a/app/Jobs/SendScheduledTask.php +++ b/app/Jobs/SendScheduledTask.php @@ -1,5 +1,26 @@ + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ namespace Pterodactyl\Jobs; use Pterodactyl\Jobs\Job; @@ -11,6 +32,7 @@ use DB; use Carbon; use Pterodactyl\Models; use Pterodactyl\Repositories\Daemon\CommandRepository; +use Pterodactyl\Repositories\Daemon\PowerRepository; class SendScheduledTask extends Job implements ShouldQueue { @@ -42,30 +64,42 @@ class SendScheduledTask extends Job implements ShouldQueue public function handle() { $time = Carbon::now(); + $log = new Models\TaskLog; + + if ($this->attempts() >= 1) { + // Just delete the job, we will attempt it again later anyways. + $this->delete(); + } + try { if ($this->task->action === 'command') { $repo = new CommandRepository($this->server); $response = $repo->send($this->task->data); + } else if ($this->task->action === 'power') { + $repo = new PowerRepository($this->server); + $response = $repo->do($this->task->data); } - + $log->fill([ + 'task_id' => $this->task->id, + 'run_time' => $time, + 'run_status' => 0, + 'response' => $response + ]); + } catch (\Exception $ex) { + $log->fill([ + 'task_id' => $this->task->id, + 'run_time' => $time, + 'run_status' => 1, + 'response' => $ex->getMessage() + ]); + throw $ex; + } finally { $this->task->fill([ 'last_run' => $time, 'next_run' => $time->addMonths($this->task->month)->addWeeks($this->task->week)->addDays($this->task->day)->addHours($this->task->hour)->addMinutes($this->task->minute)->addSeconds($this->task->second), 'queued' => 0 ]); $this->task->save(); - } catch (\Exception $ex) { - $wasError = true; - $response = $ex->getMessage(); - throw $ex; - } finally { - $log = new Models\TaskLog; - $log->fill([ - 'task_id' => $this->task->id, - 'run_time' => $time, - 'run_status' => (int) isset($wasError), - 'response' => $response - ]); $log->save(); } } diff --git a/app/Repositories/Daemon/CommandRepository.php b/app/Repositories/Daemon/CommandRepository.php index f9b5bd5a0..ebaf9dc61 100644 --- a/app/Repositories/Daemon/CommandRepository.php +++ b/app/Repositories/Daemon/CommandRepository.php @@ -70,7 +70,7 @@ class CommandRepository { throw new DisplayException('Command sending responded with a non-200 error code.'); } - return true; + return $response->getBody(); } catch (\Exception $ex) { throw $ex; } diff --git a/app/Repositories/Daemon/PowerRepository.php b/app/Repositories/Daemon/PowerRepository.php new file mode 100644 index 000000000..2fa56d5da --- /dev/null +++ b/app/Repositories/Daemon/PowerRepository.php @@ -0,0 +1,92 @@ + + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +namespace Pterodactyl\Repositories\Daemon; + +use Pterodactyl\Models; +use Pterodactyl\Exceptions\DisplayException; + +use GuzzleHttp\Client; +use GuzzleHttp\Exception\RequestException; + +class PowerRepository { + + protected $server; + protected $node; + protected $client; + + public function __construct($server) + { + $this->server = ($server instanceof Models\Server) ? $server : Models\Server::findOrFail($server); + $this->node = Models\Node::getByID($this->server->node); + $this->client = Models\Node::guzzleRequest($this->server->node); + } + + public function do($action) + { + // We don't use the user's specific daemon secret here since we + // are assuming that a call to this function has been validated. + // Additionally not all calls to this will be from a logged in user. + // (e.g. task queue or API) + try { + $response = $this->client->request('PUT', '/server/power', [ + 'headers' => [ + 'X-Access-Token' => $this->server->daemonSecret, + 'X-Access-Server' => $this->server->uuid + ], + 'json' => [ + 'action' => $action + ] + ]); + + if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) { + throw new DisplayException('Power status responded with a non-200 error code.'); + } + + return $response->getBody(); + } catch (\Exception $ex) { + throw $ex; + } + } + + public function start() + { + $this->do('start'); + } + + public function stop() + { + $this->do('stop'); + } + + public function restart() + { + $this->do('restart'); + } + + public function kill() + { + $this->do('kill'); + } + +} diff --git a/database/migrations/2016_02_27_190725_add_active_task_option.php b/database/migrations/2016_02_27_190725_add_active_task_option.php new file mode 100644 index 000000000..d63111424 --- /dev/null +++ b/database/migrations/2016_02_27_190725_add_active_task_option.php @@ -0,0 +1,31 @@ +tinyInteger('active')->default(1)->after('server'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tasks', function (Blueprint $table) { + $table->dropColumn('active'); + }); + } +}