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,15 +23,12 @@ 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")
@ -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

@ -50,7 +50,6 @@ func NewInitCmd() *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,
} }
@ -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