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 })