diff --git a/docs/user_docs/cli/cli.md b/docs/user_docs/cli/cli.md index 2d36c8eab8c..6d2c377564e 100644 --- a/docs/user_docs/cli/cli.md +++ b/docs/user_docs/cli/cli.md @@ -120,6 +120,7 @@ Cluster command. ClusterDefinition command. +* [kbcli clusterdefinition describe](kbcli_clusterdefinition_describe.md) - Describe ClusterDefinition. * [kbcli clusterdefinition list](kbcli_clusterdefinition_list.md) - List ClusterDefinitions. * [kbcli clusterdefinition list-components](kbcli_clusterdefinition_list-components.md) - List cluster definition components. diff --git a/docs/user_docs/cli/kbcli_clusterdefinition.md b/docs/user_docs/cli/kbcli_clusterdefinition.md index 02f8872f266..12f63b314f7 100644 --- a/docs/user_docs/cli/kbcli_clusterdefinition.md +++ b/docs/user_docs/cli/kbcli_clusterdefinition.md @@ -37,6 +37,7 @@ ClusterDefinition command. ### SEE ALSO +* [kbcli clusterdefinition describe](kbcli_clusterdefinition_describe.md) - Describe ClusterDefinition. * [kbcli clusterdefinition list](kbcli_clusterdefinition_list.md) - List ClusterDefinitions. * [kbcli clusterdefinition list-components](kbcli_clusterdefinition_list-components.md) - List cluster definition components. diff --git a/docs/user_docs/cli/kbcli_clusterdefinition_describe.md b/docs/user_docs/cli/kbcli_clusterdefinition_describe.md new file mode 100644 index 00000000000..12da1191bd4 --- /dev/null +++ b/docs/user_docs/cli/kbcli_clusterdefinition_describe.md @@ -0,0 +1,53 @@ +--- +title: kbcli clusterdefinition describe +--- + +Describe ClusterDefinition. + +``` +kbcli clusterdefinition describe [flags] +``` + +### Examples + +``` + # describe a specified cluster definition + kbcli clusterdefinition describe myclusterdef +``` + +### Options + +``` + -h, --help help for describe +``` + +### Options inherited from parent commands + +``` + --as string Username to impersonate for the operation. User could be a regular user or a service account in a namespace. + --as-group stringArray Group to impersonate for the operation, this flag can be repeated to specify multiple groups. + --as-uid string UID to impersonate for the operation. + --cache-dir string Default cache directory (default "$HOME/.kube/cache") + --certificate-authority string Path to a cert file for the certificate authority + --client-certificate string Path to a client certificate file for TLS + --client-key string Path to a client key file for TLS + --cluster string The name of the kubeconfig cluster to use + --context string The name of the kubeconfig context to use + --disable-compression If true, opt-out of response compression for all requests to the server + --insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure + --kubeconfig string Path to the kubeconfig file to use for CLI requests. + --match-server-version Require server version to match client version + -n, --namespace string If present, the namespace scope for this CLI request + --request-timeout string The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests. (default "0") + -s, --server string The address and port of the Kubernetes API server + --tls-server-name string Server name to use for server certificate validation. If it is not provided, the hostname used to contact the server is used + --token string Bearer token for authentication to the API server + --user string The name of the kubeconfig user to use +``` + +### SEE ALSO + +* [kbcli clusterdefinition](kbcli_clusterdefinition.md) - ClusterDefinition command. + +#### Go Back to [CLI Overview](cli.md) Homepage. + diff --git a/internal/cli/cmd/clusterdefinition/clusterdefinition.go b/internal/cli/cmd/clusterdefinition/clusterdefinition.go index 78449a8a916..8fad035f4e0 100644 --- a/internal/cli/cmd/clusterdefinition/clusterdefinition.go +++ b/internal/cli/cmd/clusterdefinition/clusterdefinition.go @@ -43,6 +43,7 @@ func NewClusterDefinitionCmd(f cmdutil.Factory, streams genericclioptions.IOStre cmd.AddCommand(NewListCmd(f, streams)) cmd.AddCommand(NewListComponentsCmd(f, streams)) + cmd.AddCommand(NewDescribeCmd(f, streams)) return cmd } diff --git a/internal/cli/cmd/clusterdefinition/describe.go b/internal/cli/cmd/clusterdefinition/describe.go new file mode 100644 index 00000000000..9367f0e562f --- /dev/null +++ b/internal/cli/cmd/clusterdefinition/describe.go @@ -0,0 +1,179 @@ +/* +Copyright (C) 2022-2023 ApeCloud Co., Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package clusterdefinition + +import ( + "context" + "fmt" + "io" + + "github.com/spf13/cobra" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/dynamic" + clientset "k8s.io/client-go/kubernetes" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/util/templates" + + "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" + "github.com/apecloud/kubeblocks/internal/cli/printer" + "github.com/apecloud/kubeblocks/internal/cli/types" + "github.com/apecloud/kubeblocks/internal/cli/util" + "github.com/apecloud/kubeblocks/internal/constant" + dptypes "github.com/apecloud/kubeblocks/internal/dataprotection/types" + "github.com/apecloud/kubeblocks/internal/dataprotection/utils/boolptr" +) + +var ( + describeExample = templates.Examples(` + # describe a specified cluster definition + kbcli clusterdefinition describe myclusterdef`) +) + +type describeOptions struct { + factory cmdutil.Factory + client clientset.Interface + dynamic dynamic.Interface + namespace string + + names []string + genericclioptions.IOStreams +} + +func NewDescribeCmd(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { + o := &describeOptions{ + factory: f, + IOStreams: streams, + } + cmd := &cobra.Command{ + Use: "describe", + Short: "Describe ClusterDefinition.", + Example: describeExample, + Aliases: []string{"desc"}, + ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.ClusterDefGVR()), + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(o.complete(args)) + cmdutil.CheckErr(o.run()) + }, + } + return cmd +} + +func (o *describeOptions) complete(args []string) error { + var err error + + if len(args) == 0 { + return fmt.Errorf("cluster definition name should be specified") + } + o.names = args + + if o.client, err = o.factory.KubernetesClientSet(); err != nil { + return err + } + + if o.dynamic, err = o.factory.DynamicClient(); err != nil { + return err + } + + if o.namespace, _, err = o.factory.ToRawKubeConfigLoader().Namespace(); err != nil { + return err + } + + return nil +} + +func (o *describeOptions) run() error { + for _, name := range o.names { + if err := o.describeClusterDef(name); err != nil { + return err + } + } + return nil +} + +func (o *describeOptions) describeClusterDef(name string) error { + // get cluster definition + clusterDefObject, err := o.dynamic.Resource(types.ClusterDefGVR()).Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return err + } + clusterDef := v1alpha1.ClusterDefinition{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(clusterDefObject.Object, &clusterDef); err != nil { + return err + } + + // get backup policy templates of the cluster definition + opts := metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", constant.ClusterDefLabelKey, name), + } + backupTemplatesListObj, err := o.dynamic.Resource(types.BackupPolicyTemplateGVR()).List(context.TODO(), opts) + if err != nil { + return err + } + var backupPolicyTemplates []*v1alpha1.BackupPolicyTemplate + for _, item := range backupTemplatesListObj.Items { + backupTemplate := v1alpha1.BackupPolicyTemplate{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.Object, &backupTemplate); err != nil { + return err + } + backupPolicyTemplates = append(backupPolicyTemplates, &backupTemplate) + } + + showClusterDef(&clusterDef, o.Out) + + showBackupConfig(backupPolicyTemplates, o.Out) + + return nil +} + +func showClusterDef(cd *v1alpha1.ClusterDefinition, out io.Writer) { + if cd == nil { + return + } + fmt.Fprintf(out, "Name: %s\t Type: %s\n\n", cd.Name, cd.Spec.Type) +} + +func showBackupConfig(backupPolicyTemplates []*v1alpha1.BackupPolicyTemplate, out io.Writer) { + if len(backupPolicyTemplates) == 0 { + return + } + fmt.Fprintf(out, "Backup Config:\n") + tbl := printer.NewTablePrinter(out) + defaultBackupPolicyTemplate := &v1alpha1.BackupPolicyTemplate{} + // if there is only one backup policy template, it will be the default backup policy template + if len(backupPolicyTemplates) == 1 { + defaultBackupPolicyTemplate = backupPolicyTemplates[0] + } + for _, item := range backupPolicyTemplates { + if item.Annotations[dptypes.DefaultBackupPolicyTemplateAnnotationKey] == "true" { + defaultBackupPolicyTemplate = item + break + } + } + tbl.SetHeader("BACKUP-METHOD", "ACTION-SET", "SNAPSHOT-VOLUME") + for _, policy := range defaultBackupPolicyTemplate.Spec.BackupPolicies { + for _, method := range policy.BackupMethods { + snapshotVolume := "false" + if boolptr.IsSetToTrue(method.SnapshotVolumes) { + snapshotVolume = "true" + } + tbl.AddRow(method.Name, method.ActionSetName, snapshotVolume) + } + } + tbl.Print() +} diff --git a/internal/cli/types/types.go b/internal/cli/types/types.go index d392d9702b9..444e275b3b1 100644 --- a/internal/cli/types/types.go +++ b/internal/cli/types/types.go @@ -140,6 +140,7 @@ const ( ResourceBackupPolicies = "backuppolicies" ResourceBackupRepos = "backuprepos" ResourceBackupSchedules = "backupschedules" + ResourceBackupTemplates = "backuppolicytemplates" ) // Extensions API group @@ -252,6 +253,10 @@ func BackupPolicyGVR() schema.GroupVersionResource { return schema.GroupVersionResource{Group: DPAPIGroup, Version: DPAPIVersion, Resource: ResourceBackupPolicies} } +func BackupPolicyTemplateGVR() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: AppsAPIGroup, Version: DPAPIVersion, Resource: ResourceBackupTemplates} +} + func BackupScheduleGVR() schema.GroupVersionResource { return schema.GroupVersionResource{Group: DPAPIGroup, Version: DPAPIVersion, Resource: ResourceBackupSchedules} }