github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/test/integration/controller_suite_test.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package appstest
    18  
    19  import (
    20  	"context"
    21  	"go/build"
    22  	"path/filepath"
    23  	"testing"
    24  	"time"
    25  
    26  	. "github.com/onsi/ginkgo/v2"
    27  	. "github.com/onsi/gomega"
    28  
    29  	"github.com/go-logr/logr"
    30  	snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
    31  	"go.uber.org/zap/zapcore"
    32  	appsv1 "k8s.io/api/apps/v1"
    33  	corev1 "k8s.io/api/core/v1"
    34  	"k8s.io/apimachinery/pkg/api/resource"
    35  	"k8s.io/client-go/dynamic"
    36  	"k8s.io/client-go/kubernetes/scheme"
    37  	"k8s.io/client-go/rest"
    38  	"k8s.io/client-go/tools/record"
    39  	ctrl "sigs.k8s.io/controller-runtime"
    40  	"sigs.k8s.io/controller-runtime/pkg/client"
    41  	"sigs.k8s.io/controller-runtime/pkg/envtest"
    42  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    43  	"sigs.k8s.io/controller-runtime/pkg/log/zap"
    44  
    45  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    46  	dpv1alpha1 "github.com/1aal/kubeblocks/apis/dataprotection/v1alpha1"
    47  	"github.com/1aal/kubeblocks/controllers/apps"
    48  	dpctrl "github.com/1aal/kubeblocks/controllers/dataprotection"
    49  	"github.com/1aal/kubeblocks/controllers/k8score"
    50  	"github.com/1aal/kubeblocks/pkg/common"
    51  	cfgcore "github.com/1aal/kubeblocks/pkg/configuration/core"
    52  	"github.com/1aal/kubeblocks/pkg/constant"
    53  	intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    54  	"github.com/1aal/kubeblocks/pkg/testutil"
    55  	testapps "github.com/1aal/kubeblocks/pkg/testutil/apps"
    56  	viper "github.com/1aal/kubeblocks/pkg/viperx"
    57  	"github.com/1aal/kubeblocks/test/testutils"
    58  )
    59  
    60  // These tests use Ginkgo (BDD-style Go testing framework). Refer to
    61  // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
    62  
    63  var cfg *rest.Config
    64  var k8sClient client.Client
    65  var dynamicClient dynamic.Interface
    66  var testEnv *envtest.Environment
    67  var ctx context.Context
    68  var cancel context.CancelFunc
    69  var testCtx testutil.TestContext
    70  var clusterRecorder record.EventRecorder
    71  var logger logr.Logger
    72  
    73  func init() {
    74  	viper.AutomaticEnv()
    75  	// viper.Set("ENABLE_DEBUG_LOG", "true")
    76  }
    77  
    78  func TestIntegrationController(t *testing.T) {
    79  	if testing.Short() {
    80  		t.Skip()
    81  	}
    82  
    83  	RegisterFailHandler(Fail)
    84  
    85  	RunSpecs(t, "Integration Test Suite")
    86  }
    87  
    88  // GetConsensusRoleCountMap gets a role:count map from a consensusSet cluster
    89  func GetConsensusRoleCountMap(testCtx testutil.TestContext, k8sClient client.Client, cluster *appsv1alpha1.Cluster) (roleCountMap map[string]int) {
    90  	clusterkey := client.ObjectKeyFromObject(cluster)
    91  	stsList := &appsv1.StatefulSetList{}
    92  	err := testCtx.Cli.List(testCtx.Ctx, stsList, client.MatchingLabels{
    93  		constant.AppInstanceLabelKey: clusterkey.Name,
    94  	}, client.InNamespace(clusterkey.Namespace))
    95  
    96  	roleCountMap = make(map[string]int)
    97  	roleCountMap["leader"] = 0
    98  	roleCountMap["follower"] = 0
    99  	roleCountMap["learner"] = 0
   100  
   101  	if err != nil || len(stsList.Items) == 0 {
   102  		return roleCountMap
   103  	}
   104  
   105  	sts := stsList.Items[0]
   106  	pods, err := common.GetPodListByStatefulSet(testCtx.Ctx, k8sClient, &sts)
   107  
   108  	if err != nil {
   109  		return roleCountMap
   110  	}
   111  
   112  	for _, pod := range pods {
   113  		role := pod.Labels[constant.RoleLabelKey]
   114  		roleCountMap[role]++
   115  	}
   116  
   117  	return roleCountMap
   118  }
   119  
   120  func CreateSimpleConsensusMySQLClusterWithConfig(
   121  	testCtx testutil.TestContext,
   122  	clusterDefName,
   123  	clusterVersionName,
   124  	clusterName,
   125  	mysqlConfigTemplatePath,
   126  	mysqlConfigConstraintPath,
   127  	mysqlScriptsPath string) (
   128  	*appsv1alpha1.ClusterDefinition, *appsv1alpha1.ClusterVersion, *appsv1alpha1.Cluster) {
   129  	const mysqlCompName = "mysql"
   130  	const mysqlCompDefName = "mysql"
   131  	const mysqlConfigName = "mysql-component-config"
   132  	const mysqlConfigConstraintName = "mysql8.0-config-constraints"
   133  	const mysqlScriptsConfigName = "apecloud-mysql-scripts"
   134  	const mysqlDataVolumeName = "data"
   135  	const mysqlConfigVolumeName = "mysql-config"
   136  	const mysqlScriptsVolumeName = "scripts"
   137  	const mysqlErrorFilePath = "/data/mysql/log/mysqld-error.log"
   138  	const mysqlGeneralFilePath = "/data/mysql/log/mysqld.log"
   139  	const mysqlSlowlogFilePath = "/data/mysql/log/mysqld-slowquery.log"
   140  
   141  	mysqlConsensusType := string(testapps.ConsensusMySQLComponent)
   142  
   143  	configmap := testapps.CreateCustomizedObj(&testCtx,
   144  		mysqlConfigTemplatePath, &corev1.ConfigMap{},
   145  		testCtx.UseDefaultNamespace(),
   146  		testapps.WithLabels(
   147  			constant.AppNameLabelKey, clusterName,
   148  			constant.AppInstanceLabelKey, clusterName,
   149  			constant.KBAppComponentLabelKey, mysqlConsensusType,
   150  			constant.CMConfigurationTemplateNameLabelKey, mysqlConfigName,
   151  			constant.CMConfigurationConstraintsNameLabelKey, mysqlConfigConstraintName,
   152  			constant.CMConfigurationSpecProviderLabelKey, mysqlConfigName,
   153  			constant.CMConfigurationTypeLabelKey, constant.ConfigInstanceType,
   154  		))
   155  
   156  	_ = testapps.CreateCustomizedObj(&testCtx, mysqlScriptsPath, &corev1.ConfigMap{},
   157  		testapps.WithName(mysqlScriptsConfigName), testCtx.UseDefaultNamespace())
   158  
   159  	By("Create a constraint obj")
   160  	constraint := testapps.CreateCustomizedObj(&testCtx,
   161  		mysqlConfigConstraintPath,
   162  		&appsv1alpha1.ConfigConstraint{})
   163  
   164  	mysqlVolumeMounts := []corev1.VolumeMount{
   165  		{
   166  			Name:      mysqlConfigVolumeName,
   167  			MountPath: "/opt/mysql",
   168  		},
   169  		{
   170  			Name:      mysqlScriptsVolumeName,
   171  			MountPath: "/scripts",
   172  		},
   173  		{
   174  			Name:      mysqlDataVolumeName,
   175  			MountPath: "/data/mysql",
   176  		},
   177  	}
   178  
   179  	By("Create a clusterDefinition obj")
   180  	mode := int32(0755)
   181  	clusterDefObj := testapps.NewClusterDefFactory(clusterDefName).
   182  		SetConnectionCredential(map[string]string{"username": "root", "password": ""}, nil).
   183  		AddComponentDef(testapps.ConsensusMySQLComponent, mysqlCompDefName).
   184  		AddConfigTemplate(mysqlConfigName, configmap.Name, constraint.Name,
   185  			testCtx.DefaultNamespace, mysqlConfigVolumeName).
   186  		AddScriptTemplate(mysqlScriptsConfigName, mysqlScriptsConfigName,
   187  			testCtx.DefaultNamespace, mysqlScriptsVolumeName, &mode).
   188  		AddContainerVolumeMounts(testapps.DefaultMySQLContainerName, mysqlVolumeMounts).
   189  		AddLogConfig("error", mysqlErrorFilePath).
   190  		AddLogConfig("general", mysqlGeneralFilePath).
   191  		AddLogConfig("slow", mysqlSlowlogFilePath).
   192  		AddLabels(cfgcore.GenerateTPLUniqLabelKeyWithConfig(mysqlConfigName), configmap.Name,
   193  			cfgcore.GenerateConstraintsUniqLabelKeyWithConfig(constraint.Name), constraint.Name).
   194  		AddContainerEnv(testapps.DefaultMySQLContainerName, corev1.EnvVar{Name: "MYSQL_ALLOW_EMPTY_PASSWORD", Value: "yes"}).
   195  		AddContainerEnv(testapps.DefaultMySQLContainerName, corev1.EnvVar{Name: "CLUSTER_START_INDEX", Value: "1"}).
   196  		AddContainerEnv(testapps.DefaultMySQLContainerName, corev1.EnvVar{Name: "CLUSTER_ID", Value: "1"}).
   197  		Create(&testCtx).GetObject()
   198  
   199  	By("Create a clusterVersion obj")
   200  	clusterVersionObj := testapps.NewClusterVersionFactory(clusterVersionName, clusterDefObj.GetName()).
   201  		AddComponentVersion(mysqlCompDefName).
   202  		AddContainerShort(testapps.DefaultMySQLContainerName, testapps.ApeCloudMySQLImage).
   203  		AddLabels(cfgcore.GenerateTPLUniqLabelKeyWithConfig(mysqlConfigName), configmap.Name,
   204  			cfgcore.GenerateConstraintsUniqLabelKeyWithConfig(constraint.Name), constraint.Name).
   205  		Create(&testCtx).GetObject()
   206  
   207  	By("Creating a cluster")
   208  	pvcSpec := appsv1alpha1.PersistentVolumeClaimSpec{
   209  		AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
   210  		Resources: corev1.ResourceRequirements{
   211  			Requests: corev1.ResourceList{
   212  				corev1.ResourceStorage: resource.MustParse("1Gi"),
   213  			},
   214  		},
   215  	}
   216  	clusterObj := testapps.NewClusterFactory(testCtx.DefaultNamespace, clusterName,
   217  		clusterDefObj.Name, clusterVersionObj.Name).
   218  		AddComponent(mysqlCompName, mysqlCompDefName).
   219  		SetReplicas(3).
   220  		SetEnabledLogs("error", "general", "slow").
   221  		AddVolumeClaimTemplate(testapps.DataVolumeName, pvcSpec).
   222  		Create(&testCtx).GetObject()
   223  
   224  	return clusterDefObj, clusterVersionObj, clusterObj
   225  }
   226  
   227  var _ = BeforeSuite(func() {
   228  	if viper.GetBool("ENABLE_DEBUG_LOG") {
   229  		logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true), func(o *zap.Options) {
   230  			o.TimeEncoder = zapcore.ISO8601TimeEncoder
   231  		}))
   232  	}
   233  
   234  	ctx, cancel = context.WithCancel(context.TODO())
   235  	logger = logf.FromContext(ctx).WithValues()
   236  	logger.Info("logger start")
   237  
   238  	By("bootstrapping test environment")
   239  	var flag = true
   240  	testEnv = &envtest.Environment{
   241  		CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases"),
   242  			// use dependent external CRDs.
   243  			// resolved by ref: https://github.com/operator-framework/operator-sdk/issues/4434#issuecomment-786794418
   244  			filepath.Join(build.Default.GOPATH, "pkg", "mod", "github.com", "kubernetes-csi/external-snapshotter/",
   245  				"client/v6@v6.2.0", "config", "crd")},
   246  		ErrorIfCRDPathMissing: true,
   247  		UseExistingCluster:    &flag,
   248  	}
   249  
   250  	var err error
   251  	// cfg is defined in this file globally.
   252  	cfg, err = testEnv.Start()
   253  	Expect(err).NotTo(HaveOccurred())
   254  	Expect(cfg).NotTo(BeNil())
   255  
   256  	err = appsv1alpha1.AddToScheme(scheme.Scheme)
   257  	Expect(err).NotTo(HaveOccurred())
   258  
   259  	err = dpv1alpha1.AddToScheme(scheme.Scheme)
   260  	Expect(err).NotTo(HaveOccurred())
   261  
   262  	err = snapshotv1.AddToScheme(scheme.Scheme)
   263  	Expect(err).NotTo(HaveOccurred())
   264  
   265  	// +kubebuilder:scaffold:scheme
   266  
   267  	k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
   268  	Expect(err).NotTo(HaveOccurred())
   269  	Expect(k8sClient).NotTo(BeNil())
   270  
   271  	dynamicClient, err = testutils.NewFactory().DynamicClient()
   272  	Expect(err).NotTo(HaveOccurred())
   273  	Expect(dynamicClient).NotTo(BeNil())
   274  
   275  	// run reconcile
   276  	k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
   277  		Scheme:                scheme.Scheme,
   278  		MetricsBindAddress:    "0",
   279  		ClientDisableCacheFor: intctrlutil.GetUncachedObjects(),
   280  	})
   281  	Expect(err).ToNot(HaveOccurred())
   282  
   283  	viper.SetDefault("CERT_DIR", "/tmp/k8s-webhook-server/serving-certs")
   284  	viper.SetDefault(constant.KBToolsImage, "apecloud/kubeblocks-tools:latest")
   285  	viper.SetDefault("PROBE_SERVICE_PORT", 3501)
   286  	viper.SetDefault("PROBE_SERVICE_LOG_LEVEL", "info")
   287  
   288  	clusterRecorder = k8sManager.GetEventRecorderFor("db-cluster-controller")
   289  	err = (&apps.ClusterReconciler{
   290  		Client:   k8sManager.GetClient(),
   291  		Scheme:   k8sManager.GetScheme(),
   292  		Recorder: clusterRecorder,
   293  	}).SetupWithManager(k8sManager)
   294  	Expect(err).ToNot(HaveOccurred())
   295  
   296  	err = (&apps.ClusterDefinitionReconciler{
   297  		Client:   k8sManager.GetClient(),
   298  		Scheme:   k8sManager.GetScheme(),
   299  		Recorder: k8sManager.GetEventRecorderFor("cluster-definition-controller"),
   300  	}).SetupWithManager(k8sManager)
   301  	Expect(err).ToNot(HaveOccurred())
   302  
   303  	err = (&apps.ClusterVersionReconciler{
   304  		Client:   k8sManager.GetClient(),
   305  		Scheme:   k8sManager.GetScheme(),
   306  		Recorder: k8sManager.GetEventRecorderFor("cluster-version-controller"),
   307  	}).SetupWithManager(k8sManager)
   308  	Expect(err).ToNot(HaveOccurred())
   309  
   310  	err = (&apps.OpsRequestReconciler{
   311  		Client:   k8sManager.GetClient(),
   312  		Scheme:   k8sManager.GetScheme(),
   313  		Recorder: k8sManager.GetEventRecorderFor("ops-request-controller"),
   314  	}).SetupWithManager(k8sManager)
   315  	Expect(err).ToNot(HaveOccurred())
   316  
   317  	err = (&apps.SystemAccountReconciler{
   318  		Client:   k8sManager.GetClient(),
   319  		Scheme:   k8sManager.GetScheme(),
   320  		Recorder: k8sManager.GetEventRecorderFor("system-account-controller"),
   321  	}).SetupWithManager(k8sManager)
   322  	Expect(err).ToNot(HaveOccurred())
   323  
   324  	err = (&k8score.EventReconciler{
   325  		Client:   k8sManager.GetClient(),
   326  		Scheme:   k8sManager.GetScheme(),
   327  		Recorder: k8sManager.GetEventRecorderFor("event-controller"),
   328  	}).SetupWithManager(k8sManager)
   329  	Expect(err).ToNot(HaveOccurred())
   330  
   331  	err = (&dpctrl.BackupReconciler{
   332  		Client:   k8sManager.GetClient(),
   333  		Scheme:   k8sManager.GetScheme(),
   334  		Recorder: k8sManager.GetEventRecorderFor("backup-controller"),
   335  	}).SetupWithManager(k8sManager)
   336  	Expect(err).ToNot(HaveOccurred())
   337  
   338  	err = (&dpctrl.BackupScheduleReconciler{
   339  		Client:   k8sManager.GetClient(),
   340  		Scheme:   k8sManager.GetScheme(),
   341  		Recorder: k8sManager.GetEventRecorderFor("backup-policy-controller"),
   342  	}).SetupWithManager(k8sManager)
   343  	Expect(err).ToNot(HaveOccurred())
   344  
   345  	err = (&dpctrl.ActionSetReconciler{
   346  		Client:   k8sManager.GetClient(),
   347  		Scheme:   k8sManager.GetScheme(),
   348  		Recorder: k8sManager.GetEventRecorderFor("backup-tool-controller"),
   349  	}).SetupWithManager(k8sManager)
   350  	Expect(err).ToNot(HaveOccurred())
   351  
   352  	// pulling docker images is slow
   353  	viper.SetDefault("EventuallyTimeout", time.Second*300)
   354  	testCtx = testutil.NewDefaultTestContext(ctx, k8sClient, testEnv)
   355  
   356  	go func() {
   357  		defer GinkgoRecover()
   358  		err = k8sManager.Start(ctx)
   359  		Expect(err).ToNot(HaveOccurred(), "failed to run manager")
   360  	}()
   361  })
   362  
   363  var _ = AfterSuite(func() {
   364  	cancel()
   365  	By("tearing down the test environment")
   366  	err := testEnv.Stop()
   367  	Expect(err).NotTo(HaveOccurred())
   368  })