add basic scheduler and queue processing for tasks
This commit is contained in:
parent
b6719129e1
commit
1296d08dcb
11 changed files with 441 additions and 4 deletions
83
app/Console/Commands/RunTasks.php
Normal file
83
app/Console/Commands/RunTasks.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
|
||||
*
|
||||
* 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\Console\Commands;
|
||||
|
||||
use DB;
|
||||
use Carbon;
|
||||
use Pterodactyl\Models;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
|
||||
use Pterodactyl\Jobs\SendScheduledTask;
|
||||
|
||||
class RunTasks extends Command
|
||||
{
|
||||
|
||||
use DispatchesJobs;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'pterodactyl:tasks';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Find and run scheduled tasks.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$tasks = Models\Task::where('queued', 0)->where('next_run', '<=', (Carbon::now())->toAtomString())->get();
|
||||
|
||||
$this->info(sprintf('Preparing to queue %d tasks.', count($tasks)));
|
||||
$bar = $this->output->createProgressBar(count($tasks));
|
||||
|
||||
foreach ($tasks as &$task) {
|
||||
$bar->advance();
|
||||
$this->dispatch(new SendScheduledTask(Models\Server::findOrFail($task->server), $task));
|
||||
}
|
||||
|
||||
$bar->finish();
|
||||
$this->info("\nFinished queuing tasks for running.");
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ class Kernel extends ConsoleKernel
|
|||
\Pterodactyl\Console\Commands\MakeUser::class,
|
||||
\Pterodactyl\Console\Commands\ShowVersion::class,
|
||||
\Pterodactyl\Console\Commands\UpdateEnvironment::class,
|
||||
\Pterodactyl\Console\Commands\RunTasks::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -27,7 +28,6 @@ class Kernel extends ConsoleKernel
|
|||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$schedule->command('inspire')
|
||||
->hourly();
|
||||
$schedule->command('pterodactyl:tasks')->everyFiveMinutes()->withoutOverlapping();
|
||||
}
|
||||
}
|
||||
|
|
72
app/Jobs/SendScheduledTask.php
Normal file
72
app/Jobs/SendScheduledTask.php
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Jobs;
|
||||
|
||||
use Pterodactyl\Jobs\Job;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
use DB;
|
||||
use Carbon;
|
||||
use Pterodactyl\Models;
|
||||
use Pterodactyl\Repositories\Daemon\CommandRepository;
|
||||
|
||||
class SendScheduledTask extends Job implements ShouldQueue
|
||||
{
|
||||
use InteractsWithQueue, SerializesModels;
|
||||
|
||||
protected $server;
|
||||
|
||||
protected $task;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Models\Server $server, Models\Task $task)
|
||||
{
|
||||
$this->server = $server;
|
||||
$this->task = $task;
|
||||
|
||||
$task->queued = 1;
|
||||
$task->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$time = Carbon::now();
|
||||
try {
|
||||
if ($this->task->action === 'command') {
|
||||
$repo = new CommandRepository($this->server);
|
||||
$response = $repo->send($this->task->data);
|
||||
}
|
||||
|
||||
$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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -109,7 +109,6 @@ class Node extends Model
|
|||
|
||||
$nodeData = self::getByID($node);
|
||||
|
||||
// @TODO: Better solution to disabling verification. Security risk.
|
||||
self::$guzzle[$node] = new Client([
|
||||
'base_uri' => sprintf('%s://%s:%s/', $nodeData->scheme, $nodeData->fqdn, $nodeData->daemonListen),
|
||||
'timeout' => 5.0,
|
||||
|
|
63
app/Models/Task.php
Normal file
63
app/Models/Task.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
|
||||
*
|
||||
* 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\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Task extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'tasks';
|
||||
|
||||
/**
|
||||
* Fields that are not mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = ['id', 'created_at', 'updated_at'];
|
||||
|
||||
/**
|
||||
* Cast values to correct type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'server' => 'integer',
|
||||
'queued' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['last_run', 'next_run', 'created_at', 'updated_at'];
|
||||
|
||||
}
|
63
app/Models/TaskLog.php
Normal file
63
app/Models/TaskLog.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
|
||||
*
|
||||
* 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\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class TaskLog extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'tasks_log';
|
||||
|
||||
/**
|
||||
* Fields that are not mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = ['id', 'created_at', 'updated_at'];
|
||||
|
||||
/**
|
||||
* Cast values to correct type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'task_id' => 'integer',
|
||||
'run_status' => 'integer'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['run_time', 'created_at', 'updated_at'];
|
||||
|
||||
}
|
79
app/Repositories/Daemon/CommandRepository.php
Normal file
79
app/Repositories/Daemon/CommandRepository.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
|
||||
*
|
||||
* 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 CommandRepository {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* [send description]
|
||||
* @param string $command
|
||||
* @return boolean
|
||||
* @throws DisplayException
|
||||
* @throws RequestException
|
||||
*/
|
||||
public function send($command)
|
||||
{
|
||||
// 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('POST', '/server/command', [
|
||||
'headers' => [
|
||||
'X-Access-Token' => $this->server->daemonSecret,
|
||||
'X-Access-Server' => $this->server->uuid
|
||||
],
|
||||
'json' => [
|
||||
'command' => $command
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
|
||||
throw new DisplayException('Command sending responded with a non-200 error code.');
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (\Exception $ex) {
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -27,7 +27,8 @@
|
|||
"prologue/alerts": "^0.4.0",
|
||||
"s1lentium/iptools": "^1.0",
|
||||
"edvinaskrucas/settings": "^2.0",
|
||||
"igaster/laravel-theme": "^1.1"
|
||||
"igaster/laravel-theme": "^1.1",
|
||||
"nesbot/carbon": "^1.21"
|
||||
},
|
||||
"require-dev": {
|
||||
"fzaninotto/faker": "~1.4",
|
||||
|
|
|
@ -180,6 +180,7 @@ return [
|
|||
'Blade' => Illuminate\Support\Facades\Blade::class,
|
||||
'Bus' => Illuminate\Support\Facades\Bus::class,
|
||||
'Cache' => Illuminate\Support\Facades\Cache::class,
|
||||
'Carbon' => Carbon\Carbon::class,
|
||||
'Config' => Illuminate\Support\Facades\Config::class,
|
||||
'Cookie' => Illuminate\Support\Facades\Cookie::class,
|
||||
'Crypt' => Illuminate\Support\Facades\Crypt::class,
|
||||
|
|
42
database/migrations/2016_02_27_163411_add_tasks_table.php
Normal file
42
database/migrations/2016_02_27_163411_add_tasks_table.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddTasksTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('tasks', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('server')->unsigned();
|
||||
$table->string('action');
|
||||
$table->text('data');
|
||||
$table->tinyInteger('queued')->unsigned()->default(0);
|
||||
$table->integer('month')->default(0);
|
||||
$table->integer('week')->default(0);
|
||||
$table->integer('day')->default(0);
|
||||
$table->integer('hour')->default(0);
|
||||
$table->integer('minute')->default(0);
|
||||
$table->integer('second')->default(0);
|
||||
$table->timestamp('last_run');
|
||||
$table->timestamp('next_run');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('tasks');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddTasksLogTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('tasks_log', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('task_id')->unsigned();
|
||||
$table->timestamp('run_time');
|
||||
$table->integer('run_status')->unsigned();
|
||||
$table->text('response');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('tasks_log');
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue