github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/support-perf-object.go (about)

     1  // Copyright (c) 2015-2022 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"os"
    23  	"time"
    24  
    25  	tea "github.com/charmbracelet/bubbletea"
    26  	"github.com/dustin/go-humanize"
    27  	"github.com/minio/cli"
    28  	"github.com/minio/madmin-go/v3"
    29  	"github.com/minio/mc/pkg/probe"
    30  )
    31  
    32  var adminSpeedtestCmd = cli.Command{
    33  	Name:               "speedtest",
    34  	Usage:              "Run server side speed test",
    35  	Action:             mainAdminSpeedtest,
    36  	OnUsageError:       onUsageError,
    37  	Before:             setGlobalsFromContext,
    38  	HideHelpCommand:    true,
    39  	Hidden:             true,
    40  	CustomHelpTemplate: "Please use 'mc support perf'",
    41  }
    42  
    43  func mainAdminSpeedtest(_ *cli.Context) error {
    44  	deprecatedError("mc support perf")
    45  	return nil
    46  }
    47  
    48  func mainAdminSpeedTestObject(ctx *cli.Context, aliasedURL string, outCh chan<- PerfTestResult) error {
    49  	client, perr := newAdminClient(aliasedURL)
    50  	if perr != nil {
    51  		fatalIf(perr.Trace(aliasedURL), "Unable to initialize admin client.")
    52  		return nil
    53  	}
    54  
    55  	ctxt, cancel := context.WithCancel(globalContext)
    56  	defer cancel()
    57  
    58  	duration, e := time.ParseDuration(ctx.String("duration"))
    59  	if e != nil {
    60  		fatalIf(probe.NewError(e), "Unable to parse duration")
    61  		return nil
    62  	}
    63  	if duration <= 0 {
    64  		fatalIf(errInvalidArgument(), "duration cannot be 0 or negative")
    65  		return nil
    66  	}
    67  	size, e := humanize.ParseBytes(ctx.String("size"))
    68  	if e != nil {
    69  		fatalIf(probe.NewError(e), "Unable to parse object size")
    70  		return nil
    71  	}
    72  	if size <= 0 {
    73  		fatalIf(errInvalidArgument(), "size is expected to be more than 0 bytes")
    74  		return nil
    75  	}
    76  	concurrent := ctx.Int("concurrent")
    77  	if concurrent <= 0 {
    78  		fatalIf(errInvalidArgument(), "concurrency cannot be '0' or negative")
    79  		return nil
    80  	}
    81  	globalPerfTestVerbose = ctx.Bool("verbose")
    82  
    83  	// Turn-off autotuning only when "concurrent" is specified
    84  	// in all other scenarios keep auto-tuning on.
    85  	autotune := !ctx.IsSet("concurrent")
    86  
    87  	resultCh, e := client.Speedtest(ctxt, madmin.SpeedtestOpts{
    88  		Size:        int(size),
    89  		Duration:    duration,
    90  		Concurrency: concurrent,
    91  		Autotune:    autotune,
    92  		Bucket:      ctx.String("bucket"), // This is a hidden flag.
    93  		NoClear:     ctx.Bool("noclear"),
    94  	})
    95  
    96  	if globalJSON {
    97  		if e != nil {
    98  			printMsg(convertPerfResult(PerfTestResult{
    99  				Type:  ObjectPerfTest,
   100  				Err:   e.Error(),
   101  				Final: true,
   102  			}))
   103  			return nil
   104  		}
   105  
   106  		var result madmin.SpeedTestResult
   107  		for result = range resultCh {
   108  			if result.Version == "" {
   109  				continue
   110  			}
   111  		}
   112  
   113  		printMsg(convertPerfResult(PerfTestResult{
   114  			Type:         ObjectPerfTest,
   115  			ObjectResult: &result,
   116  			Final:        true,
   117  		}))
   118  
   119  		return nil
   120  	}
   121  
   122  	done := make(chan struct{})
   123  
   124  	p := tea.NewProgram(initSpeedTestUI())
   125  	go func() {
   126  		if _, e := p.Run(); e != nil {
   127  			os.Exit(1)
   128  		}
   129  		close(done)
   130  	}()
   131  
   132  	go func() {
   133  		if e != nil {
   134  			r := PerfTestResult{
   135  				Type:  ObjectPerfTest,
   136  				Err:   e.Error(),
   137  				Final: true,
   138  			}
   139  			p.Send(r)
   140  			if outCh != nil {
   141  				outCh <- r
   142  			}
   143  			return
   144  		}
   145  
   146  		var result madmin.SpeedTestResult
   147  		for result = range resultCh {
   148  			p.Send(PerfTestResult{
   149  				Type:         ObjectPerfTest,
   150  				ObjectResult: &result,
   151  			})
   152  		}
   153  		r := PerfTestResult{
   154  			Type:         ObjectPerfTest,
   155  			ObjectResult: &result,
   156  			Final:        true,
   157  		}
   158  		p.Send(r)
   159  		if outCh != nil {
   160  			outCh <- r
   161  		}
   162  	}()
   163  
   164  	<-done
   165  
   166  	return nil
   167  }