-
Notifications
You must be signed in to change notification settings - Fork 260
Fix incorrect error "UnsupportedAPI" returned by CNI #1889
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d9177c9
5426afb
cccc81f
cfc862f
3384d24
4753c6c
8f8ec2c
786fdea
c08af0e
ae34861
f49eaa7
58d93c3
8b262ac
3fe14f0
d91073c
8c55e65
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,14 +3,18 @@ package network | |
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "errors" | ||
| "net" | ||
| "testing" | ||
|
|
||
| "github.com/Azure/azure-container-networking/cni" | ||
| "github.com/Azure/azure-container-networking/cns" | ||
| "github.com/Azure/azure-container-networking/cns/client" | ||
| "github.com/Azure/azure-container-networking/cns/types" | ||
| "github.com/Azure/azure-container-networking/network" | ||
| cniTypes "github.com/containernetworking/cni/pkg/types" | ||
| cniTypesCurr "github.com/containernetworking/cni/pkg/types/100" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
|
|
@@ -42,7 +46,19 @@ type getAllNetworkContainersConfigurationHandler struct { | |
| err error | ||
| } | ||
|
|
||
| type cnsAPIName string | ||
|
|
||
| const ( | ||
| GetAllNetworkContainers cnsAPIName = "GetAllNetworkContainers" | ||
| ) | ||
|
|
||
| var ( | ||
| errUnsupportedAPI = errors.New("Unsupported API") | ||
| errNoOrchestratorContextFound = errors.New("No CNI OrchestratorContext Found") | ||
| ) | ||
|
|
||
| type MockCNSClient struct { | ||
| unsupportedAPIs map[cnsAPIName]struct{} | ||
| require *require.Assertions | ||
| request requestIPAddressHandler | ||
| release releaseIPAddressHandler | ||
|
|
@@ -61,12 +77,23 @@ func (c *MockCNSClient) ReleaseIPAddress(_ context.Context, ipconfig cns.IPConfi | |
| } | ||
|
|
||
| func (c *MockCNSClient) GetNetworkContainer(ctx context.Context, orchestratorContext []byte) (*cns.GetNetworkContainerResponse, error) { | ||
| c.require.Exactly(c.getNetworkContainerConfiguration.orchestratorContext, orchestratorContext) | ||
| if !cmp.Equal(c.getNetworkContainerConfiguration.orchestratorContext, orchestratorContext) { | ||
| return nil, errNoOrchestratorContextFound | ||
| } | ||
| return c.getNetworkContainerConfiguration.returnResponse, c.getNetworkContainerConfiguration.err | ||
| } | ||
|
|
||
| func (c *MockCNSClient) GetAllNetworkContainers(ctx context.Context, orchestratorContext []byte) ([]cns.GetNetworkContainerResponse, error) { | ||
| c.require.Exactly(c.getAllNetworkContainersConfiguration.orchestratorContext, orchestratorContext) | ||
| if _, isUnsupported := c.unsupportedAPIs[GetAllNetworkContainers]; isUnsupported { | ||
| e := &client.CNSClientError{} | ||
| e.Code = types.UnsupportedAPI | ||
| e.Err = errUnsupportedAPI | ||
| return nil, e | ||
| } | ||
|
|
||
| if !cmp.Equal(c.getAllNetworkContainersConfiguration.orchestratorContext, orchestratorContext) { | ||
| return nil, errNoOrchestratorContextFound | ||
| } | ||
| return c.getAllNetworkContainersConfiguration.returnResponse, c.getAllNetworkContainersConfiguration.err | ||
| } | ||
|
|
||
|
|
@@ -456,7 +483,7 @@ func TestGetMultiTenancyCNIResult(t *testing.T) { | |
| tt.args.k8sNamespace, | ||
| tt.args.ifName) | ||
| if (err != nil) != tt.wantErr { | ||
| t.Errorf("GetContainerNetworkConfiguration() error = %v, wantErr %v", err, tt.wantErr) | ||
| t.Errorf("GetAllNetworkContainers() error = %v, wantErr %v", err, tt.wantErr) | ||
| return | ||
| } | ||
| if tt.wantErr { | ||
|
|
@@ -473,3 +500,287 @@ func TestGetMultiTenancyCNIResult(t *testing.T) { | |
| }) | ||
| } | ||
| } | ||
|
|
||
| // TestGetMultiTenancyCNIResultUnsupportedAPI tests if new CNS API is not supported and old CNS API can handle to get ncConfig with "Unsupported API" error | ||
| func TestGetMultiTenancyCNIResultUnsupportedAPI(t *testing.T) { | ||
| require := require.New(t) //nolint:gocritic | ||
|
|
||
| ncResponse := cns.GetNetworkContainerResponse{ | ||
| PrimaryInterfaceIdentifier: "10.0.0.0/16", | ||
| LocalIPConfiguration: cns.IPConfiguration{ | ||
| IPSubnet: cns.IPSubnet{ | ||
| IPAddress: "10.0.0.5", | ||
| PrefixLength: 16, | ||
| }, | ||
| GatewayIPAddress: "", | ||
| }, | ||
| CnetAddressSpace: []cns.IPSubnet{ | ||
| { | ||
| IPAddress: "10.1.0.0", | ||
| PrefixLength: 16, | ||
| }, | ||
| }, | ||
| IPConfiguration: cns.IPConfiguration{ | ||
| IPSubnet: cns.IPSubnet{ | ||
| IPAddress: "10.1.0.5", | ||
| PrefixLength: 16, | ||
| }, | ||
| DNSServers: nil, | ||
| GatewayIPAddress: "10.1.0.1", | ||
| }, | ||
| Routes: []cns.Route{ | ||
| { | ||
| IPAddress: "10.1.0.0/16", | ||
| GatewayIPAddress: "10.1.0.1", | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| // set new CNS API is not supported | ||
| unsupportedAPIs := make(map[cnsAPIName]struct{}) | ||
| unsupportedAPIs["GetAllNetworkContainers"] = struct{}{} | ||
|
|
||
| type args struct { | ||
| ctx context.Context | ||
| enableInfraVnet bool | ||
| nwCfg *cni.NetworkConfig | ||
| plugin *NetPlugin | ||
| k8sPodName string | ||
| k8sNamespace string | ||
| ifName string | ||
| } | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| args args | ||
| want *cns.GetNetworkContainerResponse | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "test happy path for Unsupported API with old CNS API", | ||
| args: args{ | ||
| enableInfraVnet: true, | ||
| nwCfg: &cni.NetworkConfig{ | ||
| MultiTenancy: true, | ||
| EnableSnatOnHost: true, | ||
| EnableExactMatchForPodName: true, | ||
| InfraVnetAddressSpace: "10.0.0.0/16", | ||
| IPAM: cni.IPAM{Type: "azure-vnet-ipam"}, | ||
| }, | ||
| plugin: &NetPlugin{ | ||
| ipamInvoker: NewMockIpamInvoker(false, false, false), | ||
| multitenancyClient: &Multitenancy{ | ||
| netioshim: &mockNetIOShim{}, | ||
| cnsclient: &MockCNSClient{ | ||
| unsupportedAPIs: unsupportedAPIs, | ||
| require: require, | ||
| getNetworkContainerConfiguration: getNetworkContainerConfigurationHandler{ | ||
| orchestratorContext: marshallPodInfo(cns.KubernetesPodInfo{ | ||
| PodName: "testpod", | ||
| PodNamespace: "testnamespace", | ||
| }), | ||
| returnResponse: &ncResponse, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| k8sPodName: "testpod", | ||
| k8sNamespace: "testnamespace", | ||
| ifName: "eth0", | ||
| }, | ||
| want: &cns.GetNetworkContainerResponse{ | ||
| PrimaryInterfaceIdentifier: "10.0.0.0/16", | ||
| LocalIPConfiguration: cns.IPConfiguration{ | ||
| IPSubnet: cns.IPSubnet{ | ||
| IPAddress: "10.0.0.5", | ||
| PrefixLength: 16, | ||
| }, | ||
| GatewayIPAddress: "", | ||
| }, | ||
| CnetAddressSpace: []cns.IPSubnet{ | ||
| { | ||
| IPAddress: "10.1.0.0", | ||
| PrefixLength: 16, | ||
| }, | ||
| }, | ||
| IPConfiguration: cns.IPConfiguration{ | ||
| IPSubnet: cns.IPSubnet{ | ||
| IPAddress: "10.1.0.5", | ||
| PrefixLength: 16, | ||
| }, | ||
| DNSServers: nil, | ||
| GatewayIPAddress: "10.1.0.1", | ||
| }, | ||
| Routes: []cns.Route{ | ||
| { | ||
| IPAddress: "10.1.0.0/16", | ||
| GatewayIPAddress: "10.1.0.1", | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| tt := tt | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| got, err := tt.args.plugin.multitenancyClient.GetAllNetworkContainers( | ||
| tt.args.ctx, | ||
| tt.args.nwCfg, | ||
| tt.args.k8sPodName, | ||
| tt.args.k8sNamespace, | ||
| tt.args.ifName) | ||
| if err != nil && tt.wantErr { | ||
| t.Fatalf("expected an error %+v but none received", err) | ||
| } | ||
| require.NoError(err) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use either
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, use t.Fatal() and t.Fatalf() instead |
||
| require.Exactly(tt.want, got[0].ncResponse) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| // TestGetMultiTenancyCNIResultNotFound test includes two sub test cases: | ||
| // 1. CNS supports new API and it does not have orchestratorContext info | ||
| // 2. CNS does not support new API and it does not have orchestratorContext info | ||
| func TestGetMultiTenancyCNIResultNotFound(t *testing.T) { | ||
| require := require.New(t) //nolint:gocritic | ||
|
|
||
| ncResponse := cns.GetNetworkContainerResponse{ | ||
| PrimaryInterfaceIdentifier: "10.0.0.0/16", | ||
| LocalIPConfiguration: cns.IPConfiguration{ | ||
| IPSubnet: cns.IPSubnet{ | ||
| IPAddress: "10.0.0.5", | ||
| PrefixLength: 16, | ||
| }, | ||
| GatewayIPAddress: "", | ||
| }, | ||
| CnetAddressSpace: []cns.IPSubnet{ | ||
| { | ||
| IPAddress: "10.1.0.0", | ||
| PrefixLength: 16, | ||
| }, | ||
| }, | ||
| IPConfiguration: cns.IPConfiguration{ | ||
| IPSubnet: cns.IPSubnet{ | ||
| IPAddress: "10.1.0.5", | ||
| PrefixLength: 16, | ||
| }, | ||
| DNSServers: nil, | ||
| GatewayIPAddress: "10.1.0.1", | ||
| }, | ||
| Routes: []cns.Route{ | ||
| { | ||
| IPAddress: "10.1.0.0/16", | ||
| GatewayIPAddress: "10.1.0.1", | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| // set new CNS API is not supported | ||
| unsupportedAPIs := make(map[cnsAPIName]struct{}) | ||
| unsupportedAPIs["GetAllNetworkContainers"] = struct{}{} | ||
|
|
||
| type args struct { | ||
| ctx context.Context | ||
| enableInfraVnet bool | ||
| nwCfg *cni.NetworkConfig | ||
| plugin *NetPlugin | ||
| k8sPodName string | ||
| k8sNamespace string | ||
| ifName string | ||
| } | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| args args | ||
| want *cns.GetNetworkContainerResponse | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "test happy path, CNS does not support new API without orchestratorContext found", | ||
| args: args{ | ||
| enableInfraVnet: true, | ||
| nwCfg: &cni.NetworkConfig{ | ||
| MultiTenancy: true, | ||
| EnableSnatOnHost: true, | ||
| EnableExactMatchForPodName: true, | ||
| InfraVnetAddressSpace: "10.0.0.0/16", | ||
| IPAM: cni.IPAM{Type: "azure-vnet-ipam"}, | ||
| }, | ||
| plugin: &NetPlugin{ | ||
| ipamInvoker: NewMockIpamInvoker(false, false, false), | ||
| multitenancyClient: &Multitenancy{ | ||
| netioshim: &mockNetIOShim{}, | ||
| cnsclient: &MockCNSClient{ | ||
| unsupportedAPIs: unsupportedAPIs, | ||
| require: require, | ||
| getNetworkContainerConfiguration: getNetworkContainerConfigurationHandler{ | ||
| orchestratorContext: marshallPodInfo(cns.KubernetesPodInfo{ | ||
| PodName: "testpod", | ||
| PodNamespace: "testnamespace", | ||
| }), | ||
| returnResponse: &ncResponse, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| // use mismatched k8sPodName and k8sNamespace | ||
| k8sPodName: "testpod1", | ||
| k8sNamespace: "testnamespace1", | ||
| ifName: "eth0", | ||
| }, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "test happy path, CNS does support new API without orchestratorContext found", | ||
| args: args{ | ||
| enableInfraVnet: true, | ||
| nwCfg: &cni.NetworkConfig{ | ||
| MultiTenancy: true, | ||
| EnableSnatOnHost: true, | ||
| EnableExactMatchForPodName: true, | ||
| InfraVnetAddressSpace: "10.0.0.0/16", | ||
| IPAM: cni.IPAM{Type: "azure-vnet-ipam"}, | ||
| }, | ||
| plugin: &NetPlugin{ | ||
| ipamInvoker: NewMockIpamInvoker(false, false, false), | ||
| multitenancyClient: &Multitenancy{ | ||
| netioshim: &mockNetIOShim{}, | ||
| cnsclient: &MockCNSClient{ | ||
| require: require, | ||
| getNetworkContainerConfiguration: getNetworkContainerConfigurationHandler{ | ||
| orchestratorContext: marshallPodInfo(cns.KubernetesPodInfo{ | ||
| PodName: "testpod", | ||
| PodNamespace: "testnamespace", | ||
| }), | ||
| returnResponse: &ncResponse, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| // use mismatched k8sPodName and k8sNamespace | ||
| k8sPodName: "testpod1", | ||
| k8sNamespace: "testnamespace1", | ||
| ifName: "eth0", | ||
| }, | ||
| wantErr: true, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| tt := tt | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| _, err := tt.args.plugin.multitenancyClient.GetAllNetworkContainers( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is wrong -
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is fine. Logic is: |
||
| tt.args.ctx, | ||
| tt.args.nwCfg, | ||
| tt.args.k8sPodName, | ||
| tt.args.k8sNamespace, | ||
| tt.args.ifName) | ||
| if err == nil && tt.wantErr { | ||
| t.Fatalf("expected an error %+v but none received", err) | ||
| } | ||
|
|
||
| if !errors.Is(err, errNoOrchestratorContextFound) { | ||
| t.Fatalf("expected an error %s but %v received", errNoOrchestratorContextFound, err) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.