cli: support StackIT provider on config generate (#1803)

* support stackit provider on config generate

* update cli reference

* default config values

* deploy csi driver

Co-authored-by: Moritz Eckert <m1gh7ym0@gmail.com>

---------

Co-authored-by: Moritz Eckert <m1gh7ym0@gmail.com>
This commit is contained in:
Moritz Sanft 2023-05-30 09:02:50 +02:00 committed by GitHub
parent a0dea7e69b
commit 6d5e7e1f7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 108 additions and 32 deletions

View File

@ -25,7 +25,7 @@ import (
func newConfigGenerateCmd() *cobra.Command { func newConfigGenerateCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "generate {aws|azure|gcp|openstack|qemu}", Use: "generate {aws|azure|gcp|openstack|qemu|stackit}",
Short: "Generate a default configuration file", Short: "Generate a default configuration file",
Long: "Generate a default configuration file for your selected cloud provider.", Long: "Generate a default configuration file for your selected cloud provider.",
Args: cobra.MatchAll( Args: cobra.MatchAll(
@ -61,10 +61,10 @@ func runConfigGenerate(cmd *cobra.Command, args []string) error {
fileHandler := file.NewHandler(afero.NewOsFs()) fileHandler := file.NewHandler(afero.NewOsFs())
provider := cloudprovider.FromString(args[0]) provider := cloudprovider.FromString(args[0])
cg := &configGenerateCmd{log: log} cg := &configGenerateCmd{log: log}
return cg.configGenerate(cmd, fileHandler, provider) return cg.configGenerate(cmd, fileHandler, provider, args[0])
} }
func (cg *configGenerateCmd) configGenerate(cmd *cobra.Command, fileHandler file.Handler, provider cloudprovider.Provider) error { func (cg *configGenerateCmd) configGenerate(cmd *cobra.Command, fileHandler file.Handler, provider cloudprovider.Provider, rawProvider string) error {
flags, err := parseGenerateFlags(cmd) flags, err := parseGenerateFlags(cmd)
if err != nil { if err != nil {
return err return err
@ -72,7 +72,7 @@ func (cg *configGenerateCmd) configGenerate(cmd *cobra.Command, fileHandler file
cg.log.Debugf("Parsed flags as %v", flags) cg.log.Debugf("Parsed flags as %v", flags)
cg.log.Debugf("Using cloud provider %s", provider.String()) cg.log.Debugf("Using cloud provider %s", provider.String())
conf, err := createConfigWithAttestationType(provider, flags.attestationVariant) conf, err := createConfigWithAttestationType(provider, rawProvider, flags.attestationVariant)
if err != nil { if err != nil {
return fmt.Errorf("creating config: %w", err) return fmt.Errorf("creating config: %w", err)
} }
@ -102,8 +102,8 @@ func (cg *configGenerateCmd) configGenerate(cmd *cobra.Command, fileHandler file
} }
// createConfig creates a config file for the given provider. // createConfig creates a config file for the given provider.
func createConfigWithAttestationType(provider cloudprovider.Provider, attestationVariant variant.Variant) (*config.Config, error) { func createConfigWithAttestationType(provider cloudprovider.Provider, rawProvider string, attestationVariant variant.Variant) (*config.Config, error) {
conf := config.Default() conf := config.Default().WithOpenStackProviderDefaults(rawProvider)
conf.RemoveProviderExcept(provider) conf.RemoveProviderExcept(provider)
// set a lower default for QEMU's state disk // set a lower default for QEMU's state disk
@ -128,7 +128,8 @@ func createConfigWithAttestationType(provider cloudprovider.Provider, attestatio
// createConfig creates a config file for the given provider. // createConfig creates a config file for the given provider.
func createConfig(provider cloudprovider.Provider) *config.Config { func createConfig(provider cloudprovider.Provider) *config.Config {
res, _ := createConfigWithAttestationType(provider, variant.Dummy{}) // rawProvider can be hardcoded as it only matters for OpenStack
res, _ := createConfigWithAttestationType(provider, "", variant.Dummy{})
return res return res
} }
@ -186,7 +187,7 @@ func parseGenerateFlags(cmd *cobra.Command) (generateFlags, error) {
func generateCompletion(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) { func generateCompletion(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
switch len(args) { switch len(args) {
case 0: case 0:
return []string{"aws", "gcp", "azure", "qemu"}, cobra.ShellCompDirectiveNoFileComp return []string{"aws", "gcp", "azure", "qemu", "stackit"}, cobra.ShellCompDirectiveNoFileComp
default: default:
return []string{}, cobra.ShellCompDirectiveError return []string{}, cobra.ShellCompDirectiveError
} }

View File

@ -55,7 +55,7 @@ func TestConfigGenerateKubernetesVersion(t *testing.T) {
require.NoError(err) require.NoError(err)
cg := &configGenerateCmd{log: logger.NewTest(t)} cg := &configGenerateCmd{log: logger.NewTest(t)}
err = cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown) err = cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown, "")
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
@ -74,7 +74,7 @@ func TestConfigGenerateDefault(t *testing.T) {
cmd := newConfigGenerateCmd() cmd := newConfigGenerateCmd()
cg := &configGenerateCmd{log: logger.NewTest(t)} cg := &configGenerateCmd{log: logger.NewTest(t)}
require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown)) require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown, ""))
var readConfig config.Config var readConfig config.Config
err := fileHandler.ReadYAML(constants.ConfigFilename, &readConfig) err := fileHandler.ReadYAML(constants.ConfigFilename, &readConfig)
@ -82,23 +82,59 @@ func TestConfigGenerateDefault(t *testing.T) {
assert.Equal(*config.Default(), readConfig) assert.Equal(*config.Default(), readConfig)
} }
func TestConfigGenerateDefaultGCPSpecific(t *testing.T) { func TestConfigGenerateDefaultProviderSpecific(t *testing.T) {
assert := assert.New(t) providers := []cloudprovider.Provider{
require := require.New(t) cloudprovider.AWS,
cloudprovider.Azure,
cloudprovider.GCP,
cloudprovider.OpenStack,
}
fileHandler := file.NewHandler(afero.NewMemMapFs()) for _, provider := range providers {
cmd := newConfigGenerateCmd() t.Run(provider.String(), func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
wantConf := config.Default() fileHandler := file.NewHandler(afero.NewMemMapFs())
wantConf.RemoveProviderAndAttestationExcept(cloudprovider.GCP) cmd := newConfigGenerateCmd()
cg := &configGenerateCmd{log: logger.NewTest(t)} wantConf := config.Default()
require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.GCP)) wantConf.RemoveProviderAndAttestationExcept(provider)
var readConfig config.Config cg := &configGenerateCmd{log: logger.NewTest(t)}
err := fileHandler.ReadYAML(constants.ConfigFilename, &readConfig) require.NoError(cg.configGenerate(cmd, fileHandler, provider, ""))
assert.NoError(err)
assert.Equal(*wantConf, readConfig) var readConfig config.Config
err := fileHandler.ReadYAML(constants.ConfigFilename, &readConfig)
assert.NoError(err)
assert.Equal(*wantConf, readConfig)
})
}
}
func TestConfigGenerateWithStackIt(t *testing.T) {
openStackProviders := []string{"stackit"}
for _, openStackProvider := range openStackProviders {
t.Run(openStackProvider, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
fileHandler := file.NewHandler(afero.NewMemMapFs())
cmd := newConfigGenerateCmd()
wantConf := config.Default().WithOpenStackProviderDefaults(openStackProvider)
wantConf.RemoveProviderAndAttestationExcept(cloudprovider.OpenStack)
cg := &configGenerateCmd{log: logger.NewTest(t)}
require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.OpenStack, openStackProvider))
var readConfig config.Config
err := fileHandler.ReadYAML(constants.ConfigFilename, &readConfig)
assert.NoError(err)
assert.Equal(*wantConf, readConfig)
})
}
} }
func TestConfigGenerateDefaultExists(t *testing.T) { func TestConfigGenerateDefaultExists(t *testing.T) {
@ -109,7 +145,7 @@ func TestConfigGenerateDefaultExists(t *testing.T) {
cmd := newConfigGenerateCmd() cmd := newConfigGenerateCmd()
cg := &configGenerateCmd{log: logger.NewTest(t)} cg := &configGenerateCmd{log: logger.NewTest(t)}
require.Error(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown)) require.Error(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown, ""))
} }
func TestConfigGenerateFileFlagRemoved(t *testing.T) { func TestConfigGenerateFileFlagRemoved(t *testing.T) {
@ -120,7 +156,7 @@ func TestConfigGenerateFileFlagRemoved(t *testing.T) {
cmd.ResetFlags() cmd.ResetFlags()
cg := &configGenerateCmd{log: logger.NewTest(t)} cg := &configGenerateCmd{log: logger.NewTest(t)}
require.Error(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown)) require.Error(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown, ""))
} }
func TestConfigGenerateStdOut(t *testing.T) { func TestConfigGenerateStdOut(t *testing.T) {
@ -135,7 +171,7 @@ func TestConfigGenerateStdOut(t *testing.T) {
require.NoError(cmd.Flags().Set("file", "-")) require.NoError(cmd.Flags().Set("file", "-"))
cg := &configGenerateCmd{log: logger.NewTest(t)} cg := &configGenerateCmd{log: logger.NewTest(t)}
require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown)) require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown, ""))
var readConfig config.Config var readConfig config.Config
require.NoError(yaml.NewDecoder(&outBuffer).Decode(&readConfig)) require.NoError(yaml.NewDecoder(&outBuffer).Decode(&readConfig))
@ -153,10 +189,11 @@ func TestNoValidProviderAttestationCombination(t *testing.T) {
{cloudprovider.AWS, variant.AzureTrustedLaunch{}}, {cloudprovider.AWS, variant.AzureTrustedLaunch{}},
{cloudprovider.GCP, variant.AWSNitroTPM{}}, {cloudprovider.GCP, variant.AWSNitroTPM{}},
{cloudprovider.QEMU, variant.GCPSEVES{}}, {cloudprovider.QEMU, variant.GCPSEVES{}},
{cloudprovider.OpenStack, variant.AWSNitroTPM{}},
} }
for _, test := range tests { for _, test := range tests {
t.Run("", func(t *testing.T) { t.Run("", func(t *testing.T) {
_, err := createConfigWithAttestationType(test.provider, test.attestation) _, err := createConfigWithAttestationType(test.provider, "", test.attestation)
assert.Error(err) assert.Error(err)
}) })
} }
@ -204,7 +241,7 @@ func TestValidProviderAttestationCombination(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
t.Run(fmt.Sprintf("Provider:%s,Attestation:%s", test.provider, test.attestation), func(t *testing.T) { t.Run(fmt.Sprintf("Provider:%s,Attestation:%s", test.provider, test.attestation), func(t *testing.T) {
sut, err := createConfigWithAttestationType(test.provider, test.attestation) sut, err := createConfigWithAttestationType(test.provider, "", test.attestation)
assert := assert.New(t) assert := assert.New(t)
assert.NoError(err) assert.NoError(err)
assert.Equal(test.expected, sut.Attestation) assert.Equal(test.expected, sut.Attestation)
@ -259,7 +296,7 @@ func TestAttestationArgument(t *testing.T) {
fileHandler := file.NewHandler(afero.NewMemMapFs()) fileHandler := file.NewHandler(afero.NewMemMapFs())
cg := &configGenerateCmd{log: logger.NewTest(t)} cg := &configGenerateCmd{log: logger.NewTest(t)}
err := cg.configGenerate(cmd, fileHandler, test.provider) err := cg.configGenerate(cmd, fileHandler, test.provider, "")
if test.expectErr { if test.expectErr {
assert.Error(err) assert.Error(err)
} else { } else {

View File

@ -69,7 +69,7 @@ Generate a default configuration file
Generate a default configuration file for your selected cloud provider. Generate a default configuration file for your selected cloud provider.
``` ```
constellation config generate {aws|azure|gcp|openstack|qemu} [flags] constellation config generate {aws|azure|gcp|openstack|qemu|stackit} [flags]
``` ```
### Options ### Options

View File

@ -64,6 +64,10 @@ func (p *Provider) UnmarshalYAML(unmarshal func(any) error) error {
// FromString returns cloud provider from string. // FromString returns cloud provider from string.
func FromString(s string) Provider { func FromString(s string) Provider {
s = strings.ToLower(s) s = strings.ToLower(s)
if isOpenStackProvider(s) {
return OpenStack
}
switch s { switch s {
case "aws": case "aws":
return AWS return AWS
@ -71,11 +75,18 @@ func FromString(s string) Provider {
return Azure return Azure
case "gcp": case "gcp":
return GCP return GCP
case "openstack":
return OpenStack
case "qemu": case "qemu":
return QEMU return QEMU
default: default:
return Unknown return Unknown
} }
} }
// IsOpenStackProvider returns true if the provider is based on OpenStack.
func isOpenStackProvider(s string) bool {
switch strings.ToLower(s) {
case "openstack", "stackit":
return true
}
return false
}

View File

@ -235,6 +235,10 @@ func TestFromString(t *testing.T) {
input: "openstack", input: "openstack",
want: OpenStack, want: OpenStack,
}, },
"stackit": {
input: "stackit",
want: OpenStack,
},
"qemu": { "qemu": {
input: "qemu", input: "qemu",
want: QEMU, want: QEMU,

View File

@ -730,6 +730,29 @@ func (c *Config) Validate(force bool) error {
return &ValidationError{validationErrMsgs: validationErrMsgs} return &ValidationError{validationErrMsgs: validationErrMsgs}
} }
// WithOpenStackProviderDefaults fills the default values for the specific OpenStack provider.
// If the provider is not supported or not an OpenStack provider, the config is returned unchanged.
func (c *Config) WithOpenStackProviderDefaults(openStackProvider string) *Config {
switch openStackProvider {
case "stackit":
c.Provider.OpenStack.Cloud = "stackit"
c.Provider.OpenStack.FlavorID = "2715eabe-3ffc-4c36-b02a-efa8c141a96a"
c.Provider.OpenStack.FloatingIPPoolID = "970ace5c-458f-484a-a660-0903bcfd91ad"
c.Provider.OpenStack.StateDiskType = "storage_premium_perf6"
c.Provider.OpenStack.AuthURL = "https://keystone.api.iaas.eu01.stackit.cloud/v3"
c.Provider.OpenStack.UserDomainName = "portal_mvp"
c.Provider.OpenStack.ProjectDomainName = "portal_mvp"
c.Provider.OpenStack.RegionName = "RegionOne"
c.Provider.OpenStack.DeployYawolLoadBalancer = toPtr(true)
c.Provider.OpenStack.YawolImageID = "43d9bede-1e7a-4ca7-82c5-0a5c72388619"
c.Provider.OpenStack.YawolFlavorID = "3b11b27e-6c73-470d-b595-1d85b95a8cdf"
c.Provider.OpenStack.DeployCSIDriver = toPtr(true)
c.Provider.OpenStack.DirectDownload = toPtr(true)
return c
}
return c
}
// AWSNitroTPM is the configuration for AWS Nitro TPM attestation. // AWSNitroTPM is the configuration for AWS Nitro TPM attestation.
type AWSNitroTPM struct { type AWSNitroTPM struct {
// description: | // description: |