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)
}
// 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()

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");
* 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{
var aliasListCmd = cli.Command{
Name: "list",
ShortName: "ls",
Usage: "list hosts in configuration file",
Action: mainConfigHostList,
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,
})
}
// Sort hosts by alias names lexically.
sort.Sort(byAlias(hosts))
if deprecated {
aliasMsg.Lookup = v.Path
} else {
aliasMsg.Path = v.Path
}
// Display all the hosts.
printHosts(hosts...)
aliases = append(aliases, aliasMsg)
}
// Sort by alias names lexically.
sort.Sort(byAlias(aliases))
return
}

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");
* 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,
// 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,
Flags: globalFlags,
Subcommands: []cli.Command{
configHostAddCmd,
configHostRemoveCmd,
configHostListCmd,
},
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"`
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.")

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");
* 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{
var aliasRemoveCmd = cli.Command{
Name: "remove",
ShortName: "rm",
Usage: "remove a host from configuration file",
Action: mainConfigHostRemove,
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}
}

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");
* 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 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")
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
}

View File

@ -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+"/")
}

View File

@ -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 {

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");
* 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)

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()
// 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())
}

View File

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

View File

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

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");
* 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 {
// configV10 config version.
type configV10 struct {
Version string `json:"version"`
Hosts map[string]hostConfigV9 `json:"hosts"`
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 {

View File

@ -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)) {

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");
* 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_<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 (
@ -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
}

View File

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

View File

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

View File

@ -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()
}

View File

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

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).
```
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 <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.
@ -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.
<a name="config"></a>
### 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.
<a name="alias"></a>
### 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
```
<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
{
"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.