mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
chore: combine some e2e tests to reduce the number of ucind clusters hence used system resources
This commit is contained in:
@@ -1,215 +0,0 @@
|
|||||||
package e2e
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/psviderski/uncloud/internal/ucind"
|
|
||||||
"github.com/psviderski/uncloud/pkg/api"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestInternalDNS tests the internal DNS functionality including the new machine-specific service lookups
|
|
||||||
func TestInternalDNS(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
clusterName := "ucind-test.dns"
|
|
||||||
ctx := context.Background()
|
|
||||||
c, _ := createTestCluster(t, clusterName, ucind.CreateClusterOptions{Machines: 3}, true)
|
|
||||||
|
|
||||||
cli, err := c.Machines[0].Connect(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Create a test service with multiple replicas across machines
|
|
||||||
serviceName := "test-dns-service"
|
|
||||||
t.Cleanup(func() {
|
|
||||||
err := cli.RemoveService(ctx, serviceName)
|
|
||||||
if err != nil && !strings.Contains(err.Error(), "not found") {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Deploy a service across all machines in global mode using pause container
|
|
||||||
spec := api.ServiceSpec{
|
|
||||||
Name: serviceName,
|
|
||||||
Mode: api.ServiceModeGlobal,
|
|
||||||
Container: api.ContainerSpec{
|
|
||||||
Image: "portainer/pause:latest",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
deployment := cli.NewDeployment(spec, nil)
|
|
||||||
_, err = deployment.Run(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Wait for the service to be deployed
|
|
||||||
var svc api.Service
|
|
||||||
require.Eventually(t, func() bool {
|
|
||||||
svc, err = cli.InspectService(ctx, serviceName)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Should have 3 containers (one per machine) and all should be running
|
|
||||||
if len(svc.Containers) != 3 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, ctr := range svc.Containers {
|
|
||||||
if ctr.Container.State.Status != "running" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}, 30*time.Second, 1*time.Second, "Service should be deployed and running on all machines")
|
|
||||||
|
|
||||||
// Deploy a single "network-multitool" service to be used for all DNS queries
|
|
||||||
queryServiceName := "dns-query-service"
|
|
||||||
t.Cleanup(func() {
|
|
||||||
err := cli.RemoveService(ctx, queryServiceName)
|
|
||||||
if err != nil && !strings.Contains(err.Error(), "not found") {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
querySvcSpec := api.ServiceSpec{
|
|
||||||
Name: queryServiceName,
|
|
||||||
Mode: api.ServiceModeReplicated,
|
|
||||||
Replicas: 1,
|
|
||||||
Placement: api.Placement{
|
|
||||||
Machines: []string{c.Machines[0].Name},
|
|
||||||
},
|
|
||||||
Container: api.ContainerSpec{
|
|
||||||
Image: "wbitt/network-multitool",
|
|
||||||
Command: []string{"sleep", "infinity"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, err = cli.RunService(ctx, querySvcSpec)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Wait for the query service to be deployed
|
|
||||||
var querySvc api.Service
|
|
||||||
require.Eventually(t, func() bool {
|
|
||||||
querySvc, err = cli.InspectService(ctx, queryServiceName)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Should have 1 container and it should be running
|
|
||||||
if len(querySvc.Containers) != 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return querySvc.Containers[0].Container.State.Status == "running"
|
|
||||||
}, 30*time.Second, 1*time.Second, "Query service should be deployed and running")
|
|
||||||
queryContainer := querySvc.Containers[0]
|
|
||||||
|
|
||||||
// Run nslookup for given query
|
|
||||||
runNslookup := func(t *testing.T, dnsQuery string) string {
|
|
||||||
dnsOutput, err := execInContainerAndReadOutput(
|
|
||||||
t, ctx, cli, queryServiceName, queryContainer.Container.ID,
|
|
||||||
[]string{"nslookup", dnsQuery},
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return dnsOutput
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to verify DNS output doesn't contain errors
|
|
||||||
assertNoDNSErrors := func(t *testing.T, dnsOutput string) {
|
|
||||||
assert.NotContains(t, dnsOutput, "can't resolve", "DNS query should not contain resolution errors")
|
|
||||||
assert.NotContains(t, dnsOutput, "Name or service not known",
|
|
||||||
"DNS query should not contain unknown service errors")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("service name resolves to all container IPs", func(t *testing.T) {
|
|
||||||
dnsOutput := runNslookup(t, serviceName+".internal")
|
|
||||||
t.Logf("DNS query output:\n%s", dnsOutput)
|
|
||||||
|
|
||||||
// Verify that all service container IPs are in the DNS response
|
|
||||||
for _, ctr := range svc.Containers {
|
|
||||||
containerIP := ctr.Container.UncloudNetworkIP().String()
|
|
||||||
assert.Contains(t, dnsOutput, containerIP,
|
|
||||||
"Service DNS should resolve to container IP %s", containerIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNoDNSErrors(t, dnsOutput)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("machine-specific service DNS lookups", func(t *testing.T) {
|
|
||||||
// Test the new <machine-id>.m.<service-name>.internal DNS feature
|
|
||||||
for _, targetContainer := range svc.Containers {
|
|
||||||
targetMachineID := targetContainer.MachineID
|
|
||||||
targetContainerIP := targetContainer.Container.UncloudNetworkIP().String()
|
|
||||||
|
|
||||||
// Construct the machine-specific DNS name
|
|
||||||
machineSpecificDNS := targetMachineID + ".m." + serviceName + ".internal"
|
|
||||||
|
|
||||||
dnsOutput := runNslookup(t, machineSpecificDNS)
|
|
||||||
t.Logf("Machine-specific DNS query output for %s:\n%s", machineSpecificDNS, dnsOutput)
|
|
||||||
|
|
||||||
// Verify that the specific container IP is returned
|
|
||||||
assert.Contains(t, dnsOutput, targetContainerIP,
|
|
||||||
"Machine-specific DNS %s should resolve to container IP %s",
|
|
||||||
machineSpecificDNS, targetContainerIP)
|
|
||||||
|
|
||||||
// Verify that other container IPs are not returned (machine-specific should return only one IP)
|
|
||||||
for _, ctr := range svc.Containers {
|
|
||||||
if ctr.MachineID != targetMachineID {
|
|
||||||
otherContainerIP := ctr.Container.UncloudNetworkIP().String()
|
|
||||||
assert.NotContains(t, dnsOutput, otherContainerIP,
|
|
||||||
"Machine-specific DNS %s should not resolve to other container IP %s",
|
|
||||||
machineSpecificDNS, otherContainerIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNoDNSErrors(t, dnsOutput)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("service ID DNS lookup", func(t *testing.T) {
|
|
||||||
// Test that service ID also resolves (existing functionality)
|
|
||||||
dnsOutput := runNslookup(t, svc.ID+".internal")
|
|
||||||
t.Logf("Service ID DNS query output:\n%s", dnsOutput)
|
|
||||||
|
|
||||||
// Verify that all service container IPs are in the DNS response
|
|
||||||
for _, ctr := range svc.Containers {
|
|
||||||
containerIP := ctr.Container.UncloudNetworkIP().String()
|
|
||||||
assert.Contains(t, dnsOutput, containerIP,
|
|
||||||
"Service ID DNS should resolve to container IP %s", containerIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNoDNSErrors(t, dnsOutput)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("nearest mode prioritizes local subnet IPs", func(t *testing.T) {
|
|
||||||
// Find the service container on the same machine as the query container.
|
|
||||||
var localIP string
|
|
||||||
for _, ctr := range svc.Containers {
|
|
||||||
if ctr.MachineID == queryContainer.MachineID {
|
|
||||||
localIP = ctr.Container.UncloudNetworkIP().String()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
require.NotEmpty(t, localIP, "Should find local container IP on query machine %s", queryContainer.MachineID)
|
|
||||||
|
|
||||||
// We will extract the first IP address from the DNS output using a regex.
|
|
||||||
// Pattern matches "Name: nearest.test-dns-service.internal" followed by "Address: X.X.X.X".
|
|
||||||
re := regexp.MustCompile(`(?m)Name:\s+[\w\.\-]+\s+Address:\s+([\d\.]+)`)
|
|
||||||
|
|
||||||
// Test the "nearest" mode which should sort local subnet IPs first.
|
|
||||||
// The default behavior randomizes the order, so run it a few times
|
|
||||||
// to reduce the chance we're just getting lucky with the order.
|
|
||||||
for range 5 {
|
|
||||||
dnsOutput := runNslookup(t, "nearest."+serviceName+".internal")
|
|
||||||
t.Logf("Nearest mode DNS query output:\n%s", dnsOutput)
|
|
||||||
|
|
||||||
matches := re.FindStringSubmatch(dnsOutput)
|
|
||||||
require.Len(t, matches, 2, "Should find Name's Address in DNS output")
|
|
||||||
firstIP := matches[1]
|
|
||||||
assert.Equal(t, localIP, firstIP,
|
|
||||||
"Nearest mode should return local subnet IP first (query machine: %s, local IP: %s, first DNS result: %s)",
|
|
||||||
queryContainer.MachineID, localIP, firstIP)
|
|
||||||
|
|
||||||
assertNoDNSErrors(t, dnsOutput)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
+61
-75
@@ -12,10 +12,10 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMachineRename(t *testing.T) {
|
func TestMachineOperations(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
name := "ucind-test.machine-rename"
|
name := "ucind-test.machine-ops"
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
c, _ := createTestCluster(t, name, ucind.CreateClusterOptions{Machines: 3}, true)
|
c, _ := createTestCluster(t, name, ucind.CreateClusterOptions{Machines: 3}, true)
|
||||||
|
|
||||||
@@ -23,59 +23,61 @@ func TestMachineRename(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
|
|
||||||
|
// RenameMachine subtests.
|
||||||
|
|
||||||
t.Run("rename machine by name", func(t *testing.T) {
|
t.Run("rename machine by name", func(t *testing.T) {
|
||||||
// Get initial machine state
|
// Get initial machine state.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, machines, 3)
|
require.Len(t, machines, 3)
|
||||||
|
|
||||||
// Select the second machine to rename
|
// Select the second machine to rename.
|
||||||
originalMachine := machines[1]
|
originalMachine := machines[1]
|
||||||
originalName := originalMachine.Machine.Name
|
originalName := originalMachine.Machine.Name
|
||||||
newName := "renamed-machine-1"
|
newName := "renamed-machine-1"
|
||||||
|
|
||||||
// Rename the machine
|
// Rename the machine.
|
||||||
updatedMachine, err := cli.RenameMachine(ctx, originalName, newName)
|
updatedMachine, err := cli.RenameMachine(ctx, originalName, newName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, newName, updatedMachine.Name)
|
assert.Equal(t, newName, updatedMachine.Name)
|
||||||
assert.Equal(t, originalMachine.Machine.Id, updatedMachine.Id)
|
assert.Equal(t, originalMachine.Machine.Id, updatedMachine.Id)
|
||||||
|
|
||||||
// Verify the machine list reflects the change
|
// Verify the machine list reflects the change.
|
||||||
machines, err = cli.ListMachines(ctx, nil)
|
machines, err = cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, machines, 3)
|
require.Len(t, machines, 3)
|
||||||
|
|
||||||
// Find the renamed machine
|
// Find the renamed machine.
|
||||||
var found bool
|
var found bool
|
||||||
for _, m := range machines {
|
for _, m := range machines {
|
||||||
if m.Machine.Id == originalMachine.Machine.Id {
|
if m.Machine.Id == originalMachine.Machine.Id {
|
||||||
assert.Equal(t, newName, m.Machine.Name)
|
assert.Equal(t, newName, m.Machine.Name)
|
||||||
found = true
|
found = true
|
||||||
} else {
|
} else {
|
||||||
// Ensure other machines are unaffected
|
// Ensure other machines are unaffected.
|
||||||
assert.NotEqual(t, newName, m.Machine.Name)
|
assert.NotEqual(t, newName, m.Machine.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.True(t, found, "Renamed machine should be in the list")
|
assert.True(t, found, "Renamed machine should be in the list")
|
||||||
|
|
||||||
// Verify we can inspect the machine by its new name
|
// Verify we can inspect the machine by its new name.
|
||||||
inspectedMachine, err := cli.InspectMachine(ctx, newName)
|
inspectedMachine, err := cli.InspectMachine(ctx, newName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, newName, inspectedMachine.Machine.Name)
|
assert.Equal(t, newName, inspectedMachine.Machine.Name)
|
||||||
assert.Equal(t, originalMachine.Machine.Id, inspectedMachine.Machine.Id)
|
assert.Equal(t, originalMachine.Machine.Id, inspectedMachine.Machine.Id)
|
||||||
|
|
||||||
// Verify the old name no longer works
|
// Verify the old name no longer works.
|
||||||
_, err = cli.InspectMachine(ctx, originalName)
|
_, err = cli.InspectMachine(ctx, originalName)
|
||||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("rename machine by ID", func(t *testing.T) {
|
t.Run("rename machine by ID", func(t *testing.T) {
|
||||||
// Get the third machine
|
// Get the third machine.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, machines, 3)
|
require.Len(t, machines, 3)
|
||||||
|
|
||||||
// Find a machine that hasn't been renamed yet
|
// Find a machine that hasn't been renamed yet.
|
||||||
var targetMachine *pb.MachineMember
|
var targetMachine *pb.MachineMember
|
||||||
for _, m := range machines {
|
for _, m := range machines {
|
||||||
if m.Machine.Name != "renamed-machine-1" {
|
if m.Machine.Name != "renamed-machine-1" {
|
||||||
@@ -89,67 +91,67 @@ func TestMachineRename(t *testing.T) {
|
|||||||
machineID := targetMachine.Machine.Id
|
machineID := targetMachine.Machine.Id
|
||||||
newName := "renamed-machine-2"
|
newName := "renamed-machine-2"
|
||||||
|
|
||||||
// Rename using ID instead of name
|
// Rename using ID instead of name.
|
||||||
updatedMachine, err := cli.RenameMachine(ctx, machineID, newName)
|
updatedMachine, err := cli.RenameMachine(ctx, machineID, newName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, newName, updatedMachine.Name)
|
assert.Equal(t, newName, updatedMachine.Name)
|
||||||
assert.Equal(t, machineID, updatedMachine.Id)
|
assert.Equal(t, machineID, updatedMachine.Id)
|
||||||
|
|
||||||
// Verify the rename was successful
|
// Verify the rename was successful.
|
||||||
inspectedMachine, err := cli.InspectMachine(ctx, newName)
|
inspectedMachine, err := cli.InspectMachine(ctx, newName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, newName, inspectedMachine.Machine.Name)
|
assert.Equal(t, newName, inspectedMachine.Machine.Name)
|
||||||
assert.Equal(t, machineID, inspectedMachine.Machine.Id)
|
assert.Equal(t, machineID, inspectedMachine.Machine.Id)
|
||||||
|
|
||||||
// Verify the old name no longer works
|
// Verify the old name no longer works.
|
||||||
_, err = cli.InspectMachine(ctx, originalName)
|
_, err = cli.InspectMachine(ctx, originalName)
|
||||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("rename non-existent machine", func(t *testing.T) {
|
t.Run("rename non-existent machine", func(t *testing.T) {
|
||||||
// Try to rename a machine that doesn't exist
|
// Try to rename a machine that doesn't exist.
|
||||||
_, err := cli.RenameMachine(ctx, "non-existent-machine", "new-name")
|
_, err := cli.RenameMachine(ctx, "non-existent-machine", "new-name")
|
||||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||||
|
|
||||||
// Try with a non-existent ID
|
// Try with a non-existent ID.
|
||||||
_, err = cli.RenameMachine(ctx, "non-existent-id-12345", "new-name")
|
_, err = cli.RenameMachine(ctx, "non-existent-id-12345", "new-name")
|
||||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("rename to existing name", func(t *testing.T) {
|
t.Run("rename to existing name", func(t *testing.T) {
|
||||||
// Get current machines
|
// Get current machines.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, machines, 3)
|
require.Len(t, machines, 3)
|
||||||
|
|
||||||
// Try to rename machine 0 to the name of machine 1
|
// Try to rename machine 0 to the name of machine 1.
|
||||||
machine0Name := machines[0].Machine.Name
|
machine0Name := machines[0].Machine.Name
|
||||||
machine1Name := machines[1].Machine.Name
|
machine1Name := machines[1].Machine.Name
|
||||||
|
|
||||||
// This should fail because the name is already taken
|
// This should fail because the name is already taken.
|
||||||
_, err = cli.RenameMachine(ctx, machine0Name, machine1Name)
|
_, err = cli.RenameMachine(ctx, machine0Name, machine1Name)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("rename with empty name", func(t *testing.T) {
|
t.Run("rename with empty name", func(t *testing.T) {
|
||||||
// Get a machine to rename
|
// Get a machine to rename.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, machines, 3)
|
require.Len(t, machines, 3)
|
||||||
|
|
||||||
machineName := machines[0].Machine.Name
|
machineName := machines[0].Machine.Name
|
||||||
|
|
||||||
// Try to rename with empty string
|
// Try to rename with empty string.
|
||||||
_, err = cli.RenameMachine(ctx, machineName, "")
|
_, err = cli.RenameMachine(ctx, machineName, "")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("service continuity after rename", func(t *testing.T) {
|
t.Run("service continuity after rename", func(t *testing.T) {
|
||||||
// Deploy a service on a specific machine
|
// Deploy a service on a specific machine.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Find a machine that hasn't been renamed to test with
|
// Find a machine that hasn't been renamed to test with.
|
||||||
var targetMachine *pb.MachineMember
|
var targetMachine *pb.MachineMember
|
||||||
for _, m := range machines {
|
for _, m := range machines {
|
||||||
if m.Machine.Name != "renamed-machine-1" && m.Machine.Name != "renamed-machine-2" {
|
if m.Machine.Name != "renamed-machine-1" && m.Machine.Name != "renamed-machine-2" {
|
||||||
@@ -162,7 +164,7 @@ func TestMachineRename(t *testing.T) {
|
|||||||
originalMachineName := targetMachine.Machine.Name
|
originalMachineName := targetMachine.Machine.Name
|
||||||
serviceName := "test-service-rename-continuity"
|
serviceName := "test-service-rename-continuity"
|
||||||
|
|
||||||
// Create a service on the specific machine
|
// Create a service on the specific machine.
|
||||||
spec := api.ServiceSpec{
|
spec := api.ServiceSpec{
|
||||||
Name: serviceName,
|
Name: serviceName,
|
||||||
Mode: api.ServiceModeGlobal,
|
Mode: api.ServiceModeGlobal,
|
||||||
@@ -184,51 +186,41 @@ func TestMachineRename(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Verify service is running on the machine
|
// Verify service is running on the machine.
|
||||||
svc, err := cli.InspectService(ctx, serviceName)
|
svc, err := cli.InspectService(ctx, serviceName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, svc.Containers, 1)
|
assert.Len(t, svc.Containers, 1)
|
||||||
assert.Equal(t, targetMachine.Machine.Id, svc.Containers[0].MachineID)
|
assert.Equal(t, targetMachine.Machine.Id, svc.Containers[0].MachineID)
|
||||||
|
|
||||||
// Rename the machine
|
// Rename the machine.
|
||||||
newMachineName := "renamed-for-service-test"
|
newMachineName := "renamed-for-service-test"
|
||||||
_, err = cli.RenameMachine(ctx, originalMachineName, newMachineName)
|
_, err = cli.RenameMachine(ctx, originalMachineName, newMachineName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Verify service is still running on the renamed machine
|
// Verify service is still running on the renamed machine.
|
||||||
svc, err = cli.InspectService(ctx, serviceName)
|
svc, err = cli.InspectService(ctx, serviceName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, svc.Containers, 1)
|
assert.Len(t, svc.Containers, 1)
|
||||||
assert.Equal(t, targetMachine.Machine.Id, svc.Containers[0].MachineID)
|
assert.Equal(t, targetMachine.Machine.Id, svc.Containers[0].MachineID)
|
||||||
|
|
||||||
// The service spec's placement still references the old name,
|
// The service spec's placement still references the old name,
|
||||||
// but the service should continue to run on the same machine id
|
// but the service should continue to run on the same machine id.
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateMachine(t *testing.T) {
|
// UpdateMachine subtests.
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
name := "ucind-test.machine-update"
|
|
||||||
ctx := context.Background()
|
|
||||||
c, _ := createTestCluster(t, name, ucind.CreateClusterOptions{Machines: 3}, true)
|
|
||||||
|
|
||||||
cli, err := c.Machines[0].Connect(ctx)
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer cli.Close()
|
|
||||||
|
|
||||||
t.Run("update machine name", func(t *testing.T) {
|
t.Run("update machine name", func(t *testing.T) {
|
||||||
// Get initial machine state
|
// Get initial machine state.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, machines, 3)
|
require.Len(t, machines, 3)
|
||||||
|
|
||||||
// Select a machine to update
|
// Select a machine to update.
|
||||||
targetMachine := machines[1]
|
targetMachine := machines[1]
|
||||||
originalName := targetMachine.Machine.Name
|
originalName := targetMachine.Machine.Name
|
||||||
newName := "updated-machine-name"
|
newName := "updated-machine-name"
|
||||||
|
|
||||||
// Update the machine name using UpdateMachine directly
|
// Update the machine name using UpdateMachine directly.
|
||||||
req := &pb.UpdateMachineRequest{
|
req := &pb.UpdateMachineRequest{
|
||||||
MachineId: targetMachine.Machine.Id,
|
MachineId: targetMachine.Machine.Id,
|
||||||
Name: &newName,
|
Name: &newName,
|
||||||
@@ -238,22 +230,22 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
assert.Equal(t, newName, updatedMachine.Name)
|
assert.Equal(t, newName, updatedMachine.Name)
|
||||||
assert.Equal(t, targetMachine.Machine.Id, updatedMachine.Id)
|
assert.Equal(t, targetMachine.Machine.Id, updatedMachine.Id)
|
||||||
|
|
||||||
// Verify the change persisted
|
// Verify the change persisted.
|
||||||
inspected, err := cli.InspectMachine(ctx, updatedMachine.Id)
|
inspected, err := cli.InspectMachine(ctx, updatedMachine.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, newName, inspected.Machine.Name)
|
assert.Equal(t, newName, inspected.Machine.Name)
|
||||||
|
|
||||||
// Verify old name no longer works
|
// Verify old name no longer works.
|
||||||
_, err = cli.InspectMachine(ctx, originalName)
|
_, err = cli.InspectMachine(ctx, originalName)
|
||||||
assert.ErrorIs(t, err, api.ErrNotFound)
|
assert.ErrorIs(t, err, api.ErrNotFound)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update machine public IP", func(t *testing.T) {
|
t.Run("update machine public IP", func(t *testing.T) {
|
||||||
// Get a machine to update
|
// Get a machine to update.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Find a machine that hasn't been renamed
|
// Find a machine that hasn't been renamed.
|
||||||
var targetMachine *pb.MachineMember
|
var targetMachine *pb.MachineMember
|
||||||
for _, m := range machines {
|
for _, m := range machines {
|
||||||
if m.Machine.Name != "updated-machine-name" {
|
if m.Machine.Name != "updated-machine-name" {
|
||||||
@@ -263,12 +255,12 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.NotNil(t, targetMachine)
|
require.NotNil(t, targetMachine)
|
||||||
|
|
||||||
// Create a new public IP (must be a valid public IP address)
|
// Create a new public IP (must be a valid public IP address).
|
||||||
newPublicIP := &pb.IP{
|
newPublicIP := &pb.IP{
|
||||||
Ip: []byte{8, 8, 8, 8},
|
Ip: []byte{8, 8, 8, 8},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the public IP
|
// Update the public IP.
|
||||||
req := &pb.UpdateMachineRequest{
|
req := &pb.UpdateMachineRequest{
|
||||||
MachineId: targetMachine.Machine.Id,
|
MachineId: targetMachine.Machine.Id,
|
||||||
PublicIp: newPublicIP,
|
PublicIp: newPublicIP,
|
||||||
@@ -277,31 +269,31 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, newPublicIP.Ip, updatedMachine.PublicIp.Ip)
|
assert.Equal(t, newPublicIP.Ip, updatedMachine.PublicIp.Ip)
|
||||||
|
|
||||||
// Verify the change persisted
|
// Verify the change persisted.
|
||||||
inspected, err := cli.InspectMachine(ctx, targetMachine.Machine.Id)
|
inspected, err := cli.InspectMachine(ctx, targetMachine.Machine.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, newPublicIP.Ip, inspected.Machine.PublicIp.Ip)
|
assert.Equal(t, newPublicIP.Ip, inspected.Machine.PublicIp.Ip)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("remove machine public IP", func(t *testing.T) {
|
t.Run("remove machine public IP", func(t *testing.T) {
|
||||||
// Get machines
|
// Get machines.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, len(machines) > 0, "Need at least one machine")
|
require.True(t, len(machines) > 0, "Need at least one machine")
|
||||||
|
|
||||||
// First, set a public IP on a machine
|
// First, set a public IP on a machine.
|
||||||
targetMachine := machines[0]
|
targetMachine := machines[0]
|
||||||
setIPReq := &pb.UpdateMachineRequest{
|
setIPReq := &pb.UpdateMachineRequest{
|
||||||
MachineId: targetMachine.Machine.Id,
|
MachineId: targetMachine.Machine.Id,
|
||||||
PublicIp: &pb.IP{Ip: []byte{192, 0, 2, 1}}, // TEST-NET-1 address
|
PublicIp: &pb.IP{Ip: []byte{192, 0, 2, 1}}, // TEST-NET-1 address.
|
||||||
}
|
}
|
||||||
updatedMachine, err := cli.UpdateMachine(ctx, setIPReq)
|
updatedMachine, err := cli.UpdateMachine(ctx, setIPReq)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, updatedMachine.PublicIp)
|
require.NotNil(t, updatedMachine.PublicIp)
|
||||||
|
|
||||||
// Now test removing the public IP
|
// Now test removing the public IP.
|
||||||
|
|
||||||
// Remove the public IP by setting it to empty
|
// Remove the public IP by setting it to empty.
|
||||||
emptyIP := &pb.IP{}
|
emptyIP := &pb.IP{}
|
||||||
req := &pb.UpdateMachineRequest{
|
req := &pb.UpdateMachineRequest{
|
||||||
MachineId: updatedMachine.Id,
|
MachineId: updatedMachine.Id,
|
||||||
@@ -311,23 +303,17 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Nil(t, removedIPMachine.PublicIp)
|
assert.Nil(t, removedIPMachine.PublicIp)
|
||||||
|
|
||||||
// Verify the change persisted
|
// Verify the change persisted.
|
||||||
inspected, err := cli.InspectMachine(ctx, updatedMachine.Id)
|
inspected, err := cli.InspectMachine(ctx, updatedMachine.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Nil(t, inspected.Machine.PublicIp)
|
assert.Nil(t, inspected.Machine.PublicIp)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update machine endpoints", func(t *testing.T) {
|
t.Run("update machine endpoints", func(t *testing.T) {
|
||||||
// Get a machine to update
|
// Get a machine to update.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
targetMachine := machines[0]
|
||||||
var targetMachine *pb.MachineMember
|
|
||||||
for _, m := range machines {
|
|
||||||
targetMachine = m
|
|
||||||
break
|
|
||||||
}
|
|
||||||
require.NotNil(t, targetMachine)
|
|
||||||
|
|
||||||
newEndpoints := []*pb.IPPort{
|
newEndpoints := []*pb.IPPort{
|
||||||
{
|
{
|
||||||
@@ -348,13 +334,13 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, len(newEndpoints), len(updatedMachine.Network.Endpoints))
|
assert.Equal(t, len(newEndpoints), len(updatedMachine.Network.Endpoints))
|
||||||
|
|
||||||
// Verify endpoints were updated
|
// Verify endpoints were updated.
|
||||||
for i, endpoint := range updatedMachine.Network.Endpoints {
|
for i, endpoint := range updatedMachine.Network.Endpoints {
|
||||||
assert.Equal(t, newEndpoints[i].Ip.Ip, endpoint.Ip.Ip)
|
assert.Equal(t, newEndpoints[i].Ip.Ip, endpoint.Ip.Ip)
|
||||||
assert.Equal(t, newEndpoints[i].Port, endpoint.Port)
|
assert.Equal(t, newEndpoints[i].Port, endpoint.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify other network fields remain unchanged
|
// Verify other network fields remain unchanged.
|
||||||
assert.Equal(t, targetMachine.Machine.Network.Subnet.Ip.Ip, updatedMachine.Network.Subnet.Ip.Ip)
|
assert.Equal(t, targetMachine.Machine.Network.Subnet.Ip.Ip, updatedMachine.Network.Subnet.Ip.Ip)
|
||||||
assert.Equal(t, targetMachine.Machine.Network.Subnet.Bits, updatedMachine.Network.Subnet.Bits)
|
assert.Equal(t, targetMachine.Machine.Network.Subnet.Bits, updatedMachine.Network.Subnet.Bits)
|
||||||
assert.Equal(t, targetMachine.Machine.Network.ManagementIp.Ip, updatedMachine.Network.ManagementIp.Ip)
|
assert.Equal(t, targetMachine.Machine.Network.ManagementIp.Ip, updatedMachine.Network.ManagementIp.Ip)
|
||||||
@@ -362,7 +348,7 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update multiple fields simultaneously", func(t *testing.T) {
|
t.Run("update multiple fields simultaneously", func(t *testing.T) {
|
||||||
// Get a machine to update
|
// Get a machine to update.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -375,7 +361,7 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.NotNil(t, targetMachine)
|
require.NotNil(t, targetMachine)
|
||||||
|
|
||||||
// Update both name and public IP
|
// Update both name and public IP.
|
||||||
newName := "multi-update-machine"
|
newName := "multi-update-machine"
|
||||||
newPublicIP := &pb.IP{
|
newPublicIP := &pb.IP{
|
||||||
Ip: []byte{1, 1, 1, 1},
|
Ip: []byte{1, 1, 1, 1},
|
||||||
@@ -391,7 +377,7 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
assert.Equal(t, newName, updatedMachine.Name)
|
assert.Equal(t, newName, updatedMachine.Name)
|
||||||
assert.Equal(t, newPublicIP.Ip, updatedMachine.PublicIp.Ip)
|
assert.Equal(t, newPublicIP.Ip, updatedMachine.PublicIp.Ip)
|
||||||
|
|
||||||
// Verify both changes persisted
|
// Verify both changes persisted.
|
||||||
inspected, err := cli.InspectMachine(ctx, updatedMachine.Id)
|
inspected, err := cli.InspectMachine(ctx, updatedMachine.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, newName, inspected.Machine.Name)
|
assert.Equal(t, newName, inspected.Machine.Name)
|
||||||
@@ -399,7 +385,7 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update non-existent machine", func(t *testing.T) {
|
t.Run("update non-existent machine", func(t *testing.T) {
|
||||||
// Try to update properties on a machine that doesn't exist
|
// Try to update properties on a machine that doesn't exist.
|
||||||
nonExistentName := "should-be-updated"
|
nonExistentName := "should-be-updated"
|
||||||
req := &pb.UpdateMachineRequest{
|
req := &pb.UpdateMachineRequest{
|
||||||
MachineId: "non-existent-machine-id",
|
MachineId: "non-existent-machine-id",
|
||||||
@@ -410,7 +396,7 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update to duplicate name", func(t *testing.T) {
|
t.Run("update to duplicate name", func(t *testing.T) {
|
||||||
// Get two machines
|
// Get two machines.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, machines, 3)
|
require.Len(t, machines, 3)
|
||||||
@@ -418,7 +404,7 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
machine1 := machines[0]
|
machine1 := machines[0]
|
||||||
machine2 := machines[1]
|
machine2 := machines[1]
|
||||||
|
|
||||||
// Try to update machine2 with machine1's name
|
// Try to update machine2 with machine1's name.
|
||||||
req := &pb.UpdateMachineRequest{
|
req := &pb.UpdateMachineRequest{
|
||||||
MachineId: machine2.Machine.Id,
|
MachineId: machine2.Machine.Id,
|
||||||
Name: &machine1.Machine.Name,
|
Name: &machine1.Machine.Name,
|
||||||
@@ -428,20 +414,20 @@ func TestUpdateMachine(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update with empty request", func(t *testing.T) {
|
t.Run("update with empty request", func(t *testing.T) {
|
||||||
// Get a machine
|
// Get a machine.
|
||||||
machines, err := cli.ListMachines(ctx, nil)
|
machines, err := cli.ListMachines(ctx, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
targetMachine := machines[0]
|
targetMachine := machines[0]
|
||||||
|
|
||||||
// Update with no fields set (should be a no-op)
|
// Update with no fields set (should be a no-op).
|
||||||
req := &pb.UpdateMachineRequest{
|
req := &pb.UpdateMachineRequest{
|
||||||
MachineId: targetMachine.Machine.Id,
|
MachineId: targetMachine.Machine.Id,
|
||||||
}
|
}
|
||||||
updatedMachine, err := cli.UpdateMachine(ctx, req)
|
updatedMachine, err := cli.UpdateMachine(ctx, req)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Machine should remain unchanged
|
// Machine should remain unchanged.
|
||||||
assert.Equal(t, targetMachine.Machine.Name, updatedMachine.Name)
|
assert.Equal(t, targetMachine.Machine.Name, updatedMachine.Name)
|
||||||
if targetMachine.Machine.PublicIp != nil && updatedMachine.PublicIp != nil {
|
if targetMachine.Machine.PublicIp != nil && updatedMachine.PublicIp != nil {
|
||||||
assert.Equal(t, targetMachine.Machine.PublicIp.Ip, updatedMachine.PublicIp.Ip)
|
assert.Equal(t, targetMachine.Machine.PublicIp.Ip, updatedMachine.PublicIp.Ip)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"regexp"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -2022,4 +2023,153 @@ func TestServiceLifecycle(t *testing.T) {
|
|||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.Contains(t, err.Error(), "machines not found")
|
assert.Contains(t, err.Error(), "machines not found")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("internal DNS", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Deploy a global service so each machine gets one container.
|
||||||
|
serviceName := "test-dns-service"
|
||||||
|
t.Cleanup(func() {
|
||||||
|
err := cli.RemoveService(ctx, serviceName)
|
||||||
|
if err != nil && !errors.Is(err, api.ErrNotFound) {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
spec := api.ServiceSpec{
|
||||||
|
Name: serviceName,
|
||||||
|
Mode: api.ServiceModeGlobal,
|
||||||
|
Container: api.ContainerSpec{
|
||||||
|
Image: "portainer/pause:latest",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := cli.RunService(ctx, spec)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
svc, err := cli.InspectService(ctx, serviceName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Deploy a test service to run DNS queries from.
|
||||||
|
queryServiceName := "test-dns-query-service"
|
||||||
|
t.Cleanup(func() {
|
||||||
|
err := cli.RemoveService(ctx, queryServiceName)
|
||||||
|
if err != nil && !errors.Is(err, api.ErrNotFound) {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
querySvcSpec := api.ServiceSpec{
|
||||||
|
Name: queryServiceName,
|
||||||
|
Mode: api.ServiceModeReplicated,
|
||||||
|
Replicas: 1,
|
||||||
|
Placement: api.Placement{
|
||||||
|
Machines: []string{c.Machines[0].Name},
|
||||||
|
},
|
||||||
|
Container: api.ContainerSpec{
|
||||||
|
Image: "alpine:3.20",
|
||||||
|
Command: []string{"sleep", "infinity"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err = cli.RunService(ctx, querySvcSpec)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
querySvc, err := cli.InspectService(ctx, queryServiceName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
queryContainer := querySvc.Containers[0]
|
||||||
|
|
||||||
|
runNslookup := func(t *testing.T, dnsQuery string) string {
|
||||||
|
dnsOutput, err := execInContainerAndReadOutput(
|
||||||
|
t, ctx, cli, queryServiceName, queryContainer.Container.ID,
|
||||||
|
[]string{"nslookup", dnsQuery},
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
return dnsOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNoDNSErrors := func(t *testing.T, dnsOutput string) {
|
||||||
|
assert.NotContains(t, dnsOutput, "server can't find", "DNS query should not contain NXDOMAIN/SERVFAIL errors")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("service name resolves to all container IPs", func(t *testing.T) {
|
||||||
|
dnsOutput := runNslookup(t, serviceName+".internal")
|
||||||
|
t.Logf("DNS query output:\n%s", dnsOutput)
|
||||||
|
|
||||||
|
for _, ctr := range svc.Containers {
|
||||||
|
containerIP := ctr.Container.UncloudNetworkIP().String()
|
||||||
|
assert.Contains(t, dnsOutput, containerIP,
|
||||||
|
"Service DNS should resolve to container IP %s", containerIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNoDNSErrors(t, dnsOutput)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("machine-specific service DNS lookups", func(t *testing.T) {
|
||||||
|
for _, targetContainer := range svc.Containers {
|
||||||
|
targetMachineID := targetContainer.MachineID
|
||||||
|
targetContainerIP := targetContainer.Container.UncloudNetworkIP().String()
|
||||||
|
|
||||||
|
machineSpecificDNS := targetMachineID + ".m." + serviceName + ".internal"
|
||||||
|
|
||||||
|
dnsOutput := runNslookup(t, machineSpecificDNS)
|
||||||
|
t.Logf("Machine-specific DNS query output for %s:\n%s", machineSpecificDNS, dnsOutput)
|
||||||
|
|
||||||
|
assert.Contains(t, dnsOutput, targetContainerIP,
|
||||||
|
"Machine-specific DNS %s should resolve to container IP %s",
|
||||||
|
machineSpecificDNS, targetContainerIP)
|
||||||
|
|
||||||
|
// Machine-specific lookup should return only the container on that machine.
|
||||||
|
for _, ctr := range svc.Containers {
|
||||||
|
if ctr.MachineID != targetMachineID {
|
||||||
|
otherContainerIP := ctr.Container.UncloudNetworkIP().String()
|
||||||
|
assert.NotContains(t, dnsOutput, otherContainerIP,
|
||||||
|
"Machine-specific DNS %s should not resolve to other container IP %s",
|
||||||
|
machineSpecificDNS, otherContainerIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNoDNSErrors(t, dnsOutput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("service ID DNS lookup", func(t *testing.T) {
|
||||||
|
dnsOutput := runNslookup(t, svc.ID+".internal")
|
||||||
|
t.Logf("Service ID DNS query output:\n%s", dnsOutput)
|
||||||
|
|
||||||
|
for _, ctr := range svc.Containers {
|
||||||
|
containerIP := ctr.Container.UncloudNetworkIP().String()
|
||||||
|
assert.Contains(t, dnsOutput, containerIP,
|
||||||
|
"Service ID DNS should resolve to container IP %s", containerIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNoDNSErrors(t, dnsOutput)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("nearest mode prioritizes local subnet IPs", func(t *testing.T) {
|
||||||
|
var localIP string
|
||||||
|
for _, ctr := range svc.Containers {
|
||||||
|
if ctr.MachineID == queryContainer.MachineID {
|
||||||
|
localIP = ctr.Container.UncloudNetworkIP().String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.NotEmpty(t, localIP, "Should find local container IP on query machine %s", queryContainer.MachineID)
|
||||||
|
|
||||||
|
// Extracts the first IP from nslookup output after the "Name:" line.
|
||||||
|
re := regexp.MustCompile(`(?m)Name:\s+[\w\.\-]+\s+Address:\s+([\d\.]+)`)
|
||||||
|
|
||||||
|
// The default mode randomizes IP order, so run multiple times to reduce the chance of passing by coincidence.
|
||||||
|
for range 5 {
|
||||||
|
dnsOutput := runNslookup(t, "nearest."+serviceName+".internal")
|
||||||
|
t.Logf("Nearest mode DNS query output:\n%s", dnsOutput)
|
||||||
|
|
||||||
|
matches := re.FindStringSubmatch(dnsOutput)
|
||||||
|
require.Len(t, matches, 2, "Should find Name's Address in DNS output")
|
||||||
|
firstIP := matches[1]
|
||||||
|
assert.Equal(t, localIP, firstIP,
|
||||||
|
"Nearest mode should return local subnet IP first (query machine: %s, local IP: %s, first DNS result: %s)",
|
||||||
|
queryContainer.MachineID, localIP, firstIP)
|
||||||
|
|
||||||
|
assertNoDNSErrors(t, dnsOutput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user