diff --git a/cmd/admin-bucket-replication-set.go b/cmd/admin-bucket-replication-set.go index 825ba965..768d47b0 100644 --- a/cmd/admin-bucket-replication-set.go +++ b/cmd/admin-bucket-replication-set.go @@ -126,17 +126,6 @@ func (r replicationMessage) JSON() string { return string(jsonMessageBytes) } -// isValidPath - validates if bucket path is of valid type -func isValidPath(path string) (ok bool) { - l := strings.ToLower(strings.TrimSpace(path)) - for _, v := range []string{"on", "off", "auto"} { - if l == v { - return true - } - } - return false -} - // fetchReplicationTarget - returns the dest bucket, dest endpoint, access and secret key func fetchReplicationTarget(cli *cli.Context) (sourceBucket string, replTarget *madmin.BucketReplicationTarget, err error) { args := cli.Args() diff --git a/cmd/config-host-list.go b/cmd/alias-list.go similarity index 58% rename from cmd/config-host-list.go rename to cmd/alias-list.go index 6aa4889a..71bd1502 100644 --- a/cmd/config-host-list.go +++ b/cmd/alias-list.go @@ -1,5 +1,5 @@ /* - * MinIO Client (C) 2017 MinIO, Inc. + * MinIO Client (C) 2017-2020 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,11 +25,13 @@ import ( "github.com/minio/minio/pkg/console" ) -var configHostListCmd = cli.Command{ - Name: "list", - ShortName: "ls", - Usage: "list hosts in configuration file", - Action: mainConfigHostList, +var aliasListCmd = cli.Command{ + Name: "list", + ShortName: "ls", + Usage: "list aliases in configuration file", + Action: func(ctx *cli.Context) error { + return mainAliasList(ctx, false) + }, Before: setGlobalsFromContext, Flags: globalFlags, HideHelpCommand: true, @@ -43,110 +45,122 @@ FLAGS: {{range .VisibleFlags}}{{.}} {{end}} EXAMPLES: - 1. List all hosts. + 1. List all aliases. {{.Prompt}} {{.HelpName}} - 2. List a specific host. + 2. List a specific alias. {{.Prompt}} {{.HelpName}} s3 `, } // Input argument validator.. -func checkConfigHostListSyntax(ctx *cli.Context) { +func checkAliasListSyntax(ctx *cli.Context) { args := ctx.Args() if len(ctx.Args()) > 1 { fatalIf(errInvalidArgument().Trace(args...), - "Incorrect number of arguments to list hosts.") + "Incorrect number of arguments to alias list command.") } } -func mainConfigHostList(ctx *cli.Context) error { - checkConfigHostListSyntax(ctx) +func mainAliasList(ctx *cli.Context, deprecated bool) error { + checkAliasListSyntax(ctx) - // Additional command speific theme customization. + // Additional command specific theme customization. console.SetColor("Alias", color.New(color.FgCyan, color.Bold)) console.SetColor("URL", color.New(color.FgYellow)) console.SetColor("AccessKey", color.New(color.FgCyan)) console.SetColor("SecretKey", color.New(color.FgCyan)) console.SetColor("API", color.New(color.FgBlue)) - console.SetColor("Lookup", color.New(color.FgCyan)) + console.SetColor("Path", color.New(color.FgCyan)) alias := cleanAlias(ctx.Args().Get(0)) - listHosts(alias) // List all configured hosts. + aliasesMsgs := listAliases(alias, deprecated) // List all configured hosts. + for i := range aliasesMsgs { + aliasesMsgs[i].op = "list" + } + printAliases(aliasesMsgs...) return nil } -// Prints all the hosts. -func printHosts(hosts ...hostMessage) { +// Prints all the aliases +func printAliases(aliases ...aliasMessage) { var maxAlias = 0 - for _, host := range hosts { - if len(host.Alias) > maxAlias { - maxAlias = len(host.Alias) + for _, alias := range aliases { + if len(alias.Alias) > maxAlias { + maxAlias = len(alias.Alias) } } - for _, host := range hosts { + for _, alias := range aliases { if !globalJSON { // Format properly for alignment based on alias length only in non json mode. - host.Alias = fmt.Sprintf("%-*.*s", maxAlias, maxAlias, host.Alias) + alias.Alias = fmt.Sprintf("%-*.*s", maxAlias, maxAlias, alias.Alias) } - if host.AccessKey == "" || host.SecretKey == "" { - host.AccessKey = "" - host.SecretKey = "" - host.API = "" + if alias.AccessKey == "" || alias.SecretKey == "" { + alias.AccessKey = "" + alias.SecretKey = "" + alias.API = "" } - printMsg(host) + printMsg(alias) } } // byAlias is a collection satisfying sort.Interface -type byAlias []hostMessage +type byAlias []aliasMessage func (d byAlias) Len() int { return len(d) } func (d byAlias) Swap(i, j int) { d[i], d[j] = d[j], d[i] } func (d byAlias) Less(i, j int) bool { return d[i].Alias < d[j].Alias } -// listHosts - list all host URLs or a requested host. -func listHosts(alias string) { +// listAliases - list one or all aliases +func listAliases(alias string, deprecated bool) (aliases []aliasMessage) { conf, err := loadMcConfig() fatalIf(err.Trace(globalMCConfigVersion), "Unable to load config version `"+globalMCConfigVersion+"`.") // If specific alias is requested, look for it and print. if alias != "" { - if v, ok := conf.Hosts[alias]; ok { - printHosts(hostMessage{ - op: "list", + if v, ok := conf.Aliases[alias]; ok { + aliasMsg := aliasMessage{ prettyPrint: false, Alias: alias, URL: v.URL, AccessKey: v.AccessKey, SecretKey: v.SecretKey, API: v.API, - Lookup: v.Lookup, - }) - return + } + + if deprecated { + aliasMsg.Lookup = v.Path + } else { + aliasMsg.Path = v.Path + } + + return []aliasMessage{aliasMsg} } fatalIf(errInvalidAliasedURL(alias), "No such alias `"+alias+"` found.") } - var hosts []hostMessage - for k, v := range conf.Hosts { - hosts = append(hosts, hostMessage{ - op: "list", + for k, v := range conf.Aliases { + aliasMsg := aliasMessage{ prettyPrint: true, Alias: k, URL: v.URL, AccessKey: v.AccessKey, SecretKey: v.SecretKey, API: v.API, - Lookup: v.Lookup, - }) + } + + if deprecated { + aliasMsg.Lookup = v.Path + } else { + aliasMsg.Path = v.Path + } + + aliases = append(aliases, aliasMsg) } - // Sort hosts by alias names lexically. - sort.Sort(byAlias(hosts)) - - // Display all the hosts. - printHosts(hosts...) + // Sort by alias names lexically. + sort.Sort(byAlias(aliases)) + return } diff --git a/cmd/config-host.go b/cmd/alias-main.go similarity index 53% rename from cmd/config-host.go rename to cmd/alias-main.go index 3e9aea3a..af62f763 100644 --- a/cmd/config-host.go +++ b/cmd/alias-main.go @@ -1,5 +1,5 @@ /* - * MinIO Client (C) 2017 MinIO, Inc. + * MinIO Client (C) 2014-2020 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,29 +23,44 @@ import ( "github.com/minio/minio/pkg/console" ) -var configHostCmd = cli.Command{ - Name: "host", - Usage: "add, remove and list hosts in configuration file", - Action: mainConfigHost, - Before: setGlobalsFromContext, - Flags: globalFlags, - Subcommands: []cli.Command{ - configHostAddCmd, - configHostRemoveCmd, - configHostListCmd, - }, +// Configure an alias in MinIO Client +// +// ---- +// NOTE: that the alias command only writes values to the config file. +// It does not use any configuration values from the environment variables. +// +// One needs to edit configuration file manually, this is purposefully done +// so to avoid taking credentials over cli arguments. It is a security precaution +// ---- +// + +var ( + aliasFlags = []cli.Flag{} +) + +var aliasCmd = cli.Command{ + Name: "alias", + Usage: "set, remove and list aliases in configuration file", + Action: mainAlias, + Before: setGlobalsFromContext, HideHelpCommand: true, + Flags: append(aliasFlags, globalFlags...), + Subcommands: []cli.Command{ + aliasSetCmd, + aliasListCmd, + aliasRemoveCmd, + }, } -// mainConfigHost is the handle for "mc config host" command. -func mainConfigHost(ctx *cli.Context) error { +// mainAlias is the handle for "mc alias" command. provides sub-commands which write configuration data in json format to config file. +func mainAlias(ctx *cli.Context) error { cli.ShowCommandHelp(ctx, ctx.Args().First()) return nil - // Sub-commands like "remove", "list" have their own main. + // Sub-commands like add, list and remove have their own main. } -// hostMessage container for content message structure -type hostMessage struct { +// aliasMessage container for content message structure +type aliasMessage struct { op string prettyPrint bool Status string `json:"status"` @@ -54,13 +69,15 @@ type hostMessage struct { AccessKey string `json:"accessKey,omitempty"` SecretKey string `json:"secretKey,omitempty"` API string `json:"api,omitempty"` - Lookup string `json:"lookup,omitempty"` + Path string `json:"path,omitempty"` + // Deprecated field, replaced by Path + Lookup string `json:"lookup,omitempty"` } // Print the config information of one alias, when prettyPrint flag // is activated, fields contents are cut and '...' will be added to // show a pretty table of all aliases configurations -func (h hostMessage) String() string { +func (h aliasMessage) String() string { switch h.op { case "list": // Create a new pretty table with cols configuration @@ -70,20 +87,27 @@ func (h hostMessage) String() string { Row{"AccessKey", "AccessKey"}, Row{"SecretKey", "SecretKey"}, Row{"API", "API"}, - Row{"Lookup", "Lookup"}, + Row{"Path", "Path"}, ) - return t.buildRecord(h.Alias, h.URL, h.AccessKey, h.SecretKey, h.API, h.Lookup) + // Handle deprecated lookup + path := h.Path + if path == "" { + path = h.Lookup + } + return t.buildRecord(h.Alias, h.URL, h.AccessKey, h.SecretKey, h.API, path) case "remove": - return console.Colorize("HostMessage", "Removed `"+h.Alias+"` successfully.") - case "add": - return console.Colorize("HostMessage", "Added `"+h.Alias+"` successfully.") + return console.Colorize("AliasMessage", "Removed `"+h.Alias+"` successfully.") + case "add": // add is deprecated + fallthrough + case "set": + return console.Colorize("AliasMessage", "Added `"+h.Alias+"` successfully.") default: return "" } } // JSON jsonified host message -func (h hostMessage) JSON() string { +func (h aliasMessage) JSON() string { h.Status = "success" jsonMessageBytes, e := json.MarshalIndent(h, "", " ") fatalIf(probe.NewError(e), "Unable to marshal into JSON.") diff --git a/cmd/config-host-remove.go b/cmd/alias-remove.go similarity index 56% rename from cmd/config-host-remove.go rename to cmd/alias-remove.go index a2198795..98540587 100644 --- a/cmd/config-host-remove.go +++ b/cmd/alias-remove.go @@ -1,5 +1,5 @@ /* - * MinIO Client (C) 2017 MinIO, Inc. + * MinIO Client (C) 2017-2020 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,11 +22,13 @@ import ( "github.com/minio/minio/pkg/console" ) -var configHostRemoveCmd = cli.Command{ - Name: "remove", - ShortName: "rm", - Usage: "remove a host from configuration file", - Action: mainConfigHostRemove, +var aliasRemoveCmd = cli.Command{ + Name: "remove", + ShortName: "rm", + Usage: "remove an alias from configuration file", + Action: func(ctx *cli.Context) error { + return mainAliasRemove(ctx, false) + }, Before: setGlobalsFromContext, Flags: globalFlags, HideHelpCommand: true, @@ -40,19 +42,19 @@ FLAGS: {{range .VisibleFlags}}{{.}} {{end}} EXAMPLES: - 1. Remove "goodisk" from config. + 1. Remove "goodisk" alias from the configuration. {{.Prompt}} {{.HelpName}} goodisk `, } -// checkConfigHostRemoveSyntax - verifies input arguments to 'config host remove'. -func checkConfigHostRemoveSyntax(ctx *cli.Context) { +// checkAliasRemoveSyntax - verifies input arguments to 'alias remove'. +func checkAliasRemoveSyntax(ctx *cli.Context) { args := ctx.Args() if len(ctx.Args()) != 1 { fatalIf(errInvalidArgument().Trace(args...), - "Incorrect number of arguments for remove host command.") + "Incorrect number of arguments for alias remove command.") } alias := cleanAlias(args.Get(0)) @@ -61,28 +63,31 @@ func checkConfigHostRemoveSyntax(ctx *cli.Context) { } } -// mainConfigHost is the handle for "mc config host rm" command. -func mainConfigHostRemove(ctx *cli.Context) error { - checkConfigHostRemoveSyntax(ctx) +// mainAliasRemove is the handle for "mc alias rm" command. +func mainAliasRemove(ctx *cli.Context, deprecated bool) error { + checkAliasRemoveSyntax(ctx) - console.SetColor("HostMessage", color.New(color.FgGreen)) + console.SetColor("AliasMessage", color.New(color.FgGreen)) args := ctx.Args() alias := args.Get(0) - removeHost(alias) // Remove a host. + + aliasMsg := removeAlias(alias) // Remove an alias + aliasMsg.op = "remove" + printMsg(aliasMsg) return nil } -// removeHost - removes a host. -func removeHost(alias string) { +// removeAlias - removes an alias. +func removeAlias(alias string) aliasMessage { conf, err := loadMcConfig() fatalIf(err.Trace(globalMCConfigVersion), "Unable to load config version `"+globalMCConfigVersion+"`.") - // Remove host. - delete(conf.Hosts, alias) + // Remove the alias from the config. + delete(conf.Aliases, alias) err = saveMcConfig(conf) - fatalIf(err.Trace(alias), "Unable to save deleted hosts in config version `"+globalMCConfigVersion+"`.") + fatalIf(err.Trace(alias), "Unable to save the delete alias in config version `"+globalMCConfigVersion+"`.") - printMsg(hostMessage{op: "remove", Alias: alias}) + return aliasMessage{Alias: alias} } diff --git a/cmd/config-host-add.go b/cmd/alias-set.go similarity index 71% rename from cmd/config-host-add.go rename to cmd/alias-set.go index be089976..3c765968 100644 --- a/cmd/config-host-add.go +++ b/cmd/alias-set.go @@ -1,5 +1,5 @@ /* - * MinIO Client (C) 2017 MinIO, Inc. + * MinIO Client (C) 2017-2020 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import ( "fmt" "math/rand" "os" + "strings" "time" "github.com/fatih/color" @@ -34,24 +35,27 @@ import ( const cred = "YellowItalics" -var hostAddFlags = []cli.Flag{ +var aliasSetFlags = []cli.Flag{ cli.StringFlag{ - Name: "lookup", + Name: "path", Value: "auto", - Usage: "bucket lookup supported by the server. Valid options are '[dns,path,auto]'", + Usage: "bucket path lookup supported by the server. Valid options are '[auto, on, off]'", }, cli.StringFlag{ Name: "api", Usage: "API signature. Valid options are '[S3v4, S3v2]'", }, } -var configHostAddCmd = cli.Command{ - Name: "add", - ShortName: "a", - Usage: "add a new host to configuration file", - Action: mainConfigHostAdd, + +var aliasSetCmd = cli.Command{ + Name: "set", + ShortName: "s", + Usage: "set a new alias to configuration file", + Action: func(cli *cli.Context) error { + return mainAliasSet(cli, false) + }, Before: setGlobalsFromContext, - Flags: append(hostAddFlags, globalFlags...), + Flags: append(aliasSetFlags, globalFlags...), HideHelpCommand: true, CustomHelpTemplate: `NAME: {{.HelpName}} - {{.Usage}} @@ -93,18 +97,19 @@ EXAMPLES: `, } -// checkConfigHostAddSyntax - verifies input arguments to 'config host add'. -func checkConfigHostAddSyntax(ctx *cli.Context, accessKey string, secretKey string) { +// checkAliasSetSyntax - verifies input arguments to 'alias set'. +func checkAliasSetSyntax(ctx *cli.Context, accessKey string, secretKey string, deprecated bool) { args := ctx.Args() argsNr := len(args) if argsNr > 4 || argsNr < 2 { fatalIf(errInvalidArgument().Trace(ctx.Args().Tail()...), - "Incorrect number of arguments for host add command.") + "Incorrect number of arguments for alias set command.") } alias := cleanAlias(args.Get(0)) url := args.Get(1) api := ctx.String("api") + path := ctx.String("path") bucketLookup := ctx.String("lookup") if !isValidAlias(alias) { @@ -130,32 +135,38 @@ func checkConfigHostAddSyntax(ctx *cli.Context, accessKey string, secretKey stri "Unrecognized API signature. Valid options are `[S3v4, S3v2]`.") } - if !isValidLookup(bucketLookup) { - fatalIf(errInvalidArgument().Trace(bucketLookup), - "Unrecognized bucket lookup. Valid options are `[dns,auto, path]`.") + if deprecated { + if !isValidLookup(bucketLookup) { + fatalIf(errInvalidArgument().Trace(bucketLookup), + "Unrecognized bucket lookup. Valid options are `[dns,auto, path]`.") + } + } else { + if !isValidPath(path) { + fatalIf(errInvalidArgument().Trace(bucketLookup), + "Unrecognized path value. Valid options are `[auto, on, off]`.") + } } } -// addHost - add a host config. -func addHost(alias string, hostCfgV9 hostConfigV9) { - mcCfgV9, err := loadMcConfig() +// setAlias - set an alias config. +func setAlias(alias string, aliasCfgV10 aliasConfigV10) aliasMessage { + mcCfgV10, err := loadMcConfig() fatalIf(err.Trace(globalMCConfigVersion), "Unable to load config `"+mustGetMcConfigPath()+"`.") // Add new host. - mcCfgV9.Hosts[alias] = hostCfgV9 + mcCfgV10.Aliases[alias] = aliasCfgV10 - err = saveMcConfig(mcCfgV9) + err = saveMcConfig(mcCfgV10) fatalIf(err.Trace(alias), "Unable to update hosts in config version `"+mustGetMcConfigPath()+"`.") - printMsg(hostMessage{ - op: "add", + return aliasMessage{ Alias: alias, - URL: hostCfgV9.URL, - AccessKey: hostCfgV9.AccessKey, - SecretKey: hostCfgV9.SecretKey, - API: hostCfgV9.API, - Lookup: hostCfgV9.Lookup, - }) + URL: aliasCfgV10.URL, + AccessKey: aliasCfgV10.AccessKey, + SecretKey: aliasCfgV10.SecretKey, + API: aliasCfgV10.API, + Path: aliasCfgV10.Path, + } } // probeS3Signature - auto probe S3 server signature: issue a Stat call @@ -210,13 +221,12 @@ func probeS3Signature(ctx context.Context, accessKey, secretKey, url string) (st // BuildS3Config constructs an S3 Config and does // signature auto-probe when needed. -func BuildS3Config(ctx context.Context, url, accessKey, secretKey, api, lookup string) (*Config, *probe.Error) { - - s3Config := NewS3Config(url, &hostConfigV9{ +func BuildS3Config(ctx context.Context, url, accessKey, secretKey, api, path string) (*Config, *probe.Error) { + s3Config := NewS3Config(url, &aliasConfigV10{ AccessKey: accessKey, SecretKey: secretKey, URL: url, - Lookup: lookup, + Path: path, }) // If api is provided we do not auto probe signature, this is @@ -228,7 +238,7 @@ func BuildS3Config(ctx context.Context, url, accessKey, secretKey, api, lookup s // Probe S3 signature version api, err := probeS3Signature(ctx, accessKey, secretKey, url) if err != nil { - return nil, err.Trace(url, accessKey, secretKey, api, lookup) + return nil, err.Trace(url, accessKey, secretKey, api, path) } s3Config.Signature = api @@ -236,8 +246,8 @@ func BuildS3Config(ctx context.Context, url, accessKey, secretKey, api, lookup s return s3Config, nil } -// fetchHostKeys - returns the user accessKey and secretKey -func fetchHostKeys(args cli.Args) (string, string) { +// fetchAliasKeys - returns the user accessKey and secretKey +func fetchAliasKeys(args cli.Args) (string, string) { accessKey := "" secretKey := "" console.SetColor(cred, color.New(color.FgYellow, color.Italic)) @@ -273,31 +283,52 @@ func fetchHostKeys(args cli.Args) (string, string) { return accessKey, secretKey } -func mainConfigHostAdd(cli *cli.Context) error { - - console.SetColor("HostMessage", color.New(color.FgGreen)) +func mainAliasSet(cli *cli.Context, deprecated bool) error { + console.SetColor("AliasMessage", color.New(color.FgGreen)) var ( - args = cli.Args() - alias = cleanAlias(args.Get(0)) - url = trimTrailingSeparator(args.Get(1)) - api = cli.String("api") - lookup = cli.String("lookup") + args = cli.Args() + alias = cleanAlias(args.Get(0)) + url = trimTrailingSeparator(args.Get(1)) + api = cli.String("api") + path = cli.String("path") ) - accessKey, secretKey := fetchHostKeys(args) - checkConfigHostAddSyntax(cli, accessKey, secretKey) - ctx, cancelHostAdd := context.WithCancel(globalContext) - defer cancelHostAdd() + // Support deprecated lookup flag + if deprecated { + lookup := strings.ToLower(strings.TrimSpace(cli.String("lookup"))) + switch lookup { + case "", "auto": + path = "auto" + case "path": + path = "on" + case "dns": + path = "off" + default: + } + } - s3Config, err := BuildS3Config(ctx, url, accessKey, secretKey, api, lookup) - fatalIf(err.Trace(cli.Args()...), "Unable to initialize new config from the provided credentials.") + accessKey, secretKey := fetchAliasKeys(args) + checkAliasSetSyntax(cli, accessKey, secretKey, deprecated) - addHost(alias, hostConfigV9{ + ctx, cancelAliasAdd := context.WithCancel(globalContext) + defer cancelAliasAdd() + + s3Config, err := BuildS3Config(ctx, url, accessKey, secretKey, api, path) + fatalIf(err.Trace(cli.Args()...), "Unable to initialize new alias from the provided credentials.") + + msg := setAlias(alias, aliasConfigV10{ URL: s3Config.HostURL, AccessKey: s3Config.AccessKey, SecretKey: s3Config.SecretKey, API: s3Config.Signature, - Lookup: lookup, - }) // Add a host with specified credentials. + Path: path, + }) // Add an alias with specified credentials. + + msg.op = "set" + if deprecated { + msg.op = "add" + } + + printMsg(msg) return nil } diff --git a/cmd/auto-complete.go b/cmd/auto-complete.go index 231c372d..47e14076 100644 --- a/cmd/auto-complete.go +++ b/cmd/auto-complete.go @@ -136,10 +136,10 @@ func (adm adminConfigComplete) Predict(a complete.Args) (prediction []string) { arg := a.Last lastArg := a.LastCompleted - if _, ok := conf.Hosts[filepath.Clean(a.LastCompleted)]; !ok { + if _, ok := conf.Aliases[filepath.Clean(a.LastCompleted)]; !ok { if strings.IndexByte(arg, '/') == -1 { // Only predict alias since '/' is not found - for alias := range conf.Hosts { + for alias := range conf.Aliases { if strings.HasPrefix(alias, arg) { prediction = append(prediction, alias+"/") } @@ -173,7 +173,7 @@ func (s3 s3Complete) Predict(a complete.Args) (prediction []string) { if strings.IndexByte(arg, '/') == -1 { // Only predict alias since '/' is not found - for alias := range conf.Hosts { + for alias := range conf.Aliases { if strings.HasPrefix(alias, arg) { prediction = append(prediction, alias+"/") } @@ -210,7 +210,7 @@ func (al aliasComplete) Predict(a complete.Args) (prediction []string) { } arg := a.Last - for alias := range conf.Hosts { + for alias := range conf.Aliases { if strings.HasPrefix(alias, arg) { prediction = append(prediction, alias+"/") } diff --git a/cmd/client-admin.go b/cmd/client-admin.go index a1511a8a..95de72d6 100644 --- a/cmd/client-admin.go +++ b/cmd/client-admin.go @@ -131,21 +131,21 @@ func NewAdminFactory() func(config *Config) (*madmin.AdminClient, *probe.Error) // newAdminClient gives a new client interface func newAdminClient(aliasedURL string) (*madmin.AdminClient, *probe.Error) { - alias, urlStrFull, hostCfg, err := expandAlias(aliasedURL) + alias, urlStrFull, aliasCfg, err := expandAlias(aliasedURL) if err != nil { return nil, err.Trace(aliasedURL) } // Verify if the aliasedURL is a real URL, fail in those cases // indicating the user to add alias. - if hostCfg == nil && urlRgx.MatchString(aliasedURL) { + if aliasCfg == nil && urlRgx.MatchString(aliasedURL) { return nil, errInvalidAliasedURL(aliasedURL).Trace(aliasedURL) } - if hostCfg == nil { + if aliasCfg == nil { return nil, probe.NewError(fmt.Errorf("No valid configuration found for '%s' host alias", urlStrFull)) } - s3Config := NewS3Config(urlStrFull, hostCfg) + s3Config := NewS3Config(urlStrFull, aliasCfg) s3Client, err := s3AdminNew(s3Config) if err != nil { diff --git a/cmd/config-deprecated.go b/cmd/config-deprecated.go new file mode 100644 index 00000000..7683ba97 --- /dev/null +++ b/cmd/config-deprecated.go @@ -0,0 +1,100 @@ +/* + * MinIO Client (C) 2014, 2015 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cmd + +import "github.com/minio/cli" + +var configCmd = cli.Command{ + Name: "config", + Usage: "configure MinIO client", + Action: func(ctx *cli.Context) error { + cli.ShowCommandHelp(ctx, ctx.Args().First()) + return nil + }, + Hidden: true, + Before: setGlobalsFromContext, + HideHelpCommand: true, + Flags: globalFlags, + Subcommands: []cli.Command{ + configHostCmd, + }, +} + +var configHostCmd = cli.Command{ + Name: "host", + Usage: "add, remove and list hosts in configuration file", + Action: func(ctx *cli.Context) error { + cli.ShowCommandHelp(ctx, ctx.Args().First()) + return nil + }, + Before: setGlobalsFromContext, + Flags: globalFlags, + Subcommands: []cli.Command{ + configHostAddCmd, + configHostRemoveCmd, + configHostListCmd, + }, + HideHelpCommand: true, +} + +var configHostAddFlags = []cli.Flag{ + cli.StringFlag{ + Name: "lookup", + Value: "auto", + Usage: "bucket lookup supported by the server. Valid options are '[dns, path, auto]'", + }, + cli.StringFlag{ + Name: "api", + Usage: "API signature. Valid options are '[S3v4, S3v2]'", + }, +} + +var configHostAddCmd = cli.Command{ + Name: "add", + ShortName: "a", + Usage: "add a new host to configuration file", + Action: func(cli *cli.Context) error { + return mainAliasSet(cli, true) + }, + Before: setGlobalsFromContext, + Flags: append(configHostAddFlags, globalFlags...), + HideHelpCommand: true, +} + +var configHostListCmd = cli.Command{ + Name: "list", + ShortName: "ls", + Usage: "list hosts in configuration file", + Action: func(cli *cli.Context) error { + return mainAliasList(cli, true) + }, + Before: setGlobalsFromContext, + Flags: globalFlags, + HideHelpCommand: true, +} + +var configHostRemoveCmd = cli.Command{ + Name: "remove", + ShortName: "rm", + Usage: "remove a host from configuration file", + Action: func(cli *cli.Context) error { + return mainAliasRemove(cli, true) + }, + Before: setGlobalsFromContext, + Flags: globalFlags, + HideHelpCommand: true, +} diff --git a/cmd/config-fix.go b/cmd/config-fix.go index 6d327cbf..54ae8c81 100644 --- a/cmd/config-fix.go +++ b/cmd/config-fix.go @@ -1,5 +1,5 @@ /* - * MinIO Client (C) 2015 MinIO, Inc. + * MinIO Client (C) 2015-2020 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,6 +42,12 @@ func fixConfig() { */ } +// ConfigAnyVersion is a generic structure to parse any +// config.json version file and only extracts its version number +type ConfigAnyVersion struct { + Version string +} + /////////////////// Broken Config V3 /////////////////// type brokenHostConfigV3 struct { AccessKeyID string @@ -73,14 +79,18 @@ func fixConfigV3() { if !isMcConfigExists() { return } + + // Check if this is the correct version to fix + configAllVersions, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config.") + if configAllVersions.Version() != "3" { + return + } + brokenCfgV3 := newBrokenConfigV3() brokenMcCfgV3, e := quick.LoadConfig(mustGetMcConfigPath(), nil, brokenCfgV3) fatalIf(probe.NewError(e), "Unable to load config.") - if brokenMcCfgV3.Version() != "3" { - return - } - cfgV3 := newConfigV3() isMutated := false for k, v := range brokenMcCfgV3.Data().(*brokenConfigV3).Aliases { @@ -122,13 +132,16 @@ func fixConfigV6ForHosts() { return } - brokenMcCfgV6, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV6()) + // Check the current config version + configAllVersions, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) fatalIf(probe.NewError(e), "Unable to load config.") - - if brokenMcCfgV6.Version() != "6" { + if configAllVersions.Version() != "6" { return } + brokenMcCfgV6, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV6()) + fatalIf(probe.NewError(e), "Unable to load config.") + newCfgV6 := newConfigV6() isMutated := false @@ -175,16 +188,19 @@ func fixConfigV6() { if !isMcConfigExists() { return } + + configAllVersions, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config.") + if configAllVersions.Version() != "6" { + return + } + config, e := quick.NewConfig(newConfigV6(), nil) fatalIf(probe.NewError(e), "Unable to initialize config.") e = config.Load(mustGetMcConfigPath()) fatalIf(probe.NewError(e).Trace(mustGetMcConfigPath()), "Unable to load config.") - if config.Data().(*configV6).Version != "6" { - return - } - newConfig := new(configV6) isMutated := false newConfig.Aliases = make(map[string]string) diff --git a/cmd/config-main.go b/cmd/config-main.go deleted file mode 100644 index f36c7234..00000000 --- a/cmd/config-main.go +++ /dev/null @@ -1,53 +0,0 @@ -/* - * MinIO Client (C) 2014, 2015 MinIO, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package cmd - -import "github.com/minio/cli" - -// Configure MinIO Client -// -// ---- -// NOTE: that the configure command only writes values to the config file. -// It does not use any configuration values from the environment variables. -// -// One needs to edit configuration file manually, this is purposefully done -// so to avoid taking credentials over cli arguments. It is a security precaution -// ---- -// - -var ( - configFlags = []cli.Flag{} -) - -var configCmd = cli.Command{ - Name: "config", - Usage: "configure MinIO client", - Action: mainConfig, - Before: setGlobalsFromContext, - HideHelpCommand: true, - Flags: append(configFlags, globalFlags...), - Subcommands: []cli.Command{ - configHostCmd, - }, -} - -// mainConfig is the handle for "mc config" command. provides sub-commands which write configuration data in json format to config file. -func mainConfig(ctx *cli.Context) error { - cli.ShowCommandHelp(ctx, ctx.Args().First()) - return nil - // Sub-commands like "host" and "alias" have their own main. -} diff --git a/cmd/config-migrate.go b/cmd/config-migrate.go index 6c38f802..a33b8442 100644 --- a/cmd/config-migrate.go +++ b/cmd/config-migrate.go @@ -45,6 +45,8 @@ func migrateConfig() { migrateConfigV7ToV8() // Migrate config V8 to V9 migrateConfigV8ToV9() + // Migrate config V9 to V10 + migrateConfigV9ToV10() } // Migrate from config version 1.0 to 1.0.1. Populate example entries and save it back. @@ -52,14 +54,17 @@ func migrateConfigV1ToV101() { if !isMcConfigExists() { return } - mcCfgV1, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV1()) - fatalIf(probe.NewError(e), "Unable to load config version `1`.") - // If loaded config version does not match 1.0.0, we do nothing. - if mcCfgV1.Version() != "1.0.0" { + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version `1`.") + if anyCfg.Version() != "1.0.0" { return } + mcCfgV1, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV1()) + fatalIf(probe.NewError(e), "Unable to load config version `1`.") + // 1.0.1 is compatible to 1.0.0. We are just adding new entries. cfgV101 := newConfigV101() @@ -109,13 +114,18 @@ func migrateConfigV101ToV2() { if !isMcConfigExists() { return } + + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version `1`.") + if anyCfg.Version() != "1.0.1" { + return + } + mcCfgV101, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV101()) fatalIf(probe.NewError(e), "Unable to load config version `1.0.1`.") // update to newer version - if mcCfgV101.Version() != "1.0.1" { - return - } cfgV2 := newConfigV2() @@ -148,14 +158,16 @@ func migrateConfigV2ToV3() { return } - mcCfgV2, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV2()) - fatalIf(probe.NewError(e), "Unable to load mc config V2.") - - // update to newer version - if mcCfgV2.Version() != "2" { + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version.") + if anyCfg.Version() != "2" { return } + mcCfgV2, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV2()) + fatalIf(probe.NewError(e), "Unable to load mc config V2.") + cfgV3 := newConfigV3() // Copy aliases. @@ -187,14 +199,17 @@ func migrateConfigV3ToV4() { if !isMcConfigExists() { return } - mcCfgV3, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV3()) - fatalIf(probe.NewError(e), "Unable to load mc config V2.") - // update to newer version - if mcCfgV3.Version() != "3" { + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version.") + if anyCfg.Version() != "3" { return } + mcCfgV3, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV3()) + fatalIf(probe.NewError(e), "Unable to load mc config V3.") + cfgV4 := newConfigV4() for k, v := range mcCfgV3.Data().(*configV3).Aliases { cfgV4.Aliases[k] = v @@ -226,14 +241,17 @@ func migrateConfigV4ToV5() { if !isMcConfigExists() { return } - mcCfgV4, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV4()) - fatalIf(probe.NewError(e), "Unable to load mc config V4.") - // update to newer version - if mcCfgV4.Version() != "4" { + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version.") + if anyCfg.Version() != "4" { return } + mcCfgV4, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV4()) + fatalIf(probe.NewError(e), "Unable to load mc config V4.") + cfgV5 := newConfigV5() for k, v := range mcCfgV4.Data().(*configV4).Aliases { cfgV5.Aliases[k] = v @@ -261,14 +279,17 @@ func migrateConfigV5ToV6() { if !isMcConfigExists() { return } - mcCfgV5, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV5()) - fatalIf(probe.NewError(e), "Unable to load mc config V5.") - // update to newer version - if mcCfgV5.Version() != "5" { + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version.") + if anyCfg.Version() != "5" { return } + mcCfgV5, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV5()) + fatalIf(probe.NewError(e), "Unable to load mc config V5.") + cfgV6 := newConfigV6() // Add new Google Cloud Storage alias. @@ -328,13 +349,16 @@ func migrateConfigV6ToV7() { return } - mcCfgV6, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV6()) - fatalIf(probe.NewError(e), "Unable to load mc config V6.") - - if mcCfgV6.Version() != "6" { + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version.") + if anyCfg.Version() != "6" { return } + mcCfgV6, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV6()) + fatalIf(probe.NewError(e), "Unable to load mc config V6.") + cfgV7 := newConfigV7() aliasIndex := 0 @@ -407,13 +431,16 @@ func migrateConfigV7ToV8() { return } - mcCfgV7, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV7()) - fatalIf(probe.NewError(e), "Unable to load mc config V7.") - - if mcCfgV7.Version() != "7" { + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version.") + if anyCfg.Version() != "7" { return } + mcCfgV7, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV7()) + fatalIf(probe.NewError(e), "Unable to load mc config V7.") + cfgV8 := newConfigV8() // We dropped alias support in v7. We only need to migrate host configs. for host, hostCfgV7 := range mcCfgV7.Data().(*configV7).Hosts { @@ -445,22 +472,23 @@ func migrateConfigV8ToV9() { return } - mcCfgV8, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV8()) - fatalIf(probe.NewError(e), "Unable to load mc config V8.") - - if mcCfgV8.Version() != "8" { + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version.") + if anyCfg.Version() != "8" { return } + mcCfgV8, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV8()) + fatalIf(probe.NewError(e), "Unable to load mc config V8.") + cfgV9 := newConfigV9() - isEmpty := true // We dropped alias support in v8. We only need to migrate host configs. for host, hostCfgV8 := range mcCfgV8.Data().(*configV8).Hosts { // Ignore 'player', 'play' and 'dl' aliases. if host == "player" || host == "dl" || host == "play" { continue } - isEmpty = false hostCfgV9 := hostConfigV9{} hostCfgV9.URL = hostCfgV8.URL hostCfgV9.AccessKey = hostCfgV8.AccessKey @@ -469,10 +497,6 @@ func migrateConfigV8ToV9() { hostCfgV9.Lookup = "auto" cfgV9.Hosts[host] = hostCfgV9 } - if isEmpty { - // Load default settings. - cfgV9.loadDefaults() - } mcNewCfgV9, e := quick.NewConfig(cfgV9, nil) fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `9`.") @@ -482,3 +506,55 @@ func migrateConfigV8ToV9() { console.Infof("Successfully migrated %s from version `8` to version `9`.\n", mustGetMcConfigPath()) } + +// Migrate config version `9` to `10'. Rename 'hosts' to 'aliases' and 'lookup' to 'path' +func migrateConfigV9ToV10() { + if !isMcConfigExists() { + return + } + + // Check the config version and quit early if the actual version is out of this function scope + anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{}) + fatalIf(probe.NewError(e), "Unable to load config version.") + if anyCfg.Version() != "9" { + return + } + + mcCfgV9, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV9()) + fatalIf(probe.NewError(e), "Unable to load mc config V8.") + + cfgV10 := newConfigV10() + isEmpty := true + // We dropped alias support in v8. We only need to migrate host configs. + for host, hostCfgV9 := range mcCfgV9.Data().(*configV9).Hosts { + isEmpty = false + hostCfgV10 := aliasConfigV10{} + hostCfgV10.URL = hostCfgV9.URL + hostCfgV10.AccessKey = hostCfgV9.AccessKey + hostCfgV10.SecretKey = hostCfgV9.SecretKey + hostCfgV10.API = hostCfgV9.API + switch hostCfgV9.Lookup { + case "dns": + hostCfgV10.Path = "off" + case "path": + hostCfgV10.Path = "on" + default: + hostCfgV10.Path = "auto" + } + + cfgV10.Aliases[host] = hostCfgV10 + } + + if isEmpty { + // Load default settings. + cfgV10.loadDefaults() + } + + mcNewCfgV10, e := quick.NewConfig(cfgV10, nil) + fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `10`.") + + e = mcNewCfgV10.Save(mustGetMcConfigPath()) + fatalIf(probe.NewError(e), "Unable to save config version `10`.") + + console.Infof("Successfully migrated %s from version `9` to version `10`.\n", mustGetMcConfigPath()) +} diff --git a/cmd/config-old.go b/cmd/config-old.go index 91402d5f..31ad012d 100644 --- a/cmd/config-old.go +++ b/cmd/config-old.go @@ -305,4 +305,29 @@ func (c *configV8) loadDefaults() { } /////////////////// Config V9 /////////////////// + +// hostConfig configuration of a host. +type hostConfigV9 struct { + URL string `json:"url"` + AccessKey string `json:"accessKey"` + SecretKey string `json:"secretKey"` + SessionToken string `json:"sessionToken,omitempty"` + API string `json:"api"` + Lookup string `json:"lookup"` +} + +// configV8 config version. +type configV9 struct { + Version string `json:"version"` + Hosts map[string]hostConfigV9 `json:"hosts"` +} + +func newConfigV9() *configV9 { + cfg := new(configV9) + cfg.Version = "9" + cfg.Hosts = make(map[string]hostConfigV9) + return cfg +} + +/////////////////// Config V10 /////////////////// // RESERVED FOR FUTURE diff --git a/cmd/config-utils.go b/cmd/config-utils.go index e8eaa8f5..6be40f86 100644 --- a/cmd/config-utils.go +++ b/cmd/config-utils.go @@ -79,3 +79,14 @@ func isValidLookup(lookup string) (ok bool) { } return false } + +// isValidPath - validates the alias path config +func isValidPath(path string) (ok bool) { + l := strings.ToLower(strings.TrimSpace(path)) + for _, v := range []string{"on", "off", "auto"} { + if l == v { + return true + } + } + return false +} diff --git a/cmd/config-v9.go b/cmd/config-v10.go similarity index 66% rename from cmd/config-v9.go rename to cmd/config-v10.go index b15ed012..1af266cc 100644 --- a/cmd/config-v9.go +++ b/cmd/config-v10.go @@ -1,5 +1,5 @@ /* - * MinIO Client (C) 2015 MinIO, Inc. + * MinIO Client (C) 2020 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,89 +30,89 @@ const ( var ( // set once during first load. - cacheCfgV9 *configV9 + cacheCfgV10 *configV10 // All access to mc config file should be synchronized. cfgMutex = &sync.RWMutex{} ) -// hostConfig configuration of a host. -type hostConfigV9 struct { +// aliasConfig configuration of an alias. +type aliasConfigV10 struct { URL string `json:"url"` AccessKey string `json:"accessKey"` SecretKey string `json:"secretKey"` SessionToken string `json:"sessionToken,omitempty"` API string `json:"api"` - Lookup string `json:"lookup"` + Path string `json:"path"` } -// configV8 config version. -type configV9 struct { - Version string `json:"version"` - Hosts map[string]hostConfigV9 `json:"hosts"` +// configV10 config version. +type configV10 struct { + Version string `json:"version"` + Aliases map[string]aliasConfigV10 `json:"aliases"` } -// newConfigV9 - new config version. -func newConfigV9() *configV9 { - cfg := new(configV9) +// newConfigV10 - new config version. +func newConfigV10() *configV10 { + cfg := new(configV10) cfg.Version = globalMCConfigVersion - cfg.Hosts = make(map[string]hostConfigV9) + cfg.Aliases = make(map[string]aliasConfigV10) return cfg } -// SetHost sets host config if not empty. -func (c *configV9) setHost(alias string, cfg hostConfigV9) { - if _, ok := c.Hosts[alias]; !ok { - c.Hosts[alias] = cfg +// SetAlias sets host config if not empty. +func (c *configV10) setAlias(alias string, cfg aliasConfigV10) { + if _, ok := c.Aliases[alias]; !ok { + c.Aliases[alias] = cfg } } // load default values for missing entries. -func (c *configV9) loadDefaults() { +func (c *configV10) loadDefaults() { // MinIO server running locally. - c.setHost("local", hostConfigV9{ + c.setAlias("local", aliasConfigV10{ URL: "http://localhost:9000", AccessKey: "", SecretKey: "", API: "S3v4", - Lookup: "auto", + Path: "auto", }) // Amazon S3 cloud storage service. - c.setHost("s3", hostConfigV9{ + c.setAlias("s3", aliasConfigV10{ URL: "https://s3.amazonaws.com", AccessKey: defaultAccessKey, SecretKey: defaultSecretKey, API: "S3v4", - Lookup: "dns", + Path: "dns", }) // Google cloud storage service. - c.setHost("gcs", hostConfigV9{ + c.setAlias("gcs", aliasConfigV10{ URL: "https://storage.googleapis.com", AccessKey: defaultAccessKey, SecretKey: defaultSecretKey, API: "S3v2", - Lookup: "dns", + Path: "dns", }) // MinIO anonymous server for demo. - c.setHost("play", hostConfigV9{ + c.setAlias("play", aliasConfigV10{ URL: "https://play.min.io", AccessKey: "Q3AM3UQ867SPQQA43P2F", SecretKey: "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", API: "S3v4", - Lookup: "auto", + Path: "auto", }) } -// loadConfigV9 - loads a new config. -func loadConfigV9() (*configV9, *probe.Error) { +// loadConfigV10 - loads a new config. +func loadConfigV10() (*configV10, *probe.Error) { cfgMutex.RLock() defer cfgMutex.RUnlock() // If already cached, return the cached value. - if cacheCfgV9 != nil { - return cacheCfgV9, nil + if cacheCfgV10 != nil { + return cacheCfgV10, nil } if !isMcConfigExists() { @@ -120,7 +120,7 @@ func loadConfigV9() (*configV9, *probe.Error) { } // Initialize a new config loader. - qc, e := quick.NewConfig(newConfigV9(), nil) + qc, e := quick.NewConfig(newConfigV10(), nil) if e != nil { return nil, probe.NewError(e) } @@ -131,27 +131,27 @@ func loadConfigV9() (*configV9, *probe.Error) { return nil, probe.NewError(e) } - cfgV9 := qc.Data().(*configV9) + cfgV10 := qc.Data().(*configV10) // Cache config. - cacheCfgV9 = cfgV9 + cacheCfgV10 = cfgV10 // Success. - return cfgV9, nil + return cfgV10, nil } -// saveConfigV8 - saves an updated config. -func saveConfigV9(cfgV9 *configV9) *probe.Error { +// saveConfigV10 - saves an updated config. +func saveConfigV10(cfgV10 *configV10) *probe.Error { cfgMutex.Lock() defer cfgMutex.Unlock() - qs, e := quick.NewConfig(cfgV9, nil) + qs, e := quick.NewConfig(cfgV10, nil) if e != nil { return probe.NewError(e) } // update the cache. - cacheCfgV9 = cfgV9 + cacheCfgV10 = cfgV10 e = qs.Save(mustGetMcConfigPath()) if e != nil { diff --git a/cmd/config-validate.go b/cmd/config-validate.go index a2515569..99422793 100644 --- a/cmd/config-validate.go +++ b/cmd/config-validate.go @@ -22,7 +22,7 @@ import ( ) // Check if version of the config is valid -func validateConfigVersion(config *configV9) (bool, string) { +func validateConfigVersion(config *configV10) (bool, string) { if config.Version != globalMCConfigVersion { return false, fmt.Sprintf("Config version '%s' does not match mc config version '%s', please update your binary.\n", config.Version, globalMCConfigVersion) @@ -31,7 +31,7 @@ func validateConfigVersion(config *configV9) (bool, string) { } // Verifies the config file of the MinIO Client -func validateConfigFile(config *configV9) (bool, []string) { +func validateConfigFile(config *configV10) (bool, []string) { ok, err := validateConfigVersion(config) var validationSuccessful = true var errors []string @@ -39,18 +39,18 @@ func validateConfigFile(config *configV9) (bool, []string) { validationSuccessful = false errors = append(errors, err) } - hosts := config.Hosts - for _, hostConfig := range hosts { - hostConfigHealthOk, hostErrors := validateConfigHost(hostConfig) - if !hostConfigHealthOk { + aliases := config.Aliases + for _, aliasConfig := range aliases { + aliasConfigHealthOk, aliasErrors := validateConfigHost(aliasConfig) + if !aliasConfigHealthOk { validationSuccessful = false - errors = append(errors, hostErrors...) + errors = append(errors, aliasErrors...) } } return validationSuccessful, errors } -func validateConfigHost(host hostConfigV9) (bool, []string) { +func validateConfigHost(host aliasConfigV10) (bool, []string) { var validationSuccessful = true var hostErrors []string if !isValidAPI(strings.ToLower(host.API)) { diff --git a/cmd/config.go b/cmd/config.go index 03a128a3..602de4b1 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -1,5 +1,5 @@ /* - * MinIO Client (C) 2015 MinIO, Inc. + * MinIO Client (C) 2015-2020 MinIO, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,29 +105,29 @@ func mustGetMcConfigPath() string { return path } -// newMcConfig - initializes a new version '9' config. -func newMcConfig() *configV9 { - cfg := newConfigV9() +// newMcConfig - initializes a new version '10' config. +func newMcConfig() *configV10 { + cfg := newConfigV10() cfg.loadDefaults() return cfg } // loadMcConfigCached - returns loadMcConfig with a closure for config cache. -func loadMcConfigFactory() func() (*configV9, *probe.Error) { +func loadMcConfigFactory() func() (*configV10, *probe.Error) { // Load once and cache in a closure. - cfgCache, err := loadConfigV9() + cfgCache, err := loadConfigV10() // loadMcConfig - reads configuration file and returns config. - return func() (*configV9, *probe.Error) { + return func() (*configV10, *probe.Error) { return cfgCache, err } } // loadMcConfig - returns configuration, initialized later. -var loadMcConfig func() (*configV9, *probe.Error) +var loadMcConfig func() (*configV10, *probe.Error) // saveMcConfig - saves configuration file and returns error if any. -func saveMcConfig(config *configV9) *probe.Error { +func saveMcConfig(config *configV10) *probe.Error { if config == nil { return errInvalidArgument().Trace() } @@ -138,7 +138,7 @@ func saveMcConfig(config *configV9) *probe.Error { } // Save the config. - if err := saveConfigV9(config); err != nil { + if err := saveConfigV10(config); err != nil { return err.Trace(mustGetMcConfigPath()) } @@ -172,16 +172,16 @@ func isValidAlias(alias string) bool { return regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-_]+$").MatchString(alias) } -// getHostConfig retrieves host specific configuration such as access keys, signature type. -func getHostConfig(alias string) (*hostConfigV9, *probe.Error) { +// getAliasConfig retrieves host specific configuration such as access keys, signature type. +func getAliasConfig(alias string) (*aliasConfigV10, *probe.Error) { mcCfg, err := loadMcConfig() if err != nil { return nil, err.Trace(alias) } // if host is exact return quickly. - if _, ok := mcCfg.Hosts[alias]; ok { - hostCfg := mcCfg.Hosts[alias] + if _, ok := mcCfg.Aliases[alias]; ok { + hostCfg := mcCfg.Aliases[alias] return &hostCfg, nil } @@ -190,22 +190,22 @@ func getHostConfig(alias string) (*hostConfigV9, *probe.Error) { } // mustGetHostConfig retrieves host specific configuration such as access keys, signature type. -func mustGetHostConfig(alias string) *hostConfigV9 { - hostCfg, _ := getHostConfig(alias) +func mustGetHostConfig(alias string) *aliasConfigV10 { + aliasCfg, _ := getAliasConfig(alias) // If alias is not found, // look for it in the environment variable. - if hostCfg == nil { + if aliasCfg == nil { if envConfig, ok := os.LookupEnv(mcEnvHostPrefix + alias); ok { - hostCfg, _ = expandAliasFromEnv(envConfig) + aliasCfg, _ = expandAliasFromEnv(envConfig) } } - if hostCfg == nil { + if aliasCfg == nil { if envConfig, ok := os.LookupEnv(mcEnvHostsDeprecatedPrefix + alias); ok { errorIf(errInvalidArgument().Trace(mcEnvHostsDeprecatedPrefix+alias), "`MC_HOSTS_` environment variable is deprecated. Please use `MC_HOST_` instead for the same functionality.") - hostCfg, _ = expandAliasFromEnv(envConfig) + aliasCfg, _ = expandAliasFromEnv(envConfig) } } - return hostCfg + return aliasCfg } var ( @@ -265,13 +265,13 @@ const ( mcEnvHostsDeprecatedPrefix = "MC_HOSTS_" ) -func expandAliasFromEnv(envURL string) (*hostConfigV9, *probe.Error) { +func expandAliasFromEnv(envURL string) (*aliasConfigV10, *probe.Error) { u, accessKey, secretKey, sessionToken, err := parseEnvURLStr(envURL) if err != nil { return nil, err.Trace(envURL) } - return &hostConfigV9{ + return &aliasConfigV10{ URL: u.String(), API: "S3v4", AccessKey: accessKey, @@ -281,7 +281,7 @@ func expandAliasFromEnv(envURL string) (*hostConfigV9, *probe.Error) { } // expandAlias expands aliased URL if any match is found, returns as is otherwise. -func expandAlias(aliasedURL string) (alias string, urlStr string, hostCfg *hostConfigV9, err *probe.Error) { +func expandAlias(aliasedURL string) (alias string, urlStr string, aliasCfg *aliasConfigV10, err *probe.Error) { // Extract alias from the URL. alias, path := url2Alias(aliasedURL) @@ -296,22 +296,22 @@ func expandAlias(aliasedURL string) (alias string, urlStr string, hostCfg *hostC } if ok { - hostCfg, err = expandAliasFromEnv(envConfig) + aliasCfg, err = expandAliasFromEnv(envConfig) if err != nil { return "", "", nil, err.Trace(aliasedURL) } - return alias, urlJoinPath(hostCfg.URL, path), hostCfg, nil + return alias, urlJoinPath(aliasCfg.URL, path), aliasCfg, nil } // Find the matching alias entry and expand the URL. - if hostCfg = mustGetHostConfig(alias); hostCfg != nil { - return alias, urlJoinPath(hostCfg.URL, path), hostCfg, nil + if aliasCfg = mustGetHostConfig(alias); aliasCfg != nil { + return alias, urlJoinPath(aliasCfg.URL, path), aliasCfg, nil } return "", aliasedURL, nil, nil // No matching entry found. Return original URL as is. } // mustExpandAlias expands aliased URL if any match is found, returns as is otherwise. -func mustExpandAlias(aliasedURL string) (alias string, urlStr string, hostCfg *hostConfigV9) { - alias, urlStr, hostCfg, _ = expandAlias(aliasedURL) - return alias, urlStr, hostCfg +func mustExpandAlias(aliasedURL string) (alias string, urlStr string, aliasCfg *aliasConfigV10) { + alias, urlStr, aliasCfg, _ = expandAlias(aliasedURL) + return alias, urlStr, aliasCfg } diff --git a/cmd/globals.go b/cmd/globals.go index 91545925..143ce3f2 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -26,7 +26,7 @@ import ( ) const ( - globalMCConfigVersion = "9" + globalMCConfigVersion = "10" globalMCConfigFile = "config.json" globalMCCertsDir = "certs" diff --git a/cmd/main.go b/cmd/main.go index 883895f9..3db86f4f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -319,6 +319,7 @@ func checkUpdate(ctx *cli.Context) { } var appCmds = []cli.Command{ + aliasCmd, lsCmd, mbCmd, rbCmd, diff --git a/cmd/typed-errors.go b/cmd/typed-errors.go index 37b58a47..cae97e75 100644 --- a/cmd/typed-errors.go +++ b/cmd/typed-errors.go @@ -48,7 +48,7 @@ var errUnrecognizedDiffType = func(diff differType) *probe.Error { type invalidAliasedURLErr error var errInvalidAliasedURL = func(URL string) *probe.Error { - msg := "Use `mc config host add mycloud " + URL + " ...` to add an alias. Use the alias for S3 operations." + msg := "Use `mc alias set mycloud " + URL + " ...` to add an alias. Use the alias for S3 operations." return probe.NewError(invalidAliasedURLErr(errors.New(msg))).Untrace() } diff --git a/cmd/utils.go b/cmd/utils.go index 9b463716..83a66650 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -125,7 +125,7 @@ func splitStr(path, sep string, n int) []string { // NewS3Config simply creates a new Config struct using the passed // parameters. -func NewS3Config(urlStr string, hostCfg *hostConfigV9) *Config { +func NewS3Config(urlStr string, aliasCfg *aliasConfigV10) *Config { // We have a valid alias and hostConfig. We populate the // credentials from the match found in the config file. s3Config := new(Config) @@ -137,13 +137,13 @@ func NewS3Config(urlStr string, hostCfg *hostConfigV9) *Config { s3Config.Insecure = globalInsecure s3Config.HostURL = urlStr - if hostCfg != nil { - s3Config.AccessKey = hostCfg.AccessKey - s3Config.SecretKey = hostCfg.SecretKey - s3Config.SessionToken = hostCfg.SessionToken - s3Config.Signature = hostCfg.API + if aliasCfg != nil { + s3Config.AccessKey = aliasCfg.AccessKey + s3Config.SecretKey = aliasCfg.SecretKey + s3Config.SessionToken = aliasCfg.SessionToken + s3Config.Signature = aliasCfg.API } - s3Config.Lookup = getLookupType(hostCfg.Lookup) + s3Config.Lookup = getLookupType(aliasCfg.Path) return s3Config } @@ -189,9 +189,9 @@ func isNewer(ti time.Time, newerRef string) bool { func getLookupType(l string) minio.BucketLookupType { l = strings.ToLower(l) switch l { - case "dns": + case "off": return minio.BucketLookupDNS - case "path": + case "on": return minio.BucketLookupPath } return minio.BucketLookupAuto diff --git a/docs/minio-client-complete-guide.md b/docs/minio-client-complete-guide.md index da5b1399..498c586a 100644 --- a/docs/minio-client-complete-guide.md +++ b/docs/minio-client-complete-guide.md @@ -3,6 +3,7 @@ MinIO Client (mc) provides a modern alternative to UNIX commands like ls, cat, cp, mirror, diff etc. It supports filesystems and Amazon S3 compatible cloud storage service (AWS Signature v2 and v4). ``` +alias set up an alias for an S3 server ls list buckets and objects mb make a bucket rb remove a bucket @@ -29,7 +30,6 @@ watch listen for object notification events policy manage anonymous access to buckets and objects tag manage tags for bucket(s) and object(s) admin manage MinIO servers -config manage MinIO client update update mc to latest release ``` @@ -52,7 +52,7 @@ docker run minio/mc:edge ls play docker run -it --entrypoint=/bin/sh minio/mc ``` -then use the [`mc config` command](#3-add-a-cloud-storage-service). +then use the [`mc alias` command](#3-add-a-cloud-storage-service). ### Homebrew (macOS) Install mc packages using [Homebrew](http://brew.sh/) @@ -123,7 +123,7 @@ To add one or more Amazon S3 compatible hosts, please follow the instructions be #### Usage ``` -mc config host add [YOUR-ACCESS-KEY] [YOUR-SECRET-KEY] [--api API-SIGNATURE] +mc alias set [YOUR-ACCESS-KEY] [YOUR-SECRET-KEY] [--api API-SIGNATURE] ``` Keys must be supplied by argument or standard input. @@ -135,21 +135,21 @@ MinIO server displays URL, access and secret keys. ``` -mc config host add minio http://192.168.1.51 BKIKJAA5BMMU2RHO6IBB V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 --api S3v4 +mc alias set minio http://192.168.1.51 BKIKJAA5BMMU2RHO6IBB V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 --api S3v4 ``` ### Example - Amazon S3 Cloud Storage Get your AccessKeyID and SecretAccessKey by following [AWS Credentials Guide](http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSGettingStartedGuide/AWSCredentials.html). ``` -mc config host add s3 https://s3.amazonaws.com BKIKJAA5BMMU2RHO6IBB V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 --api S3v4 +mc alias set s3 https://s3.amazonaws.com BKIKJAA5BMMU2RHO6IBB V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 --api S3v4 ``` ### Example - Google Cloud Storage Get your AccessKeyID and SecretAccessKey by following [Google Credentials Guide](https://cloud.google.com/storage/docs/migrating?hl=en#keys) ``` -mc config host add gcs https://storage.googleapis.com BKIKJAA5BMMU2RHO6IBB V8f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 +mc alias set gcs https://storage.googleapis.com BKIKJAA5BMMU2RHO6IBB V8f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 ``` ### Example - Specify keys using standard input @@ -157,7 +157,7 @@ mc config host add gcs https://storage.googleapis.com BKIKJAA5BMMU2RHO6IBB V8f1 #### Prompt ``` -mc config host add minio http://192.168.1.51 --api S3v4 +mc alias set minio http://192.168.1.51 --api S3v4 Enter Access Key: BKIKJAA5BMMU2RHO6IBB Enter Secret Key: V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 ``` @@ -166,7 +166,7 @@ Enter Secret Key: V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 ``` echo -e "BKIKJAA5BMMU2RHO6IBB\nV7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12" | \ -mc config host add minio http://192.168.1.51 --api S3v4 +mc alias set minio http://192.168.1.51 --api S3v4 ``` ### Specify temporary host configuration through environment variable @@ -303,7 +303,7 @@ mc version RELEASE.2020-04-25T00-43-23Z | [**cp** - copy objects](#cp) | [**rb** - remove a bucket](#rb) | [**pipe** - stream STDIN to an object](#pipe) | [**version** - manage bucket version](#version) | | [**share** - generate URL for temporary access to an object](#share) | [**rm** - remove objects](#rm) | [**find** - find files and objects](#find) | | | [**diff** - list differences in object name, size, and date between two buckets](#diff) | [**mirror** - synchronize object(s) to a remote site](#mirror) | [**ilm** - manage bucket lifecycle](#ilm) | | -| [**config** - manage config file](#config) | [**policy** - set public policy on bucket or prefix](#policy) | [**event** - manage events on your buckets](#event) |[**replicate** - manage bucket replication](#replicate) | +| [**alias** - manage aliases](#alias) | [**policy** - set public policy on bucket or prefix](#policy) | [**event** - manage events on your buckets](#event) |[**replicate** - manage bucket replication](#replicate) | | [**update** - manage software updates](#update) | [**watch** - watch for events](#watch) | [**stat** - stat contents of objects and folders](#stat) | | | [**head** - display first 'n' lines of an object](#head) | [**lock** - manage default bucket object lock configuration](#lock) | [**retention** - set retention for object(s)](#retention) | | | [**mv** - move objects](#mv) | [**sql** - run sql queries on objects](#sql) | [**legalhold** - set legal hold for object(s)](#legalhold) | | @@ -1437,18 +1437,18 @@ Replication configuration removed successfully from myminio/testbucket. ### Command `admin` Please visit [here](https://docs.min.io/docs/minio-admin-complete-guide) for a more comprehensive admin guide. - -### Command `config` -`config host` command provides a convenient way to manage host entries in your config file `~/.mc/config.json`. It is also OK to edit the config file manually using a text editor. + +### Command `alias` +`alias` command provides a convenient way to manage aliases entries in your config file `~/.mc/config.json`. It is also OK to edit the config file manually using a text editor. ``` USAGE: - mc config host COMMAND [COMMAND FLAGS | -h] [ARGUMENTS...] + mc alias COMMAND [COMMAND FLAGS | -h] [ARGUMENTS...] COMMANDS: - add, a add a new host to configuration file - remove, rm remove a host from configuration file - list, ls lists hosts in configuration file + set, s add a new alias to configuration file + remove, rm remove an alias from configuration file + list, ls lists aliases in configuration file FLAGS: --help, -h show help @@ -1456,24 +1456,24 @@ FLAGS: *Example: Manage Config File* -Add MinIO server access and secret keys to config file host entry. Note that, the history feature of your shell may record these keys and pose a security risk. On `bash` shell, use `set -o` and `set +o` to disable and enable history feature momentarily. +Add MinIO server access and secret keys to config file alias entry. Note that, the history feature of your shell may record these keys and pose a security risk. On `bash` shell, use `set -o` and `set +o` to disable and enable history feature momentarily. ``` set +o history -mc config host add myminio http://localhost:9000 OMQAGGOL63D7UNVQFY8X GcY5RHNmnEWvD/1QxD3spEIGj+Vt9L7eHaAaBTkJ +mc alias set myminio http://localhost:9000 OMQAGGOL63D7UNVQFY8X GcY5RHNmnEWvD/1QxD3spEIGj+Vt9L7eHaAaBTkJ set -o history ``` -Remove the host from the config file. +Remove the alias from the config file. ``` -mc config host remove myminio +mc alias remove myminio ``` -List all configured host +List all configured aliases ``` -mc config host list +mc alias list ``` diff --git a/docs/minio-client-configuration-files.md b/docs/minio-client-configuration-files.md index 50cc5462..18c41467 100644 --- a/docs/minio-client-configuration-files.md +++ b/docs/minio-client-configuration-files.md @@ -26,37 +26,42 @@ config.json is the configuration file for MinIO Client, it gets generated after ``` cat config.json { - "version": "8", - "hosts": { + "version": "10", + "aliases": { "XL": { "url": "http://127.0.0.1:9000", "accessKey": "YI7S1CKXB76RGOGT6R8W", "secretKey": "FJ9PWUVNXGPfiI72WMRFepN3LsFgW3MjsxSALroV", - "api": "S3v4" + "api": "S3v4", + "path": "auto" }, "fs": { "url": "http://127.0.0.1:9000", "accessKey": "YI7S1CKXB76RGOGT6R8W", "secretKey": "FJ9PWUVNXGPfiI72WMRFepN3LsFgW3MjsxSALroV", - "api": "S3v4" + "api": "S3v4", + "path": "auto" }, "gcs": { "url": "https://storage.googleapis.com", "accessKey": "YOUR-ACCESS-KEY-HERE", "secretKey": "YOUR-SECRET-KEY-HERE", - "api": "S3v2" + "api": "S3v2", + "path": "auto" }, "play": { "url": "https://play.min.io", "accessKey": "Q3AM3UQ867SPQQA43P2F", "secretKey": "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", - "api": "S3v4" + "api": "S3v4", + "path": "auto" }, "s3": { "url": "https://s3.amazonaws.com", "accessKey": "YOUR-ACCESS-KEY-HERE", "secretKey": "YOUR-SECRET-KEY-HERE", - "api": "S3v4" + "api": "S3v4", + "path": "auto" } } } @@ -64,7 +69,7 @@ cat config.json ``version`` tells the version of the file. -``hosts`` stores authentication credentials which will be used by MinIO Client. +``aliases`` stores authentication credentials which will be used by MinIO Client. #### ``config.json.old`` This file keeps previous config file version details.