From abdc90cdb5f3cabf70c832c35be27fba286cfa0a Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Tue, 26 Nov 2019 18:52:32 -0800 Subject: [PATCH] Add support for auto-complete in 'mc admin config' commands --- cmd/{complete.go => auto-complete.go} | 159 +++++++++++++++++++------- 1 file changed, 117 insertions(+), 42 deletions(-) rename cmd/{complete.go => auto-complete.go} (63%) diff --git a/cmd/complete.go b/cmd/auto-complete.go similarity index 63% rename from cmd/complete.go rename to cmd/auto-complete.go index 043d644344..b0e5e459c9 100644 --- a/cmd/complete.go +++ b/cmd/auto-complete.go @@ -33,6 +33,28 @@ func (fs fsComplete) Predict(a complete.Args) (prediction []string) { return complete.PredictFiles("*").Predict(a) } +func completeAdminConfigKeys(aliasPath string, keyPrefix string) (prediction []string) { + // Convert alias/bucket/incompl to alias/bucket/ to list its contents + parentDirPath := filepath.Dir(aliasPath) + "/" + clnt, err := newAdminClient(parentDirPath) + if err != nil { + return nil + } + + h, e := clnt.HelpConfigKV("", "", false) + if e != nil { + return nil + } + + for _, hkv := range h.KeysHelp { + if strings.HasPrefix(hkv.Key, keyPrefix) { + prediction = append(prediction, hkv.Key) + } + } + + return prediction +} + // Complete S3 path. If the prediction result is only one directory, // then recursively scans it. This is needed to satisfy posener/complete // (look at posener/complete.PredictFiles) @@ -51,14 +73,14 @@ func completeS3Path(s3Path string) (prediction []string) { // List dirPath content and only pick elements that corresponds // to the path that we want to complete for content := range clnt.List(false, false, false, DirFirst) { - completeS3Path := alias + getKey(content) + cmplS3Path := alias + getKey(content) if content.Type.IsDir() { - if !strings.HasSuffix(completeS3Path, "/") { - completeS3Path += "/" + if !strings.HasSuffix(cmplS3Path, "/") { + cmplS3Path += "/" } } - if strings.HasPrefix(completeS3Path, s3Path) { - prediction = append(prediction, completeS3Path) + if strings.HasPrefix(cmplS3Path, s3Path) { + prediction = append(prediction, cmplS3Path) } } @@ -70,6 +92,43 @@ func completeS3Path(s3Path string) (prediction []string) { return } +type adminConfigComplete struct{} + +func (adm adminConfigComplete) Predict(a complete.Args) (prediction []string) { + defer func() { + sort.Strings(prediction) + }() + + loadMcConfig = loadMcConfigFactory() + conf, err := loadMcConfig() + if err != nil { + return + } + + // We have already predicted the keys, we are done. + if len(a.Completed) == 3 { + return + } + + arg := a.Last + lastArg := a.LastCompleted + if _, ok := conf.Hosts[filepath.Clean(a.LastCompleted)]; !ok { + if strings.IndexByte(arg, '/') == -1 { + // Only predict alias since '/' is not found + for alias := range conf.Hosts { + if strings.HasPrefix(alias, arg) { + prediction = append(prediction, alias+"/") + } + } + } else { + prediction = completeAdminConfigKeys(arg, "") + } + } else { + prediction = completeAdminConfigKeys(lastArg, arg) + } + return +} + // s3Complete knows how to complete an mc s3 path type s3Complete struct { deepLevel int @@ -136,6 +195,7 @@ func (al aliasComplete) Predict(a complete.Args) (prediction []string) { return } +var adminConfigCompleter = adminConfigComplete{} var s3Completer = s3Complete{} var aliasCompleter = aliasComplete{} var fsCompleter = fsComplete{} @@ -143,37 +203,60 @@ var fsCompleter = fsComplete{} // The list of all commands supported by mc with their mapping // with their bash completer function var completeCmds = map[string]complete.Predictor{ - "/ls": complete.PredictOr(s3Completer, fsCompleter), - "/cp": complete.PredictOr(s3Completer, fsCompleter), - "/rm": complete.PredictOr(s3Completer, fsCompleter), - "/rb": complete.PredictOr(s3Complete{deepLevel: 2}, fsCompleter), - "/cat": complete.PredictOr(s3Completer, fsCompleter), - "/head": complete.PredictOr(s3Completer, fsCompleter), - "/diff": complete.PredictOr(s3Completer, fsCompleter), - "/find": complete.PredictOr(s3Completer, fsCompleter), - "/mirror": complete.PredictOr(s3Completer, fsCompleter), - "/pipe": complete.PredictOr(s3Completer, fsCompleter), - "/stat": complete.PredictOr(s3Completer, fsCompleter), - "/watch": complete.PredictOr(s3Completer, fsCompleter), - "/policy": complete.PredictOr(s3Completer, fsCompleter), - "/tree": complete.PredictOr(s3Complete{deepLevel: 2}, fsCompleter), - "/du": complete.PredictOr(s3Complete{deepLevel: 2}, fsCompleter), - - "/mb": aliasCompleter, - "/sql": s3Completer, - - "/admin/info": aliasCompleter, - "/admin/heal": s3Completer, - "/admin/credential": aliasCompleter, - - "/admin/config/get": aliasCompleter, - "/admin/config/set": aliasCompleter, - - "/admin/service/status": aliasCompleter, - "/admin/service/restart": aliasCompleter, + // S3 API level commands + "/ls": complete.PredictOr(s3Completer, fsCompleter), + "/cp": complete.PredictOr(s3Completer, fsCompleter), + "/rm": complete.PredictOr(s3Completer, fsCompleter), + "/rb": complete.PredictOr(s3Complete{deepLevel: 2}, fsCompleter), + "/cat": complete.PredictOr(s3Completer, fsCompleter), + "/head": complete.PredictOr(s3Completer, fsCompleter), + "/diff": complete.PredictOr(s3Completer, fsCompleter), + "/find": complete.PredictOr(s3Completer, fsCompleter), + "/mirror": complete.PredictOr(s3Completer, fsCompleter), + "/pipe": complete.PredictOr(s3Completer, fsCompleter), + "/stat": complete.PredictOr(s3Completer, fsCompleter), + "/watch": complete.PredictOr(s3Completer, fsCompleter), + "/policy": complete.PredictOr(s3Completer, fsCompleter), + "/tree": complete.PredictOr(s3Complete{deepLevel: 2}, fsCompleter), + "/du": complete.PredictOr(s3Complete{deepLevel: 2}, fsCompleter), + "/retention": s3Completer, + "/sql": s3Completer, + "/lock": complete.PredictOr(s3Complete{deepLevel: 2}), + "/mb": aliasCompleter, + + "/event/add": aliasCompleter, + "/event/list": aliasCompleter, + "/event/remove": aliasCompleter, + + "/share/download": s3Completer, + "/share/list": nil, + "/share/upload": s3Completer, + + // Admin API commands MinIO only. + "/admin/heal": s3Completer, + + "/admin/info/server": aliasCompleter, + "/admin/info/cpu": aliasCompleter, + "/admin/info/mem": aliasCompleter, + + "/admin/config/get": adminConfigCompleter, + "/admin/config/set": adminConfigCompleter, + "/admin/config/del": adminConfigCompleter, + "/admin/config/import": aliasCompleter, + "/admin/config/export": aliasCompleter, + "/admin/config/history/restore": aliasCompleter, + "/admin/config/history/list": aliasCompleter, + "/admin/config/history/clear": aliasCompleter, + + "/admin/trace": aliasCompleter, + "/admin/console": aliasCompleter, + "/admin/update": aliasCompleter, + "/admin/top/locks": aliasCompleter, + "/admin/service/stop": aliasCompleter, + "/admin/service/restart": aliasCompleter, - "/admin/trace": aliasCompleter, + "/admin/prometheus/generate": aliasCompleter, "/admin/profile/start": aliasCompleter, "/admin/profile/stop": aliasCompleter, @@ -198,18 +281,10 @@ var completeCmds = map[string]complete.Predictor{ "/admin/group/remove": aliasCompleter, "/admin/group/info": aliasCompleter, - "/event/add": aliasCompleter, - "/event/list": aliasCompleter, - "/event/remove": aliasCompleter, - "/session/clear": nil, "/session/list": nil, "/session/resume": nil, - "/share/download": nil, - "/share/list": nil, - "/share/upload": nil, - "/config/host/add": nil, "/config/host/list": aliasCompleter, "/config/host/remove": aliasCompleter,