diff --git a/cmd/endpoint.go b/cmd/endpoint.go index 959ed38a5..159fb6b1f 100644 --- a/cmd/endpoint.go +++ b/cmd/endpoint.go @@ -30,6 +30,7 @@ import ( "time" humanize "github.com/dustin/go-humanize" + "github.com/minio/cli" "github.com/minio/minio-go/pkg/set" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/cpu" @@ -391,6 +392,26 @@ func NewEndpointList(args ...string) (endpoints EndpointList, err error) { return endpoints, nil } +func checkEndpointsSubOptimal(ctx *cli.Context, setupType SetupType, endpoints EndpointList) (err error) { + // Validate sub optimal ordering only for distributed setup. + if setupType != DistXLSetupType { + return nil + } + var endpointOrder int + err = fmt.Errorf("Too many disk args are local, input is in sub-optimal order. Please review input args: %s", ctx.Args()) + for _, endpoint := range endpoints { + if endpoint.IsLocal { + endpointOrder++ + } else { + endpointOrder-- + } + if endpointOrder >= 2 { + return err + } + } + return nil +} + // Checks if there are any cross device mounts. func checkCrossDeviceMounts(endpoints EndpointList) (err error) { var absPaths []string diff --git a/cmd/endpoint_test.go b/cmd/endpoint_test.go index 445e7526e..e684447aa 100644 --- a/cmd/endpoint_test.go +++ b/cmd/endpoint_test.go @@ -17,13 +17,53 @@ package cmd import ( + "flag" "fmt" "net/url" "reflect" "strings" "testing" + + "github.com/minio/cli" ) +func TestSubOptimalEndpointInput(t *testing.T) { + args1 := []string{"http://localhost/d1", "http://localhost/d2", "http://localhost/d3", "http://localhost/d4"} + args2 := []string{"http://example.org/d1", "http://example.com/d1", "http://example.net/d1", "http://example.edu/d1"} + + tests := []struct { + setupType SetupType + ctx *cli.Context + endpoints EndpointList + isErr bool + }{ + { + setupType: DistXLSetupType, + ctx: cli.NewContext(cli.NewApp(), flag.NewFlagSet("", flag.ContinueOnError), nil), + endpoints: mustGetNewEndpointList(args1...), + isErr: false, + }, + { + setupType: DistXLSetupType, + ctx: cli.NewContext(cli.NewApp(), flag.NewFlagSet("", flag.ContinueOnError), nil), + endpoints: mustGetNewEndpointList(args2...), + isErr: false, + }, + } + for i, test := range tests { + test := test + t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) { + err := checkEndpointsSubOptimal(test.ctx, test.setupType, test.endpoints) + if test.isErr && err == nil { + t.Error("expected err but found nil") + } + if !test.isErr && err != nil { + t.Errorf("expected err nil but found an err %s", err) + } + }) + } +} + func TestNewEndpoint(t *testing.T) { u1, _ := url.Parse("http://localhost/path") u2, _ := url.Parse("https://example.org/path") diff --git a/cmd/server-main.go b/cmd/server-main.go index abc6a066f..aa6e0a619 100644 --- a/cmd/server-main.go +++ b/cmd/server-main.go @@ -167,6 +167,8 @@ func serverHandleCmdArgs(ctx *cli.Context) { } logger.FatalIf(err, "Invalid command line arguments") + logger.LogIf(context.Background(), checkEndpointsSubOptimal(ctx, setupType, globalEndpoints)) + globalMinioHost, globalMinioPort = mustSplitHostPort(globalMinioAddr) // On macOS, if a process already listens on LOCALIPADDR:PORT, net.Listen() falls back