Merge pull request #6840 from coollabsio/andrasbacsai/hetzner-ipv4-ipv6-config

feat: add IPv4/IPv6 network configuration for Hetzner server creation
This commit is contained in:
Andras Bacsai 2025-10-10 18:30:24 +02:00 committed by GitHub
commit bd6b597b09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 122 additions and 2 deletions

View file

@ -58,6 +58,10 @@ class ByHetzner extends Component
public bool $loading_data = false;
public bool $enable_ipv4 = true;
public bool $enable_ipv6 = true;
public function mount()
{
$this->authorize('viewAny', CloudProviderToken::class);
@ -129,6 +133,8 @@ protected function rules(): array
'private_key_id' => 'required|integer|exists:private_keys,id,team_id,'.currentTeam()->id,
'selectedHetznerSshKeyIds' => 'nullable|array',
'selectedHetznerSshKeyIds.*' => 'integer',
'enable_ipv4' => 'required|boolean',
'enable_ipv6' => 'required|boolean',
]);
}
@ -410,6 +416,10 @@ private function createHetznerServer(string $token): array
'location' => $this->selected_location,
'start_after_create' => true,
'ssh_keys' => $sshKeys,
'public_net' => [
'enable_ipv4' => $this->enable_ipv4,
'enable_ipv6' => $this->enable_ipv6,
],
];
ray('Server creation parameters', $params);
@ -438,10 +448,22 @@ public function submit()
// Create server on Hetzner
$hetznerServer = $this->createHetznerServer($hetznerToken);
// Determine IP address to use (prefer IPv4, fallback to IPv6)
$ipAddress = null;
if ($this->enable_ipv4 && isset($hetznerServer['public_net']['ipv4']['ip'])) {
$ipAddress = $hetznerServer['public_net']['ipv4']['ip'];
} elseif ($this->enable_ipv6 && isset($hetznerServer['public_net']['ipv6']['ip'])) {
$ipAddress = $hetznerServer['public_net']['ipv6']['ip'];
}
if (! $ipAddress) {
throw new \Exception('No public IP address available. Enable at least one of IPv4 or IPv6.');
}
// Create server in Coolify database
$server = Server::create([
'name' => $this->server_name,
'ip' => $hetznerServer['public_net']['ipv4']['ip'],
'ip' => $ipAddress,
'user' => 'root',
'port' => 22,
'team_id' => currentTeam()->id,

View file

@ -145,6 +145,17 @@ class="p-4 border border-yellow-500 dark:border-yellow-600 rounded bg-yellow-50
@endforeach
</x-forms.datalist>
</div>
<div class="flex flex-col gap-2">
<label class="text-sm font-medium">Network Configuration</label>
<div class="flex gap-4">
<x-forms.checkbox id="enable_ipv4" label="Enable IPv4"
helper="Enable public IPv4 address for this server" />
<x-forms.checkbox id="enable_ipv6" label="Enable IPv6"
helper="Enable public IPv6 address for this server" />
</div>
</div>
<div class="flex gap-2 justify-between">
<x-forms.button type="button" wire:click="previousStep">
Back

View file

@ -1,7 +1,94 @@
<?php
// Note: Full Livewire integration tests require database setup
// These tests verify the SSH key merging logic works correctly
// These tests verify the SSH key merging logic and public_net configuration
it('validates public_net configuration with IPv4 and IPv6 enabled', function () {
$enableIpv4 = true;
$enableIpv6 = true;
$publicNet = [
'enable_ipv4' => $enableIpv4,
'enable_ipv6' => $enableIpv6,
];
expect($publicNet)->toBe([
'enable_ipv4' => true,
'enable_ipv6' => true,
]);
});
it('validates public_net configuration with IPv4 only', function () {
$enableIpv4 = true;
$enableIpv6 = false;
$publicNet = [
'enable_ipv4' => $enableIpv4,
'enable_ipv6' => $enableIpv6,
];
expect($publicNet)->toBe([
'enable_ipv4' => true,
'enable_ipv6' => false,
]);
});
it('validates public_net configuration with IPv6 only', function () {
$enableIpv4 = false;
$enableIpv6 = true;
$publicNet = [
'enable_ipv4' => $enableIpv4,
'enable_ipv6' => $enableIpv6,
];
expect($publicNet)->toBe([
'enable_ipv4' => false,
'enable_ipv6' => true,
]);
});
it('validates IP address selection prefers IPv4 when both are enabled', function () {
$enableIpv4 = true;
$enableIpv6 = true;
$hetznerServer = [
'public_net' => [
'ipv4' => ['ip' => '1.2.3.4'],
'ipv6' => ['ip' => '2001:db8::1'],
],
];
$ipAddress = null;
if ($enableIpv4 && isset($hetznerServer['public_net']['ipv4']['ip'])) {
$ipAddress = $hetznerServer['public_net']['ipv4']['ip'];
} elseif ($enableIpv6 && isset($hetznerServer['public_net']['ipv6']['ip'])) {
$ipAddress = $hetznerServer['public_net']['ipv6']['ip'];
}
expect($ipAddress)->toBe('1.2.3.4');
});
it('validates IP address selection uses IPv6 when only IPv6 is enabled', function () {
$enableIpv4 = false;
$enableIpv6 = true;
$hetznerServer = [
'public_net' => [
'ipv4' => ['ip' => '1.2.3.4'],
'ipv6' => ['ip' => '2001:db8::1'],
],
];
$ipAddress = null;
if ($enableIpv4 && isset($hetznerServer['public_net']['ipv4']['ip'])) {
$ipAddress = $hetznerServer['public_net']['ipv4']['ip'];
} elseif ($enableIpv6 && isset($hetznerServer['public_net']['ipv6']['ip'])) {
$ipAddress = $hetznerServer['public_net']['ipv6']['ip'];
}
expect($ipAddress)->toBe('2001:db8::1');
});
it('validates SSH key array merging logic with Coolify key', function () {
$coolifyKeyId = 123;