fix: create AAAA DNS record for IPv6 machines" (#257)

* chore: update gitignore
* fix: create AAAA DNS record for IPv6 machines"
* test: randomize execution order to prevent order-based bugs in tests
This commit is contained in:
Gabriel
2026-03-04 15:17:27 +10:00
committed by GitHub
parent d9934b9dba
commit a507b202ec
4 changed files with 133 additions and 16 deletions
+2
View File
@@ -40,3 +40,5 @@ node_modules/
# VS Code # VS Code
.vscode .vscode
.devcontainer/ .devcontainer/
.zed/
+3 -3
View File
@@ -73,14 +73,14 @@ mocks:
.PHONY: test .PHONY: test
test: test:
ifeq ($(TEST_NAME),) ifeq ($(TEST_NAME),)
go test -count=1 -v ./... go test -shuffle=on -count=1 -v ./...
else else
go test -race -count=1 -v -run ^$(TEST_NAME)$$ ./... go test -shuffle=on -race -count=1 -v -run ^$(TEST_NAME)$$ ./...
endif endif
.PHONY: test-e2e .PHONY: test-e2e
test-e2e: test-e2e:
go test -race -count=1 -v ./test/e2e go test -shuffle=on -race -count=1 -v ./test/e2e
.PHONY: test-clean .PHONY: test-clean
test-clean: test-clean:
+54 -13
View File
@@ -75,24 +75,17 @@ func (cli *Client) CreateIngressRecords(ctx context.Context, serviceID string) (
close(reachableMachines) close(reachableMachines)
}() }()
var ingressIPs []string var machines []*pb.MachineInfo
for m := range reachableMachines { for m := range reachableMachines {
ip, _ := m.PublicIp.ToAddr() machines = append(machines, m)
ingressIPs = append(ingressIPs, ip.String())
} }
if len(ingressIPs) == 0 { if len(machines) == 0 {
return nil, ErrNoReachableMachines return nil, ErrNoReachableMachines
} }
req := &pb.CreateDomainRecordsRequest{ req, err := getCreateDomainRecordsRequest(machines)
Records: []*pb.DNSRecord{ if err != nil {
{ return nil, fmt.Errorf("create CreateDomainRecordsRequest: %w", err)
Name: "*",
Type: pb.DNSRecord_A,
Values: ingressIPs,
},
// TODO: Add AAAA record with routable IPv6 addresses of machines running Caddy containers.
},
} }
resp, err := cli.CreateDomainRecords(ctx, req) resp, err := cli.CreateDomainRecords(ctx, req)
if err != nil { if err != nil {
@@ -102,6 +95,54 @@ func (cli *Client) CreateIngressRecords(ctx context.Context, serviceID string) (
return resp.Records, nil return resp.Records, nil
} }
func getCreateDomainRecordsRequest(machines []*pb.MachineInfo) (*pb.CreateDomainRecordsRequest, error) {
if len(machines) == 0 {
return nil, fmt.Errorf("at least one machine must be provided")
}
var ipv4IngressIPs []string
var ipv6IngressIPs []string
var errs error
for _, m := range machines {
ip, _ := m.PublicIp.ToAddr()
if ip.Is4() {
ipv4IngressIPs = append(ipv4IngressIPs, ip.String())
} else if ip.Is6() {
ipv6IngressIPs = append(ipv6IngressIPs, ip.String())
} else {
// This is just a save guard, in case some special case is ever missed.
errs = errors.Join(errs, fmt.Errorf("machine with name %s (and ID: %s) has the public IP address '%s' which is neither IPv4 nor IPv6", m.Name, m.Id, ip.String()))
}
}
if errs != nil {
return nil, errs
}
records := make([]*pb.DNSRecord, 0, 2)
if len(ipv4IngressIPs) > 0 {
records = append(records,
&pb.DNSRecord{
Name: "*",
Type: pb.DNSRecord_A,
Values: ipv4IngressIPs,
},
)
}
if len(ipv6IngressIPs) > 0 {
records = append(records,
&pb.DNSRecord{
Name: "*",
Type: pb.DNSRecord_AAAA,
Values: ipv6IngressIPs,
},
)
}
return &pb.CreateDomainRecordsRequest{
Records: records,
}, nil
}
// verifyCaddyReachable verifies that the Caddy service is reachable on the machine by its public IP. // verifyCaddyReachable verifies that the Caddy service is reachable on the machine by its public IP.
func verifyCaddyReachable(ctx context.Context, m *pb.MachineInfo) error { func verifyCaddyReachable(ctx context.Context, m *pb.MachineInfo) error {
publicIP, _ := m.PublicIp.ToAddr() publicIP, _ := m.PublicIp.ToAddr()
+74
View File
@@ -1,10 +1,13 @@
package client package client
import ( import (
"fmt"
"net/netip" "net/netip"
"testing" "testing"
"github.com/psviderski/uncloud/internal/machine/api/pb"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestGetVerifyURL(t *testing.T) { func TestGetVerifyURL(t *testing.T) {
@@ -28,3 +31,74 @@ func TestGetVerifyURL(t *testing.T) {
}) })
} }
} }
func TestGetCreateDomainRecordsRequest(t *testing.T) {
tests := map[string]struct {
given []*pb.MachineInfo
wantReq *pb.CreateDomainRecordsRequest
wantErr error
}{
"single IPv4 machine": {
given: []*pb.MachineInfo{
{Id: "m1", Name: "machine-1", PublicIp: pb.NewIP(netip.MustParseAddr("1.2.3.4"))},
},
wantReq: &pb.CreateDomainRecordsRequest{
Records: []*pb.DNSRecord{
{Name: "*", Type: pb.DNSRecord_A, Values: []string{"1.2.3.4"}},
},
},
},
"single IPv6 machine": {
given: []*pb.MachineInfo{
{Id: "m1", Name: "machine-1", PublicIp: pb.NewIP(netip.MustParseAddr("2001:db8::1"))},
},
wantReq: &pb.CreateDomainRecordsRequest{
Records: []*pb.DNSRecord{
{Name: "*", Type: pb.DNSRecord_AAAA, Values: []string{"2001:db8::1"}},
},
},
},
"multiple IPv4 machines": {
given: []*pb.MachineInfo{
{Id: "m1", Name: "machine-1", PublicIp: pb.NewIP(netip.MustParseAddr("1.2.3.4"))},
{Id: "m2", Name: "machine-2", PublicIp: pb.NewIP(netip.MustParseAddr("5.6.7.8"))},
},
wantReq: &pb.CreateDomainRecordsRequest{
Records: []*pb.DNSRecord{
{Name: "*", Type: pb.DNSRecord_A, Values: []string{"1.2.3.4", "5.6.7.8"}},
},
},
},
"mixed IPv4 and IPv6 machines": {
given: []*pb.MachineInfo{
{Id: "m1", Name: "machine-1", PublicIp: pb.NewIP(netip.MustParseAddr("1.2.3.4"))},
{Id: "m2", Name: "machine-2", PublicIp: pb.NewIP(netip.MustParseAddr("2001:db8::1"))},
{Id: "m3", Name: "machine-3", PublicIp: pb.NewIP(netip.MustParseAddr("10.0.0.1"))},
{Id: "m4", Name: "machine-4", PublicIp: pb.NewIP(netip.MustParseAddr("2001:db8::2"))},
},
wantReq: &pb.CreateDomainRecordsRequest{
Records: []*pb.DNSRecord{
{Name: "*", Type: pb.DNSRecord_A, Values: []string{"1.2.3.4", "10.0.0.1"}},
{Name: "*", Type: pb.DNSRecord_AAAA, Values: []string{"2001:db8::1", "2001:db8::2"}},
},
},
},
"empty machine list": {
given: nil,
wantErr: fmt.Errorf("at least one machine must be provided"),
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
gotReq, gotErr := getCreateDomainRecordsRequest(tt.given)
if tt.wantErr == nil {
require.NoError(t, gotErr)
assert.Equal(t, tt.wantReq, gotReq)
} else {
assert.Nil(t, gotReq)
assert.Equal(t, tt.wantErr.Error(), gotErr.Error())
}
})
}
}