244 lines
10 KiB
PHP
244 lines
10 KiB
PHP
<?php
|
|
|
|
use App\Actions\Environments\PlanBuildArtifact;
|
|
use App\Enums\BuildArtifactStatus;
|
|
use App\Enums\BuildStrategy;
|
|
use App\Enums\DeployPolicy;
|
|
use App\Enums\RegistryType;
|
|
use App\Enums\ServiceCategory;
|
|
use App\Enums\ServiceType;
|
|
use App\Models\Application;
|
|
use App\Models\Environment;
|
|
use App\Models\Network;
|
|
use App\Models\Organisation;
|
|
use App\Models\Provider;
|
|
use App\Models\Server;
|
|
use App\Models\Service;
|
|
|
|
it('plans single-server builds on the target server without requiring a registry', function () {
|
|
$organisation = Organisation::factory()->create();
|
|
$application = Application::factory()->for($organisation)->create(['name' => 'Billing API']);
|
|
$environment = Environment::factory()->for($application)->create();
|
|
Service::factory()->for($environment)->create([
|
|
'organisation_id' => $organisation->id,
|
|
'category' => ServiceCategory::APPLICATION,
|
|
'type' => ServiceType::LARAVEL,
|
|
'version' => 'php-8.4',
|
|
'version_track' => 'php-8.4',
|
|
'driver_name' => 'laravel.php-8.4',
|
|
'deploy_policy' => DeployPolicy::WITH_ENVIRONMENT,
|
|
'desired_replicas' => 1,
|
|
]);
|
|
|
|
$artifact = app(PlanBuildArtifact::class)->execute($environment, str_repeat('a', 40));
|
|
|
|
expect($artifact->status)->toBe(BuildArtifactStatus::PENDING)
|
|
->and($artifact->image_tag)->toBe('billing-api:aaaaaaaaaaaa')
|
|
->and($artifact->registry_ref)->toBeNull()
|
|
->and($artifact->metadata['build_strategy'])->toBe(BuildStrategy::TARGET_SERVER->value);
|
|
});
|
|
|
|
it('requires a registry before planning multi-server builds', function () {
|
|
config(['keystone.managed_registry.url' => null]);
|
|
|
|
$organisation = Organisation::factory()->create();
|
|
$application = Application::factory()->for($organisation)->create();
|
|
$environment = Environment::factory()->for($application)->create();
|
|
Service::factory()->for($environment)->create([
|
|
'organisation_id' => $organisation->id,
|
|
'category' => ServiceCategory::APPLICATION,
|
|
'type' => ServiceType::LARAVEL,
|
|
'version' => 'php-8.4',
|
|
'version_track' => 'php-8.4',
|
|
'driver_name' => 'laravel.php-8.4',
|
|
'deploy_policy' => DeployPolicy::WITH_ENVIRONMENT,
|
|
'desired_replicas' => 2,
|
|
]);
|
|
|
|
expect(fn () => app(PlanBuildArtifact::class)->execute($environment, str_repeat('b', 40)))
|
|
->toThrow(RuntimeException::class, 'A registry is required before building artifacts for multi-server deployments.');
|
|
});
|
|
|
|
it('plans multi-server builds against a persisted managed registry', function () {
|
|
$organisation = Organisation::factory()->create();
|
|
$server = buildEnabledServerFor($organisation);
|
|
$organisation->registries()->create([
|
|
'name' => 'Managed',
|
|
'type' => RegistryType::MANAGED,
|
|
'url' => 'registry.example.com',
|
|
'credentials' => [
|
|
'build_username' => 'keystone-build',
|
|
'build_password' => 'secret',
|
|
'runtime_username' => 'keystone-runtime',
|
|
'runtime_password' => 'runtime-secret',
|
|
],
|
|
'control_server_id' => $server->id,
|
|
'health_status' => 'healthy',
|
|
'readiness_checks' => ['control_https' => 'passed', 'build_push' => 'passed'],
|
|
'ready_at' => now(),
|
|
]);
|
|
$application = Application::factory()->for($organisation)->create(['name' => 'Billing API']);
|
|
$environment = Environment::factory()->for($application)->create();
|
|
Service::factory()->for($environment)->create([
|
|
'organisation_id' => $organisation->id,
|
|
'category' => ServiceCategory::APPLICATION,
|
|
'type' => ServiceType::LARAVEL,
|
|
'version' => 'php-8.4',
|
|
'version_track' => 'php-8.4',
|
|
'driver_name' => 'laravel.php-8.4',
|
|
'deploy_policy' => DeployPolicy::WITH_ENVIRONMENT,
|
|
'desired_replicas' => 2,
|
|
]);
|
|
|
|
$artifact = app(PlanBuildArtifact::class)->execute($environment, str_repeat('d', 40));
|
|
|
|
expect($artifact->image_tag)->toBe("keystone/{$application->uuid}/{$environment->uuid}:dddddddddddd")
|
|
->and($artifact->registry_ref)->toBe("registry.example.com/keystone/{$application->uuid}/{$environment->uuid}:dddddddddddd")
|
|
->and($artifact->metadata['build_strategy'])->toBe(BuildStrategy::DEDICATED_BUILDER->value)
|
|
->and($artifact->metadata['build_server_id'])->toBe($server->id)
|
|
->and($artifact->metadata['registry_type'])->toBe(RegistryType::MANAGED->value);
|
|
});
|
|
|
|
it('blocks managed registry builds until the registry is ready', function () {
|
|
$organisation = Organisation::factory()->create();
|
|
$server = buildEnabledServerFor($organisation);
|
|
$organisation->registries()->create([
|
|
'name' => 'Managed',
|
|
'type' => RegistryType::MANAGED,
|
|
'url' => 'registry.example.com',
|
|
'credentials' => [
|
|
'build_username' => 'keystone-build',
|
|
'build_password' => 'secret',
|
|
'runtime_username' => 'keystone-runtime',
|
|
'runtime_password' => 'runtime-secret',
|
|
],
|
|
'control_server_id' => $server->id,
|
|
'health_status' => 'pending',
|
|
]);
|
|
$application = Application::factory()->for($organisation)->create();
|
|
$environment = Environment::factory()->for($application)->create();
|
|
Service::factory()->for($environment)->create([
|
|
'organisation_id' => $organisation->id,
|
|
'category' => ServiceCategory::APPLICATION,
|
|
'type' => ServiceType::LARAVEL,
|
|
'version' => 'php-8.4',
|
|
'version_track' => 'php-8.4',
|
|
'driver_name' => 'laravel.php-8.4',
|
|
'deploy_policy' => DeployPolicy::WITH_ENVIRONMENT,
|
|
'desired_replicas' => 2,
|
|
]);
|
|
|
|
expect(fn () => app(PlanBuildArtifact::class)->execute($environment, str_repeat('g', 40)))
|
|
->toThrow(RuntimeException::class, 'Managed registry has not passed readiness checks.');
|
|
});
|
|
|
|
it('does not treat the managed registry config value as a ready registry record', function () {
|
|
config(['keystone.managed_registry.url' => 'registry.example.com']);
|
|
|
|
$organisation = Organisation::factory()->create();
|
|
$application = Application::factory()->for($organisation)->create();
|
|
$environment = Environment::factory()->for($application)->create();
|
|
Service::factory()->for($environment)->create([
|
|
'organisation_id' => $organisation->id,
|
|
'category' => ServiceCategory::APPLICATION,
|
|
'type' => ServiceType::LARAVEL,
|
|
'version' => 'php-8.4',
|
|
'version_track' => 'php-8.4',
|
|
'driver_name' => 'laravel.php-8.4',
|
|
'deploy_policy' => DeployPolicy::WITH_ENVIRONMENT,
|
|
'desired_replicas' => 2,
|
|
]);
|
|
|
|
expect(fn () => app(PlanBuildArtifact::class)->execute($environment, str_repeat('e', 40)))
|
|
->toThrow(RuntimeException::class, 'A registry is required before building artifacts for multi-server deployments.');
|
|
});
|
|
|
|
it('plans multi-server builds against the configured external registry', function () {
|
|
$organisation = Organisation::factory()->create();
|
|
$organisation->registries()->create([
|
|
'name' => 'GHCR',
|
|
'type' => RegistryType::GHCR,
|
|
'url' => 'ghcr.io/example',
|
|
]);
|
|
$application = Application::factory()->for($organisation)->create(['name' => 'Billing API']);
|
|
$environment = Environment::factory()->for($application)->create();
|
|
Service::factory()->for($environment)->create([
|
|
'organisation_id' => $organisation->id,
|
|
'category' => ServiceCategory::APPLICATION,
|
|
'type' => ServiceType::LARAVEL,
|
|
'version' => 'php-8.4',
|
|
'version_track' => 'php-8.4',
|
|
'driver_name' => 'laravel.php-8.4',
|
|
'deploy_policy' => DeployPolicy::WITH_ENVIRONMENT,
|
|
'desired_replicas' => 2,
|
|
]);
|
|
|
|
$artifact = app(PlanBuildArtifact::class)->execute($environment, str_repeat('c', 40));
|
|
|
|
expect($artifact->registry_ref)->toBe('ghcr.io/example/billing-api:cccccccccccc')
|
|
->and($artifact->metadata['build_strategy'])->toBe(BuildStrategy::EXTERNAL_REGISTRY->value);
|
|
});
|
|
|
|
it('prefers a configured external registry over the managed default', function () {
|
|
$organisation = Organisation::factory()->create();
|
|
$server = buildEnabledServerFor($organisation);
|
|
$organisation->registries()->create([
|
|
'name' => 'Managed',
|
|
'type' => RegistryType::MANAGED,
|
|
'url' => 'registry.example.com',
|
|
'credentials' => [
|
|
'build_username' => 'keystone-build',
|
|
'build_password' => 'secret',
|
|
'runtime_username' => 'keystone-runtime',
|
|
'runtime_password' => 'runtime-secret',
|
|
],
|
|
'control_server_id' => $server->id,
|
|
'health_status' => 'healthy',
|
|
'readiness_checks' => ['control_https' => 'passed', 'build_push' => 'passed'],
|
|
'ready_at' => now(),
|
|
]);
|
|
$organisation->registries()->create([
|
|
'name' => 'GHCR',
|
|
'type' => RegistryType::GHCR,
|
|
'url' => 'ghcr.io/example',
|
|
]);
|
|
$application = Application::factory()->for($organisation)->create(['name' => 'Billing API']);
|
|
$environment = Environment::factory()->for($application)->create();
|
|
Service::factory()->for($environment)->create([
|
|
'organisation_id' => $organisation->id,
|
|
'category' => ServiceCategory::APPLICATION,
|
|
'type' => ServiceType::LARAVEL,
|
|
'version' => 'php-8.4',
|
|
'version_track' => 'php-8.4',
|
|
'driver_name' => 'laravel.php-8.4',
|
|
'deploy_policy' => DeployPolicy::WITH_ENVIRONMENT,
|
|
'desired_replicas' => 2,
|
|
]);
|
|
|
|
$artifact = app(PlanBuildArtifact::class)->execute($environment, str_repeat('f', 40));
|
|
|
|
expect($artifact->registry_ref)->toBe('ghcr.io/example/billing-api:ffffffffffff')
|
|
->and($artifact->metadata['build_strategy'])->toBe(BuildStrategy::EXTERNAL_REGISTRY->value)
|
|
->and($artifact->metadata['registry_type'])->toBe(RegistryType::GHCR->value);
|
|
});
|
|
|
|
function buildEnabledServerFor(Organisation $organisation): Server
|
|
{
|
|
$provider = Provider::factory()->forOrganisation($organisation)->create();
|
|
$network = Network::create([
|
|
'organisation_id' => $organisation->id,
|
|
'provider_id' => $provider->id,
|
|
'name' => 'test-network',
|
|
'ip_range' => '10.0.0.0/24',
|
|
]);
|
|
|
|
return Server::factory()
|
|
->forOrganisation($organisation->id)
|
|
->forProvider($provider->id)
|
|
->forNetwork($network->id)
|
|
->create([
|
|
'is_control_node' => true,
|
|
'build_enabled' => true,
|
|
]);
|
|
}
|