github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/factory/builder_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 factory
    21  
    22  import (
    23  	"encoding/json"
    24  	"fmt"
    25  
    26  	. "github.com/onsi/ginkgo/v2"
    27  	. "github.com/onsi/gomega"
    28  
    29  	"golang.org/x/exp/slices"
    30  	appsv1 "k8s.io/api/apps/v1"
    31  	corev1 "k8s.io/api/core/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/types"
    34  	"sigs.k8s.io/controller-runtime/pkg/client"
    35  
    36  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    37  	workloads "github.com/1aal/kubeblocks/apis/workloads/v1alpha1"
    38  	cfgcm "github.com/1aal/kubeblocks/pkg/configuration/config_manager"
    39  	"github.com/1aal/kubeblocks/pkg/constant"
    40  	"github.com/1aal/kubeblocks/pkg/controller/component"
    41  	intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    42  	testapps "github.com/1aal/kubeblocks/pkg/testutil/apps"
    43  	viper "github.com/1aal/kubeblocks/pkg/viperx"
    44  )
    45  
    46  var _ = Describe("builder", func() {
    47  	const clusterDefName = "test-clusterdef"
    48  	const clusterVersionName = "test-clusterversion"
    49  	const clusterName = "test-cluster"
    50  	const mysqlCompDefName = "replicasets"
    51  	const mysqlCompName = "mysql"
    52  	const proxyCompDefName = "proxy"
    53  
    54  	allFieldsClusterDefObj := func(needCreate bool) *appsv1alpha1.ClusterDefinition {
    55  		By("By assure an clusterDefinition obj")
    56  		clusterDefObj := testapps.NewClusterDefFactory(clusterDefName).
    57  			AddComponentDef(testapps.StatefulMySQLComponent, mysqlCompDefName).
    58  			AddComponentDef(testapps.StatelessNginxComponent, proxyCompDefName).
    59  			GetObject()
    60  		if needCreate {
    61  			Expect(testCtx.CreateObj(testCtx.Ctx, clusterDefObj)).Should(Succeed())
    62  		}
    63  		return clusterDefObj
    64  	}
    65  
    66  	allFieldsClusterVersionObj := func(needCreate bool) *appsv1alpha1.ClusterVersion {
    67  		By("By assure an clusterVersion obj")
    68  		clusterVersionObj := testapps.NewClusterVersionFactory(clusterVersionName, clusterDefName).
    69  			AddComponentVersion(mysqlCompDefName).
    70  			AddContainerShort("mysql", testapps.ApeCloudMySQLImage).
    71  			AddComponentVersion(proxyCompDefName).
    72  			AddInitContainerShort("nginx-init", testapps.NginxImage).
    73  			AddContainerShort("nginx", testapps.NginxImage).
    74  			GetObject()
    75  		if needCreate {
    76  			Expect(testCtx.CreateObj(testCtx.Ctx, clusterVersionObj)).Should(Succeed())
    77  		}
    78  		return clusterVersionObj
    79  	}
    80  
    81  	newExtraEnvs := func() map[string]string {
    82  		jsonStr, _ := json.Marshal(map[string]string{
    83  			"mock-key": "mock-value",
    84  		})
    85  		return map[string]string{
    86  			constant.ExtraEnvAnnotationKey: string(jsonStr),
    87  		}
    88  	}
    89  
    90  	newAllFieldsClusterObj := func(
    91  		clusterDefObj *appsv1alpha1.ClusterDefinition,
    92  		clusterVersionObj *appsv1alpha1.ClusterVersion,
    93  		needCreate bool,
    94  	) (*appsv1alpha1.Cluster, *appsv1alpha1.ClusterDefinition, *appsv1alpha1.ClusterVersion, types.NamespacedName) {
    95  		// setup Cluster obj requires default ClusterDefinition and ClusterVersion objects
    96  		if clusterDefObj == nil {
    97  			clusterDefObj = allFieldsClusterDefObj(needCreate)
    98  		}
    99  		if clusterVersionObj == nil {
   100  			clusterVersionObj = allFieldsClusterVersionObj(needCreate)
   101  		}
   102  		pvcSpec := testapps.NewPVCSpec("1Gi")
   103  		clusterObj := testapps.NewClusterFactory(testCtx.DefaultNamespace, clusterName,
   104  			clusterDefObj.Name, clusterVersionObj.Name).
   105  			AddAnnotationsInMap(newExtraEnvs()).
   106  			AddComponent(mysqlCompName, mysqlCompDefName).SetReplicas(1).
   107  			AddVolumeClaimTemplate(testapps.DataVolumeName, pvcSpec).
   108  			AddService(testapps.ServiceVPCName, corev1.ServiceTypeLoadBalancer).
   109  			AddService(testapps.ServiceInternetName, corev1.ServiceTypeLoadBalancer).
   110  			GetObject()
   111  		key := client.ObjectKeyFromObject(clusterObj)
   112  		if needCreate {
   113  			Expect(testCtx.CreateObj(testCtx.Ctx, clusterObj)).Should(Succeed())
   114  		}
   115  		return clusterObj, clusterDefObj, clusterVersionObj, key
   116  	}
   117  
   118  	newStsObj := func() *appsv1.StatefulSet {
   119  		container := corev1.Container{
   120  			Name: "mysql",
   121  			VolumeMounts: []corev1.VolumeMount{{
   122  				Name:      "mysql-config",
   123  				MountPath: "/mnt/config",
   124  			}},
   125  		}
   126  		return testapps.NewStatefulSetFactory(testCtx.DefaultNamespace, "mock-sts", clusterName, mysqlCompName).
   127  			AddAppNameLabel("mock-app").
   128  			AddAppInstanceLabel(clusterName).
   129  			AddAppComponentLabel(mysqlCompName).
   130  			SetReplicas(1).AddContainer(container).
   131  			AddVolumeClaimTemplate(corev1.PersistentVolumeClaim{
   132  				ObjectMeta: metav1.ObjectMeta{Name: testapps.DataVolumeName},
   133  				Spec:       testapps.NewPVC("1Gi"),
   134  			}).GetObject()
   135  	}
   136  	newReqCtx := func() intctrlutil.RequestCtx {
   137  		reqCtx := intctrlutil.RequestCtx{
   138  			Ctx:      testCtx.Ctx,
   139  			Log:      logger,
   140  			Recorder: clusterRecorder,
   141  		}
   142  		return reqCtx
   143  	}
   144  	newAllFieldsComponent := func(clusterDef *appsv1alpha1.ClusterDefinition, clusterVersion *appsv1alpha1.ClusterVersion) *component.SynthesizedComponent {
   145  		cluster, clusterDef, clusterVersion, _ := newAllFieldsClusterObj(clusterDef, clusterVersion, false)
   146  		reqCtx := newReqCtx()
   147  		By("assign every available fields")
   148  		component, err := component.BuildComponent(
   149  			reqCtx,
   150  			nil,
   151  			cluster,
   152  			clusterDef,
   153  			&clusterDef.Spec.ComponentDefs[0],
   154  			&cluster.Spec.ComponentSpecs[0],
   155  			nil,
   156  			&clusterVersion.Spec.ComponentVersions[0])
   157  		Expect(err).Should(Succeed())
   158  		Expect(component).ShouldNot(BeNil())
   159  		return component
   160  	}
   161  	newClusterObjs := func(clusterDefObj *appsv1alpha1.ClusterDefinition) (*appsv1alpha1.ClusterDefinition, *appsv1alpha1.Cluster, *component.SynthesizedComponent) {
   162  		cluster, clusterDef, clusterVersion, _ := newAllFieldsClusterObj(clusterDefObj, nil, false)
   163  		synthesizedComponent := newAllFieldsComponent(clusterDef, clusterVersion)
   164  		return clusterDef, cluster, synthesizedComponent
   165  	}
   166  
   167  	Context("has helper function which builds specific object from cue template", func() {
   168  		It("builds PVC correctly", func() {
   169  			snapshotName := "test-snapshot-name"
   170  			sts := newStsObj()
   171  			_, cluster, synthesizedComponent := newClusterObjs(nil)
   172  			pvcKey := types.NamespacedName{
   173  				Namespace: "default",
   174  				Name:      "data-mysql-01-replicasets-0",
   175  			}
   176  			pvc := BuildPVC(cluster, synthesizedComponent, &synthesizedComponent.VolumeClaimTemplates[0], pvcKey, snapshotName)
   177  			Expect(pvc).ShouldNot(BeNil())
   178  			Expect(pvc.Spec.AccessModes).Should(Equal(sts.Spec.VolumeClaimTemplates[0].Spec.AccessModes))
   179  			Expect(pvc.Spec.Resources).Should(Equal(synthesizedComponent.VolumeClaimTemplates[0].Spec.Resources))
   180  			Expect(pvc.Spec.StorageClassName).Should(Equal(synthesizedComponent.VolumeClaimTemplates[0].Spec.StorageClassName))
   181  			Expect(pvc.Labels[constant.VolumeTypeLabelKey]).ShouldNot(BeEmpty())
   182  		})
   183  
   184  		It("builds Conn. Credential correctly", func() {
   185  			var (
   186  				clusterDefObj                             = testapps.NewClusterDefFactoryWithConnCredential("conn-cred").GetObject()
   187  				clusterDef, cluster, synthesizedComponent = newClusterObjs(clusterDefObj)
   188  			)
   189  			credential := BuildConnCredential(clusterDef, cluster, synthesizedComponent)
   190  			Expect(credential).ShouldNot(BeNil())
   191  			Expect(credential.Labels["apps.kubeblocks.io/cluster-type"]).Should(BeEmpty())
   192  			By("setting type")
   193  			characterType := "test-character-type"
   194  			clusterDef.Spec.Type = characterType
   195  			credential = BuildConnCredential(clusterDef, cluster, synthesizedComponent)
   196  			Expect(credential).ShouldNot(BeNil())
   197  			Expect(credential.Labels["apps.kubeblocks.io/cluster-type"]).Should(Equal(characterType))
   198  			// "username":      "root",
   199  			// "SVC_FQDN":      "$(SVC_FQDN)",
   200  			// "RANDOM_PASSWD": "$(RANDOM_PASSWD)",
   201  			// "tcpEndpoint":   "tcp:$(SVC_FQDN):$(SVC_PORT_mysql)",
   202  			// "paxosEndpoint": "paxos:$(SVC_FQDN):$(SVC_PORT_paxos)",
   203  			// "UUID":          "$(UUID)",
   204  			// "UUID_B64":      "$(UUID_B64)",
   205  			// "UUID_STR_B64":  "$(UUID_STR_B64)",
   206  			// "UUID_HEX":      "$(UUID_HEX)",
   207  			Expect(credential.StringData).ShouldNot(BeEmpty())
   208  			Expect(credential.StringData["username"]).Should(Equal("root"))
   209  
   210  			for _, v := range []string{
   211  				"SVC_FQDN",
   212  				"RANDOM_PASSWD",
   213  				"UUID",
   214  				"UUID_B64",
   215  				"UUID_STR_B64",
   216  				"UUID_HEX",
   217  				"HEADLESS_SVC_FQDN",
   218  			} {
   219  				Expect(credential.StringData[v]).ShouldNot(BeEquivalentTo(fmt.Sprintf("$(%s)", v)))
   220  			}
   221  			Expect(credential.StringData["RANDOM_PASSWD"]).Should(HaveLen(8))
   222  			svcFQDN := fmt.Sprintf("%s-%s.%s.svc", cluster.Name, synthesizedComponent.Name, cluster.Namespace)
   223  			headlessSvcFQDN := fmt.Sprintf("%s-%s-headless.%s.svc", cluster.Name, synthesizedComponent.Name, cluster.Namespace)
   224  			var mysqlPort corev1.ServicePort
   225  			var paxosPort corev1.ServicePort
   226  			for _, s := range synthesizedComponent.Services[0].Spec.Ports {
   227  				switch s.Name {
   228  				case "mysql":
   229  					mysqlPort = s
   230  				case "paxos":
   231  					paxosPort = s
   232  				}
   233  			}
   234  			Expect(credential.StringData["SVC_FQDN"]).Should(Equal(svcFQDN))
   235  			Expect(credential.StringData["HEADLESS_SVC_FQDN"]).Should(Equal(headlessSvcFQDN))
   236  			Expect(credential.StringData["tcpEndpoint"]).Should(Equal(fmt.Sprintf("tcp:%s:%d", svcFQDN, mysqlPort.Port)))
   237  			Expect(credential.StringData["paxosEndpoint"]).Should(Equal(fmt.Sprintf("paxos:%s:%d", svcFQDN, paxosPort.Port)))
   238  
   239  		})
   240  
   241  		It("builds Conn. Credential during restoring from backup", func() {
   242  			originalPassword := "test-passw0rd"
   243  			encryptionKey := "encryptionKey"
   244  			viper.Set(constant.CfgKeyDPEncryptionKey, encryptionKey)
   245  			var (
   246  				clusterDefObj                             = testapps.NewClusterDefFactoryWithConnCredential("conn-cred").GetObject()
   247  				clusterDef, cluster, synthesizedComponent = newClusterObjs(clusterDefObj)
   248  			)
   249  			e := intctrlutil.NewEncryptor(encryptionKey)
   250  			ciphertext, _ := e.Encrypt([]byte(originalPassword))
   251  			cluster.Annotations[constant.RestoreFromBackupAnnotationKey] = fmt.Sprintf(`{"%s":{"%s":"%s"}}`,
   252  				synthesizedComponent.Name, constant.ConnectionPassword, ciphertext)
   253  			credential := BuildConnCredential(clusterDef, cluster, synthesizedComponent)
   254  			Expect(credential).ShouldNot(BeNil())
   255  			Expect(credential.StringData["RANDOM_PASSWD"]).Should(Equal(originalPassword))
   256  		})
   257  
   258  		It("builds StatefulSet correctly", func() {
   259  			reqCtx := newReqCtx()
   260  			_, cluster, synthesizedComponent := newClusterObjs(nil)
   261  			envConfigName := "test-env-config-name"
   262  
   263  			sts, err := BuildSts(reqCtx, cluster, synthesizedComponent, envConfigName)
   264  			Expect(err).Should(BeNil())
   265  			Expect(sts).ShouldNot(BeNil())
   266  			// test  replicas = 0
   267  			newComponent := *synthesizedComponent
   268  			newComponent.Replicas = 0
   269  			sts, err = BuildSts(reqCtx, cluster, &newComponent, envConfigName)
   270  			Expect(err).Should(BeNil())
   271  			Expect(sts).ShouldNot(BeNil())
   272  			Expect(*sts.Spec.Replicas).Should(Equal(int32(0)))
   273  			Expect(sts.Spec.VolumeClaimTemplates[0].Labels[constant.VolumeTypeLabelKey]).
   274  				Should(Equal(string(appsv1alpha1.VolumeTypeData)))
   275  			// test workload type replication
   276  			replComponent := *synthesizedComponent
   277  			replComponent.Replicas = 2
   278  			replComponent.WorkloadType = appsv1alpha1.Replication
   279  			sts, err = BuildSts(reqCtx, cluster, &replComponent, envConfigName)
   280  			Expect(err).Should(BeNil())
   281  			Expect(sts).ShouldNot(BeNil())
   282  			Expect(*sts.Spec.Replicas).Should(BeEquivalentTo(2))
   283  			// test extra envs
   284  			Expect(sts.Spec.Template.Spec.Containers).ShouldNot(BeEmpty())
   285  			for _, container := range sts.Spec.Template.Spec.Containers {
   286  				isContainEnv := false
   287  				for _, env := range container.Env {
   288  					if env.Name == "mock-key" && env.Value == "mock-value" {
   289  						isContainEnv = true
   290  						break
   291  					}
   292  				}
   293  				Expect(isContainEnv).Should(BeTrue())
   294  			}
   295  		})
   296  
   297  		It("builds RSM correctly", func() {
   298  			reqCtx := newReqCtx()
   299  			_, cluster, synthesizedComponent := newClusterObjs(nil)
   300  			envConfigName := "test-env-config-name"
   301  
   302  			rsm, err := BuildRSM(reqCtx, cluster, synthesizedComponent, envConfigName)
   303  			Expect(err).Should(BeNil())
   304  			Expect(rsm).ShouldNot(BeNil())
   305  
   306  			By("set replicas = 0")
   307  			newComponent := *synthesizedComponent
   308  			newComponent.Replicas = 0
   309  			rsm, err = BuildRSM(reqCtx, cluster, &newComponent, envConfigName)
   310  			Expect(err).Should(BeNil())
   311  			Expect(rsm).ShouldNot(BeNil())
   312  			Expect(*rsm.Spec.Replicas).Should(Equal(int32(0)))
   313  			Expect(rsm.Spec.VolumeClaimTemplates[0].Labels[constant.VolumeTypeLabelKey]).
   314  				Should(Equal(string(appsv1alpha1.VolumeTypeData)))
   315  
   316  			By("set workload type to Replication")
   317  			replComponent := *synthesizedComponent
   318  			replComponent.Replicas = 2
   319  			replComponent.WorkloadType = appsv1alpha1.Replication
   320  			rsm, err = BuildRSM(reqCtx, cluster, &replComponent, envConfigName)
   321  			Expect(err).Should(BeNil())
   322  			Expect(rsm).ShouldNot(BeNil())
   323  			Expect(*rsm.Spec.Replicas).Should(BeEquivalentTo(2))
   324  			// test extra envs
   325  			Expect(rsm.Spec.Template.Spec.Containers).ShouldNot(BeEmpty())
   326  			for _, container := range rsm.Spec.Template.Spec.Containers {
   327  				isContainEnv := false
   328  				for _, env := range container.Env {
   329  					if env.Name == "mock-key" && env.Value == "mock-value" {
   330  						isContainEnv = true
   331  						break
   332  					}
   333  				}
   334  				Expect(isContainEnv).Should(BeTrue())
   335  			}
   336  
   337  			// test service labels
   338  			expectLabelsExist := func(labels map[string]string) {
   339  				expectedLabels := map[string]string{
   340  					constant.AppManagedByLabelKey:   constant.AppName,
   341  					constant.AppNameLabelKey:        replComponent.ClusterDefName,
   342  					constant.AppInstanceLabelKey:    cluster.Name,
   343  					constant.KBAppComponentLabelKey: replComponent.Name,
   344  					constant.AppComponentLabelKey:   replComponent.CompDefName,
   345  				}
   346  				Expect(labels).ShouldNot(BeNil())
   347  				for k, ev := range expectedLabels {
   348  					v, ok := labels[k]
   349  					Expect(ok).Should(BeTrue())
   350  					Expect(v).Should(Equal(ev))
   351  				}
   352  			}
   353  			Expect(rsm.Spec.Service).ShouldNot(BeNil())
   354  			expectLabelsExist(rsm.Spec.Service.Labels)
   355  
   356  			// test roles
   357  			Expect(rsm.Spec.Roles).Should(HaveLen(2))
   358  			for _, roleName := range []string{constant.Primary, constant.Secondary} {
   359  				Expect(slices.IndexFunc(rsm.Spec.Roles, func(role workloads.ReplicaRole) bool {
   360  					return role.Name == roleName
   361  				})).Should(BeNumerically(">", -1))
   362  			}
   363  
   364  			// test role probe
   365  			Expect(rsm.Spec.RoleProbe).ShouldNot(BeNil())
   366  
   367  			// test member update strategy
   368  			Expect(rsm.Spec.MemberUpdateStrategy).ShouldNot(BeNil())
   369  			Expect(*rsm.Spec.MemberUpdateStrategy).Should(BeEquivalentTo(workloads.SerialUpdateStrategy))
   370  
   371  			By("set workload type to Consensus")
   372  			csComponent := *synthesizedComponent
   373  			csComponent.Replicas = 3
   374  			csComponent.WorkloadType = appsv1alpha1.Consensus
   375  			csComponent.CharacterType = "mysql"
   376  			csComponent.ConsensusSpec = appsv1alpha1.NewConsensusSetSpec()
   377  			csComponent.ConsensusSpec.UpdateStrategy = appsv1alpha1.BestEffortParallelStrategy
   378  			rsm, err = BuildRSM(reqCtx, cluster, &csComponent, envConfigName)
   379  			Expect(err).Should(BeNil())
   380  			Expect(rsm).ShouldNot(BeNil())
   381  
   382  			// test roles
   383  			Expect(rsm.Spec.Roles).Should(HaveLen(1))
   384  			Expect(rsm.Spec.Roles[0].Name).Should(Equal(appsv1alpha1.DefaultLeader.Name))
   385  
   386  			// test role probe
   387  			Expect(rsm.Spec.RoleProbe).ShouldNot(BeNil())
   388  
   389  			// test member update strategy
   390  			Expect(rsm.Spec.MemberUpdateStrategy).ShouldNot(BeNil())
   391  			Expect(*rsm.Spec.MemberUpdateStrategy).Should(BeEquivalentTo(workloads.BestEffortParallelUpdateStrategy))
   392  		})
   393  
   394  		It("builds PDB correctly", func() {
   395  			_, cluster, synthesizedComponent := newClusterObjs(nil)
   396  			pdb := BuildPDB(cluster, synthesizedComponent)
   397  			Expect(pdb).ShouldNot(BeNil())
   398  		})
   399  
   400  		It("builds BackupJob correctly", func() {
   401  			_, cluster, synthesizedComponent := newClusterObjs(nil)
   402  			backupJobKey := types.NamespacedName{
   403  				Namespace: "default",
   404  				Name:      "test-backup-job",
   405  			}
   406  			backupPolicyName := "test-backup-policy"
   407  			backupJob := BuildBackup(cluster, synthesizedComponent, backupPolicyName, backupJobKey, "snapshot")
   408  			Expect(backupJob).ShouldNot(BeNil())
   409  		})
   410  
   411  		It("builds ConfigMap with template correctly", func() {
   412  			config := map[string]string{}
   413  			_, cluster, synthesizedComponent := newClusterObjs(nil)
   414  			tplCfg := appsv1alpha1.ComponentConfigSpec{
   415  				ComponentTemplateSpec: appsv1alpha1.ComponentTemplateSpec{
   416  					Name:        "test-config-tpl",
   417  					TemplateRef: "test-config-tpl",
   418  				},
   419  				ConfigConstraintRef: "test-config-constraint",
   420  			}
   421  			configmap := BuildConfigMapWithTemplate(cluster, synthesizedComponent, config,
   422  				"test-cm", tplCfg.ComponentTemplateSpec)
   423  			Expect(configmap).ShouldNot(BeNil())
   424  		})
   425  
   426  		It("builds config manager sidecar container correctly", func() {
   427  			_, cluster, synthesizedComponent := newClusterObjs(nil)
   428  			cfg := BuildEnvConfig(cluster, synthesizedComponent)
   429  			sidecarRenderedParam := &cfgcm.CfgManagerBuildParams{
   430  				ManagerName:   "cfgmgr",
   431  				SecreteName:   "test-secret",
   432  				ComponentName: synthesizedComponent.Name,
   433  				CharacterType: synthesizedComponent.CharacterType,
   434  				EnvConfigName: cfg.Name,
   435  				Image:         constant.KBToolsImage,
   436  				Args:          []string{},
   437  				Envs:          []corev1.EnvVar{},
   438  				Volumes:       []corev1.VolumeMount{},
   439  				Cluster:       cluster,
   440  			}
   441  			configmap, err := BuildCfgManagerContainer(sidecarRenderedParam, synthesizedComponent)
   442  			Expect(err).Should(BeNil())
   443  			Expect(configmap).ShouldNot(BeNil())
   444  			Expect(configmap.SecurityContext).Should(BeNil())
   445  		})
   446  
   447  		It("builds config manager sidecar container correctly", func() {
   448  			_, cluster, synthesizedComponent := newClusterObjs(nil)
   449  			sidecarRenderedParam := &cfgcm.CfgManagerBuildParams{
   450  				ManagerName:           "cfgmgr",
   451  				CharacterType:         "mysql",
   452  				SecreteName:           "test-secret",
   453  				Image:                 constant.KBToolsImage,
   454  				ShareProcessNamespace: true,
   455  				Args:                  []string{},
   456  				Envs:                  []corev1.EnvVar{},
   457  				Volumes:               []corev1.VolumeMount{},
   458  				Cluster:               cluster,
   459  			}
   460  			configmap, err := BuildCfgManagerContainer(sidecarRenderedParam, synthesizedComponent)
   461  			Expect(err).Should(BeNil())
   462  			Expect(configmap).ShouldNot(BeNil())
   463  			Expect(configmap.SecurityContext).ShouldNot(BeNil())
   464  			Expect(configmap.SecurityContext.RunAsUser).ShouldNot(BeNil())
   465  			Expect(*configmap.SecurityContext.RunAsUser).Should(BeEquivalentTo(int64(0)))
   466  		})
   467  
   468  		It("builds restore job correctly", func() {
   469  			key := types.NamespacedName{Name: "restore", Namespace: "default"}
   470  			volumes := []corev1.Volume{}
   471  			volumeMounts := []corev1.VolumeMount{}
   472  			env := []corev1.EnvVar{}
   473  			component := &component.SynthesizedComponent{
   474  				Name: mysqlCompName,
   475  			}
   476  			cluster := &appsv1alpha1.Cluster{
   477  				ObjectMeta: metav1.ObjectMeta{Namespace: key.Namespace},
   478  				Spec: appsv1alpha1.ClusterSpec{
   479  					Tolerations: []corev1.Toleration{
   480  						{
   481  							Key:      "testKey",
   482  							Value:    "testVaule",
   483  							Operator: corev1.TolerationOpExists,
   484  						},
   485  					},
   486  				},
   487  			}
   488  			job, err := BuildRestoreJob(cluster, component, key.Name, "", []string{"sh"}, volumes, volumeMounts, env, nil)
   489  			Expect(err).Should(BeNil())
   490  			Expect(job.Spec.Template.Spec.Tolerations[0].Key).Should(Equal("testKey"))
   491  			Expect(job).ShouldNot(BeNil())
   492  			Expect(job.Name).Should(Equal(key.Name))
   493  		})
   494  
   495  		It("builds volume snapshot class correctly", func() {
   496  			className := "vsc-test"
   497  			driverName := "csi-driver-test"
   498  			obj := BuildVolumeSnapshotClass(className, driverName)
   499  			Expect(obj).ShouldNot(BeNil())
   500  			Expect(obj.Name).Should(Equal(className))
   501  			Expect(obj.Driver).Should(Equal(driverName))
   502  		})
   503  
   504  		It("builds cfg manager tools  correctly", func() {
   505  			_, cluster, synthesizedComponent := newClusterObjs(nil)
   506  			cfgManagerParams := &cfgcm.CfgManagerBuildParams{
   507  				ManagerName:               constant.ConfigSidecarName,
   508  				SecreteName:               component.GenerateConnCredential(cluster.Name),
   509  				EnvConfigName:             component.GenerateComponentEnvName(cluster.Name, synthesizedComponent.Name),
   510  				Image:                     viper.GetString(constant.KBToolsImage),
   511  				Cluster:                   cluster,
   512  				ConfigLazyRenderedVolumes: make(map[string]corev1.VolumeMount),
   513  			}
   514  			toolContainers := []appsv1alpha1.ToolConfig{
   515  				{Name: "test-tool", Image: "test-image", Command: []string{"sh"}},
   516  			}
   517  
   518  			obj, err := BuildCfgManagerToolsContainer(cfgManagerParams, synthesizedComponent, toolContainers, map[string]cfgcm.ConfigSpecMeta{})
   519  			Expect(err).Should(BeNil())
   520  			Expect(obj).ShouldNot(BeEmpty())
   521  		})
   522  
   523  		It("builds serviceaccount correctly", func() {
   524  			_, cluster, _ := newClusterObjs(nil)
   525  			expectName := fmt.Sprintf("kb-%s", cluster.Name)
   526  			sa := BuildServiceAccount(cluster)
   527  			Expect(sa).ShouldNot(BeNil())
   528  			Expect(sa.Name).Should(Equal(expectName))
   529  		})
   530  
   531  		It("builds rolebinding correctly", func() {
   532  			_, cluster, _ := newClusterObjs(nil)
   533  			expectName := fmt.Sprintf("kb-%s", cluster.Name)
   534  			rb := BuildRoleBinding(cluster)
   535  			Expect(rb).ShouldNot(BeNil())
   536  			Expect(rb.Name).Should(Equal(expectName))
   537  		})
   538  
   539  		It("builds clusterrolebinding correctly", func() {
   540  			_, cluster, _ := newClusterObjs(nil)
   541  			expectName := fmt.Sprintf("kb-%s", cluster.Name)
   542  			crb := BuildClusterRoleBinding(cluster)
   543  			Expect(crb).ShouldNot(BeNil())
   544  			Expect(crb.Name).Should(Equal(expectName))
   545  		})
   546  	})
   547  })