1
0
mirror of https://github.com/minio/mc.git synced 2025-07-30 07:23:03 +03:00

Move from mc config host to alias command (#3311)

```
mc alias set
   alias remove
   alias list
```

are new commands that replace mc config host sub-commands.

mc config host will still be available but hidden for backward
compatiblity. The JSON output is also kept if the user is running mc
config host command.
This commit is contained in:
Anis Elleuch
2020-07-28 18:26:41 +01:00
committed by GitHub
parent 9c09f78982
commit 90dc31f6ba
22 changed files with 634 additions and 390 deletions

View File

@ -126,17 +126,6 @@ func (r replicationMessage) JSON() string {
return string(jsonMessageBytes) 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 // fetchReplicationTarget - returns the dest bucket, dest endpoint, access and secret key
func fetchReplicationTarget(cli *cli.Context) (sourceBucket string, replTarget *madmin.BucketReplicationTarget, err error) { func fetchReplicationTarget(cli *cli.Context) (sourceBucket string, replTarget *madmin.BucketReplicationTarget, err error) {
args := cli.Args() args := cli.Args()

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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" "github.com/minio/minio/pkg/console"
) )
var configHostListCmd = cli.Command{ var aliasListCmd = cli.Command{
Name: "list", Name: "list",
ShortName: "ls", ShortName: "ls",
Usage: "list hosts in configuration file", Usage: "list aliases in configuration file",
Action: mainConfigHostList, Action: func(ctx *cli.Context) error {
return mainAliasList(ctx, false)
},
Before: setGlobalsFromContext, Before: setGlobalsFromContext,
Flags: globalFlags, Flags: globalFlags,
HideHelpCommand: true, HideHelpCommand: true,
@ -43,110 +45,122 @@ FLAGS:
{{range .VisibleFlags}}{{.}} {{range .VisibleFlags}}{{.}}
{{end}} {{end}}
EXAMPLES: EXAMPLES:
1. List all hosts. 1. List all aliases.
{{.Prompt}} {{.HelpName}} {{.Prompt}} {{.HelpName}}
2. List a specific host. 2. List a specific alias.
{{.Prompt}} {{.HelpName}} s3 {{.Prompt}} {{.HelpName}} s3
`, `,
} }
// Input argument validator.. // Input argument validator..
func checkConfigHostListSyntax(ctx *cli.Context) { func checkAliasListSyntax(ctx *cli.Context) {
args := ctx.Args() args := ctx.Args()
if len(ctx.Args()) > 1 { if len(ctx.Args()) > 1 {
fatalIf(errInvalidArgument().Trace(args...), 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 { func mainAliasList(ctx *cli.Context, deprecated bool) error {
checkConfigHostListSyntax(ctx) checkAliasListSyntax(ctx)
// Additional command speific theme customization. // Additional command specific theme customization.
console.SetColor("Alias", color.New(color.FgCyan, color.Bold)) console.SetColor("Alias", color.New(color.FgCyan, color.Bold))
console.SetColor("URL", color.New(color.FgYellow)) console.SetColor("URL", color.New(color.FgYellow))
console.SetColor("AccessKey", color.New(color.FgCyan)) console.SetColor("AccessKey", color.New(color.FgCyan))
console.SetColor("SecretKey", color.New(color.FgCyan)) console.SetColor("SecretKey", color.New(color.FgCyan))
console.SetColor("API", color.New(color.FgBlue)) 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)) 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 return nil
} }
// Prints all the hosts. // Prints all the aliases
func printHosts(hosts ...hostMessage) { func printAliases(aliases ...aliasMessage) {
var maxAlias = 0 var maxAlias = 0
for _, host := range hosts { for _, alias := range aliases {
if len(host.Alias) > maxAlias { if len(alias.Alias) > maxAlias {
maxAlias = len(host.Alias) maxAlias = len(alias.Alias)
} }
} }
for _, host := range hosts { for _, alias := range aliases {
if !globalJSON { if !globalJSON {
// Format properly for alignment based on alias length only in non json mode. // 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 == "" { if alias.AccessKey == "" || alias.SecretKey == "" {
host.AccessKey = "" alias.AccessKey = ""
host.SecretKey = "" alias.SecretKey = ""
host.API = "" alias.API = ""
} }
printMsg(host) printMsg(alias)
} }
} }
// byAlias is a collection satisfying sort.Interface // byAlias is a collection satisfying sort.Interface
type byAlias []hostMessage type byAlias []aliasMessage
func (d byAlias) Len() int { return len(d) } 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) 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 } func (d byAlias) Less(i, j int) bool { return d[i].Alias < d[j].Alias }
// listHosts - list all host URLs or a requested host. // listAliases - list one or all aliases
func listHosts(alias string) { func listAliases(alias string, deprecated bool) (aliases []aliasMessage) {
conf, err := loadMcConfig() conf, err := loadMcConfig()
fatalIf(err.Trace(globalMCConfigVersion), "Unable to load config version `"+globalMCConfigVersion+"`.") fatalIf(err.Trace(globalMCConfigVersion), "Unable to load config version `"+globalMCConfigVersion+"`.")
// If specific alias is requested, look for it and print. // If specific alias is requested, look for it and print.
if alias != "" { if alias != "" {
if v, ok := conf.Hosts[alias]; ok { if v, ok := conf.Aliases[alias]; ok {
printHosts(hostMessage{ aliasMsg := aliasMessage{
op: "list",
prettyPrint: false, prettyPrint: false,
Alias: alias, Alias: alias,
URL: v.URL, URL: v.URL,
AccessKey: v.AccessKey, AccessKey: v.AccessKey,
SecretKey: v.SecretKey, SecretKey: v.SecretKey,
API: v.API, 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.") fatalIf(errInvalidAliasedURL(alias), "No such alias `"+alias+"` found.")
} }
var hosts []hostMessage for k, v := range conf.Aliases {
for k, v := range conf.Hosts { aliasMsg := aliasMessage{
hosts = append(hosts, hostMessage{
op: "list",
prettyPrint: true, prettyPrint: true,
Alias: k, Alias: k,
URL: v.URL, URL: v.URL,
AccessKey: v.AccessKey, AccessKey: v.AccessKey,
SecretKey: v.SecretKey, SecretKey: v.SecretKey,
API: v.API, 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 by alias names lexically.
sort.Sort(byAlias(hosts)) sort.Sort(byAlias(aliases))
return
// Display all the hosts.
printHosts(hosts...)
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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" "github.com/minio/minio/pkg/console"
) )
var configHostCmd = cli.Command{ // Configure an alias in MinIO Client
Name: "host", //
Usage: "add, remove and list hosts in configuration file", // ----
Action: mainConfigHost, // NOTE: that the alias command only writes values to the config file.
Before: setGlobalsFromContext, // It does not use any configuration values from the environment variables.
Flags: globalFlags, //
Subcommands: []cli.Command{ // One needs to edit configuration file manually, this is purposefully done
configHostAddCmd, // so to avoid taking credentials over cli arguments. It is a security precaution
configHostRemoveCmd, // ----
configHostListCmd, //
},
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, HideHelpCommand: true,
Flags: append(aliasFlags, globalFlags...),
Subcommands: []cli.Command{
aliasSetCmd,
aliasListCmd,
aliasRemoveCmd,
},
} }
// mainConfigHost is the handle for "mc config host" command. // mainAlias is the handle for "mc alias" command. provides sub-commands which write configuration data in json format to config file.
func mainConfigHost(ctx *cli.Context) error { func mainAlias(ctx *cli.Context) error {
cli.ShowCommandHelp(ctx, ctx.Args().First()) cli.ShowCommandHelp(ctx, ctx.Args().First())
return nil 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 // aliasMessage container for content message structure
type hostMessage struct { type aliasMessage struct {
op string op string
prettyPrint bool prettyPrint bool
Status string `json:"status"` Status string `json:"status"`
@ -54,13 +69,15 @@ type hostMessage struct {
AccessKey string `json:"accessKey,omitempty"` AccessKey string `json:"accessKey,omitempty"`
SecretKey string `json:"secretKey,omitempty"` SecretKey string `json:"secretKey,omitempty"`
API string `json:"api,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 // Print the config information of one alias, when prettyPrint flag
// is activated, fields contents are cut and '...' will be added to // is activated, fields contents are cut and '...' will be added to
// show a pretty table of all aliases configurations // show a pretty table of all aliases configurations
func (h hostMessage) String() string { func (h aliasMessage) String() string {
switch h.op { switch h.op {
case "list": case "list":
// Create a new pretty table with cols configuration // Create a new pretty table with cols configuration
@ -70,20 +87,27 @@ func (h hostMessage) String() string {
Row{"AccessKey", "AccessKey"}, Row{"AccessKey", "AccessKey"},
Row{"SecretKey", "SecretKey"}, Row{"SecretKey", "SecretKey"},
Row{"API", "API"}, 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": case "remove":
return console.Colorize("HostMessage", "Removed `"+h.Alias+"` successfully.") return console.Colorize("AliasMessage", "Removed `"+h.Alias+"` successfully.")
case "add": case "add": // add is deprecated
return console.Colorize("HostMessage", "Added `"+h.Alias+"` successfully.") fallthrough
case "set":
return console.Colorize("AliasMessage", "Added `"+h.Alias+"` successfully.")
default: default:
return "" return ""
} }
} }
// JSON jsonified host message // JSON jsonified host message
func (h hostMessage) JSON() string { func (h aliasMessage) JSON() string {
h.Status = "success" h.Status = "success"
jsonMessageBytes, e := json.MarshalIndent(h, "", " ") jsonMessageBytes, e := json.MarshalIndent(h, "", " ")
fatalIf(probe.NewError(e), "Unable to marshal into JSON.") fatalIf(probe.NewError(e), "Unable to marshal into JSON.")

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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" "github.com/minio/minio/pkg/console"
) )
var configHostRemoveCmd = cli.Command{ var aliasRemoveCmd = cli.Command{
Name: "remove", Name: "remove",
ShortName: "rm", ShortName: "rm",
Usage: "remove a host from configuration file", Usage: "remove an alias from configuration file",
Action: mainConfigHostRemove, Action: func(ctx *cli.Context) error {
return mainAliasRemove(ctx, false)
},
Before: setGlobalsFromContext, Before: setGlobalsFromContext,
Flags: globalFlags, Flags: globalFlags,
HideHelpCommand: true, HideHelpCommand: true,
@ -40,19 +42,19 @@ FLAGS:
{{range .VisibleFlags}}{{.}} {{range .VisibleFlags}}{{.}}
{{end}} {{end}}
EXAMPLES: EXAMPLES:
1. Remove "goodisk" from config. 1. Remove "goodisk" alias from the configuration.
{{.Prompt}} {{.HelpName}} goodisk {{.Prompt}} {{.HelpName}} goodisk
`, `,
} }
// checkConfigHostRemoveSyntax - verifies input arguments to 'config host remove'. // checkAliasRemoveSyntax - verifies input arguments to 'alias remove'.
func checkConfigHostRemoveSyntax(ctx *cli.Context) { func checkAliasRemoveSyntax(ctx *cli.Context) {
args := ctx.Args() args := ctx.Args()
if len(ctx.Args()) != 1 { if len(ctx.Args()) != 1 {
fatalIf(errInvalidArgument().Trace(args...), 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)) alias := cleanAlias(args.Get(0))
@ -61,28 +63,31 @@ func checkConfigHostRemoveSyntax(ctx *cli.Context) {
} }
} }
// mainConfigHost is the handle for "mc config host rm" command. // mainAliasRemove is the handle for "mc alias rm" command.
func mainConfigHostRemove(ctx *cli.Context) error { func mainAliasRemove(ctx *cli.Context, deprecated bool) error {
checkConfigHostRemoveSyntax(ctx) checkAliasRemoveSyntax(ctx)
console.SetColor("HostMessage", color.New(color.FgGreen)) console.SetColor("AliasMessage", color.New(color.FgGreen))
args := ctx.Args() args := ctx.Args()
alias := args.Get(0) alias := args.Get(0)
removeHost(alias) // Remove a host.
aliasMsg := removeAlias(alias) // Remove an alias
aliasMsg.op = "remove"
printMsg(aliasMsg)
return nil return nil
} }
// removeHost - removes a host. // removeAlias - removes an alias.
func removeHost(alias string) { func removeAlias(alias string) aliasMessage {
conf, err := loadMcConfig() conf, err := loadMcConfig()
fatalIf(err.Trace(globalMCConfigVersion), "Unable to load config version `"+globalMCConfigVersion+"`.") fatalIf(err.Trace(globalMCConfigVersion), "Unable to load config version `"+globalMCConfigVersion+"`.")
// Remove host. // Remove the alias from the config.
delete(conf.Hosts, alias) delete(conf.Aliases, alias)
err = saveMcConfig(conf) 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}
} }

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"os" "os"
"strings"
"time" "time"
"github.com/fatih/color" "github.com/fatih/color"
@ -34,24 +35,27 @@ import (
const cred = "YellowItalics" const cred = "YellowItalics"
var hostAddFlags = []cli.Flag{ var aliasSetFlags = []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "lookup", Name: "path",
Value: "auto", 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{ cli.StringFlag{
Name: "api", Name: "api",
Usage: "API signature. Valid options are '[S3v4, S3v2]'", Usage: "API signature. Valid options are '[S3v4, S3v2]'",
}, },
} }
var configHostAddCmd = cli.Command{
Name: "add", var aliasSetCmd = cli.Command{
ShortName: "a", Name: "set",
Usage: "add a new host to configuration file", ShortName: "s",
Action: mainConfigHostAdd, Usage: "set a new alias to configuration file",
Action: func(cli *cli.Context) error {
return mainAliasSet(cli, false)
},
Before: setGlobalsFromContext, Before: setGlobalsFromContext,
Flags: append(hostAddFlags, globalFlags...), Flags: append(aliasSetFlags, globalFlags...),
HideHelpCommand: true, HideHelpCommand: true,
CustomHelpTemplate: `NAME: CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}} {{.HelpName}} - {{.Usage}}
@ -93,18 +97,19 @@ EXAMPLES:
`, `,
} }
// checkConfigHostAddSyntax - verifies input arguments to 'config host add'. // checkAliasSetSyntax - verifies input arguments to 'alias set'.
func checkConfigHostAddSyntax(ctx *cli.Context, accessKey string, secretKey string) { func checkAliasSetSyntax(ctx *cli.Context, accessKey string, secretKey string, deprecated bool) {
args := ctx.Args() args := ctx.Args()
argsNr := len(args) argsNr := len(args)
if argsNr > 4 || argsNr < 2 { if argsNr > 4 || argsNr < 2 {
fatalIf(errInvalidArgument().Trace(ctx.Args().Tail()...), 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)) alias := cleanAlias(args.Get(0))
url := args.Get(1) url := args.Get(1)
api := ctx.String("api") api := ctx.String("api")
path := ctx.String("path")
bucketLookup := ctx.String("lookup") bucketLookup := ctx.String("lookup")
if !isValidAlias(alias) { if !isValidAlias(alias) {
@ -130,32 +135,38 @@ func checkConfigHostAddSyntax(ctx *cli.Context, accessKey string, secretKey stri
"Unrecognized API signature. Valid options are `[S3v4, S3v2]`.") "Unrecognized API signature. Valid options are `[S3v4, S3v2]`.")
} }
if !isValidLookup(bucketLookup) { if deprecated {
fatalIf(errInvalidArgument().Trace(bucketLookup), if !isValidLookup(bucketLookup) {
"Unrecognized bucket lookup. Valid options are `[dns,auto, path]`.") 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. // setAlias - set an alias config.
func addHost(alias string, hostCfgV9 hostConfigV9) { func setAlias(alias string, aliasCfgV10 aliasConfigV10) aliasMessage {
mcCfgV9, err := loadMcConfig() mcCfgV10, err := loadMcConfig()
fatalIf(err.Trace(globalMCConfigVersion), "Unable to load config `"+mustGetMcConfigPath()+"`.") fatalIf(err.Trace(globalMCConfigVersion), "Unable to load config `"+mustGetMcConfigPath()+"`.")
// Add new host. // 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()+"`.") fatalIf(err.Trace(alias), "Unable to update hosts in config version `"+mustGetMcConfigPath()+"`.")
printMsg(hostMessage{ return aliasMessage{
op: "add",
Alias: alias, Alias: alias,
URL: hostCfgV9.URL, URL: aliasCfgV10.URL,
AccessKey: hostCfgV9.AccessKey, AccessKey: aliasCfgV10.AccessKey,
SecretKey: hostCfgV9.SecretKey, SecretKey: aliasCfgV10.SecretKey,
API: hostCfgV9.API, API: aliasCfgV10.API,
Lookup: hostCfgV9.Lookup, Path: aliasCfgV10.Path,
}) }
} }
// probeS3Signature - auto probe S3 server signature: issue a Stat call // 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 // BuildS3Config constructs an S3 Config and does
// signature auto-probe when needed. // signature auto-probe when needed.
func BuildS3Config(ctx context.Context, url, accessKey, secretKey, api, lookup string) (*Config, *probe.Error) { func BuildS3Config(ctx context.Context, url, accessKey, secretKey, api, path string) (*Config, *probe.Error) {
s3Config := NewS3Config(url, &aliasConfigV10{
s3Config := NewS3Config(url, &hostConfigV9{
AccessKey: accessKey, AccessKey: accessKey,
SecretKey: secretKey, SecretKey: secretKey,
URL: url, URL: url,
Lookup: lookup, Path: path,
}) })
// If api is provided we do not auto probe signature, this is // 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 // Probe S3 signature version
api, err := probeS3Signature(ctx, accessKey, secretKey, url) api, err := probeS3Signature(ctx, accessKey, secretKey, url)
if err != nil { if err != nil {
return nil, err.Trace(url, accessKey, secretKey, api, lookup) return nil, err.Trace(url, accessKey, secretKey, api, path)
} }
s3Config.Signature = api s3Config.Signature = api
@ -236,8 +246,8 @@ func BuildS3Config(ctx context.Context, url, accessKey, secretKey, api, lookup s
return s3Config, nil return s3Config, nil
} }
// fetchHostKeys - returns the user accessKey and secretKey // fetchAliasKeys - returns the user accessKey and secretKey
func fetchHostKeys(args cli.Args) (string, string) { func fetchAliasKeys(args cli.Args) (string, string) {
accessKey := "" accessKey := ""
secretKey := "" secretKey := ""
console.SetColor(cred, color.New(color.FgYellow, color.Italic)) console.SetColor(cred, color.New(color.FgYellow, color.Italic))
@ -273,31 +283,52 @@ func fetchHostKeys(args cli.Args) (string, string) {
return accessKey, secretKey return accessKey, secretKey
} }
func mainConfigHostAdd(cli *cli.Context) error { func mainAliasSet(cli *cli.Context, deprecated bool) error {
console.SetColor("AliasMessage", color.New(color.FgGreen))
console.SetColor("HostMessage", color.New(color.FgGreen))
var ( var (
args = cli.Args() args = cli.Args()
alias = cleanAlias(args.Get(0)) alias = cleanAlias(args.Get(0))
url = trimTrailingSeparator(args.Get(1)) url = trimTrailingSeparator(args.Get(1))
api = cli.String("api") api = cli.String("api")
lookup = cli.String("lookup") path = cli.String("path")
) )
accessKey, secretKey := fetchHostKeys(args)
checkConfigHostAddSyntax(cli, accessKey, secretKey)
ctx, cancelHostAdd := context.WithCancel(globalContext) // Support deprecated lookup flag
defer cancelHostAdd() 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) accessKey, secretKey := fetchAliasKeys(args)
fatalIf(err.Trace(cli.Args()...), "Unable to initialize new config from the provided credentials.") 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, URL: s3Config.HostURL,
AccessKey: s3Config.AccessKey, AccessKey: s3Config.AccessKey,
SecretKey: s3Config.SecretKey, SecretKey: s3Config.SecretKey,
API: s3Config.Signature, API: s3Config.Signature,
Lookup: lookup, Path: path,
}) // Add a host with specified credentials. }) // Add an alias with specified credentials.
msg.op = "set"
if deprecated {
msg.op = "add"
}
printMsg(msg)
return nil return nil
} }

View File

@ -136,10 +136,10 @@ func (adm adminConfigComplete) Predict(a complete.Args) (prediction []string) {
arg := a.Last arg := a.Last
lastArg := a.LastCompleted 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 { if strings.IndexByte(arg, '/') == -1 {
// Only predict alias since '/' is not found // Only predict alias since '/' is not found
for alias := range conf.Hosts { for alias := range conf.Aliases {
if strings.HasPrefix(alias, arg) { if strings.HasPrefix(alias, arg) {
prediction = append(prediction, alias+"/") prediction = append(prediction, alias+"/")
} }
@ -173,7 +173,7 @@ func (s3 s3Complete) Predict(a complete.Args) (prediction []string) {
if strings.IndexByte(arg, '/') == -1 { if strings.IndexByte(arg, '/') == -1 {
// Only predict alias since '/' is not found // Only predict alias since '/' is not found
for alias := range conf.Hosts { for alias := range conf.Aliases {
if strings.HasPrefix(alias, arg) { if strings.HasPrefix(alias, arg) {
prediction = append(prediction, alias+"/") prediction = append(prediction, alias+"/")
} }
@ -210,7 +210,7 @@ func (al aliasComplete) Predict(a complete.Args) (prediction []string) {
} }
arg := a.Last arg := a.Last
for alias := range conf.Hosts { for alias := range conf.Aliases {
if strings.HasPrefix(alias, arg) { if strings.HasPrefix(alias, arg) {
prediction = append(prediction, alias+"/") prediction = append(prediction, alias+"/")
} }

View File

@ -131,21 +131,21 @@ func NewAdminFactory() func(config *Config) (*madmin.AdminClient, *probe.Error)
// newAdminClient gives a new client interface // newAdminClient gives a new client interface
func newAdminClient(aliasedURL string) (*madmin.AdminClient, *probe.Error) { func newAdminClient(aliasedURL string) (*madmin.AdminClient, *probe.Error) {
alias, urlStrFull, hostCfg, err := expandAlias(aliasedURL) alias, urlStrFull, aliasCfg, err := expandAlias(aliasedURL)
if err != nil { if err != nil {
return nil, err.Trace(aliasedURL) return nil, err.Trace(aliasedURL)
} }
// Verify if the aliasedURL is a real URL, fail in those cases // Verify if the aliasedURL is a real URL, fail in those cases
// indicating the user to add alias. // indicating the user to add alias.
if hostCfg == nil && urlRgx.MatchString(aliasedURL) { if aliasCfg == nil && urlRgx.MatchString(aliasedURL) {
return nil, errInvalidAliasedURL(aliasedURL).Trace(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)) 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) s3Client, err := s3AdminNew(s3Config)
if err != nil { if err != nil {

100
cmd/config-deprecated.go Normal file
View File

@ -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,
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 /////////////////// /////////////////// Broken Config V3 ///////////////////
type brokenHostConfigV3 struct { type brokenHostConfigV3 struct {
AccessKeyID string AccessKeyID string
@ -73,14 +79,18 @@ func fixConfigV3() {
if !isMcConfigExists() { if !isMcConfigExists() {
return 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() brokenCfgV3 := newBrokenConfigV3()
brokenMcCfgV3, e := quick.LoadConfig(mustGetMcConfigPath(), nil, brokenCfgV3) brokenMcCfgV3, e := quick.LoadConfig(mustGetMcConfigPath(), nil, brokenCfgV3)
fatalIf(probe.NewError(e), "Unable to load config.") fatalIf(probe.NewError(e), "Unable to load config.")
if brokenMcCfgV3.Version() != "3" {
return
}
cfgV3 := newConfigV3() cfgV3 := newConfigV3()
isMutated := false isMutated := false
for k, v := range brokenMcCfgV3.Data().(*brokenConfigV3).Aliases { for k, v := range brokenMcCfgV3.Data().(*brokenConfigV3).Aliases {
@ -122,13 +132,16 @@ func fixConfigV6ForHosts() {
return 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.") fatalIf(probe.NewError(e), "Unable to load config.")
if configAllVersions.Version() != "6" {
if brokenMcCfgV6.Version() != "6" {
return return
} }
brokenMcCfgV6, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV6())
fatalIf(probe.NewError(e), "Unable to load config.")
newCfgV6 := newConfigV6() newCfgV6 := newConfigV6()
isMutated := false isMutated := false
@ -175,16 +188,19 @@ func fixConfigV6() {
if !isMcConfigExists() { if !isMcConfigExists() {
return 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) config, e := quick.NewConfig(newConfigV6(), nil)
fatalIf(probe.NewError(e), "Unable to initialize config.") fatalIf(probe.NewError(e), "Unable to initialize config.")
e = config.Load(mustGetMcConfigPath()) e = config.Load(mustGetMcConfigPath())
fatalIf(probe.NewError(e).Trace(mustGetMcConfigPath()), "Unable to load config.") fatalIf(probe.NewError(e).Trace(mustGetMcConfigPath()), "Unable to load config.")
if config.Data().(*configV6).Version != "6" {
return
}
newConfig := new(configV6) newConfig := new(configV6)
isMutated := false isMutated := false
newConfig.Aliases = make(map[string]string) newConfig.Aliases = make(map[string]string)

View File

@ -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.
}

View File

@ -45,6 +45,8 @@ func migrateConfig() {
migrateConfigV7ToV8() migrateConfigV7ToV8()
// Migrate config V8 to V9 // Migrate config V8 to V9
migrateConfigV8ToV9() migrateConfigV8ToV9()
// Migrate config V9 to V10
migrateConfigV9ToV10()
} }
// Migrate from config version 1.0 to 1.0.1. Populate example entries and save it back. // 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() { if !isMcConfigExists() {
return 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. // Check the config version and quit early if the actual version is out of this function scope
if mcCfgV1.Version() != "1.0.0" { anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
fatalIf(probe.NewError(e), "Unable to load config version `1`.")
if anyCfg.Version() != "1.0.0" {
return 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. // 1.0.1 is compatible to 1.0.0. We are just adding new entries.
cfgV101 := newConfigV101() cfgV101 := newConfigV101()
@ -109,13 +114,18 @@ func migrateConfigV101ToV2() {
if !isMcConfigExists() { if !isMcConfigExists() {
return 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()) mcCfgV101, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV101())
fatalIf(probe.NewError(e), "Unable to load config version `1.0.1`.") fatalIf(probe.NewError(e), "Unable to load config version `1.0.1`.")
// update to newer version // update to newer version
if mcCfgV101.Version() != "1.0.1" {
return
}
cfgV2 := newConfigV2() cfgV2 := newConfigV2()
@ -148,14 +158,16 @@ func migrateConfigV2ToV3() {
return return
} }
mcCfgV2, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV2()) // Check the config version and quit early if the actual version is out of this function scope
fatalIf(probe.NewError(e), "Unable to load mc config V2.") anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
fatalIf(probe.NewError(e), "Unable to load config version.")
// update to newer version if anyCfg.Version() != "2" {
if mcCfgV2.Version() != "2" {
return return
} }
mcCfgV2, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV2())
fatalIf(probe.NewError(e), "Unable to load mc config V2.")
cfgV3 := newConfigV3() cfgV3 := newConfigV3()
// Copy aliases. // Copy aliases.
@ -187,14 +199,17 @@ func migrateConfigV3ToV4() {
if !isMcConfigExists() { if !isMcConfigExists() {
return return
} }
mcCfgV3, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV3())
fatalIf(probe.NewError(e), "Unable to load mc config V2.")
// update to newer version // Check the config version and quit early if the actual version is out of this function scope
if mcCfgV3.Version() != "3" { anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
fatalIf(probe.NewError(e), "Unable to load config version.")
if anyCfg.Version() != "3" {
return return
} }
mcCfgV3, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV3())
fatalIf(probe.NewError(e), "Unable to load mc config V3.")
cfgV4 := newConfigV4() cfgV4 := newConfigV4()
for k, v := range mcCfgV3.Data().(*configV3).Aliases { for k, v := range mcCfgV3.Data().(*configV3).Aliases {
cfgV4.Aliases[k] = v cfgV4.Aliases[k] = v
@ -226,14 +241,17 @@ func migrateConfigV4ToV5() {
if !isMcConfigExists() { if !isMcConfigExists() {
return return
} }
mcCfgV4, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV4())
fatalIf(probe.NewError(e), "Unable to load mc config V4.")
// update to newer version // Check the config version and quit early if the actual version is out of this function scope
if mcCfgV4.Version() != "4" { anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
fatalIf(probe.NewError(e), "Unable to load config version.")
if anyCfg.Version() != "4" {
return return
} }
mcCfgV4, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV4())
fatalIf(probe.NewError(e), "Unable to load mc config V4.")
cfgV5 := newConfigV5() cfgV5 := newConfigV5()
for k, v := range mcCfgV4.Data().(*configV4).Aliases { for k, v := range mcCfgV4.Data().(*configV4).Aliases {
cfgV5.Aliases[k] = v cfgV5.Aliases[k] = v
@ -261,14 +279,17 @@ func migrateConfigV5ToV6() {
if !isMcConfigExists() { if !isMcConfigExists() {
return return
} }
mcCfgV5, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV5())
fatalIf(probe.NewError(e), "Unable to load mc config V5.")
// update to newer version // Check the config version and quit early if the actual version is out of this function scope
if mcCfgV5.Version() != "5" { anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
fatalIf(probe.NewError(e), "Unable to load config version.")
if anyCfg.Version() != "5" {
return return
} }
mcCfgV5, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV5())
fatalIf(probe.NewError(e), "Unable to load mc config V5.")
cfgV6 := newConfigV6() cfgV6 := newConfigV6()
// Add new Google Cloud Storage alias. // Add new Google Cloud Storage alias.
@ -328,13 +349,16 @@ func migrateConfigV6ToV7() {
return return
} }
mcCfgV6, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV6()) // Check the config version and quit early if the actual version is out of this function scope
fatalIf(probe.NewError(e), "Unable to load mc config V6.") anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
fatalIf(probe.NewError(e), "Unable to load config version.")
if mcCfgV6.Version() != "6" { if anyCfg.Version() != "6" {
return return
} }
mcCfgV6, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV6())
fatalIf(probe.NewError(e), "Unable to load mc config V6.")
cfgV7 := newConfigV7() cfgV7 := newConfigV7()
aliasIndex := 0 aliasIndex := 0
@ -407,13 +431,16 @@ func migrateConfigV7ToV8() {
return return
} }
mcCfgV7, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV7()) // Check the config version and quit early if the actual version is out of this function scope
fatalIf(probe.NewError(e), "Unable to load mc config V7.") anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
fatalIf(probe.NewError(e), "Unable to load config version.")
if mcCfgV7.Version() != "7" { if anyCfg.Version() != "7" {
return return
} }
mcCfgV7, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV7())
fatalIf(probe.NewError(e), "Unable to load mc config V7.")
cfgV8 := newConfigV8() cfgV8 := newConfigV8()
// We dropped alias support in v7. We only need to migrate host configs. // We dropped alias support in v7. We only need to migrate host configs.
for host, hostCfgV7 := range mcCfgV7.Data().(*configV7).Hosts { for host, hostCfgV7 := range mcCfgV7.Data().(*configV7).Hosts {
@ -445,22 +472,23 @@ func migrateConfigV8ToV9() {
return return
} }
mcCfgV8, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV8()) // Check the config version and quit early if the actual version is out of this function scope
fatalIf(probe.NewError(e), "Unable to load mc config V8.") anyCfg, e := quick.LoadConfig(mustGetMcConfigPath(), nil, &ConfigAnyVersion{})
fatalIf(probe.NewError(e), "Unable to load config version.")
if mcCfgV8.Version() != "8" { if anyCfg.Version() != "8" {
return return
} }
mcCfgV8, e := quick.LoadConfig(mustGetMcConfigPath(), nil, newConfigV8())
fatalIf(probe.NewError(e), "Unable to load mc config V8.")
cfgV9 := newConfigV9() cfgV9 := newConfigV9()
isEmpty := true
// We dropped alias support in v8. We only need to migrate host configs. // We dropped alias support in v8. We only need to migrate host configs.
for host, hostCfgV8 := range mcCfgV8.Data().(*configV8).Hosts { for host, hostCfgV8 := range mcCfgV8.Data().(*configV8).Hosts {
// Ignore 'player', 'play' and 'dl' aliases. // Ignore 'player', 'play' and 'dl' aliases.
if host == "player" || host == "dl" || host == "play" { if host == "player" || host == "dl" || host == "play" {
continue continue
} }
isEmpty = false
hostCfgV9 := hostConfigV9{} hostCfgV9 := hostConfigV9{}
hostCfgV9.URL = hostCfgV8.URL hostCfgV9.URL = hostCfgV8.URL
hostCfgV9.AccessKey = hostCfgV8.AccessKey hostCfgV9.AccessKey = hostCfgV8.AccessKey
@ -469,10 +497,6 @@ func migrateConfigV8ToV9() {
hostCfgV9.Lookup = "auto" hostCfgV9.Lookup = "auto"
cfgV9.Hosts[host] = hostCfgV9 cfgV9.Hosts[host] = hostCfgV9
} }
if isEmpty {
// Load default settings.
cfgV9.loadDefaults()
}
mcNewCfgV9, e := quick.NewConfig(cfgV9, nil) mcNewCfgV9, e := quick.NewConfig(cfgV9, nil)
fatalIf(probe.NewError(e), "Unable to initialize quick config for config version `9`.") 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()) 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())
}

View File

@ -305,4 +305,29 @@ func (c *configV8) loadDefaults() {
} }
/////////////////// Config V9 /////////////////// /////////////////// 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 // RESERVED FOR FUTURE

View File

@ -79,3 +79,14 @@ func isValidLookup(lookup string) (ok bool) {
} }
return false 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
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -30,89 +30,89 @@ const (
var ( var (
// set once during first load. // set once during first load.
cacheCfgV9 *configV9 cacheCfgV10 *configV10
// All access to mc config file should be synchronized. // All access to mc config file should be synchronized.
cfgMutex = &sync.RWMutex{} cfgMutex = &sync.RWMutex{}
) )
// hostConfig configuration of a host. // aliasConfig configuration of an alias.
type hostConfigV9 struct { type aliasConfigV10 struct {
URL string `json:"url"` URL string `json:"url"`
AccessKey string `json:"accessKey"` AccessKey string `json:"accessKey"`
SecretKey string `json:"secretKey"` SecretKey string `json:"secretKey"`
SessionToken string `json:"sessionToken,omitempty"` SessionToken string `json:"sessionToken,omitempty"`
API string `json:"api"` API string `json:"api"`
Lookup string `json:"lookup"` Path string `json:"path"`
} }
// configV8 config version. // configV10 config version.
type configV9 struct { type configV10 struct {
Version string `json:"version"` Version string `json:"version"`
Hosts map[string]hostConfigV9 `json:"hosts"` Aliases map[string]aliasConfigV10 `json:"aliases"`
} }
// newConfigV9 - new config version. // newConfigV10 - new config version.
func newConfigV9() *configV9 { func newConfigV10() *configV10 {
cfg := new(configV9) cfg := new(configV10)
cfg.Version = globalMCConfigVersion cfg.Version = globalMCConfigVersion
cfg.Hosts = make(map[string]hostConfigV9) cfg.Aliases = make(map[string]aliasConfigV10)
return cfg return cfg
} }
// SetHost sets host config if not empty. // SetAlias sets host config if not empty.
func (c *configV9) setHost(alias string, cfg hostConfigV9) { func (c *configV10) setAlias(alias string, cfg aliasConfigV10) {
if _, ok := c.Hosts[alias]; !ok { if _, ok := c.Aliases[alias]; !ok {
c.Hosts[alias] = cfg c.Aliases[alias] = cfg
} }
} }
// load default values for missing entries. // load default values for missing entries.
func (c *configV9) loadDefaults() { func (c *configV10) loadDefaults() {
// MinIO server running locally. // MinIO server running locally.
c.setHost("local", hostConfigV9{ c.setAlias("local", aliasConfigV10{
URL: "http://localhost:9000", URL: "http://localhost:9000",
AccessKey: "", AccessKey: "",
SecretKey: "", SecretKey: "",
API: "S3v4", API: "S3v4",
Lookup: "auto", Path: "auto",
}) })
// Amazon S3 cloud storage service. // Amazon S3 cloud storage service.
c.setHost("s3", hostConfigV9{ c.setAlias("s3", aliasConfigV10{
URL: "https://s3.amazonaws.com", URL: "https://s3.amazonaws.com",
AccessKey: defaultAccessKey, AccessKey: defaultAccessKey,
SecretKey: defaultSecretKey, SecretKey: defaultSecretKey,
API: "S3v4", API: "S3v4",
Lookup: "dns", Path: "dns",
}) })
// Google cloud storage service. // Google cloud storage service.
c.setHost("gcs", hostConfigV9{ c.setAlias("gcs", aliasConfigV10{
URL: "https://storage.googleapis.com", URL: "https://storage.googleapis.com",
AccessKey: defaultAccessKey, AccessKey: defaultAccessKey,
SecretKey: defaultSecretKey, SecretKey: defaultSecretKey,
API: "S3v2", API: "S3v2",
Lookup: "dns", Path: "dns",
}) })
// MinIO anonymous server for demo. // MinIO anonymous server for demo.
c.setHost("play", hostConfigV9{ c.setAlias("play", aliasConfigV10{
URL: "https://play.min.io", URL: "https://play.min.io",
AccessKey: "Q3AM3UQ867SPQQA43P2F", AccessKey: "Q3AM3UQ867SPQQA43P2F",
SecretKey: "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", SecretKey: "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
API: "S3v4", API: "S3v4",
Lookup: "auto", Path: "auto",
}) })
} }
// loadConfigV9 - loads a new config. // loadConfigV10 - loads a new config.
func loadConfigV9() (*configV9, *probe.Error) { func loadConfigV10() (*configV10, *probe.Error) {
cfgMutex.RLock() cfgMutex.RLock()
defer cfgMutex.RUnlock() defer cfgMutex.RUnlock()
// If already cached, return the cached value. // If already cached, return the cached value.
if cacheCfgV9 != nil { if cacheCfgV10 != nil {
return cacheCfgV9, nil return cacheCfgV10, nil
} }
if !isMcConfigExists() { if !isMcConfigExists() {
@ -120,7 +120,7 @@ func loadConfigV9() (*configV9, *probe.Error) {
} }
// Initialize a new config loader. // Initialize a new config loader.
qc, e := quick.NewConfig(newConfigV9(), nil) qc, e := quick.NewConfig(newConfigV10(), nil)
if e != nil { if e != nil {
return nil, probe.NewError(e) return nil, probe.NewError(e)
} }
@ -131,27 +131,27 @@ func loadConfigV9() (*configV9, *probe.Error) {
return nil, probe.NewError(e) return nil, probe.NewError(e)
} }
cfgV9 := qc.Data().(*configV9) cfgV10 := qc.Data().(*configV10)
// Cache config. // Cache config.
cacheCfgV9 = cfgV9 cacheCfgV10 = cfgV10
// Success. // Success.
return cfgV9, nil return cfgV10, nil
} }
// saveConfigV8 - saves an updated config. // saveConfigV10 - saves an updated config.
func saveConfigV9(cfgV9 *configV9) *probe.Error { func saveConfigV10(cfgV10 *configV10) *probe.Error {
cfgMutex.Lock() cfgMutex.Lock()
defer cfgMutex.Unlock() defer cfgMutex.Unlock()
qs, e := quick.NewConfig(cfgV9, nil) qs, e := quick.NewConfig(cfgV10, nil)
if e != nil { if e != nil {
return probe.NewError(e) return probe.NewError(e)
} }
// update the cache. // update the cache.
cacheCfgV9 = cfgV9 cacheCfgV10 = cfgV10
e = qs.Save(mustGetMcConfigPath()) e = qs.Save(mustGetMcConfigPath())
if e != nil { if e != nil {

View File

@ -22,7 +22,7 @@ import (
) )
// Check if version of the config is valid // Check if version of the config is valid
func validateConfigVersion(config *configV9) (bool, string) { func validateConfigVersion(config *configV10) (bool, string) {
if config.Version != globalMCConfigVersion { if config.Version != globalMCConfigVersion {
return false, fmt.Sprintf("Config version '%s' does not match mc config version '%s', please update your binary.\n", return false, fmt.Sprintf("Config version '%s' does not match mc config version '%s', please update your binary.\n",
config.Version, globalMCConfigVersion) config.Version, globalMCConfigVersion)
@ -31,7 +31,7 @@ func validateConfigVersion(config *configV9) (bool, string) {
} }
// Verifies the config file of the MinIO Client // Verifies the config file of the MinIO Client
func validateConfigFile(config *configV9) (bool, []string) { func validateConfigFile(config *configV10) (bool, []string) {
ok, err := validateConfigVersion(config) ok, err := validateConfigVersion(config)
var validationSuccessful = true var validationSuccessful = true
var errors []string var errors []string
@ -39,18 +39,18 @@ func validateConfigFile(config *configV9) (bool, []string) {
validationSuccessful = false validationSuccessful = false
errors = append(errors, err) errors = append(errors, err)
} }
hosts := config.Hosts aliases := config.Aliases
for _, hostConfig := range hosts { for _, aliasConfig := range aliases {
hostConfigHealthOk, hostErrors := validateConfigHost(hostConfig) aliasConfigHealthOk, aliasErrors := validateConfigHost(aliasConfig)
if !hostConfigHealthOk { if !aliasConfigHealthOk {
validationSuccessful = false validationSuccessful = false
errors = append(errors, hostErrors...) errors = append(errors, aliasErrors...)
} }
} }
return validationSuccessful, errors return validationSuccessful, errors
} }
func validateConfigHost(host hostConfigV9) (bool, []string) { func validateConfigHost(host aliasConfigV10) (bool, []string) {
var validationSuccessful = true var validationSuccessful = true
var hostErrors []string var hostErrors []string
if !isValidAPI(strings.ToLower(host.API)) { if !isValidAPI(strings.ToLower(host.API)) {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -105,29 +105,29 @@ func mustGetMcConfigPath() string {
return path return path
} }
// newMcConfig - initializes a new version '9' config. // newMcConfig - initializes a new version '10' config.
func newMcConfig() *configV9 { func newMcConfig() *configV10 {
cfg := newConfigV9() cfg := newConfigV10()
cfg.loadDefaults() cfg.loadDefaults()
return cfg return cfg
} }
// loadMcConfigCached - returns loadMcConfig with a closure for config cache. // 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. // Load once and cache in a closure.
cfgCache, err := loadConfigV9() cfgCache, err := loadConfigV10()
// loadMcConfig - reads configuration file and returns config. // loadMcConfig - reads configuration file and returns config.
return func() (*configV9, *probe.Error) { return func() (*configV10, *probe.Error) {
return cfgCache, err return cfgCache, err
} }
} }
// loadMcConfig - returns configuration, initialized later. // 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. // saveMcConfig - saves configuration file and returns error if any.
func saveMcConfig(config *configV9) *probe.Error { func saveMcConfig(config *configV10) *probe.Error {
if config == nil { if config == nil {
return errInvalidArgument().Trace() return errInvalidArgument().Trace()
} }
@ -138,7 +138,7 @@ func saveMcConfig(config *configV9) *probe.Error {
} }
// Save the config. // Save the config.
if err := saveConfigV9(config); err != nil { if err := saveConfigV10(config); err != nil {
return err.Trace(mustGetMcConfigPath()) 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) return regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-_]+$").MatchString(alias)
} }
// getHostConfig retrieves host specific configuration such as access keys, signature type. // getAliasConfig retrieves host specific configuration such as access keys, signature type.
func getHostConfig(alias string) (*hostConfigV9, *probe.Error) { func getAliasConfig(alias string) (*aliasConfigV10, *probe.Error) {
mcCfg, err := loadMcConfig() mcCfg, err := loadMcConfig()
if err != nil { if err != nil {
return nil, err.Trace(alias) return nil, err.Trace(alias)
} }
// if host is exact return quickly. // if host is exact return quickly.
if _, ok := mcCfg.Hosts[alias]; ok { if _, ok := mcCfg.Aliases[alias]; ok {
hostCfg := mcCfg.Hosts[alias] hostCfg := mcCfg.Aliases[alias]
return &hostCfg, nil 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. // mustGetHostConfig retrieves host specific configuration such as access keys, signature type.
func mustGetHostConfig(alias string) *hostConfigV9 { func mustGetHostConfig(alias string) *aliasConfigV10 {
hostCfg, _ := getHostConfig(alias) aliasCfg, _ := getAliasConfig(alias)
// If alias is not found, // If alias is not found,
// look for it in the environment variable. // look for it in the environment variable.
if hostCfg == nil { if aliasCfg == nil {
if envConfig, ok := os.LookupEnv(mcEnvHostPrefix + alias); ok { 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 { if envConfig, ok := os.LookupEnv(mcEnvHostsDeprecatedPrefix + alias); ok {
errorIf(errInvalidArgument().Trace(mcEnvHostsDeprecatedPrefix+alias), "`MC_HOSTS_<alias>` environment variable is deprecated. Please use `MC_HOST_<alias>` instead for the same functionality.") errorIf(errInvalidArgument().Trace(mcEnvHostsDeprecatedPrefix+alias), "`MC_HOSTS_<alias>` environment variable is deprecated. Please use `MC_HOST_<alias>` instead for the same functionality.")
hostCfg, _ = expandAliasFromEnv(envConfig) aliasCfg, _ = expandAliasFromEnv(envConfig)
} }
} }
return hostCfg return aliasCfg
} }
var ( var (
@ -265,13 +265,13 @@ const (
mcEnvHostsDeprecatedPrefix = "MC_HOSTS_" mcEnvHostsDeprecatedPrefix = "MC_HOSTS_"
) )
func expandAliasFromEnv(envURL string) (*hostConfigV9, *probe.Error) { func expandAliasFromEnv(envURL string) (*aliasConfigV10, *probe.Error) {
u, accessKey, secretKey, sessionToken, err := parseEnvURLStr(envURL) u, accessKey, secretKey, sessionToken, err := parseEnvURLStr(envURL)
if err != nil { if err != nil {
return nil, err.Trace(envURL) return nil, err.Trace(envURL)
} }
return &hostConfigV9{ return &aliasConfigV10{
URL: u.String(), URL: u.String(),
API: "S3v4", API: "S3v4",
AccessKey: accessKey, 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. // 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. // Extract alias from the URL.
alias, path := url2Alias(aliasedURL) alias, path := url2Alias(aliasedURL)
@ -296,22 +296,22 @@ func expandAlias(aliasedURL string) (alias string, urlStr string, hostCfg *hostC
} }
if ok { if ok {
hostCfg, err = expandAliasFromEnv(envConfig) aliasCfg, err = expandAliasFromEnv(envConfig)
if err != nil { if err != nil {
return "", "", nil, err.Trace(aliasedURL) 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. // Find the matching alias entry and expand the URL.
if hostCfg = mustGetHostConfig(alias); hostCfg != nil { if aliasCfg = mustGetHostConfig(alias); aliasCfg != nil {
return alias, urlJoinPath(hostCfg.URL, path), hostCfg, nil return alias, urlJoinPath(aliasCfg.URL, path), aliasCfg, nil
} }
return "", aliasedURL, nil, nil // No matching entry found. Return original URL as is. 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. // mustExpandAlias expands aliased URL if any match is found, returns as is otherwise.
func mustExpandAlias(aliasedURL string) (alias string, urlStr string, hostCfg *hostConfigV9) { func mustExpandAlias(aliasedURL string) (alias string, urlStr string, aliasCfg *aliasConfigV10) {
alias, urlStr, hostCfg, _ = expandAlias(aliasedURL) alias, urlStr, aliasCfg, _ = expandAlias(aliasedURL)
return alias, urlStr, hostCfg return alias, urlStr, aliasCfg
} }

View File

@ -26,7 +26,7 @@ import (
) )
const ( const (
globalMCConfigVersion = "9" globalMCConfigVersion = "10"
globalMCConfigFile = "config.json" globalMCConfigFile = "config.json"
globalMCCertsDir = "certs" globalMCCertsDir = "certs"

View File

@ -319,6 +319,7 @@ func checkUpdate(ctx *cli.Context) {
} }
var appCmds = []cli.Command{ var appCmds = []cli.Command{
aliasCmd,
lsCmd, lsCmd,
mbCmd, mbCmd,
rbCmd, rbCmd,

View File

@ -48,7 +48,7 @@ var errUnrecognizedDiffType = func(diff differType) *probe.Error {
type invalidAliasedURLErr error type invalidAliasedURLErr error
var errInvalidAliasedURL = func(URL string) *probe.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() return probe.NewError(invalidAliasedURLErr(errors.New(msg))).Untrace()
} }

View File

@ -125,7 +125,7 @@ func splitStr(path, sep string, n int) []string {
// NewS3Config simply creates a new Config struct using the passed // NewS3Config simply creates a new Config struct using the passed
// parameters. // 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 // We have a valid alias and hostConfig. We populate the
// credentials from the match found in the config file. // credentials from the match found in the config file.
s3Config := new(Config) s3Config := new(Config)
@ -137,13 +137,13 @@ func NewS3Config(urlStr string, hostCfg *hostConfigV9) *Config {
s3Config.Insecure = globalInsecure s3Config.Insecure = globalInsecure
s3Config.HostURL = urlStr s3Config.HostURL = urlStr
if hostCfg != nil { if aliasCfg != nil {
s3Config.AccessKey = hostCfg.AccessKey s3Config.AccessKey = aliasCfg.AccessKey
s3Config.SecretKey = hostCfg.SecretKey s3Config.SecretKey = aliasCfg.SecretKey
s3Config.SessionToken = hostCfg.SessionToken s3Config.SessionToken = aliasCfg.SessionToken
s3Config.Signature = hostCfg.API s3Config.Signature = aliasCfg.API
} }
s3Config.Lookup = getLookupType(hostCfg.Lookup) s3Config.Lookup = getLookupType(aliasCfg.Path)
return s3Config return s3Config
} }
@ -189,9 +189,9 @@ func isNewer(ti time.Time, newerRef string) bool {
func getLookupType(l string) minio.BucketLookupType { func getLookupType(l string) minio.BucketLookupType {
l = strings.ToLower(l) l = strings.ToLower(l)
switch l { switch l {
case "dns": case "off":
return minio.BucketLookupDNS return minio.BucketLookupDNS
case "path": case "on":
return minio.BucketLookupPath return minio.BucketLookupPath
} }
return minio.BucketLookupAuto return minio.BucketLookupAuto

View File

@ -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). 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 ls list buckets and objects
mb make a bucket mb make a bucket
rb remove a bucket rb remove a bucket
@ -29,7 +30,6 @@ watch listen for object notification events
policy manage anonymous access to buckets and objects policy manage anonymous access to buckets and objects
tag manage tags for bucket(s) and object(s) tag manage tags for bucket(s) and object(s)
admin manage MinIO servers admin manage MinIO servers
config manage MinIO client
update update mc to latest release 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 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) ### Homebrew (macOS)
Install mc packages using [Homebrew](http://brew.sh/) 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 #### Usage
``` ```
mc config host add <ALIAS> <YOUR-S3-ENDPOINT> [YOUR-ACCESS-KEY] [YOUR-SECRET-KEY] [--api API-SIGNATURE] mc alias set <ALIAS> <YOUR-S3-ENDPOINT> [YOUR-ACCESS-KEY] [YOUR-SECRET-KEY] [--api API-SIGNATURE]
``` ```
Keys must be supplied by argument or standard input. 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 ### 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). 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 ### Example - Google Cloud Storage
Get your AccessKeyID and SecretAccessKey by following [Google Credentials Guide](https://cloud.google.com/storage/docs/migrating?hl=en#keys) 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 ### Example - Specify keys using standard input
@ -157,7 +157,7 @@ mc config host add gcs https://storage.googleapis.com BKIKJAA5BMMU2RHO6IBB V8f1
#### Prompt #### 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 Access Key: BKIKJAA5BMMU2RHO6IBB
Enter Secret Key: V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12 Enter Secret Key: V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12
``` ```
@ -166,7 +166,7 @@ Enter Secret Key: V7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12
``` ```
echo -e "BKIKJAA5BMMU2RHO6IBB\nV7f1CwQqAcwo80UEIJEjc5gVQUSSx5ohQ9GSrr12" | \ 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 ### 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) | | [**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) | | | [**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) | | | [**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) | | | [**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) | | | [**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) | | | [**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` ### Command `admin`
Please visit [here](https://docs.min.io/docs/minio-admin-complete-guide) for a more comprehensive admin guide. Please visit [here](https://docs.min.io/docs/minio-admin-complete-guide) for a more comprehensive admin guide.
<a name="config"></a> <a name="alias"></a>
### Command `config` ### Command `alias`
`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. `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: USAGE:
mc config host COMMAND [COMMAND FLAGS | -h] [ARGUMENTS...] mc alias COMMAND [COMMAND FLAGS | -h] [ARGUMENTS...]
COMMANDS: COMMANDS:
add, a add a new host to configuration file set, s add a new alias to configuration file
remove, rm remove a host from configuration file remove, rm remove an alias from configuration file
list, ls lists hosts in configuration file list, ls lists aliases in configuration file
FLAGS: FLAGS:
--help, -h show help --help, -h show help
@ -1456,24 +1456,24 @@ FLAGS:
*Example: Manage Config File* *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 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 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
``` ```
<a name="update"></a> <a name="update"></a>

View File

@ -26,37 +26,42 @@ config.json is the configuration file for MinIO Client, it gets generated after
``` ```
cat config.json cat config.json
{ {
"version": "8", "version": "10",
"hosts": { "aliases": {
"XL": { "XL": {
"url": "http://127.0.0.1:9000", "url": "http://127.0.0.1:9000",
"accessKey": "YI7S1CKXB76RGOGT6R8W", "accessKey": "YI7S1CKXB76RGOGT6R8W",
"secretKey": "FJ9PWUVNXGPfiI72WMRFepN3LsFgW3MjsxSALroV", "secretKey": "FJ9PWUVNXGPfiI72WMRFepN3LsFgW3MjsxSALroV",
"api": "S3v4" "api": "S3v4",
"path": "auto"
}, },
"fs": { "fs": {
"url": "http://127.0.0.1:9000", "url": "http://127.0.0.1:9000",
"accessKey": "YI7S1CKXB76RGOGT6R8W", "accessKey": "YI7S1CKXB76RGOGT6R8W",
"secretKey": "FJ9PWUVNXGPfiI72WMRFepN3LsFgW3MjsxSALroV", "secretKey": "FJ9PWUVNXGPfiI72WMRFepN3LsFgW3MjsxSALroV",
"api": "S3v4" "api": "S3v4",
"path": "auto"
}, },
"gcs": { "gcs": {
"url": "https://storage.googleapis.com", "url": "https://storage.googleapis.com",
"accessKey": "YOUR-ACCESS-KEY-HERE", "accessKey": "YOUR-ACCESS-KEY-HERE",
"secretKey": "YOUR-SECRET-KEY-HERE", "secretKey": "YOUR-SECRET-KEY-HERE",
"api": "S3v2" "api": "S3v2",
"path": "auto"
}, },
"play": { "play": {
"url": "https://play.min.io", "url": "https://play.min.io",
"accessKey": "Q3AM3UQ867SPQQA43P2F", "accessKey": "Q3AM3UQ867SPQQA43P2F",
"secretKey": "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG", "secretKey": "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
"api": "S3v4" "api": "S3v4",
"path": "auto"
}, },
"s3": { "s3": {
"url": "https://s3.amazonaws.com", "url": "https://s3.amazonaws.com",
"accessKey": "YOUR-ACCESS-KEY-HERE", "accessKey": "YOUR-ACCESS-KEY-HERE",
"secretKey": "YOUR-SECRET-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. ``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`` #### ``config.json.old``
This file keeps previous config file version details. This file keeps previous config file version details.