mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-20 06:23:23 +00:00
fix(servers): retain cloud instances awaiting IPs
Persist DigitalOcean, Hetzner, and Vultr servers before public IP assignment, then backfill placeholder addresses from provider state. Treat partial Sentinel snapshots as non-authoritative and document the destinations API with OpenAPI schemas.
This commit is contained in:
@@ -10,7 +10,7 @@ use Tests\TestCase;
|
||||
|
||||
uses(TestCase::class, RefreshDatabase::class);
|
||||
|
||||
function createDigitalOceanServerForStateTest(string $status = 'active'): Server
|
||||
function createDigitalOceanServerForStateTest(string $status = 'active', array $attributes = []): Server
|
||||
{
|
||||
$team = Team::create([
|
||||
'name' => 'Test Team',
|
||||
@@ -24,13 +24,13 @@ function createDigitalOceanServerForStateTest(string $status = 'active'): Server
|
||||
]);
|
||||
$privateKey = PrivateKey::factory()->create(['team_id' => $team->id]);
|
||||
|
||||
return Server::factory()->create([
|
||||
return Server::factory()->create(array_merge([
|
||||
'team_id' => $team->id,
|
||||
'private_key_id' => $privateKey->id,
|
||||
'cloud_provider_token_id' => $token->id,
|
||||
'digitalocean_droplet_id' => 987,
|
||||
'digitalocean_droplet_status' => $status,
|
||||
]);
|
||||
], $attributes));
|
||||
}
|
||||
|
||||
it('marks a DigitalOcean droplet as deleted when the provider returns 404', function () {
|
||||
@@ -45,6 +45,48 @@ it('marks a DigitalOcean droplet as deleted when the provider returns 404', func
|
||||
expect($server->fresh()->digitalocean_droplet_status)->toBe('deleted');
|
||||
});
|
||||
|
||||
it('backfills a placeholder IP from the DigitalOcean droplet state', function () {
|
||||
$server = createDigitalOceanServerForStateTest('new', ['ip' => Server::PLACEHOLDER_IP]);
|
||||
|
||||
Http::fake([
|
||||
'https://api.digitalocean.com/v2/droplets/987' => Http::response([
|
||||
'droplet' => [
|
||||
'id' => 987,
|
||||
'status' => 'active',
|
||||
'networks' => [
|
||||
'v4' => [
|
||||
['type' => 'public', 'ip_address' => '203.0.113.10'],
|
||||
],
|
||||
],
|
||||
],
|
||||
], 200),
|
||||
]);
|
||||
|
||||
expect($server->refreshDigitalOceanState())->toBe('active');
|
||||
expect($server->fresh()->ip)->toBe('203.0.113.10');
|
||||
});
|
||||
|
||||
it('does not overwrite a real IP from the DigitalOcean droplet state', function () {
|
||||
$server = createDigitalOceanServerForStateTest('active', ['ip' => '198.51.100.20']);
|
||||
|
||||
Http::fake([
|
||||
'https://api.digitalocean.com/v2/droplets/987' => Http::response([
|
||||
'droplet' => [
|
||||
'id' => 987,
|
||||
'status' => 'active',
|
||||
'networks' => [
|
||||
'v4' => [
|
||||
['type' => 'public', 'ip_address' => '203.0.113.10'],
|
||||
],
|
||||
],
|
||||
],
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$server->refreshDigitalOceanState();
|
||||
expect($server->fresh()->ip)->toBe('198.51.100.20');
|
||||
});
|
||||
|
||||
it('does not mark a DigitalOcean droplet as deleted on transient provider errors', function () {
|
||||
$server = createDigitalOceanServerForStateTest();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user