Skip to content

Commit f712d5d

Browse files
authored
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: #25627
1 parent 34025aa commit f712d5d

File tree

7 files changed

+67
-14
lines changed

7 files changed

+67
-14
lines changed

.changelog/25632.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
consul: Add AllocIPv6 option to allow IPv6 address being used for service registration
3+
```

client/serviceregistration/address.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func GetAddress(
134134

135135
return driverNet.IP, port, nil
136136

137-
case structs.AddressModeAlloc:
137+
case structs.AddressModeAlloc, structs.AddressModeAllocIPv6:
138138
// Cannot use address mode alloc with custom advertise address.
139139
if address != "" {
140140
return "", 0, fmt.Errorf("cannot use custom advertise address with %q address mode", structs.AddressModeAlloc)
@@ -147,16 +147,17 @@ func GetAddress(
147147

148148
// If no port label is specified just return the IP
149149
if portLabel == "" {
150-
return netStatus.Address, 0, nil
150+
return getAddressPort(addressMode, netStatus, 0)
151151
}
152152

153153
// If port is a label and is found then return it
154154
if port, ok := ports.Get(portLabel); ok {
155155
// Use port.To value unless not set
156156
if port.To > 0 {
157-
return netStatus.Address, port.To, nil
157+
return getAddressPort(addressMode, netStatus, port.To)
158158
}
159-
return netStatus.Address, port.Value, nil
159+
160+
return getAddressPort(addressMode, netStatus, port.Value)
160161
}
161162

162163
// Check if port is a literal number
@@ -168,10 +169,19 @@ func GetAddress(
168169
if port <= 0 {
169170
return "", 0, fmt.Errorf("invalid port: %q: port must be >0", portLabel)
170171
}
171-
return netStatus.Address, port, nil
172172

173+
return getAddressPort(addressMode, netStatus, port)
173174
default:
174175
// Shouldn't happen due to validation, but enforce invariants
175176
return "", 0, fmt.Errorf("invalid address mode %q", addressMode)
176177
}
177178
}
179+
180+
// getAddressPort is a helper function to return the IPv6 or IPv4 address based on the addressMode
181+
func getAddressPort(addressMode string, netStatus *structs.AllocNetworkStatus, port int) (string, int, error) {
182+
if addressMode == structs.AddressModeAllocIPv6 {
183+
return netStatus.AddressIPv6, port, nil
184+
}
185+
186+
return netStatus.Address, port, nil
187+
}

client/serviceregistration/address_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,26 @@ func Test_GetAddress(t *testing.T) {
298298
expIP: "172.26.0.1",
299299
expPort: 6379,
300300
},
301+
{
302+
name: "Alloc",
303+
mode: structs.AddressModeAllocIPv6,
304+
portLabel: "db",
305+
ports: []structs.AllocatedPortMapping{
306+
{
307+
Label: "db",
308+
Value: 12345,
309+
To: 6379,
310+
HostIP: HostIP,
311+
},
312+
},
313+
status: &structs.AllocNetworkStatus{
314+
InterfaceName: "eth0",
315+
Address: "172.26.0.1",
316+
AddressIPv6: "2001:db8::8a2e:370:7334",
317+
},
318+
expIP: "2001:db8::8a2e:370:7334",
319+
expPort: 6379,
320+
},
301321
{
302322
name: "Alloc no to value",
303323
mode: structs.AddressModeAlloc,
@@ -383,6 +403,12 @@ func Test_GetAddress(t *testing.T) {
383403
advertise: "example.com",
384404
expErr: `cannot use custom advertise address with "alloc" address mode`,
385405
},
406+
{
407+
name: "Address with alloc IPv6 mode",
408+
mode: structs.AddressModeAllocIPv6,
409+
advertise: "example.com",
410+
expErr: `cannot use custom advertise address with "alloc" address mode`,
411+
},
386412
}
387413

388414
for _, tc := range testCases {

client/serviceregistration/checks/result.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type Query struct {
4040

4141
Timeout time.Duration // connection / request timeout
4242

43-
AddressMode string // host, driver, or alloc
43+
AddressMode string // host, driver, alloc or alloc_advertise_ipv6
4444
PortLabel string // label or value
4545

4646
Protocol string // http checks only (http or https)

nomad/structs/services.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ func (sc *ServiceCheck) validateCommon(allowableTypes []string) error {
305305

306306
// validate address_mode
307307
switch sc.AddressMode {
308-
case "", AddressModeHost, AddressModeDriver, AddressModeAlloc:
308+
case "", AddressModeHost, AddressModeDriver, AddressModeAlloc, AddressModeAllocIPv6:
309309
// Ok
310310
case AddressModeAuto:
311311
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) {
562562
}
563563

564564
const (
565-
AddressModeAuto = "auto"
566-
AddressModeHost = "host"
567-
AddressModeDriver = "driver"
568-
AddressModeAlloc = "alloc"
565+
AddressModeAuto = "auto"
566+
AddressModeHost = "host"
567+
AddressModeDriver = "driver"
568+
AddressModeAlloc = "alloc"
569+
AddressModeAllocIPv6 = "alloc_ipv6"
569570

570571
// ServiceProviderConsul is the default service provider and the way Nomad
571572
// worked before native service discovery.
@@ -597,7 +598,8 @@ type Service struct {
597598
PortLabel string
598599

599600
// AddressMode specifies how the address in service registration is
600-
// determined. Must be "auto" (default), "host", "driver", or "alloc".
601+
// determined. Must be "auto" (default), "host", "driver", "alloc" or
602+
// "alloc_ipv6".
601603
AddressMode string
602604

603605
// Address enables explicitly setting a custom address to use in service
@@ -768,7 +770,7 @@ func (s *Service) Validate() error {
768770

769771
switch s.AddressMode {
770772
case "", AddressModeAuto:
771-
case AddressModeHost, AddressModeDriver, AddressModeAlloc:
773+
case AddressModeHost, AddressModeDriver, AddressModeAlloc, AddressModeAllocIPv6:
772774
if s.Address != "" {
773775
mErr.Errors = append(mErr.Errors, fmt.Errorf("Service address_mode must be %q if address is set", AddressModeAuto))
774776
}

nomad/structs/structs.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8313,6 +8313,10 @@ func validateServices(t *Task, tgNetworks Networks) error {
83138313
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))
83148314
}
83158315

8316+
if service.AddressMode == AddressModeAllocIPv6 {
8317+
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))
8318+
}
8319+
83168320
// Ensure that services with the same name are not being registered for
83178321
// the same port
83188322
if _, ok := knownServices[service.Name+service.PortLabel]; ok {
@@ -8350,6 +8354,10 @@ func validateServices(t *Task, tgNetworks Networks) error {
83508354
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))
83518355
}
83528356

8357+
if check.AddressMode == AddressModeAllocIPv6 {
8358+
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))
8359+
}
8360+
83538361
if !check.RequiresPort() {
83548362
// No need to continue validating check if it doesn't need a port
83558363
continue

website/content/docs/job-specification/service.mdx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ service mesh][connect] integration.
150150
If a `to` value is not set, the port falls back to using the allocated host port. The `port`
151151
field may be a numeric port or a port label specified in the same group's network block.
152152

153+
- `alloc_ipv6` - Same as `alloc` but use the IPv6 address in case of dual-stack or IPv6-only.
154+
153155
- `driver` - Advertise the port determined by the driver (e.g. Docker).
154156
The `port` may be a numeric port or a port label specified in the driver's
155157
`ports` field.
@@ -190,7 +192,7 @@ service mesh][connect] integration.
190192
- `tagged_addresses` `(map<string|string>` - Specifies custom [tagged addresses][tagged_addresses] to
191193
advertise in the Consul service registration. Only available where `provider = "consul"`.
192194

193-
- `address_mode` `(string: "auto")` - Specifies which address (host, alloc or
195+
- `address_mode` `(string: "auto")` - Specifies which address (host, alloc, alloc_ipv6 or
194196
driver-specific) this service should advertise. See [below for
195197
examples.](#using-driver-address-mode) Valid options are:
196198

@@ -200,6 +202,8 @@ service mesh][connect] integration.
200202
where no port mapping is necessary. This mode can only be set for services which
201203
are defined in a "group" block.
202204

205+
- `alloc_ipv6` - Same as `alloc` but use the IPv6 address in case of dual-stack or IPv6-only.
206+
203207
- `auto` - Allows the driver to determine whether the host or driver address
204208
should be used. Defaults to `host` and only implemented by Docker. If you
205209
use a Docker network plugin such as weave, Docker will automatically use

0 commit comments

Comments
 (0)