mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
Cloning an application, database, service or environment did not copy the secret manager link, so variables with remote secret references failed on the next deployment with "no secret manager source is configured". Clones now get a copy of the link when the integration token belongs to the clone's team. Server transfer does not export integration tokens or links; the export now warns which resources need the token and link again on the target instance.
137 lines
5.4 KiB
PHP
137 lines
5.4 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Project\CloneMe;
|
|
use App\Livewire\Project\Shared\ResourceOperations;
|
|
use App\Models\Application;
|
|
use App\Models\Environment;
|
|
use App\Models\InstanceSettings;
|
|
use App\Models\IntegrationToken;
|
|
use App\Models\Project;
|
|
use App\Models\SecretManagerLink;
|
|
use App\Models\Server;
|
|
use App\Models\StandalonePostgresql;
|
|
use App\Models\Team;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
$this->withoutVite();
|
|
InstanceSettings::forceCreate(['id' => 0]);
|
|
Server::flushIdentityMap();
|
|
|
|
$this->user = User::factory()->create();
|
|
$this->team = Team::factory()->create();
|
|
$this->user->teams()->attach($this->team, ['role' => 'owner']);
|
|
|
|
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
|
$this->destination = $this->server->standaloneDockers()->firstOrFail();
|
|
$this->project = Project::factory()->create(['team_id' => $this->team->id]);
|
|
$this->environment = Environment::factory()->create(['project_id' => $this->project->id]);
|
|
|
|
$this->application = Application::factory()->create([
|
|
'environment_id' => $this->environment->id,
|
|
'destination_id' => $this->destination->id,
|
|
'destination_type' => $this->destination->getMorphClass(),
|
|
]);
|
|
$this->application->settings->fill(['is_container_label_readonly_enabled' => false])->save();
|
|
|
|
$this->token = IntegrationToken::factory()->create([
|
|
'team_id' => $this->team->id,
|
|
'provider' => 'vault',
|
|
'capabilities' => ['secrets'],
|
|
'metadata' => ['base_url' => 'https://vault.example.com'],
|
|
]);
|
|
|
|
$this->actingAs($this->user);
|
|
session(['currentTeam' => $this->team]);
|
|
});
|
|
|
|
function cloneLinkTestDatabase(Environment $environment, $destination): StandalonePostgresql
|
|
{
|
|
return StandalonePostgresql::create([
|
|
'name' => 'pg-linked',
|
|
'uuid' => new_public_id(),
|
|
'postgres_password' => 'secret',
|
|
'postgres_user' => 'postgres',
|
|
'postgres_db' => 'postgres',
|
|
'environment_id' => $environment->id,
|
|
'destination_id' => $destination->id,
|
|
'destination_type' => $destination->getMorphClass(),
|
|
'status' => 'exited',
|
|
]);
|
|
}
|
|
|
|
test('cloning an application copies its secret manager link', function () {
|
|
$this->application->secretManagerLink()->create([
|
|
'integration_token_id' => $this->token->id,
|
|
'settings' => ['mount' => 'secret', 'path' => 'apps/demo'],
|
|
]);
|
|
|
|
$clone = clone_application($this->application, $this->destination, [
|
|
'environment_id' => $this->environment->id,
|
|
]);
|
|
|
|
$link = $clone->secretManagerLink()->first();
|
|
expect($link)->not->toBeNull()
|
|
->and($link->integration_token_id)->toBe($this->token->id)
|
|
->and($link->settings)->toBe(['mount' => 'secret', 'path' => 'apps/demo'])
|
|
->and($this->application->secretManagerLink()->exists())->toBeTrue()
|
|
->and(SecretManagerLink::count())->toBe(2);
|
|
});
|
|
|
|
test('cloning a database copies its secret manager link', function () {
|
|
$database = cloneLinkTestDatabase($this->environment, $this->destination);
|
|
$database->secretManagerLink()->create([
|
|
'integration_token_id' => $this->token->id,
|
|
'settings' => ['mount' => 'secret', 'path' => 'db'],
|
|
]);
|
|
|
|
Livewire::test(ResourceOperations::class, ['resource' => $database])
|
|
->call('cloneTo', $this->destination->uuid)
|
|
->assertRedirect();
|
|
|
|
$clone = StandalonePostgresql::whereKeyNot($database->id)->sole();
|
|
expect($clone->secretManagerLink()->first()?->integration_token_id)->toBe($this->token->id)
|
|
->and($clone->secretManagerLink()->first()?->settings)->toBe(['mount' => 'secret', 'path' => 'db']);
|
|
});
|
|
|
|
test('cloning an environment copies the secret manager links of its resources', function () {
|
|
$this->application->secretManagerLink()->create(['integration_token_id' => $this->token->id]);
|
|
$database = cloneLinkTestDatabase($this->environment, $this->destination);
|
|
$database->secretManagerLink()->create(['integration_token_id' => $this->token->id]);
|
|
|
|
Livewire::test(CloneMe::class, [
|
|
'project_uuid' => $this->project->uuid,
|
|
'environment_uuid' => $this->environment->uuid,
|
|
])
|
|
->set('selectedDestination', $this->destination->uuid)
|
|
->set('newName', 'linked-clone')
|
|
->call('clone', 'environment')
|
|
->assertNotDispatched('error');
|
|
|
|
$clonedEnvironment = $this->project->environments()->where('name', 'linked-clone')->sole();
|
|
$clonedApplication = $clonedEnvironment->applications()->sole();
|
|
$clonedDatabase = StandalonePostgresql::where('environment_id', $clonedEnvironment->id)->sole();
|
|
|
|
expect($clonedApplication->secretManagerLink()->first()?->integration_token_id)->toBe($this->token->id)
|
|
->and($clonedDatabase->secretManagerLink()->first()?->integration_token_id)->toBe($this->token->id);
|
|
});
|
|
|
|
test('cloning does not copy a secret manager link whose token belongs to another team', function () {
|
|
$foreignToken = IntegrationToken::factory()->create([
|
|
'team_id' => Team::factory()->create()->id,
|
|
'provider' => 'vault',
|
|
'capabilities' => ['secrets'],
|
|
]);
|
|
$this->application->secretManagerLink()->create(['integration_token_id' => $foreignToken->id]);
|
|
|
|
$clone = clone_application($this->application, $this->destination, [
|
|
'environment_id' => $this->environment->id,
|
|
]);
|
|
|
|
expect($clone->secretManagerLink()->exists())->toBeFalse();
|
|
});
|