Add AllocIPv6 option to allow IPv6 address being used for service registration (#25632)

Fixes #25627 by adding an extra `alloc_advertise_ipv6` option similar to the `AdvertiseIPv6Addr` with the docker driver config.

Fixes: https://github.com/hashicorp/nomad/issues/25627
This commit is contained in:
Wim
2025-08-08 21:01:46 +02:00
committed by GitHub
parent 34025aa6b6
commit f712d5db90
7 changed files with 67 additions and 14 deletions

3
.changelog/25632.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:bug
consul: Add AllocIPv6 option to allow IPv6 address being used for service registration
```

View File

@@ -134,7 +134,7 @@ func GetAddress(
return driverNet.IP, port, nil
case structs.AddressModeAlloc:
case structs.AddressModeAlloc, structs.AddressModeAllocIPv6:
// Cannot use address mode alloc with custom advertise address.
if address != "" {
return "", 0, fmt.Errorf("cannot use custom advertise address with %q address mode", structs.AddressModeAlloc)
@@ -147,16 +147,17 @@ func GetAddress(
// If no port label is specified just return the IP
if portLabel == "" {
return netStatus.Address, 0, nil
return getAddressPort(addressMode, netStatus, 0)
}
// If port is a label and is found then return it
if port, ok := ports.Get(portLabel); ok {
// Use port.To value unless not set
if port.To > 0 {
return netStatus.Address, port.To, nil
return getAddressPort(addressMode, netStatus, port.To)
}
return netStatus.Address, port.Value, nil
return getAddressPort(addressMode, netStatus, port.Value)
}
// Check if port is a literal number
@@ -168,10 +169,19 @@ func GetAddress(
if port <= 0 {
return "", 0, fmt.Errorf("invalid port: %q: port must be >0", portLabel)
}
return netStatus.Address, port, nil
return getAddressPort(addressMode, netStatus, port)
default:
// Shouldn't happen due to validation, but enforce invariants
return "", 0, fmt.Errorf("invalid address mode %q", addressMode)
}
}
// getAddressPort is a helper function to return the IPv6 or IPv4 address based on the addressMode
func getAddressPort(addressMode string, netStatus *structs.AllocNetworkStatus, port int) (string, int, error) {
if addressMode == structs.AddressModeAllocIPv6 {
return netStatus.AddressIPv6, port, nil
}
return netStatus.Address, port, nil
}

View File

@@ -298,6 +298,26 @@ func Test_GetAddress(t *testing.T) {
expIP: "172.26.0.1",
expPort: 6379,
},
{
name: "Alloc",
mode: structs.AddressModeAllocIPv6,
portLabel: "db",
ports: []structs.AllocatedPortMapping{
{
Label: "db",
Value: 12345,
To: 6379,
HostIP: HostIP,
},
},
status: &structs.AllocNetworkStatus{
InterfaceName: "eth0",
Address: "172.26.0.1",
AddressIPv6: "2001:db8::8a2e:370:7334",
},
expIP: "2001:db8::8a2e:370:7334",
expPort: 6379,
},
{
name: "Alloc no to value",
mode: structs.AddressModeAlloc,
@@ -383,6 +403,12 @@ func Test_GetAddress(t *testing.T) {
advertise: "example.com",
expErr: `cannot use custom advertise address with "alloc" address mode`,
},
{
name: "Address with alloc IPv6 mode",
mode: structs.AddressModeAllocIPv6,
advertise: "example.com",
expErr: `cannot use custom advertise address with "alloc" address mode`,
},
}
for _, tc := range testCases {

View File

@@ -40,7 +40,7 @@ type Query struct {
Timeout time.Duration // connection / request timeout
AddressMode string // host, driver, or alloc
AddressMode string // host, driver, alloc or alloc_advertise_ipv6
PortLabel string // label or value
Protocol string // http checks only (http or https)

View File

@@ -305,7 +305,7 @@ func (sc *ServiceCheck) validateCommon(allowableTypes []string) error {
// validate address_mode
switch sc.AddressMode {
case "", AddressModeHost, AddressModeDriver, AddressModeAlloc:
case "", AddressModeHost, AddressModeDriver, AddressModeAlloc, AddressModeAllocIPv6:
// Ok
case AddressModeAuto:
return fmt.Errorf("invalid address_mode %q - %s only valid for services", sc.AddressMode, AddressModeAuto)
@@ -562,10 +562,11 @@ func hashHeader(h hash.Hash, m map[string][]string) {
}
const (
AddressModeAuto = "auto"
AddressModeHost = "host"
AddressModeDriver = "driver"
AddressModeAlloc = "alloc"
AddressModeAuto = "auto"
AddressModeHost = "host"
AddressModeDriver = "driver"
AddressModeAlloc = "alloc"
AddressModeAllocIPv6 = "alloc_ipv6"
// ServiceProviderConsul is the default service provider and the way Nomad
// worked before native service discovery.
@@ -597,7 +598,8 @@ type Service struct {
PortLabel string
// AddressMode specifies how the address in service registration is
// determined. Must be "auto" (default), "host", "driver", or "alloc".
// determined. Must be "auto" (default), "host", "driver", "alloc" or
// "alloc_ipv6".
AddressMode string
// Address enables explicitly setting a custom address to use in service
@@ -768,7 +770,7 @@ func (s *Service) Validate() error {
switch s.AddressMode {
case "", AddressModeAuto:
case AddressModeHost, AddressModeDriver, AddressModeAlloc:
case AddressModeHost, AddressModeDriver, AddressModeAlloc, AddressModeAllocIPv6:
if s.Address != "" {
mErr.Errors = append(mErr.Errors, fmt.Errorf("Service address_mode must be %q if address is set", AddressModeAuto))
}

View File

@@ -8313,6 +8313,10 @@ func validateServices(t *Task, tgNetworks Networks) error {
mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q cannot use address_mode=\"alloc\", only services defined in a \"group\" block can use this mode", service.Name))
}
if service.AddressMode == AddressModeAllocIPv6 {
mErr.Errors = append(mErr.Errors, fmt.Errorf("service %q cannot use address_mode=\"alloc_ipv6\", only services defined in a \"group\" block can use this mode", service.Name))
}
// Ensure that services with the same name are not being registered for
// the same port
if _, ok := knownServices[service.Name+service.PortLabel]; ok {
@@ -8350,6 +8354,10 @@ func validateServices(t *Task, tgNetworks Networks) error {
mErr.Errors = append(mErr.Errors, fmt.Errorf("check %q cannot use address_mode=\"alloc\", only checks defined in a \"group\" service block can use this mode", service.Name))
}
if check.AddressMode == AddressModeAllocIPv6 {
mErr.Errors = append(mErr.Errors, fmt.Errorf("check %q cannot use address_mode=\"alloc_ipv6\", only checks defined in a \"group\" service block can use this mode", service.Name))
}
if !check.RequiresPort() {
// No need to continue validating check if it doesn't need a port
continue

View File

@@ -150,6 +150,8 @@ service mesh][connect] integration.
If a `to` value is not set, the port falls back to using the allocated host port. The `port`
field may be a numeric port or a port label specified in the same group's network block.
- `alloc_ipv6` - Same as `alloc` but use the IPv6 address in case of dual-stack or IPv6-only.
- `driver` - Advertise the port determined by the driver (e.g. Docker).
The `port` may be a numeric port or a port label specified in the driver's
`ports` field.
@@ -190,7 +192,7 @@ service mesh][connect] integration.
- `tagged_addresses` `(map<string|string>` - Specifies custom [tagged addresses][tagged_addresses] to
advertise in the Consul service registration. Only available where `provider = "consul"`.
- `address_mode` `(string: "auto")` - Specifies which address (host, alloc or
- `address_mode` `(string: "auto")` - Specifies which address (host, alloc, alloc_ipv6 or
driver-specific) this service should advertise. See [below for
examples.](#using-driver-address-mode) Valid options are:
@@ -200,6 +202,8 @@ service mesh][connect] integration.
where no port mapping is necessary. This mode can only be set for services which
are defined in a "group" block.
- `alloc_ipv6` - Same as `alloc` but use the IPv6 address in case of dual-stack or IPv6-only.
- `auto` - Allows the driver to determine whether the host or driver address
should be used. Defaults to `host` and only implemented by Docker. If you
use a Docker network plugin such as weave, Docker will automatically use