From 0d50f49c51e9f0e5a8ba89f77110bcdc6e8fd866 Mon Sep 17 00:00:00 2001 From: l1b0k Date: Fri, 3 Jan 2025 15:40:17 +0800 Subject: [PATCH] update ut Signed-off-by: l1b0k --- pkg/eni/crdv2_test.go | 62 +++++++ pkg/eni/remote_test.go | 67 +++++++- pkg/ip/ip_test.go | 23 +++ pkg/k8s/k8s_test.go | 226 +++++++++++++++++++++++++ types/controlplane/annotations_test.go | 83 +++++++++ types/daemon/config_test.go | 60 +++++++ types/daemon/dynamicconfig_test.go | 56 ++++++ types/daemon/res_test.go | 50 ++++++ types/helper_test.go | 134 +++++++++++++++ types/secret/secret_test.go | 32 ++++ 10 files changed, 785 insertions(+), 8 deletions(-) create mode 100644 pkg/k8s/k8s_test.go create mode 100644 types/controlplane/annotations_test.go create mode 100644 types/daemon/dynamicconfig_test.go create mode 100644 types/daemon/res_test.go create mode 100644 types/helper_test.go create mode 100644 types/secret/secret_test.go diff --git a/pkg/eni/crdv2_test.go b/pkg/eni/crdv2_test.go index f9c599df..36ee7a09 100644 --- a/pkg/eni/crdv2_test.go +++ b/pkg/eni/crdv2_test.go @@ -10,6 +10,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" + pkgclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" networkv1beta1 "github.com/AliyunContainerService/terway/pkg/apis/network.alibabacloud.com/v1beta1" @@ -470,3 +471,64 @@ func Test_removeDeleted(t *testing.T) { }) } } + +func TestSyncNodeRuntimeReturnsNilWhenNoDeletedPods(t *testing.T) { + r := &CRDV2{ + deletedPods: make(map[string]*networkv1beta1.RuntimePodStatus), + } + err := r.syncNodeRuntime(context.Background()) + assert.NoError(t, err) +} + +func TestSyncNodeRuntimeReturnsErrorWhenGetRuntimeNodeFails(t *testing.T) { + client := fake.NewClientBuilder().WithScheme(types.Scheme).Build() + r := &CRDV2{ + client: client, + deletedPods: map[string]*networkv1beta1.RuntimePodStatus{"pod-1": {PodID: "pod-1"}}, + } + err := r.syncNodeRuntime(context.Background()) + assert.Error(t, err) +} + +func TestSyncNodeRuntimeUpdatesDeletedPods(t *testing.T) { + n := &networkv1beta1.NodeRuntime{ + ObjectMeta: metav1.ObjectMeta{Name: "node1"}, + Spec: networkv1beta1.NodeRuntimeSpec{}, + Status: networkv1beta1.NodeRuntimeStatus{Pods: map[string]*networkv1beta1.RuntimePodStatus{}}, + } + client := fake.NewClientBuilder().WithScheme(types.Scheme). + WithStatusSubresource(n). + WithObjects(n).Build() + r := &CRDV2{ + client: client, + nodeName: "node1", + deletedPods: map[string]*networkv1beta1.RuntimePodStatus{"pod-1": {PodID: "pod-1"}}, + } + + err := r.syncNodeRuntime(context.Background()) + assert.NoError(t, err) + + nodeRuntime := &networkv1beta1.NodeRuntime{} + err = client.Get(context.Background(), pkgclient.ObjectKey{Name: "node1"}, nodeRuntime) + assert.NoError(t, err) + assert.Contains(t, nodeRuntime.Status.Pods, "pod-1") + assert.Contains(t, nodeRuntime.Status.Pods["pod-1"].Status, networkv1beta1.CNIStatusDeleted) +} + +func TestSyncNodeRuntimeClearsDeletedPods(t *testing.T) { + n := &networkv1beta1.NodeRuntime{ + ObjectMeta: metav1.ObjectMeta{Name: "node1"}, + Spec: networkv1beta1.NodeRuntimeSpec{}, + Status: networkv1beta1.NodeRuntimeStatus{Pods: map[string]*networkv1beta1.RuntimePodStatus{}}, + } + + client := fake.NewClientBuilder().WithScheme(types.Scheme).WithStatusSubresource(n).WithObjects(n).Build() + r := &CRDV2{ + client: client, + nodeName: "node1", + deletedPods: map[string]*networkv1beta1.RuntimePodStatus{"pod-1": {PodID: "pod-1"}}, + } + err := r.syncNodeRuntime(context.Background()) + assert.NoError(t, err) + assert.Empty(t, r.deletedPods) +} diff --git a/pkg/eni/remote_test.go b/pkg/eni/remote_test.go index 934b796f..0e6e3aeb 100644 --- a/pkg/eni/remote_test.go +++ b/pkg/eni/remote_test.go @@ -1,46 +1,49 @@ package eni import ( + "context" "net" "testing" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/AliyunContainerService/terway/types" "github.com/AliyunContainerService/terway/types/daemon" - podENITypes "github.com/AliyunContainerService/terway/pkg/apis/network.alibabacloud.com/v1beta1" + networkv1beta1 "github.com/AliyunContainerService/terway/pkg/apis/network.alibabacloud.com/v1beta1" ) func TestToRPC(t *testing.T) { t.Run("test with valid IPv4 and IPv6 allocations", func(t *testing.T) { l := &RemoteIPResource{ - podENI: podENITypes.PodENI{ - Spec: podENITypes.PodENISpec{ - Allocations: []podENITypes.Allocation{ + podENI: networkv1beta1.PodENI{ + Spec: networkv1beta1.PodENISpec{ + Allocations: []networkv1beta1.Allocation{ { IPv4: "192.168.1.1", IPv4CIDR: "192.168.1.0/24", IPv6: "fd00:db8::1", IPv6CIDR: "fd00:db8::/64", - ENI: podENITypes.ENI{ + ENI: networkv1beta1.ENI{ ID: "eni-11", MAC: "00:00:00:00:00:00", }, Interface: "eth0", - ExtraRoutes: []podENITypes.Route{}, + ExtraRoutes: []networkv1beta1.Route{}, DefaultRoute: true, }, }, }, - Status: podENITypes.PodENIStatus{ + Status: networkv1beta1.PodENIStatus{ Phase: "", InstanceID: "i-123456", TrunkENIID: "eni-12345678", Msg: "", PodLastSeen: metav1.Time{}, - ENIInfos: map[string]podENITypes.ENIInfo{ + ENIInfos: map[string]networkv1beta1.ENIInfo{ "eni-11": {}, }, }, @@ -68,3 +71,51 @@ func TestToRPC(t *testing.T) { assert.Equal(t, true, result[0].DefaultRoute) }) } + +func TestAllocateReturnsErrorWhenResourceTypeMismatch(t *testing.T) { + r := &Remote{} + resp, traces := r.Allocate(context.Background(), &daemon.CNI{}, &LocalIPResource{}) + assert.Nil(t, resp) + assert.Equal(t, ResourceTypeMismatch, traces[0].Condition) +} + +func TestAllocateReturnsNetworkResourcesWhenPodENIReady(t *testing.T) { + scheme := runtime.NewScheme() + _ = networkv1beta1.AddToScheme(scheme) + // Build the fake client with scheme and objects + client := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(&networkv1beta1.PodENI{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-1", + Namespace: "default", + }, + Spec: networkv1beta1.PodENISpec{ + Allocations: []networkv1beta1.Allocation{ + { + IPv4: "192.168.1.1", + IPv4CIDR: "192.168.1.0/24", + ENI: networkv1beta1.ENI{ + ID: "eni-1", + MAC: "00:00:00:00:00:00", + }, + Interface: "eth0", + DefaultRoute: true, + }, + }, + }, + Status: networkv1beta1.PodENIStatus{ + Phase: networkv1beta1.ENIPhaseBind, + InstanceID: "i-123456", + }, + }). + Build() + + r := NewRemote(client, nil) + cni := &daemon.CNI{PodNamespace: "default", PodName: "pod-1"} + resp, _ := r.Allocate(context.Background(), cni, &RemoteIPRequest{}) + result := <-resp + assert.NoError(t, result.Err) + assert.NotNil(t, result.NetworkConfigs) + assert.Equal(t, "192.168.1.1", result.NetworkConfigs[0].ToRPC()[0].BasicInfo.PodIP.IPv4) +} diff --git a/pkg/ip/ip_test.go b/pkg/ip/ip_test.go index 9eb494e3..4c6e3951 100644 --- a/pkg/ip/ip_test.go +++ b/pkg/ip/ip_test.go @@ -2,7 +2,10 @@ package ip import ( "net" + "net/netip" "testing" + + "github.com/stretchr/testify/assert" ) func Test_ipIntersect(t *testing.T) { @@ -53,3 +56,23 @@ func Test_ipIntersect(t *testing.T) { }) } } + +func TestIPAddrs2str_MultipleValidIPs_ReturnsCorrectStrings(t *testing.T) { + ip1, _ := netip.ParseAddr("192.0.2.1") + ip2, _ := netip.ParseAddr("192.0.2.2") + input := []netip.Addr{ip1, ip2} + expected := []string{"192.0.2.1", "192.0.2.2"} + result := IPAddrs2str(input) + if len(result) != len(expected) { + t.Errorf("Expected %v, got %v", expected, result) + } + for i := range expected { + if result[i] != expected[i] { + t.Errorf("Expected %v, got %v", expected, result) + } + } +} + +func TestDeriveGatewayIP(t *testing.T) { + assert.Equal(t, "192.168.0.253", DeriveGatewayIP("192.168.0.0/24")) +} diff --git a/pkg/k8s/k8s_test.go b/pkg/k8s/k8s_test.go new file mode 100644 index 00000000..342f4aa2 --- /dev/null +++ b/pkg/k8s/k8s_test.go @@ -0,0 +1,226 @@ +package k8s + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/AliyunContainerService/terway/deviceplugin" + "github.com/AliyunContainerService/terway/types" + "github.com/AliyunContainerService/terway/types/daemon" +) + +func TestGetNodeReturnsNodeWhenExists(t *testing.T) { + client := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(&corev1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node1"}, + }).Build() + node, err := getNode(context.Background(), client, "node1") + assert.NoError(t, err) + assert.NotNil(t, node) + assert.Equal(t, "node1", node.Name) +} + +func TestGetPodReturnsPodWhenExists(t *testing.T) { + client := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(&corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "pod1", Namespace: "default"}, + }).Build() + pod, err := getPod(context.Background(), client, "default", "pod1", true) + assert.NoError(t, err) + assert.NotNil(t, pod) + assert.Equal(t, "pod1", pod.Name) +} + +func TestGetCMReturnsConfigMapWhenExists(t *testing.T) { + client := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: "cm1", Namespace: "default"}, + }).Build() + cm, err := getCM(context.Background(), client, "default", "cm1") + assert.NoError(t, err) + assert.NotNil(t, cm) + assert.Equal(t, "cm1", cm.Name) +} + +func TestConvertPodReturnsPodInfoWithCorrectNetworkType(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "pod1", Namespace: "default"}, + Status: corev1.PodStatus{ + PodIP: "192.168.1.1", + PodIPs: []corev1.PodIP{ + {IP: "192.168.1.1"}, + }, + }, + } + result := convertPod(daemon.ModeENIMultiIP, false, sets.New[string](), pod) + assert.Equal(t, daemon.PodNetworkTypeENIMultiIP, result.PodNetworkType) +} + +func TestConvertPodSetsIngressAndEgressBandwidth(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod1", + Namespace: "default", + Annotations: map[string]string{ + podIngressBandwidth: "1M", + podEgressBandwidth: "2M", + }, + }, + } + result := convertPod(daemon.ModeENIMultiIP, false, sets.New[string](), pod) + assert.Equal(t, uint64(1*MEGABYTE), result.TcIngress) + assert.Equal(t, uint64(2*MEGABYTE), result.TcEgress) +} + +func TestConvertPodHandlesInvalidBandwidthAnnotations(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod1", + Namespace: "default", + Annotations: map[string]string{ + podIngressBandwidth: "invalid", + podEgressBandwidth: "invalid", + }, + }, + } + result := convertPod(daemon.ModeENIMultiIP, false, sets.New[string](), pod) + assert.Equal(t, uint64(0), result.TcIngress) + assert.Equal(t, uint64(0), result.TcEgress) +} + +func TestConvertPodSetsPodENI(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod1", + Namespace: "default", + Annotations: map[string]string{ + types.PodENI: "true", + }, + }, + } + result := convertPod(daemon.ModeENIMultiIP, false, sets.New[string](), pod) + assert.True(t, result.PodENI) +} + +func TestConvertPodHandlesInvalidPodENIAnnotation(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod1", + Namespace: "default", + Annotations: map[string]string{ + types.PodENI: "invalid", + }, + }, + } + result := convertPod(daemon.ModeENIMultiIP, false, sets.New[string](), pod) + assert.False(t, result.PodENI) +} + +func TestConvertPodSetsNetworkPriority(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod1", + Namespace: "default", + Annotations: map[string]string{ + types.NetworkPriority: string(types.NetworkPrioGuaranteed), + }, + }, + } + result := convertPod(daemon.ModeENIMultiIP, false, sets.New[string](), pod) + assert.Equal(t, string(types.NetworkPrioGuaranteed), result.NetworkPriority) +} + +func TestConvertPodHandlesInvalidNetworkPriorityAnnotation(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod1", + Namespace: "default", + Annotations: map[string]string{ + types.NetworkPriority: "invalid", + }, + }, + } + result := convertPod(daemon.ModeENIMultiIP, false, sets.New[string](), pod) + assert.Empty(t, result.NetworkPriority) +} + +func TestConvertPodSetsERdmaWhenEnabled(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{Name: "pod1", Namespace: "default"}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + deviceplugin.ERDMAResName: resource.MustParse("1"), + }, + }, + }, + }, + }, + } + result := convertPod(daemon.ModeENIMultiIP, true, sets.New[string](), pod) + assert.True(t, result.ERdma) +} + +func TestConvertPodSetsIPStickTimeForStatefulWorkload(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod1", + Namespace: "default", + Annotations: map[string]string{ + types.PodIPReservation: "true", + }, + }, + } + result := convertPod(daemon.ModeENIMultiIP, false, sets.New[string](), pod) + assert.Equal(t, defaultStickTimeForSts, result.IPStickTime) +} + +func TestServiceCidrFromAPIServerReturnsErrorWhenConfigMapNotFound(t *testing.T) { + client := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() + _, err := serviceCidrFromAPIServer(client) + assert.Error(t, err) +} + +func TestServiceCidrFromAPIServerReturnsErrorWhenConfigMapDataMissing(t *testing.T) { + client := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: k8sKubeadmConfigmap, Namespace: k8sSystemNamespace}, + }).Build() + _, err := serviceCidrFromAPIServer(client) + assert.Error(t, err) +} + +func TestServiceCidrFromAPIServerReturnsErrorWhenUnmarshalFails(t *testing.T) { + client := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: k8sKubeadmConfigmap, Namespace: k8sSystemNamespace}, + Data: map[string]string{k8sKubeadmConfigmapNetworking: "invalid-yaml"}, + }).Build() + _, err := serviceCidrFromAPIServer(client) + assert.Error(t, err) +} + +func TestServiceCidrFromAPIServerReturnsErrorWhenServiceSubnetNotFound(t *testing.T) { + client := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: k8sKubeadmConfigmap, Namespace: k8sSystemNamespace}, + Data: map[string]string{k8sKubeadmConfigmapNetworking: "networking: {}"}, + }).Build() + _, err := serviceCidrFromAPIServer(client) + assert.Error(t, err) +} + +func TestServiceCidrFromAPIServerReturnsParsedCidr(t *testing.T) { + client := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Name: k8sKubeadmConfigmap, Namespace: k8sSystemNamespace}, + Data: map[string]string{k8sKubeadmConfigmapNetworking: "networking:\n serviceSubnet: 10.96.0.0/12"}, + }).Build() + cidr, err := serviceCidrFromAPIServer(client) + assert.NoError(t, err) + assert.NotNil(t, cidr) + assert.Equal(t, "10.96.0.0/12", cidr.String()) +} diff --git a/types/controlplane/annotations_test.go b/types/controlplane/annotations_test.go new file mode 100644 index 00000000..a17f4fbf --- /dev/null +++ b/types/controlplane/annotations_test.go @@ -0,0 +1,83 @@ +package controlplane + +import ( + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/AliyunContainerService/terway/pkg/apis/network.alibabacloud.com/v1beta1" +) + +func TestParsePodNetworksFromAnnotation(t *testing.T) { + tests := []struct { + name string + podAnnotations map[string]string + expectedResult *PodNetworksAnnotation + expectedError string + }{ + { + name: "AnnotationDoesNotExist", + podAnnotations: map[string]string{}, + expectedResult: &PodNetworksAnnotation{}, + expectedError: "", + }, + { + name: "AnnotationExistsAndValid", + podAnnotations: map[string]string{ + "k8s.aliyun.com/pod-networks": `{ "podNetworks": [{"vSwitchOptions": [ + "vsw-a","vsw-b","vsw-c" + ], "interface": "eth0", + "securityGroupIDs": [ + "sg-1" + ]}]}`, + }, + expectedResult: &PodNetworksAnnotation{ + PodNetworks: []PodNetworks{ + { + Interface: "eth0", + VSwitchOptions: []string{ + "vsw-a", "vsw-b", "vsw-c", + }, + SecurityGroupIDs: []string{ + "sg-1", + }, + }, + }, + }, + expectedError: "", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: test.podAnnotations, + }, + } + + result, err := ParsePodNetworksFromAnnotation(pod) + if test.expectedError != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), test.expectedError) + } else { + assert.NoError(t, err) + assert.Equal(t, test.expectedResult, result) + } + }) + } +} + +func TestReturnsParsedAllocTypeWhenStringIsValidJSON(t *testing.T) { + input := `{"Type": "Fixed", "ReleaseStrategy": "Never", "ReleaseAfter": "5m"}` + expected := &v1beta1.AllocationType{ + Type: v1beta1.IPAllocTypeFixed, + ReleaseStrategy: v1beta1.ReleaseStrategyNever, + ReleaseAfter: "5m", + } + result, err := ParsePodIPType(input) + assert.NoError(t, err) + assert.Equal(t, expected, result) +} diff --git a/types/daemon/config_test.go b/types/daemon/config_test.go index d2b8a6a3..3510206a 100644 --- a/types/daemon/config_test.go +++ b/types/daemon/config_test.go @@ -6,6 +6,8 @@ import ( "testing" "github.com/stretchr/testify/assert" + + "github.com/AliyunContainerService/terway/types" ) func Test_MergeConfigAndUnmarshal(t *testing.T) { @@ -105,3 +107,61 @@ func TestGetAddonSecret(t *testing.T) { assert.Equal(t, "key", ak) assert.Equal(t, "secret", sk) } + +func TestGetVSwitchIDsReturnsAllVSwitchIDs(t *testing.T) { + cfg := &Config{ + VSwitches: map[string][]string{ + "zone-a": {"vsw-1", "vsw-2"}, + "zone-b": {"vsw-3"}, + }, + } + vsws := cfg.GetVSwitchIDs() + assert.ElementsMatch(t, []string{"vsw-1", "vsw-2", "vsw-3"}, vsws) +} + +func TestGetVSwitchIDsReturnsEmptyWhenNoVSwitches(t *testing.T) { + cfg := &Config{ + VSwitches: map[string][]string{}, + } + vsws := cfg.GetVSwitchIDs() + assert.Empty(t, vsws) +} + +func TestGetExtraRoutesReturnsAllRoutes(t *testing.T) { + cfg := &Config{ + VSwitches: map[string][]string{ + "zone-a": {"vsw-1", "vsw-2"}, + "zone-b": {"vsw-3"}, + }, + } + routes := cfg.GetExtraRoutes() + assert.ElementsMatch(t, []string{"vsw-1", "vsw-2", "vsw-3"}, routes) +} + +func TestGetExtraRoutesReturnsEmptyWhenNoRoutes(t *testing.T) { + cfg := &Config{ + VSwitches: map[string][]string{}, + } + routes := cfg.GetExtraRoutes() + assert.Empty(t, routes) +} + +func TestPopulateSetsDefaultValues(t *testing.T) { + cfg := &Config{} + cfg.Populate() + assert.Equal(t, 1.0, cfg.EniCapRatio) + assert.Equal(t, VSwitchSelectionPolicyRandom, cfg.VSwitchSelectionPolicy) + assert.Equal(t, string(types.IPStackIPv4), cfg.IPStack) +} + +func TestPopulateDoesNotOverrideExistingValues(t *testing.T) { + cfg := &Config{ + EniCapRatio: 0.5, + VSwitchSelectionPolicy: "custom", + IPStack: string(types.IPStackDual), + } + cfg.Populate() + assert.Equal(t, 0.5, cfg.EniCapRatio) + assert.Equal(t, "custom", cfg.VSwitchSelectionPolicy) + assert.Equal(t, string(types.IPStackDual), cfg.IPStack) +} diff --git a/types/daemon/dynamicconfig_test.go b/types/daemon/dynamicconfig_test.go new file mode 100644 index 00000000..43259de8 --- /dev/null +++ b/types/daemon/dynamicconfig_test.go @@ -0,0 +1,56 @@ +package daemon + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +func TestConfigFromConfigMapReturnsErrorWhenBaseConfigMapNotFound(t *testing.T) { + client := fake.NewFakeClient() + _, err := ConfigFromConfigMap(context.Background(), client, "") + assert.Error(t, err) +} + +func TestConfigFromConfigMapReturnsErrorWhenBaseConfigIsEmpty(t *testing.T) { + client := fake.NewFakeClient(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eni-config", + Namespace: "kube-system", + }, + Data: map[string]string{"eni_conf": ""}, + }) + _, err := ConfigFromConfigMap(context.Background(), client, "") + assert.Error(t, err) +} + +func TestConfigFromConfigMapReturnsConfigWhenNodeNameIsNotEmpty(t *testing.T) { + client := fake.NewFakeClient( + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eni-config", + Namespace: "kube-system", + }, + Data: map[string]string{"eni_conf": `{"version": "1"}`}, + }, + ) + cfg, err := ConfigFromConfigMap(context.Background(), client, "node-1") + assert.NoError(t, err) + assert.Equal(t, "1", cfg.Version) +} + +func TestConfigFromConfigMapReturnsErrorWhenSecurityGroupsExceedLimit(t *testing.T) { + client := fake.NewFakeClient(&corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "eni-config", + Namespace: "kube-system", + }, + Data: map[string]string{"eni_conf": `{"security_groups": ["sg-1", "sg-2", "sg-3", "sg-4", "sg-5", "sg-6"]}`}, + }) + _, err := ConfigFromConfigMap(context.Background(), client, "") + assert.Error(t, err) +} diff --git a/types/daemon/res_test.go b/types/daemon/res_test.go new file mode 100644 index 00000000..5ce52836 --- /dev/null +++ b/types/daemon/res_test.go @@ -0,0 +1,50 @@ +package daemon + +import ( + "reflect" + "testing" +) + +// TestGetResourceItemByType tests the GetResourceItemByType method of PodResources. +func TestGetResourceItemByType(t *testing.T) { + tests := []struct { + name string + resType string + res []ResourceItem + expected []ResourceItem + }{ + { + name: "MatchingType", + resType: "network", + res: []ResourceItem{ + {Type: "network", ID: "1", ENIID: "eni-1", ENIMAC: "02:12:34:56:78:90", IPv4: "10.0.0.1", IPv6: "2001:0db8:85a3:0000:0000:8a2e:0700:7344"}, + {Type: "storage", ID: "2", ENIID: "eni-2", ENIMAC: "02:12:34:56:78:91", IPv4: "10.0.0.2", IPv6: "2001:0db8:85a3:0000:0000:8a2e:0700:7345"}, + }, + expected: []ResourceItem{ + {Type: "network", ID: "1", ENIID: "eni-1", ENIMAC: "02:12:34:56:78:90", IPv4: "10.0.0.1", IPv6: "2001:0db8:85a3:0000:0000:8a2e:0700:7344"}, + }, + }, + { + name: "MultipleMatchingTypes", + resType: "network", + res: []ResourceItem{ + {Type: "network", ID: "1", ENIID: "eni-1", ENIMAC: "02:12:34:56:78:90", IPv4: "10.0.0.1", IPv6: "2001:0db8:85a3:0000:0000:8a2e:0700:7344"}, + {Type: "network", ID: "2", ENIID: "eni-2", ENIMAC: "02:12:34:56:78:91", IPv4: "10.0.0.2", IPv6: "2001:0db8:85a3:0000:0000:8a2e:0700:7345"}, + }, + expected: []ResourceItem{ + {Type: "network", ID: "1", ENIID: "eni-1", ENIMAC: "02:12:34:56:78:90", IPv4: "10.0.0.1", IPv6: "2001:0db8:85a3:0000:0000:8a2e:0700:7344"}, + {Type: "network", ID: "2", ENIID: "eni-2", ENIMAC: "02:12:34:56:78:91", IPv4: "10.0.0.2", IPv6: "2001:0db8:85a3:0000:0000:8a2e:0700:7345"}, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + podResources := PodResources{Resources: test.res} + result := podResources.GetResourceItemByType(test.resType) + if !reflect.DeepEqual(result, test.expected) { + t.Errorf("GetResourceItemByType(%s) = %v, want %v", test.resType, result, test.expected) + } + }) + } +} diff --git a/types/helper_test.go b/types/helper_test.go new file mode 100644 index 00000000..d3210ca9 --- /dev/null +++ b/types/helper_test.go @@ -0,0 +1,134 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/AliyunContainerService/terway/rpc" +) + +func TestBuildIPNet_EmptyInputs_ReturnsEmptyIPNetSet(t *testing.T) { + ipNetSet, err := BuildIPNet(nil, nil) + assert.NoError(t, err) + assert.NotNil(t, ipNetSet) + assert.Nil(t, ipNetSet.IPv4) + assert.Nil(t, ipNetSet.IPv6) +} + +func TestBuildIPNet_PartiallyEmptyInputs_ReturnsEmptyIPNetSet(t *testing.T) { + ip := &rpc.IPSet{IPv4: "192.168.1.1", IPv6: "2001:db8::1"} + ipNetSet, err := BuildIPNet(ip, nil) + assert.NoError(t, err) + assert.NotNil(t, ipNetSet) + assert.Nil(t, ipNetSet.IPv4) + assert.Nil(t, ipNetSet.IPv6) + + subnet := &rpc.IPSet{IPv4: "192.168.1.0/24", IPv6: "2001:db8::/64"} + ipNetSet, err = BuildIPNet(nil, subnet) + assert.NoError(t, err) + assert.NotNil(t, ipNetSet) + assert.Nil(t, ipNetSet.IPv4) + assert.Nil(t, ipNetSet.IPv6) +} + +func TestBuildIPNet_ValidInputs_ReturnsCorrectIPNetSet(t *testing.T) { + ip := &rpc.IPSet{IPv4: "192.168.1.1", IPv6: "2001:db8::1"} + subnet := &rpc.IPSet{IPv4: "192.168.1.0/24", IPv6: "2001:db8::/64"} + ipNetSet, err := BuildIPNet(ip, subnet) + assert.NoError(t, err) + assert.NotNil(t, ipNetSet) + assert.NotNil(t, ipNetSet.IPv4) + assert.NotNil(t, ipNetSet.IPv6) + assert.Equal(t, "192.168.1.1/24", ipNetSet.IPv4.String()) + assert.Equal(t, "2001:db8::1/64", ipNetSet.IPv6.String()) +} + +func TestBuildIPNet_InvalidIP_ReturnsError(t *testing.T) { + ip := &rpc.IPSet{IPv4: "invalid", IPv6: "2001:db8::1"} + subnet := &rpc.IPSet{IPv4: "192.168.1.0/24", IPv6: "2001:db8::/64"} + ipNetSet, err := BuildIPNet(ip, subnet) + assert.Error(t, err) + assert.Nil(t, ipNetSet) +} + +func TestBuildIPNet_InvalidSubnet_ReturnsError(t *testing.T) { + ip := &rpc.IPSet{IPv4: "192.168.1.1", IPv6: "2001:db8::1"} + subnet := &rpc.IPSet{IPv4: "invalid", IPv6: "2001:db8::/64"} + ipNetSet, err := BuildIPNet(ip, subnet) + assert.Error(t, err) + assert.Nil(t, ipNetSet) +} + +func TestBuildIPNet_OnlyIPv4_ReturnsCorrectIPNetSet(t *testing.T) { + ip := &rpc.IPSet{IPv4: "192.168.1.1"} + subnet := &rpc.IPSet{IPv4: "192.168.1.0/24"} + ipNetSet, err := BuildIPNet(ip, subnet) + assert.NoError(t, err) + assert.NotNil(t, ipNetSet) + assert.NotNil(t, ipNetSet.IPv4) + assert.Nil(t, ipNetSet.IPv6) + assert.Equal(t, "192.168.1.1/24", ipNetSet.IPv4.String()) +} + +func TestBuildIPNet_OnlyIPv6_ReturnsCorrectIPNetSet(t *testing.T) { + ip := &rpc.IPSet{IPv6: "2001:db8::1"} + subnet := &rpc.IPSet{IPv6: "2001:db8::/64"} + ipNetSet, err := BuildIPNet(ip, subnet) + assert.NoError(t, err) + assert.NotNil(t, ipNetSet) + assert.Nil(t, ipNetSet.IPv4) + assert.NotNil(t, ipNetSet.IPv6) + assert.Equal(t, "2001:db8::1/64", ipNetSet.IPv6.String()) +} + +func TestToIPNetSetReturnsErrorWhenIPIsNil(t *testing.T) { + ipNetSet, err := ToIPNetSet(nil) + assert.Error(t, err) + assert.Nil(t, ipNetSet) +} + +func TestToIPNetSetReturnsCorrectIPNetSetWhenValidIPv4(t *testing.T) { + ip := &rpc.IPSet{IPv4: "192.168.1.0/24"} + ipNetSet, err := ToIPNetSet(ip) + assert.NoError(t, err) + assert.NotNil(t, ipNetSet) + assert.NotNil(t, ipNetSet.IPv4) + assert.Nil(t, ipNetSet.IPv6) + assert.Equal(t, "192.168.1.0/24", ipNetSet.IPv4.String()) +} + +func TestToIPNetSetReturnsCorrectIPNetSetWhenValidIPv6(t *testing.T) { + ip := &rpc.IPSet{IPv6: "2001:db8::/64"} + ipNetSet, err := ToIPNetSet(ip) + assert.NoError(t, err) + assert.NotNil(t, ipNetSet) + assert.Nil(t, ipNetSet.IPv4) + assert.NotNil(t, ipNetSet.IPv6) + assert.Equal(t, "2001:db8::/64", ipNetSet.IPv6.String()) +} + +func TestToIPNetSetReturnsCorrectIPNetSetWhenValidIPv4AndIPv6(t *testing.T) { + ip := &rpc.IPSet{IPv4: "192.168.1.0/24", IPv6: "2001:db8::/64"} + ipNetSet, err := ToIPNetSet(ip) + assert.NoError(t, err) + assert.NotNil(t, ipNetSet) + assert.NotNil(t, ipNetSet.IPv4) + assert.NotNil(t, ipNetSet.IPv6) + assert.Equal(t, "192.168.1.0/24", ipNetSet.IPv4.String()) + assert.Equal(t, "2001:db8::/64", ipNetSet.IPv6.String()) +} + +func TestToIPNetSetReturnsErrorWhenInvalidIPv4(t *testing.T) { + ip := &rpc.IPSet{IPv4: "invalid"} + ipNetSet, err := ToIPNetSet(ip) + assert.Error(t, err) + assert.Nil(t, ipNetSet) +} + +func TestToIPNetSetReturnsErrorWhenInvalidIPv6(t *testing.T) { + ip := &rpc.IPSet{IPv6: "invalid"} + ipNetSet, err := ToIPNetSet(ip) + assert.Error(t, err) + assert.Nil(t, ipNetSet) +} diff --git a/types/secret/secret_test.go b/types/secret/secret_test.go new file mode 100644 index 00000000..7a2813af --- /dev/null +++ b/types/secret/secret_test.go @@ -0,0 +1,32 @@ +package secret + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStringReturnsMaskedValue(t *testing.T) { + s := Secret("mysecret") + assert.Equal(t, "******", s.String()) + assert.Equal(t, "mysecret", string((s))) +} + +func TestGoStringReturnsMaskedValue(t *testing.T) { + s := Secret("mysecret") + assert.Equal(t, "******", s.GoString()) +} + +func TestMarshalJSONReturnsMaskedValue(t *testing.T) { + s := Secret("mysecret") + json, err := s.MarshalJSON() + assert.NoError(t, err) + assert.Equal(t, `"******"`, string(json)) +} + +func TestMarshalJSONHandlesEmptySecret(t *testing.T) { + s := Secret("") + json, err := s.MarshalJSON() + assert.NoError(t, err) + assert.Equal(t, `"******"`, string(json)) +}