github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/apis/apps/v1alpha1/cluster_webhook_test.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package v1alpha1 18 19 import ( 20 "context" 21 "fmt" 22 23 . "github.com/onsi/ginkgo/v2" 24 . "github.com/onsi/gomega" 25 26 corev1 "k8s.io/api/core/v1" 27 apierrors "k8s.io/apimachinery/pkg/api/errors" 28 "k8s.io/apimachinery/pkg/api/resource" 29 "k8s.io/apimachinery/pkg/util/yaml" 30 "sigs.k8s.io/controller-runtime/pkg/client" 31 ) 32 33 var _ = Describe("cluster webhook", func() { 34 var ( 35 randomStr string 36 clusterName string 37 clusterDefinitionName string 38 secondClusterDefinition string 39 clusterVersionName string 40 ) 41 42 initParams := func() { 43 randomStr = testCtx.GetRandomStr() 44 clusterName = "cluster-webhook-mysql-" + randomStr 45 clusterDefinitionName = "cluster-webhook-mysql-definition-" + randomStr 46 secondClusterDefinition = "cluster-webhook-mysql-definition2-" + randomStr 47 clusterVersionName = "cluster-webhook-mysql-clusterversion-" + randomStr 48 } 49 cleanupObjects := func() { 50 // Add any setup steps that needs to be executed before each test 51 err := k8sClient.DeleteAllOf(ctx, &Cluster{}, client.InNamespace(testCtx.DefaultNamespace), client.HasLabels{testCtx.TestObjLabelKey}) 52 Expect(err).NotTo(HaveOccurred()) 53 err = k8sClient.DeleteAllOf(ctx, &ClusterVersion{}, client.HasLabels{testCtx.TestObjLabelKey}) 54 Expect(err).NotTo(HaveOccurred()) 55 err = k8sClient.DeleteAllOf(ctx, &ClusterDefinition{}, client.HasLabels{testCtx.TestObjLabelKey}) 56 Expect(err).NotTo(HaveOccurred()) 57 } 58 BeforeEach(func() { 59 initParams() 60 cleanupObjects() 61 }) 62 63 AfterEach(func() { 64 cleanupObjects() 65 }) 66 67 Context("When cluster create and update", func() { 68 It("Should webhook validate passed", func() { 69 By("By testing creating a new clusterDefinition when no clusterVersion and clusterDefinition") 70 cluster, _ := createTestCluster(clusterDefinitionName, clusterVersionName, clusterName) 71 Expect(testCtx.CreateObj(ctx, cluster).Error()).To(ContainSubstring("not found")) 72 73 By("By creating a new clusterDefinition") 74 clusterDef, _ := createTestClusterDefinitionObj(clusterDefinitionName) 75 Expect(testCtx.CreateObj(ctx, clusterDef)).Should(Succeed()) 76 77 clusterDefSecond, _ := createTestClusterDefinitionObj(secondClusterDefinition) 78 Expect(testCtx.CreateObj(ctx, clusterDefSecond)).Should(Succeed()) 79 80 // wait until ClusterDefinition created 81 Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: clusterDefinitionName}, clusterDef)).Should(Succeed()) 82 83 By("By creating a new clusterVersion") 84 clusterVersion := createTestClusterVersionObj(clusterDefinitionName, clusterVersionName) 85 Expect(testCtx.CreateObj(ctx, clusterVersion)).Should(Succeed()) 86 // wait until ClusterVersion created 87 Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: clusterVersionName}, clusterVersion)).Should(Succeed()) 88 89 By("By creating a new Cluster") 90 cluster, _ = createTestCluster(clusterDefinitionName, clusterVersionName, clusterName) 91 Expect(testCtx.CreateObj(ctx, cluster)).Should(Succeed()) 92 93 By("By testing update spec.clusterDefinitionRef") 94 patch := client.MergeFrom(cluster.DeepCopy()) 95 cluster.Spec.ClusterDefRef = secondClusterDefinition 96 Expect(k8sClient.Patch(ctx, cluster, patch).Error()).To(ContainSubstring("spec.clusterDefinitionRef")) 97 // restore 98 cluster.Spec.ClusterDefRef = clusterDefinitionName 99 100 By("By testing spec.components[?].type not found in clusterDefinitionRef") 101 patch = client.MergeFrom(cluster.DeepCopy()) 102 cluster.Spec.ComponentSpecs[0].ComponentDefRef = "replicaset" 103 Expect(k8sClient.Patch(ctx, cluster, patch).Error()).To(ContainSubstring("componentDefRef is immutable")) 104 // restore 105 cluster.Spec.ComponentSpecs[0].ComponentDefRef = "replicasets" 106 107 // restore 108 cluster.Spec.ComponentSpecs[0].Name = "replicasets" 109 110 By("By updating spec.components[?].volumeClaimTemplates storage size, expect succeed") 111 patch = client.MergeFrom(cluster.DeepCopy()) 112 cluster.Spec.ComponentSpecs[0].VolumeClaimTemplates[0].Spec.Resources.Requests[corev1.ResourceStorage] = resource.MustParse("2Gi") 113 Expect(k8sClient.Patch(ctx, cluster, patch)).Should(Succeed()) 114 115 By("By updating spec.components[?].volumeClaimTemplates[?].name, expect not succeed") 116 patch = client.MergeFrom(cluster.DeepCopy()) 117 cluster.Spec.ComponentSpecs[0].VolumeClaimTemplates[0].Name = "test" 118 Expect(k8sClient.Patch(ctx, cluster, patch).Error()).To(ContainSubstring("volumeClaimTemplates is forbidden modification except for storage size.")) 119 120 By("By updating component resources") 121 // restore test volume claim template name to data 122 patch = client.MergeFrom(cluster.DeepCopy()) 123 cluster.Spec.ComponentSpecs[0].VolumeClaimTemplates[0].Name = "data" 124 cluster.Spec.ComponentSpecs[0].Resources = corev1.ResourceRequirements{ 125 Requests: corev1.ResourceList{ 126 "cpu": resource.MustParse("100m"), 127 "memory": resource.MustParse("200Mi"), 128 }, 129 } 130 Expect(k8sClient.Patch(ctx, cluster, patch)).Should(Succeed()) 131 patch = client.MergeFrom(cluster.DeepCopy()) 132 cluster.Spec.ComponentSpecs[0].Resources = corev1.ResourceRequirements{ 133 Requests: corev1.ResourceList{ 134 "cpu": resource.MustParse("100m"), 135 "memory1": resource.MustParse("200Mi"), 136 }, 137 } 138 Expect(k8sClient.Patch(ctx, cluster, patch).Error()).To(ContainSubstring("resource key is not cpu or memory or hugepages- ")) 139 patch = client.MergeFrom(cluster.DeepCopy()) 140 cluster.Spec.ComponentSpecs[0].Resources = corev1.ResourceRequirements{ 141 Requests: corev1.ResourceList{ 142 "cpu": resource.MustParse("100m"), 143 "memory": resource.MustParse("200Mi"), 144 }, 145 Limits: corev1.ResourceList{ 146 "cpu": resource.MustParse("100m"), 147 "memory": resource.MustParse("100Mi"), 148 }, 149 } 150 Expect(k8sClient.Patch(ctx, cluster, patch).Error()).To(ContainSubstring("must be less than or equal to memory limit")) 151 patch = client.MergeFrom(cluster.DeepCopy()) 152 cluster.Spec.ComponentSpecs[0].Resources.Requests[corev1.ResourceMemory] = resource.MustParse("80Mi") 153 Expect(k8sClient.Patch(ctx, cluster, patch)).Should(Succeed()) 154 }) 155 }) 156 157 Context("tls validation", func() { 158 BeforeEach(func() { 159 By("By creating a new clusterDefinition") 160 clusterDef, _ := createTestClusterDefinitionObj(clusterDefinitionName) 161 Expect(testCtx.CreateObj(ctx, clusterDef)).Should(Succeed()) 162 163 // wait until ClusterDefinition created 164 Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: clusterDefinitionName}, clusterDef)).Should(Succeed()) 165 166 By("By creating a new clusterVersion") 167 clusterVersion := createTestClusterVersionObj(clusterDefinitionName, clusterVersionName) 168 Expect(testCtx.CreateObj(ctx, clusterVersion)).Should(Succeed()) 169 // wait until ClusterVersion created 170 Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: clusterVersionName}, clusterVersion)).Should(Succeed()) 171 }) 172 It("should assure tls fields setting properly", func() { 173 By("creating cluster with nil issuer") 174 cluster, _ := createTestCluster(clusterDefinitionName, clusterVersionName, clusterName) 175 cluster.Spec.ComponentSpecs[0].TLS = true 176 Expect(testCtx.CreateObj(ctx, cluster)).ShouldNot(Succeed()) 177 178 By("creating cluster with nil secret ref") 179 cluster.Spec.ComponentSpecs[0].Issuer = &Issuer{Name: IssuerUserProvided} 180 Expect(testCtx.CreateObj(ctx, cluster)).ShouldNot(Succeed()) 181 182 By("creating cluster with KubeBlocks issuer") 183 cluster.Spec.ComponentSpecs[0].Issuer = &Issuer{Name: IssuerKubeBlocks} 184 Expect(testCtx.CreateObj(ctx, cluster)).Should(Succeed()) 185 186 By("creating cluster with UserProvided issuer and secret ref provided") 187 Expect(k8sClient.Delete(ctx, cluster)).Should(Succeed()) 188 err := k8sClient.Get(ctx, client.ObjectKeyFromObject(cluster), &Cluster{}) 189 Expect(apierrors.IsNotFound(err)).Should(BeTrue()) 190 cluster, _ = createTestCluster(clusterDefinitionName, clusterVersionName, clusterName) 191 cluster.Spec.ComponentSpecs[0].TLS = true 192 cluster.Spec.ComponentSpecs[0].Issuer = &Issuer{ 193 Name: IssuerUserProvided, 194 SecretRef: &TLSSecretRef{ 195 Name: "test-tls-secret", 196 CA: "ca.crt", 197 Cert: "cert.crt", 198 Key: "key.crt", 199 }, 200 } 201 Expect(testCtx.CreateObj(ctx, cluster)).Should(Succeed()) 202 }) 203 }) 204 }) 205 206 func createTestCluster(clusterDefinitionName, clusterVersionName, clusterName string) (*Cluster, error) { 207 clusterYaml := fmt.Sprintf(` 208 apiVersion: apps.kubeblocks.io/v1alpha1 209 kind: Cluster 210 metadata: 211 name: %s 212 namespace: default 213 spec: 214 clusterDefinitionRef: %s 215 clusterVersionRef: %s 216 componentSpecs: 217 - name: replicasets 218 componentDefRef: replicasets 219 replicas: 1 220 volumeClaimTemplates: 221 - name: data 222 spec: 223 storageClassName: standard 224 resources: 225 requests: 226 storage: 1Gi 227 - name: proxy 228 componentDefRef: proxy 229 replicas: 1 230 `, clusterName, clusterDefinitionName, clusterVersionName) 231 cluster := &Cluster{} 232 err := yaml.Unmarshal([]byte(clusterYaml), cluster) 233 cluster.Spec.TerminationPolicy = WipeOut 234 return cluster, err 235 }