github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/test/integration/redis_hscale_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 appstest 18 19 import ( 20 "fmt" 21 "strconv" 22 "strings" 23 24 . "github.com/onsi/ginkgo/v2" 25 . "github.com/onsi/gomega" 26 27 corev1 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/types" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 31 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 32 "github.com/1aal/kubeblocks/pkg/common" 33 "github.com/1aal/kubeblocks/pkg/constant" 34 intctrlutil "github.com/1aal/kubeblocks/pkg/generics" 35 testapps "github.com/1aal/kubeblocks/pkg/testutil/apps" 36 testk8s "github.com/1aal/kubeblocks/pkg/testutil/k8s" 37 ) 38 39 var _ = Describe("Redis Horizontal Scale function", func() { 40 41 const clusterDefName = "test-clusterdef" 42 const clusterVersionName = "test-clusterversion" 43 const clusterNamePrefix = "test-cluster" 44 45 const scriptConfigName = "test-cluster-redis-scripts" 46 const primaryConfigName = "redis-primary-config" 47 const secondaryConfigName = "redis-secondary-config" 48 49 const replicas = 3 50 51 // Cleanups 52 53 cleanEnv := func() { 54 // must wait until resources deleted and no longer exist before the testcases start, 55 // otherwise if later it needs to create some new resource objects with the same name, 56 // in race conditions, it will find the existence of old objects, resulting failure to 57 // create the new objects. 58 By("clean resources") 59 60 // delete cluster(and all dependent sub-resources), clusterversion and clusterdef 61 testapps.ClearClusterResources(&testCtx) 62 63 // delete rest configurations 64 inNS := client.InNamespace(testCtx.DefaultNamespace) 65 ml := client.HasLabels{testCtx.TestObjLabelKey} 66 // namespaced 67 testapps.ClearResources(&testCtx, intctrlutil.ConfigMapSignature, inNS, ml) 68 // non-namespaced 69 testapps.ClearResources(&testCtx, intctrlutil.ConfigConstraintSignature, ml) 70 testapps.ClearResources(&testCtx, intctrlutil.BackupPolicyTemplateSignature, ml) 71 72 } 73 74 BeforeEach(cleanEnv) 75 76 AfterEach(cleanEnv) 77 78 // Testcases 79 80 var ( 81 clusterDefObj *appsv1alpha1.ClusterDefinition 82 clusterVersionObj *appsv1alpha1.ClusterVersion 83 clusterObj *appsv1alpha1.Cluster 84 clusterKey types.NamespacedName 85 ) 86 87 testReplicationRedisHorizontalScale := func() { 88 89 By("Mock a cluster obj with replication workloadType.") 90 pvcSpec := testapps.NewPVCSpec("1Gi") 91 clusterObj = testapps.NewClusterFactory(testCtx.DefaultNamespace, clusterNamePrefix, 92 clusterDefObj.Name, clusterVersionObj.Name).WithRandomName(). 93 AddComponent(testapps.DefaultRedisCompSpecName, testapps.DefaultRedisCompDefName). 94 SetReplicas(replicas).AddVolumeClaimTemplate(testapps.DataVolumeName, pvcSpec). 95 Create(&testCtx).GetObject() 96 clusterKey = client.ObjectKeyFromObject(clusterObj) 97 98 By("Waiting for cluster creation") 99 Eventually(testapps.GetClusterObservedGeneration(&testCtx, clusterKey)).Should(BeEquivalentTo(1)) 100 101 By("Waiting for the cluster to be running") 102 Eventually(testapps.GetClusterPhase(&testCtx, clusterKey)).Should(Equal(appsv1alpha1.RunningClusterPhase)) 103 104 By("Checking statefulSet number") 105 stsList := testk8s.ListAndCheckStatefulSet(&testCtx, clusterKey) 106 Expect(len(stsList.Items)).Should(BeEquivalentTo(1)) 107 108 By("Checking pods number and role label in StatefulSet") 109 podList, err := common.GetPodListByStatefulSet(ctx, k8sClient, &stsList.Items[0]) 110 Expect(err).To(Succeed()) 111 Expect(len(podList)).Should(BeEquivalentTo(replicas)) 112 for _, pod := range podList { 113 if strings.HasSuffix(pod.Name, strconv.Itoa(testapps.DefaultReplicationCandidateIndex)) { 114 Expect(pod.Labels[constant.RoleLabelKey]).Should(BeEquivalentTo(constant.Primary)) 115 } else { 116 Expect(pod.Labels[constant.RoleLabelKey]).Should(BeEquivalentTo(constant.Secondary)) 117 } 118 } 119 120 By("Checking services status") 121 svcList := &corev1.ServiceList{} 122 Expect(k8sClient.List(ctx, svcList, client.MatchingLabels{ 123 constant.AppInstanceLabelKey: clusterKey.Name, 124 }, client.InNamespace(clusterKey.Namespace))).Should(Succeed()) 125 // we should have both external service and headless service 126 Expect(len(svcList.Items)).Should(Equal(2)) 127 var externalSvc corev1.Service 128 for _, svc := range svcList.Items { 129 if svc.Spec.ClusterIP != "None" { 130 externalSvc = svc 131 } 132 } 133 Expect(externalSvc).ShouldNot(BeNil()) 134 135 for _, newReplicas := range []int32{4, 2, 7, 1} { 136 By(fmt.Sprintf("horizontal scale out to %d", newReplicas)) 137 Expect(testapps.ChangeObj(&testCtx, clusterObj, func(lcluster *appsv1alpha1.Cluster) { 138 lcluster.Spec.ComponentSpecs[0].Replicas = newReplicas 139 })).Should(Succeed()) 140 141 By("Wait for the cluster to be running") 142 Consistently(testapps.GetClusterPhase(&testCtx, clusterKey)).Should(Equal(appsv1alpha1.RunningClusterPhase)) 143 144 By("Checking pods' status and count are updated in cluster status after scale-out") 145 Eventually(testapps.CheckObj(&testCtx, clusterKey, func(g Gomega, fetched *appsv1alpha1.Cluster) { 146 compName := fetched.Spec.ComponentSpecs[0].Name 147 g.Expect(fetched.Status.Components).NotTo(BeNil()) 148 g.Expect(fetched.Status.Components).To(HaveKey(compName)) 149 replicationStatus := fetched.Status.Components[compName].ReplicationSetStatus 150 g.Expect(replicationStatus).NotTo(BeNil()) 151 g.Expect(len(replicationStatus.Secondaries)).To(BeEquivalentTo(newReplicas - 1)) 152 })).Should(Succeed()) 153 } 154 } 155 156 // Scenarios 157 158 Context("with Redis defined as replication Type and doing Horizontal scale", func() { 159 BeforeEach(func() { 160 _ = testapps.CreateCustomizedObj(&testCtx, "resources/redis-scripts.yaml", &corev1.ConfigMap{}, 161 testapps.WithName(scriptConfigName), testCtx.UseDefaultNamespace()) 162 163 _ = testapps.CreateCustomizedObj(&testCtx, "resources/redis-primary-config-template.yaml", &corev1.ConfigMap{}, 164 testapps.WithName(primaryConfigName), testCtx.UseDefaultNamespace()) 165 166 _ = testapps.CreateCustomizedObj(&testCtx, "resources/redis-secondary-config-template.yaml", &corev1.ConfigMap{}, 167 testapps.WithName(secondaryConfigName), testCtx.UseDefaultNamespace()) 168 169 replicationRedisConfigVolumeMounts := []corev1.VolumeMount{ 170 { 171 Name: constant.Primary, 172 MountPath: "/etc/conf/primary", 173 }, 174 { 175 Name: constant.Secondary, 176 MountPath: "/etc/conf/secondary", 177 }, 178 } 179 180 By("Create a clusterDefinition obj with replication workloadType.") 181 mode := int32(0755) 182 clusterDefObj = testapps.NewClusterDefFactory(clusterDefName). 183 AddComponentDef(testapps.ReplicationRedisComponent, testapps.DefaultRedisCompDefName). 184 AddScriptTemplate(scriptConfigName, scriptConfigName, testCtx.DefaultNamespace, testapps.ScriptsVolumeName, &mode). 185 AddConfigTemplate(primaryConfigName, primaryConfigName, "", testCtx.DefaultNamespace, constant.Primary). 186 AddConfigTemplate(secondaryConfigName, secondaryConfigName, "", testCtx.DefaultNamespace, constant.Secondary). 187 AddInitContainerVolumeMounts(testapps.DefaultRedisInitContainerName, replicationRedisConfigVolumeMounts). 188 AddContainerVolumeMounts(testapps.DefaultRedisContainerName, replicationRedisConfigVolumeMounts). 189 Create(&testCtx).GetObject() 190 191 By("Create a clusterVersion obj with replication workloadType.") 192 clusterVersionObj = testapps.NewClusterVersionFactory(clusterVersionName, clusterDefObj.Name). 193 AddComponentVersion(testapps.DefaultRedisCompDefName). 194 AddInitContainerShort(testapps.DefaultRedisInitContainerName, testapps.DefaultRedisImageName). 195 AddContainerShort(testapps.DefaultRedisContainerName, testapps.DefaultRedisImageName). 196 Create(&testCtx).GetObject() 197 }) 198 199 It("Should success with one primary and x secondaries when changes the number of replicas", func() { 200 testReplicationRedisHorizontalScale() 201 }) 202 }) 203 })