diff --git a/pkg/utils/net-attach-def.go b/pkg/utils/net-attach-def.go index e865f396..bd563000 100644 --- a/pkg/utils/net-attach-def.go +++ b/pkg/utils/net-attach-def.go @@ -122,6 +122,67 @@ func GetNetworkStatus(pod *corev1.Pod) ([]v1.NetworkStatus, error) { return netStatuses, err } +// CreateNetworkStatuses creates an array of NetworkStatus from CNI result +// Not to be confused with CreateNetworkStatus (singular) +// This is the preferred method and picks up when CNI ADD results contain multiple container interfaces +func CreateNetworkStatuses(r cnitypes.Result, networkName string, defaultNetwork bool, dev *v1.DeviceInfo) ([]*v1.NetworkStatus, error) { + var networkStatuses []*v1.NetworkStatus + // indexMap is from original CNI result index to networkStatuses index + indexMap := make(map[int]int) + + // Convert whatever the IPAM result was into the current Result type + result, err := cni100.NewResultFromResult(r) + if err != nil { + return nil, fmt.Errorf("error converting the type.Result to cni100.Result: %v", err) + } + + // Discover default routes upfront and reuse them if necessary. + var useDefaultRoute []string + for _, route := range result.Routes { + if isDefaultRoute(route) { + useDefaultRoute = append(useDefaultRoute, route.GW.String()) + } + } + + // Same for DNS + v1dns := convertDNS(result.DNS) + + // Initialize NetworkStatus for each container interface (e.g. with sandbox present) + indexOfFoundPodInterface := 0 + for i, iface := range result.Interfaces { + if iface.Sandbox != "" { + ns := &v1.NetworkStatus{ + Name: networkName, + Default: defaultNetwork, + Interface: iface.Name, + Mac: iface.Mac, + Mtu: iface.Mtu, + IPs: []string{}, + Gateway: useDefaultRoute, + DeviceInfo: dev, + DNS: *v1dns, + } + networkStatuses = append(networkStatuses, ns) + // Map original index to the new slice index + indexMap[i] = indexOfFoundPodInterface + indexOfFoundPodInterface++ + } + } + + // Map IPs to network interface based on index + for _, ipConfig := range result.IPs { + if ipConfig.Interface != nil { + originalIndex := *ipConfig.Interface + if newIndex, ok := indexMap[originalIndex]; ok { + ns := networkStatuses[newIndex] + ns.IPs = append(ns.IPs, ipConfig.Address.IP.String()) + } + } + } + + return networkStatuses, nil +} + // CreateNetworkStatus create NetworkStatus from CNI result func CreateNetworkStatus(r cnitypes.Result, networkName string, defaultNetwork bool, dev *v1.DeviceInfo) (*v1.NetworkStatus, error) { netStatus := &v1.NetworkStatus{} diff --git a/pkg/utils/net-attach-def_test.go b/pkg/utils/net-attach-def_test.go index cdcd8855..10fe5c33 100644 --- a/pkg/utils/net-attach-def_test.go +++ b/pkg/utils/net-attach-def_test.go @@ -224,6 +224,79 @@ var _ = Describe("Netwok Attachment Definition manipulations", func() { }) }) + Context("create network statuses from CNI result with multiple interfaces", func() { + var cniResult *cni100.Result + var networkStatuses []*v1.NetworkStatus + + BeforeEach(func() { + cniResult = &cni100.Result{ + CNIVersion: "1.1.0", + Interfaces: []*cni100.Interface{ + { + Name: "example0", + Mac: "00:AA:BB:CC:DD:01", + Sandbox: "/path/to/network/namespace", + Mtu: 1500, + }, + { + Name: "foo", + Mac: "00:AA:BB:CC:DD:33", + }, + { + Name: "example1", + Mac: "00:AA:BB:CC:DD:02", + Sandbox: "/path/to/network/namespace", + Mtu: 1500, + }, + }, + IPs: []*cni100.IPConfig{ + { + Address: *EnsureCIDR("192.0.2.1/24"), + Interface: &[]int{0}[0], + }, + { + Address: *EnsureCIDR("192.0.2.2/24"), + Interface: &[]int{0}[0], + }, + { + Address: *EnsureCIDR("192.0.2.3/24"), + Interface: &[]int{2}[0], + }, + }, + DNS: cnitypes.DNS{ + Nameservers: []string{"8.8.8.8", "8.8.4.4"}, + Search: []string{"example.com"}, + Options: []string{"ndots:2"}, + }, + } + var err error + networkStatuses, err = CreateNetworkStatuses(cniResult, "test-multi-net-attach-def", false, nil) + Expect(err).NotTo(HaveOccurred()) + }) + + It("creates network statuses for interfaces with sandbox", func() { + Expect(networkStatuses).To(HaveLen(2)) + // Check details for the first returned network status + Expect(networkStatuses[0].Name).To(Equal("test-multi-net-attach-def")) + Expect(networkStatuses[0].Interface).To(Equal("example0")) + Expect(networkStatuses[0].Mtu).To(Equal(1500)) + Expect(networkStatuses[0].Mac).To(Equal("00:AA:BB:CC:DD:01")) + Expect(networkStatuses[0].IPs).To(ConsistOf("192.0.2.1", "192.0.2.2")) + + // Check details for the second returned network status + Expect(networkStatuses[1].Interface).To(Equal("example1")) + Expect(networkStatuses[1].Mtu).To(Equal(1500)) + Expect(networkStatuses[1].Mac).To(Equal("00:AA:BB:CC:DD:02")) + Expect(networkStatuses[1].IPs).To(ConsistOf("192.0.2.3")) + }) + + It("excludes interfaces without a sandbox", func() { + for _, status := range networkStatuses { + Expect(status.Interface).NotTo(Equal("foo")) + } + }) + }) + It("parse network selection element in pod", func() { selectionElement := ` [{