8000 chore: support switchover for sharding components by wangyelei · Pull Request #8786 · apecloud/kubeblocks · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
8000

chore: support switchover for sharding components #8786

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions apis/apps/v1alpha1/opsrequest_types.go

Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,6 @@ type SpecificOpsRequest struct {
// Lists Switchover objects, each specifying a Component to perform the switchover operation.
//
// +optional
// +patchMergeKey=componentName
// +patchStrategy=merge,retainKeys
// +listType=map
// +listMapKey=componentName
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="forbidden to update spec.switchover"
SwitchoverList []Switchover `json:"switchover,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"componentName"`

Expand Down Expand Up @@ -270,7 +266,7 @@ type SpecificOpsRequest struct {

// ComponentOps specifies the Component to be operated on.
type ComponentOps struct {
// Specifies the name of the Component.
// Specifies the name of the Component as defined in the cluster.spec
// +kubebuilder:validation:Required
ComponentName string `json:"componentName"`
}
Expand Down Expand Up @@ -329,9 +325,16 @@ type Instance struct {
TargetNodeName string `json:"targetNodeName,omitempty"`
}

// +kubebuilder:validation:XValidation:rule="(has(self.componentName) && !has(self.componentObjectName)) || (!has(self.componentName) && has(self.componentObjectName))",message="need to specified only componentName or componentObjectName"

type Switchover struct {
// Specifies the name of the Component.
ComponentOps `json:",inline"`
// Specifies the name of the Component as defined in the cluster.spec.
// +optional
ComponentName string `json:"componentName,omitempty"`

// Specifies the name of the Component object.
// +optional
ComponentObjectName string `json:"componentObjectName,omitempty"`

// Specifies the instance to become the primary or leader during a switchover operation.
//
Expand Down Expand Up @@ -1492,3 +1495,10 @@ func (p *ProgressStatusDetail) SetStatusAndMessage(status ProgressStatus, messag
p.Message = message
p.Status = status
}

func (s Switchover) GetComponentName() string {
if len(s.ComponentObjectName) > 0 {
return s.ComponentObjectName
}
return s.ComponentName
}
92 changes: 68 additions & 24 deletions apis/apps/v1alpha1/opsrequest_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,10 +640,14 @@ func (r *OpsRequest) validateSwitchover(ctx context.Context, cli client.Client,
if len(switchoverList) == 0 {
return notEmptyError("spec.switchover")
}
compOpsList := make([]ComponentOps, len(switchoverList))
for i, v := range switchoverList {
compOpsList[i] = v.ComponentOps

compOpsList := make([]ComponentOps, 0)
for _, v := range switchoverList {
if len(v.ComponentName) == 0 {
continue
}
compOpsList = append(compOpsList, ComponentOps{
ComponentName: v.ComponentName,
})
}
if err := r.checkComponentExistence(cluster, compOpsList); err != nil {
return err
Expand Down Expand Up @@ -962,6 +966,39 @@ func GetRunningOpsByOpsType(ctx context.Context, cli client.Client,
return runningOpsList, nil
}

func getClusterCompSpec(cluster *Cluster, componentName string) (*ClusterComponentSpec, error) {
compSpec := cluster.Spec.GetComponentByName(componentName)
if compSpec != nil {
return compSpec, nil
}
shardingSpec := cluster.Spec.GetShardingByName(componentName)
if shardingSpec != nil {
return &shardingSpec.Template, nil
}
return nil, fmt.Errorf(`component "%s" not found`, componentName)
}

func GetCompSpecBySwitchover(ctx context.Context, cli client.Client, cluster *Cluster, switchover Switchover) (*ClusterComponentSpec, error) {
if len(switchover.ComponentName) > 0 {
return getClusterCompSpec(cluster, switchover.ComponentName)
}
compObj := &Component{}
if err := cli.Get(ctx, client.ObjectKey{
Namespace: cluster.Namespace,
Name: switchover.ComponentObjectName,
}, compObj); err != nil {
if apierrors.IsNotFound(err) {
return nil, fmt.Errorf(`component object "%s" not found`, switchover.ComponentObjectName)
}
return nil, err
}
compName := compObj.Labels[constant.KBAppShardingNameLabelKey]
if len(compName) == 0 {
compName = compObj.Labels[constant.KBAppComponentLabelK 8000 ey]
}
return getClusterCompSpec(cluster, compName)
}

// validateSwitchoverResourceList checks if switchover resourceList is legal.
func validateSwitchoverResourceList(ctx context.Context, cli client.Client, cluster *Cluster, switchoverList []Switchover) error {
var (
Expand All @@ -972,27 +1009,39 @@ func validateSwitchoverResourceList(ctx context.Context, cli client.Client, clus
return notEmptyError("switchover.instanceName")
}

validatePodLegal := func(pod *corev1.Pod) error {
podLegalPrefix := fmt.Sprintf("%s-%s", cluster.Name, switchover.ComponentName)
if len(switchover.ComponentObjectName) > 0 {
podLegalPrefix = switchover.ComponentObjectName
}
if !strings.HasPrefix(pod.Name, podLegalPrefix) {
return fmt.Errorf("instanceName %s does not belong to the current component, please check the validity of the instance using \"kbcli cluster list-instances\"", switchover.InstanceName)
}
return nil
}

// TODO(xingran): this will be removed in the future.
validateBaseOnClusterCompDef := func(clusterCmpDef string) error {
// check clusterComponentDefinition whether support switchover
clusterCompDefObj, err := getClusterComponentDefByName(ctx, cli, *cluster, clusterCmpDef)
if err != nil {
return err
}
compName := switchover.GetComponentName()
if clusterCompDefObj == nil {
return fmt.Errorf("this cluster component %s is invalid", switchover.ComponentName)
return fmt.Errorf("this cluster component %s is invalid", compName)
}
if clusterCompDefObj.SwitchoverSpec == nil {
return fmt.Errorf("this cluster component %s does not support switchover", switchover.ComponentName)
return fmt.Errorf("this cluster component %s does not support switchover", compName)
}
switch switchover.InstanceName {
case KBSwitchoverCandidateInstanceForAnyPod:
if clusterCompDefObj.SwitchoverSpec.WithoutCandidate == nil {
return fmt.Errorf("this cluster component %s does not support promote without specifying an instance. Please specify a specific instance for the promotion", switchover.ComponentName)
return fmt.Errorf("this cluster component %s does not support promote without specifying an instance. Please specify a specific instance for the promotion", compName)
}
default:
if clusterCompDefObj.SwitchoverSpec.WithCandidate == nil {
return fmt.Errorf("this cluster component %s does not support specifying an instance for promote. If you want to perform a promote operation, please do not specify an instance", switchover.ComponentName)
return fmt.Errorf("this cluster component %s does not support specifying an instance for promote. If you want to perform a promote operation, please do not specify an instance", compName)
}
}
// check switchover.InstanceName whether exist and role label is correct
Expand All @@ -1010,10 +1059,7 @@ func validateSwitchoverResourceList(ctx context.Context, cli client.Client, clus
if v == constant.Primary || v == constant.Leader {
return fmt.Errorf("instanceName %s cannot be promoted because it is already the primary or leader instance", switchover.InstanceName)
}
if !strings.HasPrefix(pod.Name, fmt.Sprintf("%s-%s", cluster.Name, switchover.ComponentName)) {
return fmt.Errorf("instanceName %s does not belong to the current component, please check the validity of the instance using \"kbcli cluster list-instances\"", switchover.InstanceName)
}
return nil
return validatePodLegal(pod)
}

validateBaseOnCompDef := func(compDef string) error {
Expand All @@ -1036,20 +1082,21 @@ func validateSwitchoverResourceList(ctx context.Context, cli client.Client, clus
if err != nil {
return err
}
compName := switchover.GetComponentName()
if compDefObj == nil {
return fmt.Errorf("this component %s referenced componentDefinition is invalid", switchover.ComponentName)
return fmt.Errorf("this component %s referenced componentDefinition is invalid", compName)
}
if compDefObj.Spec.LifecycleActions == nil || compDefObj.Spec.LifecycleActions.Switchover == nil {
return fmt.Errorf("this cluster component %s does not support switchover", switchover.ComponentName)
return fmt.Errorf("this cluster component %s does not support switchover", compName)
}
switch switchover.InstanceName {
case KBSwitchoverCandidateInstanceForAnyPod:
if compDefObj.Spec.LifecycleActions.Switchover.WithoutCandidate == nil {
return fmt.Errorf("this cluster component %s does not support promote without specifying an instance. Please specify a specific instance for the promotion", switchover.ComponentName)
return fmt.Errorf("this cluster component %s does not support promote without specifying an instance. Please specify a specific instance for the promotion", compName)
}
default:
if compDefObj.Spec.LifecycleActions.Switchover.WithCandidate == nil {
return fmt.Errorf("this cluster component %s does not support specifying an instance for promote. If you want to perform a promote operation, please do not specify an instance", switchover.ComponentName)
return fmt.Errorf("this cluster component %s does not support specifying an instance for promote. If you want to perform a promote operation, please do not specify an instance", compName)
}
}
// check switchover.InstanceName whether exist and role label is correct
Expand All @@ -1074,20 +1121,17 @@ func validateSwitchoverResourceList(ctx context.Context, cli client.Client, clus
if v == targetRole {
return fmt.Errorf("instanceName %s cannot be promoted because it is already the primary or leader instance", switchover.InstanceName)
}
if !strings.HasPrefix(pod.Name, fmt.Sprintf("%s-%s", cluster.Name, switchover.ComponentName)) {
return fmt.Errorf("instanceName %s does not belong to the current component, please check the validity of the instance using \"kbcli cluster list-instances\"", switchover.InstanceName)
}
return nil
return validatePodLegal(pod)
}

compSpec := cluster.Spec.GetComponentByName(switchover.ComponentName)
if compSpec == nil {
return fmt.Errorf("component %s not found", switchover.ComponentName)
compSpec, err := GetCompSpecBySwitchover(ctx, cli, cluster, switchover)
if err != nil {
return err
}
if compSpec.ComponentDef != "" {
return validateBaseOnCompDef(compSpec.ComponentDef)
} else {
return validateBaseOnClusterCompDef(cluster.Spec.GetComponentDefRefName(switchover.ComponentName))
return validateBaseOnClusterCompDef(compSpec.ComponentDefRef)
}
}
return nil
Expand Down
40 changes: 20 additions & 20 deletions apis/apps/v1alpha1/opsrequest_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,24 +503,24 @@ var _ = Describe("OpsRequest webhook", func() {
testSwitchover := func(clusterDef *ClusterDefinition, cluster *Cluster) {
switchoverList := []Switchover{
{
ComponentOps: ComponentOps{ComponentName: "switchover-component-not-exist"},
InstanceName: "*",
ComponentName: "switchover-component-not-exist",
InstanceName: "*",
},
{
ComponentOps: ComponentOps{ComponentName: componentName},
InstanceName: "",
ComponentName: componentName,
InstanceName: "",
},
{
ComponentOps: ComponentOps{ComponentName: componentName},
InstanceName: "switchover-instance-name-not-exist",
ComponentName: componentName,
InstanceName: "switchover-instance-name-not-exist",
},
{
ComponentOps: ComponentOps{ComponentName: componentName},
InstanceName: "*",
ComponentName: componentName,
InstanceName: "*",
},
{
ComponentOps: ComponentOps{ComponentName: componentName},
InstanceName: fmt.Sprintf("%s-%s-0", cluster.Name, componentName),
ComponentName: componentName,
InstanceName: fmt.Sprintf("%s-%s-0", cluster.Name, componentName),
},
}

Expand Down Expand Up @@ -590,24 +590,24 @@ var _ = Describe("OpsRequest webhook", func() {
testSwitchoverWithCompDef := func(_ *ClusterDefinition, compDef *ComponentDefinition, cluster *Cluster) {
switchoverList := []Switchover{
{
ComponentOps: ComponentOps{ComponentName: "switchover-component-not-exist"},
InstanceName: "*",
ComponentName: "switchover-component-not-exist",
InstanceName: "*",
},
{
ComponentOps: ComponentOps{ComponentName: componentName},
InstanceName: "",
ComponentName: componentName,
InstanceName: "",
},
{
ComponentOps: ComponentOps{ComponentName: componentName},
InstanceName: "switchover-instance-name-not-exist",
ComponentName: componentName,
InstanceName: "switchover-instance-name-not-exist",
},
{
ComponentOps: ComponentOps{ComponentName: componentName},
InstanceName: "*",
ComponentName: componentName,
InstanceName: "*",
},
{
ComponentOps: ComponentOps{ComponentName: componentName},
InstanceName: fmt.Sprintf("%s-%s-0", cluster.Name, componentName),
ComponentName: componentName,
InstanceName: fmt.Sprintf("%s-%s-0", cluster.Name, componentName),
},
}

Expand Down
1 change: 0 additions & 1 deletion apis/apps/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading
0