@@ -3,14 +3,18 @@ package network
3
3
import (
4
4
"context"
5
5
"encoding/json"
6
+ "errors"
6
7
"net"
7
8
"testing"
8
9
9
10
"github.com/Azure/azure-container-networking/cni"
10
11
"github.com/Azure/azure-container-networking/cns"
12
+ "github.com/Azure/azure-container-networking/cns/client"
13
+ "github.com/Azure/azure-container-networking/cns/types"
11
14
"github.com/Azure/azure-container-networking/network"
12
15
cniTypes "github.com/containernetworking/cni/pkg/types"
13
16
cniTypesCurr "github.com/containernetworking/cni/pkg/types/100"
17
+ "github.com/google/go-cmp/cmp"
14
18
"github.com/stretchr/testify/require"
15
19
)
16
20
@@ -42,7 +46,19 @@ type getAllNetworkContainersConfigurationHandler struct {
42
46
err error
43
47
}
44
48
49
+ type cnsAPIName string
50
+
51
+ const (
52
+ GetAllNetworkContainers cnsAPIName = "GetAllNetworkContainers"
53
+ )
54
+
55
+ var (
56
+ errUnsupportedAPI = errors .New ("Unsupported API" )
57
+ errNoOrchestratorContextFound = errors .New ("No CNI OrchestratorContext Found" )
58
+ )
59
+
45
60
type MockCNSClient struct {
61
+ unsupportedAPIs map [cnsAPIName ]struct {}
46
62
require * require.Assertions
47
63
request requestIPAddressHandler
48
64
release releaseIPAddressHandler
@@ -61,12 +77,23 @@ func (c *MockCNSClient) ReleaseIPAddress(_ context.Context, ipconfig cns.IPConfi
61
77
}
62
78
63
79
func (c * MockCNSClient ) GetNetworkContainer (ctx context.Context , orchestratorContext []byte ) (* cns.GetNetworkContainerResponse , error ) {
64
- c .require .Exactly (c .getNetworkContainerConfiguration .orchestratorContext , orchestratorContext )
80
+ if ! cmp .Equal (c .getNetworkContainerConfiguration .orchestratorContext , orchestratorContext ) {
81
+ return nil , errNoOrchestratorContextFound
82
+ }
65
83
return c .getNetworkContainerConfiguration .returnResponse , c .getNetworkContainerConfiguration .err
66
84
}
67
85
68
86
func (c * MockCNSClient ) GetAllNetworkContainers (ctx context.Context , orchestratorContext []byte ) ([]cns.GetNetworkContainerResponse , error ) {
69
- c .require .Exactly (c .getAllNetworkContainersConfiguration .orchestratorContext , orchestratorContext )
87
+ if _ , isUnsupported := c .unsupportedAPIs [GetAllNetworkContainers ]; isUnsupported {
88
+ e := & client.CNSClientError {}
89
+ e .Code = types .UnsupportedAPI
90
+ e .Err = errUnsupportedAPI
91
+ return nil , e
92
+ }
93
+
94
+ if ! cmp .Equal (c .getAllNetworkContainersConfiguration .orchestratorContext , orchestratorContext ) {
95
+ return nil , errNoOrchestratorContextFound
96
+ }
70
97
return c .getAllNetworkContainersConfiguration .returnResponse , c .getAllNetworkContainersConfiguration .err
71
98
}
72
99
@@ -456,7 +483,7 @@ func TestGetMultiTenancyCNIResult(t *testing.T) {
456
483
tt .args .k8sNamespace ,
457
484
tt .args .ifName )
458
485
if (err != nil ) != tt .wantErr {
459
- t .Errorf ("GetContainerNetworkConfiguration () error = %v, wantErr %v" , err , tt .wantErr )
486
+ t .Errorf ("GetAllNetworkContainers () error = %v, wantErr %v" , err , tt .wantErr )
460
487
return
461
488
}
462
489
if tt .wantErr {
@@ -473,3 +500,287 @@ func TestGetMultiTenancyCNIResult(t *testing.T) {
473
500
})
474
501
}
475
502
}
503
+
504
+ // TestGetMultiTenancyCNIResultUnsupportedAPI tests if new CNS API is not supported and old CNS API can handle to get ncConfig with "Unsupported API" error
505
+ func TestGetMultiTenancyCNIResultUnsupportedAPI (t * testing.T ) {
506
+ require := require .New (t ) //nolint:gocritic
507
+
508
+ ncResponse := cns.GetNetworkContainerResponse {
509
+ PrimaryInterfaceIdentifier : "10.0.0.0/16" ,
510
+ LocalIPConfiguration : cns.IPConfiguration {
511
+ IPSubnet : cns.IPSubnet {
512
+ IPAddress : "10.0.0.5" ,
513
+ PrefixLength : 16 ,
514
+ },
515
+ GatewayIPAddress : "" ,
516
+ },
517
+ CnetAddressSpace : []cns.IPSubnet {
518
+ {
519
+ IPAddress : "10.1.0.0" ,
520
+ PrefixLength : 16 ,
521
+ },
522
+ },
523
+ IPConfiguration : cns.IPConfiguration {
524
+ IPSubnet : cns.IPSubnet {
525
+ IPAddress : "10.1.0.5" ,
526
+ PrefixLength : 16 ,
527
+ },
528
+ DNSServers : nil ,
529
+ GatewayIPAddress : "10.1.0.1" ,
530
+ },
531
+ Routes : []cns.Route {
532
+ {
533
+ IPAddress : "10.1.0.0/16" ,
534
+ GatewayIPAddress : "10.1.0.1" ,
535
+ },
536
+ },
537
+ }
538
+
539
+ // set new CNS API is not supported
540
+ unsupportedAPIs := make (map [cnsAPIName ]struct {})
541
+ unsupportedAPIs ["GetAllNetworkContainers" ] = struct {}{}
542
+
543
+ type args struct {
544
+ ctx context.Context
545
+ enableInfraVnet bool
546
+ nwCfg * cni.NetworkConfig
547
+ plugin * NetPlugin
548
+ k8sPodName string
549
+ k8sNamespace string
550
+ ifName string
551
+ }
552
+
553
+ tests := []struct {
554
+ name string
555
+ args args
556
+ want * cns.GetNetworkContainerResponse
557
+ wantErr bool
558
+ }{
559
+ {
560
+ name : "test happy path for Unsupported API with old CNS API" ,
561
+ args : args {
562
+ enableInfraVnet : true ,
563
+ nwCfg : & cni.NetworkConfig {
564
+ MultiTenancy : true ,
565
+ EnableSnatOnHost : true ,
566
+ EnableExactMatchForPodName : true ,
567
+ InfraVnetAddressSpace : "10.0.0.0/16" ,
568
+ IPAM : cni.IPAM {Type : "azure-vnet-ipam" },
569
+ },
570
+ plugin : & NetPlugin {
571
+ ipamInvoker : NewMockIpamInvoker (false , false , false ),
572
+ multitenancyClient : & Multitenancy {
573
+ netioshim : & mockNetIOShim {},
574
+ cnsclient : & MockCNSClient {
575
+ unsupportedAPIs : unsupportedAPIs ,
576
+ require : require ,
577
+ getNetworkContainerConfiguration : getNetworkContainerConfigurationHandler {
578
+ orchestratorContext : marshallPodInfo (cns.KubernetesPodInfo {
579
+ PodName : "testpod" ,
580
+ PodNamespace : "testnamespace" ,
581
+ }),
582
+ returnResponse : & ncResponse ,
583
+ },
584
+ },
585
+ },
586
+ },
587
+ k8sPodName : "testpod" ,
588
+ k8sNamespace : "testnamespace" ,
589
+ ifName : "eth0" ,
590
+ },
591
+ want : & cns.GetNetworkContainerResponse {
592
+ PrimaryInterfaceIdentifier : "10.0.0.0/16" ,
593
+ LocalIPConfiguration : cns.IPConfiguration {
594
+ IPSubnet : cns.IPSubnet {
595
+ IPAddress : "10.0.0.5" ,
596
+ PrefixLength : 16 ,
597
+ },
598
+ GatewayIPAddress : "" ,
599
+ },
600
+ CnetAddressSpace : []cns.IPSubnet {
601
+ {
602
+ IPAddress : "10.1.0.0" ,
603
+ PrefixLength : 16 ,
604
+ },
605
+ },
606
+ IPConfiguration : cns.IPConfiguration {
607
+ IPSubnet : cns.IPSubnet {
608
+ IPAddress : "10.1.0.5" ,
609
+ PrefixLength : 16 ,
610
+ },
611
+ DNSServers : nil ,
612
+ GatewayIPAddress : "10.1.0.1" ,
613
+ },
614
+ Routes : []cns.Route {
615
+ {
616
+ IPAddress : "10.1.0.0/16" ,
617
+ GatewayIPAddress : "10.1.0.1" ,
618
+ },
619
+ },
620
+ },
621
+ },
622
+ }
623
+ for _ , tt := range tests {
624
+ tt := tt
625
+ t .Run (tt .name , func (t * testing.T ) {
626
+ got , err := tt .args .plugin .multitenancyClient .GetAllNetworkContainers (
627
+ tt .args .ctx ,
628
+ tt .args .nwCfg ,
629
+ tt .args .k8sPodName ,
630
+ tt .args .k8sNamespace ,
631
+ tt .args .ifName )
632
+ if err != nil && tt .wantErr {
633
+ t .Fatalf ("expected an error %+v but none received" , err )
634
+ }
635
+ require .NoError (err )
636
+ require .Exactly (tt .want , got [0 ].ncResponse )
637
+ })
638
+ }
639
+ }
640
+
641
+ // TestGetMultiTenancyCNIResultNotFound test includes two sub test cases:
642
+ // 1. CNS supports new API and it does not have orchestratorContext info
643
+ // 2. CNS does not support new API and it does not have orchestratorContext info
644
+ func TestGetMultiTenancyCNIResultNotFound (t * testing.T ) {
645
+ require := require .New (t ) //nolint:gocritic
646
+
647
+ ncResponse := cns.GetNetworkContainerResponse {
648
+ PrimaryInterfaceIdentifier : "10.0.0.0/16" ,
649
+ LocalIPConfiguration : cns.IPConfiguration {
650
+ IPSubnet : cns.IPSubnet {
651
+ IPAddress : "10.0.0.5" ,
652
+ PrefixLength : 16 ,
653
+ },
654
+ GatewayIPAddress : "" ,
655
+ },
656
+ CnetAddressSpace : []cns.IPSubnet {
657
+ {
658
+ IPAddress : "10.1.0.0" ,
659
+ PrefixLength : 16 ,
660
+ },
661
+ },
662
+ IPConfiguration : cns.IPConfiguration {
663
+ IPSubnet : cns.IPSubnet {
664
+ IPAddress : "10.1.0.5" ,
665
+ PrefixLength : 16 ,
666
+ },
667
+ DNSServers : nil ,
668
+ GatewayIPAddress : "10.1.0.1" ,
669
+ },
670
+ Routes : []cns.Route {
671
+ {
672
+ IPAddress : "10.1.0.0/16" ,
673
+ GatewayIPAddress : "10.1.0.1" ,
674
+ },
675
+ },
676
+ }
677
+
678
+ // set new CNS API is not supported
679
+ unsupportedAPIs := make (map [cnsAPIName ]struct {})
680
+ unsupportedAPIs ["GetAllNetworkContainers" ] = struct {}{}
681
+
682
+ type args struct {
683
+ ctx context.Context
684
+ enableInfraVnet bool
685
+ nwCfg * cni.NetworkConfig
686
+ plugin * NetPlugin
687
+ k8sPodName string
688
+ k8sNamespace string
689
+ ifName string
690
+ }
691
+
692
+ tests := []struct {
693
+ name string
694
+ args args
695
+ want * cns.GetNetworkContainerResponse
696
+ wantErr bool
697
+ }{
698
+ {
699
+ name : "test happy path, CNS does not support new API without orchestratorContext found" ,
700
+ args : args {
701
+ enableInfraVnet : true ,
702
+ nwCfg : & cni.NetworkConfig {
703
+ MultiTenancy : true ,
704
+ EnableSnatOnHost : true ,
705
+ EnableExactMatchForPodName : true ,
706
+ InfraVnetAddressSpace : "10.0.0.0/16" ,
707
+ IPAM : cni.IPAM {Type : "azure-vnet-ipam" },
708
+ },
709
+ plugin : & NetPlugin {
710
+ ipamInvoker : NewMockIpamInvoker (false , false , false ),
711
+ multitenancyClient : & Multitenancy {
712
+ netioshim : & mockNetIOShim {},
713
+ cnsclient : & MockCNSClient {
714
+ unsupportedAPIs : unsupportedAPIs ,
715
+ require : require ,
716
+ getNetworkContainerConfiguration : getNetworkContainerConfigurationHandler {
717
+ orchestratorContext : marshallPodInfo (cns.KubernetesPodInfo {
718
+ PodName : "testpod" ,
719
+ PodNamespace : "testnamespace" ,
720
+ }),
721
+ returnResponse : & ncResponse ,
722
+ },
723
+ },
724
+ },
725
+ },
726
+ // use mismatched k8sPodName and k8sNamespace
727
+ k8sPodName : "testpod1" ,
728
+ k8sNamespace : "testnamespace1" ,
729
+ ifName : "eth0" ,
730
+ },
731
+ wantErr : true ,
732
+ },
733
+ {
734
+ name : "test happy path, CNS does support new API without orchestratorContext found" ,
735
+ args : args {
736
+ enableInfraVnet : true ,
737
+ nwCfg : & cni.NetworkConfig {
738
+ MultiTenancy : true ,
739
+ EnableSnatOnHost : true ,
740
+ EnableExactMatchForPodName : true ,
741
+ InfraVnetAddressSpace : "10.0.0.0/16" ,
742
+ IPAM : cni.IPAM {Type : "azure-vnet-ipam" },
743
+ },
744
+ plugin : & NetPlugin {
745
+ ipamInvoker : NewMockIpamInvoker (false , false , false ),
746
+ multitenancyClient : & Multitenancy {
747
+ netioshim : & mockNetIOShim {},
748
+ cnsclient : & MockCNSClient {
749
+ require : require ,
750
+ getNetworkContainerConfiguration : getNetworkContainerConfigurationHandler {
751
+ orchestratorContext : marshallPodInfo (cns.KubernetesPodInfo {
752
+ PodName : "testpod" ,
753
+ PodNamespace : "testnamespace" ,
754
+ }),
755
+ returnResponse : & ncResponse ,
756
+ },
757
+ },
758
+ },
759
+ },
760
+ // use mismatched k8sPodName and k8sNamespace
761
+ k8sPodName : "testpod1" ,
762
+ k8sNamespace : "testnamespace1" ,
763
+ ifName : "eth0" ,
764
+ },
765
+ wantErr : true ,
766
+ },
767
+ }
768
+ for _ , tt := range tests {
769
+ tt := tt
770
+ t .Run (tt .name , func (t * testing.T ) {
771
+ _ , err := tt .args .plugin .multitenancyClient .GetAllNetworkContainers (
772
+ tt .args .ctx ,
773
+ tt .args .nwCfg ,
774
+ tt .args .k8sPodName ,
775
+ tt .args .k8sNamespace ,
776
+ tt .args .ifName )
777
+ if err == nil && tt .wantErr {
778
+ t .Fatalf ("expected an error %+v but none received" , err )
779
+ }
780
+
781
+ if ! errors .Is (err , errNoOrchestratorContextFound ) {
782
+ t .Fatalf ("expected an error %s but %v received" , errNoOrchestratorContextFound , err )
783
+ }
784
+ })
785
+ }
786
+ }
0 commit comments