153 lines
5.1 KiB
PHP
153 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace App\Actions\Environments;
|
|
|
|
use App\Drivers\Concerns\SupportsSlices;
|
|
use App\Enums\EnvironmentAttachmentRole;
|
|
use App\Enums\EnvironmentVariableSource;
|
|
use App\Enums\OperationKind;
|
|
use App\Enums\OperationStatus;
|
|
use App\Enums\ServiceType;
|
|
use App\Models\Environment;
|
|
use App\Models\EnvironmentAttachment;
|
|
use App\Models\Service;
|
|
use App\Models\ServiceSlice;
|
|
use Illuminate\Support\Str;
|
|
use InvalidArgumentException;
|
|
|
|
class AttachManagedService
|
|
{
|
|
public function execute(
|
|
Environment $environment,
|
|
Service $service,
|
|
EnvironmentAttachmentRole $role,
|
|
?string $name = null,
|
|
?string $envPrefix = null,
|
|
bool $isPrimary = true,
|
|
): EnvironmentAttachment {
|
|
$slice = $this->createDefaultSlice($environment, $service, $role, $name);
|
|
|
|
$attachment = $environment->attachments()->create([
|
|
'service_id' => $service->id,
|
|
'service_slice_id' => $slice?->id,
|
|
'role' => $role,
|
|
'env_prefix' => $envPrefix,
|
|
'is_primary' => $isPrimary,
|
|
]);
|
|
|
|
$this->syncManagedVariables($environment, $service, $slice, $envPrefix, $role);
|
|
$this->createSliceProvisionOperation($service, $slice);
|
|
|
|
return $attachment;
|
|
}
|
|
|
|
private function createDefaultSlice(
|
|
Environment $environment,
|
|
Service $service,
|
|
EnvironmentAttachmentRole $role,
|
|
?string $name,
|
|
): ?ServiceSlice {
|
|
return match ($service->type) {
|
|
ServiceType::POSTGRES => $service->slices()->firstOrCreate([
|
|
'environment_id' => $environment->id,
|
|
'type' => 'database_user',
|
|
'name' => $name ?? $this->sliceName($environment),
|
|
], [
|
|
'status' => 'pending',
|
|
'config' => [],
|
|
'credentials' => [
|
|
'database' => $name ?? $this->sliceName($environment),
|
|
'username' => $name ?? $this->sliceName($environment),
|
|
'password' => Str::password(32),
|
|
],
|
|
]),
|
|
ServiceType::VALKEY => $service->slices()->firstOrCreate([
|
|
'environment_id' => $environment->id,
|
|
'type' => 'logical_database',
|
|
'name' => $name ?? $this->sliceName($environment),
|
|
], [
|
|
'status' => 'pending',
|
|
'config' => [
|
|
'database' => $this->nextValkeyDatabase($service),
|
|
],
|
|
]),
|
|
ServiceType::CADDY => $service->slices()->firstOrCreate([
|
|
'environment_id' => $environment->id,
|
|
'type' => 'route',
|
|
'name' => $name ?? $environment->name,
|
|
], [
|
|
'status' => 'pending',
|
|
'config' => [],
|
|
]),
|
|
default => $role === EnvironmentAttachmentRole::CUSTOM ? null : throw new InvalidArgumentException("Service [{$service->type->value}] does not support managed attachments."),
|
|
};
|
|
}
|
|
|
|
private function syncManagedVariables(Environment $environment, Service $service, ?ServiceSlice $slice, ?string $envPrefix, EnvironmentAttachmentRole $role): void
|
|
{
|
|
if (! $slice) {
|
|
return;
|
|
}
|
|
|
|
$driver = $service->driver();
|
|
|
|
if (! $driver instanceof SupportsSlices) {
|
|
return;
|
|
}
|
|
|
|
foreach ($driver->environmentExportsForSlice($slice, $role) as $key => $value) {
|
|
$environment->variables()->updateOrCreate([
|
|
'key' => $this->variableKey($key, $envPrefix),
|
|
], [
|
|
'value' => $value,
|
|
'source' => EnvironmentVariableSource::MANAGED_ATTACHMENT,
|
|
'service_slice_id' => $slice->id,
|
|
'overridable' => false,
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function createSliceProvisionOperation(Service $service, ?ServiceSlice $slice): void
|
|
{
|
|
if (! $slice || ! $slice->wasRecentlyCreated) {
|
|
return;
|
|
}
|
|
|
|
$driver = $service->driver();
|
|
|
|
if (! $driver instanceof SupportsSlices) {
|
|
return;
|
|
}
|
|
|
|
$operation = $slice->operations()->create([
|
|
'kind' => OperationKind::SLICE_PROVISION,
|
|
'status' => OperationStatus::PENDING,
|
|
]);
|
|
|
|
$operation->steps()->create([
|
|
'name' => 'Provision '.$service->type->value.' slice',
|
|
'order' => 1,
|
|
'status' => OperationStatus::PENDING,
|
|
'script' => $driver->provisionSliceScript($slice),
|
|
]);
|
|
}
|
|
|
|
private function nextValkeyDatabase(Service $service): int
|
|
{
|
|
return ((int) $service->slices()
|
|
->where('type', 'logical_database')
|
|
->get()
|
|
->max(fn (ServiceSlice $slice): int => (int) ($slice->config['database'] ?? 0))) + 1;
|
|
}
|
|
|
|
private function sliceName(Environment $environment): string
|
|
{
|
|
return str($environment->application->name.' '.$environment->name)->slug('_')->value();
|
|
}
|
|
|
|
private function variableKey(string $key, ?string $envPrefix): string
|
|
{
|
|
return $envPrefix ? $envPrefix.'_'.$key : $key;
|
|
}
|
|
}
|