github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/operations/switchover_util_test.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package operations 21 22 import ( 23 "fmt" 24 25 . "github.com/onsi/ginkgo/v2" 26 . "github.com/onsi/gomega" 27 28 corev1 "k8s.io/api/core/v1" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 31 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 32 "github.com/1aal/kubeblocks/pkg/constant" 33 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 34 "github.com/1aal/kubeblocks/pkg/generics" 35 testapps "github.com/1aal/kubeblocks/pkg/testutil/apps" 36 ) 37 38 var _ = Describe("Switchover Util", func() { 39 40 var ( 41 clusterName = "test-cluster-repl" 42 clusterDefName = "test-cluster-def-repl" 43 clusterVersionName = "test-cluster-version-repl" 44 ) 45 46 var ( 47 clusterDefObj *appsv1alpha1.ClusterDefinition 48 clusterVersionObj *appsv1alpha1.ClusterVersion 49 clusterObj *appsv1alpha1.Cluster 50 ) 51 52 defaultRole := func(index int32) string { 53 role := constant.Secondary 54 if index == 0 { 55 role = constant.Primary 56 } 57 return role 58 } 59 60 cleanAll := func() { 61 // must wait till resources deleted and no longer existed before the testcases start, 62 // otherwise if later it needs to create some new resource objects with the same name, 63 // in race conditions, it will find the existence of old objects, resulting failure to 64 // create the new objects. 65 By("clean resources") 66 // delete cluster(and all dependent sub-resources), clusterversion and clusterdef 67 testapps.ClearClusterResources(&testCtx) 68 69 // clear rest resources 70 inNS := client.InNamespace(testCtx.DefaultNamespace) 71 ml := client.HasLabels{testCtx.TestObjLabelKey} 72 // namespaced resources 73 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.StatefulSetSignature, true, inNS, ml) 74 testapps.ClearResources(&testCtx, generics.PodSignature, inNS, ml, client.GracePeriodSeconds(0)) 75 } 76 77 BeforeEach(cleanAll) 78 79 AfterEach(cleanAll) 80 81 testNeedDoSwitchover := func() { 82 By("Creating a cluster with replication workloadType.") 83 clusterObj = testapps.NewClusterFactory(testCtx.DefaultNamespace, clusterName, 84 clusterDefObj.Name, clusterVersionObj.Name).WithRandomName(). 85 AddComponent(testapps.DefaultRedisCompSpecName, testapps.DefaultRedisCompDefName). 86 SetReplicas(testapps.DefaultReplicationReplicas). 87 Create(&testCtx).GetObject() 88 89 By("Creating a statefulSet of replication workloadType.") 90 container := corev1.Container{ 91 Name: "mock-redis-container", 92 Image: testapps.DefaultRedisImageName, 93 ImagePullPolicy: corev1.PullIfNotPresent, 94 } 95 sts := testapps.NewStatefulSetFactory(testCtx.DefaultNamespace, 96 clusterObj.Name+"-"+testapps.DefaultRedisCompSpecName, clusterObj.Name, testapps.DefaultRedisCompSpecName). 97 AddFinalizers([]string{constant.DBClusterFinalizerName}). 98 AddContainer(container). 99 AddAppInstanceLabel(clusterObj.Name). 100 AddAppComponentLabel(testapps.DefaultRedisCompSpecName). 101 AddAppManagedByLabel(). 102 SetReplicas(2). 103 Create(&testCtx).GetObject() 104 105 By("Creating Pods of replication workloadType.") 106 for i := int32(0); i < *sts.Spec.Replicas; i++ { 107 _ = testapps.NewPodFactory(testCtx.DefaultNamespace, fmt.Sprintf("%s-%d", sts.Name, i)). 108 AddContainer(container). 109 AddLabelsInMap(sts.Labels). 110 AddRoleLabel(defaultRole(i)). 111 Create(&testCtx).GetObject() 112 } 113 114 opsSwitchover := &appsv1alpha1.Switchover{ 115 ComponentOps: appsv1alpha1.ComponentOps{ComponentName: testapps.DefaultRedisCompSpecName}, 116 InstanceName: fmt.Sprintf("%s-%s-%d", clusterObj.Name, testapps.DefaultRedisCompSpecName, 0), 117 } 118 By("Test opsSwitchover.Instance is already primary, and do not need to do switchover.") 119 needSwitchover, err := needDoSwitchover(testCtx.Ctx, k8sClient, clusterObj, &clusterObj.Spec.ComponentSpecs[0], opsSwitchover) 120 Expect(err).Should(Succeed()) 121 Expect(needSwitchover).Should(BeFalse()) 122 123 By("Test opsSwitchover.Instance is not primary, and need to do switchover.") 124 opsSwitchover.InstanceName = fmt.Sprintf("%s-%s-%d", clusterObj.Name, testapps.DefaultRedisCompSpecName, 1) 125 needSwitchover, err = needDoSwitchover(testCtx.Ctx, k8sClient, clusterObj, &clusterObj.Spec.ComponentSpecs[0], opsSwitchover) 126 Expect(err).Should(Succeed()) 127 Expect(needSwitchover).Should(BeTrue()) 128 129 By("Test opsSwitchover.Instance is *, and need to do switchover.") 130 opsSwitchover.InstanceName = "*" 131 needSwitchover, err = needDoSwitchover(testCtx.Ctx, k8sClient, clusterObj, &clusterObj.Spec.ComponentSpecs[0], opsSwitchover) 132 Expect(err).Should(Succeed()) 133 Expect(needSwitchover).Should(BeTrue()) 134 } 135 136 testDoSwitchover := func() { 137 By("Creating a cluster with replication workloadType.") 138 clusterObj = testapps.NewClusterFactory(testCtx.DefaultNamespace, clusterName, 139 clusterDefObj.Name, clusterVersionObj.Name).WithRandomName(). 140 AddComponent(testapps.DefaultRedisCompSpecName, testapps.DefaultRedisCompDefName). 141 SetReplicas(testapps.DefaultReplicationReplicas). 142 Create(&testCtx).GetObject() 143 144 By("Creating a statefulSet of replication workloadType.") 145 container := corev1.Container{ 146 Name: "mock-redis-container", 147 Image: testapps.DefaultRedisImageName, 148 ImagePullPolicy: corev1.PullIfNotPresent, 149 } 150 sts := testapps.NewStatefulSetFactory(testCtx.DefaultNamespace, 151 clusterObj.Name+"-"+testapps.DefaultRedisCompSpecName, clusterObj.Name, testapps.DefaultRedisCompSpecName). 152 AddFinalizers([]string{constant.DBClusterFinalizerName}). 153 AddContainer(container). 154 AddAppInstanceLabel(clusterObj.Name). 155 AddAppComponentLabel(testapps.DefaultRedisCompSpecName). 156 AddAppManagedByLabel(). 157 SetReplicas(2). 158 Create(&testCtx).GetObject() 159 160 By("Creating Pods of replication workloadType.") 161 for i := int32(0); i < *sts.Spec.Replicas; i++ { 162 _ = testapps.NewPodFactory(testCtx.DefaultNamespace, fmt.Sprintf("%s-%d", sts.Name, i)). 163 AddContainer(container). 164 AddLabelsInMap(sts.Labels). 165 AddRoleLabel(defaultRole(i)). 166 Create(&testCtx).GetObject() 167 } 168 opsSwitchover := &appsv1alpha1.Switchover{ 169 ComponentOps: appsv1alpha1.ComponentOps{ComponentName: testapps.DefaultRedisCompSpecName}, 170 InstanceName: fmt.Sprintf("%s-%s-%d", clusterObj.Name, testapps.DefaultRedisCompSpecName, 1), 171 } 172 reqCtx := intctrlutil.RequestCtx{ 173 Ctx: testCtx.Ctx, 174 Recorder: k8sManager.GetEventRecorderFor("opsrequest-controller"), 175 } 176 By("Test create a job to do switchover") 177 err := createSwitchoverJob(reqCtx, k8sClient, clusterObj, &clusterObj.Spec.ComponentSpecs[0], &clusterDefObj.Spec.ComponentDefs[0], opsSwitchover) 178 Expect(err).Should(Succeed()) 179 } 180 181 // Scenarios 182 Context("test switchover util", func() { 183 BeforeEach(func() { 184 By("Create a clusterDefinition obj with replication workloadType.") 185 commandExecutorEnvItem := &appsv1alpha1.CommandExecutorEnvItem{ 186 Image: testapps.DefaultRedisImageName, 187 } 188 commandExecutorItem := &appsv1alpha1.CommandExecutorItem{ 189 Command: []string{"echo", "hello"}, 190 Args: []string{}, 191 } 192 scriptSpecSelectors := []appsv1alpha1.ScriptSpecSelector{ 193 { 194 Name: "test-mock-cm", 195 }, 196 { 197 Name: "test-mock-cm-2", 198 }, 199 } 200 switchoverSpec := &appsv1alpha1.SwitchoverSpec{ 201 WithCandidate: &appsv1alpha1.SwitchoverAction{ 202 CmdExecutorConfig: &appsv1alpha1.CmdExecutorConfig{ 203 CommandExecutorEnvItem: *commandExecutorEnvItem, 204 CommandExecutorItem: *commandExecutorItem, 205 }, 206 ScriptSpecSelectors: scriptSpecSelectors, 207 }, 208 WithoutCandidate: &appsv1alpha1.SwitchoverAction{ 209 CmdExecutorConfig: &appsv1alpha1.CmdExecutorConfig{ 210 CommandExecutorEnvItem: *commandExecutorEnvItem, 211 CommandExecutorItem: *commandExecutorItem, 212 }, 213 ScriptSpecSelectors: scriptSpecSelectors, 214 }, 215 } 216 clusterDefObj = testapps.NewClusterDefFactory(clusterDefName). 217 AddComponentDef(testapps.ReplicationRedisComponent, testapps.DefaultRedisCompDefName). 218 AddSwitchoverSpec(switchoverSpec). 219 Create(&testCtx).GetObject() 220 221 By("Create a clusterVersion obj with replication workloadType.") 222 clusterVersionObj = testapps.NewClusterVersionFactory(clusterVersionName, clusterDefObj.GetName()). 223 AddComponentVersion(testapps.DefaultRedisCompDefName).AddContainerShort(testapps.DefaultRedisContainerName, testapps.DefaultRedisImageName). 224 Create(&testCtx).GetObject() 225 226 }) 227 228 It("Test needDoSwitchover with different conditions", func() { 229 testNeedDoSwitchover() 230 }) 231 232 It("Test doSwitchover when opsRequest triggers", func() { 233 testDoSwitchover() 234 }) 235 }) 236 })