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  })