Action defines a customizable hook or procedure tailored for different database engines,
@@ -7747,7 +7747,7 @@ ContainerVars
InstanceTemplate allows customization of individual replica configurations in a Component.
@@ -7791,6 +7791,24 @@ The default value is 1. A value of 0 disables instance creation.
+ordinals
+
+
+Ordinals
+
+
+ |
+
+ Specifies the desired Ordinals of this InstanceTemplate.
+The Ordinals used to specify the ordinal of the instance (pod) names to be generated under this InstanceTemplate.
+For example, if Ordinals is {ranges: [{start: 0, end: 1}], discrete: [7]},
+then the instance names generated under this InstanceTemplate would be
+$(cluster.name)-$(component.name)-$(template.name)-0、$(cluster.name)-$(component.name)-$(template.name)-1 and
+$(cluster.name)-$(component.name)-$(template.name)-7
+ |
+
+
+
annotations
map[string]string
@@ -8324,6 +8342,46 @@ VarOption
|
+
Ordinals
+
+
+(Appears on:InstanceTemplate, InstanceSetSpec)
+
+
+
Ordinals represents a combination of continuous segments and individual values.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+ranges
+
+
+[]Range
+
+
+ |
+
+ |
+
+
+
+discrete
+
+[]int32
+
+ |
+
+ |
+
+
+
PasswordConfig
@@ -8698,6 +8756,45 @@ string
+
Range
+
+
+(Appears on:Ordinals)
+
+
+
Range represents a range with a start and an end value.
+It is used to define a continuous segment.
+
+
+
+
+Field |
+Description |
+
+
+
+
+
+start
+
+int32
+
+ |
+
+ |
+
+
+
+end
+
+int32
+
+ |
+
+ |
+
+
+
ReplicaRole
@@ -28738,7 +28835,7 @@ Defaults to 1 if unspecified.
defaultTemplateOrdinals
-
+
Ordinals
@@ -28796,7 +28893,7 @@ Kubernetes core/v1.PodTemplateSpec
|
instances
-
+
[]InstanceTemplate
@@ -29255,7 +29352,7 @@ Defaults to 1 if unspecified.
|
defaultTemplateOrdinals
-
+
Ordinals
@@ -29313,7 +29410,7 @@ Kubernetes core/v1.PodTemplateSpec
|
instances
-
+
[]InstanceTemplate
@@ -29748,201 +29845,6 @@ key is the pod name, value is the revision.
-InstanceTemplate
-
-
-(Appears on:InstanceSetSpec)
-
-
- InstanceTemplate allows customization of individual replica configurations within a Component,
-without altering the base component template defined in ClusterComponentSpec.
-It enables the application of distinct settings to specific instances (replicas),
-providing flexibility while maintaining a common configuration baseline.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-name
-
-string
-
- |
-
- Name specifies the unique name of the instance Pod created using this InstanceTemplate.
-This name is constructed by concatenating the component’s name, the template’s name, and the instance’s ordinal
-using the pattern: $(cluster.name)-$(component.name)-$(template.name)-$(ordinal). Ordinals start from 0.
-The specified name overrides any default naming conventions or patterns.
- |
-
-
-
-replicas
-
-int32
-
- |
-
-(Optional)
- Specifies the number of instances (Pods) to create from this InstanceTemplate.
-This field allows setting how many replicated instances of the component,
-with the specific overrides in the InstanceTemplate, are created.
-The default value is 1. A value of 0 disables instance creation.
- |
-
-
-
-ordinals
-
-
-Ordinals
-
-
- |
-
- Specifies the desired Ordinals of this InstanceTemplate.
-The Ordinals used to specify the ordinal of the instance (pod) names to be generated under this InstanceTemplate.
-For example, if Ordinals is {ranges: [{start: 0, end: 1}], discrete: [7]},
-then the instance names generated under this InstanceTemplate would be
-$(cluster.name)-$(component.name)-$(template.name)-0、$(cluster.name)-$(component.name)-$(template.name)-1 and
-$(cluster.name)-$(component.name)-$(template.name)-7
- |
-
-
-
-annotations
-
-map[string]string
-
- |
-
-(Optional)
- Specifies a map of key-value pairs to be merged into the Pod’s existing annotations.
-Existing keys will have their values overwritten, while new keys will be added to the annotations.
- |
-
-
-
-labels
-
-map[string]string
-
- |
-
-(Optional)
- Specifies a map of key-value pairs that will be merged into the Pod’s existing labels.
-Values for existing keys will be overwritten, and new keys will be added.
- |
-
-
-
-image
-
-string
-
- |
-
-(Optional)
- Specifies an override for the first container’s image in the pod.
- |
-
-
-
-schedulingPolicy
-
-
-SchedulingPolicy
-
-
- |
-
-(Optional)
- Specifies the scheduling policy for the Component.
- |
-
-
-
-resources
-
-
-Kubernetes core/v1.ResourceRequirements
-
-
- |
-
-(Optional)
- Specifies an override for the resource requirements of the first container in the Pod.
-This field allows for customizing resource allocation (CPU, memory, etc.) for the container.
- |
-
-
-
-env
-
-
-[]Kubernetes core/v1.EnvVar
-
-
- |
-
-(Optional)
- Defines Env to override.
-Add new or override existing envs.
- |
-
-
-
-volumes
-
-
-[]Kubernetes core/v1.Volume
-
-
- |
-
-(Optional)
- Defines Volumes to override.
-Add new or override existing volumes.
- |
-
-
-
-volumeMounts
-
-
-[]Kubernetes core/v1.VolumeMount
-
-
- |
-
-(Optional)
- Defines VolumeMounts to override.
-Add new or override existing volume mounts of the first container in the pod.
- |
-
-
-
-volumeClaimTemplates
-
-
-[]Kubernetes core/v1.PersistentVolumeClaim
-
-
- |
-
-(Optional)
- Defines VolumeClaimTemplates to override.
-Add new or override existing volume claim templates.
- |
-
-
-
InstanceTemplateStatus
@@ -30194,44 +30096,18 @@ Action
If the Image is not configured, the Image from the previous non-nil action will be used.
|
-
-
-
Ordinals
-
-
-(Appears on:InstanceSetSpec, InstanceTemplate)
-
-
-
Ordinals represents a combination of continuous segments and individual values.
-
-
-
-
-Field |
-Description |
-
-
-
-ranges
+switchover
-
-[]Range
+
+Action
|
- |
-
-
-
-discrete
-
-[]int32
-
- |
-
+(Optional)
+ Defines the procedure for a controlled transition of a role to a new replica.
|
@@ -30260,45 +30136,6 @@ Any attempt to modify other fields will be rejected.
-
Range
-
-
-(Appears on:Ordinals)
-
-
-
Range represents a range with a start and an end value.
-It is used to define a continuous segment.
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-start
-
-int32
-
- |
-
- |
-
-
-
-end
-
-int32
-
- |
-
- |
-
-
-
ReplicaRole
@@ -30382,117 +30219,6 @@ bool
|
-
SchedulingPolicy
-
-
-(Appears on:InstanceTemplate)
-
-
-
SchedulingPolicy the scheduling policy.
-Deprecated: Unify with apps/v1alpha1.SchedulingPolicy
-
-
-
-
-Field |
-Description |
-
-
-
-
-
-schedulerName
-
-string
-
- |
-
-(Optional)
- If specified, the Pod will be dispatched by specified scheduler.
-If not specified, the Pod will be dispatched by default scheduler.
- |
-
-
-
-nodeSelector
-
-map[string]string
-
- |
-
-(Optional)
- NodeSelector is a selector which must be true for the Pod to fit on a node.
-Selector which must match a node’s labels for the Pod to be scheduled on that node.
-More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
- |
-
-
-
-nodeName
-
-string
-
- |
-
-(Optional)
- NodeName is a request to schedule this Pod onto a specific node. If it is non-empty,
-the scheduler simply schedules this Pod onto that node, assuming that it fits resource
-requirements.
- |
-
-
-
-affinity
-
-
-Kubernetes core/v1.Affinity
-
-
- |
-
-(Optional)
- Specifies a group of affinity scheduling rules of the Cluster, including NodeAffinity, PodAffinity, and PodAntiAffinity.
- |
-
-
-
-tolerations
-
-
-[]Kubernetes core/v1.Toleration
-
-
- |
-
-(Optional)
- Allows Pods to be scheduled onto nodes with matching taints.
-Each toleration in the array allows the Pod to tolerate node taints based on
-specified key , value , effect , and operator .
-
-- The
key , value , and effect identify the taint that the toleration matches.
-- The
operator determines how the toleration matches the taint.
-
-Pods with matching tolerations are allowed to be scheduled on tainted nodes, typically reserved for specific purposes.
- |
-
-
-
-topologySpreadConstraints
-
-
-[]Kubernetes core/v1.TopologySpreadConstraint
-
-
- |
-
-(Optional)
- TopologySpreadConstraints describes how a group of Pods ought to spread across topology
-domains. Scheduler will schedule Pods in a way which abides by the constraints.
-All topologySpreadConstraints are ANDed.
- |
-
-
-
workloads.kubeblocks.io/v1alpha1
diff --git a/pkg/controller/builder/builder_instance_set.go b/pkg/controller/builder/builder_instance_set.go
index 79ea71f824e..1106d82f044 100644
--- a/pkg/controller/builder/builder_instance_set.go
+++ b/pkg/controller/builder/builder_instance_set.go
@@ -25,6 +25,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
+ kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
)
@@ -86,6 +87,16 @@ func (builder *InstanceSetBuilder) SetRoles(roles []workloads.ReplicaRole) *Inst
return builder
}
+func (builder *InstanceSetBuilder) SetLifecycleActions(lifecycleActions *kbappsv1.ComponentLifecycleActions) *InstanceSetBuilder {
+ if lifecycleActions != nil && lifecycleActions.Switchover != nil {
+ if builder.get().Spec.MembershipReconfiguration == nil {
+ builder.get().Spec.MembershipReconfiguration = &workloads.MembershipReconfiguration{}
+ }
+ builder.get().Spec.MembershipReconfiguration.Switchover = lifecycleActions.Switchover
+ }
+ return builder
+}
+
func (builder *InstanceSetBuilder) SetTemplate(template corev1.PodTemplateSpec) *InstanceSetBuilder {
builder.get().Spec.Template = template
return builder
diff --git a/pkg/controller/component/its_convertor.go b/pkg/controller/component/its_convertor.go
index af8af9b5d16..afe6a8ce55a 100644
--- a/pkg/controller/component/its_convertor.go
+++ b/pkg/controller/component/its_convertor.go
@@ -24,13 +24,11 @@ import (
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
"github.com/apecloud/kubeblocks/pkg/constant"
- viper "github.com/apecloud/kubeblocks/pkg/viperx"
)
// BuildWorkloadFrom builds a new Component object based on SynthesizedComponent.
@@ -186,58 +184,19 @@ func AppsInstanceToWorkloadInstance(instance *kbappsv1.InstanceTemplate) *worklo
if instance == nil {
return nil
}
- var schedulingPolicy *workloads.SchedulingPolicy
- if instance.SchedulingPolicy != nil {
- schedulingPolicy = &workloads.SchedulingPolicy{
- SchedulerName: instance.SchedulingPolicy.SchedulerName,
- NodeSelector: instance.SchedulingPolicy.NodeSelector,
- NodeName: instance.SchedulingPolicy.NodeName,
- Affinity: instance.SchedulingPolicy.Affinity,
- Tolerations: instance.SchedulingPolicy.Tolerations,
- TopologySpreadConstraints: instance.SchedulingPolicy.TopologySpreadConstraints,
- }
- }
-
return &workloads.InstanceTemplate{
Name: instance.Name,
Replicas: instance.Replicas,
Annotations: instance.Annotations,
Labels: instance.Labels,
Image: instance.Image,
- SchedulingPolicy: schedulingPolicy,
+ SchedulingPolicy: instance.SchedulingPolicy,
Resources: instance.Resources,
Env: instance.Env,
Volumes: instance.Volumes,
VolumeMounts: instance.VolumeMounts,
- VolumeClaimTemplates: toPersistentVolumeClaims(instance.VolumeClaimTemplates),
- }
-}
-
-func toPersistentVolumeClaims(vcts []kbappsv1.ClusterComponentVolumeClaimTemplate) []corev1.PersistentVolumeClaim {
- storageClassName := func(spec kbappsv1.PersistentVolumeClaimSpec, defaultStorageClass string) *string {
- if spec.StorageClassName != nil && *spec.StorageClassName != "" {
- return spec.StorageClassName
- }
- if defaultStorageClass != "" {
- return &defaultStorageClass
- }
- return nil
- }
- var pvcs []corev1.PersistentVolumeClaim
- for _, v := range vcts {
- pvcs = append(pvcs, corev1.PersistentVolumeClaim{
- ObjectMeta: metav1.ObjectMeta{
- Name: v.Name,
- },
- Spec: corev1.PersistentVolumeClaimSpec{
- AccessModes: v.Spec.AccessModes,
- Resources: v.Spec.Resources,
- StorageClassName: storageClassName(v.Spec, viper.GetString(constant.CfgKeyDefaultStorageClass)),
- VolumeMode: v.Spec.VolumeMode,
- },
- })
+ VolumeClaimTemplates: instance.VolumeClaimTemplates,
}
- return pvcs
}
// parseITSConvertorArgs parses the args of ITS convertor.
diff --git a/pkg/controller/component/replicas.go b/pkg/controller/component/replicas.go
index 8dfcc89c652..041038deb98 100644
--- a/pkg/controller/component/replicas.go
+++ b/pkg/controller/component/replicas.go
@@ -221,7 +221,7 @@ func NewReplicaTask(compName, uid string, source *corev1.Pod, replicas []string)
NotifyAtFinish: true,
ReportPeriodSeconds: defaultNewReplicaTaskReportPeriodSeconds,
NewReplica: &proto.NewReplicaTask{
- Remote: PodFQDN(source.Namespace, compName, source.Name),
+ Remote: intctrlutil.PodFQDN(source.Namespace, compName, source.Name),
Port: port,
Replicas: strings.Join(replicas, ","),
},
diff --git a/pkg/controller/component/service_reference_test.go b/pkg/controller/component/service_reference_test.go
index b05cf84304d..088a8001f26 100644
--- a/pkg/controller/component/service_reference_test.go
+++ b/pkg/controller/component/service_reference_test.go
@@ -31,9 +31,9 @@ import (
"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/instanceset"
+ intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
"github.com/apecloud/kubeblocks/pkg/generics"
testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps"
)
@@ -434,7 +434,7 @@ var _ = Describe("service references", func() {
err := buildServiceReferencesWithoutResolve(testCtx.Ctx, reader, synthesizedComp, compDef, comp)
Expect(err).Should(Succeed())
- svcFQDN := serviceFQDN("external", reader.objs[0].GetName())
+ svcFQDN := intctrlutil.ServiceFQDN("external", reader.objs[0].GetName())
Expect(synthesizedComp.ServiceReferences).Should(HaveKey(serviceRefDeclaration.Name))
serviceDescriptor := synthesizedComp.ServiceReferences[serviceRefDeclaration.Name]
@@ -564,10 +564,10 @@ var _ = Describe("service references", func() {
}
etcdComp := reader.objs[0].(*appsv1.Component)
- podNames, _ := instanceset.GenerateAllInstanceNames(etcdComp.Name, etcdComp.Spec.Replicas, nil, nil, workloads.Ordinals{})
+ podNames, _ := instanceset.GenerateAllInstanceNames(etcdComp.Name, etcdComp.Spec.Replicas, nil, nil, appsv1.Ordinals{})
expectedPodFQDNs := strings.Join([]string{
- PodFQDN(namespace, etcdComp.Name, podNames[0]),
- PodFQDN(namespace, etcdComp.Name, podNames[1]),
+ intctrlutil.PodFQDN(namespace, etcdComp.Name, podNames[0]),
+ intctrlutil.PodFQDN(namespace, etcdComp.Name, podNames[1]),
}, ",")
err := buildServiceReferencesWithoutResolve(testCtx.Ctx, reader, synthesizedComp, compDef, comp)
@@ -630,7 +630,7 @@ var _ = Describe("service references", func() {
}
compName := constant.GenerateClusterComponentName(etcdCluster, etcdComponent)
- expectedPodFQDNs := PodFQDN(namespace, compName, reader.objs[1].GetName())
+ expectedPodFQDNs := intctrlutil.PodFQDN(namespace, compName, reader.objs[1].GetName())
err := buildServiceReferencesWithoutResolve(testCtx.Ctx, reader, synthesizedComp, compDef, comp)
Expect(err).Should(Succeed())
diff --git a/pkg/controller/component/vars.go b/pkg/controller/component/vars.go
index 80fa3fca267..5d8504b4b3a 100644
--- a/pkg/controller/component/vars.go
+++ b/pkg/controller/component/vars.go
@@ -37,10 +37,10 @@ import (
"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/common"
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/instanceset"
+ intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
"github.com/apecloud/kubeblocks/pkg/generics"
)
@@ -530,7 +530,7 @@ func composeHostValueFromServices(obj any, fqdn bool) string {
if !fqdn {
return svc.Name
}
- return serviceFQDN(svc.Namespace, svc.Name)
+ return intctrlutil.ServiceFQDN(svc.Namespace, svc.Name)
}
svcNames := make([]string, 0)
@@ -1153,13 +1153,13 @@ func componentVarPodsGetter(ctx context.Context, cli client.Reader,
for i := range comp.Spec.Instances {
templates = append(templates, &comp.Spec.Instances[i])
}
- names, err := instanceset.GenerateAllInstanceNames(comp.Name, comp.Spec.Replicas, templates, comp.Spec.OfflineInstances, workloads.Ordinals{})
+ names, err := instanceset.GenerateAllInstanceNames(comp.Name, comp.Spec.Replicas, templates, comp.Spec.OfflineInstances, appsv1.Ordinals{})
if err != nil {
return "", err
}
if fqdn {
for i := range names {
- names[i] = PodFQDN(namespace, comp.Name, names[i])
+ names[i] = intctrlutil.PodFQDN(namespace, comp.Name, names[i])
}
}
return strings.Join(names, ","), nil
@@ -1189,7 +1189,7 @@ func componentVarPodsWithRoleGetter(ctx context.Context, cli client.Reader,
if fqdn {
fullCompName := constant.GenerateClusterComponentName(clusterName, compName)
for i := range names {
- names[i] = PodFQDN(namespace, fullCompName, names[i])
+ names[i] = intctrlutil.PodFQDN(namespace, fullCompName, names[i])
}
}
return strings.Join(names, ","), nil
diff --git a/pkg/controller/component/vars_test.go b/pkg/controller/component/vars_test.go
index 8dde6e8c1eb..fe7cd91d495 100644
--- a/pkg/controller/component/vars_test.go
+++ b/pkg/controller/component/vars_test.go
@@ -34,6 +34,7 @@ import (
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
"github.com/apecloud/kubeblocks/pkg/constant"
+ intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
)
var _ = Describe("vars", func() {
@@ -1781,12 +1782,13 @@ var _ = Describe("vars", func() {
fqdnList := func(fn ...func(string) string) []string {
l := make([]string, 0)
for _, i := range mockInstanceList {
- l = append(l, PodFQDN(synthesizedComp.Namespace, synthesizedComp.FullCompName, i))
+ l = append(l, intctrlutil.PodFQDN(synthesizedComp.Namespace, synthesizedComp.FullCompName, i))
}
return l
}
checkEnvVarWithValue(envVars, "podFQDNs", strings.Join(fqdnList(), ","))
- checkEnvVarWithValue(envVars, "podFQDNs4Leader", PodFQDN(synthesizedComp.Namespace, synthesizedComp.FullCompName, podName("leader")))
+ checkEnvVarWithValue(envVars, "podFQDNs4Leader",
+ intctrlutil.PodFQDN(synthesizedComp.Namespace, synthesizedComp.FullCompName, podName("leader")))
})
})
diff --git a/pkg/controller/component/workload_utils.go b/pkg/controller/component/workload_utils.go
index 9d3d16d269c..080abecd368 100644
--- a/pkg/controller/component/workload_utils.go
+++ b/pkg/controller/component/workload_utils.go
@@ -35,7 +35,6 @@ import (
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/instanceset"
"github.com/apecloud/kubeblocks/pkg/generics"
- viper "github.com/apecloud/kubeblocks/pkg/viperx"
)
func ListOwnedWorkloads(ctx context.Context, cli client.Reader, namespace, clusterName, compName string) ([]*workloads.InstanceSet, error) {
@@ -139,7 +138,7 @@ func GenerateAllPodNames(
Replicas: instances[i].Replicas,
})
}
- return instanceset.GenerateAllInstanceNames(fullCompName, compReplicas, templates, offlineInstances, workloads.Ordinals{})
+ return instanceset.GenerateAllInstanceNames(fullCompName, compReplicas, templates, offlineInstances, appsv1.Ordinals{})
}
// GenerateAllPodNamesToSet generate all pod names for a component
@@ -180,15 +179,3 @@ func GetTemplateNameAndOrdinal(workloadName, podName string) (string, int32, err
}
return templateName, int32(index), nil
}
-
-func PodFQDN(namespace, compName, podName string) string {
- return fmt.Sprintf("%s.%s-headless.%s.svc.%s", podName, compName, namespace, clusterDomain())
-}
-
-func serviceFQDN(namespace, serviceName string) string {
- return fmt.Sprintf("%s.%s.svc.%s", serviceName, namespace, clusterDomain())
-}
-
-func clusterDomain() string {
- return viper.GetString(constant.KubernetesClusterDomainEnv)
-}
diff --git a/pkg/controller/factory/builder.go b/pkg/controller/factory/builder.go
index 428e4d9e41f..981ea564212 100644
--- a/pkg/controller/factory/builder.go
+++ b/pkg/controller/factory/builder.go
@@ -77,7 +77,8 @@ func BuildInstanceSet(synthesizedComp *component.SynthesizedComponent, component
SetTemplate(template).
AddMatchLabelsInMap(constant.GetCompLabels(clusterName, compName)).
SetReplicas(synthesizedComp.Replicas).
- SetMinReadySeconds(synthesizedComp.MinReadySeconds)
+ SetMinReadySeconds(synthesizedComp.MinReadySeconds).
+ SetLifecycleActions(synthesizedComp.LifecycleActions)
var vcts []corev1.PersistentVolumeClaim
for _, vct := range synthesizedComp.VolumeClaimTemplates {
diff --git a/pkg/controller/instanceset/instance_util.go b/pkg/controller/instanceset/instance_util.go
index 750da4ab900..11a5432e33a 100644
--- a/pkg/controller/instanceset/instance_util.go
+++ b/pkg/controller/instanceset/instance_util.go
@@ -39,6 +39,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+ kbappsv1 "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/builder"
@@ -50,7 +51,7 @@ import (
type InstanceTemplate interface {
GetName() string
GetReplicas() int32
- GetOrdinals() workloads.Ordinals
+ GetOrdinals() kbappsv1.Ordinals
}
type instanceTemplateExt struct {
@@ -339,7 +340,7 @@ func buildInstanceName2TemplateMap(itsExt *instanceSetExt) (map[string]*instance
return allNameTemplateMap, nil
}
-func GenerateAllInstanceNames(parentName string, replicas int32, templates []InstanceTemplate, offlineInstances []string, defaultTemplateOrdinals workloads.Ordinals) ([]string, error) {
+func GenerateAllInstanceNames(parentName string, replicas int32, templates []InstanceTemplate, offlineInstances []string, defaultTemplateOrdinals kbappsv1.Ordinals) ([]string, error) {
totalReplicas := int32(0)
instanceNameList := make([]string, 0)
for _, template := range templates {
@@ -439,7 +440,7 @@ func GetOrdinalListByTemplateName(its *workloads.InstanceSet, templateName strin
return ConvertOrdinalsToSortedList(ordinals)
}
-func GetOrdinalsByTemplateName(its *workloads.InstanceSet, templateName string) (workloads.Ordinals, error) {
+func GetOrdinalsByTemplateName(its *workloads.InstanceSet, templateName string) (kbappsv1.Ordinals, error) {
if templateName == "" {
return its.Spec.DefaultTemplateOrdinals, nil
}
@@ -448,10 +449,10 @@ func GetOrdinalsByTemplateName(its *workloads.InstanceSet, templateName string)
return template.Ordinals, nil
}
}
- return workloads.Ordinals{}, fmt.Errorf("template %s not found", templateName)
+ return kbappsv1.Ordinals{}, fmt.Errorf("template %s not found", templateName)
}
-func ConvertOrdinalsToSortedList(ordinals workloads.Ordinals) ([]int32, error) {
+func ConvertOrdinalsToSortedList(ordinals kbappsv1.Ordinals) ([]int32, error) {
ordinalList := sets.New(ordinals.Discrete...)
for _, item := range ordinals.Ranges {
start := item.Start
@@ -956,7 +957,8 @@ func buildInstanceTemplateExt(template workloads.InstanceTemplate, templateExt *
return vm.Name == item.Name
}
})
- intctrlutil.MergeList(&template.VolumeClaimTemplates, &templateExt.VolumeClaimTemplates,
+ vcts := intctrlutil.ToCoreV1PVCs(template.VolumeClaimTemplates)
+ intctrlutil.MergeList(&vcts, &templateExt.VolumeClaimTemplates,
func(item corev1.PersistentVolumeClaim) func(corev1.PersistentVolumeClaim) bool {
return func(claim corev1.PersistentVolumeClaim) bool {
return claim.Name == item.Name
diff --git a/pkg/controller/instanceset/instance_util_test.go b/pkg/controller/instanceset/instance_util_test.go
index e7fea0a6574..7b92a354680 100644
--- a/pkg/controller/instanceset/instance_util_test.go
+++ b/pkg/controller/instanceset/instance_util_test.go
@@ -35,6 +35,7 @@ import (
"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"
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/builder"
@@ -528,7 +529,7 @@ var _ = Describe("instance util test", func() {
var templates []InstanceTemplate
templates = append(templates, templatesFoo, templateBar)
offlineInstances := []string{"foo-bar-1", "foo-0"}
- instanceNameList, err := GenerateAllInstanceNames(parentName, 5, templates, offlineInstances, workloads.Ordinals{})
+ instanceNameList, err := GenerateAllInstanceNames(parentName, 5, templates, offlineInstances, kbappsv1.Ordinals{})
Expect(err).Should(BeNil())
podNamesExpected := []string{"foo-1", "foo-2", "foo-bar-0", "foo-bar-2", "foo-foo-0"}
@@ -536,8 +537,8 @@ var _ = Describe("instance util test", func() {
})
It("with Ordinals, without offlineInstances", func() {
parentName := "foo"
- defaultTemplateOrdinals := workloads.Ordinals{
- Ranges: []workloads.Range{
+ defaultTemplateOrdinals := kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 1,
End: 2,
@@ -547,15 +548,15 @@ var _ = Describe("instance util test", func() {
templatesFoo := &workloads.InstanceTemplate{
Name: "foo",
Replicas: pointer.Int32(1),
- Ordinals: workloads.Ordinals{
+ Ordinals: kbappsv1.Ordinals{
Discrete: []int32{0},
},
}
templateBar := &workloads.InstanceTemplate{
Name: "bar",
Replicas: pointer.Int32(3),
- Ordinals: workloads.Ordinals{
- Ranges: []workloads.Range{
+ Ordinals: kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 2,
End: 3,
@@ -574,8 +575,8 @@ var _ = Describe("instance util test", func() {
})
It("with templatesOrdinals, with offlineInstances", func() {
parentName := "foo"
- defaultTemplateOrdinals := workloads.Ordinals{
- Ranges: []workloads.Range{
+ defaultTemplateOrdinals := kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 1,
End: 2,
@@ -585,15 +586,15 @@ var _ = Describe("instance util test", func() {
templatesFoo := &workloads.InstanceTemplate{
Name: "foo",
Replicas: pointer.Int32(1),
- Ordinals: workloads.Ordinals{
+ Ordinals: kbappsv1.Ordinals{
Discrete: []int32{0},
},
}
templateBar := &workloads.InstanceTemplate{
Name: "bar",
Replicas: pointer.Int32(2),
- Ordinals: workloads.Ordinals{
- Ranges: []workloads.Range{
+ Ordinals: kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 2,
End: 3,
@@ -613,8 +614,8 @@ var _ = Describe("instance util test", func() {
})
It("with templatesOrdinals, with offlineInstances, replicas error", func() {
parentName := "foo"
- defaultTemplateOrdinals := workloads.Ordinals{
- Ranges: []workloads.Range{
+ defaultTemplateOrdinals := kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 1,
End: 2,
@@ -624,15 +625,15 @@ var _ = Describe("instance util test", func() {
templatesFoo := &workloads.InstanceTemplate{
Name: "foo",
Replicas: pointer.Int32(1),
- Ordinals: workloads.Ordinals{
+ Ordinals: kbappsv1.Ordinals{
Discrete: []int32{0},
},
}
templateBar := &workloads.InstanceTemplate{
Name: "bar",
Replicas: pointer.Int32(3),
- Ordinals: workloads.Ordinals{
- Ranges: []workloads.Range{
+ Ordinals: kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 2,
End: 3,
@@ -657,8 +658,8 @@ var _ = Describe("instance util test", func() {
It("should work well", func() {
its := &workloads.InstanceSet{
Spec: workloads.InstanceSetSpec{
- DefaultTemplateOrdinals: workloads.Ordinals{
- Ranges: []workloads.Range{
+ DefaultTemplateOrdinals: kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 1,
End: 2,
@@ -668,14 +669,14 @@ var _ = Describe("instance util test", func() {
Instances: []workloads.InstanceTemplate{
{
Name: "foo",
- Ordinals: workloads.Ordinals{
+ Ordinals: kbappsv1.Ordinals{
Discrete: []int32{0},
},
},
{
Name: "bar",
- Ordinals: workloads.Ordinals{
- Ranges: []workloads.Range{
+ Ordinals: kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 2,
End: 3,
@@ -718,8 +719,8 @@ var _ = Describe("instance util test", func() {
It("should work well", func() {
its := &workloads.InstanceSet{
Spec: workloads.InstanceSetSpec{
- DefaultTemplateOrdinals: workloads.Ordinals{
- Ranges: []workloads.Range{
+ DefaultTemplateOrdinals: kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 1,
End: 2,
@@ -729,14 +730,14 @@ var _ = Describe("instance util test", func() {
Instances: []workloads.InstanceTemplate{
{
Name: "foo",
- Ordinals: workloads.Ordinals{
+ Ordinals: kbappsv1.Ordinals{
Discrete: []int32{0},
},
},
{
Name: "bar",
- Ordinals: workloads.Ordinals{
- Ranges: []workloads.Range{
+ Ordinals: kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 2,
End: 3,
@@ -755,8 +756,8 @@ var _ = Describe("instance util test", func() {
ordinalsDefault, err := GetOrdinalsByTemplateName(its, templateNameDefault)
Expect(err).Should(BeNil())
- ordinalsDefaultExpected := workloads.Ordinals{
- Ranges: []workloads.Range{
+ ordinalsDefaultExpected := kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 1,
End: 2,
@@ -767,15 +768,15 @@ var _ = Describe("instance util test", func() {
ordinalsFoo, err := GetOrdinalsByTemplateName(its, templateNameFoo)
Expect(err).Should(BeNil())
- ordinalsFooExpected := workloads.Ordinals{
+ ordinalsFooExpected := kbappsv1.Ordinals{
Discrete: []int32{0},
}
Expect(ordinalsFoo).Should(Equal(ordinalsFooExpected))
ordinalsBar, err := GetOrdinalsByTemplateName(its, templateNameBar)
Expect(err).Should(BeNil())
- ordinalsBarExpected := workloads.Ordinals{
- Ranges: []workloads.Range{
+ ordinalsBarExpected := kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 2,
End: 3,
@@ -786,7 +787,7 @@ var _ = Describe("instance util test", func() {
Expect(ordinalsBar).Should(Equal(ordinalsBarExpected))
ordinalsNotFound, err := GetOrdinalsByTemplateName(its, templateNameNotFound)
- Expect(ordinalsNotFound).Should(Equal(workloads.Ordinals{}))
+ Expect(ordinalsNotFound).Should(Equal(kbappsv1.Ordinals{}))
errExpected := fmt.Errorf("template %s not found", templateNameNotFound)
Expect(err).Should(Equal(errExpected))
})
@@ -794,8 +795,8 @@ var _ = Describe("instance util test", func() {
Context("ConvertOrdinalsToSortedList", func() {
It("should work well", func() {
- ordinals := workloads.Ordinals{
- Ranges: []workloads.Range{
+ ordinals := kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 2,
End: 4,
@@ -808,8 +809,8 @@ var _ = Describe("instance util test", func() {
sets.New(ordinalList...).Equal(sets.New[int32](0, 2, 3, 4, 6))
})
It("rightNumber must >= leftNumber", func() {
- ordinals := workloads.Ordinals{
- Ranges: []workloads.Range{
+ ordinals := kbappsv1.Ordinals{
+ Ranges: []kbappsv1.Range{
{
Start: 4,
End: 2,
diff --git a/pkg/controller/instanceset/reconciler_update.go b/pkg/controller/instanceset/reconciler_update.go
index 7e8c453063e..b63d82dae8e 100644
--- a/pkg/controller/instanceset/reconciler_update.go
+++ b/pkg/controller/instanceset/reconciler_update.go
@@ -20,6 +20,7 @@ along with this program. If not, see .
package instanceset
import (
+ "errors"
"fmt"
"time"
@@ -30,8 +31,10 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets"
+ kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
workloads "github.com/apecloud/kubeblocks/apis/workloads/v1"
"github.com/apecloud/kubeblocks/pkg/controller/kubebuilderx"
+ "github.com/apecloud/kubeblocks/pkg/controller/lifecycle"
"github.com/apecloud/kubeblocks/pkg/controller/model"
)
@@ -95,6 +98,7 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
// 3. do update
// do nothing if UpdateStrategyType is 'OnDelete'
if its.Spec.UpdateStrategy.Type == apps.OnDeleteStatefulSetStrategyType {
+ // TODO: how to handle the OnDelete type?
return kubebuilderx.Continue, nil
}
@@ -176,12 +180,18 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
return kubebuilderx.Continue, err
}
newPod := copyAndMerge(pod, newInstance.pod)
+ if err = r.switchover(tree, its, newPod.(*corev1.Pod)); err != nil {
+ return kubebuilderx.Continue, err
+ }
if err = tree.Update(newPod); err != nil {
return kubebuilderx.Continue, err
}
updatingPods++
} else if updatePolicy == RecreatePolicy {
if !isTerminating(pod) {
+ if err = r.switchover(tree, its, pod); err != nil {
+ return kubebuilderx.Continue, err
+ }
if err = tree.Delete(pod); err != nil {
return kubebuilderx.Continue, err
}
@@ -199,6 +209,30 @@ func (r *updateReconciler) Reconcile(tree *kubebuilderx.ObjectTree) (kubebuilder
return kubebuilderx.Continue, nil
}
+func (r *updateReconciler) switchover(tree *kubebuilderx.ObjectTree, its *workloads.InstanceSet, pod *corev1.Pod) error {
+ if its.Spec.MembershipReconfiguration == nil || its.Spec.MembershipReconfiguration.Switchover == nil {
+ return nil
+ }
+
+ lifecycleActions := &kbappsv1.ComponentLifecycleActions{
+ Switchover: its.Spec.MembershipReconfiguration.Switchover,
+ }
+ // TODO: template vars
+ lfa, err := lifecycle.New(its.Namespace, its.Name, its.Name, lifecycleActions, nil, pod)
+ if err != nil {
+ return err
+ }
+ err = lfa.Switchover(tree.Context, nil, nil, "")
+ if err != nil {
+ if errors.Is(err, lifecycle.ErrActionNotDefined) {
+ return nil
+ }
+ return err
+ }
+ tree.Logger.Info("successfully call switchover action for pod", "pod", pod.Name)
+ return nil
+}
+
func buildBlockedCondition(its *workloads.InstanceSet, message string) *metav1.Condition {
return &metav1.Condition{
Type: string(workloads.InstanceUpdateRestricted),
diff --git a/pkg/controller/instanceset/tree_loader.go b/pkg/controller/instanceset/tree_loader.go
index ea3de56d94c..78a25b30ca3 100644
--- a/pkg/controller/instanceset/tree_loader.go
+++ b/pkg/controller/instanceset/tree_loader.go
@@ -50,6 +50,7 @@ func (r *treeLoader) Load(ctx context.Context, reader client.Reader, req ctrl.Re
return nil, err
}
+ tree.Context = ctx
tree.EventRecorder = recorder
tree.Logger = logger
tree.SetFinalizer(finalizer)
diff --git a/pkg/controller/instanceset/utils.go b/pkg/controller/instanceset/utils.go
index 78b8a2740c3..df7110ffa1e 100644
--- a/pkg/controller/instanceset/utils.go
+++ b/pkg/controller/instanceset/utils.go
@@ -90,67 +90,6 @@ func getRoleName(pod *corev1.Pod) string {
return strings.ToLower(pod.Labels[constant.RoleLabelKey])
}
-// IsInstancesReady gives Instance level 'ready' state when all instances are available
-func IsInstancesReady(its *workloads.InstanceSet) bool {
- if its == nil {
- return false
- }
- // check whether the cluster has been initialized
- if its.Status.ReadyInitReplicas != its.Status.InitReplicas {
- return false
- }
- // check whether latest spec has been sent to the underlying workload
- if its.Status.ObservedGeneration != its.Generation {
- return false
- }
- // check whether the underlying workload is ready
- if its.Spec.Replicas == nil {
- return false
- }
- replicas := *its.Spec.Replicas
- if its.Status.Replicas != replicas ||
- its.Status.ReadyReplicas != replicas ||
- its.Status.UpdatedReplicas != replicas {
- return false
- }
- // check availableReplicas only if minReadySeconds is set
- if its.Spec.MinReadySeconds > 0 && its.Status.AvailableReplicas != replicas {
- return false
- }
-
- return true
-}
-
-// IsInstanceSetReady gives InstanceSet level 'ready' state:
-// 1. all instances are available
-// 2. and all members have role set (if they are role-ful)
-func IsInstanceSetReady(its *workloads.InstanceSet) bool {
- instancesReady := IsInstancesReady(its)
- if !instancesReady {
- return false
- }
-
- // check whether role probe has done
- if len(its.Spec.Roles) == 0 {
- return true
- }
- membersStatus := its.Status.MembersStatus
- if len(membersStatus) != int(*its.Spec.Replicas) {
- return false
- }
- if its.Status.ReadyWithoutPrimary {
- return true
- }
- hasLeader := false
- for _, status := range membersStatus {
- if status.ReplicaRole != nil && status.ReplicaRole.IsLeader {
- hasLeader = true
- break
- }
- }
- return hasLeader
-}
-
// AddAnnotationScope will add AnnotationScope defined by 'scope' to all keys in map 'annotations'.
func AddAnnotationScope(scope AnnotationScope, annotations map[string]string) map[string]string {
if annotations == nil {
diff --git a/pkg/controller/instanceset/utils_test.go b/pkg/controller/instanceset/utils_test.go
index bde7f772e09..b21504d8552 100644
--- a/pkg/controller/instanceset/utils_test.go
+++ b/pkg/controller/instanceset/utils_test.go
@@ -171,7 +171,7 @@ var _ = Describe("utils test", func() {
It("should work well", func() {
By("set its to nil")
its = nil
- Expect(IsInstanceSetReady(its)).Should(BeFalse())
+ Expect(its.IsInstanceSetReady()).Should(BeFalse())
By("set its to not initialized")
replicas := int32(3)
@@ -182,17 +182,17 @@ var _ = Describe("utils test", func() {
its.Status = workloads.InstanceSetStatus{
InitReplicas: replicas,
}
- Expect(IsInstanceSetReady(its)).Should(BeFalse())
+ Expect(its.IsInstanceSetReady()).Should(BeFalse())
By("set its.status.observedGeneration to not equal generation")
its.Status.ReadyInitReplicas = replicas
its.Generation = 1
- Expect(IsInstanceSetReady(its)).Should(BeFalse())
+ Expect(its.IsInstanceSetReady()).Should(BeFalse())
By("set its.status.replicas to not as expected")
its.Status.ObservedGeneration = its.Generation
its.Status.Replicas = replicas - 1
- Expect(IsInstanceSetReady(its)).Should(BeFalse())
+ Expect(its.IsInstanceSetReady()).Should(BeFalse())
By("set spec.minReadySeconds to not zero")
its.Status.Replicas = replicas
@@ -200,16 +200,16 @@ var _ = Describe("utils test", func() {
its.Status.UpdatedReplicas = replicas
its.Status.AvailableReplicas = replicas - 1
its.Spec.MinReadySeconds = int32(5)
- Expect(IsInstanceSetReady(its)).Should(BeFalse())
+ Expect(its.IsInstanceSetReady()).Should(BeFalse())
By("set its to role-less")
its.Status.AvailableReplicas = replicas
its.Spec.Roles = nil
- Expect(IsInstanceSetReady(its)).Should(BeTrue())
+ Expect(its.IsInstanceSetReady()).Should(BeTrue())
By("set its to role-ful")
its.Spec.Roles = roles
- Expect(IsInstanceSetReady(its)).Should(BeFalse())
+ Expect(its.IsInstanceSetReady()).Should(BeFalse())
By("set membersStatus to ready")
its.Status.MembersStatus = []workloads.MemberStatus{
@@ -226,7 +226,7 @@ var _ = Describe("utils test", func() {
ReplicaRole: &roles[2],
},
}
- Expect(IsInstanceSetReady(its)).Should(BeTrue())
+ Expect(its.IsInstanceSetReady()).Should(BeTrue())
})
})
diff --git a/pkg/controller/kubebuilderx/reconciler.go b/pkg/controller/kubebuilderx/reconciler.go
index 1926d1f146d..c939338b1ae 100644
--- a/pkg/controller/kubebuilderx/reconciler.go
+++ b/pkg/controller/kubebuilderx/reconciler.go
@@ -35,6 +35,7 @@ import (
type ObjectTree struct {
// TODO(free6om): should find a better place to hold these two params?
+ context.Context
record.EventRecorder
logr.Logger
@@ -113,6 +114,7 @@ func (t *ObjectTree) DeepCopy() (*ObjectTree, error) {
}
out.children = children
out.finalizer = t.finalizer
+ out.Context = t.Context
out.EventRecorder = t.EventRecorder
out.Logger = t.Logger
return out, nil
diff --git a/pkg/controller/component/lifecycle/errors.go b/pkg/controller/lifecycle/errors.go
similarity index 100%
rename from pkg/controller/component/lifecycle/errors.go
rename to pkg/controller/lifecycle/errors.go
diff --git a/pkg/controller/component/lifecycle/kbagent.go b/pkg/controller/lifecycle/kbagent.go
similarity index 84%
rename from pkg/controller/component/lifecycle/kbagent.go
rename to pkg/controller/lifecycle/kbagent.go
index 0c8019abc37..1b4a9f69637 100644
--- a/pkg/controller/component/lifecycle/kbagent.go
+++ b/pkg/controller/lifecycle/kbagent.go
@@ -33,8 +33,6 @@ import (
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/component"
- "github.com/apecloud/kubeblocks/pkg/controller/instanceset"
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
kbagt "github.com/apecloud/kubeblocks/pkg/kbagent"
kbacli "github.com/apecloud/kubeblocks/pkg/kbagent/client"
@@ -47,68 +45,72 @@ type lifecycleAction interface {
}
type kbagent struct {
- synthesizedComp *component.SynthesizedComponent
- pods []*corev1.Pod
- pod *corev1.Pod
+ namespace string
+ clusterName string
+ compName string
+ lifecycleActions *appsv1.ComponentLifecycleActions
+ templateVars map[string]any
+ pods []*corev1.Pod
+ pod *corev1.Pod
}
var _ Lifecycle = &kbagent{}
func (a *kbagent) PostProvision(ctx context.Context, cli client.Reader, opts *Options) error {
lfa := &postProvision{
- namespace: a.synthesizedComp.Namespace,
- clusterName: a.synthesizedComp.ClusterName,
- compName: a.synthesizedComp.Name,
- action: a.synthesizedComp.LifecycleActions.PostProvision,
+ namespace: a.namespace,
+ clusterName: a.clusterName,
+ compName: a.compName,
+ action: a.lifecycleActions.PostProvision,
}
return a.ignoreOutput(a.checkedCallAction(ctx, cli, lfa.action, lfa, opts))
}
func (a *kbagent) PreTerminate(ctx context.Context, cli client.Reader, opts *Options) error {
lfa := &preTerminate{
- namespace: a.synthesizedComp.Namespace,
- clusterName: a.synthesizedComp.ClusterName,
- compName: a.synthesizedComp.Name,
- action: a.synthesizedComp.LifecycleActions.PreTerminate,
+ namespace: a.namespace,
+ clusterName: a.clusterName,
+ compName: a.compName,
+ action: a.lifecycleActions.PreTerminate,
}
return a.ignoreOutput(a.checkedCallAction(ctx, cli, lfa.action, lfa, opts))
}
func (a *kbagent) RoleProbe(ctx context.Context, cli client.Reader, opts *Options) ([]byte, error) {
- return a.checkedCallProbe(ctx, cli, a.synthesizedComp.LifecycleActions.RoleProbe, &roleProbe{}, opts)
+ return a.checkedCallProbe(ctx, cli, a.lifecycleActions.RoleProbe, &roleProbe{}, opts)
}
func (a *kbagent) Switchover(ctx context.Context, cli client.Reader, opts *Options, candidate string) error {
roleName := a.pod.Labels[constant.RoleLabelKey]
lfa := &switchover{
- namespace: a.synthesizedComp.Namespace,
- clusterName: a.synthesizedComp.ClusterName,
- compName: a.synthesizedComp.Name,
+ namespace: a.namespace,
+ clusterName: a.clusterName,
+ compName: a.compName,
role: roleName,
currentPod: a.pod.Name,
candidatePod: candidate,
}
- return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.synthesizedComp.LifecycleActions.Switchover, lfa, opts))
+ return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.lifecycleActions.Switchover, lfa, opts))
}
func (a *kbagent) MemberJoin(ctx context.Context, cli client.Reader, opts *Options) error {
lfa := &memberJoin{
- namespace: a.synthesizedComp.Namespace,
- clusterName: a.synthesizedComp.ClusterName,
- compName: a.synthesizedComp.Name,
+ namespace: a.namespace,
+ clusterName: a.clusterName,
+ compName: a.compName,
pod: a.pod,
}
- return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.synthesizedComp.LifecycleActions.MemberJoin, lfa, opts))
+ return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.lifecycleActions.MemberJoin, lfa, opts))
}
func (a *kbagent) MemberLeave(ctx context.Context, cli client.Reader, opts *Options) error {
lfa := &memberLeave{
- namespace: a.synthesizedComp.Namespace,
- clusterName: a.synthesizedComp.ClusterName,
- compName: a.synthesizedComp.Name,
+ namespace: a.namespace,
+ clusterName: a.clusterName,
+ compName: a.compName,
pod: a.pod,
}
- return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.synthesizedComp.LifecycleActions.MemberLeave, lfa, opts))
+ return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.lifecycleActions.MemberLeave, lfa, opts))
}
func (a *kbagent) AccountProvision(ctx context.Context, cli client.Reader, opts *Options, statement, user, password string) error {
@@ -117,7 +119,7 @@ func (a *kbagent) AccountProvision(ctx context.Context, cli client.Reader, opts
user: user,
password: password,
}
- return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.synthesizedComp.LifecycleActions.AccountProvision, lfa, opts))
+ return a.ignoreOutput(a.checkedCallAction(ctx, cli, a.lifecycleActions.AccountProvision, lfa, opts))
}
func (a *kbagent) ignoreOutput(_ []byte, err error) error {
@@ -165,7 +167,7 @@ func (a *kbagent) clusterReadyCheck(ctx context.Context, cli client.Reader) erro
cluster := object.(*appsv1.Cluster)
return cluster.Status.Phase == appsv1.RunningClusterPhase
}
- return a.readyCheck(ctx, cli, a.synthesizedComp.ClusterName, "cluster", &appsv1.Cluster{}, ready)
+ return a.readyCheck(ctx, cli, a.clusterName, "cluster", &appsv1.Cluster{}, ready)
}
func (a *kbagent) compReadyCheck(ctx context.Context, cli client.Reader) error {
@@ -173,22 +175,22 @@ func (a *kbagent) compReadyCheck(ctx context.Context, cli client.Reader) error {
comp := object.(*appsv1.Component)
return comp.Status.Phase == appsv1.RunningComponentPhase
}
- compName := constant.GenerateClusterComponentName(a.synthesizedComp.ClusterName, a.synthesizedComp.Name)
+ compName := constant.GenerateClusterComponentName(a.clusterName, a.compName)
return a.readyCheck(ctx, cli, compName, "component", &appsv1.Component{}, ready)
}
func (a *kbagent) runtimeReadyCheck(ctx context.Context, cli client.Reader) error {
- name := constant.GenerateWorkloadNamePattern(a.synthesizedComp.ClusterName, a.synthesizedComp.Name)
+ name := constant.GenerateWorkloadNamePattern(a.clusterName, a.compName)
ready := func(object client.Object) bool {
its := object.(*workloads.InstanceSet)
- return instanceset.IsInstancesReady(its)
+ return its.IsInstancesReady()
}
return a.readyCheck(ctx, cli, name, "runtime", &workloads.InstanceSet{}, ready)
}
func (a *kbagent) readyCheck(ctx context.Context, cli client.Reader, name, kind string, obj client.Object, ready func(object client.Object) bool) error {
key := types.NamespacedName{
- Namespace: a.synthesizedComp.Namespace,
+ Namespace: a.namespace,
Name: name,
}
if err := cli.Get(ctx, key, obj); err != nil {
@@ -255,7 +257,7 @@ func (a *kbagent) parameters(ctx context.Context, cli client.Reader, lfa lifecyc
func (a *kbagent) templateVarsParameters() (map[string]string, error) {
m := map[string]string{}
- for k, v := range a.synthesizedComp.TemplateVars {
+ for k, v := range a.templateVars {
m[k] = v.(string)
}
return m, nil
diff --git a/pkg/controller/component/lifecycle/lfa_account.go b/pkg/controller/lifecycle/lfa_account.go
similarity index 100%
rename from pkg/controller/component/lifecycle/lfa_account.go
rename to pkg/controller/lifecycle/lfa_account.go
diff --git a/pkg/controller/component/lifecycle/lfa_component.go b/pkg/controller/lifecycle/lfa_component.go
similarity index 92%
rename from pkg/controller/component/lifecycle/lfa_component.go
rename to pkg/controller/lifecycle/lfa_component.go
index ccdbaf73f95..eb5a6baf4b6 100644
--- a/pkg/controller/component/lifecycle/lfa_component.go
+++ b/pkg/controller/lifecycle/lfa_component.go
@@ -21,14 +21,15 @@ package lifecycle
import (
"context"
+ "fmt"
"strings"
"sigs.k8s.io/controller-runtime/pkg/client"
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
"github.com/apecloud/kubeblocks/pkg/constant"
- "github.com/apecloud/kubeblocks/pkg/controller/component"
"github.com/apecloud/kubeblocks/pkg/controller/model"
+ intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
)
const (
@@ -126,8 +127,8 @@ func hackParameters4Comp(ctx context.Context, cli client.Reader, namespace, clus
cl := make([][]string, 0)
ccl := make([][]string, 0)
for _, comp := range compList.Items {
- name, _ := component.ShortName(clusterName, comp.Name)
- pods, err := component.ListOwnedPods(ctx, cli, namespace, clusterName, name)
+ name, _ := shortName(clusterName, comp.Name)
+ pods, err := intctrlutil.ListOwnedPods(ctx, cli, namespace, clusterName, name)
if err != nil {
return err
}
@@ -163,7 +164,7 @@ func hackParameters4Comp(ctx context.Context, cli client.Reader, namespace, clus
func() {
all, deleting, undeleted := make([]string, 0), make([]string, 0), make([]string, 0)
for _, comp := range compList.Items {
- name, _ := component.ShortName(clusterName, comp.Name)
+ name, _ := shortName(clusterName, comp.Name)
all = append(all, name)
if model.IsObjectDeleting(&comp) {
deleting = append(deleting, name)
@@ -179,7 +180,7 @@ func hackParameters4Comp(ctx context.Context, cli client.Reader, namespace, clus
if terminate {
func() {
for _, comp := range compList.Items {
- name, _ := component.ShortName(clusterName, comp.Name)
+ name, _ := shortName(clusterName, comp.Name)
if name == compName {
if comp.Annotations != nil {
val, ok := comp.Annotations[constant.ComponentScaleInAnnotationKey]
@@ -194,3 +195,11 @@ func hackParameters4Comp(ctx context.Context, cli client.Reader, namespace, clus
}
return m, nil
}
+
+func shortName(clusterName, compName string) (string, error) {
+ name, found := strings.CutPrefix(compName, fmt.Sprintf("%s-", clusterName))
+ if !found {
+ return "", fmt.Errorf("the component name has no cluster name as prefix: %s", compName)
+ }
+ return name, nil
+}
diff --git a/pkg/controller/component/lifecycle/lfa_member.go b/pkg/controller/lifecycle/lfa_member.go
similarity index 90%
rename from pkg/controller/component/lifecycle/lfa_member.go
rename to pkg/controller/lifecycle/lfa_member.go
index 3d274216c92..445381a85ab 100644
--- a/pkg/controller/component/lifecycle/lfa_member.go
+++ b/pkg/controller/lifecycle/lfa_member.go
@@ -26,7 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/apecloud/kubeblocks/pkg/constant"
- "github.com/apecloud/kubeblocks/pkg/controller/component"
+ intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
)
const (
@@ -74,10 +74,10 @@ func (a *switchover) parameters(ctx context.Context, cli client.Reader) (map[str
compName := constant.GenerateClusterComponentName(a.clusterName, a.compName)
if len(a.candidatePod) > 0 {
m[switchoverCandidateName] = a.candidatePod
- m[switchoverCandidateFQDN] = component.PodFQDN(a.namespace, compName, a.candidatePod)
+ m[switchoverCandidateFQDN] = intctrlutil.PodFQDN(a.namespace, compName, a.candidatePod)
}
m[switchoverCurrentName] = a.currentPod
- m[switchoverCurrentFQDN] = component.PodFQDN(a.namespace, compName, a.currentPod)
+ m[switchoverCurrentFQDN] = intctrlutil.PodFQDN(a.namespace, compName, a.currentPod)
m[switchoverRole] = a.role
return m, nil
}
@@ -102,7 +102,7 @@ func (a *memberJoin) parameters(ctx context.Context, cli client.Reader) (map[str
// - KB_JOIN_MEMBER_POD_NAME: The pod name of the replica being added to the group.
compName := constant.GenerateClusterComponentName(a.clusterName, a.compName)
return map[string]string{
- joinMemberPodFQDNVar: component.PodFQDN(a.namespace, compName, a.pod.Name),
+ joinMemberPodFQDNVar: intctrlutil.PodFQDN(a.namespace, compName, a.pod.Name),
joinMemberPodNameVar: a.pod.Name,
}, nil
}
@@ -127,7 +127,7 @@ func (a *memberLeave) parameters(ctx context.Context, cli client.Reader) (map[st
// - KB_LEAVE_MEMBER_POD_NAME: The pod name of the replica being removed from the group.
compName := constant.GenerateClusterComponentName(a.clusterName, a.compName)
return map[string]string{
- leaveMemberPodFQDNVar: component.PodFQDN(a.namespace, compName, a.pod.Name),
+ leaveMemberPodFQDNVar: intctrlutil.PodFQDN(a.namespace, compName, a.pod.Name),
leaveMemberPodNameVar: a.pod.Name,
}, nil
}
diff --git a/pkg/controller/component/lifecycle/lifecycle.go b/pkg/controller/lifecycle/lifecycle.go
similarity index 83%
rename from pkg/controller/component/lifecycle/lifecycle.go
rename to pkg/controller/lifecycle/lifecycle.go
index eebfbd28982..18253406aa1 100644
--- a/pkg/controller/component/lifecycle/lifecycle.go
+++ b/pkg/controller/lifecycle/lifecycle.go
@@ -27,7 +27,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
- "github.com/apecloud/kubeblocks/pkg/controller/component"
)
type Options struct {
@@ -58,7 +57,8 @@ type Lifecycle interface {
AccountProvision(ctx context.Context, cli client.Reader, opts *Options, statement, user, password string) error
}
-func New(synthesizedComp *component.SynthesizedComponent, pod *corev1.Pod, pods ...*corev1.Pod) (Lifecycle, error) {
+func New(namespace, clusterName, compName string, lifecycleActions *appsv1.ComponentLifecycleActions,
+ templateVars map[string]any, pod *corev1.Pod, pods ...*corev1.Pod) (Lifecycle, error) {
if pod == nil && len(pods) == 0 {
return nil, fmt.Errorf("either pod or pods must be provided to call lifecycle actions")
}
@@ -69,8 +69,12 @@ func New(synthesizedComp *component.SynthesizedComponent, pod *corev1.Pod, pods
pods = []*corev1.Pod{pod}
}
return &kbagent{
- synthesizedComp: synthesizedComp,
- pods: pods,
- pod: pod,
+ namespace: namespace,
+ clusterName: clusterName,
+ compName: compName,
+ lifecycleActions: lifecycleActions,
+ templateVars: templateVars,
+ pods: pods,
+ pod: pod,
}, nil
}
diff --git a/pkg/controller/component/lifecycle/lifecycle_test.go b/pkg/controller/lifecycle/lifecycle_test.go
similarity index 80%
rename from pkg/controller/component/lifecycle/lifecycle_test.go
rename to pkg/controller/lifecycle/lifecycle_test.go
index 7ad5323b33e..f6d64108149 100644
--- a/pkg/controller/component/lifecycle/lifecycle_test.go
+++ b/pkg/controller/lifecycle/lifecycle_test.go
@@ -37,7 +37,6 @@ import (
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
"github.com/apecloud/kubeblocks/pkg/constant"
- "github.com/apecloud/kubeblocks/pkg/controller/component"
kbacli "github.com/apecloud/kubeblocks/pkg/kbagent/client"
"github.com/apecloud/kubeblocks/pkg/kbagent/proto"
)
@@ -94,8 +93,11 @@ var mockKBAgentClient = func(mock func(*kbacli.MockClientMockRecorder)) {
var _ = Describe("lifecycle", func() {
var (
- synthesizedComp *component.SynthesizedComponent
- pods []*corev1.Pod
+ namespace string
+ clusterName string
+ compName string
+ lifecycleActions *appsv1.ComponentLifecycleActions
+ pods []*corev1.Pod
)
cleanEnv := func() {
@@ -109,46 +111,34 @@ var _ = Describe("lifecycle", func() {
BeforeEach(func() {
cleanEnv()
- synthesizedComp = &component.SynthesizedComponent{
- Namespace: "default",
- ClusterName: "test-cluster",
- Name: "kbagent",
- PodSpec: &corev1.PodSpec{
- Containers: []corev1.Container{
- {
- Name: "test-kbagent",
- },
+ namespace = "default"
+ clusterName = "test-cluster"
+ compName = "kbagent"
+ lifecycleActions = &appsv1.ComponentLifecycleActions{
+ PostProvision: &appsv1.Action{
+ Exec: &appsv1.ExecAction{
+ Command: []string{"/bin/bash", "-c", "echo -n post-provision"},
+ },
+ TimeoutSeconds: 5,
+ RetryPolicy: &appsv1.RetryPolicy{
+ MaxRetries: 5,
+ RetryInterval: 10,
},
},
- LifecycleActions: &appsv1.ComponentLifecycleActions{
- PostProvision: &appsv1.Action{
+ RoleProbe: &appsv1.Probe{
+ Action: appsv1.Action{
Exec: &appsv1.ExecAction{
- Command: []string{"/bin/bash", "-c", "echo -n post-provision"},
+ Command: []string{"/bin/bash", "-c", "echo -n role-probe"},
},
TimeoutSeconds: 5,
- RetryPolicy: &appsv1.RetryPolicy{
- MaxRetries: 5,
- RetryInterval: 10,
- },
- },
- RoleProbe: &appsv1.Probe{
- Action: appsv1.Action{
- Exec: &appsv1.ExecAction{
- Command: []string{"/bin/bash", "-c", "echo -n role-probe"},
- },
- TimeoutSeconds: 5,
- },
- InitialDelaySeconds: 5,
- PeriodSeconds: 1,
- SuccessThreshold: 3,
- FailureThreshold: 3,
},
+ InitialDelaySeconds: 5,
+ PeriodSeconds: 1,
+ SuccessThreshold: 3,
+ FailureThreshold: 3,
},
}
-
- pods = []*corev1.Pod{
- {},
- }
+ pods = []*corev1.Pod{{}}
})
AfterEach(func() {
@@ -159,19 +149,22 @@ var _ = Describe("lifecycle", func() {
Context("new", func() {
It("nil pod", func() {
- _, err := New(nil, nil)
+ _, err := New("", "", "", nil, nil, nil)
Expect(err).ShouldNot(BeNil())
Expect(err.Error()).Should(ContainSubstring("either pod or pods must be provided to call lifecycle actions"))
})
It("pod", func() {
pod := pods[0]
- lifecycle, err := New(synthesizedComp, pod)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, pod)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
agent := lifecycle.(*kbagent)
- Expect(agent.synthesizedComp).Should(Equal(synthesizedComp))
+ Expect(agent.namespace).Should(Equal(namespace))
+ Expect(agent.clusterName).Should(Equal(clusterName))
+ Expect(agent.compName).Should(Equal(compName))
+ Expect(agent.lifecycleActions).Should(Equal(lifecycleActions))
Expect(agent.pod).Should(Equal(pod))
Expect(agent.pods).Should(HaveLen(1))
Expect(agent.pods[0]).Should(Equal(pod))
@@ -179,12 +172,15 @@ var _ = Describe("lifecycle", func() {
It("pods", func() {
pod := pods[0]
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
agent := lifecycle.(*kbagent)
- Expect(agent.synthesizedComp).Should(Equal(synthesizedComp))
+ Expect(agent.namespace).Should(Equal(namespace))
+ Expect(agent.clusterName).Should(Equal(clusterName))
+ Expect(agent.compName).Should(Equal(compName))
+ Expect(agent.lifecycleActions).Should(Equal(lifecycleActions))
Expect(agent.pod).Should(Equal(pod))
Expect(agent.pods).Should(HaveLen(1))
Expect(agent.pods[0]).Should(Equal(pod))
@@ -193,7 +189,7 @@ var _ = Describe("lifecycle", func() {
Context("call action", func() {
It("not defined", func() {
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -203,11 +199,11 @@ var _ = Describe("lifecycle", func() {
})
It("action request", func() {
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
- action := synthesizedComp.LifecycleActions.PostProvision
+ action := lifecycleActions.PostProvision
mockKBAgentClient(func(recorder *kbacli.MockClientMockRecorder) {
recorder.Action(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req proto.ActionRequest) (proto.ActionResponse, error) {
Expect(req.Action).Should(Equal("postProvision"))
@@ -233,7 +229,7 @@ var _ = Describe("lifecycle", func() {
})
It("succeed", func() {
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -248,7 +244,7 @@ var _ = Describe("lifecycle", func() {
})
It("succeed and stdout", func() {
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -266,7 +262,7 @@ var _ = Describe("lifecycle", func() {
})
It("fail - error code", func() {
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -335,7 +331,7 @@ var _ = Describe("lifecycle", func() {
})
It("fail - error msg", func() {
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -355,7 +351,7 @@ var _ = Describe("lifecycle", func() {
})
It("parameters", func() {
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -364,19 +360,19 @@ var _ = Describe("lifecycle", func() {
objs: []client.Object{
&appsv1.Component{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
- Name: constant.GenerateClusterComponentName(synthesizedComp.ClusterName, synthesizedComp.Name),
+ Namespace: namespace,
+ Name: constant.GenerateClusterComponentName(clusterName, compName),
Labels: map[string]string{
- constant.AppInstanceLabelKey: synthesizedComp.ClusterName,
+ constant.AppInstanceLabelKey: clusterName,
},
},
},
&appsv1.Component{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
- Name: constant.GenerateClusterComponentName(synthesizedComp.ClusterName, "another"),
+ Namespace: namespace,
+ Name: constant.GenerateClusterComponentName(clusterName, "another"),
Labels: map[string]string{
- constant.AppInstanceLabelKey: synthesizedComp.ClusterName,
+ constant.AppInstanceLabelKey: clusterName,
},
},
},
@@ -387,7 +383,7 @@ var _ = Describe("lifecycle", func() {
recorder.Action(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, req proto.ActionRequest) (proto.ActionResponse, error) {
Expect(req.Action).Should(Equal("postProvision"))
Expect(req.Parameters).ShouldNot(BeNil()) // legacy parameters for post-provision action
- Expect(req.Parameters[hackedAllCompList]).Should(Equal(strings.Join([]string{synthesizedComp.Name, "another"}, ",")))
+ Expect(req.Parameters[hackedAllCompList]).Should(Equal(strings.Join([]string{compName, "another"}, ",")))
return proto.ActionResponse{}, nil
}).AnyTimes()
})
@@ -399,9 +395,8 @@ var _ = Describe("lifecycle", func() {
It("template vars", func() {
key := "TEMPLATE_VAR1"
val := "template-vars1"
- synthesizedComp.TemplateVars = map[string]any{key: val}
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, map[string]any{key: val}, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -423,9 +418,9 @@ var _ = Describe("lifecycle", func() {
It("precondition", func() {
clusterReady := appsv1.ClusterReadyPreConditionType
- synthesizedComp.LifecycleActions.PostProvision.PreCondition = &clusterReady
+ lifecycleActions.PostProvision.PreCondition = &clusterReady
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -434,8 +429,8 @@ var _ = Describe("lifecycle", func() {
objs: []client.Object{
&appsv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
- Name: synthesizedComp.ClusterName,
+ Namespace: namespace,
+ Name: clusterName,
},
Status: appsv1.ClusterStatus{
Phase: appsv1.RunningClusterPhase,
@@ -456,9 +451,9 @@ var _ = Describe("lifecycle", func() {
It("precondition - fail", func() {
clusterReady := appsv1.ClusterReadyPreConditionType
- synthesizedComp.LifecycleActions.PostProvision.PreCondition = &clusterReady
+ lifecycleActions.PostProvision.PreCondition = &clusterReady
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -467,8 +462,8 @@ var _ = Describe("lifecycle", func() {
objs: []client.Object{
&appsv1.Cluster{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
- Name: synthesizedComp.ClusterName,
+ Namespace: namespace,
+ Name: clusterName,
},
Status: appsv1.ClusterStatus{
Phase: appsv1.FailedClusterPhase,
@@ -483,11 +478,11 @@ var _ = Describe("lifecycle", func() {
})
It("pod selector - any", func() {
- synthesizedComp.LifecycleActions.PostProvision.Exec.TargetPodSelector = appsv1.AnyReplica
+ lifecycleActions.PostProvision.Exec.TargetPodSelector = appsv1.AnyReplica
pods = []*corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
+ Namespace: namespace,
Name: "pod-0",
},
Spec: corev1.PodSpec{
@@ -505,7 +500,7 @@ var _ = Describe("lifecycle", func() {
},
{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
+ Namespace: namespace,
Name: "pod-1",
},
Spec: corev1.PodSpec{
@@ -523,7 +518,7 @@ var _ = Describe("lifecycle", func() {
},
}
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -537,12 +532,12 @@ var _ = Describe("lifecycle", func() {
})
It("pod selector - role", func() {
- synthesizedComp.LifecycleActions.PostProvision.Exec.TargetPodSelector = appsv1.RoleSelector
- synthesizedComp.LifecycleActions.PostProvision.Exec.MatchingKey = "leader"
+ lifecycleActions.PostProvision.Exec.TargetPodSelector = appsv1.RoleSelector
+ lifecycleActions.PostProvision.Exec.MatchingKey = "leader"
pods = []*corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
+ Namespace: namespace,
Name: "pod-0",
Labels: map[string]string{
constant.RoleLabelKey: "follower",
@@ -563,7 +558,7 @@ var _ = Describe("lifecycle", func() {
},
{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
+ Namespace: namespace,
Name: "pod-1",
Labels: map[string]string{
constant.RoleLabelKey: "leader",
@@ -584,7 +579,7 @@ var _ = Describe("lifecycle", func() {
},
}
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
@@ -594,12 +589,12 @@ var _ = Describe("lifecycle", func() {
})
It("pod selector - has no matched", func() {
- synthesizedComp.LifecycleActions.PostProvision.Exec.TargetPodSelector = appsv1.RoleSelector
- synthesizedComp.LifecycleActions.PostProvision.Exec.MatchingKey = "leader"
+ lifecycleActions.PostProvision.Exec.TargetPodSelector = appsv1.RoleSelector
+ lifecycleActions.PostProvision.Exec.MatchingKey = "leader"
pods = []*corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
+ Namespace: namespace,
Name: "pod-0",
Labels: map[string]string{
constant.RoleLabelKey: "follower",
@@ -608,7 +603,7 @@ var _ = Describe("lifecycle", func() {
},
{
ObjectMeta: metav1.ObjectMeta{
- Namespace: synthesizedComp.Namespace,
+ Namespace: namespace,
Name: "pod-1",
Labels: map[string]string{
constant.RoleLabelKey: "follower",
@@ -617,7 +612,7 @@ var _ = Describe("lifecycle", func() {
},
}
- lifecycle, err := New(synthesizedComp, nil, pods...)
+ lifecycle, err := New(namespace, clusterName, compName, lifecycleActions, nil, nil, pods...)
Expect(err).Should(BeNil())
Expect(lifecycle).ShouldNot(BeNil())
diff --git a/pkg/controller/component/lifecycle/suite_test.go b/pkg/controller/lifecycle/suite_test.go
similarity index 98%
rename from pkg/controller/component/lifecycle/suite_test.go
rename to pkg/controller/lifecycle/suite_test.go
index 541c2ba6798..2d1dd2cb62b 100644
--- a/pkg/controller/component/lifecycle/suite_test.go
+++ b/pkg/controller/lifecycle/suite_test.go
@@ -84,7 +84,7 @@ var _ = BeforeSuite(func() {
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{
- filepath.Join("..", "..", "..", "..", "config", "crd", "bases"),
+ 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/",
diff --git a/pkg/controller/multicluster/setup.go b/pkg/controller/multicluster/setup.go
index 782395fbb0e..74957ec4d72 100644
--- a/pkg/controller/multicluster/setup.go
+++ b/pkg/controller/multicluster/setup.go
@@ -23,6 +23,7 @@ import (
"fmt"
"strings"
+ corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
@@ -32,7 +33,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
"sigs.k8s.io/controller-runtime/pkg/client/config"
- intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
+ appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
+ appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1"
)
var (
@@ -201,11 +203,38 @@ func clientOptions(scheme *runtime.Scheme, ctx string, config *rest.Config) (cli
Mapper: mapper,
Cache: &client.CacheOptions{
Unstructured: false,
- DisableFor: intctrlutil.GetUncachedObjects(),
+ DisableFor: getUncachedObjects(),
},
}, nil
}
+// getUncachedObjects returns a list of K8s objects, for these object types,
+// and their list types, client.Reader will read directly from the API server instead
+// of the cache, which may not be up-to-date.
+// see sigs.k8s.io/controller-runtime/pkg/client/split.go to understand how client
+// works with this UncachedObjects filter.
+func getUncachedObjects() []client.Object {
+ // client-side read cache reduces the number of requests processed in the API server,
+ // which is good for performance. However, it can sometimes lead to obscure issues,
+ // most notably lacking read-after-write consistency, i.e. reading a value immediately
+ // after updating it may miss to see the changes.
+ // while in most cases this problem can be mitigated by retrying later in an idempotent
+ // manner, there are some cases where it cannot, for example if a decision is to be made
+ // that has side-effect operations such as returning an error message to the user
+ // (in webhook) or deleting certain resources (in controllerutil.HandleCRDeletion).
+ // additionally, retry loops cause unnecessary delays when reconciliations are processed.
+ // for the sake of performance, now only the objects created by the end-user is listed here,
+ // to solve the two problems mentioned above.
+ // consider carefully before adding new objects to this list.
+ return []client.Object{
+ // avoid to cache potential large data objects
+ &corev1.ConfigMap{},
+ &corev1.Secret{},
+ &appsv1.Cluster{},
+ &appsv1alpha1.Configuration{},
+ }
+}
+
func cacheOptions(opts client.Options) cache.Options {
return cache.Options{
HTTPClient: opts.HTTPClient,
diff --git a/pkg/controllerutil/volume_util.go b/pkg/controllerutil/volume_util.go
index 01ee24a0fda..e9a3be8b900 100644
--- a/pkg/controllerutil/volume_util.go
+++ b/pkg/controllerutil/volume_util.go
@@ -26,6 +26,7 @@ import (
"golang.org/x/exp/maps"
corev1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
"github.com/apecloud/kubeblocks/pkg/constant"
@@ -116,3 +117,30 @@ func BuildVolumeMode(configs []string, configSpec appsv1.ComponentTemplateSpec)
}
return &scriptsDefaultMode
}
+
+func ToCoreV1PVCs(vcts []appsv1.ClusterComponentVolumeClaimTemplate) []corev1.PersistentVolumeClaim {
+ storageClassName := func(spec appsv1.PersistentVolumeClaimSpec, defaultStorageClass string) *string {
+ if spec.StorageClassName != nil && *spec.StorageClassName != "" {
+ return spec.StorageClassName
+ }
+ if defaultStorageClass != "" {
+ return &defaultStorageClass
+ }
+ return nil
+ }
+ var pvcs []corev1.PersistentVolumeClaim
+ for _, v := range vcts {
+ pvcs = append(pvcs, corev1.PersistentVolumeClaim{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: v.Name,
+ },
+ Spec: corev1.PersistentVolumeClaimSpec{
+ AccessModes: v.Spec.AccessModes,
+ Resources: v.Spec.Resources,
+ StorageClassName: storageClassName(v.Spec, viper.GetString(constant.CfgKeyDefaultStorageClass)),
+ VolumeMode: v.Spec.VolumeMode,
+ },
+ })
+ }
+ return pvcs
+}
diff --git a/pkg/controllerutil/workload_utils.go b/pkg/controllerutil/workload_utils.go
new file mode 100644
index 00000000000..c9f53868d1a
--- /dev/null
+++ b/pkg/controllerutil/workload_utils.go
@@ -0,0 +1,90 @@
+/*
+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 controllerutil
+
+import (
+ "context"
+ "fmt"
+ "maps"
+ "reflect"
+
+ corev1 "k8s.io/api/core/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ "github.com/apecloud/kubeblocks/pkg/constant"
+ "github.com/apecloud/kubeblocks/pkg/controller/multicluster"
+ "github.com/apecloud/kubeblocks/pkg/generics"
+ viper "github.com/apecloud/kubeblocks/pkg/viperx"
+)
+
+func ListOwnedPods(ctx context.Context, cli client.Reader, namespace, clusterName, compName string,
+ opts ...client.ListOption) ([]*corev1.Pod, error) {
+ return listPods(ctx, cli, namespace, clusterName, compName, nil, opts...)
+}
+
+func listPods(ctx context.Context, cli client.Reader, namespace, clusterName, compName string,
+ labels map[string]string, opts ...client.ListOption) ([]*corev1.Pod, error) {
+ if labels == nil {
+ labels = constant.GetCompLabels(clusterName, compName)
+ } else {
+ maps.Copy(labels, constant.GetCompLabels(clusterName, compName))
+ }
+ if opts == nil {
+ opts = make([]client.ListOption, 0)
+ }
+ opts = append(opts, inDataContext())
+ return listObjWithLabelsInNamespace(ctx, cli, generics.PodSignature, namespace, labels, opts...)
+}
+
+func listObjWithLabelsInNamespace[T generics.Object, PT generics.PObject[T], L generics.ObjList[T], PL generics.PObjList[T, L]](
+ ctx context.Context, cli client.Reader, _ func(T, PT, L, PL), namespace string, labels client.MatchingLabels, opts ...client.ListOption) ([]PT, error) {
+ if opts == nil {
+ opts = make([]client.ListOption, 0)
+ }
+ opts = append(opts, []client.ListOption{labels, client.InNamespace(namespace)}...)
+
+ var objList L
+ if err := cli.List(ctx, PL(&objList), opts...); err != nil {
+ return nil, err
+ }
+
+ objs := make([]PT, 0)
+ items := reflect.ValueOf(&objList).Elem().FieldByName("Items").Interface().([]T)
+ for i := range items {
+ objs = append(objs, &items[i])
+ }
+ return objs, nil
+}
+
+func inDataContext() *multicluster.ClientOption {
+ return multicluster.InDataContext()
+}
+
+func PodFQDN(namespace, compName, podName string) string {
+ return fmt.Sprintf("%s.%s-headless.%s.svc.%s", podName, compName, namespace, clusterDomain())
+}
+
+func ServiceFQDN(namespace, serviceName string) string {
+ return fmt.Sprintf("%s.%s.svc.%s", serviceName, namespace, clusterDomain())
+}
+
+func clusterDomain() string {
+ return viper.GetString(constant.KubernetesClusterDomainEnv)
+}
diff --git a/pkg/operations/switchover.go b/pkg/operations/switchover.go
index bb44784b613..aec7fa45b2c 100644
--- a/pkg/operations/switchover.go
+++ b/pkg/operations/switchover.go
@@ -38,7 +38,7 @@ import (
opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1"
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/component"
- "github.com/apecloud/kubeblocks/pkg/controller/component/lifecycle"
+ "github.com/apecloud/kubeblocks/pkg/controller/lifecycle"
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
)
@@ -257,7 +257,8 @@ func doSwitchover(ctx context.Context, cli client.Reader, synthesizedComp *compo
}
}
- lfa, err := lifecycle.New(synthesizedComp, pod, pods...)
+ lfa, err := lifecycle.New(synthesizedComp.Namespace, synthesizedComp.ClusterName, synthesizedComp.Name,
+ synthesizedComp.LifecycleActions, synthesizedComp.TemplateVars, pod, pods...)
if err != nil {
return err
}