68 lines
2.4 KiB
PHP
68 lines
2.4 KiB
PHP
<?php
|
|
|
|
namespace App\Provisioning\Steps\Customer;
|
|
|
|
use App\Models\ProvisioningRun;
|
|
use App\Provisioning\StepResult;
|
|
use App\Services\Dns\HetznerDnsClient;
|
|
use App\Services\Traefik\TraefikWriter;
|
|
|
|
class ConfigureDnsAndTls extends CustomerStep
|
|
{
|
|
public function __construct(
|
|
private HetznerDnsClient $dns,
|
|
private TraefikWriter $traefik,
|
|
) {}
|
|
|
|
public function key(): string
|
|
{
|
|
return 'configure_dns_and_tls';
|
|
}
|
|
|
|
public function maxDuration(): int
|
|
{
|
|
// Above the 840s cert own-deadline below, so the step's own fail fires first.
|
|
return 960;
|
|
}
|
|
|
|
public function execute(ProvisioningRun $run): StepResult
|
|
{
|
|
$instance = $this->instance($run);
|
|
$host = $instance->host;
|
|
$fqdn = $instance->subdomain.'.'.config('provisioning.dns.zone');
|
|
|
|
// DNS — provider upsert is idempotent; the local row uses firstOrCreate on
|
|
// the record id and is written BEFORE the breadcrumb (the short-circuit guard).
|
|
if (! $this->hasResource($run, 'dns_record_id')) {
|
|
$recordId = $this->dns->upsertRecord($fqdn, 'A', $host->public_ip);
|
|
$instance->dnsRecords()->firstOrCreate(
|
|
['record_id' => $recordId],
|
|
['provider' => 'hetzner', 'fqdn' => $fqdn, 'type' => 'A', 'value' => $host->public_ip],
|
|
);
|
|
$this->recordResource($run, $host, 'dns_record_id', $recordId);
|
|
}
|
|
|
|
// Traefik file-provider route, written on the serving host (DNS points at
|
|
// it) and pointing at the guest VM.
|
|
if (! $instance->route_written) {
|
|
$trafficHost = $host->wg_ip ?? $host->public_ip;
|
|
$backend = $instance->guest_ip ?: $host->public_ip;
|
|
$this->traefik->write($trafficHost, $instance->subdomain, $backend);
|
|
$instance->update(['route_written' => true]);
|
|
}
|
|
|
|
// TLS via HTTP-01 — poll until the certificate is served.
|
|
if (! $instance->cert_ok) {
|
|
if ($this->traefik->certReachable($fqdn)) {
|
|
$instance->update(['cert_ok' => true]);
|
|
} elseif ($run->started_at !== null && $run->started_at->copy()->addSeconds(840)->isPast()) {
|
|
return StepResult::fail('cert_timeout');
|
|
} else {
|
|
return StepResult::poll(20, 'waiting for certificate');
|
|
}
|
|
}
|
|
|
|
return StepResult::advance();
|
|
}
|
|
}
|