mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
Replace mutiple args with flags
AB#1955
This commit is contained in:
parent
469b2ff46c
commit
1189078c5a
@ -4,8 +4,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/cli/azure"
|
||||
"github.com/edgelesssys/constellation/cli/cloud/cloudcmd"
|
||||
@ -20,42 +18,39 @@ import (
|
||||
|
||||
func newCreateCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "create {aws|gcp|azure} C_COUNT W_COUNT TYPE",
|
||||
Use: "create {aws|gcp|azure}",
|
||||
Short: "Create instances on a cloud platform for your Constellation.",
|
||||
Long: `Create instances on a cloud platform for your Constellation.
|
||||
A Constellation with C_COUNT control-plane nodes and W_COUNT workers is created.
|
||||
TYPE is the instance type used for all instances.`,
|
||||
Long: "Create instances on a cloud platform for your Constellation.",
|
||||
Args: cobra.MatchAll(
|
||||
cobra.ExactArgs(4),
|
||||
isIntGreaterZeroArg(1),
|
||||
isIntGreaterZeroArg(2),
|
||||
isInstanceTypeForProvider(3, 0),
|
||||
cobra.ExactArgs(1),
|
||||
isCloudProvider(0),
|
||||
warnAWS(0),
|
||||
),
|
||||
ValidArgsFunction: createCompletion,
|
||||
RunE: runCreate,
|
||||
}
|
||||
cmd.Flags().String("name", "constell", "Set this flag to create the Constellation with the specified name.")
|
||||
cmd.Flags().BoolP("yes", "y", false, "Set this flag to create the Constellation without further confirmation.")
|
||||
|
||||
cmd.Flags().String("name", "constell", "Create the Constellation cluster with the specified name.")
|
||||
cmd.Flags().BoolP("yes", "y", false, "Create the Constellation cluster without further confirmation.")
|
||||
cmd.Flags().IntP("control-plane-nodes", "c", 1, "Number of control-plane nodes.")
|
||||
must(cobra.MarkFlagRequired(cmd.Flags(), "control-plane-nodes"))
|
||||
cmd.Flags().IntP("worker-nodes", "w", 1, "Number of worker nodes.")
|
||||
must(cobra.MarkFlagRequired(cmd.Flags(), "worker-nodes"))
|
||||
cmd.Flags().StringP("instance-type", "t", "", "Instance type of cluster nodes.")
|
||||
must(cmd.RegisterFlagCompletionFunc("instance-type", instanceTypeCompletion))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runCreate(cmd *cobra.Command, args []string) error {
|
||||
provider := cloudprovider.FromString(args[0])
|
||||
countCoord, _ := strconv.Atoi(args[1]) // err checked in args validation
|
||||
countNode, _ := strconv.Atoi(args[2]) // err checked in args validation
|
||||
insType := strings.ToLower(args[3])
|
||||
fileHandler := file.NewHandler(afero.NewOsFs())
|
||||
creator := cloudcmd.NewCreator(cmd.OutOrStdout())
|
||||
|
||||
return create(cmd, creator, fileHandler, countCoord, countNode, provider, insType)
|
||||
return create(cmd, creator, fileHandler, provider)
|
||||
}
|
||||
|
||||
func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
|
||||
countCoord, countNode int, provider cloudprovider.Provider, insType string,
|
||||
func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler, provider cloudprovider.Provider,
|
||||
) (retErr error) {
|
||||
flags, err := parseCreateFlags(cmd)
|
||||
flags, err := parseCreateFlags(cmd, provider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -72,8 +67,8 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
|
||||
if !flags.yes {
|
||||
// Ask user to confirm action.
|
||||
cmd.Printf("The following Constellation will be created:\n")
|
||||
cmd.Printf("%d control-planes nodes of type %s will be created.\n", countCoord, insType)
|
||||
cmd.Printf("%d worker nodes of type %s will be created.\n", countNode, insType)
|
||||
cmd.Printf("%d control-planes nodes of type %s will be created.\n", flags.controllerCount, flags.insType)
|
||||
cmd.Printf("%d worker nodes of type %s will be created.\n", flags.workerCount, flags.insType)
|
||||
ok, err := askToConfirm(cmd, "Do you want to create this Constellation?")
|
||||
if err != nil {
|
||||
return err
|
||||
@ -84,7 +79,7 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
|
||||
}
|
||||
}
|
||||
|
||||
state, err := creator.Create(cmd.Context(), provider, config, flags.name, insType, countCoord, countNode)
|
||||
state, err := creator.Create(cmd.Context(), provider, config, flags.name, flags.insType, flags.controllerCount, flags.workerCount)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -98,7 +93,34 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
|
||||
}
|
||||
|
||||
// parseCreateFlags parses the flags of the create command.
|
||||
func parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
|
||||
func parseCreateFlags(cmd *cobra.Command, provider cloudprovider.Provider) (createFlags, error) {
|
||||
controllerCount, err := cmd.Flags().GetInt("control-plane-nodes")
|
||||
if err != nil {
|
||||
return createFlags{}, err
|
||||
}
|
||||
if controllerCount < constants.MinControllerCount {
|
||||
return createFlags{}, fmt.Errorf("number of control-plane nodes must be at least %d", constants.MinControllerCount)
|
||||
}
|
||||
|
||||
workerCount, err := cmd.Flags().GetInt("worker-nodes")
|
||||
if err != nil {
|
||||
return createFlags{}, err
|
||||
}
|
||||
if workerCount < constants.MinWorkerCount {
|
||||
return createFlags{}, fmt.Errorf("number of worker nodes must be at least %d", constants.MinWorkerCount)
|
||||
}
|
||||
|
||||
insType, err := cmd.Flags().GetString("instance-type")
|
||||
if err != nil {
|
||||
return createFlags{}, err
|
||||
}
|
||||
if insType == "" {
|
||||
insType = defaultInstanceType(provider)
|
||||
}
|
||||
if err := validInstanceTypeForProvider(insType, provider); err != nil {
|
||||
return createFlags{}, err
|
||||
}
|
||||
|
||||
name, err := cmd.Flags().GetString("name")
|
||||
if err != nil {
|
||||
return createFlags{}, err
|
||||
@ -109,27 +131,47 @@ func parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
|
||||
constellationNameLength, len(name), name,
|
||||
)
|
||||
}
|
||||
|
||||
yes, err := cmd.Flags().GetBool("yes")
|
||||
if err != nil {
|
||||
return createFlags{}, err
|
||||
}
|
||||
|
||||
devConfigPath, err := cmd.Flags().GetString("dev-config")
|
||||
if err != nil {
|
||||
return createFlags{}, err
|
||||
}
|
||||
|
||||
return createFlags{
|
||||
name: name,
|
||||
devConfigPath: devConfigPath,
|
||||
yes: yes,
|
||||
controllerCount: controllerCount,
|
||||
workerCount: workerCount,
|
||||
insType: insType,
|
||||
name: name,
|
||||
devConfigPath: devConfigPath,
|
||||
yes: yes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// createFlags contains the parsed flags of the create command.
|
||||
type createFlags struct {
|
||||
name string
|
||||
devConfigPath string
|
||||
yes bool
|
||||
controllerCount int
|
||||
workerCount int
|
||||
insType string
|
||||
name string
|
||||
devConfigPath string
|
||||
yes bool
|
||||
}
|
||||
|
||||
// defaultInstanceType returns the default instance type for the given provider.
|
||||
func defaultInstanceType(provider cloudprovider.Provider) string {
|
||||
switch provider {
|
||||
case cloudprovider.GCP:
|
||||
return gcp.InstanceTypes[0]
|
||||
case cloudprovider.Azure:
|
||||
return azure.InstanceTypes[0]
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// checkDirClean checks if files of a previous Constellation are left in the current working dir.
|
||||
@ -153,27 +195,20 @@ func createCompletion(cmd *cobra.Command, args []string, toComplete string) ([]s
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return []string{"aws", "gcp", "azure"}, cobra.ShellCompDirectiveNoFileComp
|
||||
case 1:
|
||||
return []string{}, cobra.ShellCompDirectiveNoFileComp
|
||||
case 2:
|
||||
return []string{}, cobra.ShellCompDirectiveNoFileComp
|
||||
case 3:
|
||||
var instanceTypeList []string
|
||||
switch args[0] {
|
||||
case "aws":
|
||||
instanceTypeList = []string{
|
||||
"4xlarge",
|
||||
"8xlarge",
|
||||
"12xlarge",
|
||||
"16xlarge",
|
||||
"24xlarge",
|
||||
}
|
||||
case "gcp":
|
||||
instanceTypeList = gcp.InstanceTypes
|
||||
case "azure":
|
||||
instanceTypeList = azure.InstanceTypes
|
||||
}
|
||||
return instanceTypeList, cobra.ShellCompDirectiveNoFileComp
|
||||
default:
|
||||
return []string{}, cobra.ShellCompDirectiveError
|
||||
}
|
||||
}
|
||||
|
||||
func instanceTypeCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 1 {
|
||||
return []string{}, cobra.ShellCompDirectiveError
|
||||
}
|
||||
switch args[0] {
|
||||
case "gcp":
|
||||
return gcp.InstanceTypes, cobra.ShellCompDirectiveNoFileComp
|
||||
case "azure":
|
||||
return azure.InstanceTypes, cobra.ShellCompDirectiveNoFileComp
|
||||
default:
|
||||
return []string{}, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package cmd
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -23,29 +24,10 @@ func TestCreateArgumentValidation(t *testing.T) {
|
||||
args []string
|
||||
wantErr bool
|
||||
}{
|
||||
"gcp valid create 1": {[]string{"gcp", "3", "3", "n2d-standard-2"}, false},
|
||||
"gcp valid create 2": {[]string{"gcp", "3", "7", "n2d-standard-16"}, false},
|
||||
"gcp valid create 3": {[]string{"gcp", "1", "2", "n2d-standard-96"}, false},
|
||||
"gcp invalid too many arguments": {[]string{"gcp", "3", "2", "n2d-standard-2", "n2d-standard-2"}, true},
|
||||
"gcp invalid too many arguments 2": {[]string{"gcp", "3", "2", "n2d-standard-2", "2"}, true},
|
||||
"gcp invalid no control planes": {[]string{"gcp", "0", "1", "n2d-standard-2"}, true},
|
||||
"gcp invalid no workers": {[]string{"gcp", "1", "0", "n2d-standard-2"}, true},
|
||||
"gcp invalid first is no int": {[]string{"gcp", "n2d-standard-2", "1", "n2d-standard-2"}, true},
|
||||
"gcp invalid second is no int": {[]string{"gcp", "3", "n2d-standard-2", "n2d-standard-2"}, true},
|
||||
"gcp invalid third is no size": {[]string{"gcp", "2", "2", "2"}, true},
|
||||
"gcp invalid wrong order": {[]string{"gcp", "n2d-standard-2", "2", "2"}, true},
|
||||
"azure valid create 1": {[]string{"azure", "3", "3", "Standard_DC2as_v5"}, false},
|
||||
"azure valid create 2": {[]string{"azure", "3", "7", "Standard_DC4as_v5"}, false},
|
||||
"azure valid create 3": {[]string{"azure", "1", "2", "Standard_DC8as_v5"}, false},
|
||||
"azure invalid to many arguments": {[]string{"azure", "3", "2", "Standard_DC2as_v5", "Standard_DC2as_v5"}, true},
|
||||
"azure invalid to many arguments 2": {[]string{"azure", "3", "2", "Standard_DC2as_v5", "2"}, true},
|
||||
"azure invalid no control planes": {[]string{"azure", "0", "1", "Standard_DC2as_v5"}, true},
|
||||
"azure invalid no workers": {[]string{"azure", "1", "0", "Standard_DC2as_v5"}, true},
|
||||
"azure invalid first is no int": {[]string{"azure", "Standard_DC2as_v5", "1", "Standard_DC2as_v5"}, true},
|
||||
"azure invalid second is no int": {[]string{"azure", "1", "Standard_DC2as_v5", "Standard_DC2as_v5"}, true},
|
||||
"azure invalid third is no size": {[]string{"azure", "2", "2", "2"}, true},
|
||||
"azure invalid wrong order": {[]string{"azure", "Standard_DC2as_v5", "2", "2"}, true},
|
||||
"aws waring": {[]string{"aws", "1", "2", "4xlarge"}, true},
|
||||
"gcp": {[]string{"gcp"}, false},
|
||||
"azure": {[]string{"azure"}, false},
|
||||
"aws waring": {[]string{"aws"}, true},
|
||||
"too many args": {[]string{"gcp", "1", "2"}, true},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
@ -68,15 +50,18 @@ func TestCreate(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
|
||||
testCases := map[string]struct {
|
||||
setupFs func(*require.Assertions) afero.Fs
|
||||
creator *stubCloudCreator
|
||||
provider cloudprovider.Provider
|
||||
yesFlag bool
|
||||
devConfigFlag string
|
||||
nameFlag string
|
||||
stdin string
|
||||
wantErr bool
|
||||
wantAbbort bool
|
||||
setupFs func(*require.Assertions) afero.Fs
|
||||
creator *stubCloudCreator
|
||||
provider cloudprovider.Provider
|
||||
yesFlag bool
|
||||
controllerCountFlag *int
|
||||
workerCountFlag *int
|
||||
insTypeFlag string
|
||||
devConfigFlag string
|
||||
nameFlag string
|
||||
stdin string
|
||||
wantErr bool
|
||||
wantAbbort bool
|
||||
}{
|
||||
"create": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
@ -87,7 +72,7 @@ func TestCreate(t *testing.T) {
|
||||
"interactive": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
creator: &stubCloudCreator{state: testState},
|
||||
provider: cloudprovider.GCP,
|
||||
provider: cloudprovider.Azure,
|
||||
stdin: "yes\n",
|
||||
},
|
||||
"interactive abort": {
|
||||
@ -111,6 +96,43 @@ func TestCreate(t *testing.T) {
|
||||
nameFlag: strings.Repeat("a", constellationNameLength+1),
|
||||
wantErr: true,
|
||||
},
|
||||
"flag control-plane-count invalid": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
creator: &stubCloudCreator{},
|
||||
provider: cloudprovider.GCP,
|
||||
controllerCountFlag: intPtr(0),
|
||||
workerCountFlag: intPtr(3),
|
||||
wantErr: true,
|
||||
},
|
||||
"flag worker-count invalid": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
creator: &stubCloudCreator{},
|
||||
provider: cloudprovider.GCP,
|
||||
controllerCountFlag: intPtr(3),
|
||||
workerCountFlag: intPtr(-1),
|
||||
wantErr: true,
|
||||
},
|
||||
"flag control-plane-count missing": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
creator: &stubCloudCreator{},
|
||||
provider: cloudprovider.GCP,
|
||||
workerCountFlag: intPtr(3),
|
||||
wantErr: true,
|
||||
},
|
||||
"flag worker-count missing": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
creator: &stubCloudCreator{},
|
||||
provider: cloudprovider.GCP,
|
||||
controllerCountFlag: intPtr(3),
|
||||
wantErr: true,
|
||||
},
|
||||
"flag invalid instance-type": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
creator: &stubCloudCreator{},
|
||||
provider: cloudprovider.GCP,
|
||||
insTypeFlag: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"old state in directory": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs {
|
||||
fs := afero.NewMemMapFs()
|
||||
@ -180,10 +202,10 @@ func TestCreate(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
cmd := newCreateCmd()
|
||||
cmd.Flags().String("dev-config", "", "") // register persisten flag manually
|
||||
cmd.SetOut(&bytes.Buffer{})
|
||||
cmd.SetErr(&bytes.Buffer{})
|
||||
cmd.SetIn(bytes.NewBufferString(tc.stdin))
|
||||
cmd.Flags().String("dev-config", "", "") // register persisten flag manually
|
||||
if tc.yesFlag {
|
||||
require.NoError(cmd.Flags().Set("yes", "true"))
|
||||
}
|
||||
@ -193,9 +215,19 @@ func TestCreate(t *testing.T) {
|
||||
if tc.devConfigFlag != "" {
|
||||
require.NoError(cmd.Flags().Set("dev-config", tc.devConfigFlag))
|
||||
}
|
||||
if tc.controllerCountFlag != nil {
|
||||
require.NoError(cmd.Flags().Set("control-plane-nodes", fmt.Sprint(*tc.controllerCountFlag)))
|
||||
}
|
||||
if tc.workerCountFlag != nil {
|
||||
require.NoError(cmd.Flags().Set("worker-nodes", fmt.Sprint(*tc.workerCountFlag)))
|
||||
}
|
||||
if tc.insTypeFlag != "" {
|
||||
require.NoError(cmd.Flags().Set("instance-type", tc.insTypeFlag))
|
||||
}
|
||||
|
||||
fileHandler := file.NewHandler(tc.setupFs(require))
|
||||
|
||||
err := create(cmd, tc.creator, fileHandler, 3, 3, tc.provider, "type")
|
||||
err := create(cmd, tc.creator, fileHandler, tc.provider)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
@ -277,38 +309,7 @@ func TestCreateCompletion(t *testing.T) {
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"second arg": {
|
||||
args: []string{"gcp"},
|
||||
wantResult: []string{},
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"third arg": {
|
||||
args: []string{"gcp", "1"},
|
||||
wantResult: []string{},
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"fourth arg aws": {
|
||||
args: []string{"aws", "1", "2"},
|
||||
wantResult: []string{
|
||||
"4xlarge",
|
||||
"8xlarge",
|
||||
"12xlarge",
|
||||
"16xlarge",
|
||||
"24xlarge",
|
||||
},
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"fourth arg gcp": {
|
||||
args: []string{"gcp", "1", "2"},
|
||||
wantResult: gcp.InstanceTypes,
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"fourth arg azure": {
|
||||
args: []string{"azure", "1", "2"},
|
||||
wantResult: azure.InstanceTypes,
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"fifth arg": {
|
||||
args: []string{"aws", "1", "2", "4xlarge"},
|
||||
args: []string{"gcp", "foo"},
|
||||
wantResult: []string{},
|
||||
wantShellCD: cobra.ShellCompDirectiveError,
|
||||
},
|
||||
@ -325,3 +326,48 @@ func TestCreateCompletion(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstanceTypeCompletion(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
wantResult []string
|
||||
wantShellCD cobra.ShellCompDirective
|
||||
}{
|
||||
"azure": {
|
||||
args: []string{"azure"},
|
||||
wantResult: azure.InstanceTypes,
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"gcp": {
|
||||
args: []string{"gcp"},
|
||||
wantResult: gcp.InstanceTypes,
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"empty args": {
|
||||
args: []string{},
|
||||
wantResult: []string{},
|
||||
wantShellCD: cobra.ShellCompDirectiveError,
|
||||
},
|
||||
"unknown provider": {
|
||||
args: []string{"foo"},
|
||||
wantResult: []string{},
|
||||
wantShellCD: cobra.ShellCompDirectiveError,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cmd := &cobra.Command{}
|
||||
result, shellCD := instanceTypeCompletion(cmd, tc.args, "")
|
||||
|
||||
assert.Equal(tc.wantResult, result)
|
||||
assert.Equal(tc.wantShellCD, shellCD)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func intPtr(i int) *int {
|
||||
return &i
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ func newInitCmd() *cobra.Command {
|
||||
cmd.Flags().String("privatekey", "", "path to your private key.")
|
||||
cmd.Flags().String("master-secret", "", "path to base64 encoded master secret.")
|
||||
cmd.Flags().Bool("wg-autoconfig", false, "enable automatic configuration of WireGuard interface")
|
||||
must(cmd.Flags().MarkHidden("wg-autoconfig"))
|
||||
cmd.Flags().Bool("autoscale", false, "enable Kubernetes cluster-autoscaler")
|
||||
return cmd
|
||||
}
|
||||
|
@ -32,9 +32,9 @@ func newRecoverCmd() *cobra.Command {
|
||||
RunE: runRecover,
|
||||
}
|
||||
cmd.Flags().String("ip", "", "Instance IP address.")
|
||||
_ = cmd.MarkFlagRequired("ip")
|
||||
must(cmd.MarkFlagRequired("ip"))
|
||||
cmd.Flags().String("disk-uuid", "", "Disk UUID of the encrypted state disk.")
|
||||
_ = cmd.MarkFlagRequired("disk-uuid")
|
||||
must(cmd.MarkFlagRequired("disk-uuid"))
|
||||
cmd.Flags().String("master-secret", "", "Path to base64 encoded master secret.")
|
||||
return cmd
|
||||
}
|
||||
|
@ -54,7 +54,8 @@ func init() {
|
||||
cobra.EnableCommandSorting = false
|
||||
// Set output of cmd.Print to stdout. (By default, it's stderr.)
|
||||
rootCmd.SetOut(os.Stdout)
|
||||
rootCmd.PersistentFlags().String("dev-config", "", "Set this flag to create the Constellation using settings from a development config.")
|
||||
rootCmd.PersistentFlags().String("dev-config", "", "Set this flag to create the Constellation cluster using settings from a development config.")
|
||||
must(rootCmd.MarkPersistentFlagFilename("dev-config", "json"))
|
||||
rootCmd.AddCommand(newCreateCmd())
|
||||
rootCmd.AddCommand(newInitCmd())
|
||||
rootCmd.AddCommand(newVerifyCmd())
|
||||
@ -62,3 +63,9 @@ func init() {
|
||||
rootCmd.AddCommand(newTerminateCmd())
|
||||
rootCmd.AddCommand(newVersionCmd())
|
||||
}
|
||||
|
||||
func must(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -3,46 +3,13 @@ package cmd
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/cli/azure"
|
||||
"github.com/edgelesssys/constellation/cli/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/cli/ec2"
|
||||
"github.com/edgelesssys/constellation/cli/gcp"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// isIntArg checks if argument at position arg is an integer.
|
||||
func isIntArg(arg int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if _, err := strconv.Atoi(args[arg]); err != nil {
|
||||
return fmt.Errorf("argument %d must be an integer", arg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// isIntGreaterArg checks if argument at position arg is an integer and greater i.
|
||||
func isIntGreaterArg(arg int, i int) cobra.PositionalArgs {
|
||||
return cobra.MatchAll(isIntArg(arg), func(cmd *cobra.Command, args []string) error {
|
||||
if v, _ := strconv.Atoi(args[arg]); v <= i {
|
||||
return fmt.Errorf("argument %d must be greater %d, but it's %d", arg, i, v)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func isIntLessArg(arg int, i int) cobra.PositionalArgs {
|
||||
return cobra.MatchAll(isIntArg(arg), func(cmd *cobra.Command, args []string) error {
|
||||
if v, _ := strconv.Atoi(args[arg]); v >= i {
|
||||
return fmt.Errorf("argument %d must be less %d, but it's %d", arg, i, v)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// warnAWS warns that AWS isn't supported.
|
||||
func warnAWS(providerPos int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
@ -53,60 +20,6 @@ func warnAWS(providerPos int) cobra.PositionalArgs {
|
||||
}
|
||||
}
|
||||
|
||||
// isIntGreaterZeroArg checks if argument at position arg is a positive non zero integer.
|
||||
func isIntGreaterZeroArg(arg int) cobra.PositionalArgs {
|
||||
return isIntGreaterArg(arg, 0)
|
||||
}
|
||||
|
||||
func isPort(arg int) cobra.PositionalArgs {
|
||||
return cobra.MatchAll(
|
||||
isIntGreaterArg(arg, -1),
|
||||
isIntLessArg(arg, 65536),
|
||||
)
|
||||
}
|
||||
|
||||
func isIP(arg int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if ip := net.ParseIP(args[arg]); ip == nil {
|
||||
return fmt.Errorf("argument %s isn't a valid IP address", args[arg])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// isEC2InstanceType checks if argument at position arg is a key in m.
|
||||
// The argument will always be converted to lower case letters.
|
||||
func isEC2InstanceType(arg int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if _, ok := ec2.InstanceTypes[strings.ToLower(args[arg])]; !ok {
|
||||
return fmt.Errorf("'%s' isn't an AWS EC2 instance type", args[arg])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func isGCPInstanceType(arg int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
for _, instanceType := range gcp.InstanceTypes {
|
||||
if args[arg] == instanceType {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("argument %s isn't a valid GCP instance type", args[arg])
|
||||
}
|
||||
}
|
||||
|
||||
func isAzureInstanceType(arg int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
for _, instanceType := range azure.InstanceTypes {
|
||||
if args[arg] == instanceType {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("argument %s isn't a valid Azure instance type", args[arg])
|
||||
}
|
||||
}
|
||||
|
||||
func isCloudProvider(arg int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if provider := cloudprovider.FromString(args[arg]); provider == cloudprovider.Unknown {
|
||||
@ -116,34 +29,23 @@ func isCloudProvider(arg int) cobra.PositionalArgs {
|
||||
}
|
||||
}
|
||||
|
||||
// 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))
|
||||
func validInstanceTypeForProvider(insType string, provider cloudprovider.Provider) error {
|
||||
switch provider {
|
||||
case cloudprovider.GCP:
|
||||
for _, instanceType := range gcp.InstanceTypes {
|
||||
if insType == instanceType {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
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 cloudprovider.FromString(args[providerPos]) {
|
||||
case cloudprovider.GCP:
|
||||
return isGCPInstanceType(typePos)(cmd, args)
|
||||
case cloudprovider.Azure:
|
||||
return isAzureInstanceType(typePos)(cmd, args)
|
||||
default:
|
||||
return fmt.Errorf("argument %s isn't a valid cloud platform", args[providerPos])
|
||||
return fmt.Errorf("%s isn't a valid GCP instance type", insType)
|
||||
case cloudprovider.Azure:
|
||||
for _, instanceType := range azure.InstanceTypes {
|
||||
if insType == instanceType {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("%s isn't a valid Azure instance type", insType)
|
||||
default:
|
||||
return fmt.Errorf("%s isn't a valid cloud platform", provider)
|
||||
}
|
||||
}
|
||||
|
@ -7,243 +7,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsIntArg(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
wantErr bool
|
||||
}{
|
||||
"valid int 1": {[]string{"1"}, false},
|
||||
"valid int 2": {[]string{"42"}, false},
|
||||
"valid int 3": {[]string{"987987498"}, false},
|
||||
"valid int and other args": {[]string{"3", "hello"}, false},
|
||||
"valid int and other args 2": {[]string{"3", "4"}, false},
|
||||
"invalid 1": {[]string{"hello world"}, true},
|
||||
"invalid 2": {[]string{"98798d749f8"}, true},
|
||||
"invalid 3": {[]string{"three"}, true},
|
||||
"invalid 4": {[]string{"0.3"}, true},
|
||||
}
|
||||
|
||||
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.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIntGreaterArg(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
wantErr bool
|
||||
}{
|
||||
"valid int 1": {[]string{"13"}, false},
|
||||
"valid int 2": {[]string{"42"}, false},
|
||||
"valid int 3": {[]string{"987987498"}, false},
|
||||
"invalid int 1": {[]string{"1"}, true},
|
||||
"invalid int and other args": {[]string{"3", "hello"}, true},
|
||||
"invalid int and other args 2": {[]string{"-14", "4"}, true},
|
||||
}
|
||||
|
||||
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.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIntGreaterZeroArg(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
wantErr bool
|
||||
}{
|
||||
"valid int 1": {[]string{"13"}, false},
|
||||
"valid int 2": {[]string{"42"}, false},
|
||||
"valid int 3": {[]string{"987987498"}, false},
|
||||
"invalid": {[]string{"0"}, true},
|
||||
"invalid int 1": {[]string{"-42", "hello"}, true},
|
||||
"invalid int and other args": {[]string{"-9487239847", "4"}, true},
|
||||
}
|
||||
|
||||
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.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPort(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
wantErr bool
|
||||
}{
|
||||
"valid port 1": {[]string{"80"}, false},
|
||||
"valid port 2": {[]string{"8080"}, false},
|
||||
"valid port 3": {[]string{"65535"}, false},
|
||||
"invalid port 1": {[]string{"foo"}, true},
|
||||
"invalid port 2": {[]string{"65536"}, true},
|
||||
"invalid port 3": {[]string{"-1"}, true},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testCmd := &cobra.Command{Args: isPort(0)}
|
||||
|
||||
err := testCmd.ValidateArgs(tc.args)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIP(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
wantErr bool
|
||||
}{
|
||||
"valid ip 1": {[]string{"192.168.0.2"}, false},
|
||||
"valid ip 2": {[]string{"127.0.0.1"}, false},
|
||||
"valid ip 3": {[]string{"8.8.8.8"}, false},
|
||||
"invalid ip 1": {[]string{"foo"}, true},
|
||||
"invalid ip 2": {[]string{"foo.bar.baz.1"}, true},
|
||||
"invalid ip 3": {[]string{"800.800.800.800"}, true},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
testCmd := &cobra.Command{Args: isIP(0)}
|
||||
|
||||
err := testCmd.ValidateArgs(tc.args)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsEC2InstanceType(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
wantErr 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 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.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsGCPInstanceType(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
wantErr 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 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.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAzureInstanceType(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
wantErr 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 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.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCloudProvider(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
pos int
|
||||
@ -273,41 +36,3 @@ func TestIsCloudProvider(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsInstanceTypeForProvider(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
typePos int
|
||||
providerPos int
|
||||
args []string
|
||||
wantErr bool
|
||||
}{
|
||||
"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{"n2d-standard-4", "", "foo", "gcp"}, false},
|
||||
"mixed order 2": {2, 1, []string{"", "gcp", "n2d-standard-4", "foo", "bar"}, false},
|
||||
"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.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,16 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/cli/cloud/cloudcmd"
|
||||
"github.com/edgelesssys/constellation/cli/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/cli/file"
|
||||
"github.com/edgelesssys/constellation/cli/proto"
|
||||
"github.com/edgelesssys/constellation/internal/config"
|
||||
"github.com/edgelesssys/constellation/internal/constants"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
rpcStatus "google.golang.org/grpc/status"
|
||||
@ -17,33 +21,32 @@ import (
|
||||
|
||||
func newVerifyCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "verify {azure|gcp} IP PORT",
|
||||
Short: "Verify the confidential properties of your Constellation.",
|
||||
Long: "Verify the confidential properties of your Constellation.",
|
||||
Use: "verify {aws|azure|gcp}",
|
||||
Short: "Verify the confidential properties of your Constellation cluster.",
|
||||
Long: "Verify the confidential properties of your Constellation cluster.",
|
||||
Args: cobra.MatchAll(
|
||||
cobra.ExactArgs(3),
|
||||
cobra.ExactArgs(1),
|
||||
isCloudProvider(0),
|
||||
isIP(1),
|
||||
isPort(2),
|
||||
warnAWS(0),
|
||||
),
|
||||
RunE: runVerify,
|
||||
}
|
||||
cmd.Flags().String("owner-id", "", "verify the Constellation using the owner identity derived from the master secret.")
|
||||
cmd.Flags().String("unique-id", "", "verify the Constellation using the unique cluster identity.")
|
||||
cmd.Flags().String("owner-id", "", "Verify using the owner identity derived from the master secret.")
|
||||
cmd.Flags().String("unique-id", "", "Verify using the unique cluster identity.")
|
||||
cmd.Flags().StringP("node-endpoint", "e", "", "Endpoint of the node to verify. Form: HOST[:PORT]")
|
||||
must(cmd.MarkFlagRequired("node-endpoint"))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runVerify(cmd *cobra.Command, args []string) error {
|
||||
provider := cloudprovider.FromString(args[0])
|
||||
ip := args[1]
|
||||
port := args[2]
|
||||
fileHandler := file.NewHandler(afero.NewOsFs())
|
||||
protoClient := &proto.Client{}
|
||||
defer protoClient.Close()
|
||||
return verify(cmd.Context(), cmd, provider, ip, port, fileHandler, protoClient)
|
||||
return verify(cmd.Context(), cmd, provider, fileHandler, protoClient)
|
||||
}
|
||||
|
||||
func verify(ctx context.Context, cmd *cobra.Command, provider cloudprovider.Provider, ip, port string, fileHandler file.Handler, protoClient protoClient) error {
|
||||
func verify(ctx context.Context, cmd *cobra.Command, provider cloudprovider.Provider, fileHandler file.Handler, protoClient protoClient) error {
|
||||
flags, err := parseVerifyFlags(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -66,7 +69,7 @@ func verify(ctx context.Context, cmd *cobra.Command, provider cloudprovider.Prov
|
||||
cmd.Print(validators.Warnings())
|
||||
}
|
||||
|
||||
if err := protoClient.Connect(ip, port, validators.V()); err != nil {
|
||||
if err := protoClient.Connect(flags.nodeHost, flags.nodePort, validators.V()); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := protoClient.GetState(ctx); err != nil {
|
||||
@ -93,12 +96,27 @@ func parseVerifyFlags(cmd *cobra.Command) (verifyFlags, error) {
|
||||
return verifyFlags{}, errors.New("neither owner ID nor unique ID provided to verify the Constellation")
|
||||
}
|
||||
|
||||
endpoint, err := cmd.Flags().GetString("node-endpoint")
|
||||
if err != nil {
|
||||
return verifyFlags{}, err
|
||||
}
|
||||
host, port, err := net.SplitHostPort(endpoint)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "missing port in address") {
|
||||
return verifyFlags{}, err
|
||||
}
|
||||
host = endpoint
|
||||
port = strconv.Itoa(constants.CoordinatorPort)
|
||||
}
|
||||
|
||||
devConfigPath, err := cmd.Flags().GetString("dev-config")
|
||||
if err != nil {
|
||||
return verifyFlags{}, err
|
||||
}
|
||||
|
||||
return verifyFlags{
|
||||
nodeHost: host,
|
||||
nodePort: port,
|
||||
devConfigPath: devConfigPath,
|
||||
ownerID: ownerID,
|
||||
clusterID: clusterID,
|
||||
@ -106,6 +124,8 @@ func parseVerifyFlags(cmd *cobra.Command) (verifyFlags, error) {
|
||||
}
|
||||
|
||||
type verifyFlags struct {
|
||||
nodeHost string
|
||||
nodePort string
|
||||
ownerID string
|
||||
clusterID string
|
||||
devConfigPath string
|
||||
@ -117,8 +137,6 @@ func verifyCompletion(cmd *cobra.Command, args []string, toComplete string) ([]s
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return []string{"gcp", "azure"}, cobra.ShellCompDirectiveNoFileComp
|
||||
case 1, 2:
|
||||
return []string{}, cobra.ShellCompDirectiveNoFileComp
|
||||
default:
|
||||
return []string{}, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
@ -22,15 +22,10 @@ func TestVerifyCmdArgumentValidation(t *testing.T) {
|
||||
args []string
|
||||
wantErr bool
|
||||
}{
|
||||
"no args": {[]string{}, true},
|
||||
"valid azure": {[]string{"azure", "192.0.2.1", "1234"}, false},
|
||||
"valid gcp": {[]string{"gcp", "192.0.2.1", "1234"}, false},
|
||||
"invalid provider": {[]string{"invalid", "192.0.2.1", "1234"}, true},
|
||||
"invalid ip": {[]string{"gcp", "invalid", "1234"}, true},
|
||||
"invalid port": {[]string{"gcp", "192.0.2.1", "invalid"}, true},
|
||||
"invalid port 2": {[]string{"gcp", "192.0.2.1", "65536"}, true},
|
||||
"not enough arguments": {[]string{"gcp", "192.0.2.1"}, true},
|
||||
"too many arguments": {[]string{"gcp", "192.0.2.1", "1234", "5678"}, true},
|
||||
"no args": {[]string{}, true},
|
||||
"valid azure": {[]string{"azure"}, false},
|
||||
"valid gcp": {[]string{"gcp"}, false},
|
||||
"invalid provider": {[]string{"invalid", "192.0.2.1", "1234"}, true},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
@ -54,58 +49,81 @@ func TestVerify(t *testing.T) {
|
||||
someErr := errors.New("failed")
|
||||
|
||||
testCases := map[string]struct {
|
||||
setupFs func(*require.Assertions) afero.Fs
|
||||
provider cloudprovider.Provider
|
||||
protoClient protoClient
|
||||
devConfigFlag string
|
||||
ownerIDFlag string
|
||||
clusterIDFlag string
|
||||
wantErr bool
|
||||
setupFs func(*require.Assertions) afero.Fs
|
||||
provider cloudprovider.Provider
|
||||
protoClient protoClient
|
||||
nodeEndpointFlag string
|
||||
devConfigFlag string
|
||||
ownerIDFlag string
|
||||
clusterIDFlag string
|
||||
wantErr bool
|
||||
}{
|
||||
"gcp": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.GCP,
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{},
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.GCP,
|
||||
nodeEndpointFlag: "192.0.2.1:1234",
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{},
|
||||
},
|
||||
"azure": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.Azure,
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{},
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.Azure,
|
||||
nodeEndpointFlag: "192.0.2.1:1234",
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{},
|
||||
},
|
||||
"default port": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.GCP,
|
||||
nodeEndpointFlag: "192.0.2.1",
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{},
|
||||
},
|
||||
"invalid endpoint": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.GCP,
|
||||
nodeEndpointFlag: ":::::",
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{},
|
||||
wantErr: true,
|
||||
},
|
||||
"neither owner id nor cluster id set": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.GCP,
|
||||
wantErr: true,
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.GCP,
|
||||
nodeEndpointFlag: "192.0.2.1:1234",
|
||||
wantErr: true,
|
||||
},
|
||||
"dev config file not existing": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.GCP,
|
||||
ownerIDFlag: zeroBase64,
|
||||
devConfigFlag: "./file",
|
||||
wantErr: true,
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.GCP,
|
||||
ownerIDFlag: zeroBase64,
|
||||
nodeEndpointFlag: "192.0.2.1:1234",
|
||||
devConfigFlag: "./file",
|
||||
wantErr: true,
|
||||
},
|
||||
"error protoClient Connect": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.Azure,
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{connectErr: someErr},
|
||||
wantErr: true,
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.Azure,
|
||||
nodeEndpointFlag: "192.0.2.1:1234",
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{connectErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
"error protoClient GetState": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.Azure,
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{getStateErr: rpcStatus.Error(codes.Internal, "failed")},
|
||||
wantErr: true,
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.Azure,
|
||||
nodeEndpointFlag: "192.0.2.1:1234",
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{getStateErr: rpcStatus.Error(codes.Internal, "failed")},
|
||||
wantErr: true,
|
||||
},
|
||||
"error protoClient GetState not rpc": {
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.Azure,
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{getStateErr: someErr},
|
||||
wantErr: true,
|
||||
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
|
||||
provider: cloudprovider.Azure,
|
||||
nodeEndpointFlag: "192.0.2.1:1234",
|
||||
ownerIDFlag: zeroBase64,
|
||||
protoClient: &stubProtoClient{getStateErr: someErr},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
@ -128,10 +146,13 @@ func TestVerify(t *testing.T) {
|
||||
if tc.clusterIDFlag != "" {
|
||||
require.NoError(cmd.Flags().Set("cluster-id", tc.clusterIDFlag))
|
||||
}
|
||||
if tc.nodeEndpointFlag != "" {
|
||||
require.NoError(cmd.Flags().Set("node-endpoint", tc.nodeEndpointFlag))
|
||||
}
|
||||
fileHandler := file.NewHandler(tc.setupFs(require))
|
||||
|
||||
ctx := context.Background()
|
||||
err := verify(ctx, cmd, tc.provider, "192.0.2.1", "1234", fileHandler, tc.protoClient)
|
||||
err := verify(ctx, cmd, tc.provider, fileHandler, tc.protoClient)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
@ -156,21 +177,8 @@ func TestVerifyCompletion(t *testing.T) {
|
||||
wantResult: []string{"gcp", "azure"},
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"second arg": {
|
||||
args: []string{"gcp"},
|
||||
toComplete: "192.0.2.1",
|
||||
wantResult: []string{},
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"third arg": {
|
||||
args: []string{"gcp", "192.0.2.1"},
|
||||
toComplete: "443",
|
||||
wantResult: []string{},
|
||||
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
|
||||
},
|
||||
"additional arg": {
|
||||
args: []string{"gcp", "192.0.2.1", "443"},
|
||||
toComplete: "./file",
|
||||
args: []string{"gcp", "foo"},
|
||||
wantResult: []string{},
|
||||
wantShellCD: cobra.ShellCompDirectiveError,
|
||||
},
|
||||
|
@ -32,6 +32,13 @@ const (
|
||||
// Cryptographic constants.
|
||||
//
|
||||
StateDiskKeyLength = 32
|
||||
|
||||
//
|
||||
// CLI.
|
||||
//
|
||||
|
||||
MinControllerCount = 1
|
||||
MinWorkerCount = 1
|
||||
)
|
||||
|
||||
// CliVersion is the version of the CLI. Left as a separate variable to allow override during build.
|
||||
|
Loading…
Reference in New Issue
Block a user