diff --git a/cmd/manager/main.go b/cmd/manager/main.go
index ec42b1170d9..66ab14cee1b 100644
--- a/cmd/manager/main.go
+++ b/cmd/manager/main.go
@@ -61,6 +61,8 @@ import (
workloadsv1 "github.com/apecloud/kubeblocks/apis/workloads/v1"
workloadsv1alpha1 "github.com/apecloud/kubeblocks/apis/workloads/v1alpha1"
appscontrollers "github.com/apecloud/kubeblocks/controllers/apps"
+ "github.com/apecloud/kubeblocks/controllers/apps/cluster"
+ "github.com/apecloud/kubeblocks/controllers/apps/component"
"github.com/apecloud/kubeblocks/controllers/apps/configuration"
experimentalcontrollers "github.com/apecloud/kubeblocks/controllers/experimental"
extensionscontrollers "github.com/apecloud/kubeblocks/controllers/extensions"
@@ -443,7 +445,7 @@ func main() {
os.Exit(1)
}
- if err = (&appscontrollers.ClusterReconciler{
+ if err = (&cluster.ClusterReconciler{
Client: client,
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("cluster-controller"),
@@ -453,7 +455,7 @@ func main() {
os.Exit(1)
}
- if err = (&appscontrollers.ComponentReconciler{
+ if err = (&component.ComponentReconciler{
Client: client,
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorderFor("component-controller"),
diff --git a/controllers/apps/cluster_controller.go b/controllers/apps/cluster/cluster_controller.go
similarity index 99%
rename from controllers/apps/cluster_controller.go
rename to controllers/apps/cluster/cluster_controller.go
index 9c8dff1e7cb..d6edbccac60 100644
--- a/controllers/apps/cluster_controller.go
+++ b/controllers/apps/cluster/cluster_controller.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"context"
diff --git a/controllers/apps/cluster_controller_test.go b/controllers/apps/cluster/cluster_controller_test.go
similarity index 95%
rename from controllers/apps/cluster_controller_test.go
rename to controllers/apps/cluster/cluster_controller_test.go
index 38f5578816e..0f17c67cfa4 100644
--- a/controllers/apps/cluster_controller_test.go
+++ b/controllers/apps/cluster/cluster_controller_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"encoding/json"
@@ -422,7 +422,7 @@ var _ = Describe("Cluster Controller", func() {
clusterKey = client.ObjectKeyFromObject(clusterObj)
By("waiting for the cluster controller to create resources completely")
- waitForCreatingResourceCompletely(clusterKey, compName, otherCompName)
+ waitForCreatingResourceCompletely(clusterKey)
By("scale in the target component")
Expect(testapps.GetAndChangeObj(&testCtx, clusterKey, func(cluster *appsv1.Cluster) {
@@ -512,19 +512,6 @@ var _ = Describe("Cluster Controller", func() {
g.Expect(svc.Spec.Selector).Should(HaveKeyWithValue(constant.RoleLabelKey, constant.Follower))
g.Expect(svc.Spec.ExternalTrafficPolicy).Should(BeEquivalentTo(corev1.ServiceExternalTrafficPolicyTypeLocal))
})).Should(Succeed())
-
- By("check default component service created")
- compSvcKey := types.NamespacedName{
- Namespace: clusterKey.Namespace,
- Name: constant.GenerateComponentServiceName(clusterObj.Name, compName, ""),
- }
- Eventually(testapps.CheckObj(&testCtx, compSvcKey, func(g Gomega, svc *corev1.Service) {
- g.Expect(svc.Spec.Selector).Should(HaveKeyWithValue(constant.AppManagedByLabelKey, constant.AppName))
- g.Expect(svc.Spec.Selector).Should(HaveKeyWithValue(constant.AppInstanceLabelKey, clusterObj.Name))
- g.Expect(svc.Spec.Selector).Should(HaveKeyWithValue(constant.KBAppComponentLabelKey, compName))
- g.Expect(svc.Spec.Selector).Should(HaveKey(constant.RoleLabelKey))
- g.Expect(svc.Spec.Selector).Should(HaveKeyWithValue(constant.RoleLabelKey, constant.Leader))
- })).Should(Succeed())
}
type expectService struct {
@@ -1199,7 +1186,8 @@ var _ = Describe("Cluster Controller", func() {
})).Should(Succeed())
})
- Context("cluster component annotations and labels", func() {
+ // TODO: refactor the case and should not depend on objects created by the component controller
+ PContext("cluster component annotations and labels", func() {
BeforeEach(func() {
cleanEnv()
createAllDefinitionObjects()
@@ -1216,21 +1204,22 @@ var _ = Describe("Cluster Controller", func() {
(*metaMap)[key] = value
}
- checkRelatedObject := func(compName string, checkFunc func(g Gomega, obj client.Object)) {
- // check related services of the component
- defaultSvcName := constant.GenerateComponentServiceName(clusterObj.Name, compName, "")
- Eventually(testapps.CheckObj(&testCtx, client.ObjectKey{Name: defaultSvcName,
- Namespace: testCtx.DefaultNamespace}, func(g Gomega, svc *corev1.Service) {
- checkFunc(g, svc)
- })).Should(Succeed())
-
- // check related account secret of the component
- rootAccountSecretName := constant.GenerateAccountSecretName(clusterObj.Name, compName, "root")
- Eventually(testapps.CheckObj(&testCtx, client.ObjectKey{Name: rootAccountSecretName,
- Namespace: testCtx.DefaultNamespace}, func(g Gomega, secret *corev1.Secret) {
- checkFunc(g, secret)
- })).Should(Succeed())
- }
+ // TODO: remove it
+ // checkRelatedObject := func(compName string, checkFunc func(g Gomega, obj client.Object)) {
+ // // check related services of the component
+ // defaultSvcName := constant.GenerateComponentServiceName(clusterObj.Name, compName, "")
+ // Eventually(testapps.CheckObj(&testCtx, client.ObjectKey{Name: defaultSvcName,
+ // Namespace: testCtx.DefaultNamespace}, func(g Gomega, svc *corev1.Service) {
+ // checkFunc(g, svc)
+ // })).Should(Succeed())
+ //
+ // // check related account secret of the component
+ // rootAccountSecretName := constant.GenerateAccountSecretName(clusterObj.Name, compName, "root")
+ // Eventually(testapps.CheckObj(&testCtx, client.ObjectKey{Name: rootAccountSecretName,
+ // Namespace: testCtx.DefaultNamespace}, func(g Gomega, secret *corev1.Secret) {
+ // checkFunc(g, secret)
+ // })).Should(Succeed())
+ // }
testUpdateAnnoAndLabels := func(compName string,
changeCluster func(cluster *appsv1.Cluster),
@@ -1247,17 +1236,19 @@ var _ = Describe("Cluster Controller", func() {
checkWorkloadFunc(g, compObj.Spec.Labels, compObj.Spec.Annotations, false)
})).Should(Succeed())
- By("check related objects annotations and labels")
- checkRelatedObject(defaultCompName, func(g Gomega, obj client.Object) {
- checkRelatedObjFunc(g, obj)
- })
-
- By("InstanceSet.spec.template.annotations/labels need to be consistent with component")
- // The labels and annotations of the Pod will be kept consistent with those of the InstanceSet
- Eventually(testapps.CheckObj(&testCtx, client.ObjectKey{Name: workloadName, Namespace: testCtx.DefaultNamespace},
- func(g Gomega, instanceSet *workloadsv1.InstanceSet) {
- checkWorkloadFunc(g, instanceSet.Spec.Template.GetLabels(), instanceSet.Spec.Template.GetAnnotations(), true)
- })).Should(Succeed())
+ // TODO: remove it
+ // By("check related objects annotations and labels")
+ // checkRelatedObject(defaultCompName, func(g Gomega, obj client.Object) {
+ // checkRelatedObjFunc(g, obj)
+ // })
+
+ // TODO: remove it
+ // By("InstanceSet.spec.template.annotations/labels need to be consistent with component")
+ //// The labels and annotations of the Pod will be kept consistent with those of the InstanceSet
+ // Eventually(testapps.CheckObj(&testCtx, client.ObjectKey{Name: workloadName, Namespace: testCtx.DefaultNamespace},
+ // func(g Gomega, instanceSet *workloadsv1.InstanceSet) {
+ // checkWorkloadFunc(g, instanceSet.Spec.Template.GetLabels(), instanceSet.Spec.Template.GetAnnotations(), true)
+ // })).Should(Succeed())
}
It("test add/override annotations and labels", func() {
@@ -1342,7 +1333,6 @@ var _ = Describe("Cluster Controller", func() {
g.Expect(obj.GetLabels()[key2]).Should(Equal(value2))
g.Expect(obj.GetAnnotations()[key2]).Should(Equal(value2))
})
-
})
})
})
diff --git a/controllers/apps/cluster_plan_builder.go b/controllers/apps/cluster/cluster_plan_builder.go
similarity index 98%
rename from controllers/apps/cluster_plan_builder.go
rename to controllers/apps/cluster/cluster_plan_builder.go
index edc3b3ab218..fcf086161f2 100644
--- a/controllers/apps/cluster_plan_builder.go
+++ b/controllers/apps/cluster/cluster_plan_builder.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"context"
@@ -194,11 +194,6 @@ func (c *clusterPlanBuilder) AddTransformer(transformer ...graph.Transformer) gr
return c
}
-func (c *clusterPlanBuilder) AddParallelTransformer(transformer ...graph.Transformer) graph.PlanBuilder {
- c.transformers = append(c.transformers, &ParallelTransformers{transformers: transformer})
- return c
-}
-
// Build runs all transformers to generate a plan
func (c *clusterPlanBuilder) Build() (graph.Plan, error) {
var err error
diff --git a/controllers/apps/cluster_plan_builder_test.go b/controllers/apps/cluster/cluster_plan_builder_test.go
similarity index 99%
rename from controllers/apps/cluster_plan_builder_test.go
rename to controllers/apps/cluster/cluster_plan_builder_test.go
index 5e214f8215d..43e9bacfb27 100644
--- a/controllers/apps/cluster_plan_builder_test.go
+++ b/controllers/apps/cluster/cluster_plan_builder_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
. "github.com/onsi/ginkgo/v2"
diff --git a/controllers/apps/cluster_status_conditions.go b/controllers/apps/cluster/cluster_status_conditions.go
similarity index 99%
rename from controllers/apps/cluster_status_conditions.go
rename to controllers/apps/cluster/cluster_status_conditions.go
index a016853a744..306d2dae8b6 100644
--- a/controllers/apps/cluster_status_conditions.go
+++ b/controllers/apps/cluster/cluster_status_conditions.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"fmt"
diff --git a/controllers/apps/cluster/suite_test.go b/controllers/apps/cluster/suite_test.go
new file mode 100644
index 00000000000..f7aa2991958
--- /dev/null
+++ b/controllers/apps/cluster/suite_test.go
@@ -0,0 +1,275 @@
+/*
+Copyright (C) 2022-2024 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package cluster
+
+import (
+ "context"
+ "fmt"
+ "go/build"
+ "path/filepath"
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/go-logr/logr"
+ snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
+ "go.uber.org/zap/zapcore"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/record"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+ "sigs.k8s.io/controller-runtime/pkg/metrics/server"
+
+ appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+ appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1"
+ appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1"
+ dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
+ opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1"
+ workloadsv1 "github.com/apecloud/kubeblocks/apis/workloads/v1"
+ "github.com/apecloud/kubeblocks/controllers/apps"
+ "github.com/apecloud/kubeblocks/controllers/apps/configuration"
+ "github.com/apecloud/kubeblocks/controllers/dataprotection"
+ "github.com/apecloud/kubeblocks/controllers/k8score"
+ "github.com/apecloud/kubeblocks/pkg/constant"
+ "github.com/apecloud/kubeblocks/pkg/controller/model"
+ intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
+ "github.com/apecloud/kubeblocks/pkg/testutil"
+ viper "github.com/apecloud/kubeblocks/pkg/viperx"
+)
+
+// These tests use Ginkgo (BDD-style Go testing framework). Refer to
+// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
+
+const (
+ testDataPlaneNodeAffinityKey = "testDataPlaneNodeAffinityKey"
+ testDataPlaneTolerationKey = "testDataPlaneTolerationKey"
+)
+
+var cfg *rest.Config
+var k8sClient client.Client
+var testEnv *envtest.Environment
+var ctx context.Context
+var cancel context.CancelFunc
+var testCtx testutil.TestContext
+var clusterRecorder record.EventRecorder
+var logger logr.Logger
+
+func init() {
+ viper.AutomaticEnv()
+ // viper.Set("ENABLE_DEBUG_LOG", "true")
+}
+
+func TestAPIs(t *testing.T) {
+ RegisterFailHandler(Fail)
+
+ RunSpecs(t, "Controller Suite")
+}
+
+var _ = BeforeSuite(func() {
+ if viper.GetBool("ENABLE_DEBUG_LOG") {
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true), func(o *zap.Options) {
+ o.TimeEncoder = zapcore.ISO8601TimeEncoder
+ }))
+ } else {
+ logf.SetLogger(logr.New(logf.NullLogSink{}))
+ }
+
+ viper.SetDefault(constant.CfgKeyCtrlrReconcileRetryDurationMS, 10)
+ viper.Set(constant.CfgKeyDataPlaneTolerations,
+ fmt.Sprintf("[{\"key\":\"%s\", \"operator\": \"Exists\", \"effect\": \"NoSchedule\"}]", testDataPlaneTolerationKey))
+ viper.Set(constant.CfgKeyDataPlaneAffinity,
+ fmt.Sprintf("{\"nodeAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"preference\":{\"matchExpressions\":[{\"key\":\"%s\",\"operator\":\"In\",\"values\":[\"true\"]}]},\"weight\":100}]}}", testDataPlaneNodeAffinityKey))
+
+ ctx, cancel = context.WithCancel(context.TODO())
+ logger = logf.FromContext(ctx).WithValues()
+ logger.Info("logger start")
+
+ By("bootstrapping test environment")
+ testEnv = &envtest.Environment{
+ CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases"),
+ // use dependent external CRDs.
+ // resolved by ref: https://github.com/operator-framework/operator-sdk/issues/4434#issuecomment-786794418
+ filepath.Join(build.Default.GOPATH, "pkg", "mod", "github.com", "kubernetes-csi/external-snapshotter/",
+ "client/v6@v6.2.0", "config", "crd"),
+ },
+ ErrorIfCRDPathMissing: true,
+ }
+
+ var err error
+ // cfg is defined in this file globally.
+ cfg, err = testEnv.Start()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(cfg).NotTo(BeNil())
+
+ err = appsv1alpha1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(appsv1alpha1.AddToScheme)
+
+ err = opsv1alpha1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(opsv1alpha1.AddToScheme)
+
+ err = appsv1beta1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(appsv1beta1.AddToScheme)
+
+ err = appsv1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(appsv1.AddToScheme)
+
+ err = dpv1alpha1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(dpv1alpha1.AddToScheme)
+
+ err = snapshotv1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(snapshotv1.AddToScheme)
+
+ err = workloadsv1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(workloadsv1.AddToScheme)
+
+ // +kubebuilder:scaffold:rscheme
+
+ k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient).NotTo(BeNil())
+
+ // run reconcile
+ k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
+ Scheme: scheme.Scheme,
+ Metrics: server.Options{BindAddress: "0"},
+ Client: client.Options{
+ Cache: &client.CacheOptions{
+ DisableFor: intctrlutil.GetUncachedObjects(),
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ viper.SetDefault("CERT_DIR", "/tmp/k8s-webhook-server/serving-certs")
+ viper.SetDefault(constant.KBToolsImage, "docker.io/apecloud/kubeblocks-tools:latest")
+ viper.SetDefault("PROBE_SERVICE_PORT", 3501)
+ viper.SetDefault("PROBE_SERVICE_LOG_LEVEL", "info")
+ viper.SetDefault("CM_NAMESPACE", "default")
+ viper.SetDefault("HOST_PORT_CM_NAME", "kubeblocks-host-ports")
+ viper.SetDefault(constant.EnableRBACManager, true)
+
+ err = intctrlutil.InitHostPortManager(k8sClient)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ClusterDefinitionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("cluster-definition-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ShardingDefinitionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("sharding-definition-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ComponentDefinitionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("component-definition-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ComponentVersionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("component-version-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.SidecarDefinitionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("sidecar-definition-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ clusterRecorder = k8sManager.GetEventRecorderFor("cluster-controller")
+ err = (&ClusterReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: clusterRecorder,
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ServiceDescriptorReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("service-descriptor-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&k8score.EventReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("event-controller"),
+ }).SetupWithManager(k8sManager, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&configuration.ConfigConstraintReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("configuration-template-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&configuration.ConfigurationReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("configuration-controller"),
+ }).SetupWithManager(k8sManager, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&dataprotection.BackupPolicyTemplateReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("backup-policy-template-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ testCtx = testutil.NewDefaultTestContext(ctx, k8sClient, testEnv)
+
+ go func() {
+ defer GinkgoRecover()
+ err = k8sManager.Start(ctx)
+ Expect(err).ToNot(HaveOccurred(), "failed to run manager")
+ }()
+})
+
+var _ = AfterSuite(func() {
+ cancel()
+ By("tearing down the test environment")
+ err := testEnv.Stop()
+ Expect(err).NotTo(HaveOccurred())
+})
diff --git a/controllers/apps/cluster/test_utils.go b/controllers/apps/cluster/test_utils.go
new file mode 100644
index 00000000000..58f64de91aa
--- /dev/null
+++ b/controllers/apps/cluster/test_utils.go
@@ -0,0 +1,72 @@
+/*
+Copyright (C) 2022-2024 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package cluster
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type mockReader struct {
+ objs []client.Object
+}
+
+func (r *mockReader) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
+ for _, o := range r.objs {
+ // ignore the GVK check
+ if client.ObjectKeyFromObject(o) == key {
+ reflect.ValueOf(obj).Elem().Set(reflect.ValueOf(o).Elem())
+ return nil
+ }
+ }
+ return apierrors.NewNotFound(schema.GroupResource{}, key.Name)
+}
+
+func (r *mockReader) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
+ items := reflect.ValueOf(list).Elem().FieldByName("Items")
+ if !items.IsValid() {
+ return fmt.Errorf("ObjectList has no Items field: %s", list.GetObjectKind().GroupVersionKind().String())
+ }
+
+ objs := reflect.MakeSlice(items.Type(), 0, 0)
+ if len(r.objs) > 0 {
+ listOpts := &client.ListOptions{}
+ for _, opt := range opts {
+ opt.ApplyToList(listOpts)
+ }
+
+ for i, o := range r.objs {
+ if reflect.TypeOf(r.objs[i]).Elem().AssignableTo(items.Type().Elem()) {
+ if listOpts.LabelSelector == nil || listOpts.LabelSelector.Matches(labels.Set(o.GetLabels())) {
+ objs = reflect.Append(objs, reflect.ValueOf(r.objs[i]).Elem())
+ }
+ }
+ }
+ }
+ items.Set(objs)
+
+ return nil
+}
diff --git a/controllers/apps/transformer_cluster_backup_policy.go b/controllers/apps/cluster/transformer_cluster_backup_policy.go
similarity index 99%
rename from controllers/apps/transformer_cluster_backup_policy.go
rename to controllers/apps/cluster/transformer_cluster_backup_policy.go
index f6867accd71..70d2ca64b69 100644
--- a/controllers/apps/transformer_cluster_backup_policy.go
+++ b/controllers/apps/cluster/transformer_cluster_backup_policy.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"context"
@@ -645,7 +645,7 @@ func (r *backupPolicyBuilder) mergeClusterBackup(
func (r *backupPolicyBuilder) buildAnnotations() map[string]string {
annotations := map[string]string{
- dptypes.DefaultBackupPolicyAnnotationKey: trueVal,
+ dptypes.DefaultBackupPolicyAnnotationKey: "true",
constant.BackupPolicyTemplateAnnotationKey: r.backupPolicyTPL.Name,
}
if r.backupPolicyTPL.Annotations[dptypes.ReconfigureRefAnnotationKey] != "" {
diff --git a/controllers/apps/transformer_cluster_component.go b/controllers/apps/cluster/transformer_cluster_component.go
similarity index 99%
rename from controllers/apps/transformer_cluster_component.go
rename to controllers/apps/cluster/transformer_cluster_component.go
index 49db7eedb8e..9912135f803 100644
--- a/controllers/apps/transformer_cluster_component.go
+++ b/controllers/apps/cluster/transformer_cluster_component.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"context"
@@ -761,7 +761,7 @@ func (h *clusterShardingHandler) deleteComp(transCtx *clusterTransformContext,
if comp.Annotations == nil {
comp.Annotations = make(map[string]string)
}
- comp.Annotations[constant.ComponentScaleInAnnotationKey] = trueVal
+ comp.Annotations[constant.ComponentScaleInAnnotationKey] = "true"
graphCli.Do(dag, compCopy, comp, model.ActionUpdatePtr(), vertex)
}
}
diff --git a/controllers/apps/transformer_cluster_component_status.go b/controllers/apps/cluster/transformer_cluster_component_status.go
similarity index 96%
rename from controllers/apps/transformer_cluster_component_status.go
rename to controllers/apps/cluster/transformer_cluster_component_status.go
index 2382ff47d7f..73debda941d 100644
--- a/controllers/apps/transformer_cluster_component_status.go
+++ b/controllers/apps/cluster/transformer_cluster_component_status.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"fmt"
@@ -34,6 +34,11 @@ import (
"github.com/apecloud/kubeblocks/pkg/controller/model"
)
+const (
+ // clusterCompPhaseTransition the event reason indicates that the cluster component transits to a new phase.
+ clusterCompPhaseTransition = "ClusterComponentPhaseTransition"
+)
+
// clusterComponentStatusTransformer transforms cluster components' status.
type clusterComponentStatusTransformer struct{}
@@ -171,7 +176,7 @@ func (t *clusterComponentStatusTransformer) buildClusterCompStatus(transCtx *clu
if phase != status.Phase {
msg := clusterCompNShardingPhaseTransitionMsg("component", compName, status.Phase)
if transCtx.GetRecorder() != nil && msg != "" {
- transCtx.GetRecorder().Eventf(transCtx.Cluster, corev1.EventTypeNormal, componentPhaseTransition, msg)
+ transCtx.GetRecorder().Eventf(transCtx.Cluster, corev1.EventTypeNormal, clusterCompPhaseTransition, msg)
}
transCtx.GetLogger().Info(fmt.Sprintf("cluster component phase transition: %s -> %s (%s)", phase, status.Phase, msg))
}
@@ -239,7 +244,7 @@ func (t *clusterComponentStatusTransformer) buildClusterShardingStatus(transCtx
if phase != status.Phase {
msg := clusterCompNShardingPhaseTransitionMsg("sharding", shardingName, status.Phase)
if transCtx.GetRecorder() != nil && msg != "" {
- transCtx.GetRecorder().Eventf(transCtx.Cluster, corev1.EventTypeNormal, componentPhaseTransition, msg)
+ transCtx.GetRecorder().Eventf(transCtx.Cluster, corev1.EventTypeNormal, clusterCompPhaseTransition, msg)
}
transCtx.GetLogger().Info(fmt.Sprintf("cluster sharding phase transition: %s -> %s (%s)", phase, status.Phase, msg))
}
diff --git a/controllers/apps/transformer_cluster_component_status_test.go b/controllers/apps/cluster/transformer_cluster_component_status_test.go
similarity index 99%
rename from controllers/apps/transformer_cluster_component_status_test.go
rename to controllers/apps/cluster/transformer_cluster_component_status_test.go
index d4bfe371fd3..0be5168d61a 100644
--- a/controllers/apps/transformer_cluster_component_status_test.go
+++ b/controllers/apps/cluster/transformer_cluster_component_status_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
. "github.com/onsi/ginkgo/v2"
diff --git a/controllers/apps/transformer_cluster_component_test.go b/controllers/apps/cluster/transformer_cluster_component_test.go
similarity index 97%
rename from controllers/apps/transformer_cluster_component_test.go
rename to controllers/apps/cluster/transformer_cluster_component_test.go
index f64228540e0..02ed3bffe44 100644
--- a/controllers/apps/transformer_cluster_component_test.go
+++ b/controllers/apps/cluster/transformer_cluster_component_test.go
@@ -17,12 +17,10 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
- "context"
"fmt"
- "reflect"
"strings"
. "github.com/onsi/ginkgo/v2"
@@ -30,9 +28,6 @@ import (
"github.com/onsi/gomega/types"
corev1 "k8s.io/api/core/v1"
- apierrors "k8s.io/apimachinery/pkg/api/errors"
- "k8s.io/apimachinery/pkg/labels"
- "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -45,47 +40,6 @@ import (
testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps"
)
-type mockReader struct {
- objs []client.Object
-}
-
-func (r *mockReader) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
- for _, o := range r.objs {
- // ignore the GVK check
- if client.ObjectKeyFromObject(o) == key {
- reflect.ValueOf(obj).Elem().Set(reflect.ValueOf(o).Elem())
- return nil
- }
- }
- return apierrors.NewNotFound(schema.GroupResource{}, key.Name)
-}
-
-func (r *mockReader) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
- items := reflect.ValueOf(list).Elem().FieldByName("Items")
- if !items.IsValid() {
- return fmt.Errorf("ObjectList has no Items field: %s", list.GetObjectKind().GroupVersionKind().String())
- }
-
- objs := reflect.MakeSlice(items.Type(), 0, 0)
- if len(r.objs) > 0 {
- listOpts := &client.ListOptions{}
- for _, opt := range opts {
- opt.ApplyToList(listOpts)
- }
-
- for i, o := range r.objs {
- if reflect.TypeOf(r.objs[i]).Elem().AssignableTo(items.Type().Elem()) {
- if listOpts.LabelSelector == nil || listOpts.LabelSelector.Matches(labels.Set(o.GetLabels())) {
- objs = reflect.Append(objs, reflect.ValueOf(r.objs[i]).Elem())
- }
- }
- }
- }
- items.Set(objs)
-
- return nil
-}
-
var _ = Describe("cluster component transformer test", func() {
const (
clusterDefName = "test-clusterdef"
diff --git a/controllers/apps/transformer_cluster_deletion.go b/controllers/apps/cluster/transformer_cluster_deletion.go
similarity index 99%
rename from controllers/apps/transformer_cluster_deletion.go
rename to controllers/apps/cluster/transformer_cluster_deletion.go
index da5c2a90604..b51c3ffd571 100644
--- a/controllers/apps/transformer_cluster_deletion.go
+++ b/controllers/apps/cluster/transformer_cluster_deletion.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"fmt"
diff --git a/controllers/apps/transformer_cluster_deletion_test.go b/controllers/apps/cluster/transformer_cluster_deletion_test.go
similarity index 99%
rename from controllers/apps/transformer_cluster_deletion_test.go
rename to controllers/apps/cluster/transformer_cluster_deletion_test.go
index 7b90fac7650..cc3fe852d0f 100644
--- a/controllers/apps/transformer_cluster_deletion_test.go
+++ b/controllers/apps/cluster/transformer_cluster_deletion_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"slices"
diff --git a/controllers/apps/transformer_cluster_init.go b/controllers/apps/cluster/transformer_cluster_init.go
similarity index 98%
rename from controllers/apps/transformer_cluster_init.go
rename to controllers/apps/cluster/transformer_cluster_init.go
index 81b487502b7..a71e417394b 100644
--- a/controllers/apps/transformer_cluster_init.go
+++ b/controllers/apps/cluster/transformer_cluster_init.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
diff --git a/controllers/apps/transformer_cluster_meta.go b/controllers/apps/cluster/transformer_cluster_meta.go
similarity index 99%
rename from controllers/apps/transformer_cluster_meta.go
rename to controllers/apps/cluster/transformer_cluster_meta.go
index 13285913648..b402b304e89 100644
--- a/controllers/apps/transformer_cluster_meta.go
+++ b/controllers/apps/cluster/transformer_cluster_meta.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
diff --git a/controllers/apps/transformer_cluster_normalization.go b/controllers/apps/cluster/transformer_cluster_normalization.go
similarity index 65%
rename from controllers/apps/transformer_cluster_normalization.go
rename to controllers/apps/cluster/transformer_cluster_normalization.go
index 570e7908d75..e3044fcf02f 100644
--- a/controllers/apps/transformer_cluster_normalization.go
+++ b/controllers/apps/cluster/transformer_cluster_normalization.go
@@ -17,15 +17,19 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
+ "context"
"fmt"
+ "slices"
+ "strings"
"golang.org/x/exp/maps"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/util/version"
"sigs.k8s.io/controller-runtime/pkg/client"
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
@@ -475,3 +479,241 @@ func (t *clusterNormalizationTransformer) checkNPatchCRDAPIVersionKey(transCtx *
}
return graph.ErrPrematureStop // un-supported CRD API version, stop the transformation
}
+
+// referredClusterTopology returns the cluster topology which has name @name.
+func referredClusterTopology(clusterDef *appsv1.ClusterDefinition, name string) *appsv1.ClusterTopology {
+ if clusterDef != nil {
+ if len(name) == 0 {
+ return defaultClusterTopology(clusterDef)
+ }
+ for i, topology := range clusterDef.Spec.Topologies {
+ if topology.Name == name {
+ return &clusterDef.Spec.Topologies[i]
+ }
+ }
+ }
+ return nil
+}
+
+// defaultClusterTopology returns the default cluster topology in specified cluster definition.
+func defaultClusterTopology(clusterDef *appsv1.ClusterDefinition) *appsv1.ClusterTopology {
+ for i, topology := range clusterDef.Spec.Topologies {
+ if topology.Default {
+ return &clusterDef.Spec.Topologies[i]
+ }
+ }
+ return nil
+}
+
+func clusterTopologyCompMatched(comp appsv1.ClusterTopologyComponent, compName string) bool {
+ if comp.Name == compName {
+ return true
+ }
+ if comp.Template != nil && *comp.Template {
+ return strings.HasPrefix(compName, comp.Name)
+ }
+ return false
+}
+
+// resolveShardingDefinition resolves and returns the specific sharding definition object supported.
+func resolveShardingDefinition(ctx context.Context, cli client.Reader, shardingDefName string) (*appsv1.ShardingDefinition, error) {
+ shardingDefs, err := listShardingDefinitionsWithPattern(ctx, cli, shardingDefName)
+ if err != nil {
+ return nil, err
+ }
+ if len(shardingDefs) == 0 {
+ return nil, fmt.Errorf("no sharding definition found for the specified name: %s", shardingDefName)
+ }
+
+ m := make(map[string]int)
+ for i, def := range shardingDefs {
+ m[def.Name] = i
+ }
+ // choose the latest one
+ names := maps.Keys(m)
+ slices.Sort(names)
+ latestName := names[len(names)-1]
+
+ return shardingDefs[m[latestName]], nil
+}
+
+// listShardingDefinitionsWithPattern returns all sharding definitions whose names match the given pattern
+func listShardingDefinitionsWithPattern(ctx context.Context, cli client.Reader, name string) ([]*appsv1.ShardingDefinition, error) {
+ shardingDefList := &appsv1.ShardingDefinitionList{}
+ if err := cli.List(ctx, shardingDefList); err != nil {
+ return nil, err
+ }
+ fullyMatched := make([]*appsv1.ShardingDefinition, 0)
+ patternMatched := make([]*appsv1.ShardingDefinition, 0)
+ for i, item := range shardingDefList.Items {
+ if item.Name == name {
+ fullyMatched = append(fullyMatched, &shardingDefList.Items[i])
+ }
+ if component.PrefixOrRegexMatched(item.Name, name) {
+ patternMatched = append(patternMatched, &shardingDefList.Items[i])
+ }
+ }
+ if len(fullyMatched) > 0 {
+ return fullyMatched, nil
+ }
+ return patternMatched, nil
+}
+
+func validateShardingShards(shardingDef *appsv1.ShardingDefinition, sharding *appsv1.ClusterSharding) error {
+ var (
+ limit = shardingDef.Spec.ShardsLimit
+ shards = sharding.Shards
+ )
+ if limit == nil || (shards >= limit.MinShards && shards <= limit.MaxShards) {
+ return nil
+ }
+ return shardsOutOfLimitError(sharding.Name, shards, *limit)
+}
+
+func shardsOutOfLimitError(shardingName string, shards int32, limit appsv1.ShardsLimit) error {
+ return fmt.Errorf("shards %d out-of-limit [%d, %d], sharding: %s", shards, limit.MinShards, limit.MaxShards, shardingName)
+}
+
+// resolveCompDefinitionNServiceVersion resolves and returns the specific component definition object and the service version supported.
+func resolveCompDefinitionNServiceVersion(ctx context.Context, cli client.Reader, compDefName, serviceVersion string) (*appsv1.ComponentDefinition, string, error) {
+ var (
+ compDef *appsv1.ComponentDefinition
+ )
+ compDefs, err := listCompDefinitionsWithPattern(ctx, cli, compDefName)
+ if err != nil {
+ return compDef, serviceVersion, err
+ }
+
+ // mapping from to <[]*appsv1.ComponentDefinition>
+ serviceVersionToCompDefs, err := serviceVersionToCompDefinitions(ctx, cli, compDefs, serviceVersion)
+ if err != nil {
+ return compDef, serviceVersion, err
+ }
+
+ // use specified service version or the latest.
+ if len(serviceVersion) == 0 {
+ serviceVersions := maps.Keys(serviceVersionToCompDefs)
+ if len(serviceVersions) > 0 {
+ slices.SortFunc(serviceVersions, serviceVersionComparator)
+ serviceVersion = serviceVersions[len(serviceVersions)-1]
+ }
+ }
+
+ // component definitions that support the service version
+ compatibleCompDefs := serviceVersionToCompDefs[serviceVersion]
+ if len(compatibleCompDefs) == 0 {
+ return compDef, serviceVersion, fmt.Errorf(`no matched component definition found with componentDef "%s" and serviceVersion "%s"`, compDefName, serviceVersion)
+ }
+
+ // choose the latest one
+ compatibleCompDefNames := maps.Keys(compatibleCompDefs)
+ slices.Sort(compatibleCompDefNames)
+ compatibleCompDefName := compatibleCompDefNames[len(compatibleCompDefNames)-1]
+
+ return compatibleCompDefs[compatibleCompDefName], serviceVersion, nil
+}
+
+// listCompDefinitionsWithPattern returns all component definitions whose names match the given pattern
+func listCompDefinitionsWithPattern(ctx context.Context, cli client.Reader, name string) ([]*appsv1.ComponentDefinition, error) {
+ compDefList := &appsv1.ComponentDefinitionList{}
+ if err := cli.List(ctx, compDefList); err != nil {
+ return nil, err
+ }
+ compDefsFullyMatched := make([]*appsv1.ComponentDefinition, 0)
+ compDefsPatternMatched := make([]*appsv1.ComponentDefinition, 0)
+ for i, item := range compDefList.Items {
+ if item.Name == name {
+ compDefsFullyMatched = append(compDefsFullyMatched, &compDefList.Items[i])
+ }
+ if component.PrefixOrRegexMatched(item.Name, name) {
+ compDefsPatternMatched = append(compDefsPatternMatched, &compDefList.Items[i])
+ }
+ }
+ if len(compDefsFullyMatched) > 0 {
+ return compDefsFullyMatched, nil
+ }
+ return compDefsPatternMatched, nil
+}
+
+func serviceVersionToCompDefinitions(ctx context.Context, cli client.Reader,
+ compDefs []*appsv1.ComponentDefinition, serviceVersion string) (map[string]map[string]*appsv1.ComponentDefinition, error) {
+ result := make(map[string]map[string]*appsv1.ComponentDefinition)
+
+ insert := func(version string, compDef *appsv1.ComponentDefinition) {
+ if _, ok := result[version]; !ok {
+ result[version] = make(map[string]*appsv1.ComponentDefinition)
+ }
+ result[version][compDef.Name] = compDef
+ }
+
+ checkedInsert := func(version string, compDef *appsv1.ComponentDefinition) error {
+ match, err := component.CompareServiceVersion(serviceVersion, version)
+ if err == nil && match {
+ insert(version, compDef)
+ }
+ return err
+ }
+
+ for _, compDef := range compDefs {
+ compVersions, err := component.CompatibleCompVersions4Definition(ctx, cli, compDef)
+ if err != nil {
+ return nil, err
+ }
+
+ serviceVersions := sets.New[string]()
+ // add definition's service version as default, in case there is no component versions provided
+ if compDef.Spec.ServiceVersion != "" {
+ serviceVersions.Insert(compDef.Spec.ServiceVersion)
+ }
+ for _, compVersion := range compVersions {
+ serviceVersions = serviceVersions.Union(compatibleServiceVersions4Definition(compDef, compVersion))
+ }
+
+ for version := range serviceVersions {
+ if err = checkedInsert(version, compDef); err != nil {
+ return nil, err
+ }
+ }
+ }
+ return result, nil
+}
+
+// compatibleServiceVersions4Definition returns all service versions that are compatible with specified component definition.
+func compatibleServiceVersions4Definition(compDef *appsv1.ComponentDefinition, compVersion *appsv1.ComponentVersion) sets.Set[string] {
+ match := func(pattern string) bool {
+ return component.PrefixOrRegexMatched(compDef.Name, pattern)
+ }
+ releases := make(map[string]bool, 0)
+ for _, rule := range compVersion.Spec.CompatibilityRules {
+ if slices.IndexFunc(rule.CompDefs, match) >= 0 {
+ for _, release := range rule.Releases {
+ releases[release] = true
+ }
+ }
+ }
+ serviceVersions := sets.New[string]()
+ for _, release := range compVersion.Spec.Releases {
+ if releases[release.Name] {
+ serviceVersions = serviceVersions.Insert(release.ServiceVersion)
+ }
+ }
+ return serviceVersions
+}
+
+func serviceVersionComparator(a, b string) int {
+ if len(a) == 0 {
+ return -1
+ }
+ if len(b) == 0 {
+ return 1
+ }
+ v, err1 := version.ParseSemantic(a)
+ if err1 != nil {
+ panic(fmt.Sprintf("runtime error - invalid service version in comparator: %s", err1.Error()))
+ }
+ ret, err2 := v.Compare(b)
+ if err2 != nil {
+ panic(fmt.Sprintf("runtime error - invalid service version in comparator: %s", err2.Error()))
+ }
+ return ret
+}
diff --git a/controllers/apps/cluster/transformer_cluster_normalization_test.go b/controllers/apps/cluster/transformer_cluster_normalization_test.go
new file mode 100644
index 00000000000..e3a64049447
--- /dev/null
+++ b/controllers/apps/cluster/transformer_cluster_normalization_test.go
@@ -0,0 +1,495 @@
+/*
+Copyright (C) 2022-2024 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package cluster
+
+import (
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ corev1 "k8s.io/api/core/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+ "github.com/apecloud/kubeblocks/pkg/controller/component"
+ "github.com/apecloud/kubeblocks/pkg/generics"
+ testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps"
+)
+
+var _ = Describe("resolve CompDefinition and ServiceVersion", func() {
+ var (
+ compVersionObj *appsv1.ComponentVersion
+ compDefNames = []string{
+ testapps.CompDefName("v1.0"),
+ testapps.CompDefName("v1.1"),
+ testapps.CompDefName("v2.0"),
+ testapps.CompDefName("v3.0"),
+ }
+ )
+
+ cleanEnv := func() {
+ // must wait till resources deleted and no longer existed before the testcases start,
+ // otherwise if later it needs to create some new resource objects with the same name,
+ // in race conditions, it will find the existence of old objects, resulting failure to
+ // create the new objects.
+ By("clean resources")
+
+ // inNS := client.InNamespace(testCtx.DefaultNamespace)
+ ml := client.HasLabels{testCtx.TestObjLabelKey}
+
+ // non-namespaced
+ testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ComponentDefinitionSignature, true, ml)
+ testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ComponentVersionSignature, true, ml)
+
+ // namespaced
+ }
+
+ BeforeEach(func() {
+ cleanEnv()
+ })
+
+ AfterEach(func() {
+ cleanEnv()
+ })
+
+ createCompDefinitionObjs := func() []*appsv1.ComponentDefinition {
+ By("create default ComponentDefinition objs")
+ objs := make([]*appsv1.ComponentDefinition, 0)
+ for _, name := range compDefNames {
+ f := testapps.NewComponentDefinitionFactory(name).
+ SetServiceVersion(testapps.ServiceVersion("v0")) // use v0 as init service version
+ for _, app := range []string{testapps.AppName, testapps.AppNameSamePrefix} {
+ // use empty revision as init image tag
+ f = f.SetRuntime(&corev1.Container{Name: app, Image: testapps.AppImage(app, testapps.ReleaseID(""))})
+ }
+ f.SetLifecycleAction(testapps.DefaultActionName,
+ &appsv1.Action{Exec: &appsv1.ExecAction{Image: testapps.AppImage(testapps.DefaultActionName, testapps.ReleaseID(""))}})
+ objs = append(objs, f.Create(&testCtx).GetObject())
+ }
+ for _, obj := range objs {
+ Eventually(testapps.CheckObj(&testCtx, client.ObjectKeyFromObject(obj),
+ func(g Gomega, compDef *appsv1.ComponentDefinition) {
+ g.Expect(compDef.Status.ObservedGeneration).Should(Equal(compDef.Generation))
+ })).Should(Succeed())
+ }
+ return objs
+ }
+
+ createCompVersionObj := func() *appsv1.ComponentVersion {
+ By("create a default ComponentVersion obj with multiple releases")
+ obj := testapps.NewComponentVersionFactory(testapps.CompVersionName).
+ SetSpec(appsv1.ComponentVersionSpec{
+ CompatibilityRules: []appsv1.ComponentVersionCompatibilityRule{
+ {
+ // use prefix
+ CompDefs: []string{testapps.CompDefName("v1"), testapps.CompDefName("v2")},
+ Releases: []string{testapps.ReleaseID("r0"), testapps.ReleaseID("r1"), testapps.ReleaseID("r2"), testapps.ReleaseID("r3"), testapps.ReleaseID("r4")}, // sv: v1, v2
+ },
+ {
+ // use prefix
+ CompDefs: []string{testapps.CompDefName("v3")},
+ Releases: []string{testapps.ReleaseID("r5")}, // sv: v3
+ },
+ },
+ Releases: []appsv1.ComponentVersionRelease{
+ {
+ Name: testapps.ReleaseID("r0"),
+ Changes: "init release",
+ ServiceVersion: testapps.ServiceVersion("v1"),
+ Images: map[string]string{
+ testapps.AppName: testapps.AppImage(testapps.AppName, testapps.ReleaseID("r0")),
+ testapps.AppNameSamePrefix: testapps.AppImage(testapps.AppNameSamePrefix, testapps.ReleaseID("r0")),
+ testapps.DefaultActionName: testapps.AppImage(testapps.DefaultActionName, testapps.ReleaseID("r0")),
+ },
+ },
+ {
+ Name: testapps.ReleaseID("r1"),
+ Changes: "update app image",
+ ServiceVersion: testapps.ServiceVersion("v1"),
+ Images: map[string]string{
+ testapps.AppName: testapps.AppImage(testapps.AppName, testapps.ReleaseID("r1")),
+ },
+ },
+ {
+ Name: testapps.ReleaseID("r2"),
+ Changes: "publish a new service version",
+ ServiceVersion: testapps.ServiceVersion("v2"),
+ Images: map[string]string{
+ testapps.AppName: testapps.AppImage(testapps.AppName, testapps.ReleaseID("r2")),
+ testapps.AppNameSamePrefix: testapps.AppImage(testapps.AppNameSamePrefix, testapps.ReleaseID("r2")),
+ testapps.DefaultActionName: testapps.AppImage(testapps.DefaultActionName, testapps.ReleaseID("r2")),
+ },
+ },
+ {
+ Name: testapps.ReleaseID("r3"),
+ Changes: "update app image",
+ ServiceVersion: testapps.ServiceVersion("v2"),
+ Images: map[string]string{
+ testapps.AppName: testapps.AppImage(testapps.AppName, testapps.ReleaseID("r3")),
+ },
+ },
+ {
+ Name: testapps.ReleaseID("r4"),
+ Changes: "update all app images for previous service version",
+ ServiceVersion: testapps.ServiceVersion("v1"),
+ Images: map[string]string{
+ testapps.AppName: testapps.AppImage(testapps.AppName, testapps.ReleaseID("r4")),
+ testapps.AppNameSamePrefix: testapps.AppImage(testapps.AppNameSamePrefix, testapps.ReleaseID("r4")),
+ testapps.DefaultActionName: testapps.AppImage(testapps.DefaultActionName, testapps.ReleaseID("r4")),
+ },
+ },
+ {
+ Name: testapps.ReleaseID("r5"),
+ Changes: "publish a new service version",
+ ServiceVersion: testapps.ServiceVersion("v3"),
+ Images: map[string]string{
+ testapps.AppName: testapps.AppImage(testapps.AppName, testapps.ReleaseID("r5")),
+ testapps.AppNameSamePrefix: testapps.AppImage(testapps.AppNameSamePrefix, testapps.ReleaseID("r5")),
+ testapps.DefaultActionName: testapps.AppImage(testapps.DefaultActionName, testapps.ReleaseID("r5")),
+ },
+ },
+ },
+ }).
+ Create(&testCtx).
+ GetObject()
+ Eventually(testapps.CheckObj(&testCtx, client.ObjectKeyFromObject(obj),
+ func(g Gomega, compVersion *appsv1.ComponentVersion) {
+ g.Expect(compVersion.Status.ObservedGeneration).Should(Equal(compVersion.Generation))
+ })).Should(Succeed())
+
+ return obj
+ }
+
+ updateNCheckCompDefinitionImages := func(compDef *appsv1.ComponentDefinition, serviceVersion string, r0, r1 string) {
+ Expect(compDef.Spec.Runtime.Containers[0].Image).Should(Equal(testapps.AppImage(compDef.Spec.Runtime.Containers[0].Name, testapps.ReleaseID(""))))
+ Expect(compDef.Spec.Runtime.Containers[1].Image).Should(Equal(testapps.AppImage(compDef.Spec.Runtime.Containers[1].Name, testapps.ReleaseID(""))))
+ Expect(component.UpdateCompDefinitionImages4ServiceVersion(testCtx.Ctx, testCtx.Cli, compDef, serviceVersion)).Should(Succeed())
+ Expect(compDef.Spec.Runtime.Containers).Should(HaveLen(2))
+ Expect(compDef.Spec.Runtime.Containers[0].Image).Should(Equal(testapps.AppImage(compDef.Spec.Runtime.Containers[0].Name, testapps.ReleaseID(r0))))
+ Expect(compDef.Spec.Runtime.Containers[1].Image).Should(Equal(testapps.AppImage(compDef.Spec.Runtime.Containers[1].Name, testapps.ReleaseID(r1))))
+
+ Expect(compDef.Spec.LifecycleActions).ShouldNot(BeNil())
+ Expect(compDef.Spec.LifecycleActions.PreTerminate).ShouldNot(BeNil())
+ Expect(compDef.Spec.LifecycleActions.PreTerminate.Exec).ShouldNot(BeNil())
+ Expect(compDef.Spec.LifecycleActions.PreTerminate.Exec.Image).Should(Equal(testapps.AppImage(testapps.DefaultActionName, testapps.ReleaseID(r1))))
+ }
+
+ Context("resolve component definition, service version and images", func() {
+ BeforeEach(func() {
+ createCompDefinitionObjs()
+ compVersionObj = createCompVersionObj()
+ })
+
+ AfterEach(func() {
+ cleanEnv()
+ })
+
+ It("full match", func() {
+ By("with definition v1.0 and service version v0")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), testapps.ServiceVersion("v1"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
+
+ By("with definition v1.1 and service version v0")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.1"), testapps.ServiceVersion("v1"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
+
+ By("with definition v2.0 and service version v0")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2.0"), testapps.ServiceVersion("v1"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
+
+ By("with definition v1.0 and service version v1")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), testapps.ServiceVersion("v2"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition v1.1 and service version v1")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.1"), testapps.ServiceVersion("v2"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition v2.0 and service version v1")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2.0"), testapps.ServiceVersion("v2"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition v3.0 and service version v2")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v3.0"), testapps.ServiceVersion("v3"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
+ })
+
+ It("w/o service version", func() {
+ By("with definition v1.0")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition v1.1")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.1"), "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition v2.0")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2.0"), "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition v3.0")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v3.0"), "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
+ })
+
+ It("prefix match definition", func() {
+ By("with definition prefix and service version v0")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefinitionName, testapps.ServiceVersion("v1"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
+
+ By("with definition prefix and service version v1")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefinitionName, testapps.ServiceVersion("v2"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition prefix and service version v2")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefinitionName, testapps.ServiceVersion("v3"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
+
+ By("with definition v1 prefix and service version v0")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1"), testapps.ServiceVersion("v1"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
+
+ By("with definition v2 prefix and service version v1")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2"), testapps.ServiceVersion("v2"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+ })
+
+ It("prefix match definition and w/o service version", func() {
+ By("with definition prefix")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefinitionName, "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
+
+ By("with definition v1 prefix")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1"), "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition v2 prefix")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2"), "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+ })
+
+ It("regular expression match definition", func() {
+ By("with definition exact regex and service version 1")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithExactRegex("v2.0"), testapps.ServiceVersion("v1"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
+
+ By("with definition exact regex and service version v2")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithExactRegex("v2.0"), testapps.ServiceVersion("v2"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition exact regex and service version v3")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithExactRegex("v3.0"), testapps.ServiceVersion("v3"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
+
+ By("with definition v1 fuzzy regex and service version v0")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithFuzzyRegex("v1"), testapps.ServiceVersion("v1"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
+
+ By("with definition v2 fuzzy regex and service version v1")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithFuzzyRegex("v2"), testapps.ServiceVersion("v2"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+ })
+
+ It("regular expression match definition and w/o service version", func() {
+ By("with definition regex")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, "^"+testapps.CompDefinitionName, "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
+
+ By("with definition v1 regex")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithFuzzyRegex("v1"), "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+
+ By("with definition v2 regex")
+ compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithFuzzyRegex("v2"), "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+ })
+
+ It("match from definition", func() {
+ By("with definition v1.0 and service version v0")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), testapps.ServiceVersion("v0"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v0")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "", "") // empty revision of image tag
+ })
+
+ It("resolve images from definition and version", func() {
+ By("create new definition v4.0 with service version v4")
+ compDefObj := testapps.NewComponentDefinitionFactory(testapps.CompDefName("v4.0")).
+ SetServiceVersion(testapps.ServiceVersion("v4")).
+ SetRuntime(&corev1.Container{Name: testapps.AppName, Image: testapps.AppImage(testapps.AppName, testapps.ReleaseID(""))}).
+ SetRuntime(&corev1.Container{Name: testapps.AppNameSamePrefix, Image: testapps.AppImage(testapps.AppNameSamePrefix, testapps.ReleaseID(""))}).
+ SetLifecycleAction(testapps.DefaultActionName,
+ &appsv1.Action{Exec: &appsv1.ExecAction{Image: testapps.AppImage(testapps.DefaultActionName, testapps.ReleaseID(""))}}).
+ Create(&testCtx).
+ GetObject()
+ Eventually(testapps.CheckObj(&testCtx, client.ObjectKeyFromObject(compDefObj),
+ func(g Gomega, compDef *appsv1.ComponentDefinition) {
+ g.Expect(compDef.Status.ObservedGeneration).Should(Equal(compDef.Generation))
+ })).Should(Succeed())
+
+ By("new release for the definition")
+ compVersionKey := client.ObjectKeyFromObject(compVersionObj)
+ Eventually(testapps.GetAndChangeObj(&testCtx, compVersionKey, func(compVersion *appsv1.ComponentVersion) {
+ release := appsv1.ComponentVersionRelease{
+ Name: testapps.ReleaseID("r6"),
+ Changes: "publish a new service version",
+ ServiceVersion: testapps.ServiceVersion("v4"),
+ Images: map[string]string{
+ testapps.AppName: testapps.AppImage(testapps.AppName, testapps.ReleaseID("r6")),
+ // not provide image for this app
+ // testapps.AppNameSamePrefix: testapps.AppImage(testapps.AppNameSamePrefix, testapps.ReleaseID("r6")),
+ },
+ }
+ rule := appsv1.ComponentVersionCompatibilityRule{
+ CompDefs: []string{testapps.CompDefName("v4")}, // use prefix
+ Releases: []string{testapps.ReleaseID("r6")},
+ }
+ compVersion.Spec.CompatibilityRules = append(compVersion.Spec.CompatibilityRules, rule)
+ compVersion.Spec.Releases = append(compVersion.Spec.Releases, release)
+ })).Should(Succeed())
+ Eventually(testapps.CheckObj(&testCtx, compVersionKey, func(g Gomega, compVersion *appsv1.ComponentVersion) {
+ g.Expect(compVersion.Status.ObservedGeneration).Should(Equal(compVersion.Generation))
+ })).Should(Succeed())
+
+ By("with definition v4.0 and service version v3")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v4.0"), testapps.ServiceVersion("v4"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v4.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v4")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r6", "") // app is r6 and another one is ""
+ })
+ })
+
+ Context("resolve component definition, service version without serviceVersion in componentDefinition", func() {
+ BeforeEach(func() {
+ compDefs := createCompDefinitionObjs()
+ for _, compDef := range compDefs {
+ compDefKey := client.ObjectKeyFromObject(compDef)
+ Eventually(testapps.GetAndChangeObj(&testCtx, compDefKey, func(compDef *appsv1.ComponentDefinition) {
+ compDef.Spec.ServiceVersion = ""
+ })).Should(Succeed())
+ }
+ compVersionObj = createCompVersionObj()
+ })
+
+ AfterEach(func() {
+ cleanEnv()
+ })
+
+ It("full match", func() {
+ By("with definition v1.0 and service version v0")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), testapps.ServiceVersion("v1"))
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
+ })
+
+ It("w/o service version", func() {
+ By("with definition v1.0")
+ compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), "")
+ Expect(err).Should(Succeed())
+ Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
+ Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
+ updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
+ })
+ })
+})
diff --git a/controllers/apps/transformer_cluster_ownership.go b/controllers/apps/cluster/transformer_cluster_ownership.go
similarity index 99%
rename from controllers/apps/transformer_cluster_ownership.go
rename to controllers/apps/cluster/transformer_cluster_ownership.go
index d4ffd24015a..97f9adeb927 100644
--- a/controllers/apps/transformer_cluster_ownership.go
+++ b/controllers/apps/cluster/transformer_cluster_ownership.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
diff --git a/controllers/apps/transformer_cluster_placement.go b/controllers/apps/cluster/transformer_cluster_placement.go
similarity index 99%
rename from controllers/apps/transformer_cluster_placement.go
rename to controllers/apps/cluster/transformer_cluster_placement.go
index 4921b46507f..a06b9164078 100644
--- a/controllers/apps/transformer_cluster_placement.go
+++ b/controllers/apps/cluster/transformer_cluster_placement.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"math/rand"
diff --git a/controllers/apps/transformer_cluster_restore.go b/controllers/apps/cluster/transformer_cluster_restore.go
similarity index 99%
rename from controllers/apps/transformer_cluster_restore.go
rename to controllers/apps/cluster/transformer_cluster_restore.go
index 969dfcec564..c333421f656 100644
--- a/controllers/apps/transformer_cluster_restore.go
+++ b/controllers/apps/cluster/transformer_cluster_restore.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"k8s.io/apimachinery/pkg/util/json"
diff --git a/controllers/apps/transformer_cluster_service.go b/controllers/apps/cluster/transformer_cluster_service.go
similarity index 99%
rename from controllers/apps/transformer_cluster_service.go
rename to controllers/apps/cluster/transformer_cluster_service.go
index 16944b42000..4253a7ca0c0 100644
--- a/controllers/apps/transformer_cluster_service.go
+++ b/controllers/apps/cluster/transformer_cluster_service.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"fmt"
diff --git a/controllers/apps/transformer_cluster_service_test.go b/controllers/apps/cluster/transformer_cluster_service_test.go
similarity index 99%
rename from controllers/apps/transformer_cluster_service_test.go
rename to controllers/apps/cluster/transformer_cluster_service_test.go
index 4ccc4f749dc..aa9df856ba2 100644
--- a/controllers/apps/transformer_cluster_service_test.go
+++ b/controllers/apps/cluster/transformer_cluster_service_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"slices"
diff --git a/controllers/apps/transformer_cluster_sharding_account.go b/controllers/apps/cluster/transformer_cluster_sharding_account.go
similarity index 99%
rename from controllers/apps/transformer_cluster_sharding_account.go
rename to controllers/apps/cluster/transformer_cluster_sharding_account.go
index 3718ed19153..6a987deae53 100644
--- a/controllers/apps/transformer_cluster_sharding_account.go
+++ b/controllers/apps/cluster/transformer_cluster_sharding_account.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"fmt"
diff --git a/controllers/apps/transformer_cluster_sharding_tls.go b/controllers/apps/cluster/transformer_cluster_sharding_tls.go
similarity index 99%
rename from controllers/apps/transformer_cluster_sharding_tls.go
rename to controllers/apps/cluster/transformer_cluster_sharding_tls.go
index 576fa996aa5..96e3af8673e 100644
--- a/controllers/apps/transformer_cluster_sharding_tls.go
+++ b/controllers/apps/cluster/transformer_cluster_sharding_tls.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"fmt"
diff --git a/controllers/apps/transformer_cluster_status.go b/controllers/apps/cluster/transformer_cluster_status.go
similarity index 99%
rename from controllers/apps/transformer_cluster_status.go
rename to controllers/apps/cluster/transformer_cluster_status.go
index 7646e3d0f4e..8e02789328e 100644
--- a/controllers/apps/transformer_cluster_status.go
+++ b/controllers/apps/cluster/transformer_cluster_status.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"fmt"
diff --git a/controllers/apps/transformer_cluster_validation.go b/controllers/apps/cluster/transformer_cluster_validation.go
similarity index 99%
rename from controllers/apps/transformer_cluster_validation.go
rename to controllers/apps/cluster/transformer_cluster_validation.go
index d90ea50de80..56450ca3d0e 100644
--- a/controllers/apps/transformer_cluster_validation.go
+++ b/controllers/apps/cluster/transformer_cluster_validation.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"fmt"
diff --git a/controllers/apps/transformer_cluster_validation_test.go b/controllers/apps/cluster/transformer_cluster_validation_test.go
similarity index 99%
rename from controllers/apps/transformer_cluster_validation_test.go
rename to controllers/apps/cluster/transformer_cluster_validation_test.go
index ea7951c6232..40e78f92d9f 100644
--- a/controllers/apps/transformer_cluster_validation_test.go
+++ b/controllers/apps/cluster/transformer_cluster_validation_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
. "github.com/onsi/ginkgo/v2"
diff --git a/controllers/apps/transform_types.go b/controllers/apps/cluster/types.go
similarity index 99%
rename from controllers/apps/transform_types.go
rename to controllers/apps/cluster/types.go
index 5b89554b7a1..1fb23fdf74e 100644
--- a/controllers/apps/transform_types.go
+++ b/controllers/apps/cluster/types.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
diff --git a/controllers/apps/transform_utils.go b/controllers/apps/cluster/utils.go
similarity index 59%
rename from controllers/apps/transform_utils.go
rename to controllers/apps/cluster/utils.go
index 859916a8c25..9491387e2ad 100644
--- a/controllers/apps/transform_utils.go
+++ b/controllers/apps/cluster/utils.go
@@ -17,10 +17,11 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"context"
+ "fmt"
"reflect"
"time"
@@ -34,14 +35,130 @@ import (
dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
"github.com/apecloud/kubeblocks/pkg/constant"
+ "github.com/apecloud/kubeblocks/pkg/controller/model"
+ "github.com/apecloud/kubeblocks/pkg/controller/multicluster"
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
dptypes "github.com/apecloud/kubeblocks/pkg/dataprotection/types"
)
+// default reconcile requeue after duration
+var requeueDuration = time.Millisecond * 1000
+
func newRequeueError(after time.Duration, reason string) error {
return intctrlutil.NewRequeueError(after, reason)
}
+func boolValue(b *bool) bool {
+ if b == nil {
+ return false
+ }
+ return *b
+}
+
+func mergeMap(dst, src map[string]string) {
+ for key, val := range src {
+ dst[key] = val
+ }
+}
+
+func placement(obj client.Object) string {
+ if obj == nil || obj.GetAnnotations() == nil {
+ return ""
+ }
+ return obj.GetAnnotations()[constant.KBAppMultiClusterPlacementKey]
+}
+
+func intoContext(ctx context.Context, placement string) context.Context {
+ return multicluster.IntoContext(ctx, placement)
+}
+
+func inUniversalContext4C() *multicluster.ClientOption {
+ return multicluster.InUniversalContext()
+}
+
+func inDataContext4G() model.GraphOption {
+ return model.WithClientOption(multicluster.InDataContext())
+}
+
+func inUniversalContext4G() model.GraphOption {
+ return model.WithClientOption(multicluster.InUniversalContext())
+}
+
+func clientOption(v *model.ObjectVertex) *multicluster.ClientOption {
+ if v.ClientOpt != nil {
+ opt, ok := v.ClientOpt.(*multicluster.ClientOption)
+ if ok {
+ return opt
+ }
+ panic(fmt.Sprintf("unknown client option: %T", v.ClientOpt))
+ }
+ return multicluster.InControlContext()
+}
+
+func resolveServiceDefaultFields(oldSpec, newSpec *corev1.ServiceSpec) {
+ var exist *corev1.ServicePort
+ for i, port := range newSpec.Ports {
+ for _, oldPort := range oldSpec.Ports {
+ // assume that port.Name is user specified, if it is not changed, we need to keep the old NodePort and TargetPort if they are not set
+ if port.Name != "" && port.Name == oldPort.Name {
+ exist = &oldPort
+ break
+ }
+ }
+ if exist == nil {
+ continue
+ }
+ // if the service type is NodePort or LoadBalancer, and the nodeport is not set, we should use the nodeport of the exist service
+ if shouldAllocateNodePorts(newSpec) && port.NodePort == 0 && exist.NodePort != 0 {
+ newSpec.Ports[i].NodePort = exist.NodePort
+ port.NodePort = exist.NodePort
+ }
+ if port.TargetPort.IntVal == 0 && port.TargetPort.StrVal == "" {
+ port.TargetPort = exist.TargetPort
+ }
+ if reflect.DeepEqual(port, *exist) {
+ newSpec.Ports[i].TargetPort = exist.TargetPort
+ }
+ }
+ if len(newSpec.ClusterIP) == 0 {
+ newSpec.ClusterIP = oldSpec.ClusterIP
+ }
+ if len(newSpec.ClusterIPs) == 0 {
+ newSpec.ClusterIPs = oldSpec.ClusterIPs
+ }
+ if len(newSpec.Type) == 0 {
+ newSpec.Type = oldSpec.Type
+ }
+ if len(newSpec.SessionAffinity) == 0 {
+ newSpec.SessionAffinity = oldSpec.SessionAffinity
+ }
+ if len(newSpec.IPFamilies) == 0 || (len(newSpec.IPFamilies) == 1 && *newSpec.IPFamilyPolicy != corev1.IPFamilyPolicySingleStack) {
+ newSpec.IPFamilies = oldSpec.IPFamilies
+ }
+ if newSpec.IPFamilyPolicy == nil {
+ newSpec.IPFamilyPolicy = oldSpec.IPFamilyPolicy
+ }
+ if newSpec.InternalTrafficPolicy == nil {
+ newSpec.InternalTrafficPolicy = oldSpec.InternalTrafficPolicy
+ }
+ if newSpec.ExternalTrafficPolicy == "" && oldSpec.ExternalTrafficPolicy != "" {
+ newSpec.ExternalTrafficPolicy = oldSpec.ExternalTrafficPolicy
+ }
+}
+
+func shouldAllocateNodePorts(svc *corev1.ServiceSpec) bool {
+ if svc.Type == corev1.ServiceTypeNodePort {
+ return true
+ }
+ if svc.Type == corev1.ServiceTypeLoadBalancer {
+ if svc.AllocateLoadBalancerNodePorts != nil {
+ return *svc.AllocateLoadBalancerNodePorts
+ }
+ return true
+ }
+ return false
+}
+
func getGVKName(object client.Object, scheme *runtime.Scheme) (*gvkNObjKey, error) {
gvk, err := apiutil.GVKForObject(object, scheme)
if err != nil {
@@ -147,49 +264,6 @@ func sendWarningEventWithError(
recorder.Event(obj, corev1.EventTypeWarning, reason, err.Error())
}
-func isVolumeResourceRequirementsEqual(a, b corev1.VolumeResourceRequirements) bool {
- return isResourceEqual(a.Requests, b.Requests) && isResourceEqual(a.Limits, b.Limits)
-}
-
-func isResourceRequirementsEqual(a, b corev1.ResourceRequirements) bool {
- return isResourceEqual(a.Requests, b.Requests) && isResourceEqual(a.Limits, b.Limits)
-}
-
-func isResourceEqual(a, b corev1.ResourceList) bool {
- if len(a) != len(b) {
- return false
- }
- for k, v := range a {
- if !v.Equal(b[k]) {
- return false
- }
- }
- return true
-}
-
-func isVolumeClaimTemplatesEqual(a, b []appsv1.ClusterComponentVolumeClaimTemplate) bool {
- if len(a) != len(b) {
- return false
- }
-
- for i := range a {
- // first check resource requirements
- c := a[i].DeepCopy()
- d := b[i].DeepCopy()
- if !isVolumeResourceRequirementsEqual(c.Spec.Resources, d.Spec.Resources) {
- return false
- }
-
- // then clear resource requirements and check other fields
- c.Spec.Resources = corev1.VolumeResourceRequirements{}
- d.Spec.Resources = corev1.VolumeResourceRequirements{}
- if !reflect.DeepEqual(c, d) {
- return false
- }
- }
- return true
-}
-
// isOwnedByComp is used to judge if the obj is owned by Component.
func isOwnedByComp(obj client.Object) bool {
for _, ref := range obj.GetOwnerReferences() {
diff --git a/controllers/apps/transform_utils_test.go b/controllers/apps/cluster/utils_test.go
similarity index 67%
rename from controllers/apps/transform_utils_test.go
rename to controllers/apps/cluster/utils_test.go
index 2e5e34e583a..082b1be86cd 100644
--- a/controllers/apps/transform_utils_test.go
+++ b/controllers/apps/cluster/utils_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package cluster
import (
"fmt"
@@ -26,8 +26,6 @@ import (
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
- corev1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -61,39 +59,6 @@ func TestReflect(t *testing.T) {
fmt.Println(v)
}
-func TestIsVolumeClaimTemplatesEqual(t *testing.T) {
- buildVCT := func(size string) []kbappsv1.ClusterComponentVolumeClaimTemplate {
- return []kbappsv1.ClusterComponentVolumeClaimTemplate{
- {
- Name: "data",
- Spec: kbappsv1.PersistentVolumeClaimSpec{
- Resources: corev1.VolumeResourceRequirements{
- Requests: corev1.ResourceList{
- corev1.ResourceStorage: resource.MustParse(size),
- },
- },
- },
- },
- }
- }
-
- assert.True(t, isVolumeClaimTemplatesEqual(buildVCT("1Gi"), buildVCT("1024Mi")))
-}
-
-func TestIsResourceRequirementsEqual(t *testing.T) {
- buildRR := func(cpu, memory string) corev1.ResourceRequirements {
- return corev1.ResourceRequirements{
- Requests: corev1.ResourceList{
- corev1.ResourceCPU: resource.MustParse(cpu),
- corev1.ResourceMemory: resource.MustParse(memory),
- },
- }
- }
- a := buildRR("1", "1Gi")
- b := buildRR("1000m", "1024Mi")
- assert.True(t, isResourceRequirementsEqual(a, b))
-}
-
func TestIsOwnedByInstanceSet(t *testing.T) {
its := &workloads.InstanceSet{}
assert.False(t, isOwnedByInstanceSet(its))
diff --git a/controllers/apps/clusterdefinition_controller.go b/controllers/apps/clusterdefinition_controller.go
index 22134fc2304..64a444f9058 100644
--- a/controllers/apps/clusterdefinition_controller.go
+++ b/controllers/apps/clusterdefinition_controller.go
@@ -39,6 +39,10 @@ import (
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
)
+const (
+ clusterDefinitionFinalizerName = "clusterdefinition.kubeblocks.io/finalizer"
+)
+
// +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=clusterdefinitions,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=clusterdefinitions/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=clusterdefinitions/finalizers,verbs=update
@@ -358,38 +362,3 @@ func (r *ClusterDefinitionReconciler) validateTopologyOrders(topology appsv1.Clu
}
return nil
}
-
-// defaultClusterTopology returns the default cluster topology in specified cluster definition.
-func defaultClusterTopology(clusterDef *appsv1.ClusterDefinition) *appsv1.ClusterTopology {
- for i, topology := range clusterDef.Spec.Topologies {
- if topology.Default {
- return &clusterDef.Spec.Topologies[i]
- }
- }
- return nil
-}
-
-// referredClusterTopology returns the cluster topology which has name @name.
-func referredClusterTopology(clusterDef *appsv1.ClusterDefinition, name string) *appsv1.ClusterTopology {
- if clusterDef != nil {
- if len(name) == 0 {
- return defaultClusterTopology(clusterDef)
- }
- for i, topology := range clusterDef.Spec.Topologies {
- if topology.Name == name {
- return &clusterDef.Spec.Topologies[i]
- }
- }
- }
- return nil
-}
-
-func clusterTopologyCompMatched(comp appsv1.ClusterTopologyComponent, compName string) bool {
- if comp.Name == compName {
- return true
- }
- if comp.Template != nil && *comp.Template {
- return strings.HasPrefix(compName, comp.Name)
- }
- return false
-}
diff --git a/controllers/apps/component_controller.go b/controllers/apps/component/component_controller.go
similarity index 99%
rename from controllers/apps/component_controller.go
rename to controllers/apps/component/component_controller.go
index a2bd75a6ae2..c097ea79abf 100644
--- a/controllers/apps/component_controller.go
+++ b/controllers/apps/component/component_controller.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"context"
diff --git a/controllers/apps/component_controller_test.go b/controllers/apps/component/component_controller_test.go
similarity index 99%
rename from controllers/apps/component_controller_test.go
rename to controllers/apps/component/component_controller_test.go
index b98dda0faed..8dc36dc1039 100644
--- a/controllers/apps/component_controller_test.go
+++ b/controllers/apps/component/component_controller_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"fmt"
diff --git a/controllers/apps/component_plan_builder.go b/controllers/apps/component/component_plan_builder.go
similarity index 97%
rename from controllers/apps/component_plan_builder.go
rename to controllers/apps/component/component_plan_builder.go
index 595684ab2af..a366dcfe909 100644
--- a/controllers/apps/component_plan_builder.go
+++ b/controllers/apps/component/component_plan_builder.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"context"
@@ -105,11 +105,6 @@ func (c *componentPlanBuilder) AddTransformer(transformer ...graph.Transformer)
return c
}
-func (c *componentPlanBuilder) AddParallelTransformer(transformer ...graph.Transformer) graph.PlanBuilder {
- c.transformers = append(c.transformers, &ParallelTransformers{transformers: transformer})
- return c
-}
-
// Build runs all transformers to generate a plan
func (c *componentPlanBuilder) Build() (graph.Plan, error) {
dag := graph.NewDAG()
diff --git a/controllers/apps/component_plan_builder_test.go b/controllers/apps/component/component_plan_builder_test.go
similarity index 99%
rename from controllers/apps/component_plan_builder_test.go
rename to controllers/apps/component/component_plan_builder_test.go
index 90c7ca3b4f8..5b394d035b9 100644
--- a/controllers/apps/component_plan_builder_test.go
+++ b/controllers/apps/component/component_plan_builder_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
. "github.com/onsi/ginkgo/v2"
diff --git a/controllers/apps/component/suite_test.go b/controllers/apps/component/suite_test.go
new file mode 100644
index 00000000000..d6d194373f6
--- /dev/null
+++ b/controllers/apps/component/suite_test.go
@@ -0,0 +1,283 @@
+/*
+Copyright (C) 2022-2024 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package component
+
+import (
+ "context"
+ "fmt"
+ "go/build"
+ "path/filepath"
+ "testing"
+
+ . "github.com/onsi/ginkgo/v2"
+ . "github.com/onsi/gomega"
+
+ "github.com/go-logr/logr"
+ snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
+ "go.uber.org/zap/zapcore"
+ "k8s.io/client-go/kubernetes/scheme"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/record"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/envtest"
+ logf "sigs.k8s.io/controller-runtime/pkg/log"
+ "sigs.k8s.io/controller-runtime/pkg/log/zap"
+ "sigs.k8s.io/controller-runtime/pkg/metrics/server"
+
+ appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+ appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1"
+ appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1"
+ dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
+ opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1"
+ workloadsv1 "github.com/apecloud/kubeblocks/apis/workloads/v1"
+ "github.com/apecloud/kubeblocks/controllers/apps"
+ "github.com/apecloud/kubeblocks/controllers/apps/cluster"
+ "github.com/apecloud/kubeblocks/controllers/apps/configuration"
+ "github.com/apecloud/kubeblocks/controllers/dataprotection"
+ "github.com/apecloud/kubeblocks/controllers/k8score"
+ "github.com/apecloud/kubeblocks/pkg/constant"
+ "github.com/apecloud/kubeblocks/pkg/controller/model"
+ intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
+ "github.com/apecloud/kubeblocks/pkg/testutil"
+ viper "github.com/apecloud/kubeblocks/pkg/viperx"
+)
+
+// These tests use Ginkgo (BDD-style Go testing framework). Refer to
+// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
+
+const (
+ testDataPlaneNodeAffinityKey = "testDataPlaneNodeAffinityKey"
+ testDataPlaneTolerationKey = "testDataPlaneTolerationKey"
+)
+
+var cfg *rest.Config
+var k8sClient client.Client
+var testEnv *envtest.Environment
+var ctx context.Context
+var cancel context.CancelFunc
+var testCtx testutil.TestContext
+var clusterRecorder record.EventRecorder
+var logger logr.Logger
+
+func init() {
+ viper.AutomaticEnv()
+ // viper.Set("ENABLE_DEBUG_LOG", "true")
+}
+
+func TestAPIs(t *testing.T) {
+ RegisterFailHandler(Fail)
+
+ RunSpecs(t, "Controller Suite")
+}
+
+var _ = BeforeSuite(func() {
+ if viper.GetBool("ENABLE_DEBUG_LOG") {
+ logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true), func(o *zap.Options) {
+ o.TimeEncoder = zapcore.ISO8601TimeEncoder
+ }))
+ } else {
+ logf.SetLogger(logr.New(logf.NullLogSink{}))
+ }
+
+ viper.SetDefault(constant.CfgKeyCtrlrReconcileRetryDurationMS, 10)
+ viper.Set(constant.CfgKeyDataPlaneTolerations,
+ fmt.Sprintf("[{\"key\":\"%s\", \"operator\": \"Exists\", \"effect\": \"NoSchedule\"}]", testDataPlaneTolerationKey))
+ viper.Set(constant.CfgKeyDataPlaneAffinity,
+ fmt.Sprintf("{\"nodeAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"preference\":{\"matchExpressions\":[{\"key\":\"%s\",\"operator\":\"In\",\"values\":[\"true\"]}]},\"weight\":100}]}}", testDataPlaneNodeAffinityKey))
+
+ ctx, cancel = context.WithCancel(context.TODO())
+ logger = logf.FromContext(ctx).WithValues()
+ logger.Info("logger start")
+
+ By("bootstrapping test environment")
+ testEnv = &envtest.Environment{
+ CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases"),
+ // use dependent external CRDs.
+ // resolved by ref: https://github.com/operator-framework/operator-sdk/issues/4434#issuecomment-786794418
+ filepath.Join(build.Default.GOPATH, "pkg", "mod", "github.com", "kubernetes-csi/external-snapshotter/",
+ "client/v6@v6.2.0", "config", "crd"),
+ },
+ ErrorIfCRDPathMissing: true,
+ }
+
+ var err error
+ // cfg is defined in this file globally.
+ cfg, err = testEnv.Start()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(cfg).NotTo(BeNil())
+
+ err = appsv1alpha1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(appsv1alpha1.AddToScheme)
+
+ err = opsv1alpha1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(opsv1alpha1.AddToScheme)
+
+ err = appsv1beta1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(appsv1beta1.AddToScheme)
+
+ err = appsv1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(appsv1.AddToScheme)
+
+ err = dpv1alpha1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(dpv1alpha1.AddToScheme)
+
+ err = snapshotv1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(snapshotv1.AddToScheme)
+
+ err = workloadsv1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
+ model.AddScheme(workloadsv1.AddToScheme)
+
+ // +kubebuilder:scaffold:rscheme
+
+ k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient).NotTo(BeNil())
+
+ // run reconcile
+ k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
+ Scheme: scheme.Scheme,
+ Metrics: server.Options{BindAddress: "0"},
+ Client: client.Options{
+ Cache: &client.CacheOptions{
+ DisableFor: intctrlutil.GetUncachedObjects(),
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ viper.SetDefault("CERT_DIR", "/tmp/k8s-webhook-server/serving-certs")
+ viper.SetDefault(constant.KBToolsImage, "docker.io/apecloud/kubeblocks-tools:latest")
+ viper.SetDefault("PROBE_SERVICE_PORT", 3501)
+ viper.SetDefault("PROBE_SERVICE_LOG_LEVEL", "info")
+ viper.SetDefault("CM_NAMESPACE", "default")
+ viper.SetDefault("HOST_PORT_CM_NAME", "kubeblocks-host-ports")
+ viper.SetDefault(constant.EnableRBACManager, true)
+
+ err = intctrlutil.InitHostPortManager(k8sClient)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ClusterDefinitionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("cluster-definition-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ShardingDefinitionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("sharding-definition-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ComponentDefinitionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("component-definition-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ComponentVersionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("component-version-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.SidecarDefinitionReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("sidecar-definition-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ clusterRecorder = k8sManager.GetEventRecorderFor("cluster-controller")
+ err = (&cluster.ClusterReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: clusterRecorder,
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&ComponentReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("component-controller"),
+ }).SetupWithManager(k8sManager, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&apps.ServiceDescriptorReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("service-descriptor-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&k8score.EventReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("event-controller"),
+ }).SetupWithManager(k8sManager, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&configuration.ConfigConstraintReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("configuration-template-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&configuration.ConfigurationReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("configuration-controller"),
+ }).SetupWithManager(k8sManager, nil)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = (&dataprotection.BackupPolicyTemplateReconciler{
+ Client: k8sManager.GetClient(),
+ Scheme: k8sManager.GetScheme(),
+ Recorder: k8sManager.GetEventRecorderFor("backup-policy-template-controller"),
+ }).SetupWithManager(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
+ testCtx = testutil.NewDefaultTestContext(ctx, k8sClient, testEnv)
+
+ go func() {
+ defer GinkgoRecover()
+ err = k8sManager.Start(ctx)
+ Expect(err).ToNot(HaveOccurred(), "failed to run manager")
+ }()
+})
+
+var _ = AfterSuite(func() {
+ cancel()
+ By("tearing down the test environment")
+ err := testEnv.Stop()
+ Expect(err).NotTo(HaveOccurred())
+})
diff --git a/controllers/apps/component/test_utils.go b/controllers/apps/component/test_utils.go
new file mode 100644
index 00000000000..a3d4284138a
--- /dev/null
+++ b/controllers/apps/component/test_utils.go
@@ -0,0 +1,72 @@
+/*
+Copyright (C) 2022-2024 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package component
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+type mockReader struct {
+ objs []client.Object
+}
+
+func (r *mockReader) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
+ for _, o := range r.objs {
+ // ignore the GVK check
+ if client.ObjectKeyFromObject(o) == key {
+ reflect.ValueOf(obj).Elem().Set(reflect.ValueOf(o).Elem())
+ return nil
+ }
+ }
+ return apierrors.NewNotFound(schema.GroupResource{}, key.Name)
+}
+
+func (r *mockReader) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
+ items := reflect.ValueOf(list).Elem().FieldByName("Items")
+ if !items.IsValid() {
+ return fmt.Errorf("ObjectList has no Items field: %s", list.GetObjectKind().GroupVersionKind().String())
+ }
+
+ objs := reflect.MakeSlice(items.Type(), 0, 0)
+ if len(r.objs) > 0 {
+ listOpts := &client.ListOptions{}
+ for _, opt := range opts {
+ opt.ApplyToList(listOpts)
+ }
+
+ for i, o := range r.objs {
+ if reflect.TypeOf(r.objs[i]).Elem().AssignableTo(items.Type().Elem()) {
+ if listOpts.LabelSelector == nil || listOpts.LabelSelector.Matches(labels.Set(o.GetLabels())) {
+ objs = reflect.Append(objs, reflect.ValueOf(r.objs[i]).Elem())
+ }
+ }
+ }
+ }
+ items.Set(objs)
+
+ return nil
+}
diff --git a/controllers/apps/transformer_component_account.go b/controllers/apps/component/transformer_component_account.go
similarity index 99%
rename from controllers/apps/transformer_component_account.go
rename to controllers/apps/component/transformer_component_account.go
index 71412527443..909f6526958 100644
--- a/controllers/apps/transformer_component_account.go
+++ b/controllers/apps/component/transformer_component_account.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"fmt"
diff --git a/controllers/apps/transformer_component_account_provision.go b/controllers/apps/component/transformer_component_account_provision.go
similarity index 99%
rename from controllers/apps/transformer_component_account_provision.go
rename to controllers/apps/component/transformer_component_account_provision.go
index 1598da82963..35df58f7fef 100644
--- a/controllers/apps/transformer_component_account_provision.go
+++ b/controllers/apps/component/transformer_component_account_provision.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"slices"
diff --git a/controllers/apps/transformer_component_configuration.go b/controllers/apps/component/transformer_component_configuration.go
similarity index 99%
rename from controllers/apps/transformer_component_configuration.go
rename to controllers/apps/component/transformer_component_configuration.go
index 26744822678..21f12781bfd 100644
--- a/controllers/apps/transformer_component_configuration.go
+++ b/controllers/apps/component/transformer_component_configuration.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
corev1 "k8s.io/api/core/v1"
diff --git a/controllers/apps/transformer_component_deletion.go b/controllers/apps/component/transformer_component_deletion.go
similarity index 99%
rename from controllers/apps/transformer_component_deletion.go
rename to controllers/apps/component/transformer_component_deletion.go
index fe7e58beff3..2c35763798b 100644
--- a/controllers/apps/transformer_component_deletion.go
+++ b/controllers/apps/component/transformer_component_deletion.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"fmt"
@@ -72,7 +72,7 @@ func (t *componentDeletionTransformer) Transform(ctx graph.TransformContext, dag
ml := constant.GetCompLabels(cluster.Name, compShortName)
compScaleIn, ok := comp.Annotations[constant.ComponentScaleInAnnotationKey]
- if ok && compScaleIn == trueVal {
+ if ok && compScaleIn == "true" {
return t.handleCompDeleteWhenScaleIn(transCtx, graphCli, dag, comp, ml)
}
return t.handleCompDeleteWhenClusterDelete(transCtx, graphCli, dag, cluster, comp, ml)
diff --git a/controllers/apps/transformer_component_hostnetwork.go b/controllers/apps/component/transformer_component_hostnetwork.go
similarity index 99%
rename from controllers/apps/transformer_component_hostnetwork.go
rename to controllers/apps/component/transformer_component_hostnetwork.go
index ec3ab5fb348..c216f986c5a 100644
--- a/controllers/apps/transformer_component_hostnetwork.go
+++ b/controllers/apps/component/transformer_component_hostnetwork.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
corev1 "k8s.io/api/core/v1"
diff --git a/controllers/apps/transformer_component_hostnetwork_test.go b/controllers/apps/component/transformer_component_hostnetwork_test.go
similarity index 99%
rename from controllers/apps/transformer_component_hostnetwork_test.go
rename to controllers/apps/component/transformer_component_hostnetwork_test.go
index adae0455ec7..45bff28e550 100644
--- a/controllers/apps/transformer_component_hostnetwork_test.go
+++ b/controllers/apps/component/transformer_component_hostnetwork_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
. "github.com/onsi/ginkgo/v2"
diff --git a/controllers/apps/transformer_component_init.go b/controllers/apps/component/transformer_component_init.go
similarity index 98%
rename from controllers/apps/transformer_component_init.go
rename to controllers/apps/component/transformer_component_init.go
index 0564c45b9b1..1a898f1746b 100644
--- a/controllers/apps/transformer_component_init.go
+++ b/controllers/apps/component/transformer_component_init.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"github.com/apecloud/kubeblocks/pkg/controller/graph"
diff --git a/controllers/apps/transformer_component_load_resources.go b/controllers/apps/component/transformer_component_load_resources.go
similarity index 80%
rename from controllers/apps/transformer_component_load_resources.go
rename to controllers/apps/component/transformer_component_load_resources.go
index deec5ce05e3..f5204308237 100644
--- a/controllers/apps/transformer_component_load_resources.go
+++ b/controllers/apps/component/transformer_component_load_resources.go
@@ -17,12 +17,14 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
+ "context"
"fmt"
"k8s.io/apimachinery/pkg/types"
+ "sigs.k8s.io/controller-runtime/pkg/client"
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
"github.com/apecloud/kubeblocks/pkg/controller/component"
@@ -82,3 +84,20 @@ func (t *componentLoadResourcesTransformer) transformForNativeComponent(transCtx
return nil
}
+
+func getNCheckCompDefinition(ctx context.Context, cli client.Reader, name string) (*appsv1.ComponentDefinition, error) {
+ compKey := types.NamespacedName{
+ Name: name,
+ }
+ compDef := &appsv1.ComponentDefinition{}
+ if err := cli.Get(ctx, compKey, compDef); err != nil {
+ return nil, err
+ }
+ if compDef.Generation != compDef.Status.ObservedGeneration {
+ return nil, fmt.Errorf("the referenced ComponentDefinition is not up to date: %s", compDef.Name)
+ }
+ if compDef.Status.Phase != appsv1.AvailablePhase {
+ return nil, fmt.Errorf("the referenced ComponentDefinition is unavailable: %s", compDef.Name)
+ }
+ return compDef, nil
+}
diff --git a/controllers/apps/transformer_component_meta.go b/controllers/apps/component/transformer_component_meta.go
similarity index 99%
rename from controllers/apps/transformer_component_meta.go
rename to controllers/apps/component/transformer_component_meta.go
index 97d5c37ce49..7762928214f 100644
--- a/controllers/apps/transformer_component_meta.go
+++ b/controllers/apps/component/transformer_component_meta.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"reflect"
diff --git a/controllers/apps/transformer_component_monitor_transformer.go b/controllers/apps/component/transformer_component_monitor_transformer.go
similarity index 99%
rename from controllers/apps/transformer_component_monitor_transformer.go
rename to controllers/apps/component/transformer_component_monitor_transformer.go
index f49a7be52ad..127a3d78c92 100644
--- a/controllers/apps/transformer_component_monitor_transformer.go
+++ b/controllers/apps/component/transformer_component_monitor_transformer.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package apps
+package component
import (
"slices"
diff --git a/controllers/apps/transformer_component_parameters.go b/controllers/apps/component/transformer_component_parameters.go
similarity index 99%
rename from controllers/apps/transformer_component_parameters.go
rename to controllers/apps/component/transformer_component_parameters.go
index b1e2996788e..d4b5f7c8531 100644
--- a/controllers/apps/transformer_component_parameters.go
+++ b/controllers/apps/component/transformer_component_parameters.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"sigs.k8s.io/controller-runtime/pkg/client"
diff --git a/controllers/apps/transformer_component_post_provision.go b/controllers/apps/component/transformer_component_post_provision.go
similarity index 99%
rename from controllers/apps/transformer_component_post_provision.go
rename to controllers/apps/component/transformer_component_post_provision.go
index f8ebb050815..06e08b82f7a 100644
--- a/controllers/apps/transformer_component_post_provision.go
+++ b/controllers/apps/component/transformer_component_post_provision.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"fmt"
diff --git a/controllers/apps/transformer_component_pre_terminate.go b/controllers/apps/component/transformer_component_pre_terminate.go
similarity index 99%
rename from controllers/apps/transformer_component_pre_terminate.go
rename to controllers/apps/component/transformer_component_pre_terminate.go
index 26f8769eecf..a9de13c6620 100644
--- a/controllers/apps/transformer_component_pre_terminate.go
+++ b/controllers/apps/component/transformer_component_pre_terminate.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"fmt"
diff --git a/controllers/apps/transformer_component_rbac.go b/controllers/apps/component/transformer_component_rbac.go
similarity index 99%
rename from controllers/apps/transformer_component_rbac.go
rename to controllers/apps/component/transformer_component_rbac.go
index 46e9e4ed74a..eef1d491498 100644
--- a/controllers/apps/transformer_component_rbac.go
+++ b/controllers/apps/component/transformer_component_rbac.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"fmt"
diff --git a/controllers/apps/transformer_component_rbac_test.go b/controllers/apps/component/transformer_component_rbac_test.go
similarity index 99%
rename from controllers/apps/transformer_component_rbac_test.go
rename to controllers/apps/component/transformer_component_rbac_test.go
index 34fbc4afdc4..db8116b1ec8 100644
--- a/controllers/apps/transformer_component_rbac_test.go
+++ b/controllers/apps/component/transformer_component_rbac_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
. "github.com/onsi/ginkgo/v2"
diff --git a/controllers/apps/transformer_component_restore.go b/controllers/apps/component/transformer_component_restore.go
similarity index 99%
rename from controllers/apps/transformer_component_restore.go
rename to controllers/apps/component/transformer_component_restore.go
index 9add3fc1a48..da5ee3f067f 100644
--- a/controllers/apps/transformer_component_restore.go
+++ b/controllers/apps/component/transformer_component_restore.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"reflect"
diff --git a/controllers/apps/transformer_component_service.go b/controllers/apps/component/transformer_component_service.go
similarity index 99%
rename from controllers/apps/transformer_component_service.go
rename to controllers/apps/component/transformer_component_service.go
index ba2ae5368b9..685f1952b59 100644
--- a/controllers/apps/transformer_component_service.go
+++ b/controllers/apps/component/transformer_component_service.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"context"
diff --git a/controllers/apps/transformer_component_service_test.go b/controllers/apps/component/transformer_component_service_test.go
similarity index 99%
rename from controllers/apps/transformer_component_service_test.go
rename to controllers/apps/component/transformer_component_service_test.go
index 8d53e8c727e..bc9c52acb39 100644
--- a/controllers/apps/transformer_component_service_test.go
+++ b/controllers/apps/component/transformer_component_service_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"fmt"
diff --git a/controllers/apps/transformer_component_status.go b/controllers/apps/component/transformer_component_status.go
similarity index 99%
rename from controllers/apps/transformer_component_status.go
rename to controllers/apps/component/transformer_component_status.go
index e049518b618..f0c42c17647 100644
--- a/controllers/apps/transformer_component_status.go
+++ b/controllers/apps/component/transformer_component_status.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"fmt"
diff --git a/controllers/apps/transformer_component_tls.go b/controllers/apps/component/transformer_component_tls.go
similarity index 99%
rename from controllers/apps/transformer_component_tls.go
rename to controllers/apps/component/transformer_component_tls.go
index 4a2e72f7858..7b855eddaf4 100644
--- a/controllers/apps/transformer_component_tls.go
+++ b/controllers/apps/component/transformer_component_tls.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"context"
diff --git a/controllers/apps/transformer_component_tls_test.go b/controllers/apps/component/transformer_component_tls_test.go
similarity index 99%
rename from controllers/apps/transformer_component_tls_test.go
rename to controllers/apps/component/transformer_component_tls_test.go
index bdc75b4b587..71ef04dc832 100644
--- a/controllers/apps/transformer_component_tls_test.go
+++ b/controllers/apps/component/transformer_component_tls_test.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"context"
diff --git a/controllers/apps/transformer_component_utils.go b/controllers/apps/component/transformer_component_utils.go
similarity index 99%
rename from controllers/apps/transformer_component_utils.go
rename to controllers/apps/component/transformer_component_utils.go
index 47382baa6d8..80ac1eee756 100644
--- a/controllers/apps/transformer_component_utils.go
+++ b/controllers/apps/component/transformer_component_utils.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"reflect"
diff --git a/controllers/apps/transformer_component_validation.go b/controllers/apps/component/transformer_component_validation.go
similarity index 99%
rename from controllers/apps/transformer_component_validation.go
rename to controllers/apps/component/transformer_component_validation.go
index 48e7be416b0..6fe0186788e 100644
--- a/controllers/apps/transformer_component_validation.go
+++ b/controllers/apps/component/transformer_component_validation.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"fmt"
diff --git a/controllers/apps/transformer_component_vars.go b/controllers/apps/component/transformer_component_vars.go
similarity index 99%
rename from controllers/apps/transformer_component_vars.go
rename to controllers/apps/component/transformer_component_vars.go
index b732118844e..210a3ad05fd 100644
--- a/controllers/apps/transformer_component_vars.go
+++ b/controllers/apps/component/transformer_component_vars.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"context"
diff --git a/controllers/apps/transformer_component_workload.go b/controllers/apps/component/transformer_component_workload.go
similarity index 99%
rename from controllers/apps/transformer_component_workload.go
rename to controllers/apps/component/transformer_component_workload.go
index ce94edfaba4..41989756937 100644
--- a/controllers/apps/transformer_component_workload.go
+++ b/controllers/apps/component/transformer_component_workload.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"context"
diff --git a/controllers/apps/transformer_component_workload_test.go b/controllers/apps/component/transformer_component_workload_test.go
similarity index 99%
rename from controllers/apps/transformer_component_workload_test.go
rename to controllers/apps/component/transformer_component_workload_test.go
index 68c44987685..680cc726bc1 100644
--- a/controllers/apps/transformer_component_workload_test.go
+++ b/controllers/apps/component/transformer_component_workload_test.go
@@ -13,14 +13,15 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"context"
- "github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
+
+ "github.com/golang/mock/gomock"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
diff --git a/controllers/apps/component/types.go b/controllers/apps/component/types.go
new file mode 100644
index 00000000000..ff6ecf6f3ad
--- /dev/null
+++ b/controllers/apps/component/types.go
@@ -0,0 +1,51 @@
+/*
+Copyright (C) 2022-2024 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package component
+
+import (
+ snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
+ batchv1 "k8s.io/api/batch/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ clientgoscheme "k8s.io/client-go/kubernetes/scheme"
+
+ appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+ appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1"
+ appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1"
+ dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
+ extensionsv1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1"
+ workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
+)
+
+var (
+ rscheme = runtime.NewScheme()
+)
+
+func init() {
+ utilruntime.Must(clientgoscheme.AddToScheme(rscheme))
+ utilruntime.Must(appsv1alpha1.AddToScheme(rscheme))
+ utilruntime.Must(appsv1beta1.AddToScheme(rscheme))
+ utilruntime.Must(appsv1.AddToScheme(rscheme))
+ utilruntime.Must(dpv1alpha1.AddToScheme(rscheme))
+ utilruntime.Must(snapshotv1.AddToScheme(rscheme))
+ utilruntime.Must(extensionsv1alpha1.AddToScheme(rscheme))
+ utilruntime.Must(batchv1.AddToScheme(rscheme))
+ utilruntime.Must(workloads.AddToScheme(rscheme))
+}
diff --git a/controllers/apps/utils.go b/controllers/apps/component/utils.go
similarity index 57%
rename from controllers/apps/utils.go
rename to controllers/apps/component/utils.go
index 72beb7f3756..7b5ec0746d4 100644
--- a/controllers/apps/utils.go
+++ b/controllers/apps/component/utils.go
@@ -17,7 +17,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-package apps
+package component
import (
"context"
@@ -26,27 +26,46 @@ import (
"time"
corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
+ appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+ workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/model"
"github.com/apecloud/kubeblocks/pkg/controller/multicluster"
+ intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
+)
+
+const (
+ ReasonPreCheckSucceed = "PreCheckSucceed" // ReasonPreCheckSucceed preChecks succeeded for provisioning started
+ ReasonPreCheckFailed = "PreCheckFailed" // ReasonPreCheckFailed preChecks failed for provisioning started
)
// default reconcile requeue after duration
var requeueDuration = time.Millisecond * 1000
-func boolValue(b *bool) bool {
- if b == nil {
- return false
- }
- return *b
+func newRequeueError(after time.Duration, reason string) error {
+ return intctrlutil.NewRequeueError(after, reason)
}
-func mergeMap(dst, src map[string]string) {
- for key, val := range src {
- dst[key] = val
+// sendWarningEventWithError sends a warning event when occurs error.
+func sendWarningEventWithError(
+ recorder record.EventRecorder,
+ obj client.Object,
+ reason string,
+ err error) {
+ // ignore requeue error
+ if err == nil || intctrlutil.IsRequeueError(err) {
+ return
+ }
+ controllerErr := intctrlutil.UnwrapControllerError(err)
+ if controllerErr != nil {
+ reason = string(controllerErr.Type)
}
+ recorder.Event(obj, corev1.EventTypeWarning, reason, err.Error())
}
func placement(obj client.Object) string {
@@ -64,10 +83,6 @@ func inDataContext4C() *multicluster.ClientOption {
return multicluster.InDataContext()
}
-func inUniversalContext4C() *multicluster.ClientOption {
- return multicluster.InUniversalContext()
-}
-
func inDataContext4G() model.GraphOption {
return model.WithClientOption(multicluster.InDataContext())
}
@@ -86,6 +101,7 @@ func clientOption(v *model.ObjectVertex) *multicluster.ClientOption {
}
return multicluster.InControlContext()
}
+
func resolveServiceDefaultFields(oldSpec, newSpec *corev1.ServiceSpec) {
var exist *corev1.ServicePort
for i, port := range newSpec.Ports {
@@ -149,3 +165,55 @@ func shouldAllocateNodePorts(svc *corev1.ServiceSpec) bool {
}
return false
}
+
+// isOwnedByInstanceSet is used to judge if the obj is owned by the InstanceSet controller
+func isOwnedByInstanceSet(obj client.Object) bool {
+ for _, ref := range obj.GetOwnerReferences() {
+ if ref.Kind == workloads.InstanceSetKind && ref.Controller != nil && *ref.Controller {
+ return true
+ }
+ }
+ return false
+}
+
+func setProvisioningStartedCondition(conditions *[]metav1.Condition, clusterName string, clusterGeneration int64, err error) {
+ var condition metav1.Condition
+ if err == nil {
+ condition = newProvisioningStartedCondition(clusterName, clusterGeneration)
+ } else {
+ condition = newFailedProvisioningStartedCondition(err)
+ }
+ meta.SetStatusCondition(conditions, condition)
+}
+
+// newProvisioningStartedCondition creates the provisioning started condition in cluster conditions.
+func newProvisioningStartedCondition(clusterName string, clusterGeneration int64) metav1.Condition {
+ return metav1.Condition{
+ Type: appsv1.ConditionTypeProvisioningStarted,
+ ObservedGeneration: clusterGeneration,
+ Status: metav1.ConditionTrue,
+ Message: fmt.Sprintf("The operator has started the provisioning of Cluster: %s", clusterName),
+ Reason: ReasonPreCheckSucceed,
+ }
+}
+
+func getConditionReasonWithError(defaultReason string, err error) string {
+ if err == nil {
+ return defaultReason
+ }
+ controllerErr := intctrlutil.UnwrapControllerError(err)
+ if controllerErr != nil {
+ defaultReason = string(controllerErr.Type)
+ }
+ return defaultReason
+}
+
+// newApplyResourcesCondition creates a condition when applied resources succeed.
+func newFailedProvisioningStartedCondition(err error) metav1.Condition {
+ return metav1.Condition{
+ Type: appsv1.ConditionTypeProvisioningStarted,
+ Status: metav1.ConditionFalse,
+ Message: err.Error(),
+ Reason: getConditionReasonWithError(ReasonPreCheckFailed, err),
+ }
+}
diff --git a/controllers/apps/component/utils_test.go b/controllers/apps/component/utils_test.go
new file mode 100644
index 00000000000..1974121fe7a
--- /dev/null
+++ b/controllers/apps/component/utils_test.go
@@ -0,0 +1,81 @@
+/*
+Copyright (C) 2022-2024 ApeCloud Co., Ltd
+
+This file is part of KubeBlocks project
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package component
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ appsv1 "k8s.io/api/apps/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/utils/pointer"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+ workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
+)
+
+func TestReflect(t *testing.T) {
+ var list client.ObjectList
+ sts := appsv1.StatefulSet{}
+ sts.SetName("hello")
+ list = &appsv1.StatefulSetList{Items: []appsv1.StatefulSet{sts}}
+ v := reflect.ValueOf(list).Elem().FieldByName("Items")
+ if v.Kind() != reflect.Slice {
+ t.Error("not slice")
+ }
+ c := v.Len()
+ objects := make([]client.Object, c)
+ for i := 0; i < c; i++ {
+ var st = v.Index(i).Addr().Interface()
+ objects[i] = st.(client.Object)
+ }
+ for _, e := range objects {
+ fmt.Println(e)
+ }
+
+ var o client.Object = &sts
+ ptr := reflect.ValueOf(o)
+ v = ptr.Elem().FieldByName("Spec")
+ fmt.Println(v)
+}
+
+func TestIsOwnedByInstanceSet(t *testing.T) {
+ its := &workloads.InstanceSet{}
+ assert.False(t, isOwnedByInstanceSet(its))
+
+ its.OwnerReferences = []metav1.OwnerReference{
+ {
+ Kind: workloads.InstanceSetKind,
+ Controller: pointer.Bool(true),
+ },
+ }
+ assert.True(t, isOwnedByInstanceSet(its))
+
+ its.OwnerReferences = []metav1.OwnerReference{
+ {
+ Kind: reflect.TypeOf(kbappsv1.Cluster{}).Name(),
+ Controller: pointer.Bool(true),
+ },
+ }
+ assert.False(t, isOwnedByInstanceSet(its))
+}
diff --git a/controllers/apps/componentdefinition_controller.go b/controllers/apps/componentdefinition_controller.go
index ed7518649f8..8ea751bc945 100644
--- a/controllers/apps/componentdefinition_controller.go
+++ b/controllers/apps/componentdefinition_controller.go
@@ -31,7 +31,6 @@ import (
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
- "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/rand"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/record"
@@ -47,7 +46,8 @@ import (
)
const (
- immutableHashAnnotationKey = "apps.kubeblocks.io/immutable-hash"
+ componentDefinitionFinalizerName = "componentdefinition.kubeblocks.io/finalizer"
+ immutableHashAnnotationKey = "apps.kubeblocks.io/immutable-hash"
)
// ComponentDefinitionReconciler reconciles a ComponentDefinition object
@@ -470,23 +470,6 @@ func (r *ComponentDefinitionReconciler) cmpdHash(cmpd *appsv1.ComponentDefinitio
return rand.SafeEncodeString(fmt.Sprintf("%d", hash.Sum32())), nil
}
-func getNCheckCompDefinition(ctx context.Context, cli client.Reader, name string) (*appsv1.ComponentDefinition, error) {
- compKey := types.NamespacedName{
- Name: name,
- }
- compDef := &appsv1.ComponentDefinition{}
- if err := cli.Get(ctx, compKey, compDef); err != nil {
- return nil, err
- }
- if compDef.Generation != compDef.Status.ObservedGeneration {
- return nil, fmt.Errorf("the referenced ComponentDefinition is not up to date: %s", compDef.Name)
- }
- if compDef.Status.Phase != appsv1.AvailablePhase {
- return nil, fmt.Errorf("the referenced ComponentDefinition is unavailable: %s", compDef.Name)
- }
- return compDef, nil
-}
-
// listCompDefinitionsWithPattern returns all component definitions whose names match the given pattern
func listCompDefinitionsWithPattern(ctx context.Context, cli client.Reader, name string) ([]*appsv1.ComponentDefinition, error) {
compDefList := &appsv1.ComponentDefinitionList{}
diff --git a/controllers/apps/componentversion_controller.go b/controllers/apps/componentversion_controller.go
index f156930a4cc..a91ea2d23bc 100644
--- a/controllers/apps/componentversion_controller.go
+++ b/controllers/apps/componentversion_controller.go
@@ -48,7 +48,8 @@ import (
)
const (
- compatibleDefinitionsKey = "componentversion.kubeblocks.io/compatible-definitions"
+ componentVersionFinalizerName = "componentversion.kubeblocks.io/finalizer"
+ compatibleDefinitionsKey = "componentversion.kubeblocks.io/compatible-definitions"
)
// ComponentVersionReconciler reconciles a ComponentVersion object
@@ -393,110 +394,6 @@ func validateCompatibilityRulesCompDef(compVersion *appsv1.ComponentVersion) err
return nil
}
-// resolveCompDefinitionNServiceVersion resolves and returns the specific component definition object and the service version supported.
-func resolveCompDefinitionNServiceVersion(ctx context.Context, cli client.Reader, compDefName, serviceVersion string) (*appsv1.ComponentDefinition, string, error) {
- var (
- compDef *appsv1.ComponentDefinition
- )
- compDefs, err := listCompDefinitionsWithPattern(ctx, cli, compDefName)
- if err != nil {
- return compDef, serviceVersion, err
- }
-
- // mapping from to <[]*appsv1.ComponentDefinition>
- serviceVersionToCompDefs, err := serviceVersionToCompDefinitions(ctx, cli, compDefs, serviceVersion)
- if err != nil {
- return compDef, serviceVersion, err
- }
-
- // use specified service version or the latest.
- if len(serviceVersion) == 0 {
- serviceVersions := maps.Keys(serviceVersionToCompDefs)
- if len(serviceVersions) > 0 {
- slices.SortFunc(serviceVersions, serviceVersionComparator)
- serviceVersion = serviceVersions[len(serviceVersions)-1]
- }
- }
-
- // component definitions that support the service version
- compatibleCompDefs := serviceVersionToCompDefs[serviceVersion]
- if len(compatibleCompDefs) == 0 {
- return compDef, serviceVersion, fmt.Errorf("no matched component definition found: %s", compDefName)
- }
-
- // choose the latest one
- compatibleCompDefNames := maps.Keys(compatibleCompDefs)
- slices.Sort(compatibleCompDefNames)
- compatibleCompDefName := compatibleCompDefNames[len(compatibleCompDefNames)-1]
-
- return compatibleCompDefs[compatibleCompDefName], serviceVersion, nil
-}
-
-func serviceVersionToCompDefinitions(ctx context.Context, cli client.Reader,
- compDefs []*appsv1.ComponentDefinition, serviceVersion string) (map[string]map[string]*appsv1.ComponentDefinition, error) {
- result := make(map[string]map[string]*appsv1.ComponentDefinition)
-
- insert := func(version string, compDef *appsv1.ComponentDefinition) {
- if _, ok := result[version]; !ok {
- result[version] = make(map[string]*appsv1.ComponentDefinition)
- }
- result[version][compDef.Name] = compDef
- }
-
- checkedInsert := func(version string, compDef *appsv1.ComponentDefinition) error {
- match, err := component.CompareServiceVersion(serviceVersion, version)
- if err == nil && match {
- insert(version, compDef)
- }
- return err
- }
-
- for _, compDef := range compDefs {
- compVersions, err := component.CompatibleCompVersions4Definition(ctx, cli, compDef)
- if err != nil {
- return nil, err
- }
-
- serviceVersions := sets.New[string]()
- // add definition's service version as default, in case there is no component versions provided
- if compDef.Spec.ServiceVersion != "" {
- serviceVersions.Insert(compDef.Spec.ServiceVersion)
- }
- for _, compVersion := range compVersions {
- serviceVersions = serviceVersions.Union(compatibleServiceVersions4Definition(compDef, compVersion))
- }
-
- for version := range serviceVersions {
- if err = checkedInsert(version, compDef); err != nil {
- return nil, err
- }
- }
- }
- return result, nil
-}
-
-// compatibleServiceVersions4Definition returns all service versions that are compatible with specified component definition.
-func compatibleServiceVersions4Definition(compDef *appsv1.ComponentDefinition, compVersion *appsv1.ComponentVersion) sets.Set[string] {
- match := func(pattern string) bool {
- return component.PrefixOrRegexMatched(compDef.Name, pattern)
- }
- releases := make(map[string]bool, 0)
- for _, rule := range compVersion.Spec.CompatibilityRules {
- if slices.IndexFunc(rule.CompDefs, match) >= 0 {
- for _, release := range rule.Releases {
- releases[release] = true
- }
- }
- }
- serviceVersions := sets.New[string]()
- for _, release := range compVersion.Spec.Releases {
- if releases[release.Name] {
- serviceVersions = serviceVersions.Insert(release.ServiceVersion)
- }
- }
- return serviceVersions
-}
-
func serviceVersionComparator(a, b string) int {
if len(a) == 0 {
return -1
diff --git a/controllers/apps/componentversion_controller_test.go b/controllers/apps/componentversion_controller_test.go
index 5290927e554..e27b1b3d633 100644
--- a/controllers/apps/componentversion_controller_test.go
+++ b/controllers/apps/componentversion_controller_test.go
@@ -31,19 +31,25 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
- "github.com/apecloud/kubeblocks/pkg/controller/component"
"github.com/apecloud/kubeblocks/pkg/generics"
testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps"
)
var _ = Describe("ComponentVersion Controller", func() {
var (
- // compDefinitionObjs []*appsv1.ComponentDefinition
compVersionObj *appsv1.ComponentVersion
-
- compDefNames = []string{testapps.CompDefName("v1.0"), testapps.CompDefName("v1.1"), testapps.CompDefName("v2.0"), testapps.CompDefName("v3.0")}
+ compDefNames = []string{
+ testapps.CompDefName("v1.0"),
+ testapps.CompDefName("v1.1"),
+ testapps.CompDefName("v2.0"),
+ testapps.CompDefName("v3.0"),
+ }
// in reverse order
- serviceVersions = []string{testapps.ServiceVersion("v3"), testapps.ServiceVersion("v2"), testapps.ServiceVersion("v1")}
+ serviceVersions = []string{
+ testapps.ServiceVersion("v3"),
+ testapps.ServiceVersion("v2"),
+ testapps.ServiceVersion("v1"),
+ }
)
cleanEnv := func() {
@@ -65,7 +71,6 @@ var _ = Describe("ComponentVersion Controller", func() {
BeforeEach(func() {
cleanEnv()
-
})
AfterEach(func() {
@@ -180,20 +185,6 @@ var _ = Describe("ComponentVersion Controller", func() {
return obj
}
- updateNCheckCompDefinitionImages := func(compDef *appsv1.ComponentDefinition, serviceVersion string, r0, r1 string) {
- Expect(compDef.Spec.Runtime.Containers[0].Image).Should(Equal(testapps.AppImage(compDef.Spec.Runtime.Containers[0].Name, testapps.ReleaseID(""))))
- Expect(compDef.Spec.Runtime.Containers[1].Image).Should(Equal(testapps.AppImage(compDef.Spec.Runtime.Containers[1].Name, testapps.ReleaseID(""))))
- Expect(component.UpdateCompDefinitionImages4ServiceVersion(testCtx.Ctx, testCtx.Cli, compDef, serviceVersion)).Should(Succeed())
- Expect(compDef.Spec.Runtime.Containers).Should(HaveLen(2))
- Expect(compDef.Spec.Runtime.Containers[0].Image).Should(Equal(testapps.AppImage(compDef.Spec.Runtime.Containers[0].Name, testapps.ReleaseID(r0))))
- Expect(compDef.Spec.Runtime.Containers[1].Image).Should(Equal(testapps.AppImage(compDef.Spec.Runtime.Containers[1].Name, testapps.ReleaseID(r1))))
-
- Expect(compDef.Spec.LifecycleActions).ShouldNot(BeNil())
- Expect(compDef.Spec.LifecycleActions.PreTerminate).ShouldNot(BeNil())
- Expect(compDef.Spec.LifecycleActions.PreTerminate.Exec).ShouldNot(BeNil())
- Expect(compDef.Spec.LifecycleActions.PreTerminate.Exec.Image).Should(Equal(testapps.AppImage(testapps.DefaultActionName, testapps.ReleaseID(r1))))
- }
-
Context("reconcile component version", func() {
BeforeEach(func() {
createCompDefinitionObjs()
@@ -370,307 +361,4 @@ var _ = Describe("ComponentVersion Controller", func() {
})).Should(Succeed())
})
})
-
- Context("resolve component definition, service version and images", func() {
- BeforeEach(func() {
- createCompDefinitionObjs()
- compVersionObj = createCompVersionObj()
- })
-
- AfterEach(func() {
- cleanEnv()
- })
-
- It("full match", func() {
- By("with definition v1.0 and service version v0")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), testapps.ServiceVersion("v1"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
-
- By("with definition v1.1 and service version v0")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.1"), testapps.ServiceVersion("v1"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
-
- By("with definition v2.0 and service version v0")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2.0"), testapps.ServiceVersion("v1"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
-
- By("with definition v1.0 and service version v1")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), testapps.ServiceVersion("v2"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition v1.1 and service version v1")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.1"), testapps.ServiceVersion("v2"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition v2.0 and service version v1")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2.0"), testapps.ServiceVersion("v2"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition v3.0 and service version v2")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v3.0"), testapps.ServiceVersion("v3"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
- })
-
- It("w/o service version", func() {
- By("with definition v1.0")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition v1.1")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.1"), "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition v2.0")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2.0"), "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition v3.0")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v3.0"), "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
- })
-
- It("prefix match definition", func() {
- By("with definition prefix and service version v0")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefinitionName, testapps.ServiceVersion("v1"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
-
- By("with definition prefix and service version v1")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefinitionName, testapps.ServiceVersion("v2"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition prefix and service version v2")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefinitionName, testapps.ServiceVersion("v3"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
-
- By("with definition v1 prefix and service version v0")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1"), testapps.ServiceVersion("v1"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
-
- By("with definition v2 prefix and service version v1")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2"), testapps.ServiceVersion("v2"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
- })
-
- It("prefix match definition and w/o service version", func() {
- By("with definition prefix")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefinitionName, "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
-
- By("with definition v1 prefix")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1"), "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition v2 prefix")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v2"), "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
- })
-
- It("regular expression match definition", func() {
- By("with definition exact regex and service version 1")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithExactRegex("v2.0"), testapps.ServiceVersion("v1"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
-
- By("with definition exact regex and service version v2")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithExactRegex("v2.0"), testapps.ServiceVersion("v2"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition exact regex and service version v3")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithExactRegex("v3.0"), testapps.ServiceVersion("v3"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
-
- By("with definition v1 fuzzy regex and service version v0")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithFuzzyRegex("v1"), testapps.ServiceVersion("v1"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
-
- By("with definition v2 fuzzy regex and service version v1")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithFuzzyRegex("v2"), testapps.ServiceVersion("v2"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
- })
-
- It("regular expression match definition and w/o service version", func() {
- By("with definition regex")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, "^"+testapps.CompDefinitionName, "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v3.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v3")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r5", "r5")
-
- By("with definition v1 regex")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithFuzzyRegex("v1"), "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.1")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
-
- By("with definition v2 regex")
- compDef, resolvedServiceVersion, err = resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefNameWithFuzzyRegex("v2"), "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v2.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
- })
-
- It("match from definition", func() {
- By("with definition v1.0 and service version v0")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), testapps.ServiceVersion("v0"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v0")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "", "") // empty revision of image tag
- })
-
- It("resolve images from definition and version", func() {
- By("create new definition v4.0 with service version v4")
- compDefObj := testapps.NewComponentDefinitionFactory(testapps.CompDefName("v4.0")).
- SetServiceVersion(testapps.ServiceVersion("v4")).
- SetRuntime(&corev1.Container{Name: testapps.AppName, Image: testapps.AppImage(testapps.AppName, testapps.ReleaseID(""))}).
- SetRuntime(&corev1.Container{Name: testapps.AppNameSamePrefix, Image: testapps.AppImage(testapps.AppNameSamePrefix, testapps.ReleaseID(""))}).
- SetLifecycleAction(testapps.DefaultActionName,
- &appsv1.Action{Exec: &appsv1.ExecAction{Image: testapps.AppImage(testapps.DefaultActionName, testapps.ReleaseID(""))}}).
- Create(&testCtx).
- GetObject()
- Eventually(testapps.CheckObj(&testCtx, client.ObjectKeyFromObject(compDefObj),
- func(g Gomega, compDef *appsv1.ComponentDefinition) {
- g.Expect(compDef.Status.ObservedGeneration).Should(Equal(compDef.Generation))
- })).Should(Succeed())
-
- By("new release for the definition")
- compVersionKey := client.ObjectKeyFromObject(compVersionObj)
- Eventually(testapps.GetAndChangeObj(&testCtx, compVersionKey, func(compVersion *appsv1.ComponentVersion) {
- release := appsv1.ComponentVersionRelease{
- Name: testapps.ReleaseID("r6"),
- Changes: "publish a new service version",
- ServiceVersion: testapps.ServiceVersion("v4"),
- Images: map[string]string{
- testapps.AppName: testapps.AppImage(testapps.AppName, testapps.ReleaseID("r6")),
- // not provide image for this app
- // testapps.AppNameSamePrefix: testapps.AppImage(testapps.AppNameSamePrefix, testapps.ReleaseID("r6")),
- },
- }
- rule := appsv1.ComponentVersionCompatibilityRule{
- CompDefs: []string{testapps.CompDefName("v4")}, // use prefix
- Releases: []string{testapps.ReleaseID("r6")},
- }
- compVersion.Spec.CompatibilityRules = append(compVersion.Spec.CompatibilityRules, rule)
- compVersion.Spec.Releases = append(compVersion.Spec.Releases, release)
- })).Should(Succeed())
- Eventually(testapps.CheckObj(&testCtx, compVersionKey, func(g Gomega, compVersion *appsv1.ComponentVersion) {
- g.Expect(compVersion.Status.ObservedGeneration).Should(Equal(compVersion.Generation))
- })).Should(Succeed())
-
- By("with definition v4.0 and service version v3")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v4.0"), testapps.ServiceVersion("v4"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v4.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v4")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r6", "") // app is r6 and another one is ""
- })
- })
-
- Context("resolve component definition, service version without serviceVersion in componentDefinition", func() {
- BeforeEach(func() {
- compDefs := createCompDefinitionObjs()
- for _, compDef := range compDefs {
- compDefKey := client.ObjectKeyFromObject(compDef)
- Eventually(testapps.GetAndChangeObj(&testCtx, compDefKey, func(compDef *appsv1.ComponentDefinition) {
- compDef.Spec.ServiceVersion = ""
- })).Should(Succeed())
- }
- compVersionObj = createCompVersionObj()
- })
-
- AfterEach(func() {
- cleanEnv()
- })
-
- It("full match", func() {
- By("with definition v1.0 and service version v0")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), testapps.ServiceVersion("v1"))
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v1")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r4", "r4")
- })
-
- It("w/o service version", func() {
- By("with definition v1.0")
- compDef, resolvedServiceVersion, err := resolveCompDefinitionNServiceVersion(testCtx.Ctx, testCtx.Cli, testapps.CompDefName("v1.0"), "")
- Expect(err).Should(Succeed())
- Expect(compDef.Name).Should(Equal(testapps.CompDefName("v1.0")))
- Expect(resolvedServiceVersion).Should(Equal(testapps.ServiceVersion("v2")))
- updateNCheckCompDefinitionImages(compDef, resolvedServiceVersion, "r3", "r2")
- })
- })
})
diff --git a/controllers/apps/shardingdefinition_controller.go b/controllers/apps/shardingdefinition_controller.go
index cbf14138dda..f504d04c5fc 100644
--- a/controllers/apps/shardingdefinition_controller.go
+++ b/controllers/apps/shardingdefinition_controller.go
@@ -24,10 +24,8 @@ import (
"encoding/json"
"fmt"
"hash/fnv"
- "slices"
"strings"
- "golang.org/x/exp/maps"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/rand"
@@ -42,6 +40,10 @@ import (
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
)
+const (
+ shardingDefinitionFinalizerName = "shardingdefinition.kubeblocks.io/finalizer"
+)
+
//+kubebuilder:rbac:groups=apps.kubeblocks.io,resources=shardingdefinitions,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=apps.kubeblocks.io,resources=shardingdefinitions/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=apps.kubeblocks.io,resources=shardingdefinitions/finalizers,verbs=update
@@ -294,62 +296,3 @@ func (r *ShardingDefinitionReconciler) immutableHash(cli client.Client, rctx int
shardingDef.Annotations[immutableHashAnnotationKey], _ = r.specHash(shardingDef)
return cli.Patch(rctx.Ctx, shardingDef, patch)
}
-
-// resolveShardingDefinition resolves and returns the specific sharding definition object supported.
-func resolveShardingDefinition(ctx context.Context, cli client.Reader, shardingDefName string) (*appsv1.ShardingDefinition, error) {
- shardingDefs, err := listShardingDefinitionsWithPattern(ctx, cli, shardingDefName)
- if err != nil {
- return nil, err
- }
- if len(shardingDefs) == 0 {
- return nil, fmt.Errorf("no sharding definition found for the specified name: %s", shardingDefName)
- }
-
- m := make(map[string]int)
- for i, def := range shardingDefs {
- m[def.Name] = i
- }
- // choose the latest one
- names := maps.Keys(m)
- slices.Sort(names)
- latestName := names[len(names)-1]
-
- return shardingDefs[m[latestName]], nil
-}
-
-// listShardingDefinitionsWithPattern returns all sharding definitions whose names match the given pattern
-func listShardingDefinitionsWithPattern(ctx context.Context, cli client.Reader, name string) ([]*appsv1.ShardingDefinition, error) {
- shardingDefList := &appsv1.ShardingDefinitionList{}
- if err := cli.List(ctx, shardingDefList); err != nil {
- return nil, err
- }
- fullyMatched := make([]*appsv1.ShardingDefinition, 0)
- patternMatched := make([]*appsv1.ShardingDefinition, 0)
- for i, item := range shardingDefList.Items {
- if item.Name == name {
- fullyMatched = append(fullyMatched, &shardingDefList.Items[i])
- }
- if component.PrefixOrRegexMatched(item.Name, name) {
- patternMatched = append(patternMatched, &shardingDefList.Items[i])
- }
- }
- if len(fullyMatched) > 0 {
- return fullyMatched, nil
- }
- return patternMatched, nil
-}
-
-func validateShardingShards(shardingDef *appsv1.ShardingDefinition, sharding *appsv1.ClusterSharding) error {
- var (
- limit = shardingDef.Spec.ShardsLimit
- shards = sharding.Shards
- )
- if limit == nil || (shards >= limit.MinShards && shards <= limit.MaxShards) {
- return nil
- }
- return shardsOutOfLimitError(sharding.Name, shards, *limit)
-}
-
-func shardsOutOfLimitError(shardingName string, shards int32, limit appsv1.ShardsLimit) error {
- return fmt.Errorf("shards %d out-of-limit [%d, %d], sharding: %s", shards, limit.MinShards, limit.MaxShards, shardingName)
-}
diff --git a/controllers/apps/sidecardefinition_controller.go b/controllers/apps/sidecardefinition_controller.go
index 37465557e6b..f8df7d466d6 100644
--- a/controllers/apps/sidecardefinition_controller.go
+++ b/controllers/apps/sidecardefinition_controller.go
@@ -46,6 +46,10 @@ import (
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
)
+const (
+ sidecarDefinitionFinalizerName = "sidecardefinition.kubeblocks.io/finalizer"
+)
+
// SidecarDefinitionReconciler reconciles a SidecarDefinition object
type SidecarDefinitionReconciler struct {
client.Client
diff --git a/controllers/apps/suite_test.go b/controllers/apps/suite_test.go
index 65c60ed3a18..7ed16fe9abf 100644
--- a/controllers/apps/suite_test.go
+++ b/controllers/apps/suite_test.go
@@ -30,11 +30,9 @@ import (
. "github.com/onsi/gomega"
"github.com/go-logr/logr"
- snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
"go.uber.org/zap/zapcore"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
- "k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
@@ -45,12 +43,6 @@ import (
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1"
appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1"
- dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
- opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1"
- workloadsv1 "github.com/apecloud/kubeblocks/apis/workloads/v1"
- "github.com/apecloud/kubeblocks/controllers/apps/configuration"
- "github.com/apecloud/kubeblocks/controllers/dataprotection"
- "github.com/apecloud/kubeblocks/controllers/k8score"
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/model"
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
@@ -72,7 +64,6 @@ var testEnv *envtest.Environment
var ctx context.Context
var cancel context.CancelFunc
var testCtx testutil.TestContext
-var clusterRecorder record.EventRecorder
var logger logr.Logger
func init() {
@@ -126,10 +117,6 @@ var _ = BeforeSuite(func() {
Expect(err).NotTo(HaveOccurred())
model.AddScheme(appsv1alpha1.AddToScheme)
- err = opsv1alpha1.AddToScheme(scheme.Scheme)
- Expect(err).NotTo(HaveOccurred())
- model.AddScheme(opsv1alpha1.AddToScheme)
-
err = appsv1beta1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
model.AddScheme(appsv1beta1.AddToScheme)
@@ -138,18 +125,6 @@ var _ = BeforeSuite(func() {
Expect(err).NotTo(HaveOccurred())
model.AddScheme(appsv1.AddToScheme)
- err = dpv1alpha1.AddToScheme(scheme.Scheme)
- Expect(err).NotTo(HaveOccurred())
- model.AddScheme(dpv1alpha1.AddToScheme)
-
- err = snapshotv1.AddToScheme(scheme.Scheme)
- Expect(err).NotTo(HaveOccurred())
- model.AddScheme(snapshotv1.AddToScheme)
-
- err = workloadsv1.AddToScheme(scheme.Scheme)
- Expect(err).NotTo(HaveOccurred())
- model.AddScheme(workloadsv1.AddToScheme)
-
// +kubebuilder:scaffold:rscheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
@@ -214,21 +189,6 @@ var _ = BeforeSuite(func() {
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
- clusterRecorder = k8sManager.GetEventRecorderFor("cluster-controller")
- err = (&ClusterReconciler{
- Client: k8sManager.GetClient(),
- Scheme: k8sManager.GetScheme(),
- Recorder: clusterRecorder,
- }).SetupWithManager(k8sManager)
- Expect(err).ToNot(HaveOccurred())
-
- err = (&ComponentReconciler{
- Client: k8sManager.GetClient(),
- Scheme: k8sManager.GetScheme(),
- Recorder: k8sManager.GetEventRecorderFor("component-controller"),
- }).SetupWithManager(k8sManager, nil)
- Expect(err).ToNot(HaveOccurred())
-
err = (&ServiceDescriptorReconciler{
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
@@ -236,34 +196,6 @@ var _ = BeforeSuite(func() {
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
- err = (&k8score.EventReconciler{
- Client: k8sManager.GetClient(),
- Scheme: k8sManager.GetScheme(),
- Recorder: k8sManager.GetEventRecorderFor("event-controller"),
- }).SetupWithManager(k8sManager, nil)
- Expect(err).ToNot(HaveOccurred())
-
- err = (&configuration.ConfigConstraintReconciler{
- Client: k8sManager.GetClient(),
- Scheme: k8sManager.GetScheme(),
- Recorder: k8sManager.GetEventRecorderFor("configuration-template-controller"),
- }).SetupWithManager(k8sManager)
- Expect(err).ToNot(HaveOccurred())
-
- err = (&configuration.ConfigurationReconciler{
- Client: k8sManager.GetClient(),
- Scheme: k8sManager.GetScheme(),
- Recorder: k8sManager.GetEventRecorderFor("configuration-controller"),
- }).SetupWithManager(k8sManager, nil)
- Expect(err).ToNot(HaveOccurred())
-
- err = (&dataprotection.BackupPolicyTemplateReconciler{
- Client: k8sManager.GetClient(),
- Scheme: k8sManager.GetScheme(),
- Recorder: k8sManager.GetEventRecorderFor("backup-policy-template-controller"),
- }).SetupWithManager(k8sManager)
- Expect(err).ToNot(HaveOccurred())
-
testCtx = testutil.NewDefaultTestContext(ctx, k8sClient, testEnv)
go func() {
diff --git a/controllers/apps/transformers_parallel.go b/controllers/apps/transformers_parallel.go
deleted file mode 100644
index 587f17e5b1b..00000000000
--- a/controllers/apps/transformers_parallel.go
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-Copyright (C) 2022-2024 ApeCloud Co., Ltd
-
-This file is part of KubeBlocks project
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see .
-*/
-
-package apps
-
-import (
- "fmt"
- "sync"
-
- "github.com/apecloud/kubeblocks/pkg/controller/graph"
-)
-
-type ParallelTransformers struct {
- transformers []graph.Transformer
-}
-
-var _ graph.Transformer = &ParallelTransformers{}
-
-func (t *ParallelTransformers) Transform(ctx graph.TransformContext, dag *graph.DAG) error {
- var group sync.WaitGroup
- var errs error
- for _, transformer := range t.transformers {
- transformer := transformer
- group.Add(1)
- go func() {
- err := transformer.Transform(ctx, dag)
- if err != nil {
- // TODO: sync.Mutex errs
- errs = fmt.Errorf("%v; %v", errs, err)
- }
- group.Done()
- }()
- }
- group.Wait()
- return errs
-}
diff --git a/controllers/apps/const.go b/controllers/apps/types.go
similarity index 61%
rename from controllers/apps/const.go
rename to controllers/apps/types.go
index a1a3bc3a306..9cd5337abff 100644
--- a/controllers/apps/const.go
+++ b/controllers/apps/types.go
@@ -19,15 +19,21 @@ along with this program. If not, see .
package apps
-const (
- // name of our custom finalizer
- clusterDefinitionFinalizerName = "clusterdefinition.kubeblocks.io/finalizer"
- shardingDefinitionFinalizerName = "shardingdefinition.kubeblocks.io/finalizer"
- componentDefinitionFinalizerName = "componentdefinition.kubeblocks.io/finalizer"
- componentVersionFinalizerName = "componentversion.kubeblocks.io/finalizer"
- sidecarDefinitionFinalizerName = "sidecardefinition.kubeblocks.io/finalizer"
+import (
+ "k8s.io/apimachinery/pkg/runtime"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+ clientgoscheme "k8s.io/client-go/kubernetes/scheme"
+
+ appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+ workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
)
-const (
- trueVal = "true"
+var (
+ rscheme = runtime.NewScheme()
)
+
+func init() {
+ utilruntime.Must(clientgoscheme.AddToScheme(rscheme))
+ utilruntime.Must(appsv1.AddToScheme(rscheme))
+ utilruntime.Must(workloads.AddToScheme(rscheme))
+}
diff --git a/controllers/operations/opsrequest_controller_test.go b/controllers/operations/opsrequest_controller_test.go
index ea557de3194..7cad56b3176 100644
--- a/controllers/operations/opsrequest_controller_test.go
+++ b/controllers/operations/opsrequest_controller_test.go
@@ -40,7 +40,6 @@ import (
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1"
workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
- "github.com/apecloud/kubeblocks/controllers/apps"
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/component"
intctrlutil "github.com/apecloud/kubeblocks/pkg/generics"
@@ -396,7 +395,7 @@ var _ = Describe("OpsRequest Controller", func() {
condition := meta.FindStatusCondition(fetched.Status.Conditions, appsv1.ConditionTypeProvisioningStarted)
g.Expect(condition).ShouldNot(BeNil())
g.Expect(condition.Status).Should(BeFalse())
- g.Expect(condition.Reason).Should(Equal(apps.ReasonPreCheckFailed))
+ g.Expect(condition.Reason).Should(Equal("PreCheckFailed"))
g.Expect(condition.Message).Should(Equal("HorizontalScaleFailed: volume snapshot not support"))
}))
diff --git a/controllers/operations/suite_test.go b/controllers/operations/suite_test.go
index ba7ec138078..c3a3ca269c7 100644
--- a/controllers/operations/suite_test.go
+++ b/controllers/operations/suite_test.go
@@ -49,6 +49,8 @@ import (
opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1"
workloadsv1 "github.com/apecloud/kubeblocks/apis/workloads/v1"
"github.com/apecloud/kubeblocks/controllers/apps"
+ "github.com/apecloud/kubeblocks/controllers/apps/cluster"
+ "github.com/apecloud/kubeblocks/controllers/apps/component"
"github.com/apecloud/kubeblocks/controllers/apps/configuration"
"github.com/apecloud/kubeblocks/controllers/dataprotection"
"github.com/apecloud/kubeblocks/controllers/k8score"
@@ -182,7 +184,7 @@ var _ = BeforeSuite(func() {
Expect(err).ToNot(HaveOccurred())
clusterRecorder = k8sManager.GetEventRecorderFor("cluster-controller")
- err = (&apps.ClusterReconciler{
+ err = (&cluster.ClusterReconciler{
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: clusterRecorder,
@@ -196,7 +198,7 @@ var _ = BeforeSuite(func() {
}).SetupWithManager(k8sManager)
Expect(err).ToNot(HaveOccurred())
- err = (&apps.ComponentReconciler{
+ err = (&component.ComponentReconciler{
Client: k8sManager.GetClient(),
Scheme: k8sManager.GetScheme(),
Recorder: k8sManager.GetEventRecorderFor("component-controller"),
diff --git a/controllers/trace/reconciler_tree.go b/controllers/trace/reconciler_tree.go
index a6eaf6e4806..50da88bd816 100644
--- a/controllers/trace/reconciler_tree.go
+++ b/controllers/trace/reconciler_tree.go
@@ -45,7 +45,8 @@ import (
dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1"
workloadsAPI "github.com/apecloud/kubeblocks/apis/workloads/v1"
- "github.com/apecloud/kubeblocks/controllers/apps"
+ "github.com/apecloud/kubeblocks/controllers/apps/cluster"
+ "github.com/apecloud/kubeblocks/controllers/apps/component"
"github.com/apecloud/kubeblocks/controllers/apps/configuration"
"github.com/apecloud/kubeblocks/controllers/dataprotection"
"github.com/apecloud/kubeblocks/controllers/workloads"
@@ -162,7 +163,7 @@ func newReconciler(mClient client.Client, recorder record.EventRecorder, objectT
}
func newClusterReconciler(cli client.Client, recorder record.EventRecorder) reconcile.Reconciler {
- return &apps.ClusterReconciler{
+ return &cluster.ClusterReconciler{
Client: cli,
Scheme: cli.Scheme(),
Recorder: recorder,
@@ -170,7 +171,7 @@ func newClusterReconciler(cli client.Client, recorder record.EventRecorder) reco
}
func newComponentReconciler(cli client.Client, recorder record.EventRecorder) reconcile.Reconciler {
- return &apps.ComponentReconciler{
+ return &component.ComponentReconciler{
Client: cli,
Scheme: cli.Scheme(),
Recorder: recorder,
diff --git a/pkg/controller/graph/plan_builder.go b/pkg/controller/graph/plan_builder.go
index d1e70276080..47db0782b66 100644
--- a/pkg/controller/graph/plan_builder.go
+++ b/pkg/controller/graph/plan_builder.go
@@ -28,11 +28,7 @@ type PlanBuilder interface {
// And the transformers will be executed in the add order.
AddTransformer(transformer ...Transformer) PlanBuilder
- // AddParallelTransformer adds transformers to the builder.
- // And the transformers will be executed in parallel.
- AddParallelTransformer(transformer ...Transformer) PlanBuilder
-
- // Build runs all the transformers added by AddTransformer and/or AddParallelTransformer.
+ // Build runs all the transformers added by AddTransformer.
Build() (Plan, error)
}
diff --git a/pkg/controller/kubebuilderx/plan_builder.go b/pkg/controller/kubebuilderx/plan_builder.go
index 6dea6bb2643..1a324689c1e 100644
--- a/pkg/controller/kubebuilderx/plan_builder.go
+++ b/pkg/controller/kubebuilderx/plan_builder.go
@@ -92,10 +92,6 @@ func (b *PlanBuilder) AddTransformer(_ ...graph.Transformer) graph.PlanBuilder {
return b
}
-func (b *PlanBuilder) AddParallelTransformer(_ ...graph.Transformer) graph.PlanBuilder {
- return b
-}
-
func (b *PlanBuilder) Build() (graph.Plan, error) {
vertices := buildOrderedVertices(b.transCtx.GetContext(), b.currentTree, b.desiredTree)
plan := &Plan{
diff --git a/pkg/controller/model/parallel_transformer.go b/pkg/controller/model/parallel_transformer.go
deleted file mode 100644
index 1735716adaa..00000000000
--- a/pkg/controller/model/parallel_transformer.go
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-Copyright (C) 2022-2024 ApeCloud Co., Ltd
-
-This file is part of KubeBlocks project
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see .
-*/
-
-package model
-
-import (
- "fmt"
- "sync"
-
- "github.com/apecloud/kubeblocks/pkg/controller/graph"
-)
-
-// ParallelTransformer executes a group of transformers in parallel.
-// TODO: make DAG thread-safe if ParallelTransformer called.
-type ParallelTransformer struct {
- Transformers []graph.Transformer
-}
-
-func (t *ParallelTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error {
- var group sync.WaitGroup
- var errs error
- for _, transformer := range t.Transformers {
- transformer := transformer
- group.Add(1)
- go func() {
- err := transformer.Transform(ctx, dag)
- if err != nil {
- // TODO: sync.Mutex errs
- errs = fmt.Errorf("%v; %v", errs, err)
- }
- group.Done()
- }()
- }
- group.Wait()
- return errs
-}
-
-var _ graph.Transformer = &ParallelTransformer{}
diff --git a/pkg/controller/model/parallel_transformer_test.go b/pkg/controller/model/parallel_transformer_test.go
deleted file mode 100644
index 5002e0cee6e..00000000000
--- a/pkg/controller/model/parallel_transformer_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-Copyright (C) 2022-2024 ApeCloud Co., Ltd
-
-This file is part of KubeBlocks project
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see .
-*/
-
-package model
-
-import (
- . "github.com/onsi/ginkgo/v2"
- . "github.com/onsi/gomega"
-
- "github.com/apecloud/kubeblocks/pkg/controller/graph"
-)
-
-var _ = Describe("parallel transformer test", func() {
- Context("Transform function", func() {
- It("should work well", func() {
- id1 := 1
- transformer := &ParallelTransformer{
- Transformers: []graph.Transformer{
- &testTransformer{id: id1},
- },
- }
- dag := graph.NewDAG()
- // TODO(free6om): DAG is not thread-safe currently, so parallel transformer has concurrent map writes issue.
- // parallel more transformers when DAG is ready.
- Expect(transformer.Transform(nil, dag)).Should(Succeed())
- dagExpected := graph.NewDAG()
- dagExpected.AddVertex(id1)
- Expect(dag.Equals(dagExpected, DefaultLess)).Should(BeTrue())
- })
- })
-})
-
-type testTransformer struct {
- id int
-}
-
-var _ graph.Transformer = &testTransformer{}
-
-func (t *testTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error {
- dag.AddVertex(t.id)
- return nil
-}