mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
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:
@@ -40,3 +40,5 @@ node_modules/
|
|||||||
# VS Code
|
# VS Code
|
||||||
.vscode
|
.vscode
|
||||||
.devcontainer/
|
.devcontainer/
|
||||||
|
|
||||||
|
.zed/
|
||||||
|
|||||||
@@ -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
@@ -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()
|
||||||
|
|||||||
@@ -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())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user