Skip to content

Commit

Permalink
networking: set alloc NetworkStatus.AddressIPv6 (#23959)
Browse files Browse the repository at this point in the history
when a CNI result includes an IPv6 address,
set it on the alloc's NetworkStatus for reference.

e.g.:

$ nomad alloc status -json 3dca | jq '.NetworkStatus'
{
  "Address": "172.26.64.14",
  "AddressIPv6": "fd00:a110:c8::b",
  "DNS": null,
  "InterfaceName": "eth0"
}
  • Loading branch information
gulducat authored Sep 16, 2024
1 parent 210044b commit 5e1fae2
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 21 deletions.
1 change: 1 addition & 0 deletions api/allocations.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ type AllocDeploymentStatus struct {
type AllocNetworkStatus struct {
InterfaceName string
Address string
AddressIPv6 string
DNS *DNSConfig
}

Expand Down
57 changes: 36 additions & 21 deletions client/allocrunner/networking_cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,36 +393,51 @@ func (c *cniNetworkConfigurator) cniToAllocNet(res *cni.Result) (*structs.AllocN
}
sort.Strings(names)

// Use the first sandbox interface with an IP address
for _, name := range names {
iface := res.Interfaces[name]
if iface == nil {
// setStatus sets netStatus.Address and netStatus.InterfaceName
// if it finds a suitable interface that has IP address(es)
// (at least IPv4, possibly also IPv6)
setStatus := func(requireSandbox bool) {
for _, name := range names {
iface := res.Interfaces[name]
// this should never happen but this value is coming from external
// plugins so we should guard against it
delete(res.Interfaces, name)
continue
}
if iface == nil {
continue
}

if iface.Sandbox != "" && len(iface.IPConfigs) > 0 {
netStatus.Address = iface.IPConfigs[0].IP.String()
netStatus.InterfaceName = name
break
if requireSandbox && iface.Sandbox == "" {
continue
}

for _, ipConfig := range iface.IPConfigs {
isIP4 := ipConfig.IP.To4() != nil
if netStatus.Address == "" && isIP4 {
netStatus.Address = ipConfig.IP.String()
}
if netStatus.AddressIPv6 == "" && !isIP4 {
netStatus.AddressIPv6 = ipConfig.IP.String()
}
}

// found a good interface, so we're done
if netStatus.Address != "" {
netStatus.InterfaceName = name
return
}
}
}

// Use the first sandbox interface with an IP address
setStatus(true)

// If no IP address was found, use the first interface with an address
// found as a fallback
if netStatus.Address == "" {
for _, name := range names {
iface := res.Interfaces[name]
if len(iface.IPConfigs) > 0 {
ip := iface.IPConfigs[0].IP.String()
c.logger.Debug("no sandbox interface with an address found CNI result, using first available", "interface", name, "ip", ip)
netStatus.Address = ip
netStatus.InterfaceName = name
break
}
}
setStatus(false)
c.logger.Debug("no sandbox interface with an address found CNI result, using first available",
"interface", netStatus.InterfaceName,
"ip", netStatus.Address,
)
}

// If no IP address could be found, return an error
Expand Down
33 changes: 33 additions & 0 deletions client/allocrunner/networking_cni_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,39 @@ func TestCNI_cniToAllocNet_Invalid(t *testing.T) {
require.Nil(t, allocNet)
}

func TestCNI_cniToAllocNet_Dualstack(t *testing.T) {
ci.Parallel(t)

cniResult := &cni.Result{
Interfaces: map[string]*cni.Config{
"eth0": {
Sandbox: "at-the-park",
IPConfigs: []*cni.IPConfig{
{IP: net.IPv4zero}, // 0.0.0.0
{IP: net.IPv6zero}, // ::
},
},
// "a" puts this lexicographically first when sorted
"a-skippable-interface": {
// no Sandbox, so should be skipped
IPConfigs: []*cni.IPConfig{
{IP: net.IPv4(127, 3, 2, 1)},
},
},
},
}

c := &cniNetworkConfigurator{
logger: testlog.HCLogger(t),
}
allocNet, err := c.cniToAllocNet(cniResult)
must.NoError(t, err)
must.NotNil(t, allocNet)
test.Eq(t, "0.0.0.0", allocNet.Address)
test.Eq(t, "::", allocNet.AddressIPv6)
test.Eq(t, "eth0", allocNet.InterfaceName)
}

func TestCNI_addCustomCNIArgs(t *testing.T) {
ci.Parallel(t)
cniArgs := map[string]string{
Expand Down
4 changes: 4 additions & 0 deletions nomad/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12101,6 +12101,7 @@ func (s *NodeScoreMeta) Data() interface{} {
type AllocNetworkStatus struct {
InterfaceName string
Address string
AddressIPv6 string
DNS *DNSConfig
}

Expand All @@ -12111,6 +12112,7 @@ func (a *AllocNetworkStatus) Copy() *AllocNetworkStatus {
return &AllocNetworkStatus{
InterfaceName: a.InterfaceName,
Address: a.Address,
AddressIPv6: a.AddressIPv6,
DNS: a.DNS.Copy(),
}
}
Expand All @@ -12131,6 +12133,8 @@ func (a *AllocNetworkStatus) Equal(o *AllocNetworkStatus) bool {
return false
case a.Address != o.Address:
return false
case a.AddressIPv6 != o.AddressIPv6:
return false
case !a.DNS.Equal(o.DNS):
return false
}
Expand Down

0 comments on commit 5e1fae2

Please sign in to comment.