Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for auto-complete in 'mc admin config' commands #2975

Merged
merged 1 commit into from
Nov 27, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 117 additions & 42 deletions cmd/complete.go → cmd/auto-complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
}
}

Expand All @@ -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
Expand Down Expand Up @@ -136,44 +195,68 @@ func (al aliasComplete) Predict(a complete.Args) (prediction []string) {
return
}

var adminConfigCompleter = adminConfigComplete{}
var s3Completer = s3Complete{}
var aliasCompleter = aliasComplete{}
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,
Expand All @@ -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,
Expand Down