81 lines
2.6 KiB
PHP
81 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Provisioning\Steps\Host;
|
|
|
|
use App\Models\Host;
|
|
use App\Models\ProvisioningRun;
|
|
use App\Provisioning\StepResult;
|
|
use App\Services\Dns\HetznerDnsClient;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Throwable;
|
|
|
|
/**
|
|
* Gives the host a name under <zone>: fsn-01.node.clupilot.com.
|
|
*
|
|
* The record points at the host's WireGuard address, NOT its public IP. The
|
|
* name is for us — so an operator can ssh fsn-01.node.… instead of memorising
|
|
* addresses — and publishing a Proxmox host's public address would hand every
|
|
* scanner a target, which is the one thing the whole network design avoids.
|
|
*
|
|
* DNS is convenience, not a prerequisite for a working host: a failure here
|
|
* logs and moves on rather than stranding an otherwise finished onboarding.
|
|
*/
|
|
class RegisterHostDns extends HostStep
|
|
{
|
|
public function __construct(private HetznerDnsClient $dns) {}
|
|
|
|
public function key(): string
|
|
{
|
|
return 'register_host_dns';
|
|
}
|
|
|
|
public function execute(ProvisioningRun $run): StepResult
|
|
{
|
|
$host = $this->host($run);
|
|
|
|
if (blank($host->wg_ip)) {
|
|
return StepResult::fail('The host has no management address to publish.');
|
|
}
|
|
|
|
$name = $host->dns_name ?: $this->allocateName($host);
|
|
$fqdn = $name.'.node.'.config('provisioning.dns.zone', 'clupilot.com');
|
|
|
|
try {
|
|
$recordId = $this->dns->upsertRecord($fqdn, 'A', $host->wg_ip);
|
|
} catch (Throwable $e) {
|
|
$run->events()->create([
|
|
'step' => $this->key(),
|
|
'outcome' => 'info',
|
|
'message' => 'DNS name could not be registered: '.$e->getMessage(),
|
|
]);
|
|
|
|
return StepResult::advance();
|
|
}
|
|
|
|
$host->update(['dns_name' => $name, 'dns_record_id' => $recordId]);
|
|
|
|
return StepResult::advance();
|
|
}
|
|
|
|
/**
|
|
* <datacenter>-<nn>, numbered per datacenter and never reused: the number
|
|
* is one past the highest ever issued there, so removing a host does not
|
|
* renumber the others.
|
|
*/
|
|
private function allocateName(Host $host): string
|
|
{
|
|
return Cache::lock('host-dns-name:'.$host->datacenter, 15)->block(10, function () use ($host) {
|
|
$taken = Host::query()
|
|
->where('datacenter', $host->datacenter)
|
|
->whereNotNull('dns_name')
|
|
->pluck('dns_name');
|
|
|
|
$highest = $taken
|
|
->map(fn (string $name) => (int) substr($name, strrpos($name, '-') + 1))
|
|
->max() ?? 0;
|
|
|
|
return sprintf('%s-%02d', $host->datacenter, $highest + 1);
|
|
});
|
|
}
|
|
}
|