// Prints print wrapper for Message interface, implementing both JSON() and String() methods func Prints(format string, message Message) { if !globalJSONFlag { console.Printf(format, message.String()) return } console.Printf(format, message.JSON()) }
// Print listen ips. func printListenIPs(tls bool, hosts []string, port string) { for _, host := range hosts { if tls { console.Printf(" https://%s:%s\n", host, port) } else { console.Printf(" http://%s:%s\n", host, port) } } }
// Prints retry message upon a specific retry count. func printRetryMsg(sErrs []error, storageDisks []StorageAPI) { for i, sErr := range sErrs { switch sErr { case errDiskNotFound, errFaultyDisk, errFaultyRemoteDisk: console.Printf("Disk %s is still unreachable, with error %s\n", storageDisks[i], sErr) } } }
func dodiffRecursive(firstClnt, secondClnt client.Client, ch chan DiffMessage) { firstTrie := patricia.NewTrie() secondTrie := patricia.NewTrie() wg := new(sync.WaitGroup) type urlAttr struct { Size int64 Type os.FileMode } wg.Add(1) go func(ch chan<- DiffMessage) { defer wg.Done() for firstContentCh := range firstClnt.List(true) { if firstContentCh.Err != nil { ch <- DiffMessage{ Error: firstContentCh.Err.Trace(firstClnt.URL().String()), } return } firstTrie.Insert(patricia.Prefix(firstContentCh.Content.Name), urlAttr{firstContentCh.Content.Size, firstContentCh.Content.Type}) } }(ch) wg.Add(1) go func(ch chan<- DiffMessage) { defer wg.Done() for secondContentCh := range secondClnt.List(true) { if secondContentCh.Err != nil { ch <- DiffMessage{ Error: secondContentCh.Err.Trace(secondClnt.URL().String()), } return } secondTrie.Insert(patricia.Prefix(secondContentCh.Content.Name), urlAttr{secondContentCh.Content.Size, secondContentCh.Content.Type}) } }(ch) doneCh := make(chan struct{}) defer close(doneCh) go func(doneCh <-chan struct{}) { cursorCh := cursorAnimate() for { select { case <-time.Tick(100 * time.Millisecond): if !globalQuietFlag && !globalJSONFlag { console.PrintC("\r" + "Scanning.. " + string(<-cursorCh)) } case <-doneCh: return } } }(doneCh) wg.Wait() doneCh <- struct{}{} if !globalQuietFlag && !globalJSONFlag { console.Printf("%c[2K\n", 27) console.Printf("%c[A", 27) } matchNameCh := make(chan string, 10000) go func(matchNameCh chan<- string) { itemFunc := func(prefix patricia.Prefix, item patricia.Item) error { matchNameCh <- string(prefix) return nil } firstTrie.Visit(itemFunc) defer close(matchNameCh) }(matchNameCh) for matchName := range matchNameCh { firstURLDelimited := firstClnt.URL().String()[:strings.LastIndex(firstClnt.URL().String(), string(firstClnt.URL().Separator))+1] secondURLDelimited := secondClnt.URL().String()[:strings.LastIndex(secondClnt.URL().String(), string(secondClnt.URL().Separator))+1] firstURL := firstURLDelimited + matchName secondURL := secondURLDelimited + matchName if !secondTrie.Match(patricia.Prefix(matchName)) { ch <- DiffMessage{ FirstURL: firstURL, SecondURL: secondURL, Diff: "only-in-first", } } else { firstURLAttr := firstTrie.Get(patricia.Prefix(matchName)).(urlAttr) secondURLAttr := secondTrie.Get(patricia.Prefix(matchName)).(urlAttr) if firstURLAttr.Type.IsRegular() { if !secondURLAttr.Type.IsRegular() { ch <- DiffMessage{ FirstURL: firstURL, SecondURL: secondURL, Diff: "type", } continue } } if firstURLAttr.Type.IsDir() { if !secondURLAttr.Type.IsDir() { ch <- DiffMessage{ FirstURL: firstURL, SecondURL: secondURL, Diff: "type", } continue } } if firstURLAttr.Size != secondURLAttr.Size { ch <- DiffMessage{ FirstURL: firstURL, SecondURL: secondURL, Diff: "size", } } } } }
// Implements a jitter backoff loop for formatting all disks during // initialization of the server. func retryFormattingDisks(firstDisk bool, endpoints []*url.URL, storageDisks []StorageAPI) error { if len(endpoints) == 0 { return errInvalidArgument } if storageDisks == nil { return errInvalidArgument } // Create a done channel to control 'ListObjects' go routine. doneCh := make(chan struct{}, 1) // Indicate to our routine to exit cleanly upon return. defer close(doneCh) // prepare getElapsedTime() to calculate elapsed time since we started trying formatting disks. // All times are rounded to avoid showing milli, micro and nano seconds formatStartTime := time.Now().Round(time.Second) getElapsedTime := func() string { return time.Now().Round(time.Second).Sub(formatStartTime).String() } // Wait on the jitter retry loop. retryTimerCh := newRetryTimer(time.Second, time.Second*30, MaxJitter, doneCh) for { select { case retryCount := <-retryTimerCh: // Attempt to load all `format.json` from all disks. formatConfigs, sErrs := loadAllFormats(storageDisks) if retryCount > 5 { // After 5 retry attempts we start printing actual errors // for disks not being available. printRetryMsg(sErrs, storageDisks) } if len(formatConfigs) == 1 { err := genericFormatCheckFS(formatConfigs[0], sErrs[0]) if err != nil { // For an new directory or existing data. if err == errUnformattedDisk || err == errCorruptedFormat { return initFormatFS(storageDisks[0]) } return err } return nil } // Check if this is a XL or distributed XL, anything > 1 is considered XL backend. // Pre-emptively check if one of the formatted disks // is invalid. This function returns success for the // most part unless one of the formats is not consistent // with expected XL format. For example if a user is trying // to pool FS backend. if err := checkFormatXLValues(formatConfigs); err != nil { return err } switch prepForInitXL(firstDisk, sErrs, len(storageDisks)) { case Abort: return errCorruptedFormat case FormatDisks: console.Eraseline() printFormatMsg(endpoints, storageDisks, printOnceFn()) return initFormatXL(storageDisks) case InitObjectLayer: console.Eraseline() // Validate formats loaded before proceeding forward. err := genericFormatCheckXL(formatConfigs, sErrs) if err == nil { printRegularMsg(endpoints, storageDisks, printOnceFn()) } return err case WaitForHeal: // Validate formats loaded before proceeding forward. err := genericFormatCheckXL(formatConfigs, sErrs) if err == nil { printHealMsg(endpoints, storageDisks, printOnceFn()) } return err case WaitForQuorum: console.Printf( "Initializing data volume. Waiting for minimum %d servers to come online. (elapsed %s)\n", len(storageDisks)/2+1, getElapsedTime(), ) case WaitForConfig: // Print configuration errors. printConfigErrMsg(storageDisks, sErrs, printOnceFn()) case WaitForAll: console.Printf("Initializing data volume for first time. Waiting for other servers to come online (elapsed %s)\n", getElapsedTime()) case WaitForFormatting: console.Printf("Initializing data volume for first time. Waiting for first server to come online (elapsed %s)\n", getElapsedTime()) } case <-globalServiceDoneCh: return errors.New("Initializing data volumes gracefully stopped") } } }
func doCopyCmdSession(session *sessionV2) { trapCh := signalTrap(os.Interrupt, os.Kill) if !session.HasData() { doPrepareCopyURLs(session, trapCh) } var bar barSend if !globalQuietFlag && !globalJSONFlag { // set up progress bar bar = newCpBar() bar.Extend(session.Header.TotalBytes) } // Prepare URL scanner from session data file. scanner := bufio.NewScanner(session.NewDataReader()) // isCopied returns true if an object has been already copied // or not. This is useful when we resume from a session. isCopied := isCopiedFactory(session.Header.LastCopied) wg := new(sync.WaitGroup) // Limit number of copy routines based on available CPU resources. cpQueue := make(chan bool, int(math.Max(float64(runtime.NumCPU())-1, 1))) defer close(cpQueue) // Status channel for receiveing copy return status. statusCh := make(chan copyURLs) // Go routine to monitor doCopy status and signal traps. wg.Add(1) go func() { defer wg.Done() for { select { case cpURLs, ok := <-statusCh: // Receive status. if !ok { // We are done here. Top level function has returned. if !globalQuietFlag && !globalJSONFlag { bar.Finish() } return } if cpURLs.Error == nil { session.Header.LastCopied = cpURLs.SourceContent.Name } else { // Print in new line and adjust to top so that we don't print over the ongoing progress bar if !globalQuietFlag && !globalJSONFlag { console.Printf("%c[2K\n", 27) console.Printf("%c[A", 27) } errorIf(cpURLs.Error.Trace(), fmt.Sprintf("Failed to copy ‘%s’.", cpURLs.SourceContent.Name)) } case <-trapCh: // Receive interrupt notification. // Print in new line and adjust to top so that we don't print over the ongoing progress bar if !globalQuietFlag && !globalJSONFlag { console.Printf("%c[2K\n", 27) console.Printf("%c[A", 27) } session.Close() session.Info() os.Exit(0) } } }() // Go routine to perform concurrently copying. wg.Add(1) go func() { defer wg.Done() copyWg := new(sync.WaitGroup) defer close(statusCh) for scanner.Scan() { var cpURLs copyURLs json.Unmarshal([]byte(scanner.Text()), &cpURLs) if isCopied(cpURLs.SourceContent.Name) { doCopyFake(cpURLs, &bar) } else { // Wait for other copy routines to // complete. We only have limited CPU // and network resources. cpQueue <- true // Account for each copy routines we start. copyWg.Add(1) // Do copying in background concurrently. go doCopy(cpURLs, &bar, cpQueue, copyWg, statusCh) } } copyWg.Wait() }() wg.Wait() }
// doPrepareCopyURLs scans the source URL and prepares a list of objects for copying. func doPrepareCopyURLs(session *sessionV2, trapCh <-chan bool) { // Separate source and target. 'cp' can take only one target, // but any number of sources, even the recursive URLs mixed in-between. sourceURLs := session.Header.CommandArgs[:len(session.Header.CommandArgs)-1] targetURL := session.Header.CommandArgs[len(session.Header.CommandArgs)-1] // Last one is target var totalBytes int64 var totalObjects int // Create a session data file to store the processed URLs. dataFP := session.NewDataWriter() var scanBar scanBarFunc if !globalQuietFlag && !globalJSONFlag { // set up progress bar scanBar = scanBarFactory("") } URLsCh := prepareCopyURLs(sourceURLs, targetURL) done := false for done == false { select { case cpURLs, ok := <-URLsCh: if !ok { // Done with URL prepration done = true break } if cpURLs.Error != nil { // Print in new line and adjust to top so that we don't print over the ongoing scan bar if !globalQuietFlag && !globalJSONFlag { console.Printf("%c[2K\n", 27) console.Printf("%c[A", 27) } errorIf(cpURLs.Error.Trace(), "Unable to prepare URLs for copying.") break } jsonData, err := json.Marshal(cpURLs) if err != nil { session.Close() fatalIf(probe.NewError(err), "Unable to prepare URLs for copying. Error in JSON marshaling.") } fmt.Fprintln(dataFP, string(jsonData)) if !globalQuietFlag && !globalJSONFlag { scanBar(cpURLs.SourceContent.Name) } totalBytes += cpURLs.SourceContent.Size totalObjects++ case <-trapCh: // Print in new line and adjust to top so that we don't print over the ongoing scan bar if !globalQuietFlag && !globalJSONFlag { console.Printf("%c[2K\n", 27) console.Printf("%c[A", 27) } session.Close() // If we are interrupted during the URL scanning, we drop the session. session.Delete() os.Exit(0) } } session.Header.TotalBytes = totalBytes session.Header.TotalObjects = totalObjects session.Save() }
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.") }
// Implements a jitter backoff loop for formatting all disks during // initialization of the server. func retryFormattingDisks(firstDisk bool, endpoints []*url.URL, storageDisks []StorageAPI) error { if len(endpoints) == 0 { return errInvalidArgument } if storageDisks == nil { return errInvalidArgument } // Create a done channel to control 'ListObjects' go routine. doneCh := make(chan struct{}, 1) // Indicate to our routine to exit cleanly upon return. defer close(doneCh) // Wait on the jitter retry loop. retryTimerCh := newRetryTimer(time.Second, time.Second*30, MaxJitter, doneCh) for { select { case retryCount := <-retryTimerCh: // Attempt to load all `format.json` from all disks. formatConfigs, sErrs := loadAllFormats(storageDisks) if retryCount > 5 { // After 5 retry attempts we start printing actual errors // for disks not being available. printRetryMsg(sErrs, storageDisks) } // Check if this is a XL or distributed XL, anything > 1 is considered XL backend. if len(formatConfigs) > 1 { switch prepForInitXL(firstDisk, sErrs, len(storageDisks)) { case Abort: return errCorruptedFormat case FormatDisks: console.Eraseline() printFormatMsg(endpoints, storageDisks, printOnceFn()) return initFormatXL(storageDisks) case InitObjectLayer: console.Eraseline() // Validate formats load before proceeding forward. err := genericFormatCheck(formatConfigs, sErrs) if err == nil { printRegularMsg(endpoints, storageDisks, printOnceFn()) } return err case WaitForHeal: // Validate formats load before proceeding forward. err := genericFormatCheck(formatConfigs, sErrs) if err == nil { printHealMsg(endpoints, storageDisks, printOnceFn()) } return err case WaitForQuorum: console.Printf( "Initializing data volume. Waiting for minimum %d servers to come online.\n", len(storageDisks)/2+1, ) case WaitForConfig: // Print configuration errors. printConfigErrMsg(storageDisks, sErrs, printOnceFn()) case WaitForAll: console.Println("Initializing data volume for first time. Waiting for other servers to come online") case WaitForFormatting: console.Println("Initializing data volume for first time. Waiting for first server to come online") } continue } // else We have FS backend now. Check fs format as well now. if isFormatFound(formatConfigs) { console.Eraseline() // Validate formats load before proceeding forward. return genericFormatCheck(formatConfigs, sErrs) } // else initialize the format for FS. return initFormatFS(storageDisks[0]) case <-globalServiceDoneCh: return errors.New("Initializing data volumes gracefully stopped") } } }
// doPrepareMirrorURLs scans the source URL and prepares a list of objects for mirroring. func doPrepareMirrorURLs(session *sessionV2, trapCh <-chan bool) { sourceURL := session.Header.CommandArgs[0] // first one is source. targetURLs := session.Header.CommandArgs[1:] var totalBytes int64 var totalObjects int // Create a session data file to store the processed URLs. dataFP := session.NewDataWriter() var scanBar scanBarFunc if !globalQuietFlag && !globalJSONFlag { // set up progress bar scanBar = scanBarFactory("") } URLsCh := prepareMirrorURLs(sourceURL, targetURLs) done := false for done == false { select { case sURLs, ok := <-URLsCh: if !ok { // Done with URL prepration done = true break } if sURLs.Error != nil { // Print in new line and adjust to top so that we don't print over the ongoing scan bar if !globalQuietFlag && !globalJSONFlag { console.Printf("%c[2K\n", 27) console.Printf("%c[A", 27) } errorIf(sURLs.Error.Trace(), "Unable to prepare URLs for mirroring.") break } if sURLs.isEmpty() { break } jsonData, err := json.Marshal(sURLs) if err != nil { session.Close() fatalIf(probe.NewError(err), "Unable to marshal URLs into JSON.") } fmt.Fprintln(dataFP, string(jsonData)) if !globalQuietFlag && !globalJSONFlag { scanBar(sURLs.SourceContent.Name) } totalBytes += sURLs.SourceContent.Size totalObjects++ case <-trapCh: // Print in new line and adjust to top so that we don't print over the ongoing scan bar if !globalQuietFlag && !globalJSONFlag { console.Printf("%c[2K\n", 27) console.Printf("%c[A", 27) } session.Close() // If we are interrupted during the URL scanning, we drop the session. session.Delete() os.Exit(0) } } session.Header.TotalBytes = totalBytes session.Header.TotalObjects = totalObjects session.Save() }