// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 package serviceregistration import ( "testing" "github.com/hashicorp/nomad/nomad/structs" "github.com/hashicorp/nomad/plugins/drivers" "github.com/stretchr/testify/require" ) func Test_GetAddress(t *testing.T) { const HostIP = "127.0.0.1" testCases := []struct { name string // Inputs advertise string mode string portLabel string host map[string]int // will be converted to structs.Networks driver *drivers.DriverNetwork ports structs.AllocatedPorts status *structs.AllocNetworkStatus // Expectations expIP string expPort int expErr string }{ // Valid Configurations { name: "ExampleService", mode: structs.AddressModeAuto, portLabel: "db", host: map[string]int{"db": 12435}, driver: &drivers.DriverNetwork{ PortMap: map[string]int{"db": 6379}, IP: "10.1.2.3", }, expIP: HostIP, expPort: 12435, }, { name: "host", mode: structs.AddressModeHost, portLabel: "db", host: map[string]int{"db": 12345}, driver: &drivers.DriverNetwork{ PortMap: map[string]int{"db": 6379}, IP: "10.1.2.3", }, expIP: HostIP, expPort: 12345, }, { name: "driver", mode: structs.AddressModeDriver, portLabel: "db", host: map[string]int{"db": 12345}, driver: &drivers.DriverNetwork{ PortMap: map[string]int{"db": 6379}, IP: "10.1.2.3", }, expIP: "10.1.2.3", expPort: 6379, }, { name: "AutoDriver", mode: structs.AddressModeAuto, portLabel: "db", host: map[string]int{"db": 12345}, driver: &drivers.DriverNetwork{ PortMap: map[string]int{"db": 6379}, IP: "10.1.2.3", AutoAdvertise: true, }, expIP: "10.1.2.3", expPort: 6379, }, { name: "DriverCustomPort", mode: structs.AddressModeDriver, portLabel: "7890", host: map[string]int{"db": 12345}, driver: &drivers.DriverNetwork{ PortMap: map[string]int{"db": 6379}, IP: "10.1.2.3", }, expIP: "10.1.2.3", expPort: 7890, }, // Invalid Configurations { name: "DriverWithoutNetwork", mode: structs.AddressModeDriver, portLabel: "db", host: map[string]int{"db": 12345}, driver: nil, expErr: "no driver network exists", }, { name: "DriverBadPort", mode: structs.AddressModeDriver, portLabel: "bad-port-label", host: map[string]int{"db": 12345}, driver: &drivers.DriverNetwork{ PortMap: map[string]int{"db": 6379}, IP: "10.1.2.3", }, expErr: "invalid port", }, { name: "DriverZeroPort", mode: structs.AddressModeDriver, portLabel: "0", driver: &drivers.DriverNetwork{ IP: "10.1.2.3", }, expErr: "invalid port", }, { name: "HostBadPort", mode: structs.AddressModeHost, portLabel: "bad-port-label", expErr: "invalid port", }, { name: "InvalidMode", mode: "invalid-mode", portLabel: "80", expErr: "invalid address mode", }, { name: "NoPort_AutoMode", mode: structs.AddressModeAuto, expIP: HostIP, }, { name: "NoPort_HostMode", mode: structs.AddressModeHost, expIP: HostIP, }, { name: "NoPort_DriverMode", mode: structs.AddressModeDriver, driver: &drivers.DriverNetwork{ IP: "10.1.2.3", }, expIP: "10.1.2.3", }, // Scenarios using port 0.12 networking fields (NetworkStatus, AllocatedPortMapping) { name: "ExampleServer_withAllocatedPorts", mode: structs.AddressModeAuto, portLabel: "db", ports: []structs.AllocatedPortMapping{ { Label: "db", Value: 12435, To: 6379, HostIP: HostIP, }, }, status: &structs.AllocNetworkStatus{ InterfaceName: "eth0", Address: "172.26.0.1", }, expIP: HostIP, expPort: 12435, }, { name: "Host_withAllocatedPorts", mode: structs.AddressModeHost, portLabel: "db", ports: []structs.AllocatedPortMapping{ { Label: "db", Value: 12345, To: 6379, HostIP: HostIP, }, }, status: &structs.AllocNetworkStatus{ InterfaceName: "eth0", Address: "172.26.0.1", }, expIP: HostIP, expPort: 12345, }, { name: "Driver_withAllocatedPorts", mode: structs.AddressModeDriver, portLabel: "db", ports: []structs.AllocatedPortMapping{ { Label: "db", Value: 12345, To: 6379, HostIP: HostIP, }, }, driver: &drivers.DriverNetwork{ IP: "10.1.2.3", }, status: &structs.AllocNetworkStatus{ InterfaceName: "eth0", Address: "172.26.0.1", }, expIP: "10.1.2.3", expPort: 6379, }, { name: "AutoDriver_withAllocatedPorts", mode: structs.AddressModeAuto, portLabel: "db", ports: []structs.AllocatedPortMapping{ { Label: "db", Value: 12345, To: 6379, HostIP: HostIP, }, }, driver: &drivers.DriverNetwork{ IP: "10.1.2.3", AutoAdvertise: true, }, status: &structs.AllocNetworkStatus{ InterfaceName: "eth0", Address: "172.26.0.1", }, expIP: "10.1.2.3", expPort: 6379, }, { name: "DriverCustomPort_withAllocatedPorts", mode: structs.AddressModeDriver, portLabel: "7890", ports: []structs.AllocatedPortMapping{ { Label: "db", Value: 12345, To: 6379, HostIP: HostIP, }, }, driver: &drivers.DriverNetwork{ IP: "10.1.2.3", }, status: &structs.AllocNetworkStatus{ InterfaceName: "eth0", Address: "172.26.0.1", }, expIP: "10.1.2.3", expPort: 7890, }, { name: "Host_MultiHostInterface", mode: structs.AddressModeAuto, portLabel: "db", ports: []structs.AllocatedPortMapping{ { Label: "db", Value: 12345, To: 6379, HostIP: "127.0.0.100", }, }, status: &structs.AllocNetworkStatus{ InterfaceName: "eth0", Address: "172.26.0.1", }, expIP: "127.0.0.100", expPort: 12345, }, { name: "Alloc", mode: structs.AddressModeAlloc, portLabel: "db", ports: []structs.AllocatedPortMapping{ { Label: "db", Value: 12345, To: 6379, HostIP: HostIP, }, }, status: &structs.AllocNetworkStatus{ InterfaceName: "eth0", Address: "172.26.0.1", }, expIP: "172.26.0.1", expPort: 6379, }, { name: "Alloc no to value", mode: structs.AddressModeAlloc, portLabel: "db", ports: []structs.AllocatedPortMapping{ { Label: "db", Value: 12345, HostIP: HostIP, }, }, status: &structs.AllocNetworkStatus{ InterfaceName: "eth0", Address: "172.26.0.1", }, expIP: "172.26.0.1", expPort: 12345, }, { name: "AllocCustomPort", mode: structs.AddressModeAlloc, portLabel: "6379", status: &structs.AllocNetworkStatus{ InterfaceName: "eth0", Address: "172.26.0.1", }, expIP: "172.26.0.1", expPort: 6379, }, // Cases for setting the address field { name: "Address", mode: structs.AddressModeAuto, advertise: "example.com", expIP: "example.com", expPort: 0, }, { name: "Address with numeric port", mode: structs.AddressModeAuto, advertise: "example.com", portLabel: "8080", expIP: "example.com", expPort: 8080, }, { name: "Address with mapped port", mode: structs.AddressModeAuto, portLabel: "web", advertise: "example.com", ports: []structs.AllocatedPortMapping{ { Label: "web", Value: 12345, HostIP: HostIP, }, }, expIP: "example.com", expPort: 12345, }, { name: "Address with invalid mapped port", mode: structs.AddressModeAuto, advertise: "example.com", portLabel: "foobar", expErr: `invalid port: "foobar": not a valid port mapping or numeric port`, }, { name: "Address with host mode", mode: structs.AddressModeHost, advertise: "example.com", expErr: `cannot use custom advertise address with "host" address mode`, }, { name: "Address with driver mode", mode: structs.AddressModeDriver, advertise: "example.com", expErr: `cannot use custom advertise address with "driver" address mode`, }, { name: "Address with alloc mode", mode: structs.AddressModeAlloc, advertise: "example.com", expErr: `cannot use custom advertise address with "alloc" address mode`, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { // Convert host port map into a structs.Networks. networks := []*structs.NetworkResource{ { IP: HostIP, ReservedPorts: make([]structs.Port, len(tc.host)), }, } i := 0 for label, port := range tc.host { networks[0].ReservedPorts[i].Label = label networks[0].ReservedPorts[i].Value = port i++ } // Run the GetAddress function. actualIP, actualPort, actualErr := GetAddress( tc.advertise, tc.mode, tc.portLabel, networks, tc.driver, tc.ports, tc.status, ) // Assert the results require.Equal(t, tc.expIP, actualIP, "IP mismatch") require.Equal(t, tc.expPort, actualPort, "Port mismatch") if tc.expErr == "" { require.Nil(t, actualErr) } else { require.Error(t, actualErr) require.Contains(t, actualErr.Error(), tc.expErr) } }) } }