Skip to content

Commit 4a3dbeb

Browse files
committed
chore: tidy up promote and rebuild instance cmd with new api (#539)
Co-authored-by: wangyelei <wangyelei@users.noreply.github.com> (cherry picked from commit 00acda0)
1 parent 8361d79 commit 4a3dbeb

File tree

7 files changed

+54
-65
lines changed

7 files changed

+54
-65
lines changed

docs/user_docs/cli/kbcli_cluster_promote.md

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,25 @@ title: kbcli cluster promote
55
Promote a non-primary or non-leader instance as the new primary or leader of the cluster
66

77
```
8-
kbcli cluster promote NAME [--component=<comp-name>] [--instance <instance-name>] [flags]
8+
kbcli cluster promote NAME [--instance <instance-name>] [flags]
99
```
1010

1111
### Examples
1212

1313
```
1414
# Promote the instance mycluster-mysql-1 as the new primary or leader.
15-
kbcli cluster promote mycluster --instance mycluster-mysql-1
16-
17-
# Promote a non-primary or non-leader instance as the new primary or leader, the new primary or leader is determined by the system.
18-
kbcli cluster promote mycluster
19-
20-
# If the cluster has multiple components, you need to specify a component, otherwise an error will be reported.
21-
kbcli cluster promote mycluster --component=mysql --instance mycluster-mysql-1
15+
kbcli cluster promote mycluster --candidate mycluster-mysql-1
2216
```
2317

2418
### Options
2519

2620
```
2721
--auto-approve Skip interactive approval before promote the instance
28-
--component string Specify the component name of the cluster, if the cluster has multiple components, you need to specify a component
22+
--candidate string Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running "kbcli cluster list-instances"
2923
--dry-run string[="unchanged"] Must be "client", or "server". If with client strategy, only print the object that would be sent, and no data is actually sent. If with server strategy, submit the server-side request, but no data is persistent. (default "none")
3024
--edit Edit the API resource before creating
3125
--force skip the pre-checks of the opsRequest to run the opsRequest forcibly
3226
-h, --help help for promote
33-
--instance string Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running "kbcli cluster list-instances"
3427
--name string OpsRequest name. if not specified, it will be randomly generated
3528
-o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml)
3629
--ttlSecondsAfterSucceed int Time to live after the OpsRequest succeed

docs/user_docs/cli/kbcli_cluster_rebuild-instance.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ kbcli cluster rebuild-instance NAME [flags]
3939
--node strings specified the target node which rebuilds the instance on the node otherwise will rebuild on a random node. format: insName1=nodeName,insName2=nodeName
4040
-o, --output format Prints the output in the specified format. Allowed values: JSON and YAML (default yaml)
4141
--restore-env stringArray provide the necessary env for the 'Restore' operation from the backup. format: key1=value, key2=value
42+
--source-backup-target string To rebuild a sharding component instance from a backup, you can specify the name of the source backup target
4243
--ttlSecondsAfterSucceed int Time to live after the OpsRequest succeed
4344
```
4445

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/Masterminds/semver/v3 v3.3.0
99
github.com/NimbleMarkets/ntcharts v0.1.2
1010
github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46
11-
github.com/apecloud/kubeblocks v1.0.0-beta.17
11+
github.com/apecloud/kubeblocks v1.0.0-beta.23
1212
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
1313
github.com/briandowns/spinner v1.23.0
1414
github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,8 +677,8 @@ github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4x
677677
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
678678
github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 h1:+Jcc7IjDGxPgIfIkGX2Q5Yxj35U65zgcfjh0B9rDhjo=
679679
github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46/go.mod h1:eksJtZ7z1nVcVLqDzAdcN5EfpHwXllIAvHZEks2zWys=
680-
github.com/apecloud/kubeblocks v1.0.0-beta.17 h1:taNHtwUWyCUBSHbPAx5sY5ltY0Dcf62cr+1HjxlK60w=
681-
github.com/apecloud/kubeblocks v1.0.0-beta.17/go.mod h1:bQ6uey/6S9gAuDkAJ7T89CdpmeXyxEFJpLw1hV2hANE=
680+
github.com/apecloud/kubeblocks v1.0.0-beta.23 h1:JrQBB9gJ/jtMD9wHv5js26rdfNQgeoU5+GcUEiaAmFU=
681+
github.com/apecloud/kubeblocks v1.0.0-beta.23/go.mod h1:b656nTyvHhwRwOuwNpOPG87Q0Lba3ygGRWoSOacPt5o=
682682
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
683683
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
684684
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=

pkg/action/template/cluster_operations_template.cue

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ options: {
3030
componentDefinitionName: string
3131
serviceVersion: string
3232
component: string
33+
componentObjectName: string
3334
instance: string
3435
componentNames: [...string]
3536
instanceTPLNames: [...string]
@@ -38,6 +39,7 @@ options: {
3839
componentName: string
3940
backupName?: string
4041
inPlace?: bool
42+
sourceBackupTargetName?: string
4143
instances: [
4244
...{
4345
name: string
@@ -283,13 +285,9 @@ content: {
283285
}
284286
if options.type == "Switchover" {
285287
switchover: [{
286-
componentName: options.component
287-
if options.instance == "" {
288-
instanceName: "*"
289-
}
290-
if options.instance != "" {
291-
instanceName: options.instance
292-
}
288+
componentObjectName: options.componentObjectName
289+
instanceName: options.instance
290+
candidateName: options.instance
293291
}]
294292
}
295293
if options.type == "RebuildInstance" {

pkg/cmd/cluster/operations.go

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,16 @@ type OperationsOptions struct {
108108
Services []opsv1alpha1.OpsService `json:"services,omitempty"`
109109

110110
// Switchover options
111-
Component string `json:"component"`
112-
Instance string `json:"instance"`
113-
BackupName string `json:"-"`
114-
Inplace bool `json:"-"`
115-
InstanceNames []string `json:"-"`
116-
Nodes []string `json:"-"`
117-
RebuildInstanceFrom []opsv1alpha1.RebuildInstance `json:"rebuildInstanceFrom,omitempty"`
118-
Env []string `json:"-"`
111+
Component string `json:"component"`
112+
ComponentObjectName string `json:"componentObjectName,omitempty"`
113+
Instance string `json:"instance"`
114+
BackupName string `json:"-"`
115+
Inplace bool `json:"-"`
116+
InstanceNames []string `json:"-"`
117+
Nodes []string `json:"-"`
118+
RebuildInstanceFrom []opsv1alpha1.RebuildInstance `json:"rebuildInstanceFrom,omitempty"`
119+
Env []string `json:"-"`
120+
SourceBackupTargetName string `json:"-"`
119121

120122
// Stop and Start options
121123
isComponentsFlagOptional bool
@@ -212,10 +214,11 @@ func (o *OperationsOptions) CompleteSwitchoverOps() error {
212214
}
213215

214216
if o.Component == "" {
215-
if len(clusterObj.Spec.ComponentSpecs) > 1 {
217+
if len(clusterObj.Spec.ComponentSpecs) == 1 {
218+
o.Component = clusterObj.Spec.ComponentSpecs[0].Name
219+
} else if len(clusterObj.Spec.ComponentSpecs) > 1 {
216220
return fmt.Errorf("there are multiple components in cluster, please use --component to specify the component for promote")
217221
}
218-
o.Component = clusterObj.Spec.ComponentSpecs[0].Name
219222
}
220223
return nil
221224
}
@@ -435,23 +438,19 @@ func (o *OperationsOptions) validatePromote(clusterObj *appsv1.Cluster) error {
435438
podObj = &corev1.Pod{}
436439
)
437440

438-
if o.Component == "" && o.Instance == "" {
439-
return fmt.Errorf("at least one of --component or --instance is required")
441+
if o.Instance == "" {
442+
return fmt.Errorf("--candidate is required")
440443
}
441444
// if the instance is not specified, do not need to check the validity of the instance
442-
if o.Instance != "" {
443-
// checks the validity of the instance whether it belongs to the current component and ensure it is not the primary or leader instance currently.
444-
podKey := client.ObjectKey{
445-
Namespace: clusterObj.Namespace,
446-
Name: o.Instance,
447-
}
448-
if err := util.GetResourceObjectFromGVR(types.PodGVR(), podKey, o.Dynamic, podObj); err != nil || podObj == nil {
449-
return fmt.Errorf("instance %s not found, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance)
450-
}
451-
if o.Component == "" {
452-
o.Component = cluster.GetPodComponentName(podObj)
453-
}
445+
// checks the validity of the instance whether it belongs to the current component and ensure it is not the primary or leader instance currently.
446+
podKey := client.ObjectKey{
447+
Namespace: clusterObj.Namespace,
448+
Name: o.Instance,
454449
}
450+
if err := util.GetResourceObjectFromGVR(types.PodGVR(), podKey, o.Dynamic, podObj); err != nil || podObj == nil {
451+
return fmt.Errorf("instance %s not found, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance)
452+
}
453+
o.ComponentObjectName = constant.GenerateClusterComponentName(clusterObj.Name, podObj.Labels[constant.KBAppComponentLabelKey])
455454

456455
getAndValidatePod := func(targetRoles ...string) error {
457456
// if the instance is not specified, do not need to check the validity of the instance
@@ -467,8 +466,8 @@ func (o *OperationsOptions) validatePromote(clusterObj *appsv1.Cluster) error {
467466
return fmt.Errorf("instanceName %s cannot be promoted because it is already the targetRole %s instance", o.Instance, targetRole)
468467
}
469468
}
470-
if cluster.GetPodComponentName(podObj) != o.Component || podObj.Labels[constant.AppInstanceLabelKey] != clusterObj.Name {
471-
return fmt.Errorf("instanceName %s does not belong to the current component, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance)
469+
if podObj.Labels[constant.AppInstanceLabelKey] != clusterObj.Name {
470+
return fmt.Errorf("instanceName %s does not belong to the current cluster, please check the validity of the instance using \"kbcli cluster list-instances\"", o.Instance)
472471
}
473472
return nil
474473
}
@@ -492,13 +491,13 @@ func (o *OperationsOptions) validatePromote(clusterObj *appsv1.Cluster) error {
492491
// check componentDefinition exist
493492
compDefKey := client.ObjectKey{
494493
Namespace: "",
495-
Name: cluster.GetComponentSpec(clusterObj, o.Component).ComponentDef,
494+
Name: cluster.GetComponentSpec(clusterObj, cluster.GetPodComponentName(podObj)).ComponentDef,
496495
}
497496
if err := util.GetResourceObjectFromGVR(types.CompDefGVR(), compDefKey, o.Dynamic, &compDefObj); err != nil {
498497
return err
499498
}
500499
if compDefObj.Spec.LifecycleActions == nil || compDefObj.Spec.LifecycleActions.Switchover == nil {
501-
return fmt.Errorf(`this component "%s does not support switchover, you can define the switchover action in the componentDef "%s"`, o.Component, compDefKey.Name)
500+
return fmt.Errorf(`this instance "%s does not support switchover, you can define the switchover action in the componentDef "%s"`, o.Instance, compDefKey.Name)
502501
}
503502
targetRole, err := getTargetRole(compDefObj.Spec.Roles)
504503
if err != nil {
@@ -988,20 +987,14 @@ func NewCancelCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.
988987

989988
var promoteExample = templates.Examples(`
990989
# Promote the instance mycluster-mysql-1 as the new primary or leader.
991-
kbcli cluster promote mycluster --instance mycluster-mysql-1
992-
993-
# Promote a non-primary or non-leader instance as the new primary or leader, the new primary or leader is determined by the system.
994-
kbcli cluster promote mycluster
995-
996-
# If the cluster has multiple components, you need to specify a component, otherwise an error will be reported.
997-
kbcli cluster promote mycluster --component=mysql --instance mycluster-mysql-1
990+
kbcli cluster promote mycluster --candidate mycluster-mysql-1
998991
`)
999992

1000993
// NewPromoteCmd creates a promote command
1001994
func NewPromoteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
1002995
o := newBaseOperationsOptions(f, streams, opsv1alpha1.SwitchoverType, false)
1003996
cmd := &cobra.Command{
1004-
Use: "promote NAME [--component=<comp-name>] [--instance <instance-name>]",
997+
Use: "promote NAME [--instance <instance-name>]",
1005998
Short: "Promote a non-primary or non-leader instance as the new primary or leader of the cluster",
1006999
Example: promoteExample,
10071000
ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.ClusterGVR()),
@@ -1015,9 +1008,9 @@ func NewPromoteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra
10151008
cmdutil.CheckErr(o.Run())
10161009
},
10171010
}
1018-
flags.AddComponentFlag(f, cmd, &o.Component, "Specify the component name of the cluster, if the cluster has multiple components, you need to specify a component")
1019-
cmd.Flags().StringVar(&o.Instance, "instance", "", "Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running \"kbcli cluster list-instances\"")
1011+
cmd.Flags().StringVar(&o.Instance, "candidate", "", "Specify the instance name as the new primary or leader of the cluster, you can get the instance name by running \"kbcli cluster list-instances\"")
10201012
cmd.Flags().BoolVar(&o.AutoApprove, "auto-approve", false, "Skip interactive approval before promote the instance")
1013+
_ = cmd.MarkFlagRequired("candidate")
10211014
o.addCommonFlags(cmd, f)
10221015
return cmd
10231016
}
@@ -1327,10 +1320,11 @@ func NewRebuildInstanceCmd(f cmdutil.Factory, streams genericiooptions.IOStreams
13271320
ComponentOps: opsv1alpha1.ComponentOps{
13281321
ComponentName: compName,
13291322
},
1330-
Instances: instances,
1331-
InPlace: o.Inplace,
1332-
BackupName: o.BackupName,
1333-
RestoreEnv: envVars,
1323+
Instances: instances,
1324+
InPlace: o.Inplace,
1325+
BackupName: o.BackupName,
1326+
RestoreEnv: envVars,
1327+
SourceBackupTargetName: o.SourceBackupTargetName,
13341328
},
13351329
}
13361330
return nil
@@ -1355,6 +1349,7 @@ func NewRebuildInstanceCmd(f cmdutil.Factory, streams genericiooptions.IOStreams
13551349
cmd.Flags().StringVar(&o.BackupName, "backup", "", "instances will be rebuild by the specified backup.")
13561350
cmd.Flags().StringSliceVar(&o.InstanceNames, "instances", nil, "instances which need to rebuild.")
13571351
util.CheckErr(flags.CompletedInstanceFlag(cmd, f, "instances"))
1352+
cmd.Flags().StringVar(&o.SourceBackupTargetName, "source-backup-target", "", "To rebuild a sharding component instance from a backup, you can specify the name of the source backup target")
13581353
cmd.Flags().StringSliceVar(&o.Nodes, "node", nil, `specified the target node which rebuilds the instance on the node otherwise will rebuild on a random node. format: insName1=nodeName,insName2=nodeName`)
13591354
cmd.Flags().StringArrayVar(&o.Env, "restore-env", []string{}, "provide the necessary env for the 'Restore' operation from the backup. format: key1=value, key2=value")
13601355
return cmd

pkg/cmd/cluster/operations_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,9 @@ var _ = Describe("operations", func() {
370370

371371
By("validate failed because o.Instance does not belong to the current component")
372372
o.Instance = fmt.Sprintf("%s-%s-%d", clusterName, testing.ComponentName, 1)
373+
o.Name = clusterName1
373374
Expect(o.Validate()).ShouldNot(Succeed())
374-
Expect(testing.ContainExpectStrings(o.Validate().Error(), "does not belong to the current component")).Should(BeTrue())
375+
Expect(testing.ContainExpectStrings(o.Validate().Error(), "does not belong to the current cluster")).Should(BeTrue())
375376
})
376377

377378
It("Switchover ops base on component definition", func() {
@@ -402,11 +403,12 @@ var _ = Describe("operations", func() {
402403
Expect(o.Validate()).ShouldNot(Succeed())
403404
Expect(testing.ContainExpectStrings(o.Validate().Error(), "cannot be promoted because it is already the targetRole")).Should(BeTrue())
404405

405-
By("validate failed because o.Instance does not belong to the current component")
406+
By("validate failed because o.Instance does not belong to the current cluster")
406407
o.Instance = fmt.Sprintf("%s-%s-%d", clusterName1, testing.ComponentName, 1)
407408
o.Component = testing.ComponentName
409+
o.Name = clusterName
408410
Expect(o.Validate()).ShouldNot(Succeed())
409-
Expect(testing.ContainExpectStrings(o.Validate().Error(), "does not belong to the current component")).Should(BeTrue())
411+
Expect(testing.ContainExpectStrings(o.Validate().Error(), "does not belong to the current cluster")).Should(BeTrue())
410412

411413
})
412414

0 commit comments

Comments
 (0)