fix(previews): allow re-checking DNS over a stored completed result

Starting a DNS check for a preview domain used to leave the stored
completed status in place. That blocked the new 'checking' state from
being saved. persistDnsStatuses now takes the status keys whose checks
are being started and lets those keys overwrite what is stored.

Add tests for re-checking DNS on application domains and preview
domains that already have a completed result.
This commit is contained in:
Andras Bacsai
2026-09-25 14:12:27 +02:00
parent 44b28fa6b3
commit f276f9573f
2 changed files with 66 additions and 5 deletions
@@ -339,17 +339,18 @@ class PreviewDomains extends Component
}
$row = $this->domainRows[$index];
$statusKey = $this->statusKey($row['url'], $row['service']);
$checkId = new_public_id();
$this->domainRows[$index]['dns_status'] = 'checking';
$this->domainRows[$index]['dns_message'] = 'Checking DNS...';
$this->domainRows[$index]['check_id'] = $checkId;
$this->persistDnsStatuses();
$this->persistDnsStatuses([$statusKey]);
try {
$server = $this->preview->application->destination?->server;
CheckDomainDnsJob::dispatch(
$this->preview,
$this->statusKey($row['url'], $row['service']),
$statusKey,
$row['url'],
$server,
$server ? serverDnsTargetIp($server) ?? $server->ip : null,
@@ -494,7 +495,11 @@ class PreviewDomains extends Component
return true;
}
private function persistDnsStatuses(): void
/**
* @param array<int, string> $startingCheckKeys Status keys whose check is being started by this call.
* They are allowed to replace a stored completed result.
*/
private function persistDnsStatuses(array $startingCheckKeys = []): void
{
$statuses = [];
foreach ($this->domainRows as $row) {
@@ -505,13 +510,13 @@ class PreviewDomains extends Component
];
}
DB::transaction(function () use (&$statuses): void {
DB::transaction(function () use (&$statuses, $startingCheckKeys): void {
$preview = ApplicationPreview::query()->lockForUpdate()->findOrFail($this->preview->id);
$storedStatuses = $preview->domain_dns_statuses ?? [];
foreach ($statuses as $key => $status) {
$storedStatus = $storedStatuses[$key] ?? null;
if (! is_array($storedStatus)) {
if (! is_array($storedStatus) || in_array($key, $startingCheckKeys, true)) {
continue;
}
+56
View File
@@ -3598,3 +3598,59 @@ it('does not overwrite a completed dns result with stale checking state', functi
'check_id' => 'completed-check',
]);
});
it('replaces a completed dns result when the domain is checked again', function () {
$settings = InstanceSettings::get();
$settings->is_dns_validation_enabled = true;
$settings->save();
$url = 'https://this-domain-should-not-resolve-for-coolify-tests.invalid';
$checkedAt = now()->subDays(12)->toIso8601String();
$this->application->update([
'fqdn' => $url,
'domain_dns_statuses' => [
$url => [
'status' => 'ok',
'message' => 'DNS looks correct.',
'expected_ip' => '203.0.113.10',
'checked_at' => $checkedAt,
],
],
]);
Livewire::test(Domains::class, ['application' => $this->application->fresh()])
->call('checkDomainDns', 0)
->call('pollDnsChecks')
->assertSet('domainRows.0.dns_status', 'failed');
expect($this->application->fresh()->domain_dns_statuses[$url])
->status->toBe('failed')
->checked_at->not->toBe($checkedAt);
});
it('restarts a preview dns check when the domain already has a completed result', function () {
Queue::fake();
$url = 'https://preview-recheck.example.com';
$statusKey = hash('sha256', $url.'|');
$preview = ApplicationPreview::create([
'application_id' => $this->application->id,
'pull_request_id' => 54,
'pull_request_html_url' => 'https://github.com/coollabsio/coolify/pull/54',
'fqdn' => $url,
'domain_dns_statuses' => [
$statusKey => [
'status' => 'failed',
'message' => 'Required DNS record type A pointing to 203.0.113.10',
],
],
]);
Livewire::test(PreviewDomains::class, ['preview' => $preview])
->call('checkDomainDns', 0)
->assertSet('domainRows.0.dns_status', 'checking');
expect($preview->fresh()->domain_dns_statuses[$statusKey]['status'])->toBe('checking');
Queue::assertPushed(CheckDomainDnsJob::class);
});