diff --git a/api/v1alpha1/constants.go b/api/v1alpha1/constants.go index 482f56c5..0bc8f2af 100644 --- a/api/v1alpha1/constants.go +++ b/api/v1alpha1/constants.go @@ -36,18 +36,18 @@ const ( AkoPackageInstallName = "load-balancer-and-ingress-service" AkoPreferredIPAnnotation = "ako.vmware.com/load-balancer-ip" - AviClusterLabel = "networking.tkg.tanzu.vmware.com/avi" - AviClusterDeleteConfigLabel = "networking.tkg.tanzu.vmware.com/avi-config-delete" - AviClusterSecretType = "avi.cluster.x-k8s.io/secret" - AviNamespace = "avi-system" - AviCredentialName = "avi-controller-credentials" - AviCAName = "avi-controller-ca" - AviCertificateKey = "certificateAuthorityData" - AviResourceCleanupReason = "AviResourceCleanup" - AviResourceCleanupSucceededCondition clusterv1.ConditionType = "AviResourceCleanupSucceeded" - AviUserCleanupSucceededCondition clusterv1.ConditionType = "AviUserCleanupSucceeded" - AKOIpFamilyValidationSucceededCondition clusterv1.ConditionType = "AKOIpFamilyValidationSucceeded" - PreTerminateAnnotation = clusterv1.PreTerminateDeleteHookAnnotationPrefix + "/avi-cleanup" + AviClusterLabel = "networking.tkg.tanzu.vmware.com/avi" + AviClusterDeleteConfigLabel = "networking.tkg.tanzu.vmware.com/avi-config-delete" + AviClusterSecretType = "avi.cluster.x-k8s.io/secret" + AviNamespace = "avi-system" + AviCredentialName = "avi-controller-credentials" + AviCAName = "avi-controller-ca" + AviCertificateKey = "certificateAuthorityData" + AviResourceCleanupReason = "AviResourceCleanup" + AviResourceCleanupSucceededCondition clusterv1.ConditionType = "AviResourceCleanupSucceeded" + AviUserCleanupSucceededCondition clusterv1.ConditionType = "AviUserCleanupSucceeded" + ClusterIpFamilyValidationSucceededCondition clusterv1.ConditionType = "ClusterIpFamilyValidationSucceeded" + PreTerminateAnnotation = clusterv1.PreTerminateDeleteHookAnnotationPrefix + "/avi-cleanup" HAServiceName = "control-plane" HAServiceBootstrapClusterFinalizer = "ako-operator.networking.tkg.tanzu.vmware.com/ha" diff --git a/controllers/akodeploymentconfig/cluster/cluster_controller_addon_secret.go b/controllers/akodeploymentconfig/cluster/cluster_controller_addon_secret.go index 1e469c0a..4d38bf7d 100644 --- a/controllers/akodeploymentconfig/cluster/cluster_controller_addon_secret.go +++ b/controllers/akodeploymentconfig/cluster/cluster_controller_addon_secret.go @@ -27,6 +27,13 @@ import ( runv1alpha3 "github.com/vmware-tanzu/tanzu-framework/apis/run/v1alpha3" ) +const ( + IPv4IpFamily = "V4" + IPv6IpFamily = "V6" + DualStackIPv6Primary = "V6,V4" + DualStackIPv4Primary = "V4,V6" +) + func (r *ClusterReconciler) ReconcileAddonSecret( ctx context.Context, log logr.Logger, @@ -59,13 +66,13 @@ func (r *ClusterReconciler) ReconcileAddonSecret( } } - //Stop reconciling if AKO ip family doesn't match cluster ip family - if err = ValidateADCAndClusterIpFamily(cluster, obj, isVIPProvider, log); err != nil { - errInfo := "Selected AKODeploymentConfig " + obj.Name + "'s IP family doesn't match cluster " + cluster.Namespace + - "-" + cluster.Name + "'s ip family, stop deploying AKO into cluster " + cluster.Namespace + "-" + cluster.Name + //Stop reconciling if cluster ip family doesn't satisfy the condition + if err = ValidateClusterIpFamily(cluster, obj, isVIPProvider, log); err != nil { + errInfo := "cluster " + cluster.Namespace + + "-" + cluster.Name + "'s ip family is not allowed, stop deploying AKO into cluster " + cluster.Namespace + "-" + cluster.Name log.Error(err, errInfo) clusterCondition := &clusterv1.Condition{ - Type: akoov1alpha1.AKOIpFamilyValidationSucceededCondition, + Type: akoov1alpha1.ClusterIpFamilyValidationSucceededCondition, Status: corev1.ConditionFalse, Message: errInfo, } @@ -358,30 +365,32 @@ func getAKOPackageRefFromClusterBootstrap(log logr.Logger, cb *runv1alpha3.Clust return -1, nil } -func ValidateADCAndClusterIpFamily(cluster *clusterv1.Cluster, adc *akoov1alpha1.AKODeploymentConfig, isVIPProvider bool, log logr.Logger) error { - adcIpFamily := "V4" - if adc.Spec.ExtraConfigs.IpFamily != "" { - adcIpFamily = adc.Spec.ExtraConfigs.IpFamily - } +func ValidateClusterIpFamily(cluster *clusterv1.Cluster, adc *akoov1alpha1.AKODeploymentConfig, isVIPProvider bool, log logr.Logger) error { clusterIpFamily, err := utils.GetClusterIPFamily(cluster) if err != nil { log.Error(err, "can't get cluster ip family") return err } + // AKO limitations: AKO doesn't work in IPv6 single-stack and IPv6 Primary dual-stack cluster + if clusterIpFamily == IPv6IpFamily || clusterIpFamily == DualStackIPv6Primary { + return errors.New("AKO doesn't work in IPv6 single-stack and IPv6 Primary dual-stack cluster") + } + + adcIpFamily := IPv4IpFamily + if adc.Spec.ExtraConfigs.IpFamily != "" { + adcIpFamily = adc.Spec.ExtraConfigs.IpFamily + } + // AKO limitations: AKO can't configure backend pool ip family // TODO:(chenlin) Remove validation after AKO supports configurable ip pool - if (adcIpFamily == "V4" && clusterIpFamily == "V6") || (adcIpFamily == "V6" && clusterIpFamily == "V4") { + if adcIpFamily == IPv6IpFamily && clusterIpFamily == IPv4IpFamily { errInfo := "AKO with IP family " + adcIpFamily + " can not work together with cluster with IP family " + clusterIpFamily return errors.New(errInfo) } // When enable avi as control plane ha, backend server shouldn't use secondary ip type // TODO:(chenlin) Remove validation after AKO supports configurable ip pool - if isVIPProvider { - if adcIpFamily == "V4" && clusterIpFamily == "V6,V4" { - return errors.New("When enabling avi as control plane HA, AKO with IP family V4 can not work together with ipv6 primary dual-stack cluster") - } else if adcIpFamily == "V6" && clusterIpFamily == "V4,V6" { - return errors.New("When enabling avi as control plane HA, AKO with IP family V6 can not work together with ipv4 primary dual-stack cluster") - } + if isVIPProvider && adcIpFamily == IPv6IpFamily && clusterIpFamily == DualStackIPv4Primary { + return errors.New("When enabling avi as control plane HA, AKO with IP family V6 can not work together with ipv4 primary dual-stack cluster") } return nil } diff --git a/controllers/akodeploymentconfig/cluster/cluster_controller_unit_test.go b/controllers/akodeploymentconfig/cluster/cluster_controller_unit_test.go index 8d1aa4cc..58e0eabd 100644 --- a/controllers/akodeploymentconfig/cluster/cluster_controller_unit_test.go +++ b/controllers/akodeploymentconfig/cluster/cluster_controller_unit_test.go @@ -258,7 +258,123 @@ func unitTestAKODeploymentYaml() { }) } -func unitTestValidateADCAndClusterIpFamily() { +func unitTestValidateClusterIpFamily() { + Context("Validate ipv6 cluster ip family", func() { + var ( + akoDeploymentConfig *akoov1alpha1.AKODeploymentConfig + capiCluster *clusterv1.Cluster + logger logr.Logger + isVIPProvider bool + ) + + BeforeEach(func() { + log.SetLogger(zap.New()) + logger = log.Log + }) + + When("cluster is valid", func() { + BeforeEach(func() { + akoDeploymentConfig = &akoov1alpha1.AKODeploymentConfig{ + Spec: akoov1alpha1.AKODeploymentConfigSpec{ + ExtraConfigs: akoov1alpha1.ExtraConfigs{ + IpFamily: "V4", + }, + }, + } + + capiCluster = &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + Namespace: "default", + }, + Spec: clusterv1.ClusterSpec{ + ClusterNetwork: &clusterv1.ClusterNetwork{ + Pods: &clusterv1.NetworkRanges{ + CIDRBlocks: []string{"192.168.0.0/16"}, + }, + Services: &clusterv1.NetworkRanges{ + CIDRBlocks: []string{"192.168.0.0/16"}, + }, + }, + }, + } + }) + + It("should return no error", func() { + err := cluster.ValidateClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) + Expect(err).ShouldNot(HaveOccurred()) + }) + }) + + When("cluster is invalid single-stack IPv6", func() { + BeforeEach(func() { + akoDeploymentConfig = &akoov1alpha1.AKODeploymentConfig{ + Spec: akoov1alpha1.AKODeploymentConfigSpec{ + ExtraConfigs: akoov1alpha1.ExtraConfigs{ + IpFamily: "V4", + }, + }, + } + + capiCluster = &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + Namespace: "default", + }, + Spec: clusterv1.ClusterSpec{ + ClusterNetwork: &clusterv1.ClusterNetwork{ + Pods: &clusterv1.NetworkRanges{ + CIDRBlocks: []string{"2002::1234:abcd:ffff:c0a8:101/64"}, + }, + Services: &clusterv1.NetworkRanges{ + CIDRBlocks: []string{"2002::1234:abcd:ffff:c0a8:101/64"}, + }, + }, + }, + } + }) + + It("should return no error", func() { + err := cluster.ValidateClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) + Expect(err).Should(HaveOccurred()) + }) + }) + + When("cluster is invalid dual-stack IPv6 Primary", func() { + BeforeEach(func() { + akoDeploymentConfig = &akoov1alpha1.AKODeploymentConfig{ + Spec: akoov1alpha1.AKODeploymentConfigSpec{ + ExtraConfigs: akoov1alpha1.ExtraConfigs{ + IpFamily: "V4", + }, + }, + } + + capiCluster = &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + Namespace: "default", + }, + Spec: clusterv1.ClusterSpec{ + ClusterNetwork: &clusterv1.ClusterNetwork{ + Pods: &clusterv1.NetworkRanges{ + CIDRBlocks: []string{"2002::1234:abcd:ffff:c0a8:101/64, 192.168.0.0/16"}, + }, + Services: &clusterv1.NetworkRanges{ + CIDRBlocks: []string{"2002::1234:abcd:ffff:c0a8:101/64, 192.168.0.0/16"}, + }, + }, + }, + } + }) + + It("should return no error", func() { + err := cluster.ValidateClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) + Expect(err).Should(HaveOccurred()) + }) + }) + }) + Context("Validate AKODeploymentConfig ip family and cluster ip family", func() { var ( akoDeploymentConfig *akoov1alpha1.AKODeploymentConfig @@ -335,7 +451,7 @@ func unitTestValidateADCAndClusterIpFamily() { }) It("should return no error", func() { - err := cluster.ValidateADCAndClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) + err := cluster.ValidateClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) Expect(err).ShouldNot(HaveOccurred()) }) }) @@ -383,7 +499,7 @@ func unitTestValidateADCAndClusterIpFamily() { }) It("should return error since cluster ip family is invalid", func() { - err := cluster.ValidateADCAndClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) + err := cluster.ValidateClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) Expect(err).Should(HaveOccurred()) }) }) @@ -428,7 +544,7 @@ func unitTestValidateADCAndClusterIpFamily() { }) It("should return error since the ipfamily combination is not supported", func() { - err := cluster.ValidateADCAndClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) + err := cluster.ValidateClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) Expect(err).Should(HaveOccurred()) }) }) @@ -472,7 +588,7 @@ func unitTestValidateADCAndClusterIpFamily() { }) It("should return error since the ipfamily combination is not supported", func() { - err := cluster.ValidateADCAndClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) + err := cluster.ValidateClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) Expect(err).Should(HaveOccurred()) }) }) @@ -519,7 +635,7 @@ func unitTestValidateADCAndClusterIpFamily() { }) It("should return error since the ipfamily combination is not supported", func() { - err := cluster.ValidateADCAndClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) + err := cluster.ValidateClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) Expect(err).Should(HaveOccurred()) }) }) @@ -563,7 +679,7 @@ func unitTestValidateADCAndClusterIpFamily() { }) It("should return error since the ipfamily combination is not supported", func() { - err := cluster.ValidateADCAndClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) + err := cluster.ValidateClusterIpFamily(capiCluster, akoDeploymentConfig, isVIPProvider, logger) Expect(err).Should(HaveOccurred()) }) }) diff --git a/controllers/akodeploymentconfig/cluster/suite_test.go b/controllers/akodeploymentconfig/cluster/suite_test.go index 6122a96b..7aeb810f 100644 --- a/controllers/akodeploymentconfig/cluster/suite_test.go +++ b/controllers/akodeploymentconfig/cluster/suite_test.go @@ -36,5 +36,5 @@ func intgTests() { func unitTests() { Describe("AKO Deployment Spec generation", unitTestAKODeploymentYaml) - Describe("AKODeploymentConfig and cluster ip family Validation", unitTestValidateADCAndClusterIpFamily) + Describe("Cluster ip family Validation", unitTestValidateClusterIpFamily) }