github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/plugin/index.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 plugin 21 22 import ( 23 "fmt" 24 "io" 25 "os" 26 27 "github.com/pkg/errors" 28 "github.com/spf13/cobra" 29 "k8s.io/cli-runtime/pkg/genericiooptions" 30 "k8s.io/klog/v2" 31 cmdutil "k8s.io/kubectl/pkg/cmd/util" 32 "k8s.io/kubectl/pkg/util/templates" 33 34 "github.com/1aal/kubeblocks/pkg/cli/printer" 35 "github.com/1aal/kubeblocks/pkg/cli/util" 36 ) 37 38 var ( 39 pluginListIndexExample = templates.Examples(` 40 # List all configured plugin indexes 41 kbcli plugin index list 42 `) 43 44 pluginAddIndexExample = templates.Examples(` 45 # Add a new plugin index 46 kbcli plugin index add myIndex 47 `) 48 49 pluginDeleteIndexExample = templates.Examples(` 50 # Delete a plugin index 51 kbcli plugin index delete myIndex 52 `) 53 ) 54 55 func NewPluginIndexCmd(streams genericiooptions.IOStreams) *cobra.Command { 56 cmd := &cobra.Command{ 57 Use: "index", 58 Short: "Manage custom plugin indexes", 59 Long: "Manage which repositories are used to discover plugins and install plugins from", 60 } 61 62 cmd.AddCommand(NewPluginIndexListCmd(streams)) 63 cmd.AddCommand(NewPluginIndexAddCmd(streams)) 64 cmd.AddCommand(NewPluginIndexDeleteCmd(streams)) 65 cmd.AddCommand(NewPluginIndexUpdateCmd(streams)) 66 return cmd 67 } 68 69 type PluginIndexOptions struct { 70 IndexName string 71 URL string 72 73 genericiooptions.IOStreams 74 } 75 76 func (o *PluginIndexOptions) ListIndex() error { 77 indexes, err := ListIndexes(paths) 78 if err != nil { 79 return errors.Wrap(err, "failed to list indexes") 80 } 81 82 p := NewPluginIndexPrinter(o.IOStreams.Out) 83 for _, index := range indexes { 84 addPluginIndexRow(index.Name, index.URL, p) 85 } 86 p.Print() 87 88 return nil 89 } 90 91 func (o *PluginIndexOptions) AddIndex() error { 92 err := AddIndex(paths, o.IndexName, o.URL) 93 if err != nil { 94 return err 95 } 96 return nil 97 } 98 99 func (o *PluginIndexOptions) DeleteIndex() error { 100 err := DeleteIndex(paths, o.IndexName) 101 if err != nil { 102 return err 103 } 104 return nil 105 } 106 107 func (o *PluginIndexOptions) UpdateIndex() error { 108 indexes, err := ListIndexes(paths) 109 if err != nil { 110 return errors.Wrap(err, "failed to list indexes") 111 } 112 113 for _, idx := range indexes { 114 indexPath := paths.IndexPath(idx.Name) 115 klog.V(1).Infof("Updating the local copy of plugin index (%s)", indexPath) 116 if err := util.EnsureUpdated(idx.URL, indexPath); err != nil { 117 klog.Warningf("failed to update index %q: %v", idx.Name, err) 118 continue 119 } 120 121 fmt.Fprintf(o.Out, "Updated the local copy of plugin index %q\n", idx.Name) 122 } 123 124 return nil 125 } 126 127 func NewPluginIndexListCmd(streams genericiooptions.IOStreams) *cobra.Command { 128 o := &PluginIndexOptions{ 129 IOStreams: streams, 130 } 131 132 cmd := &cobra.Command{ 133 Use: "list", 134 Short: "List configured indexes", 135 Example: pluginListIndexExample, 136 Run: func(cmd *cobra.Command, args []string) { 137 cmdutil.CheckErr(o.ListIndex()) 138 }, 139 } 140 141 return cmd 142 } 143 144 func NewPluginIndexAddCmd(streams genericiooptions.IOStreams) *cobra.Command { 145 o := &PluginIndexOptions{ 146 IOStreams: streams, 147 } 148 149 cmd := &cobra.Command{ 150 Use: "add", 151 Short: "Add a new index", 152 Example: pluginAddIndexExample, 153 Args: cobra.ExactArgs(2), 154 Run: func(cmd *cobra.Command, args []string) { 155 o.IndexName = args[0] 156 o.URL = args[1] 157 cmdutil.CheckErr(o.AddIndex()) 158 }, 159 } 160 161 return cmd 162 } 163 164 func NewPluginIndexDeleteCmd(streams genericiooptions.IOStreams) *cobra.Command { 165 o := &PluginIndexOptions{ 166 IOStreams: streams, 167 } 168 169 cmd := &cobra.Command{ 170 Use: "delete", 171 Short: "Remove a configured index", 172 Example: pluginDeleteIndexExample, 173 Args: cobra.ExactArgs(1), 174 Run: func(cmd *cobra.Command, args []string) { 175 o.IndexName = args[0] 176 cmdutil.CheckErr(o.DeleteIndex()) 177 }, 178 } 179 180 return cmd 181 } 182 183 func NewPluginIndexUpdateCmd(streams genericiooptions.IOStreams) *cobra.Command { 184 o := &PluginIndexOptions{ 185 IOStreams: streams, 186 } 187 188 cmd := &cobra.Command{ 189 Use: "update", 190 Short: "update all configured indexes", 191 Run: func(cmd *cobra.Command, args []string) { 192 cmdutil.CheckErr(o.UpdateIndex()) 193 }, 194 } 195 196 return cmd 197 } 198 199 func NewPluginIndexPrinter(out io.Writer) *printer.TablePrinter { 200 t := printer.NewTablePrinter(out) 201 t.SetHeader("INDEX", "URL") 202 return t 203 } 204 205 func addPluginIndexRow(index, url string, p *printer.TablePrinter) { 206 p.AddRow(index, url) 207 } 208 209 // ListIndexes returns a slice of Index objects. The path argument is used as 210 // the base path of the index. 211 func ListIndexes(paths *Paths) ([]Index, error) { 212 entries, err := os.ReadDir(paths.IndexBase()) 213 if err != nil { 214 return nil, err 215 } 216 217 var indexes []Index 218 for _, e := range entries { 219 if !e.IsDir() { 220 continue 221 } 222 indexName := e.Name() 223 remote, err := util.GitGetRemoteURL(paths.IndexPath(indexName)) 224 if err != nil { 225 return nil, errors.Wrapf(err, "failed to list the remote URL for index %s", indexName) 226 } 227 228 indexes = append(indexes, Index{ 229 Name: indexName, 230 URL: remote, 231 }) 232 } 233 return indexes, nil 234 } 235 236 // AddIndex initializes a new index to install plugins from. 237 func AddIndex(paths *Paths, name, url string) error { 238 if name == "" { 239 return errors.New("index name must be specified") 240 } 241 dir := paths.IndexPath(name) 242 if _, err := os.Stat(dir); os.IsNotExist(err) { 243 return util.EnsureCloned(url, dir) 244 } else if err != nil { 245 return err 246 } 247 return fmt.Errorf("index %q already exists", name) 248 } 249 250 // DeleteIndex removes specified index name. If index does not exist, returns an error that can be tested by os.IsNotExist. 251 func DeleteIndex(paths *Paths, name string) error { 252 dir := paths.IndexPath(name) 253 if _, err := os.Stat(dir); err != nil { 254 return err 255 } 256 257 return os.RemoveAll(dir) 258 }