github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/testutil/apps/cluster_consensus_test_util.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 apps 21 22 import ( 23 "context" 24 "fmt" 25 26 "github.com/onsi/gomega" 27 appsv1 "k8s.io/api/apps/v1" 28 corev1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/util/intstr" 30 "sigs.k8s.io/controller-runtime/pkg/client" 31 32 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 33 workloads "github.com/1aal/kubeblocks/apis/workloads/v1alpha1" 34 "github.com/1aal/kubeblocks/pkg/constant" 35 "github.com/1aal/kubeblocks/pkg/testutil" 36 ) 37 38 const ( 39 errorLogName = "error" 40 leader = "leader" 41 follower = "follower" 42 learner = "learner" 43 ConsensusReplicas = 3 44 ) 45 46 // InitConsensusMysql initializes a cluster environment which only contains a component of ConsensusSet type for testing, 47 // includes ClusterDefinition/ClusterVersion/Cluster resources. 48 func InitConsensusMysql(testCtx *testutil.TestContext, 49 clusterDefName, 50 clusterVersionName, 51 clusterName, 52 consensusCompType, 53 consensusCompName string) (*appsv1alpha1.ClusterDefinition, *appsv1alpha1.ClusterVersion, *appsv1alpha1.Cluster) { 54 clusterDef := CreateConsensusMysqlClusterDef(testCtx, clusterDefName, consensusCompType) 55 clusterVersion := CreateConsensusMysqlClusterVersion(testCtx, clusterDefName, clusterVersionName, consensusCompType) 56 cluster := CreateConsensusMysqlCluster(testCtx, clusterDefName, clusterVersionName, clusterName, consensusCompType, consensusCompName) 57 return clusterDef, clusterVersion, cluster 58 } 59 60 // CreateConsensusMysqlCluster creates a mysql cluster with a component of ConsensusSet type. 61 func CreateConsensusMysqlCluster( 62 testCtx *testutil.TestContext, 63 clusterDefName, 64 clusterVersionName, 65 clusterName, 66 workloadType, 67 consensusCompName string, pvcSize ...string) *appsv1alpha1.Cluster { 68 size := "2Gi" 69 if len(pvcSize) > 0 { 70 size = pvcSize[0] 71 } 72 pvcSpec := NewPVCSpec(size) 73 return NewClusterFactory(testCtx.DefaultNamespace, clusterName, clusterDefName, clusterVersionName). 74 AddComponent(consensusCompName, workloadType).SetReplicas(ConsensusReplicas).SetEnabledLogs(errorLogName). 75 AddVolumeClaimTemplate("data", pvcSpec).Create(testCtx).GetObject() 76 } 77 78 // CreateConsensusMysqlClusterDef creates a mysql clusterDefinition with a component of ConsensusSet type. 79 func CreateConsensusMysqlClusterDef(testCtx *testutil.TestContext, clusterDefName, componentDefName string) *appsv1alpha1.ClusterDefinition { 80 filePathPattern := "/data/mysql/log/mysqld.err" 81 return NewClusterDefFactory(clusterDefName).AddComponentDef(ConsensusMySQLComponent, componentDefName). 82 AddLogConfig(errorLogName, filePathPattern).Create(testCtx).GetObject() 83 } 84 85 // CreateConsensusMysqlClusterVersion creates a mysql clusterVersion with a component of ConsensusSet type. 86 func CreateConsensusMysqlClusterVersion(testCtx *testutil.TestContext, clusterDefName, clusterVersionName, workloadType string) *appsv1alpha1.ClusterVersion { 87 return NewClusterVersionFactory(clusterVersionName, clusterDefName).AddComponentVersion(workloadType).AddContainerShort("mysql", ApeCloudMySQLImage). 88 Create(testCtx).GetObject() 89 } 90 91 // MockConsensusComponentStatefulSet mocks the component statefulSet, just using in envTest 92 func MockConsensusComponentStatefulSet( 93 testCtx *testutil.TestContext, 94 clusterName, 95 consensusCompName string) *appsv1.StatefulSet { 96 stsName := clusterName + "-" + consensusCompName 97 return NewStatefulSetFactory(testCtx.DefaultNamespace, stsName, clusterName, consensusCompName).SetReplicas(ConsensusReplicas). 98 AddContainer(corev1.Container{Name: DefaultMySQLContainerName, Image: ApeCloudMySQLImage}).Create(testCtx).GetObject() 99 } 100 101 // MockRSMComponent mocks the component rsm, just using in envTest 102 func MockRSMComponent( 103 testCtx *testutil.TestContext, 104 clusterName, 105 rsmCompName string) *workloads.ReplicatedStateMachine { 106 rsmName := clusterName + "-" + rsmCompName 107 return NewRSMFactory(testCtx.DefaultNamespace, rsmName, clusterName, rsmCompName).SetReplicas(ConsensusReplicas). 108 AddContainer(corev1.Container{Name: DefaultMySQLContainerName, Image: ApeCloudMySQLImage}).Create(testCtx).GetObject() 109 } 110 111 // MockConsensusComponentStsPod mocks to create the pod of the consensus StatefulSet, just using in envTest 112 func MockConsensusComponentStsPod( 113 testCtx *testutil.TestContext, 114 sts *appsv1.StatefulSet, 115 clusterName, 116 consensusCompName, 117 podName, 118 podRole, accessMode string) *corev1.Pod { 119 var stsUpdateRevision string 120 if sts != nil { 121 stsUpdateRevision = sts.Status.UpdateRevision 122 } 123 podFactory := NewPodFactory(testCtx.DefaultNamespace, podName). 124 SetOwnerReferences("apps/v1", constant.StatefulSetKind, sts). 125 AddAppInstanceLabel(clusterName). 126 AddAppComponentLabel(consensusCompName). 127 AddAppManagedByLabel(). 128 AddRoleLabel(podRole). 129 AddConsensusSetAccessModeLabel(accessMode). 130 AddControllerRevisionHashLabel(stsUpdateRevision). 131 AddContainer(corev1.Container{ 132 Name: DefaultMySQLContainerName, 133 Image: ApeCloudMySQLImage, 134 LivenessProbe: &corev1.Probe{ 135 ProbeHandler: corev1.ProbeHandler{ 136 HTTPGet: &corev1.HTTPGetAction{ 137 Path: "/hello", 138 Port: intstr.FromInt(1024), 139 }, 140 }, 141 TimeoutSeconds: 1, 142 PeriodSeconds: 1, 143 FailureThreshold: 1, 144 }, 145 StartupProbe: &corev1.Probe{ 146 ProbeHandler: corev1.ProbeHandler{ 147 TCPSocket: &corev1.TCPSocketAction{ 148 Port: intstr.FromInt(1024), 149 }, 150 }, 151 }, 152 }) 153 if sts != nil && sts.Labels[constant.AppNameLabelKey] != "" { 154 podFactory.AddAppNameLabel(sts.Labels[constant.AppNameLabelKey]) 155 } 156 pod := podFactory.CheckedCreate(testCtx).GetObject() 157 patch := client.MergeFrom(pod.DeepCopy()) 158 pod.Status.Conditions = []corev1.PodCondition{ 159 { 160 Type: corev1.PodReady, 161 Status: corev1.ConditionTrue, 162 }, 163 } 164 gomega.Expect(testCtx.Cli.Status().Patch(context.Background(), pod, patch)).Should(gomega.Succeed()) 165 return pod 166 } 167 168 // MockConsensusComponentPods mocks the component pods, just using in envTest 169 func MockConsensusComponentPods( 170 testCtx *testutil.TestContext, 171 sts *appsv1.StatefulSet, 172 clusterName, 173 consensusCompName string) []*corev1.Pod { 174 getReplicas := func() int { 175 if sts == nil || sts.Spec.Replicas == nil { 176 return ConsensusReplicas 177 } 178 return int(*sts.Spec.Replicas) 179 } 180 replicas := getReplicas() 181 podList := make([]*corev1.Pod, replicas) 182 for i := 0; i < replicas; i++ { 183 podName := fmt.Sprintf("%s-%s-%d", clusterName, consensusCompName, i) 184 podRole := "follower" 185 accessMode := "Readonly" 186 if i == 0 { 187 podRole = "leader" 188 accessMode = "ReadWrite" 189 } 190 // mock StatefulSet to create all pods 191 pod := MockConsensusComponentStsPod(testCtx, sts, clusterName, consensusCompName, podName, podRole, accessMode) 192 podList[i] = pod 193 } 194 return podList 195 }