github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/backuprepo/describe.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package backuprepo
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  
    26  	"github.com/dustin/go-humanize"
    27  	"github.com/spf13/cobra"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	"k8s.io/cli-runtime/pkg/genericiooptions"
    32  	"k8s.io/client-go/dynamic"
    33  	clientset "k8s.io/client-go/kubernetes"
    34  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    35  	"k8s.io/kubectl/pkg/util/templates"
    36  
    37  	dpv1alpha1 "github.com/1aal/kubeblocks/apis/dataprotection/v1alpha1"
    38  	"github.com/1aal/kubeblocks/pkg/cli/printer"
    39  	"github.com/1aal/kubeblocks/pkg/cli/types"
    40  	"github.com/1aal/kubeblocks/pkg/cli/util"
    41  )
    42  
    43  var (
    44  	describeExample = templates.Examples(`
    45  	# Describe a backuprepo
    46  	kbcli backuprepo describe my-backuprepo 
    47  	`)
    48  )
    49  
    50  type describeBackupRepoOptions struct {
    51  	factory   cmdutil.Factory
    52  	client    clientset.Interface
    53  	dynamic   dynamic.Interface
    54  	namespace string
    55  
    56  	// resource type and names
    57  	gvr   schema.GroupVersionResource
    58  	names []string
    59  
    60  	genericiooptions.IOStreams
    61  }
    62  
    63  func newDescribeCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
    64  	o := &describeBackupRepoOptions{
    65  		factory:   f,
    66  		IOStreams: streams,
    67  		gvr:       types.BackupRepoGVR(),
    68  	}
    69  	cmd := &cobra.Command{
    70  		Use:               "describe",
    71  		Short:             "Describe a backup repository.",
    72  		Example:           describeExample,
    73  		ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.BackupRepoGVR()),
    74  		Run: func(cmd *cobra.Command, args []string) {
    75  			cmdutil.BehaviorOnFatal(printer.FatalWithRedColor)
    76  			cmdutil.CheckErr(o.complete(args))
    77  			cmdutil.CheckErr(o.run())
    78  		},
    79  	}
    80  	return cmd
    81  }
    82  
    83  func (o *describeBackupRepoOptions) complete(args []string) error {
    84  	var err error
    85  
    86  	if len(args) == 0 {
    87  		return fmt.Errorf("must specify a backuprepo name")
    88  	}
    89  
    90  	o.names = args
    91  	if o.client, err = o.factory.KubernetesClientSet(); err != nil {
    92  		return err
    93  	}
    94  	if o.dynamic, err = o.factory.DynamicClient(); err != nil {
    95  		return err
    96  	}
    97  	if o.namespace, _, err = o.factory.ToRawKubeConfigLoader().Namespace(); err != nil {
    98  		return err
    99  	}
   100  	return nil
   101  }
   102  
   103  func (o *describeBackupRepoOptions) run() error {
   104  	var backupRepoNameMap = make(map[string]bool)
   105  	for _, name := range o.names {
   106  		backupRepoNameMap[name] = true
   107  	}
   108  
   109  	for _, name := range o.names {
   110  		backupRepoObj, err := o.dynamic.Resource(types.BackupRepoGVR()).Get(context.TODO(), name, metav1.GetOptions{})
   111  		if err != nil {
   112  			return err
   113  		}
   114  		backupRepo := &dpv1alpha1.BackupRepo{}
   115  		if err = runtime.DefaultUnstructuredConverter.FromUnstructured(backupRepoObj.Object, backupRepo); err != nil {
   116  			return err
   117  		}
   118  		if err = o.printBackupRepo(backupRepo); err != nil {
   119  			return err
   120  		}
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  func (o *describeBackupRepoOptions) printBackupRepo(backupRepo *dpv1alpha1.BackupRepo) error {
   127  	printer.PrintLine("Summary:")
   128  	printer.PrintPairStringToLine("Name", backupRepo.Name)
   129  	printer.PrintPairStringToLine("Provider", backupRepo.Spec.StorageProviderRef)
   130  	backups, backupSize, err := countBackupNumsAndSize(o.dynamic, backupRepo)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	printer.PrintPairStringToLine("Backups", fmt.Sprintf("%d", backups))
   135  	printer.PrintPairStringToLine("Total Data Size", backupSize)
   136  
   137  	printer.PrintLine("\nSpec:")
   138  	printer.PrintPairStringToLine("AccessMethod", string(backupRepo.Spec.AccessMethod))
   139  	printer.PrintPairStringToLine("PvReclaimPolicy", string(backupRepo.Spec.PVReclaimPolicy))
   140  	printer.PrintPairStringToLine("StorageProviderRef", backupRepo.Spec.StorageProviderRef)
   141  	printer.PrintPairStringToLine("VolumeCapacity", backupRepo.Spec.VolumeCapacity.String())
   142  	printer.PrintLine("  Config:")
   143  	for k, v := range backupRepo.Spec.Config {
   144  		printer.PrintPairStringToLine(k, v, 4)
   145  	}
   146  
   147  	printer.PrintLine("\nStatus:")
   148  	printer.PrintPairStringToLine("Phase", string(backupRepo.Status.Phase))
   149  	printer.PrintPairStringToLine("BackupPVCName", backupRepo.Status.BackupPVCName)
   150  	printer.PrintPairStringToLine("ObservedGeneration", fmt.Sprintf("%d", backupRepo.Status.ObservedGeneration))
   151  
   152  	return nil
   153  }
   154  
   155  func countBackupNumsAndSize(dynamic dynamic.Interface, backupRepo *dpv1alpha1.BackupRepo) (int, string, error) {
   156  	var size uint64
   157  	count := 0
   158  
   159  	backupList, err := dynamic.Resource(types.BackupGVR()).List(context.TODO(), metav1.ListOptions{
   160  		LabelSelector: fmt.Sprintf("dataprotection.kubeblocks.io/backup-repo-name=%s", backupRepo.Name),
   161  	})
   162  	if err != nil {
   163  		return count, humanize.Bytes(size), err
   164  	}
   165  	count = len(backupList.Items)
   166  
   167  	for _, obj := range backupList.Items {
   168  		backup := &dpv1alpha1.Backup{}
   169  		if err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, backup); err != nil {
   170  			return count, humanize.Bytes(size), err
   171  		}
   172  		// if backup doesn't complete, we don't count it's size
   173  		if backup.Status.Phase != dpv1alpha1.BackupPhaseCompleted {
   174  			continue
   175  		}
   176  		backupSize, err := humanize.ParseBytes(backup.Status.TotalSize)
   177  		if err != nil {
   178  			return count, humanize.Bytes(size), fmt.Errorf("failed to parse the %s of totalSize, %s, %s", backup.Name, backup.Status.TotalSize, err)
   179  		}
   180  		size += backupSize
   181  	}
   182  	return count, humanize.Bytes(size), nil
   183  }