diff --git a/pkg/coordinatormanager/coordinator_informer.go b/pkg/coordinatormanager/coordinator_informer.go index 9d8fb00ded..f90a38aa50 100644 --- a/pkg/coordinatormanager/coordinator_informer.go +++ b/pkg/coordinatormanager/coordinator_informer.go @@ -8,7 +8,6 @@ import ( "fmt" "net" "reflect" - "regexp" "sort" "strings" "time" @@ -473,7 +472,7 @@ func (cc *CoordinatorController) updatePodAndServerCIDR(ctx context.Context, log if err == nil { logger.Sugar().Info("Trying to fetch the ClusterCIDR from kube-system/kubeadm-config") - k8sPodCIDR, k8sServiceCIDR, err = ExtractK8sCIDRFromKubeadmConfigMap(&cm) + k8sPodCIDR, k8sServiceCIDR, err = utils.ExtractK8sCIDRFromKubeadmConfigMap(&cm) if err == nil { // Success to get ClusterCIDR from kubeadm-config logger.Sugar().Infof("Success get CIDR from kubeadm-config: PodCIDR=%v, ServiceCIDR=%v", k8sPodCIDR, k8sServiceCIDR) @@ -507,7 +506,7 @@ func (cc *CoordinatorController) updatePodAndServerCIDR(ctx context.Context, log return coordCopy } - k8sPodCIDR, k8sServiceCIDR = ExtractK8sCIDRFromKCMPod(&podList.Items[0]) + k8sPodCIDR, k8sServiceCIDR = utils.ExtractK8sCIDRFromKCMPod(&podList.Items[0]) logger.Sugar().Infof("kube-controller-manager k8sPodCIDR %v, k8sServiceCIDR %v", k8sPodCIDR, k8sServiceCIDR) } @@ -778,103 +777,6 @@ func (cc *CoordinatorController) updateServiceCIDR(logger *zap.Logger, coordCopy return nil } -func ExtractK8sCIDRFromKubeadmConfigMap(cm *corev1.ConfigMap) ([]string, []string, error) { - if cm == nil { - return nil, nil, fmt.Errorf("kubeadm configmap is unexpected to nil") - } - var podCIDR, serviceCIDR []string - - clusterConfig, exists := cm.Data["ClusterConfiguration"] - if !exists { - return podCIDR, serviceCIDR, fmt.Errorf("unable to get kubeadm configmap ClusterConfiguration") - } - - podReg := regexp.MustCompile(`podSubnet:\s*(\S+)`) - serviceReg := regexp.MustCompile(`serviceSubnet:\s*(\S+)`) - - podSubnets := podReg.FindStringSubmatch(clusterConfig) - serviceSubnets := serviceReg.FindStringSubmatch(clusterConfig) - - if len(podSubnets) > 1 { - for _, cidr := range strings.Split(podSubnets[1], ",") { - cidr = strings.TrimSpace(cidr) - _, _, err := net.ParseCIDR(cidr) - if err != nil { - continue - } - podCIDR = append(podCIDR, cidr) - } - } - - if len(serviceSubnets) > 1 { - for _, cidr := range strings.Split(serviceSubnets[1], ",") { - cidr = strings.TrimSpace(cidr) - _, _, err := net.ParseCIDR(cidr) - if err != nil { - continue - } - serviceCIDR = append(serviceCIDR, cidr) - } - } - - return podCIDR, serviceCIDR, nil -} - -func ExtractK8sCIDRFromKCMPod(kcm *corev1.Pod) ([]string, []string) { - var podCIDR, serviceCIDR []string - - podReg := regexp.MustCompile(`--cluster-cidr=(.*)`) - serviceReg := regexp.MustCompile(`--service-cluster-ip-range=(.*)`) - - var podSubnets, serviceSubnets []string - findSubnets := func(l string) { - if len(podSubnets) == 0 { - podSubnets = podReg.FindStringSubmatch(l) - } - if len(serviceSubnets) == 0 { - serviceSubnets = serviceReg.FindStringSubmatch(l) - } - } - - for _, l := range kcm.Spec.Containers[0].Command { - findSubnets(l) - if len(podSubnets) != 0 && len(serviceSubnets) != 0 { - break - } - } - - if len(podSubnets) == 0 || len(serviceSubnets) == 0 { - for _, l := range kcm.Spec.Containers[0].Args { - findSubnets(l) - if len(podSubnets) != 0 && len(serviceSubnets) != 0 { - break - } - } - } - - if len(podSubnets) != 0 { - for _, cidr := range strings.Split(podSubnets[1], ",") { - _, _, err := net.ParseCIDR(cidr) - if err != nil { - continue - } - podCIDR = append(podCIDR, cidr) - } - } - - if len(serviceSubnets) != 0 { - for _, cidr := range strings.Split(serviceSubnets[1], ",") { - _, _, err := net.ParseCIDR(cidr) - if err != nil { - continue - } - serviceCIDR = append(serviceCIDR, cidr) - } - } - - return podCIDR, serviceCIDR -} - func fetchType(cniDir string) (string, error) { defaultCniName, err := utils.GetDefaultCniName(cniDir) if err != nil { diff --git a/pkg/coordinatormanager/coordinator_informer_test.go b/pkg/coordinatormanager/coordinator_informer_test.go deleted file mode 100644 index 5dac9062a2..0000000000 --- a/pkg/coordinatormanager/coordinator_informer_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2022 Authors of spidernet-io -// SPDX-License-Identifier: Apache-2.0 - -package coordinatormanager - -import ( - "encoding/json" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" -) - -var _ = Describe("Coordinator Manager", Label("coordinatorinformer", "informer_test"), Serial, func() { - DescribeTable("should extract CIDRs correctly", - func(testName, cmStr string, expectedPodCIDR, expectedServiceCIDR []string, expectError bool) { - var cm corev1.ConfigMap - err := json.Unmarshal([]byte(cmStr), &cm) - Expect(err).NotTo(HaveOccurred(), "Failed to unmarshal configMap: %v\n", err) - - podCIDR, serviceCIDR, err := ExtractK8sCIDRFromKubeadmConfigMap(&cm) - - if expectError { - Expect(err).To(HaveOccurred(), "Expected an error but got none") - } else { - Expect(err).NotTo(HaveOccurred(), "Did not expect an error but got one: %v", err) - } - - Expect(podCIDR).To(Equal(expectedPodCIDR), "Pod CIDR does not match") - Expect(serviceCIDR).To(Equal(expectedServiceCIDR), "Service CIDR does not match") - }, - Entry("ClusterConfiguration", - "ClusterConfiguration", - clusterConfigurationJson, - []string{"192.168.165.0/24"}, - []string{"245.100.128.0/18"}, - false, - ), - Entry("No ClusterConfiguration", - "No ClusterConfiguration", - noClusterConfigurationJson, - nil, - nil, - true, - ), - Entry("No CIDR", - "No CIDR", - noCIDRJson, - nil, - nil, - false, - ), - ) - -}) diff --git a/pkg/coordinatormanager/coordinatormanager_suite_test.go b/pkg/coordinatormanager/coordinatormanager_suite_test.go deleted file mode 100644 index ff9687ab24..0000000000 --- a/pkg/coordinatormanager/coordinatormanager_suite_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022 Authors of spidernet-io -// SPDX-License-Identifier: Apache-2.0 - -package coordinatormanager - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -var ( - clusterConfigurationJson string - noClusterConfigurationJson string - noCIDRJson string -) - -func TestCoordinatorManager(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "CoordinatorManager Suite") -} - -var _ = BeforeSuite(func() { - clusterConfigurationJson = ` - { - "apiVersion": "v1", - "data": { - "ClusterConfiguration": "networking:\n dnsDomain: cluster.local\n podSubnet: 192.168.165.0/24\n serviceSubnet: 245.100.128.0/18" - }, - "kind": "ConfigMap", - "metadata": { - "name": "kubeadm-config", - "namespace": "kube-system" - } - }` - noClusterConfigurationJson = ` - { - "apiVersion": "v1", - "data": { - "ClusterStatus": "apiEndpoints:\n anolios79:\n advertiseAddress: 192.168.165.128\n bindPort: 6443\napiVersion: kubeadm.k8s.io/v1beta2\nkind: ClusterStatus\n" - }, - "kind": "ConfigMap", - "metadata": { - "name": "kubeadm-config", - "namespace": "kube-system" - } - }` - noCIDRJson = ` - { - "apiVersion": "v1", - "data": { - "ClusterConfiguration": "clusterName: spider\ncontrolPlaneEndpoint: spider-control-plane:6443\ncontrollerManager:\n" - }, - "kind": "ConfigMap", - "metadata": { - "name": "kubeadm-config", - "namespace": "kube-system" - } - }` -}) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 1678a7e709..d60e459e99 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -5,10 +5,13 @@ package utils import ( "fmt" + "net" + "regexp" "sort" "strings" "github.com/containernetworking/cni/libcni" + corev1 "k8s.io/api/core/v1" ) // GetDefaultCNIConfPath according to the provided CNI file path (default is /etc/cni/net.d), @@ -68,3 +71,100 @@ func fetchCniNameFromPath(cniPath string) (string, error) { } return conf.Network.Name, nil } + +func ExtractK8sCIDRFromKubeadmConfigMap(cm *corev1.ConfigMap) ([]string, []string, error) { + if cm == nil { + return nil, nil, fmt.Errorf("kubeadm configmap is unexpected to nil") + } + var podCIDR, serviceCIDR []string + + clusterConfig, exists := cm.Data["ClusterConfiguration"] + if !exists { + return podCIDR, serviceCIDR, fmt.Errorf("unable to get kubeadm configmap ClusterConfiguration") + } + + podReg := regexp.MustCompile(`podSubnet:\s*(\S+)`) + serviceReg := regexp.MustCompile(`serviceSubnet:\s*(\S+)`) + + podSubnets := podReg.FindStringSubmatch(clusterConfig) + serviceSubnets := serviceReg.FindStringSubmatch(clusterConfig) + + if len(podSubnets) > 1 { + for _, cidr := range strings.Split(podSubnets[1], ",") { + cidr = strings.TrimSpace(cidr) + _, _, err := net.ParseCIDR(cidr) + if err != nil { + continue + } + podCIDR = append(podCIDR, cidr) + } + } + + if len(serviceSubnets) > 1 { + for _, cidr := range strings.Split(serviceSubnets[1], ",") { + cidr = strings.TrimSpace(cidr) + _, _, err := net.ParseCIDR(cidr) + if err != nil { + continue + } + serviceCIDR = append(serviceCIDR, cidr) + } + } + + return podCIDR, serviceCIDR, nil +} + +func ExtractK8sCIDRFromKCMPod(kcm *corev1.Pod) ([]string, []string) { + var podCIDR, serviceCIDR []string + + podReg := regexp.MustCompile(`--cluster-cidr=(.*)`) + serviceReg := regexp.MustCompile(`--service-cluster-ip-range=(.*)`) + + var podSubnets, serviceSubnets []string + findSubnets := func(l string) { + if len(podSubnets) == 0 { + podSubnets = podReg.FindStringSubmatch(l) + } + if len(serviceSubnets) == 0 { + serviceSubnets = serviceReg.FindStringSubmatch(l) + } + } + + for _, l := range kcm.Spec.Containers[0].Command { + findSubnets(l) + if len(podSubnets) != 0 && len(serviceSubnets) != 0 { + break + } + } + + if len(podSubnets) == 0 || len(serviceSubnets) == 0 { + for _, l := range kcm.Spec.Containers[0].Args { + findSubnets(l) + if len(podSubnets) != 0 && len(serviceSubnets) != 0 { + break + } + } + } + + if len(podSubnets) != 0 { + for _, cidr := range strings.Split(podSubnets[1], ",") { + _, _, err := net.ParseCIDR(cidr) + if err != nil { + continue + } + podCIDR = append(podCIDR, cidr) + } + } + + if len(serviceSubnets) != 0 { + for _, cidr := range strings.Split(serviceSubnets[1], ",") { + _, _, err := net.ParseCIDR(cidr) + if err != nil { + continue + } + serviceCIDR = append(serviceCIDR, cidr) + } + } + + return podCIDR, serviceCIDR +} diff --git a/pkg/utils/utils_suite_test.go b/pkg/utils/utils_suite_test.go new file mode 100644 index 0000000000..44f242e6d6 --- /dev/null +++ b/pkg/utils/utils_suite_test.go @@ -0,0 +1,15 @@ +// Copyright 2024 Authors of spidernet-io +// SPDX-License-Identifier: Apache-2.0 +package utils + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestUtils(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Utils Suite", Label("utils", "unittest")) +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go new file mode 100644 index 0000000000..c15ce5f0a1 --- /dev/null +++ b/pkg/utils/utils_test.go @@ -0,0 +1,204 @@ +// Copyright 2024 Authors of spidernet-io +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "encoding/json" + "os" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" +) + +var _ = Describe("Utils", Label("utils"), Serial, func() { + var clusterConfigurationJson = `{ + "apiVersion": "v1", + "data": { + "ClusterConfiguration": "networking:\n dnsDomain: cluster.local\n podSubnet: 192.168.165.0/24\n serviceSubnet: 245.100.128.0/18" + }, + "kind": "ConfigMap", + "metadata": { + "name": "kubeadm-config", + "namespace": "kube-system" + } +}` + + var noClusterConfigurationJson = `{ + "apiVersion": "v1", + "data": { + "ClusterStatus": "apiEndpoints:\n anolios79:\n advertiseAddress: 192.168.165.128\n bindPort: 6443\napiVersion: kubeadm.k8s.io/v1beta2\nkind: ClusterStatus\n" + }, + "kind": "ConfigMap", + "metadata": { + "name": "kubeadm-config", + "namespace": "kube-system" + } +}` + var noCIDRJson = `{ + "apiVersion": "v1", + "data": { + "ClusterConfiguration": "clusterName: spider\ncontrolPlaneEndpoint: spider-control-plane:6443\ncontrollerManager:\n" + }, + "kind": "ConfigMap", + "metadata": { + "name": "kubeadm-config", + "namespace": "kube-system" + } +}` + DescribeTable("should extract CIDRs correctly", + func(testName, cmStr string, expectedPodCIDR, expectedServiceCIDR []string, expectError bool) { + var cm corev1.ConfigMap + err := json.Unmarshal([]byte(cmStr), &cm) + Expect(err).NotTo(HaveOccurred(), "Failed to unmarshal configMap: %v\n", err) + + podCIDR, serviceCIDR, err := ExtractK8sCIDRFromKubeadmConfigMap(&cm) + + if expectError { + Expect(err).To(HaveOccurred(), "Expected an error but got none") + } else { + Expect(err).NotTo(HaveOccurred(), "Did not expect an error but got one: %v", err) + } + + Expect(podCIDR).To(Equal(expectedPodCIDR), "Pod CIDR does not match") + Expect(serviceCIDR).To(Equal(expectedServiceCIDR), "Service CIDR does not match") + }, + Entry("ClusterConfiguration", + "ClusterConfiguration", + clusterConfigurationJson, + []string{"192.168.165.0/24"}, + []string{"245.100.128.0/18"}, + false, + ), + Entry("No ClusterConfiguration", + "No ClusterConfiguration", + noClusterConfigurationJson, + nil, + nil, + true, + ), + Entry("No CIDR", + "No CIDR", + noCIDRJson, + nil, + nil, + false, + ), + ) + + Context("GetDefaultCniName", func() { + var cniFile string + + It("Test GetDefaultCniName", func() { + cniFile = "10-calico.conflist" + tempDir := "/tmp" + "/" + cniFile + + err := os.WriteFile(tempDir, []byte(`{ + "name": "calico", + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "calico", + "etcd_endpoints": "http://127.0.0.1:2379", + "log_level": "info", + "ipam": { + "type": "calico-ipam" + }, + "policy": { + "type": "k8s" + } + } + ] + }`), 0644) + Expect(err).NotTo(HaveOccurred()) + cniName, err := GetDefaultCniName("/tmp") + Expect(err).NotTo(HaveOccurred()) + + Expect(cniName).To(Equal("calico")) + // Expect(os.RemoveAll(tempCniDir)).NotTo(HaveOccurred()) + }) + }) + + Describe("ExtractK8sCIDRFromKCMPod", func() { + var ( + pod *corev1.Pod + ) + + BeforeEach(func() { + pod = &corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Command: []string{ + "/path/to/kube-controller-manager", + "--cluster-cidr=192.168.0.0/16", + "--service-cluster-ip-range=10.96.0.0/12", + }, + Args: []string{ + "--cluster-cidr=192.168.0.0/16", + "--service-cluster-ip-range=10.96.0.0/12", + }, + }, + }, + }, + } + }) + + Context("when valid CIDR values are provided", func() { + It("should extract pod CIDR and service CIDR correctly", func() { + podCIDR, serviceCIDR := ExtractK8sCIDRFromKCMPod(pod) + Expect(podCIDR).To(ConsistOf("192.168.0.0/16")) + Expect(serviceCIDR).To(ConsistOf("10.96.0.0/12")) + }) + }) + + Context("when no CIDR values are provided", func() { + BeforeEach(func() { + pod.Spec.Containers[0].Command = []string{ + "/path/to/kube-controller-manager", + } + pod.Spec.Containers[0].Args = []string{} + }) + + It("should return empty slices for pod CIDR and service CIDR", func() { + podCIDR, serviceCIDR := ExtractK8sCIDRFromKCMPod(pod) + Expect(podCIDR).To(BeEmpty()) + Expect(serviceCIDR).To(BeEmpty()) + }) + }) + + Context("when invalid CIDR values are provided", func() { + BeforeEach(func() { + pod.Spec.Containers[0].Command = []string{ + "/path/to/kube-controller-manager", + "--cluster-cidr=invalidCIDR", + "--service-cluster-ip-range=alsoInvalidCIDR", + } + }) + + It("should return empty slices for pod CIDR and service CIDR", func() { + podCIDR, serviceCIDR := ExtractK8sCIDRFromKCMPod(pod) + Expect(podCIDR).To(BeEmpty()) + Expect(serviceCIDR).To(BeEmpty()) + }) + }) + + Context("when multiple CIDR values are provided", func() { + BeforeEach(func() { + pod.Spec.Containers[0].Command = []string{ + "/path/to/kube-controller-manager", + "--cluster-cidr=192.168.0.0/16,192.168.1.0/24", + "--service-cluster-ip-range=10.96.0.0/12,10.97.0.0/16", + } + }) + + It("should extract all valid pod CIDR and service CIDR", func() { + podCIDR, serviceCIDR := ExtractK8sCIDRFromKCMPod(pod) + Expect(podCIDR).To(ConsistOf("192.168.0.0/16", "192.168.1.0/24")) + Expect(serviceCIDR).To(ConsistOf("10.96.0.0/12", "10.97.0.0/16")) + }) + }) + }) + +}) diff --git a/test/e2e/spidercoordinator/spidercoordinator_test.go b/test/e2e/spidercoordinator/spidercoordinator_test.go index 473bd72500..ab892c7956 100644 --- a/test/e2e/spidercoordinator/spidercoordinator_test.go +++ b/test/e2e/spidercoordinator/spidercoordinator_test.go @@ -19,6 +19,7 @@ import ( "github.com/spidernet-io/spiderpool/pkg/coordinatormanager" "github.com/spidernet-io/spiderpool/pkg/ip" spiderpoolv2beta1 "github.com/spidernet-io/spiderpool/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1" + "github.com/spidernet-io/spiderpool/pkg/utils" "github.com/spidernet-io/spiderpool/test/e2e/common" ) @@ -464,7 +465,7 @@ var _ = Describe("SpiderCoordinator", Label("spidercoordinator", "overlay"), Ser It("Prioritize getting ClusterCIDR from kubeadm-config", Label("V00009"), func() { GinkgoWriter.Printf("podCIDR and serviceCIDR from spidercoordinator: %v,%v\n", spc.Status.OverlayPodCIDR, spc.Status.ServiceCIDR) - podCIDR, serviceCIDr, err := coordinatormanager.ExtractK8sCIDRFromKubeadmConfigMap(cm) + podCIDR, serviceCIDr, err := utils.ExtractK8sCIDRFromKubeadmConfigMap(cm) Expect(err).NotTo(HaveOccurred(), "Failed to extract k8s CIDR from Kubeadm configMap, error is %v", err) GinkgoWriter.Printf("podCIDR and serviceCIDR from kubeadm-config : %v,%v\n", podCIDR, serviceCIDr) @@ -500,7 +501,7 @@ var _ = Describe("SpiderCoordinator", Label("spidercoordinator", "overlay"), Ser allPods, err := frame.GetPodList(client.MatchingLabels{"component": "kube-controller-manager"}) Expect(err).NotTo(HaveOccurred()) - kcmPodCIDR, kcmServiceCIDR := coordinatormanager.ExtractK8sCIDRFromKCMPod(&allPods.Items[0]) + kcmPodCIDR, kcmServiceCIDR := utils.ExtractK8sCIDRFromKCMPod(&allPods.Items[0]) GinkgoWriter.Printf("podCIDR and serviceCIDR from kube-controller-manager pod : %v,%v\n", kcmPodCIDR, kcmServiceCIDR) Eventually(func() bool {