Skip to content
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

chore: support rebuild-instance cmd and update the customOps cmd #295

Merged
merged 4 commits into from
Mar 27, 2024
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
1 change: 1 addition & 0 deletions docs/user_docs/cli/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Cluster command.
* [kbcli cluster list-ops](kbcli_cluster_list-ops.md) - List all opsRequests.
* [kbcli cluster logs](kbcli_cluster_logs.md) - Access cluster log file.
* [kbcli cluster promote](kbcli_cluster_promote.md) - Promote a non-primary or non-leader instance as the new primary or leader of the cluster
* [kbcli cluster rebuild-instance](kbcli_cluster_rebuild-instance.md) - Rebuild the specified instances in the cluster.
* [kbcli cluster register](kbcli_cluster_register.md) - Pull the cluster chart to the local cache and register the type to 'create' sub-command
* [kbcli cluster restart](kbcli_cluster_restart.md) - Restart the specified components in the cluster.
* [kbcli cluster restore](kbcli_cluster_restore.md) - Restore a new cluster from backup.
Expand Down
1 change: 1 addition & 0 deletions docs/user_docs/cli/kbcli_cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Cluster command.
* [kbcli cluster list-ops](kbcli_cluster_list-ops.md) - List all opsRequests.
* [kbcli cluster logs](kbcli_cluster_logs.md) - Access cluster log file.
* [kbcli cluster promote](kbcli_cluster_promote.md) - Promote a non-primary or non-leader instance as the new primary or leader of the cluster
* [kbcli cluster rebuild-instance](kbcli_cluster_rebuild-instance.md) - Rebuild the specified instances in the cluster.
* [kbcli cluster register](kbcli_cluster_register.md) - Pull the cluster chart to the local cache and register the type to 'create' sub-command
* [kbcli cluster restart](kbcli_cluster_restart.md) - Restart the specified components in the cluster.
* [kbcli cluster restore](kbcli_cluster_restore.md) - Restore a new cluster from backup.
Expand Down
8 changes: 4 additions & 4 deletions pkg/action/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@ type CreateOptions struct {

func (o *CreateOptions) Complete() error {
var err error
if o.Namespace, _, err = o.Factory.ToRawKubeConfigLoader().Namespace(); err != nil {
return err
if o.Namespace == "" {
if o.Namespace, _, err = o.Factory.ToRawKubeConfigLoader().Namespace(); err != nil {
return err
}
}

// now we use the first argument as the resource name
if len(o.Args) > 0 {
o.Name = o.Args[0]
Expand Down Expand Up @@ -249,7 +250,6 @@ func (o *CreateOptions) buildResourceObj() (*unstructured.Unstructured, error) {
if optionsByte, err = json.Marshal(m); err != nil {
return nil, err
}

if cueValue, err = newCueValue(o.CueTemplateName); err != nil {
return nil, err
}
Expand Down
29 changes: 25 additions & 4 deletions pkg/action/template/cluster_operations_template.cue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ options: {
component: string
instance: string
componentNames: [...string]
rebuildInstanceFrom: [
...{
componentName: string
backupName?: string
instanceNames: [...string]
envForRestore?: [
...{
name: string
value: string
},
]
},
]
cpu: string
memory: string
class: string
Expand All @@ -50,7 +63,8 @@ options: {
]
params: [
...{
[string]: {string | null}
name: string
value: string
},
]
...
Expand Down Expand Up @@ -213,11 +227,18 @@ content: {
}
}]
}
if options.type == "RebuildInstance" {
rebuildFrom: options.rebuildInstanceFrom
}
if options.type == "Custom" {
customSpec: {
componentName: options.component
opsDefinitionRef: options.opsDefinitionName
params: options.params
opsDefinitionRef: options.opsDefinitionName
components: [
{
name: options.component
parameters: options.params
}
]
}
}
}
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func NewClusterCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra
NewStopCmd(f, streams),
NewStartCmd(f, streams),
NewRestartCmd(f, streams),
NewRebuildInstanceCmd(f, streams),
NewUpgradeCmd(f, streams),
NewVolumeExpansionCmd(f, streams),
NewVerticalScalingCmd(f, streams),
Expand Down
137 changes: 122 additions & 15 deletions pkg/cmd/cluster/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,16 @@ type OperationsOptions struct {
Services []appsv1alpha1.OpsService `json:"services,omitempty"`

// Switchover options
Component string `json:"component"`
Instance string `json:"instance"`
Primary string `json:"-"`
CharacterType string `json:"-"`
LorryHAEnabled bool `json:"-"`
ExecPod *corev1.Pod `json:"-"`
Component string `json:"component"`
Instance string `json:"instance"`
Primary string `json:"-"`
CharacterType string `json:"-"`
LorryHAEnabled bool `json:"-"`
ExecPod *corev1.Pod `json:"-"`
BackupName string `json:"-"`
InstanceNames []string `json:"-"`
RebuildInstanceFrom []appsv1alpha1.RebuildInstance `json:"rebuildInstanceFrom,omitempty"`
Env []string `json:"-"`
}

func newBaseOperationsOptions(f cmdutil.Factory, streams genericiooptions.IOStreams,
Expand Down Expand Up @@ -1020,7 +1024,16 @@ func NewPromoteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra
customOpr.OpsType = "Custom"
customOpr.OpsTypeLower = strings.ToLower(string(o.OpsType))
customOpr.OpsDefinitionName = "switchover"
customOpr.Params = []map[string]string{{"primary": o.Primary, "candidate": o.Instance}}
customOpr.Params = []appsv1alpha1.Parameter{
{
Name: "primary",
Value: o.Primary,
},
{
Name: "candidate",
Value: o.Instance,
},
}
customOpr.CreateOptions.Options = customOpr
cmdutil.CheckErr(customOpr.Run())
} else {
Expand Down Expand Up @@ -1051,8 +1064,8 @@ var customOpsExample = templates.Examples(`

type CustomOperations struct {
*OperationsOptions
OpsDefinitionName string `json:"opsDefinitionName"`
Params []map[string]string `json:"params"`
OpsDefinitionName string `json:"opsDefinitionName"`
Params []appsv1alpha1.Parameter `json:"params"`
SchemaProperties *apiextensionsv1.JSONSchemaProps
}

Expand Down Expand Up @@ -1093,8 +1106,10 @@ func NewCustomOpsCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cob

func (o *CustomOperations) init() error {
var err error
if o.Namespace, _, err = o.Factory.ToRawKubeConfigLoader().Namespace(); err != nil {
return err
if o.Namespace == "" {
if o.Namespace, _, err = o.Factory.ToRawKubeConfigLoader().Namespace(); err != nil {
return err
}
}
if o.Dynamic, err = o.Factory.DynamicClient(); err != nil {
return err
Expand Down Expand Up @@ -1175,25 +1190,117 @@ func (o *CustomOperations) parseOpsDefinitionAndParams(cmd *cobra.Command, args
}

func (o *CustomOperations) completeCustomSpec(cmd *cobra.Command) error {
param := map[string]string{}
var (
params []appsv1alpha1.Parameter
paramMap = map[string]string{}
)
// Construct config and credential map from flags
if o.SchemaProperties != nil {
fromFlags := flags.FlagsToValues(cmd.LocalNonPersistentFlags(), true)
for name := range o.SchemaProperties.Properties {
flagName := strcase.KebabCase(name)
if val, ok := fromFlags[flagName]; ok {
param[name] = val.String()
params = append(params, appsv1alpha1.Parameter{
Name: name,
Value: val.String(),
})
paramMap[name] = val.String()
}
}
// validate if flags values are legal.
data, err := common.CoverStringToInterfaceBySchemaType(o.SchemaProperties, param)
data, err := common.CoverStringToInterfaceBySchemaType(o.SchemaProperties, paramMap)
if err != nil {
return err
}
if err = common.ValidateDataWithSchema(o.SchemaProperties, data); err != nil {
return err
}
}
o.Params = []map[string]string{param}
o.Params = params
return nil
}

var rebuildExample = templates.Examples(`
# rebuild instance without backup
kbcli cluster rebuild-instance mycluster --instances pod1,pod2

# rebuild instance from backup
kbcli cluster rebuild-instance mycluster --instances pod1,pod2 --backupName <backup>
`)

// NewRebuildInstanceCmd creates a rebuildInstance command
func NewRebuildInstanceCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
o := newBaseOperationsOptions(f, streams, appsv1alpha1.RebuildInstanceType, false)
completedRebuildOps := func() error {
if o.Name == "" {
return makeMissingClusterNameErr()
}
if len(o.InstanceNames) == 0 {
return fmt.Errorf("instances can not be empty")
}
_, err := cluster.GetClusterByName(o.Dynamic, o.Name, o.Namespace)
if err != nil {
return err
}
var compName string
for _, podName := range o.InstanceNames {
pod := &corev1.Pod{}
if err = util.GetK8SClientObject(o.Dynamic, pod, types.PodGVR(), o.Namespace, podName); err != nil {
return err
}
clusterName := pod.Labels[constant.AppInstanceLabelKey]
if clusterName != o.Name {
return fmt.Errorf(`the instance "%s" not belongs the cluster "%s"`, podName, o.Name)
}
insCompName := pod.Labels[constant.KBAppComponentLabelKey]
if compName != "" && compName != insCompName {
return fmt.Errorf("these instances do not belong to the same component")
}
compName = insCompName
}
var envVars []corev1.EnvVar
for _, v := range o.Env {
for _, envVar := range strings.Split(v, ",") {
kv := strings.Split(envVar, "=")
if len(kv) != 2 {
return fmt.Errorf("unknown format for env: %s", envVar)
}
envVars = append(envVars, corev1.EnvVar{
Name: kv[0],
Value: kv[1],
})
}
}
o.RebuildInstanceFrom = []appsv1alpha1.RebuildInstance{
{
ComponentOps: appsv1alpha1.ComponentOps{
ComponentName: compName,
},
InstanceNames: o.InstanceNames,
BackupName: o.BackupName,
EnvForRestore: envVars,
},
}
return nil
}
cmd := &cobra.Command{
Use: "rebuild-instance NAME",
Short: "Rebuild the specified instances in the cluster.",
Example: restartExample,
ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.ClusterGVR()),
Run: func(cmd *cobra.Command, args []string) {
o.Args = args
cmdutil.BehaviorOnFatal(printer.FatalWithRedColor)
cmdutil.CheckErr(o.Complete())
cmdutil.CheckErr(completedRebuildOps())
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
o.addCommonFlags(cmd, f)
cmd.Flags().BoolVar(&o.AutoApprove, "auto-approve", false, "Skip interactive approval before rebuilding the instances.gi")
cmd.Flags().StringVar(&o.BackupName, "backup", "", "instances will be rebuild by the specified backup.")
cmd.Flags().StringSliceVar(&o.InstanceNames, "instances", nil, "instances which need to rebuild.")
cmd.Flags().StringArrayVar(&o.Env, "env", []string{}, "provide the necessary env for the 'Restore' operation from the backup. format: key1=value, key2=value")
return cmd
}
Loading