From a62565d0ad7fc76de9b8b7adee8f92c1e66f3161 Mon Sep 17 00:00:00 2001 From: "Harry (hjbdev)" Date: Mon, 31 Mar 2025 15:29:07 +0000 Subject: [PATCH] wip --- app/Actions/Services/CreateService.php | 6 ++- app/Drivers/Driver.php | 5 ++ app/Drivers/Postgres/Postgres17Driver.php | 2 +- app/Jobs/Services/DeployService.php | 45 ++++++++++++++++ app/Jobs/Services/RunStep.php | 53 +++++++++++++++++++ app/Models/Deployment.php | 10 +++- app/Models/Service.php | 12 +++-- app/Models/Step.php | 15 ++++++ .../2025_03_31_141005_create_steps_table.php | 5 ++ 9 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 app/Jobs/Services/DeployService.php create mode 100644 app/Jobs/Services/RunStep.php diff --git a/app/Actions/Services/CreateService.php b/app/Actions/Services/CreateService.php index 99332ec..5cb9bd6 100644 --- a/app/Actions/Services/CreateService.php +++ b/app/Actions/Services/CreateService.php @@ -5,7 +5,7 @@ namespace App\Actions\Services; use App\Enums\ServiceCategory; use App\Enums\ServiceStatus; use App\Enums\ServiceType; -use App\Jobs\Services\InstallService; +use App\Jobs\Services\DeployService; use App\Models\Server; use Illuminate\Support\Str; @@ -27,5 +27,9 @@ class CreateService 'driver_name' => $driverName, // postgres 'status' => ServiceStatus::NOT_INSTALLED, ]); + + $defaultPassword = Str::random(16); + + dispatch(new DeployService($service, $defaultPassword)); } } diff --git a/app/Drivers/Driver.php b/app/Drivers/Driver.php index a0d2ad7..4061a9e 100644 --- a/app/Drivers/Driver.php +++ b/app/Drivers/Driver.php @@ -7,4 +7,9 @@ use App\Data\Deployments\Plan; interface Driver { public Plan $deploymentPlan { get; } + + public function __construct( + public ?string $containerName = null, + public ?string $containerId = null, + ); } \ No newline at end of file diff --git a/app/Drivers/Postgres/Postgres17Driver.php b/app/Drivers/Postgres/Postgres17Driver.php index 84e813b..a0209db 100644 --- a/app/Drivers/Postgres/Postgres17Driver.php +++ b/app/Drivers/Postgres/Postgres17Driver.php @@ -41,7 +41,7 @@ class Postgres17Driver implements DatabaseDriver $runCommand .= " --name {$this->containerName}"; } if ($this->defaultPassword) { - $runCommand .= " -e POSTGRES_PASSWORD=[!defaultpassword!]"; + $runCommand .= " -e POSTGRES_PASSWORD=[!defaultPassword!]"; } if ($this->defaultUser) { $runCommand .= " -e POSTGRES_USER={$this->defaultUser}"; diff --git a/app/Jobs/Services/DeployService.php b/app/Jobs/Services/DeployService.php new file mode 100644 index 0000000..a094f6f --- /dev/null +++ b/app/Jobs/Services/DeployService.php @@ -0,0 +1,45 @@ +service->driver($this->defaultPassword); + /** @var \App\Models\Deployment $deployment */ + $deployment = $this->service->deployments()->create([ + 'status' => DeploymentStatus::PENDING, + ]); + foreach ($driver->deploymentPlan->steps as $index => $plannedStep) { + $step = $deployment->steps()->create([ + 'order' => $index + 1, + 'status' => DeploymentStatus::PENDING, + 'script' => $plannedStep->getSafeScript(), + 'secrets' => [ + 'defaultPassword' => $this->defaultPassword, + ], + ]); + if ($index === 0) { + $step->dispatchJob(); + } + } + } +} diff --git a/app/Jobs/Services/RunStep.php b/app/Jobs/Services/RunStep.php new file mode 100644 index 0000000..7800628 --- /dev/null +++ b/app/Jobs/Services/RunStep.php @@ -0,0 +1,53 @@ +step->load('service.server'); + $this->step->update([ + 'status' => DeploymentStatus::IN_PROGRESS, + 'started_at' => now(), + ]); + + $server = $this->step->service->server; + + $ssh = Ssh::create('root', $server->ipv4) + ->usePrivateKey(storage_path('private/ssh/id_ed25519')) + ->onOutput(function ($output) { + $this->step->update([ + 'logs' => $this->step->logs . "\n" . trim($output), + ]); + }); + + $ssh->execute($this->step->script); + + $this->step->update([ + 'status' => DeploymentStatus::COMPLETED, + 'finished_at' => now(), + 'secrets' => null, + ]); + + // Dispatch the next step if available + if ($nextStep = $this->step->deployment->steps()->where('order', '>', $this->step->order)->orderBy('order', 'asc')->first()) { + $nextStep->dispatchJob(); + } + } +} diff --git a/app/Models/Deployment.php b/app/Models/Deployment.php index afcc6b8..19a187f 100644 --- a/app/Models/Deployment.php +++ b/app/Models/Deployment.php @@ -8,6 +8,14 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; class Deployment extends Model { + protected function casts(): array + { + return [ + 'started_at' => 'datetime', + 'finished_at' => 'datetime', + ]; + } + public function steps(): HasMany { return $this->hasMany(Step::class); @@ -15,6 +23,6 @@ class Deployment extends Model public function deployable(): MorphTo { - return $this->morphTo(); + return $this->morphTo('target'); } } diff --git a/app/Models/Service.php b/app/Models/Service.php index f82787d..174f3f2 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -36,11 +36,17 @@ class Service extends Model public function deployments(): MorphMany { - return $this->morphMany(Deployment::class, 'deployable'); + return $this->morphMany(Deployment::class, 'target'); } - public function driver()//: Driver + public function driver( + ?string $defaultPassword = null, + ): Driver { - // @todo. This is the class that controls the service + $class = config("keystone.drivers.{$this->driver_name}.{$this->version}"); + if (!class_exists($class)) { + throw new \Exception("Driver class {$class} not found"); + } + return new $class($this->container_name, $this->container_id, defaultPassword: $defaultPassword); } } diff --git a/app/Models/Step.php b/app/Models/Step.php index d3d636f..878ac9b 100644 --- a/app/Models/Step.php +++ b/app/Models/Step.php @@ -2,13 +2,28 @@ namespace App\Models; +use App\Jobs\Services\RunStep; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Step extends Model { + protected function casts(): array + { + return [ + 'started_at' => 'datetime', + 'finished_at' => 'datetime', + 'secrets' => 'encrypted:array', + ]; + } + public function deployment(): BelongsTo { return $this->belongsTo(Deployment::class); } + + public function dispatchJob(): void + { + dispatch(new RunStep($this)); + } } diff --git a/database/migrations/2025_03_31_141005_create_steps_table.php b/database/migrations/2025_03_31_141005_create_steps_table.php index 2b3b284..ea24386 100644 --- a/database/migrations/2025_03_31_141005_create_steps_table.php +++ b/database/migrations/2025_03_31_141005_create_steps_table.php @@ -12,8 +12,13 @@ return new class extends Migration Schema::create('steps', function (Blueprint $table) { $table->id(); $table->foreignIdFor(Deployment::class); + $table->integer('order'); + $table->string('status'); $table->longText('script'); $table->longText('logs')->nullable(); + $table->text('secrets')->nullable(); + $table->dateTime('started_at')->nullable(); + $table->dateTime('finished_at')->nullable(); $table->timestamps(); }); }