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 }