get deployment plan

This commit is contained in:
2025-05-22 17:19:28 +01:00
parent bfe0f8eabf
commit 4051afca4e
6 changed files with 50 additions and 17 deletions

View File

@@ -5,6 +5,7 @@ namespace App\Drivers\Caddy;
use App\Drivers\GatewayDriver; use App\Drivers\GatewayDriver;
use App\Data\Deployments\Plan; use App\Data\Deployments\Plan;
use App\Data\Deployments\PlannedStep as Step; use App\Data\Deployments\PlannedStep as Step;
use App\Enums\DeploymentStatus;
use App\Models\Service; use App\Models\Service;
class Caddy2Driver extends GatewayDriver class Caddy2Driver extends GatewayDriver
@@ -20,8 +21,15 @@ class Caddy2Driver extends GatewayDriver
$this->containerName = $containerName; $this->containerName = $containerName;
$this->containerId = $containerId; $this->containerId = $containerId;
$this->service = $service; $this->service = $service;
}
$this->deploymentPlan = new Plan(steps: [ public function getDeploymentPlan(string $deploymentHash): Plan
{
$previousDeployment = $this->service?->deployments()
->where('status', DeploymentStatus::COMPLETED)
->first();
return new Plan(steps: [
new Step( new Step(
name: 'Generate Caddyfile', name: 'Generate Caddyfile',
script: function () { script: function () {
@@ -36,23 +44,23 @@ class Caddy2Driver extends GatewayDriver
), ),
new Step( new Step(
name: 'Run the docker image', name: 'Run the docker image',
script: function () { script: function () use ($previousDeployment, $deploymentHash) {
$script = collect(); $script = collect();
if ($this->containerName) { if ($this->containerName && $previousDeployment) {
$script->push('docker stop '.$this->containerName.' || true'); $script->push("docker stop \"{$this->containerName}-{$previousDeployment->hash}\" || true");
} elseif ($this->containerId) { } elseif ($this->containerId) {
$script->push('docker stop '.$this->containerId.' || true'); $script->push('docker stop ' . $this->containerId . ' || true');
} }
$runCommand = 'docker run -d'; $runCommand = 'docker run -d';
if ($this->containerName) { if ($this->containerName) {
$runCommand .= " --name {$this->containerName}"; $runCommand .= " --name \"{$this->containerName}-{$deploymentHash}\"";
} }
$runCommand .= ' -p 80:80 -p 443:443 caddy:2'; $runCommand .= ' -p 80:80 -p 443:443 caddy:2';
$script->push($runCommand); $script->push($runCommand);
return $script->join("\n"); return $script->join(" && ");
} }
), ),
]); ]);

View File

@@ -9,8 +9,6 @@ abstract class Driver
{ {
public ?Service $service; public ?Service $service;
public Plan $deploymentPlan;
public ?string $containerName; public ?string $containerName;
public ?string $containerId; public ?string $containerId;
@@ -20,4 +18,6 @@ abstract class Driver
?string $containerId = null, ?string $containerId = null,
?Service $service = null, ?Service $service = null,
); );
abstract public function getDeploymentPlan(string $deploymentHash): Plan;
} }

View File

@@ -5,6 +5,7 @@ namespace App\Drivers\Postgres;
use App\Data\Deployments\Plan; use App\Data\Deployments\Plan;
use App\Data\Deployments\PlannedStep as Step; use App\Data\Deployments\PlannedStep as Step;
use App\Drivers\DatabaseDriver; use App\Drivers\DatabaseDriver;
use App\Enums\DeploymentStatus;
use App\Models\Service; use App\Models\Service;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@@ -19,27 +20,40 @@ class Postgres17Driver extends DatabaseDriver
public ?array $credentials = null, public ?array $credentials = null,
) { ) {
$credentials = $credentials ?? $this->defaultCredentials(); $credentials = $credentials ?? $this->defaultCredentials();
}
public function getDeploymentPlan(string $deploymentHash): Plan
{
$user = $credentials['user'] ?? null; $user = $credentials['user'] ?? null;
$password = $credentials['password'] ?? null; $password = $credentials['password'] ?? null;
$db = $credentials['db'] ?? null; $db = $credentials['db'] ?? null;
$this->deploymentPlan = new Plan(steps: [ if (!$user || !$password || !$db) {
throw new \InvalidArgumentException('Missing required credentials');
}
$previousDeployment = $this->service?->deployments()
->where('status', DeploymentStatus::COMPLETED)
->first();
return new Plan(steps: [
new Step( new Step(
name: 'Run the docker image', name: 'Run the docker image',
secrets: [ secrets: [
'password' => $password 'password' => $password
], ],
script: function () use ($user, $password, $db) { script: function () use ($user, $password, $db, $previousDeployment, $deploymentHash) {
$script = collect(); $script = collect();
if ($this->containerName) {
$script->push('docker stop '.$this->containerName.' || true'); if ($this->containerName && $previousDeployment) {
$script->push("docker stop \"{$this->containerName}-{$previousDeployment->hash}\" || true");
} elseif ($this->containerId) { } elseif ($this->containerId) {
$script->push('docker stop '.$this->containerId.' || true'); $script->push('docker stop ' . $this->containerId . ' || true');
} }
$runCommand = 'docker run -d'; $runCommand = 'docker run -d';
if ($this->containerName) { if ($this->containerName) {
$runCommand .= " --name {$this->containerName}"; $runCommand .= " --name \"{$this->containerName}-{$deploymentHash}\"";
} }
if ($password) { if ($password) {
$runCommand .= ' -e POSTGRES_PASSWORD=[!password!]'; $runCommand .= ' -e POSTGRES_PASSWORD=[!password!]';
@@ -54,7 +68,7 @@ class Postgres17Driver extends DatabaseDriver
$script->push($runCommand); $script->push($runCommand);
return $runCommand; return $script->join(" && ");
} }
), ),
new Step( new Step(

View File

@@ -24,13 +24,14 @@ class DeployService implements ShouldQueue
public function handle(): void public function handle(): void
{ {
$driver = $this->service->driver(); $driver = $this->service->driver();
$deploymentPlan = $driver->getDeploymentPlan($this->deployment->hash);
$this->service->update([ $this->service->update([
'status' => ServiceStatus::INSTALLING, 'status' => ServiceStatus::INSTALLING,
]); ]);
$this->deployment = $this->service->deployments()->create([ $this->deployment = $this->service->deployments()->create([
'status' => DeploymentStatus::PENDING, 'status' => DeploymentStatus::PENDING,
]); ]);
foreach ($driver->deploymentPlan->steps as $index => $plannedStep) { foreach ($deploymentPlan->steps as $index => $plannedStep) {
$step = $this->deployment->steps()->create([ $step = $this->deployment->steps()->create([
'order' => $index + 1, 'order' => $index + 1,
'status' => DeploymentStatus::PENDING, 'status' => DeploymentStatus::PENDING,

View File

@@ -10,6 +10,15 @@ class Deployment extends Model
{ {
protected $guarded = []; protected $guarded = [];
public static function boot(): void
{
parent::boot();
static::creating(function (self $deployment) {
$deployment->hash = str()->random(16);
});
}
protected function casts(): array protected function casts(): array
{ {
return [ return [

View File

@@ -10,6 +10,7 @@ return new class extends Migration
{ {
Schema::create('deployments', function (Blueprint $table) { Schema::create('deployments', function (Blueprint $table) {
$table->id(); $table->id();
$table->string('hash')->unique();
$table->morphs('target'); // server, service, etc. $table->morphs('target'); // server, service, etc.
$table->string('status'); $table->string('status');
$table->dateTime('started_at')->nullable(); $table->dateTime('started_at')->nullable();