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  }