// checkShareDownloadSyntax - validate command-line args. func checkShareDownloadSyntax(ctx *cli.Context) { args := ctx.Args() if !args.Present() { cli.ShowCommandHelpAndExit(ctx, "download", 1) // last argument is exit code. } // Parse expiry. expiry := shareDefaultExpiry expireArg := ctx.String("expire") if expireArg != "" { var e error expiry, e = time.ParseDuration(expireArg) fatalIf(probe.NewError(e), "Unable to parse expire=‘"+expireArg+"’.") } // Validate expiry. if expiry.Seconds() < 1 { fatalIf(errDummy().Trace(expiry.String()), "Expiry cannot be lesser than 1 second.") } if expiry.Seconds() > 604800 { fatalIf(errDummy().Trace(expiry.String()), "Expiry cannot be larger than 7 days.") } for _, url := range ctx.Args() { _, _, err := url2Stat(url) fatalIf(err.Trace(url), "Unable to stat ‘"+url+"’.") } }
// main for share upload command. func mainShareUpload(ctx *cli.Context) { // Set global flags from context. setGlobalsFromContext(ctx) // check input arguments. checkShareUploadSyntax(ctx) // Initialize share config folder. initShareConfig() // Additional command speific theme customization. shareSetColor() // Set command flags from context. isRecursive := ctx.Bool("recursive") expireArg := ctx.String("expire") expiry := shareDefaultExpiry contentType := ctx.String("content-type") if expireArg != "" { var e error expiry, e = time.ParseDuration(expireArg) fatalIf(probe.NewError(e), "Unable to parse expire=‘"+expireArg+"’.") } for _, targetURL := range ctx.Args() { err := doShareUploadURL(targetURL, isRecursive, expiry, contentType) fatalIf(err.Trace(targetURL), "Unable to generate curl command for upload ‘"+targetURL+"’.") } }
func mainEventsAdd(ctx *cli.Context) error { console.SetColor("Events", color.New(color.FgGreen, color.Bold)) setGlobalsFromContext(ctx) checkEventsAddSyntax(ctx) args := ctx.Args() path := args[0] arn := args[1] events := strings.Split(ctx.String("events"), ",") prefix := ctx.String("prefix") suffix := ctx.String("suffix") client, err := newClient(path) if err != nil { fatalIf(err.Trace(), "Cannot parse the provided url.") } s3Client, ok := client.(*s3Client) if !ok { fatalIf(errDummy().Trace(), "The provided url doesn't point to a S3 server.") } err = s3Client.AddNotificationConfig(arn, events, prefix, suffix) fatalIf(err, "Cannot enable notification on the specified bucket.") printMsg(eventsAddMessage{ ARN: arn, Events: events, Prefix: prefix, Suffix: suffix, }) return nil }
func TestApp_RunAsSubcommandParseFlags(t *testing.T) { var context *cli.Context a := cli.NewApp() a.Commands = []cli.Command{ { Name: "foo", Action: func(c *cli.Context) { context = c }, Flags: []cli.Flag{ cli.StringFlag{ Name: "lang", Value: "english", Usage: "language for the greeting", }, }, Before: func(_ *cli.Context) error { return nil }, }, } a.Run([]string{"", "foo", "--lang", "spanish", "abcd"}) expect(t, context.Args().Get(0), "abcd") expect(t, context.String("lang"), "spanish") }
// Parse command arguments and set global variables accordingly func setGlobalsFromContext(c *cli.Context) { // Set config dir switch { case c.IsSet("config-dir"): globalConfigDir = c.String("config-dir") case c.GlobalIsSet("config-dir"): globalConfigDir = c.GlobalString("config-dir") } if globalConfigDir == "" { console.Fatalf("Unable to get config file. Config directory is empty.") } // Set global quiet flag. globalQuiet = c.Bool("quiet") || c.GlobalBool("quiet") }
// main for share download. func mainShareDownload(ctx *cli.Context) error { // Set global flags from context. setGlobalsFromContext(ctx) // check input arguments. checkShareDownloadSyntax(ctx) // Initialize share config folder. initShareConfig() // Additional command speific theme customization. shareSetColor() // Set command flags from context. isRecursive := ctx.Bool("recursive") expiry := shareDefaultExpiry if ctx.String("expire") != "" { var e error expiry, e = time.ParseDuration(ctx.String("expire")) fatalIf(probe.NewError(e), "Unable to parse expire=‘"+ctx.String("expire")+"’.") } for _, targetURL := range ctx.Args() { err := doShareDownloadURL(targetURL, isRecursive, expiry) if err != nil { switch err.ToGoError().(type) { case APINotImplemented: fatalIf(err.Trace(), "Unable to share a non S3 url ‘"+targetURL+"’.") default: fatalIf(err.Trace(targetURL), "Unable to share target ‘"+targetURL+"’.") } } } return nil }
// mainMakeBucket is entry point for mb command. func mainMakeBucket(ctx *cli.Context) error { // Set global flags from context. setGlobalsFromContext(ctx) // check 'mb' cli arguments. checkMakeBucketSyntax(ctx) // Additional command speific theme customization. console.SetColor("MakeBucket", color.New(color.FgGreen, color.Bold)) // Save region. region := ctx.String("region") var cErr error for i := range ctx.Args() { targetURL := ctx.Args().Get(i) // Instantiate client for URL. clnt, err := newClient(targetURL) if err != nil { errorIf(err.Trace(targetURL), "Invalid target ‘"+targetURL+"’.") cErr = exitStatus(globalErrorExitStatus) continue } // Make bucket. err = clnt.MakeBucket(region) if err != nil { errorIf(err.Trace(targetURL), "Unable to make bucket ‘"+targetURL+"’.") cErr = exitStatus(globalErrorExitStatus) continue } // Successfully created a bucket. printMsg(makeBucketMessage{Status: "success", Bucket: targetURL}) } return cErr }
// checkShareUploadSyntax - validate command-line args. func checkShareUploadSyntax(ctx *cli.Context) { args := ctx.Args() if !args.Present() { cli.ShowCommandHelpAndExit(ctx, "upload", 1) // last argument is exit code. } // Set command flags from context. isRecursive := ctx.Bool("recursive") expireArg := ctx.String("expire") // Parse expiry. expiry := shareDefaultExpiry if expireArg != "" { var e error expiry, e = time.ParseDuration(expireArg) fatalIf(probe.NewError(e), "Unable to parse expire=‘"+expireArg+"’.") } // Validate expiry. if expiry.Seconds() < 1 { fatalIf(errDummy().Trace(expiry.String()), "Expiry cannot be lesser than 1 second.") } if expiry.Seconds() > 604800 { fatalIf(errDummy().Trace(expiry.String()), "Expiry cannot be larger than 7 days.") } for _, targetURL := range ctx.Args() { url := client.NewURL(targetURL) if strings.HasSuffix(targetURL, string(url.Separator)) && !isRecursive { fatalIf(errInvalidArgument().Trace(targetURL), "Use --recursive option to generate curl command for prefixes.") } } }
// serverMain handler called for 'minio server' command. func serverMain(c *cli.Context) { if !c.Args().Present() || c.Args().First() == "help" { cli.ShowCommandHelpAndExit(c, "server", 1) } // Set global variables after parsing passed arguments setGlobalsFromContext(c) // Initialization routine, such as config loading, enable logging, .. minioInit() // Check for minio updates from dl.minio.io checkUpdate() // Server address. serverAddr := c.String("address") // Check if requested port is available. host, portStr, err := net.SplitHostPort(serverAddr) fatalIf(err, "Unable to parse %s.", serverAddr) if portStr == "0" || portStr == "" { // Port zero or empty means use requested to choose any freely available // port. Avoid this since it won't work with any configured clients, // can lead to serious loss of availability. fatalIf(errInvalidArgument, "Invalid port `%s`, please use `--address` to pick a specific port", portStr) } globalMinioHost = host // Check if requested port is available. fatalIf(checkPortAvailability(portStr), "Port unavailable %s", portStr) globalMinioPort = portStr // Check server syntax and exit in case of errors. // Done after globalMinioHost and globalMinioPort is set as parseStorageEndpoints() // depends on it. checkServerSyntax(c) // Disks to be used in server init. endpoints, err := parseStorageEndpoints(c.Args()) fatalIf(err, "Unable to parse storage endpoints %s", c.Args()) // Should exit gracefully if none of the endpoints passed // as command line args are local to this server. if !isAnyEndpointLocal(endpoints) { fatalIf(errInvalidArgument, "None of the disks passed as command line args are local to this server.") } // Sort endpoints for consistent ordering across multiple // nodes in a distributed setup. This is to avoid format.json // corruption if the disks aren't supplied in the same order // on all nodes. sort.Sort(byHostPath(endpoints)) storageDisks, err := initStorageDisks(endpoints) fatalIf(err, "Unable to initialize storage disk(s).") // Cleanup objects that weren't successfully written into the namespace. fatalIf(houseKeeping(storageDisks), "Unable to purge temporary files.") // Initialize server config. initServerConfig(c) // First disk argument check if it is local. firstDisk := isLocalStorage(endpoints[0]) // Check if endpoints are part of distributed setup. globalIsDistXL = isDistributedSetup(endpoints) // Configure server. srvConfig := serverCmdConfig{ serverAddr: serverAddr, endpoints: endpoints, storageDisks: storageDisks, } // Configure server. handler, err := configureServerHandler(srvConfig) fatalIf(err, "Unable to configure one of server's RPC services.") // Set nodes for dsync for distributed setup. if globalIsDistXL { fatalIf(initDsyncNodes(endpoints), "Unable to initialize distributed locking") } // Initialize name space lock. initNSLock(globalIsDistXL) // Initialize a new HTTP server. apiServer := NewServerMux(serverAddr, handler) // If https. tls := isSSL() // Fetch endpoints which we are going to serve from. endPoints := finalizeEndpoints(tls, apiServer.Server) // Initialize local server address globalMinioAddr = getLocalAddress(srvConfig) // Initialize S3 Peers inter-node communication initGlobalS3Peers(endpoints) // Initialize Admin Peers inter-node communication initGlobalAdminPeers(endpoints) // Start server, automatically configures TLS if certs are available. go func(tls bool) { var lerr error cert, key := "", "" if tls { cert, key = mustGetCertFile(), mustGetKeyFile() } lerr = apiServer.ListenAndServe(cert, key) fatalIf(lerr, "Failed to start minio server.") }(tls) // Wait for formatting of disks. formattedDisks, err := waitForFormatDisks(firstDisk, endpoints, storageDisks) fatalIf(err, "formatting storage disks failed") // Once formatted, initialize object layer. newObject, err := newObjectLayer(formattedDisks) fatalIf(err, "intializing object layer failed") globalObjLayerMutex.Lock() globalObjectAPI = newObject globalObjLayerMutex.Unlock() // Prints the formatted startup message once object layer is initialized. printStartupMessage(endPoints) // Waits on the server. <-globalServiceDoneCh }
// Make sure all the command line parameters are OK and exit in case of invalid parameters. func checkServerSyntax(c *cli.Context) { serverAddr := c.String("address") host, portStr, err := net.SplitHostPort(serverAddr) fatalIf(err, "Unable to parse %s.", serverAddr) // Verify syntax for all the XL disks. disks := c.Args() endpoints, err := parseStorageEndpoints(disks) fatalIf(err, "Unable to parse storage endpoints %s", strings.Join(disks, " ")) // Validate if endpoints follow the expected syntax. err = checkEndpointsSyntax(endpoints, disks) fatalIf(err, "Invalid endpoints found %s", strings.Join(disks, " ")) // Validate for duplicate endpoints are supplied. err = checkDuplicateEndpoints(endpoints) fatalIf(err, "Duplicate entries in %s", strings.Join(disks, " ")) if len(endpoints) > 1 { // Validate if we have sufficient disks for XL setup. err = checkSufficientDisks(endpoints) fatalIf(err, "Invalid number of disks supplied.") } else { // Validate if we have invalid disk for FS setup. if endpoints[0].Host != "" && endpoints[0].Scheme != "" { fatalIf(errInvalidArgument, "%s, FS setup expects a filesystem path", endpoints[0]) } } if !isDistributedSetup(endpoints) { // for FS and singlenode-XL validation is done, return. return } // Rest of the checks applies only to distributed XL setup. if host != "" { // We are here implies --address host:port is passed, hence the user is trying // to run one minio process per export disk. if portStr == "" { fatalIf(errInvalidArgument, "Port missing, Host:Port should be specified for --address") } foundCnt := 0 for _, ep := range endpoints { if ep.Host == serverAddr { foundCnt++ } } if foundCnt == 0 { // --address host:port should be available in the XL disk list. fatalIf(errInvalidArgument, "%s is not available in %s", serverAddr, strings.Join(disks, " ")) } if foundCnt > 1 { // --address host:port should match exactly one entry in the XL disk list. fatalIf(errInvalidArgument, "%s matches % entries in %s", serverAddr, foundCnt, strings.Join(disks, " ")) } } tls := isSSL() for _, ep := range endpoints { if ep.Scheme == "https" && !tls { // Certificates should be provided for https configuration. fatalIf(errInvalidArgument, "Certificates not provided for https") } } }
func serverMain(c *cli.Context) { checkServerSyntax(c) conf, err := initServer() fatalIf(err.Trace(), "Failed to read config for minio.", nil) certFile := c.GlobalString("cert") keyFile := c.GlobalString("key") if (certFile != "" && keyFile == "") || (certFile == "" && keyFile != "") { fatalIf(probe.NewError(errInvalidArgument), "Both certificate and key are required to enable https.", nil) } minFreeDisk, err := parsePercentToInt(c.String("min-free-disk"), 64) fatalIf(err.Trace(c.String("min-free-disk")), "Invalid minium free disk size "+c.String("min-free-disk")+" passed.", nil) path := strings.TrimSpace(c.Args().Last()) // Last argument is always path if _, err := os.Stat(path); err != nil { fatalIf(probe.NewError(err), "Unable to validate the path", nil) } tls := (certFile != "" && keyFile != "") serverConfig := cloudServerConfig{ Address: c.GlobalString("address"), AccessLog: c.GlobalBool("enable-accesslog"), AccessKeyID: conf.Credentials.AccessKeyID, SecretAccessKey: conf.Credentials.SecretAccessKey, Path: path, MinFreeDisk: minFreeDisk, TLS: tls, CertFile: certFile, KeyFile: keyFile, } // configure API server. apiServer, err := configureAPIServer(serverConfig) errorIf(err.Trace(), "Failed to configure API server.", nil) Println("\nMinio Object Storage:") printServerMsg(apiServer) // configure Web server. webServer, err := configureWebServer(serverConfig) errorIf(err.Trace(), "Failed to configure Web server.", nil) Println("\nMinio Browser:") printServerMsg(webServer) Println("\nTo configure Minio Client:") if runtime.GOOS == "windows" { Println(" Download \"mc\" from https://dl.minio.io/client/mc/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/mc.exe") Println(" $ mc.exe config host add myminio http://localhost:9000 " + conf.Credentials.AccessKeyID + " " + conf.Credentials.SecretAccessKey) } else { Println(" $ wget https://dl.minio.io/client/mc/release/" + runtime.GOOS + "-" + runtime.GOARCH + "/mc") Println(" $ chmod 755 mc") Println(" $ ./mc config host add myminio http://localhost:9000 " + conf.Credentials.AccessKeyID + " " + conf.Credentials.SecretAccessKey) } // Start server. err = minhttp.ListenAndServe(apiServer, webServer) errorIf(err.Trace(), "Failed to start the minio server.", nil) }
func mainWatch(ctx *cli.Context) error { console.SetColor("Time", color.New(color.FgGreen)) console.SetColor("Size", color.New(color.FgYellow)) console.SetColor("EventType", color.New(color.FgCyan, color.Bold)) console.SetColor("ObjectName", color.New(color.Bold)) setGlobalsFromContext(ctx) checkWatchSyntax(ctx) args := ctx.Args() path := args[0] prefix := ctx.String("prefix") suffix := ctx.String("suffix") events := strings.Split(ctx.String("events"), ",") recursive := ctx.Bool("recursive") s3Client, pErr := newClient(path) if pErr != nil { fatalIf(pErr.Trace(), "Cannot parse the provided url.") } params := watchParams{ recursive: recursive, accountID: fmt.Sprintf("%d", time.Now().Unix()), events: events, prefix: prefix, suffix: suffix, } // Start watching on events wo, err := s3Client.Watch(params) fatalIf(err, "Cannot watch on the specified bucket.") trapCh := signalTrap(os.Interrupt, syscall.SIGTERM) // Initialize.. waitgroup to track the go-routine. wg := sync.WaitGroup{} // Increment wait group to wait subsequent routine. wg.Add(1) // Start routine to watching on events. go func() { defer wg.Done() // Wait for all events. for { select { case <-trapCh: // Signal received we are done. close(wo.done) return case event, ok := <-wo.Events(): if !ok { return } msg := watchMessage{Event: event} printMsg(msg) case err, ok := <-wo.Errors(): if !ok { return } errorIf(err, "Cannot watch on events.") return } } }() // Wait on the routine to be finished or exit. wg.Wait() return nil }
func serverMain(c *cli.Context) { // check 'server' cli arguments. checkServerSyntax(c) // Initialize server config. initServerConfig(c) // Server address. serverAddress := c.String("address") host, port, _ := net.SplitHostPort(serverAddress) // If port empty, default to port '80' if port == "" { port = "80" // if SSL is enabled, choose port as "443" instead. if isSSL() { port = "443" } } // Check if requested port is available. checkPortAvailability(getPort(net.JoinHostPort(host, port))) // Save all command line args as export paths. exportPaths := c.Args() // Configure server. apiServer := configureServer(serverCmdConfig{ serverAddr: serverAddress, exportPaths: exportPaths, }) // Credential. cred := serverConfig.GetCredential() // Region. region := serverConfig.GetRegion() // Print credentials and region. console.Println("\n" + cred.String() + " " + colorMagenta("Region: ") + colorWhite(region)) hosts, port := getListenIPs(apiServer) // get listen ips and port. tls := apiServer.TLSConfig != nil // 'true' if TLS is enabled. console.Println("\nMinio Object Storage:") // Print api listen ips. printListenIPs(tls, hosts, port) console.Println("\nMinio Browser:") // Print browser listen ips. printListenIPs(tls, hosts, port) console.Println("\nTo configure Minio Client:") // Figure out right endpoint for 'mc'. endpoint := fmt.Sprintf("http://%s:%s", hosts[0], port) if tls { endpoint = fmt.Sprintf("https://%s:%s", hosts[0], port) } // Download 'mc' info. if runtime.GOOS == "windows" { console.Printf(" Download 'mc' from https://dl.minio.io/client/mc/release/%s-%s/mc.exe\n", runtime.GOOS, runtime.GOARCH) console.Printf(" $ mc.exe config host add myminio %s %s %s\n", endpoint, cred.AccessKeyID, cred.SecretAccessKey) } else { console.Printf(" $ wget https://dl.minio.io/client/mc/release/%s-%s/mc\n", runtime.GOOS, runtime.GOARCH) console.Printf(" $ chmod 755 mc\n") console.Printf(" $ ./mc config host add myminio %s %s %s\n", endpoint, cred.AccessKeyID, cred.SecretAccessKey) } // Start server. var err error // Configure TLS if certs are available. if isSSL() { err = apiServer.ListenAndServeTLS(mustGetCertFile(), mustGetKeyFile()) } else { // Fallback to http. err = apiServer.ListenAndServe() } fatalIf(err, "Failed to start minio server.") }
// serverMain handler called for 'minio server' command. func serverMain(c *cli.Context) { // Check 'server' cli arguments. checkServerSyntax(c) // Initialize server config. initServerConfig(c) // If https. tls := isSSL() // Server address. serverAddress := c.String("address") // Check if requested port is available. port := getPort(serverAddress) err := checkPortAvailability(port) fatalIf(err, "Port unavailable %d", port) // Disks to be ignored in server init, to skip format healing. ignoredDisks := strings.Split(c.String("ignore-disks"), ",") // Disks to be used in server init. disks := c.Args() isDist := isDistributedSetup(disks) // Set nodes for dsync for distributed setup. if isDist { err = initDsyncNodes(disks, port) fatalIf(err, "Unable to initialize distributed locking") } // Initialize name space lock. initNSLock(isDist) // Configure server. srvConfig = serverCmdConfig{ serverAddr: serverAddress, disks: disks, ignoredDisks: ignoredDisks, } // Initialize and monitor shutdown signals. err = initGracefulShutdown(os.Exit) fatalIf(err, "Unable to initialize graceful shutdown operation") // Configure server. handler := configureServerHandler(srvConfig) apiServer := NewServerMux(serverAddress, handler) // Fetch endpoints which we are going to serve from. endPoints := finalizeEndpoints(tls, &apiServer.Server) // Register generic callbacks. globalShutdownCBs.AddGenericCB(func() errCode { // apiServer.Stop() return exitSuccess }) // Start server. // Configure TLS if certs are available. wait := make(chan struct{}, 1) go func(tls bool, wait chan<- struct{}) { fatalIf(func() error { defer func() { wait <- struct{}{} }() if tls { return apiServer.ListenAndServeTLS(mustGetCertFile(), mustGetKeyFile()) } // Fallback to http. return apiServer.ListenAndServe() }(), "Failed to start minio server.") }(tls, wait) // Wait for formatting of disks. err = formatDisks(disks, ignoredDisks) if err != nil { // FIXME: call graceful exit errorIf(err, "formatting storage disks failed") return } // Once formatted, initialize object layer. newObject, err := newObjectLayer(disks, ignoredDisks) if err != nil { // FIXME: call graceful exit errorIf(err, "intializing object layer failed") return } // Prints the formatted startup message. printStartupMessage(endPoints) objLayerMutex.Lock() globalObjectAPI = newObject objLayerMutex.Unlock() // Waits on the server. <-wait }