Skip to content

Commit 1296d08

Browse files
committed
add basic scheduler and queue processing for tasks
1 parent b671912 commit 1296d08

File tree

11 files changed

+441
-4
lines changed

11 files changed

+441
-4
lines changed

app/Console/Commands/RunTasks.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
/**
3+
* Pterodactyl - Panel
4+
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
namespace Pterodactyl\Console\Commands;
25+
26+
use DB;
27+
use Carbon;
28+
use Pterodactyl\Models;
29+
use Illuminate\Console\Command;
30+
use Illuminate\Foundation\Bus\DispatchesJobs;
31+
32+
use Pterodactyl\Jobs\SendScheduledTask;
33+
34+
class RunTasks extends Command
35+
{
36+
37+
use DispatchesJobs;
38+
39+
/**
40+
* The name and signature of the console command.
41+
*
42+
* @var string
43+
*/
44+
protected $signature = 'pterodactyl:tasks';
45+
46+
/**
47+
* The console command description.
48+
*
49+
* @var string
50+
*/
51+
protected $description = 'Find and run scheduled tasks.';
52+
53+
/**
54+
* Create a new command instance.
55+
*
56+
* @return void
57+
*/
58+
public function __construct()
59+
{
60+
parent::__construct();
61+
}
62+
63+
/**
64+
* Execute the console command.
65+
*
66+
* @return mixed
67+
*/
68+
public function handle()
69+
{
70+
$tasks = Models\Task::where('queued', 0)->where('next_run', '<=', (Carbon::now())->toAtomString())->get();
71+
72+
$this->info(sprintf('Preparing to queue %d tasks.', count($tasks)));
73+
$bar = $this->output->createProgressBar(count($tasks));
74+
75+
foreach ($tasks as &$task) {
76+
$bar->advance();
77+
$this->dispatch(new SendScheduledTask(Models\Server::findOrFail($task->server), $task));
78+
}
79+
80+
$bar->finish();
81+
$this->info("\nFinished queuing tasks for running.");
82+
}
83+
}

app/Console/Kernel.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class Kernel extends ConsoleKernel
1717
\Pterodactyl\Console\Commands\MakeUser::class,
1818
\Pterodactyl\Console\Commands\ShowVersion::class,
1919
\Pterodactyl\Console\Commands\UpdateEnvironment::class,
20+
\Pterodactyl\Console\Commands\RunTasks::class,
2021
];
2122

2223
/**
@@ -27,7 +28,6 @@ class Kernel extends ConsoleKernel
2728
*/
2829
protected function schedule(Schedule $schedule)
2930
{
30-
$schedule->command('inspire')
31-
->hourly();
31+
$schedule->command('pterodactyl:tasks')->everyFiveMinutes()->withoutOverlapping();
3232
}
3333
}

app/Jobs/SendScheduledTask.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace Pterodactyl\Jobs;
4+
5+
use Pterodactyl\Jobs\Job;
6+
use Illuminate\Queue\SerializesModels;
7+
use Illuminate\Queue\InteractsWithQueue;
8+
use Illuminate\Contracts\Queue\ShouldQueue;
9+
10+
use DB;
11+
use Carbon;
12+
use Pterodactyl\Models;
13+
use Pterodactyl\Repositories\Daemon\CommandRepository;
14+
15+
class SendScheduledTask extends Job implements ShouldQueue
16+
{
17+
use InteractsWithQueue, SerializesModels;
18+
19+
protected $server;
20+
21+
protected $task;
22+
23+
/**
24+
* Create a new job instance.
25+
*
26+
* @return void
27+
*/
28+
public function __construct(Models\Server $server, Models\Task $task)
29+
{
30+
$this->server = $server;
31+
$this->task = $task;
32+
33+
$task->queued = 1;
34+
$task->save();
35+
}
36+
37+
/**
38+
* Execute the job.
39+
*
40+
* @return void
41+
*/
42+
public function handle()
43+
{
44+
$time = Carbon::now();
45+
try {
46+
if ($this->task->action === 'command') {
47+
$repo = new CommandRepository($this->server);
48+
$response = $repo->send($this->task->data);
49+
}
50+
51+
$this->task->fill([
52+
'last_run' => $time,
53+
'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),
54+
'queued' => 0
55+
]);
56+
$this->task->save();
57+
} catch (\Exception $ex) {
58+
$wasError = true;
59+
$response = $ex->getMessage();
60+
throw $ex;
61+
} finally {
62+
$log = new Models\TaskLog;
63+
$log->fill([
64+
'task_id' => $this->task->id,
65+
'run_time' => $time,
66+
'run_status' => (int) isset($wasError),
67+
'response' => $response
68+
]);
69+
$log->save();
70+
}
71+
}
72+
}

app/Models/Node.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ public static function guzzleRequest($node)
109109

110110
$nodeData = self::getByID($node);
111111

112-
// @TODO: Better solution to disabling verification. Security risk.
113112
self::$guzzle[$node] = new Client([
114113
'base_uri' => sprintf('%s://%s:%s/', $nodeData->scheme, $nodeData->fqdn, $nodeData->daemonListen),
115114
'timeout' => 5.0,

app/Models/Task.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/**
3+
* Pterodactyl - Panel
4+
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
namespace Pterodactyl\Models;
25+
26+
use Illuminate\Database\Eloquent\Model;
27+
28+
class Task extends Model
29+
{
30+
31+
/**
32+
* The table associated with the model.
33+
*
34+
* @var string
35+
*/
36+
protected $table = 'tasks';
37+
38+
/**
39+
* Fields that are not mass assignable.
40+
*
41+
* @var array
42+
*/
43+
protected $guarded = ['id', 'created_at', 'updated_at'];
44+
45+
/**
46+
* Cast values to correct type.
47+
*
48+
* @var array
49+
*/
50+
protected $casts = [
51+
'id' => 'integer',
52+
'server' => 'integer',
53+
'queued' => 'integer',
54+
];
55+
56+
/**
57+
* The attributes that should be mutated to dates.
58+
*
59+
* @var array
60+
*/
61+
protected $dates = ['last_run', 'next_run', 'created_at', 'updated_at'];
62+
63+
}

app/Models/TaskLog.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
/**
3+
* Pterodactyl - Panel
4+
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
namespace Pterodactyl\Models;
25+
26+
use Illuminate\Database\Eloquent\Model;
27+
28+
class TaskLog extends Model
29+
{
30+
31+
/**
32+
* The table associated with the model.
33+
*
34+
* @var string
35+
*/
36+
protected $table = 'tasks_log';
37+
38+
/**
39+
* Fields that are not mass assignable.
40+
*
41+
* @var array
42+
*/
43+
protected $guarded = ['id', 'created_at', 'updated_at'];
44+
45+
/**
46+
* Cast values to correct type.
47+
*
48+
* @var array
49+
*/
50+
protected $casts = [
51+
'id' => 'integer',
52+
'task_id' => 'integer',
53+
'run_status' => 'integer'
54+
];
55+
56+
/**
57+
* The attributes that should be mutated to dates.
58+
*
59+
* @var array
60+
*/
61+
protected $dates = ['run_time', 'created_at', 'updated_at'];
62+
63+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
/**
3+
* Pterodactyl - Panel
4+
* Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
namespace Pterodactyl\Repositories\Daemon;
25+
26+
use Pterodactyl\Models;
27+
use Pterodactyl\Exceptions\DisplayException;
28+
29+
use GuzzleHttp\Client;
30+
use GuzzleHttp\Exception\RequestException;
31+
32+
class CommandRepository {
33+
34+
protected $server;
35+
protected $node;
36+
protected $client;
37+
38+
public function __construct($server)
39+
{
40+
$this->server = ($server instanceof Models\Server) ? $server : Models\Server::findOrFail($server);
41+
$this->node = Models\Node::getByID($this->server->node);
42+
$this->client = Models\Node::guzzleRequest($this->server->node);
43+
}
44+
45+
/**
46+
* [send description]
47+
* @param string $command
48+
* @return boolean
49+
* @throws DisplayException
50+
* @throws RequestException
51+
*/
52+
public function send($command)
53+
{
54+
// We don't use the user's specific daemon secret here since we
55+
// are assuming that a call to this function has been validated.
56+
// Additionally not all calls to this will be from a logged in user.
57+
// (e.g. task queue or API)
58+
try {
59+
$response = $this->client->request('POST', '/server/command', [
60+
'headers' => [
61+
'X-Access-Token' => $this->server->daemonSecret,
62+
'X-Access-Server' => $this->server->uuid
63+
],
64+
'json' => [
65+
'command' => $command
66+
]
67+
]);
68+
69+
if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) {
70+
throw new DisplayException('Command sending responded with a non-200 error code.');
71+
}
72+
73+
return true;
74+
} catch (\Exception $ex) {
75+
throw $ex;
76+
}
77+
}
78+
79+
}

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"prologue/alerts": "^0.4.0",
2828
"s1lentium/iptools": "^1.0",
2929
"edvinaskrucas/settings": "^2.0",
30-
"igaster/laravel-theme": "^1.1"
30+
"igaster/laravel-theme": "^1.1",
31+
"nesbot/carbon": "^1.21"
3132
},
3233
"require-dev": {
3334
"fzaninotto/faker": "~1.4",

0 commit comments

Comments
 (0)