diff --git a/cli/cmd/validargs.go b/cli/cmd/validargs.go index 660f6c229..e304530b6 100644 --- a/cli/cmd/validargs.go +++ b/cli/cmd/validargs.go @@ -31,19 +31,29 @@ func isIntGreaterArg(arg int, i int) cobra.PositionalArgs { }) } -// isValidAWSCoordinatorCount checks if argument at position arg is an integer exactly 1. -func isValidAWSCoordinatorCount(arg int) cobra.PositionalArgs { - return cobra.MatchAll(isIntArg(arg), func(cmd *cobra.Command, args []string) error { - if v, _ := strconv.Atoi(args[arg]); v != 1 { - return fmt.Errorf("argument %d is %d, that is not a valid coordinator count for AWS, currently the only supported coordinator count is 1", arg, v) +// isValidAWSCoordinatorCount checks additional conditions for the AWS coordinator count. +func isValidAWSCoordinatorCount(coordCountPos, providerPos int) cobra.PositionalArgs { + return func(cmd *cobra.Command, args []string) error { + if strings.ToLower(args[providerPos]) != "aws" { + return nil + } + v, err := strconv.Atoi(args[coordCountPos]) + if err != nil { + return fmt.Errorf("argument %d must be an integer", coordCountPos) + } + if v != 1 { + return fmt.Errorf( + "argument %d is %d, invalid coordinator count for AWS, has to be 1", + coordCountPos, v, + ) } return nil - }) + } } // isIntGreaterZeroArg checks if argument at position arg is a positive non zero integer. func isIntGreaterZeroArg(arg int) cobra.PositionalArgs { - return cobra.MatchAll(isIntGreaterArg(arg, 0)) + return isIntGreaterArg(arg, 0) } // isEC2InstanceType checks if argument at position arg is a key in m. @@ -78,3 +88,37 @@ func isAzureInstanceType(arg int) cobra.PositionalArgs { return fmt.Errorf("argument %s isn't a valid Azure instance type", args[arg]) } } + +// isInstanceTypeForProvider returns a argument validation function that checks if the argument +// at position typePos is a valid instance type for the cloud provider string at position +// providerPos. +func isInstanceTypeForProvider(typePos, providerPos int) cobra.PositionalArgs { + return func(cmd *cobra.Command, args []string) error { + if len(args) < 2 { + return fmt.Errorf("requires 2 arguments, but only %d are provided", len(args)) + } + if len(args) <= typePos { + return fmt.Errorf( + "%d arguments provided, but index %d of typePos is out of bound", + len(args), typePos, + ) + } + if len(args) <= providerPos { + return fmt.Errorf( + "%d arguments provided, but index %d of providerPos is out of bound", + len(args), providerPos, + ) + } + + switch strings.ToLower(args[providerPos]) { + case "aws": + return isEC2InstanceType(typePos)(cmd, args) + case "gcp": + return isGCPInstanceType(typePos)(cmd, args) + case "azure": + return isAzureInstanceType(typePos)(cmd, args) + default: + return fmt.Errorf("argument %s isn't a valid cloud platform", args[0]) + } + } +} diff --git a/cli/cmd/validargs_test.go b/cli/cmd/validargs_test.go index 74acd9785..e417beb51 100644 --- a/cli/cmd/validargs_test.go +++ b/cli/cmd/validargs_test.go @@ -8,12 +8,6 @@ import ( ) func TestIsIntArg(t *testing.T) { - testCmd := &cobra.Command{ - Use: "test", - Args: isIntArg(0), - Run: func(cmd *cobra.Command, args []string) {}, - } - testCases := map[string]struct { args []string expectErr bool @@ -32,7 +26,11 @@ func TestIsIntArg(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) + + testCmd := &cobra.Command{Args: isIntArg(0)} + err := testCmd.ValidateArgs(tc.args) + if tc.expectErr { assert.Error(err) } else { @@ -43,12 +41,6 @@ func TestIsIntArg(t *testing.T) { } func TestIsIntGreaterArg(t *testing.T) { - testCmd := &cobra.Command{ - Use: "test", - Args: isIntGreaterArg(0, 12), - Run: func(cmd *cobra.Command, args []string) {}, - } - testCases := map[string]struct { args []string expectErr bool @@ -64,7 +56,11 @@ func TestIsIntGreaterArg(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) + + testCmd := &cobra.Command{Args: isIntGreaterArg(0, 12)} + err := testCmd.ValidateArgs(tc.args) + if tc.expectErr { assert.Error(err) } else { @@ -75,12 +71,6 @@ func TestIsIntGreaterArg(t *testing.T) { } func TestIsIntGreaterZeroArg(t *testing.T) { - testCmd := &cobra.Command{ - Use: "test", - Args: isIntGreaterZeroArg(0), - Run: func(cmd *cobra.Command, args []string) {}, - } - testCases := map[string]struct { args []string expectErr bool @@ -96,7 +86,11 @@ func TestIsIntGreaterZeroArg(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) + + testCmd := &cobra.Command{Args: isIntGreaterZeroArg(0)} + err := testCmd.ValidateArgs(tc.args) + if tc.expectErr { assert.Error(err) } else { @@ -107,26 +101,24 @@ func TestIsIntGreaterZeroArg(t *testing.T) { } func TestIsEC2InstanceType(t *testing.T) { - testCmd := &cobra.Command{ - Use: "test", - Args: isEC2InstanceType(0), - Run: func(cmd *cobra.Command, args []string) {}, - } - testCases := map[string]struct { args []string expectErr bool }{ "is instance type 1": {[]string{"4xl"}, false}, "is instance type 2": {[]string{"12xlarge", "something else"}, false}, - "isn't instance type 1": {[]string{"notanInstanceType"}, true}, + "isn't instance type 1": {[]string{"notAnInstanceType"}, true}, "isn't instance type 2": {[]string{"Hello World!"}, true}, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) + + testCmd := &cobra.Command{Args: isEC2InstanceType(0)} + err := testCmd.ValidateArgs(tc.args) + if tc.expectErr { assert.Error(err) } else { @@ -137,26 +129,24 @@ func TestIsEC2InstanceType(t *testing.T) { } func TestIsGCPInstanceType(t *testing.T) { - testCmd := &cobra.Command{ - Use: "test", - Args: isGCPInstanceType(0), - Run: func(cmd *cobra.Command, args []string) {}, - } - testCases := map[string]struct { args []string expectErr bool }{ "is instance type 1": {[]string{"n2d-standard-4"}, false}, "is instance type 2": {[]string{"n2d-standard-16", "something else"}, false}, - "isn't instance type 1": {[]string{"notanInstanceType"}, true}, + "isn't instance type 1": {[]string{"notAnInstanceType"}, true}, "isn't instance type 2": {[]string{"Hello World!"}, true}, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) + + testCmd := &cobra.Command{Args: isGCPInstanceType(0)} + err := testCmd.ValidateArgs(tc.args) + if tc.expectErr { assert.Error(err) } else { @@ -167,26 +157,65 @@ func TestIsGCPInstanceType(t *testing.T) { } func TestIsAzureInstanceType(t *testing.T) { - testCmd := &cobra.Command{ - Use: "test", - Args: isAzureInstanceType(0), - Run: func(cmd *cobra.Command, args []string) {}, - } - testCases := map[string]struct { args []string expectErr bool }{ "is instance type 1": {[]string{"Standard_DC2as_v5"}, false}, "is instance type 2": {[]string{"Standard_DC8as_v5", "something else"}, false}, - "isn't instance type 1": {[]string{"notanInstanceType"}, true}, + "isn't instance type 1": {[]string{"notAnInstanceType"}, true}, "isn't instance type 2": {[]string{"Hello World!"}, true}, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) + + testCmd := &cobra.Command{Args: isAzureInstanceType(0)} + err := testCmd.ValidateArgs(tc.args) + + if tc.expectErr { + assert.Error(err) + } else { + assert.NoError(err) + } + }) + } +} + +func TestIsInstanceTypeForProvider(t *testing.T) { + testCases := map[string]struct { + typePos int + providerPos int + args []string + expectErr bool + }{ + "valid ec2 type 1": {1, 0, []string{"aws", "4xl"}, false}, + "valid ec2 type 2": {1, 0, []string{"aws", "12xlarge", "foo"}, false}, + "valid gcp type 1": {1, 0, []string{"gcp", "n2d-standard-4"}, false}, + "valid gcp type 2": {1, 0, []string{"gcp", "n2d-standard-16", "foo"}, false}, + "valid azure type 1": {1, 0, []string{"azure", "Standard_DC2as_v5"}, false}, + "valid azure type 2": {1, 0, []string{"azure", "Standard_DC8as_v5", "foo"}, false}, + "mixed order 1": {0, 3, []string{"4xl", "", "foo", "aws"}, false}, + "mixed order 2": {2, 1, []string{"", "gcp", "n2d-standard-4", "foo", "bar"}, false}, + "invalid ec2 type": {1, 0, []string{"aws", "foo"}, true}, + "invalid gcp type": {1, 0, []string{"gcp", "foo"}, true}, + "invalid azure type": {1, 0, []string{"azure", "foo"}, true}, + "args to short": {2, 0, []string{"foo"}, true}, + "provider position invalid": {1, 2, []string{"gcp", "n2d-standard-4"}, true}, + "type position invalid": {2, 0, []string{"gcp", "n2d-standard-4"}, true}, + "unknown provider": {1, 0, []string{"foo", "n2d-standard-4"}, true}, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + + testCmd := &cobra.Command{Args: isInstanceTypeForProvider(tc.typePos, tc.providerPos)} + + err := testCmd.ValidateArgs(tc.args) + if tc.expectErr { assert.Error(err) } else {