Purge provider argument from constellation create and verify

This commit is contained in:
Nils Hanke 2022-09-07 15:38:29 +02:00 committed by Nils Hanke
parent 7aded65ea8
commit ce0edc8c80
9 changed files with 31 additions and 233 deletions

View file

@ -23,16 +23,13 @@ import (
// NewCreateCmd returns a new cobra.Command for the create command. // NewCreateCmd returns a new cobra.Command for the create command.
func NewCreateCmd() *cobra.Command { func NewCreateCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create {aws|azure|gcp}", Use: "create",
Short: "Create instances on a cloud platform for your Constellation cluster", Short: "Create instances on a cloud platform for your Constellation cluster",
Long: "Create instances on a cloud platform for your Constellation cluster.", Long: "Create instances on a cloud platform for your Constellation cluster.",
Args: cobra.MatchAll( Args: cobra.MatchAll(
cobra.ExactArgs(1), cobra.ExactArgs(0),
isCloudProvider(0),
warnAWS(0),
), ),
ValidArgsFunction: createCompletion, RunE: runCreate,
RunE: runCreate,
} }
cmd.Flags().String("name", "constell", "create the cluster with the specified name") cmd.Flags().String("name", "constell", "create the cluster with the specified name")
cmd.Flags().BoolP("yes", "y", false, "create the cluster without further confirmation") cmd.Flags().BoolP("yes", "y", false, "create the cluster without further confirmation")
@ -44,15 +41,13 @@ func NewCreateCmd() *cobra.Command {
} }
func runCreate(cmd *cobra.Command, args []string) error { func runCreate(cmd *cobra.Command, args []string) error {
provider := cloudprovider.FromString(args[0])
fileHandler := file.NewHandler(afero.NewOsFs()) fileHandler := file.NewHandler(afero.NewOsFs())
creator := cloudcmd.NewCreator(cmd.OutOrStdout()) creator := cloudcmd.NewCreator(cmd.OutOrStdout())
return create(cmd, creator, fileHandler, provider) return create(cmd, creator, fileHandler)
} }
func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler, provider cloudprovider.Provider, func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler) (retErr error) {
) (retErr error) {
flags, err := parseCreateFlags(cmd) flags, err := parseCreateFlags(cmd)
if err != nil { if err != nil {
return err return err
@ -62,7 +57,7 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
return err return err
} }
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider) config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath)
if err != nil { if err != nil {
return fmt.Errorf("reading and validating config: %w", err) return fmt.Errorf("reading and validating config: %w", err)
} }
@ -92,6 +87,7 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
cmd.Println("") cmd.Println("")
} }
provider := config.GetProvider()
var instanceType string var instanceType string
switch provider { switch provider {
case cloudprovider.Azure: case cloudprovider.Azure:
@ -216,17 +212,6 @@ func writeIPtoIDFile(fileHandler file.Handler, state state.ConstellationState) e
return fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone) return fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptNone)
} }
// createCompletion handles the completion of the create command. It is frequently called
// while the user types arguments of the command to suggest completion.
func createCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
switch len(args) {
case 0:
return []string{"aws", "gcp", "azure"}, cobra.ShellCompDirectiveNoFileComp
default:
return []string{}, cobra.ShellCompDirectiveError
}
}
func must(err error) { func must(err error) {
if err != nil { if err != nil {
panic(err) panic(err)

View file

@ -18,37 +18,10 @@ import (
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestCreateArgumentValidation(t *testing.T) {
testCases := map[string]struct {
args []string
wantErr bool
}{
"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 {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
err := NewCreateCmd().ValidateArgs(tc.args)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
}
})
}
}
func TestCreate(t *testing.T) { func TestCreate(t *testing.T) {
testState := state.ConstellationState{Name: "test", LoadBalancerIP: "192.0.2.1"} testState := state.ConstellationState{Name: "test", LoadBalancerIP: "192.0.2.1"}
someErr := errors.New("failed") someErr := errors.New("failed")
@ -242,7 +215,7 @@ func TestCreate(t *testing.T) {
fileHandler := file.NewHandler(tc.setupFs(require)) fileHandler := file.NewHandler(tc.setupFs(require))
err := create(cmd, tc.creator, fileHandler, tc.provider) err := create(cmd, tc.creator, fileHandler)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
@ -314,36 +287,6 @@ func TestCheckDirClean(t *testing.T) {
} }
} }
func TestCreateCompletion(t *testing.T) {
testCases := map[string]struct {
args []string
wantResult []string
wantShellCD cobra.ShellCompDirective
}{
"first arg": {
args: []string{},
wantResult: []string{"aws", "gcp", "azure"},
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
},
"second arg": {
args: []string{"gcp", "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 := createCompletion(cmd, tc.args, "")
assert.Equal(tc.wantResult, result)
assert.Equal(tc.wantShellCD, shellCD)
})
}
}
func intPtr(i int) *int { func intPtr(i int) *int {
return &i return &i
} }

View file

@ -47,12 +47,11 @@ import (
// NewInitCmd returns a new cobra.Command for the init command. // NewInitCmd returns a new cobra.Command for the init command.
func NewInitCmd() *cobra.Command { func NewInitCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "init", Use: "init",
Short: "Initialize the Constellation cluster", Short: "Initialize the Constellation cluster",
Long: "Initialize the Constellation cluster. Start your confidential Kubernetes.", Long: "Initialize the Constellation cluster. Start your confidential Kubernetes.",
ValidArgsFunction: initCompletion, Args: cobra.ExactArgs(0),
Args: cobra.ExactArgs(0), RunE: runInitialize,
RunE: runInitialize,
} }
cmd.Flags().String("master-secret", "", "path to base64-encoded master secret") cmd.Flags().String("master-secret", "", "path to base64-encoded master secret")
cmd.Flags().String("endpoint", "", "endpoint of the bootstrapper, passed as HOST[:PORT]") cmd.Flags().String("endpoint", "", "endpoint of the bootstrapper, passed as HOST[:PORT]")
@ -92,9 +91,7 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
return fmt.Errorf("loading Constellation state file: %w", err) return fmt.Errorf("loading Constellation state file: %w", err)
} }
provider := cloudprovider.FromString(stat.CloudProvider) config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath)
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil { if err != nil {
return fmt.Errorf("reading and validating config: %w", err) return fmt.Errorf("reading and validating config: %w", err)
} }
@ -107,6 +104,7 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
cmd.Printf("Warning: Constellation with Kubernetes %v is still in preview. Use only for evaluation purposes.\n", k8sVersion) cmd.Printf("Warning: Constellation with Kubernetes %v is still in preview. Use only for evaluation purposes.\n", k8sVersion)
} }
provider := config.GetProvider()
checker := license.NewChecker(quotaChecker, fileHandler) checker := license.NewChecker(quotaChecker, fileHandler)
if err := checker.CheckLicense(cmd.Context(), provider, config.Provider, cmd.Printf); err != nil { if err := checker.CheckLicense(cmd.Context(), provider, config.Provider, cmd.Printf); err != nil {
cmd.Printf("License check failed: %v", err) cmd.Printf("License check failed: %v", err)
@ -414,15 +412,6 @@ func getScalingGroupsFromState(stat state.ConstellationState, config *config.Con
} }
} }
// initCompletion handels the completion of CLI arguments. It is frequently called
// while the user types arguments of the command to suggest completion.
func initCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return []string{}, cobra.ShellCompDirectiveError
}
return []string{}, cobra.ShellCompDirectiveDefault
}
type grpcDialer interface { type grpcDialer interface {
Dial(ctx context.Context, target string) (*grpc.ClientConn, error) Dial(ctx context.Context, target string) (*grpc.ClientConn, error)
} }

View file

@ -33,7 +33,6 @@ import (
"github.com/edgelesssys/constellation/internal/oid" "github.com/edgelesssys/constellation/internal/oid"
"github.com/edgelesssys/constellation/internal/state" "github.com/edgelesssys/constellation/internal/state"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc" "google.golang.org/grpc"
@ -273,45 +272,6 @@ func TestWriteOutput(t *testing.T) {
assert.Equal(expectedIDFile, testIDFile) assert.Equal(expectedIDFile, testIDFile)
} }
func TestInitCompletion(t *testing.T) {
testCases := map[string]struct {
args []string
toComplete string
wantResult []string
wantShellCD cobra.ShellCompDirective
}{
"first arg": {
args: []string{},
toComplete: "hello",
wantResult: []string{},
wantShellCD: cobra.ShellCompDirectiveDefault,
},
"secnod arg": {
args: []string{"23"},
toComplete: "/test/h",
wantResult: []string{},
wantShellCD: cobra.ShellCompDirectiveError,
},
"third arg": {
args: []string{"./file", "sth"},
toComplete: "./file",
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 := initCompletion(cmd, tc.args, tc.toComplete)
assert.Equal(tc.wantResult, result)
assert.Equal(tc.wantShellCD, shellCD)
})
}
}
func TestReadOrGenerateMasterSecret(t *testing.T) { func TestReadOrGenerateMasterSecret(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
filename string filename string

View file

@ -11,12 +11,11 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/config" "github.com/edgelesssys/constellation/internal/config"
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
) )
func readConfig(out io.Writer, fileHandler file.Handler, name string, provider cloudprovider.Provider) (*config.Config, error) { func readConfig(out io.Writer, fileHandler file.Handler, name string) (*config.Config, error) {
if name == "" { if name == "" {
return config.Default(), nil return config.Default(), nil
} }
@ -25,13 +24,14 @@ func readConfig(out io.Writer, fileHandler file.Handler, name string, provider c
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := validateConfig(out, cnf, provider); err != nil { if err := validateConfig(out, cnf); err != nil {
return nil, err return nil, err
} }
return cnf, nil return cnf, nil
} }
func validateConfig(out io.Writer, cnf *config.Config, provider cloudprovider.Provider) error { func validateConfig(out io.Writer, cnf *config.Config) error {
msgs, err := cnf.Validate() msgs, err := cnf.Validate()
if err != nil { if err != nil {
return fmt.Errorf("performing config validation: %w", err) return fmt.Errorf("performing config validation: %w", err)
@ -46,9 +46,5 @@ func validateConfig(out io.Writer, cnf *config.Config, provider cloudprovider.Pr
return errors.New("invalid configuration") return errors.New("invalid configuration")
} }
if provider != cloudprovider.Unknown && !cnf.HasProvider(provider) {
return fmt.Errorf("configuration doesn't contain provider: %v", provider)
}
return nil return nil
} }

View file

@ -71,13 +71,15 @@ func TestValidateConfig(t *testing.T) {
wantOutput: true, wantOutput: true,
wantErr: true, wantErr: true,
}, },
"config without provider is ok if no provider required": { "config without provider is not ok": {
cnf: func() *config.Config { cnf: func() *config.Config {
cnf := config.Default() cnf := config.Default()
cnf.Provider = config.ProviderConfig{} cnf.Provider = config.ProviderConfig{}
return cnf return cnf
}(), }(),
wantErr: true,
}, },
"config without required provider": { "config without required provider": {
cnf: func() *config.Config { cnf: func() *config.Config {
cnf := config.Default() cnf := config.Default()
@ -96,7 +98,7 @@ func TestValidateConfig(t *testing.T) {
out := &bytes.Buffer{} out := &bytes.Buffer{}
err := validateConfig(out, tc.cnf, tc.provider) err := validateConfig(out, tc.cnf)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)

View file

@ -68,8 +68,7 @@ func recover(cmd *cobra.Command, fileHandler file.Handler, recoveryClient recove
} }
provider := cloudprovider.FromString(stat.CloudProvider) provider := cloudprovider.FromString(stat.CloudProvider)
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath)
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider)
if err != nil { if err != nil {
return fmt.Errorf("reading and validating config: %w", err) return fmt.Errorf("reading and validating config: %w", err)
} }

View file

@ -17,7 +17,6 @@ import (
"github.com/edgelesssys/constellation/cli/internal/cloudcmd" "github.com/edgelesssys/constellation/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/internal/atls" "github.com/edgelesssys/constellation/internal/atls"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/crypto" "github.com/edgelesssys/constellation/internal/crypto"
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
@ -31,15 +30,13 @@ import (
// NewVerifyCmd returns a new cobra.Command for the verify command. // NewVerifyCmd returns a new cobra.Command for the verify command.
func NewVerifyCmd() *cobra.Command { func NewVerifyCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "verify {aws|azure|gcp}", Use: "verify",
Short: "Verify the confidential properties of a Constellation cluster", Short: "Verify the confidential properties of a Constellation cluster",
Long: `Verify the confidential properties of a Constellation cluster. Long: `Verify the confidential properties of a Constellation cluster.
If arguments aren't specified, values are read from ` + "`" + constants.ClusterIDsFileName + "`.", If arguments aren't specified, values are read from ` + "`" + constants.ClusterIDsFileName + "`.",
Args: cobra.MatchAll( Args: cobra.MatchAll(
cobra.ExactArgs(1), cobra.ExactArgs(0),
isCloudProvider(0),
warnAWS(0),
), ),
RunE: runVerify, RunE: runVerify,
} }
@ -50,25 +47,23 @@ If arguments aren't specified, values are read from ` + "`" + constants.ClusterI
} }
func runVerify(cmd *cobra.Command, args []string) error { func runVerify(cmd *cobra.Command, args []string) error {
provider := cloudprovider.FromString(args[0])
fileHandler := file.NewHandler(afero.NewOsFs()) fileHandler := file.NewHandler(afero.NewOsFs())
verifyClient := &constellationVerifier{dialer: dialer.New(nil, nil, &net.Dialer{})} verifyClient := &constellationVerifier{dialer: dialer.New(nil, nil, &net.Dialer{})}
return verify(cmd, provider, fileHandler, verifyClient) return verify(cmd, fileHandler, verifyClient)
} }
func verify( func verify(cmd *cobra.Command, fileHandler file.Handler, verifyClient verifyClient) error {
cmd *cobra.Command, provider cloudprovider.Provider, fileHandler file.Handler, verifyClient verifyClient,
) error {
flags, err := parseVerifyFlags(cmd, fileHandler) flags, err := parseVerifyFlags(cmd, fileHandler)
if err != nil { if err != nil {
return err return err
} }
config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath, provider) config, err := readConfig(cmd.OutOrStdout(), fileHandler, flags.configPath)
if err != nil { if err != nil {
return fmt.Errorf("reading and validating config: %w", err) return fmt.Errorf("reading and validating config: %w", err)
} }
provider := config.GetProvider()
validators, err := cloudcmd.NewValidator(provider, config) validators, err := cloudcmd.NewValidator(provider, config)
if err != nil { if err != nil {
return err return err
@ -181,17 +176,6 @@ func addPortIfMissing(endpoint string, defaultPort int) (string, error) {
return "", err return "", err
} }
// verifyCompletion handles the completion of CLI arguments. It is frequently called
// while the user types arguments of the command to suggest completion.
func verifyCompletion(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
switch len(args) {
case 0:
return []string{"gcp", "azure"}, cobra.ShellCompDirectiveNoFileComp
default:
return []string{}, cobra.ShellCompDirectiveError
}
}
type constellationVerifier struct { type constellationVerifier struct {
dialer grpcInsecureDialer dialer grpcInsecureDialer
} }

View file

@ -26,7 +26,6 @@ import (
"github.com/edgelesssys/constellation/internal/oid" "github.com/edgelesssys/constellation/internal/oid"
"github.com/edgelesssys/constellation/verify/verifyproto" "github.com/edgelesssys/constellation/verify/verifyproto"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc" "google.golang.org/grpc"
@ -34,33 +33,6 @@ import (
rpcStatus "google.golang.org/grpc/status" rpcStatus "google.golang.org/grpc/status"
) )
func TestVerifyCmdArgumentValidation(t *testing.T) {
testCases := map[string]struct {
args []string
wantErr bool
}{
"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 {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
cmd := NewVerifyCmd()
err := cmd.ValidateArgs(tc.args)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
}
})
}
}
func TestVerify(t *testing.T) { func TestVerify(t *testing.T) {
zeroBase64 := base64.StdEncoding.EncodeToString([]byte("00000000000000000000000000000000")) zeroBase64 := base64.StdEncoding.EncodeToString([]byte("00000000000000000000000000000000"))
someErr := errors.New("failed") someErr := errors.New("failed")
@ -190,7 +162,7 @@ func TestVerify(t *testing.T) {
require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, tc.idFile, file.OptNone)) require.NoError(fileHandler.WriteJSON(constants.ClusterIDsFileName, tc.idFile, file.OptNone))
} }
err := verify(cmd, tc.provider, fileHandler, tc.protoClient) err := verify(cmd, fileHandler, tc.protoClient)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
@ -203,38 +175,6 @@ func TestVerify(t *testing.T) {
} }
} }
func TestVerifyCompletion(t *testing.T) {
testCases := map[string]struct {
args []string
toComplete string
wantResult []string
wantShellCD cobra.ShellCompDirective
}{
"first arg": {
args: []string{},
toComplete: "az",
wantResult: []string{"gcp", "azure"},
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
},
"additional arg": {
args: []string{"gcp", "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 := verifyCompletion(cmd, tc.args, tc.toComplete)
assert.Equal(tc.wantResult, result)
assert.Equal(tc.wantShellCD, shellCD)
})
}
}
func TestVerifyClient(t *testing.T) { func TestVerifyClient(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
attestationDoc atls.FakeAttestationDoc attestationDoc atls.FakeAttestationDoc