diff --git a/Makefile b/Makefile index e8d7b9dfa0..416a092bc2 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,7 @@ CNI_SWIFT_BUILD_DIR = $(BUILD_DIR)/cni-swift CNI_OVERLAY_BUILD_DIR = $(BUILD_DIR)/cni-overlay CNI_BAREMETAL_BUILD_DIR = $(BUILD_DIR)/cni-baremetal CNI_DUALSTACK_BUILD_DIR = $(BUILD_DIR)/cni-dualstack +CNI_OVERLAY_CONSOLIDATED_BUILD_DIR = $(BUILD_DIR)/cni-overlay-consolidated CNS_BUILD_DIR = $(BUILD_DIR)/cns NPM_BUILD_DIR = $(BUILD_DIR)/npm TOOLS_DIR = $(REPO_ROOT)/build/tools @@ -96,6 +97,7 @@ CNI_SWIFT_ARCHIVE_NAME = azure-vnet-cni-swift-$(GOOS)-$(GOARCH)-$(CNI_VERSION).$ CNI_OVERLAY_ARCHIVE_NAME = azure-vnet-cni-overlay-$(GOOS)-$(GOARCH)-$(CNI_VERSION).$(ARCHIVE_EXT) CNI_BAREMETAL_ARCHIVE_NAME = azure-vnet-cni-baremetal-$(GOOS)-$(GOARCH)-$(CNI_VERSION).$(ARCHIVE_EXT) CNI_DUALSTACK_ARCHIVE_NAME = azure-vnet-cni-overlay-dualstack-$(GOOS)-$(GOARCH)-$(CNI_VERSION).$(ARCHIVE_EXT) +CNI_OVERLAY_CONSOLIDATED_ARCHIVE_NAME = azure-vnet-cni-overlay-consolidated-$(GOOS)-$(GOARCH)-$(CNI_VERSION).$(ARCHIVE_EXT) CNM_ARCHIVE_NAME = azure-vnet-cnm-$(GOOS)-$(GOARCH)-$(ACN_VERSION).$(ARCHIVE_EXT) CNS_ARCHIVE_NAME = azure-cns-$(GOOS)-$(GOARCH)-$(CNS_VERSION).$(ARCHIVE_EXT) NPM_ARCHIVE_NAME = azure-npm-$(GOOS)-$(GOARCH)-$(NPM_VERSION).$(ARCHIVE_EXT) @@ -632,6 +634,12 @@ endif cp $(CNI_BUILD_DIR)/azure-vnet$(EXE_EXT) $(CNI_BUILD_DIR)/azure-vnet-telemetry$(EXE_EXT) $(CNI_DUALSTACK_BUILD_DIR) cd $(CNI_DUALSTACK_BUILD_DIR) && $(ARCHIVE_CMD) $(CNI_DUALSTACK_ARCHIVE_NAME) azure-vnet$(EXE_EXT) azure-vnet-telemetry$(EXE_EXT) 10-azure.conflist azure-vnet-telemetry.config + $(MKDIR) $(CNI_OVERLAY_CONSOLIDATED_BUILD_DIR) + cp cni/azure-$(GOOS)-swift-overlay-consolidated.conflist $(CNI_OVERLAY_CONSOLIDATED_BUILD_DIR)/10-azure.conflist + cp telemetry/azure-vnet-telemetry.config $(CNI_OVERLAY_CONSOLIDATED_BUILD_DIR)/azure-vnet-telemetry.config + cp $(CNI_BUILD_DIR)/azure-vnet$(EXE_EXT) $(CNI_BUILD_DIR)/azure-vnet-telemetry$(EXE_EXT) $(CNI_OVERLAY_CONSOLIDATED_BUILD_DIR) + cd $(CNI_OVERLAY_CONSOLIDATED_BUILD_DIR) && $(ARCHIVE_CMD) $(CNI_OVERLAY_CONSOLIDATED_ARCHIVE_NAME) azure-vnet$(EXE_EXT) azure-vnet-telemetry$(EXE_EXT) 10-azure.conflist azure-vnet-telemetry.config + #baremetal mode is windows only (at least for now) ifeq ($(GOOS),windows) $(MKDIR) $(CNI_BAREMETAL_BUILD_DIR) diff --git a/cni/azure-linux-swift-overlay-consolidated.conflist b/cni/azure-linux-swift-overlay-consolidated.conflist new file mode 100644 index 0000000000..ab8844be9c --- /dev/null +++ b/cni/azure-linux-swift-overlay-consolidated.conflist @@ -0,0 +1,23 @@ +{ + "cniVersion":"0.3.0", + "name":"azure", + "plugins":[ + { + "type":"azure-vnet", + "mode":"transparent", + "executionMode":"v4swift", + "ipsToRouteViaHost":["169.254.20.10"], + "ipam":{ + "type":"azure-cns", + "mode":"overlay" + } + }, + { + "type":"portmap", + "capabilities":{ + "portMappings":true + }, + "snat":true + } + ] +} diff --git a/cni/azure-windows-swift-overlay-consolidated.conflist b/cni/azure-windows-swift-overlay-consolidated.conflist new file mode 100644 index 0000000000..6a462a7742 --- /dev/null +++ b/cni/azure-windows-swift-overlay-consolidated.conflist @@ -0,0 +1,50 @@ +{ + "cniVersion": "0.3.0", + "name": "azure", + "adapterName" : "", + "plugins": [ + { + "type": "azure-vnet", + "mode": "bridge", + "bridge": "azure0", + "executionMode": "v4swift", + "capabilities": { + "portMappings": true, + "dns": true + }, + "ipam": { + "type": "azure-cns", + "mode": "overlay" + }, + "dns": { + "Nameservers": [ + "10.0.0.10", + "168.63.129.16" + ], + "Search": [ + "svc.cluster.local" + ] + }, + "AdditionalArgs": [ + { + "Name": "EndpointPolicy", + "Value": { + "Type": "OutBoundNAT", + "ExceptionList": [ + "10.240.0.0/16", + "10.0.0.0/8" + ] + } + }, + { + "Name": "EndpointPolicy", + "Value": { + "Type": "ROUTE", + "DestinationPrefix": "10.0.0.0/8", + "NeedEncap": true + } + } + ] + } + ] +} diff --git a/cni/network/invoker_cns.go b/cni/network/invoker_cns.go index 2d42a73b6b..22864e688b 100644 --- a/cni/network/invoker_cns.go +++ b/cni/network/invoker_cns.go @@ -135,7 +135,7 @@ func (invoker *CNSIPAMInvoker) Add(addConfig IPAMAddConfig) (IPAMAddResult, erro ncgw := net.ParseIP(info.ncGatewayIPAddress) if ncgw == nil { - if (invoker.ipamMode != util.V4Overlay) && (invoker.ipamMode != util.DualStackOverlay) { + if (invoker.ipamMode != util.V4Overlay) && (invoker.ipamMode != util.DualStackOverlay) && (invoker.ipamMode != util.Overlay) { return IPAMAddResult{}, errors.Wrap(errInvalidArgs, "%w: Gateway address "+info.ncGatewayIPAddress+" from response is invalid") } @@ -199,7 +199,7 @@ func (invoker *CNSIPAMInvoker) Add(addConfig IPAMAddConfig) (IPAMAddResult, erro // set subnet prefix for host vm // setHostOptions will execute if IPAM mode is not v4 overlay and not dualStackOverlay mode - if (invoker.ipamMode != util.V4Overlay) && (invoker.ipamMode != util.DualStackOverlay) { + if (invoker.ipamMode != util.V4Overlay) && (invoker.ipamMode != util.DualStackOverlay) && (invoker.ipamMode != util.Overlay) { if err := setHostOptions(ncIPNet, addConfig.options, &info); err != nil { return IPAMAddResult{}, err } diff --git a/cni/network/invoker_cns_test.go b/cni/network/invoker_cns_test.go index 9d3325440a..6c4ad0f54f 100644 --- a/cni/network/invoker_cns_test.go +++ b/cni/network/invoker_cns_test.go @@ -148,6 +148,107 @@ func TestCNSIPAMInvoker_Add_Overlay(t *testing.T) { fields: fields{ podName: testPodInfo.PodName, podNamespace: testPodInfo.PodNamespace, + ipamMode: util.DualStackOverlay, + cnsClient: &MockCNSClient{ + require: require, + requestIPs: requestIPsHandler{ + ipconfigArgument: getTestIPConfigsRequest(), + result: &cns.IPConfigsResponse{ + PodIPInfo: []cns.PodIpInfo{ + { + PodIPConfig: cns.IPSubnet{ + IPAddress: "10.0.1.10", + PrefixLength: 24, + }, + NetworkContainerPrimaryIPConfig: cns.IPConfiguration{ + IPSubnet: cns.IPSubnet{ + IPAddress: "10.0.1.0", + PrefixLength: 24, + }, + DNSServers: nil, + GatewayIPAddress: "10.0.0.1", + }, + HostPrimaryIPInfo: cns.HostIPInfo{ + Gateway: "10.0.0.1", + PrimaryIP: "10.0.0.1", + Subnet: "10.0.0.0/24", + }, + }, + { + PodIPConfig: cns.IPSubnet{ + IPAddress: "fd11:1234::1", + PrefixLength: 24, + }, + NetworkContainerPrimaryIPConfig: cns.IPConfiguration{ + IPSubnet: cns.IPSubnet{ + IPAddress: "fd11:1234::", + PrefixLength: 112, + }, + DNSServers: nil, + GatewayIPAddress: "fe80::1234:5678:9abc", + }, + HostPrimaryIPInfo: cns.HostIPInfo{ + Gateway: "fe80::1234:5678:9abc", + PrimaryIP: "fe80::1234:5678:9abc", + Subnet: "fd11:1234::/112", + }, + }, + }, + Response: cns.Response{ + ReturnCode: 0, + Message: "", + }, + }, + err: nil, + }, + }, + }, + args: args{ + nwCfg: &cni.NetworkConfig{}, + args: &cniSkel.CmdArgs{ + ContainerID: "testcontainerid", + Netns: "testnetns", + IfName: "testifname", + }, + hostSubnetPrefix: getCIDRNotationForAddress("10.0.0.1/24"), + options: map[string]interface{}{}, + }, + wantIpv4Result: &cniTypesCurr.Result{ + IPs: []*cniTypesCurr.IPConfig{ + { + Address: *getCIDRNotationForAddress("10.0.1.10/24"), + Gateway: net.ParseIP("10.0.0.1"), + }, + }, + Routes: []*cniTypes.Route{ + { + Dst: network.Ipv4DefaultRouteDstPrefix, + GW: net.ParseIP("10.0.0.1"), + }, + }, + }, + wantIpv6Result: &cniTypesCurr.Result{ + IPs: []*cniTypesCurr.IPConfig{ + { + Address: *getCIDRNotationForAddress("fd11:1234::1/112"), + Gateway: net.ParseIP("fe80::1234:5678:9abc"), + }, + }, + Routes: []*cniTypes.Route{ + { + Dst: network.Ipv6DefaultRouteDstPrefix, + GW: net.ParseIP("fe80::1234:5678:9abc"), + }, + }, + }, + wantErr: false, + }, + { + name: "Test happy CNI Overlay add in consolidated overlay ipamMode", + fields: fields{ + podName: testPodInfo.PodName, + podNamespace: testPodInfo.PodNamespace, + ipamMode: util.Overlay, cnsClient: &MockCNSClient{ require: require, requestIPs: requestIPsHandler{ @@ -877,6 +978,29 @@ func TestCNSIPAMInvoker_Delete_Overlay(t *testing.T) { options: map[string]interface{}{}, }, }, + { + name: "test delete happy path in consolidated overlay ipamMode", + fields: fields{ + podName: testPodInfo.PodName, + podNamespace: testPodInfo.PodNamespace, + ipamMode: util.Overlay, + cnsClient: &MockCNSClient{ + require: require, + releaseIPs: releaseIPsHandler{ + ipconfigArgument: getTestIPConfigsRequest(), + }, + }, + }, + args: args{ + nwCfg: nil, + args: &cniSkel.CmdArgs{ + ContainerID: "testcontainerid", + Netns: "testnetns", + IfName: "testifname", + }, + options: map[string]interface{}{}, + }, + }, } for _, tt := range tests { tt := tt diff --git a/cni/network/network_windows.go b/cni/network/network_windows.go index 31a8b3e385..83c3c491cc 100644 --- a/cni/network/network_windows.go +++ b/cni/network/network_windows.go @@ -409,7 +409,7 @@ func determineWinVer() { } func getNATInfo(nwCfg *cni.NetworkConfig, ncPrimaryIPIface interface{}, enableSnatForDNS bool) (natInfo []policy.NATInfo) { - if nwCfg.ExecutionMode == string(util.V4Swift) && nwCfg.IPAM.Mode != string(util.V4Overlay) && nwCfg.IPAM.Mode != string(util.DualStackOverlay) { + if nwCfg.ExecutionMode == string(util.V4Swift) && nwCfg.IPAM.Mode != string(util.V4Overlay) && nwCfg.IPAM.Mode != string(util.DualStackOverlay) && nwCfg.IPAM.Mode != string(util.Overlay) { ncPrimaryIP := "" if ncPrimaryIPIface != nil { ncPrimaryIP = ncPrimaryIPIface.(string) diff --git a/cni/util/const.go b/cni/util/const.go index d3f88b2499..a1313f5040 100644 --- a/cni/util/const.go +++ b/cni/util/const.go @@ -15,4 +15,5 @@ type IpamMode string const ( V4Overlay IpamMode = "v4overlay" DualStackOverlay IpamMode = "dualStackOverlay" + Overlay IpamMode = "overlay" // Nothing changes between 'v4overlay' and 'dualStackOverlay' mode, so consolidating to one ) diff --git a/cns/cniconflist/generator.go b/cns/cniconflist/generator.go index 6bbe1744db..240f4f5b7d 100644 --- a/cns/cniconflist/generator.go +++ b/cns/cniconflist/generator.go @@ -53,6 +53,11 @@ type DualStackOverlayGenerator struct { Writer io.WriteCloser } +// OverlayGenerator generates the Azure CNI conflist for all Overlay scenarios +type OverlayGenerator struct { + Writer io.WriteCloser +} + // CiliumGenerator generates the Azure CNI conflist for the Cilium scenario type CiliumGenerator struct { Writer io.WriteCloser @@ -74,6 +79,14 @@ func (v *DualStackOverlayGenerator) Close() error { return nil } +func (v *OverlayGenerator) Close() error { + if err := v.Writer.Close(); err != nil { + return errors.Wrap(err, "error closing generator") + } + + return nil +} + func (v *CiliumGenerator) Close() error { if err := v.Writer.Close(); err != nil { return errors.Wrap(err, "error closing generator") diff --git a/cns/cniconflist/generator_linux.go b/cns/cniconflist/generator_linux.go index a91288352e..6e157c63bc 100644 --- a/cns/cniconflist/generator_linux.go +++ b/cns/cniconflist/generator_linux.go @@ -80,6 +80,35 @@ func (v *DualStackOverlayGenerator) Generate() error { return nil } +// Generate writes the CNI conflist to the Generator's output stream +func (v *OverlayGenerator) Generate() error { + conflist := cniConflist{ + CNIVersion: overlaycniVersion, + Name: overlaycniName, + Plugins: []any{ + cni.NetworkConfig{ + Type: overlaycniType, + Mode: cninet.OpModeTransparent, + ExecutionMode: string(util.V4Swift), + IPsToRouteViaHost: []string{nodeLocalDNSIP}, + IPAM: cni.IPAM{ + Type: network.AzureCNS, + Mode: string(util.Overlay), + }, + }, + portmapConfig, + }, + } + + enc := json.NewEncoder(v.Writer) + enc.SetIndent("", "\t") + if err := enc.Encode(conflist); err != nil { + return errors.Wrap(err, "error encoding conflist to json") + } + + return nil +} + // Generate writes the CNI conflist to the Generator's output stream func (v *CiliumGenerator) Generate() error { conflist := cniConflist{ diff --git a/cns/cniconflist/generator_linux_test.go b/cns/cniconflist/generator_linux_test.go index fcf9d68cb8..0e5c8c4b5f 100644 --- a/cns/cniconflist/generator_linux_test.go +++ b/cns/cniconflist/generator_linux_test.go @@ -47,6 +47,21 @@ func TestGenerateDualStackOverlayConflist(t *testing.T) { assert.Equal(t, removeNewLines(fixtureBytes), removeNewLines(buffer.Bytes())) } +func TestGenerateOverlayConflist(t *testing.T) { + fixture := "testdata/fixtures/azure-linux-swift-overlay-consolidated.conflist" + + buffer := new(bytes.Buffer) + g := cniconflist.OverlayGenerator{Writer: &bufferWriteCloser{buffer}} + err := g.Generate() + assert.NoError(t, err) + + fixtureBytes, err := os.ReadFile(fixture) + assert.NoError(t, err) + + // remove newlines and carriage returns in case these UTs are running on Windows + assert.Equal(t, removeNewLines(fixtureBytes), removeNewLines(buffer.Bytes())) +} + func TestGenerateCiliumConflist(t *testing.T) { fixture := "testdata/fixtures/cilium.conflist" diff --git a/cns/cniconflist/generator_windows.go b/cns/cniconflist/generator_windows.go index 08b62997b7..027cde81d2 100644 --- a/cns/cniconflist/generator_windows.go +++ b/cns/cniconflist/generator_windows.go @@ -14,6 +14,10 @@ func (v *DualStackOverlayGenerator) Generate() error { return errNotImplemented } +func (v *OverlayGenerator) Generate() error { + return errNotImplemented +} + func (v *CiliumGenerator) Generate() error { return errNotImplemented } diff --git a/cns/cniconflist/testdata/fixtures/azure-linux-swift-overlay-consolidated.conflist b/cns/cniconflist/testdata/fixtures/azure-linux-swift-overlay-consolidated.conflist new file mode 100644 index 0000000000..88b1ccaa96 --- /dev/null +++ b/cns/cniconflist/testdata/fixtures/azure-linux-swift-overlay-consolidated.conflist @@ -0,0 +1,30 @@ +{ + "cniVersion": "0.3.0", + "name": "azure", + "plugins": [ + { + "type": "azure-vnet", + "mode": "transparent", + "ipsToRouteViaHost": [ + "169.254.20.10" + ], + "executionMode": "v4swift", + "ipam": { + "mode": "overlay", + "type": "azure-cns" + }, + "dns": {}, + "runtimeConfig": { + "dns": {} + }, + "windowsSettings": {} + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + }, + "snat": true + } + ] +} diff --git a/cns/service/main.go b/cns/service/main.go index 5e9a64dac0..0cb2e01912 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -88,6 +88,7 @@ type cniConflistScenario string const ( scenarioV4Overlay cniConflistScenario = "v4overlay" scenarioDualStackOverlay cniConflistScenario = "dualStackOverlay" + scenarioOverlay cniConflistScenario = "overlay" scenarioCilium cniConflistScenario = "cilium" ) @@ -539,6 +540,8 @@ func main() { conflistGenerator = &cniconflist.V4OverlayGenerator{Writer: writer} case scenarioDualStackOverlay: conflistGenerator = &cniconflist.DualStackOverlayGenerator{Writer: writer} + case scenarioOverlay: + conflistGenerator = &cniconflist.OverlayGenerator{Writer: writer} case scenarioCilium: conflistGenerator = &cniconflist.CiliumGenerator{Writer: writer} default: