func migrateConfigV1ToV101() { if !isMcConfigExists() { return } conf := newConfigV1() config, err := quick.New(conf) if err != nil { console.Fatalln(NewIodine(iodine.New(err, nil))) } config.Load(mustGetMcConfigPath()) conf = config.Data().(*configV1) // version is the same return if conf.Version == mcCurrentConfigVersion { return } conf.Version = mcCurrentConfigVersion localHostConfig := new(hostConfig) localHostConfig.AccessKeyID = "" localHostConfig.SecretAccessKey = "" if _, ok := conf.Hosts["localhost:*"]; !ok { conf.Hosts["localhost:*"] = localHostConfig } if _, ok := conf.Hosts["127.0.0.1:*"]; !ok { conf.Hosts["127.0.0.1:*"] = localHostConfig } newConfig, err := quick.New(conf) if err := newConfig.Save(mustGetMcConfigPath()); err != nil { console.Fatalln(NewIodine(iodine.New(err, nil))) } console.Infof("Successfully migrated %s from version: %s to version: %s\n", mustGetMcConfigPath(), mcPreviousConfigVersion, mcCurrentConfigVersion) }
// fatalIf wrapper function which takes error and selectively prints stack frames if available on debug func fatalIf(err *probe.Error, msg string) { if err == nil { return } if globalJSON { errorMsg := errorMessage{ Message: msg, Type: "fatal", Cause: causeMessage{ Message: err.ToGoError().Error(), Error: err.ToGoError(), }, SysInfo: err.SysInfo, } if globalDebug { errorMsg.CallTrace = err.CallTrace } json, err := json.Marshal(struct { Status string `json:"status"` Error errorMessage `json:"error"` }{ Status: "error", Error: errorMsg, }) if err != nil { console.Fatalln(probe.NewError(err)) } console.Println(string(json)) console.Fatalln() } if !globalDebug { console.Fatalln(fmt.Sprintf("%s %s", msg, err.ToGoError())) } console.Fatalln(fmt.Sprintf("%s %s", msg, err)) }
// fatalIf wrapper function which takes error and selectively prints stack frames if available on debug func fatalIf(err *probe.Error, msg string) { if err == nil { return } if globalJSONFlag { errorMessage := ErrorMessage{ Message: msg, Type: "fatal", Cause: err.ToGoError(), SysInfo: err.SysInfo, } if globalDebugFlag { errorMessage.CallTrace = err.CallTrace } json, err := json.Marshal(struct { Error ErrorMessage `json:"error"` }{ Error: errorMessage, }) if err != nil { console.Fatalln(probe.NewError(err)) } console.Println(string(json)) os.Exit(1) } if !globalDebugFlag { console.Fatalln(fmt.Sprintf("%s %s", msg, err.ToGoError())) } console.Fatalln(fmt.Sprintf("%s %s", msg, err)) }
// check if minimum Go version is met. func checkGoVersion() { runtimeVersion := runtime.Version() // Checking version is always successful with go tip if strings.HasPrefix(runtimeVersion, "devel") { return } // Parsing golang version curVersion, e := version.NewVersion(runtimeVersion[2:]) if e != nil { console.Fatalln("Unable to determine current go version.", e) } // Prepare version constraint. constraints, e := version.NewConstraint(minGoVersion) if e != nil { console.Fatalln("Unable to check go version.") } // Check for minimum version. if !constraints.Check(curVersion) { console.Fatalln(fmt.Sprintf("Please recompile Minio with Golang version %s.", minGoVersion)) } }
// runMakeBucketCmd is the handler for mc mb command func runMakeBucketCmd(ctx *cli.Context) { if !ctx.Args().Present() || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "mb", 1) // last argument is exit code } if !isMcConfigExists() { console.Fatalf("Please run \"mc config generate\". %s\n", errNotConfigured{}) } config := mustGetMcConfig() for _, arg := range ctx.Args() { targetURL, err := getExpandedURL(arg, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", arg, err) } } msg, err := doMakeBucketCmd(targetURL) if err != nil { console.Fatalln(msg) } console.Infoln(msg) } }
func registerApp() *cli.App { // Register all commands. registerCommand(serverCmd) registerCommand(versionCmd) registerCommand(updateCmd) registerCommand(controlCmd) // Set up app. app := cli.NewApp() app.Name = "Minio" app.Author = "Minio.io" app.Usage = "Cloud Storage Server." app.Description = `Minio is an Amazon S3 compatible object storage server. Use it to store photos, videos, VMs, containers, log files, or any blob of data as objects.` app.Flags = globalFlags app.Commands = commands app.CustomAppHelpTemplate = minioHelpTemplate app.CommandNotFound = func(ctx *cli.Context, command string) { msg := fmt.Sprintf("‘%s’ is not a minio sub-command. See ‘minio --help’.", command) closestCommands := findClosestCommands(command) if len(closestCommands) > 0 { msg += fmt.Sprintf("\n\nDid you mean one of these?\n") for _, cmd := range closestCommands { msg += fmt.Sprintf(" ‘%s’\n", cmd) } } console.Fatalln(msg) } return app }
func runCatCmd(ctx *cli.Context) { if !ctx.Args().Present() || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cat", 1) // last argument is exit code } if !isMcConfigExists() { console.Fatalf("Please run \"mc config generate\". %s\n", errNotConfigured{}) } config := mustGetMcConfig() // Convert arguments to URLs: expand alias, fix format... for _, arg := range ctx.Args() { sourceURL, err := getExpandedURL(arg, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", arg, err) } } errorMsg, err := doCatCmd(sourceURL) if err != nil { console.Fatalln(errorMsg) } } }
func runAccessCmd(ctx *cli.Context) { if !ctx.Args().Present() || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "access", 1) // last argument is exit code } config := mustGetMcConfig() acl := bucketACL(ctx.Args().First()) if !acl.isValidBucketACL() { console.Fatalf("Valid types are [private, public, readonly]. %s\n", errInvalidACL{acl: acl.String()}) } for _, arg := range ctx.Args().Tail() { targetURL, err := getExpandedURL(arg, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", arg, err) } } msg, err := doUpdateAccessCmd(targetURL, acl) if err != nil { console.Fatalln(msg) } console.Infoln(msg) } }
// checkCastSyntax(URLs []string) func checkCastSyntax(ctx *cli.Context) { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cast", 1) // last argument is exit code. } // extract URLs. URLs, err := args2URLs(ctx.Args()) if err != nil { console.Fatalf("One or more unknown URL types found %s. %s\n", ctx.Args(), NewIodine(iodine.New(err, nil))) } srcURL := URLs[0] tgtURLs := URLs[1:] /****** Generic rules *******/ // Source cannot be a folder (except when recursive) if !isURLRecursive(srcURL) { _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, NewIodine(iodine.New(err, nil))) } if !srcContent.Type.IsRegular() { if srcContent.Type.IsDir() { console.Fatalf("Source ‘%s’ is a folder. Please use ‘%s...’ to recursively copy this folder and its contents.\n", srcURL, srcURL) } console.Fatalf("Source ‘%s’ is not a regular file.\n", srcURL) } } // Recursive URLs are not allowed in target. for _, tgtURL := range tgtURLs { if isURLRecursive(tgtURL) { console.Fatalf("Target ‘%s’ cannot be recursive. %s\n", tgtURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } } for _, tgtURL := range tgtURLs { url, err := client.Parse(tgtURL) if err != nil { console.Fatalf("Unable to parse target ‘%s’ argument. %s\n", tgtURL, NewIodine(iodine.New(err, nil))) } if url.Host != "" { if url.Path == string(url.Separator) { console.Fatalf("Bucket creation detected for %s, cloud storage URL's should use ‘mc mb’ to create buckets\n", tgtURL) } } } switch guessCastURLType(srcURL, tgtURLs) { case castURLsTypeA: // File -> File. checkCastSyntaxTypeA(srcURL, tgtURLs) case castURLsTypeB: // File -> Folder. checkCastSyntaxTypeB(srcURL, tgtURLs) case castURLsTypeC: // Folder -> Folder. checkCastSyntaxTypeC(srcURL, tgtURLs) default: console.Fatalln("Invalid arguments. Unable to determine how to cast. Please report this issue at https://github.com/minio/mc/issues") } }
// check if minimum Go version is met. func checkGoVersion() { // Current version. curVersion, e := version.NewVersion(runtime.Version()[2:]) if e != nil { console.Fatalln("Unable to determine current go version.", e) } // Prepare version constraint. constraints, e := version.NewConstraint(minGoVersion) if e != nil { console.Fatalln("Unable to check go version.") } // Check for minimum version. if !constraints.Check(curVersion) { console.Fatalln(fmt.Sprintf("Please recompile Minio with Golang version %s.", minGoVersion)) } }
// Verify main command syntax. func checkMainSyntax(c *cli.Context) { configPath, err := getConfigPath() if err != nil { console.Fatalf("Unable to obtain user's home directory. \nError: %s\n", err) } if configPath == "" { console.Fatalln("Config folder cannot be empty, please specify --config-dir <foldername>.") } }
// checkCastSyntax(URLs []string) func checkCastSyntax(ctx *cli.Context) { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cast", 1) // last argument is exit code. } // extract URLs. URLs, err := args2URLs(ctx.Args()) if err != nil { console.Fatalf("One or more unknown URL types found %s. %s\n", ctx.Args(), iodine.New(err, nil)) } srcURL := URLs[0] tgtURLs := URLs[1:] /****** Generic rules *******/ // Source cannot be a directory (except when recursive) if !isURLRecursive(srcURL) { _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, iodine.New(err, nil)) } if !srcContent.Type.IsRegular() { if srcContent.Type.IsDir() { console.Fatalf("Source ‘%s’ is a directory. Please use ‘%s...’ to recursively copy this directory and its contents.\n", srcURL, srcURL) } console.Fatalf("Source ‘%s’ is not a regular file.\n", srcURL) } } // Recursive URLs are not allowed in target. for _, tgtURL := range tgtURLs { if isURLRecursive(tgtURL) { console.Fatalf("Target ‘%s’ cannot be recursive. %s\n", tgtURL, iodine.New(err, nil)) } } switch guessCastURLType(srcURL, tgtURLs) { case castURLsTypeA: // Source is already a regular file. // case castURLsTypeB: // Source is already a regular file. // case castURLsTypeC: srcURL = stripRecursiveURL(srcURL) _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, iodine.New(err, nil)) } if srcContent.Type.IsRegular() { // Ellipses is supported only for directories. console.Fatalf("Source ‘%s’ is not a directory. %s\n", stripRecursiveURL(srcURL), iodine.New(err, nil)) } default: console.Fatalln("Invalid arguments. Unable to determine how to cast. Please report this issue at https://github.com/minio/mc/issues") } }
// runUpdateCmd - func runUpdateCmd(ctx *cli.Context) { if ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "update", 1) // last argument is exit code } msg, err := doUpdateCheck() if err != nil { console.Fatalln(msg) } // no msg do not print one if msg != "" { console.Infoln(msg) } }
func firstTimeRun() { if !isMcConfigExists() { if err := createMcConfigDir(); err != nil { console.Fatalf("Unable to create ‘mc’ config folder. %s\n", err) } config, err := newConfig() if err != nil { console.Fatalln(NewIodine(iodine.New(err, nil))) } err = writeConfig(config) if err != nil { console.Fatalln(NewIodine(iodine.New(err, nil))) } console.Infoln("Configuration written to [" + mustGetMcConfigPath() + "]. Please update your access credentials.") } if !isSessionDirExists() { if err := createSessionDir(); err != nil { console.Fatalf("Unable to create ‘mc’ session folder. %s\n", err) } } checkGolangVersion() }
// runUpdateCmd - func runUpdateCmd(ctx *cli.Context) { if ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "update", 1) // last argument is exit code } if !isMcConfigExists() { console.Fatalf("Please run \"mc config generate\". %s\n", NewIodine(iodine.New(errNotConfigured{}, nil))) } msg, err := doUpdateCheck() if err != nil { console.Fatalln(msg) } // no msg do not print one if msg != "" { console.Infoln(msg) } }
// runConfigCmd is the handle for "mc config" sub-command func runConfigCmd(ctx *cli.Context) { // show help if nothing is set if !ctx.Args().Present() || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "config", 1) // last argument is exit code } arg := ctx.Args().First() tailArgs := ctx.Args().Tail() if len(tailArgs) > 2 { console.Fatalf("Incorrect number of arguments, please use \"mc config help\". %s", errInvalidArgument{}) } msg, err := doConfig(arg, tailArgs) if err != nil { console.Fatalln(msg) } console.Infoln(msg) }
// NOTE: All the parse rules should reduced to A: Copy(Source, Target). // // * VALID RULES // ======================= // A: copy(f, f) -> copy(f, f) // B: copy(f, d) -> copy(f, d/f) -> A // C: copy(d1..., d2) -> []copy(d1/f, d1/d2/f) -> []A // // * INVALID RULES // ========================= // A: copy(d, *) // B: copy(d..., f) // C: copy(*, d...) // func checkCopySyntax(ctx *cli.Context) { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cp", 1) // last argument is exit code. } // extract URLs. URLs, err := args2URLs(ctx.Args()) if err != nil { console.Fatalf("One or more unknown URL types found %s. %s\n", ctx.Args(), iodine.New(err, nil)) } srcURLs := URLs[:len(URLs)-1] tgtURL := URLs[len(URLs)-1] /****** Generic rules *******/ // Recursive URLs are not allowed in target. if isURLRecursive(tgtURL) { console.Fatalf("Target ‘%s’ cannot be recursive. %s\n", tgtURL, iodine.New(err, nil)) } switch guessCopyURLType(srcURLs, tgtURL) { case copyURLsTypeA: // Source is already a regular file. // no verification needed, pass through case copyURLsTypeB: // Source is already a regular file. // no verification needed, pass through case copyURLsTypeC: for _, srcURL := range srcURLs { srcURL = stripRecursiveURL(srcURL) _, srcContent, err := url2Stat(srcURL) // Source exist?. if err != nil { console.Fatalf("Unable to stat source ‘%s’. %s\n", srcURL, iodine.New(err, nil)) } if srcContent.Type.IsRegular() { // Ellipses is supported only for directories. console.Fatalf("Source ‘%s’ is not a directory. %s\n", stripRecursiveURL(srcURL), iodine.New(err, nil)) } } case copyURLsTypeD: // only verify if target is a valid directory and exists if !isTargetURLDir(tgtURL) { console.Fatalf("Target ‘%s’ should be a directory and exist, when we have a mixture of files and folders in source\n", tgtURL) } default: console.Fatalln("Invalid arguments. Unable to determine how to copy. Please report this issue at https://github.com/minio/mc/issues") } }
// runDiffCmd - is a handler for mc diff command func runDiffCmd(ctx *cli.Context) { if len(ctx.Args()) != 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "diff", 1) // last argument is exit code } if !isMcConfigExists() { console.Fatalf("Please run \"mc config generate\". %s\n", errNotConfigured{}) } config := mustGetMcConfig() firstURL := ctx.Args().First() secondURL := ctx.Args()[1] var err error firstURL, err = getExpandedURL(firstURL, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", firstURL, err) } } secondURL, err = getExpandedURL(secondURL, config.Aliases) if err != nil { switch e := iodine.ToError(err).(type) { case errUnsupportedScheme: console.Fatalf("Unknown type of URL %s. %s\n", e.url, err) default: console.Fatalf("Unable to parse argument %s. %s\n", secondURL, err) } } if isURLRecursive(secondURL) { console.Fatalf("Second URL cannot be recursive. %s\n", errInvalidArgument{}) } newFirstURL := stripRecursiveURL(firstURL) for diff := range doDiffCmd(newFirstURL, secondURL, isURLRecursive(firstURL)) { if diff.err != nil { console.Fatalln(diff.message) } console.Infoln(diff.message) } }
// NOTE: All the parse rules should reduced to A: Copy(Source, Target). // // * VALID RULES // ======================= // A: copy(f, f) -> copy(f, f) // B: copy(f, d) -> copy(f, d/f) -> A // C: copy(d1..., d2) -> []copy(d1/f, d2/d1/f) -> []A // D: copy([]{d1... | f}, d2) -> []{copy(d1/f, d2/d1/f) | copy(f, d2/f )} -> []A // // * INVALID RULES // ========================= // A: copy(d, *) // B: copy(d..., f) // C: copy(*, d...) // func checkCopySyntax(ctx *cli.Context) { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "cp", 1) // last argument is exit code. } // extract URLs. URLs, err := args2URLs(ctx.Args()) if err != nil { console.Fatalf("One or more unknown URL types found %s. %s\n", ctx.Args(), NewIodine(iodine.New(err, nil))) } srcURLs := URLs[:len(URLs)-1] tgtURL := URLs[len(URLs)-1] /****** Generic rules *******/ // Recursive URLs are not allowed in target. if isURLRecursive(tgtURL) { console.Fatalf("Recursive option is not supported for target ‘%s’ argument. %s\n", tgtURL, NewIodine(iodine.New(errInvalidArgument{}, nil))) } url, err := client.Parse(tgtURL) if err != nil { console.Fatalf("Unable to parse target ‘%s’ argument. %s\n", tgtURL, NewIodine(iodine.New(err, nil))) } if url.Host != "" { if url.Path == string(url.Separator) { console.Fatalf("Bucket creation detected for %s, cloud storage URL's should use ‘mc mb’ to create buckets\n", tgtURL) } } switch guessCopyURLType(srcURLs, tgtURL) { case copyURLsTypeA: // File -> File. checkCopySyntaxTypeA(srcURLs, tgtURL) case copyURLsTypeB: // File -> Folder. checkCopySyntaxTypeB(srcURLs, tgtURL) case copyURLsTypeC: // Folder... -> Folder. checkCopySyntaxTypeC(srcURLs, tgtURL) case copyURLsTypeD: // File | Folder... -> Folder. checkCopySyntaxTypeD(srcURLs, tgtURL) default: console.Fatalln("Invalid arguments. Unable to determine how to copy. Please report this issue at https://github.com/minio/mc/issues") } }
// Check for sane config environment early on and gracefully report. func checkConfig() { // Refresh the config once. loadMcConfig = loadMcConfigFactory() // Ensures config file is sane. config, err := loadMcConfig() // Verify if the path is accesible before validating the config fatalIf(err.Trace(mustGetMcConfigPath()), "Unable to access configuration file.") // Validate and print error messges ok, errMsgs := validateConfigFile(config) if !ok { var errorMsg bytes.Buffer for index, errMsg := range errMsgs { // Print atmost 10 errors if index > 10 { break } errorMsg.WriteString(errMsg + "\n") } console.Fatalln(errorMsg.String()) } }
func runSessionCmd(ctx *cli.Context) { if len(ctx.Args()) < 1 || ctx.Args().First() == "help" { cli.ShowCommandHelpAndExit(ctx, "session", 1) // last argument is exit code } if strings.TrimSpace(ctx.Args().First()) == "" { cli.ShowCommandHelpAndExit(ctx, "session", 1) // last argument is exit code } if !isSessionDirExists() { if err := createSessionDir(); err != nil { console.Fatalf("Unable to create session folder. %s\n", err) } } switch strings.TrimSpace(ctx.Args().First()) { // list resumable sessions case "list": err := listSessions() if err != nil { console.Fatalln(err) } case "resume": if len(ctx.Args().Tail()) != 1 { cli.ShowCommandHelpAndExit(ctx, "session", 1) // last argument is exit code } if strings.TrimSpace(ctx.Args().Tail().First()) == "" { cli.ShowCommandHelpAndExit(ctx, "session", 1) // last argument is exit code } sid := strings.TrimSpace(ctx.Args().Tail().First()) _, err := os.Stat(getSessionFile(sid)) if err != nil { console.Fatalln(errInvalidSessionID{id: sid}) } s, err := loadSessionV2(sid) if err != nil { console.Fatalln(errInvalidSessionID{id: sid}) } // extra check for testing purposes if s == nil { return } savedCwd, err := os.Getwd() if err != nil { console.Fatalln("Unable to verify your current working folder. %s\n", err) } if s.Header.RootPath != "" { // chdir to RootPath os.Chdir(s.Header.RootPath) } sessionExecute(s) err = s.Close() if err != nil { console.Fatalf("Unable to close session file properly. %s\n", err) } err = s.Delete() if err != nil { console.Fatalf("Unable to clear session files properly. %s\n", err) } // change dir back os.Chdir(savedCwd) // purge a requested pending session, if "*" purge everything case "clear": if len(ctx.Args().Tail()) != 1 { cli.ShowCommandHelpAndExit(ctx, "session", 1) // last argument is exit code } if strings.TrimSpace(ctx.Args().Tail().First()) == "" { cli.ShowCommandHelpAndExit(ctx, "session", 1) // last argument is exit code } clearSession(strings.TrimSpace(ctx.Args().Tail().First())) default: cli.ShowCommandHelpAndExit(ctx, "session", 1) // last argument is exit code } }
// Close a session and exit. func (s sessionV8) CloseAndDie() { s.Close() console.Fatalln("Session safely terminated. To resume session ‘mc session resume " + s.SessionID + "’") }