github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/rsm/suite_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 rsm
    21  
    22  import (
    23  	"context"
    24  	"testing"
    25  
    26  	. "github.com/onsi/ginkgo/v2"
    27  	. "github.com/onsi/gomega"
    28  
    29  	"github.com/go-logr/logr"
    30  	"github.com/golang/mock/gomock"
    31  	apps "k8s.io/api/apps/v1"
    32  	corev1 "k8s.io/api/core/v1"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/types"
    35  	"k8s.io/apimachinery/pkg/util/intstr"
    36  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    37  
    38  	workloads "github.com/1aal/kubeblocks/apis/workloads/v1alpha1"
    39  	"github.com/1aal/kubeblocks/pkg/constant"
    40  	"github.com/1aal/kubeblocks/pkg/controller/builder"
    41  	"github.com/1aal/kubeblocks/pkg/controller/graph"
    42  	"github.com/1aal/kubeblocks/pkg/controller/model"
    43  	testutil "github.com/1aal/kubeblocks/pkg/testutil/k8s"
    44  	"github.com/1aal/kubeblocks/pkg/testutil/k8s/mocks"
    45  )
    46  
    47  var (
    48  	controller  *gomock.Controller
    49  	k8sMock     *mocks.MockClient
    50  	graphCli    model.GraphClient
    51  	ctx         context.Context
    52  	logger      logr.Logger
    53  	transCtx    *rsmTransformContext
    54  	dag         *graph.DAG
    55  	transformer graph.Transformer
    56  )
    57  
    58  const (
    59  	namespace   = "foo"
    60  	name        = "bar"
    61  	oldRevision = "old-revision"
    62  	newRevision = "new-revision"
    63  )
    64  
    65  var (
    66  	uid = types.UID("rsm-mock-uid")
    67  
    68  	selectors = map[string]string{
    69  		constant.AppInstanceLabelKey: name,
    70  		workloadsManagedByLabelKey:   kindReplicatedStateMachine,
    71  	}
    72  
    73  	headlessSvcName = name + "-headless"
    74  
    75  	roles = []workloads.ReplicaRole{
    76  		{
    77  			Name:       "leader",
    78  			IsLeader:   true,
    79  			CanVote:    true,
    80  			AccessMode: workloads.ReadWriteMode,
    81  		},
    82  		{
    83  			Name:       "follower",
    84  			IsLeader:   false,
    85  			CanVote:    true,
    86  			AccessMode: workloads.ReadonlyMode,
    87  		},
    88  		{
    89  			Name:       "logger",
    90  			IsLeader:   false,
    91  			CanVote:    true,
    92  			AccessMode: workloads.NoneMode,
    93  		},
    94  		{
    95  			Name:       "learner",
    96  			IsLeader:   false,
    97  			CanVote:    false,
    98  			AccessMode: workloads.ReadonlyMode,
    99  		},
   100  	}
   101  
   102  	roleProbe = &workloads.RoleProbe{
   103  		CustomHandler: []workloads.Action{{Command: []string{"cmd"}}},
   104  	}
   105  
   106  	reconfiguration = workloads.MembershipReconfiguration{
   107  		SwitchoverAction:  &workloads.Action{Command: []string{"cmd"}},
   108  		MemberJoinAction:  &workloads.Action{Command: []string{"cmd"}},
   109  		MemberLeaveAction: &workloads.Action{Command: []string{"cmd"}},
   110  	}
   111  
   112  	service = &corev1.Service{
   113  		ObjectMeta: metav1.ObjectMeta{
   114  			Labels: map[string]string{
   115  				constant.AppManagedByLabelKey:   constant.AppName,
   116  				constant.AppNameLabelKey:        "foo-cluster-definition",
   117  				constant.AppInstanceLabelKey:    "foo-cluster",
   118  				constant.KBAppComponentLabelKey: name,
   119  				constant.AppComponentLabelKey:   name + "def",
   120  			},
   121  		},
   122  		Spec: corev1.ServiceSpec{
   123  			Ports: []corev1.ServicePort{
   124  				{
   125  					Name:       "svc",
   126  					Protocol:   corev1.ProtocolTCP,
   127  					Port:       12345,
   128  					TargetPort: intstr.FromString("my-svc"),
   129  				},
   130  			},
   131  		},
   132  	}
   133  
   134  	credential = workloads.Credential{
   135  		Username: workloads.CredentialVar{Value: "foo"},
   136  		Password: workloads.CredentialVar{Value: "bar"},
   137  	}
   138  
   139  	pod = builder.NewPodBuilder(namespace, getPodName(name, 0)).
   140  		AddContainer(corev1.Container{
   141  			Name:  "foo",
   142  			Image: "bar",
   143  			Ports: []corev1.ContainerPort{
   144  				{
   145  					Name:          "my-svc",
   146  					Protocol:      corev1.ProtocolTCP,
   147  					ContainerPort: 12345,
   148  				},
   149  			},
   150  		}).GetObject()
   151  	template = corev1.PodTemplateSpec{
   152  		ObjectMeta: pod.ObjectMeta,
   153  		Spec:       pod.Spec,
   154  	}
   155  
   156  	observeActions = []workloads.Action{{Command: []string{"cmd"}}}
   157  
   158  	rsm *workloads.ReplicatedStateMachine
   159  )
   160  
   161  func less(v1, v2 graph.Vertex) bool {
   162  	return model.DefaultLess(v1, v2)
   163  }
   164  
   165  func makePodUpdateReady(newRevision string, pods ...*corev1.Pod) {
   166  	readyCondition := corev1.PodCondition{
   167  		Type:   corev1.PodReady,
   168  		Status: corev1.ConditionTrue,
   169  	}
   170  	for _, pod := range pods {
   171  		pod.Labels[apps.StatefulSetRevisionLabel] = newRevision
   172  		if pod.Labels[roleLabelKey] == "" {
   173  			pod.Labels[roleLabelKey] = "learner"
   174  		}
   175  		pod.Status.Conditions = append(pod.Status.Conditions, readyCondition)
   176  	}
   177  }
   178  
   179  func mockUnderlyingSts(rsm workloads.ReplicatedStateMachine, generation int64) *apps.StatefulSet {
   180  	headLessSvc := buildHeadlessSvc(rsm)
   181  	envConfig := buildEnvConfigMap(rsm)
   182  	sts := buildSts(rsm, headLessSvc.Name, *envConfig)
   183  	sts.Generation = generation
   184  	sts.Status.ObservedGeneration = generation
   185  	sts.Status.Replicas = *sts.Spec.Replicas
   186  	sts.Status.ReadyReplicas = sts.Status.Replicas
   187  	sts.Status.AvailableReplicas = sts.Status.ReadyReplicas
   188  	sts.Status.UpdatedReplicas = sts.Status.ReadyReplicas
   189  	sts.Status.UpdateRevision = rsm.Status.UpdateRevision
   190  	return sts
   191  }
   192  
   193  func mockDAG() *graph.DAG {
   194  	d := graph.NewDAG()
   195  	graphCli.Root(d, transCtx.rsmOrig, transCtx.rsm, model.ActionStatusPtr())
   196  	return d
   197  }
   198  
   199  func init() {
   200  	model.AddScheme(workloads.AddToScheme)
   201  }
   202  
   203  // These tests use Ginkgo (BDD-style Go testing framework). Refer to
   204  // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
   205  
   206  func TestAPIs(t *testing.T) {
   207  	RegisterFailHandler(Fail)
   208  
   209  	RunSpecs(t, "ReplicatedStateMachine Suite")
   210  }
   211  
   212  var _ = BeforeSuite(func() {
   213  	controller, k8sMock = testutil.SetupK8sMock()
   214  	graphCli = model.NewGraphClient(k8sMock)
   215  	ctx = context.Background()
   216  	logger = logf.FromContext(ctx).WithValues("rsm-test", namespace)
   217  
   218  	go func() {
   219  		defer GinkgoRecover()
   220  	}()
   221  })
   222  
   223  var _ = AfterSuite(func() {
   224  	controller.Finish()
   225  })