From dd571464612d2c0e8234b2631a720d4e1d6531d3 Mon Sep 17 00:00:00 2001 From: Wonkun Kim Date: Thu, 7 Jul 2022 02:24:05 +1000 Subject: [PATCH] Support Cloudstack multiple endpoint for preflight checks (#2559) * Support Cloudstack multiple endpoint for preflight checks * Update for review comments * Fix unit test failures * Remove debug print * Remove unnecessary file * Remove multierror module * Update to use single cmk object instead of list or map of multiple cmks * Remove ValidateCloudStackConnection when cleaning up VMs * Remove NewProviderCustomNet because it's the same as NewProvider * Avoid using 'failed to' in errors * Remove localAvailabilityZones from Validator * Remove multipleZone from ValidateNetworkPresent * Return list of errors CleanUpCloudstackTestResources * Increase unit test coverage * Increase unit test coverage #2 * Change error format * Change function names to be more descriptive - ValidateZonePresent -> ValidateZoneAndGetId - ValidateDomainPresent -> ValidateDomainAndGetId Change return value of ValidateDomainAndGetId - from CloudStackResourceIdentifier to string because only domainId is used * Add default verifySslValue as a constant * Fix a bug where empty Zone IDs are used in validateMachineConfig * Add error message when validating management api endpoint fails * Make MarkPass message more readable * Fix presubmit job failure --- internal/test/cleanup/cleanup.go | 14 +- pkg/dependencies/factory.go | 3 +- pkg/dependencies/factory_test.go | 7 + .../cloudstack_config_multiple_profiles.ini | 11 + pkg/executables/builder.go | 4 +- pkg/executables/cmk.go | 215 +++--- pkg/executables/cmk_cmd_builder.go | 4 - pkg/executables/cmk_test.go | 624 +++++++++++------- pkg/executables/config/cmk.ini | 15 +- .../testdata/cmk_list_network_multiple.json | 135 ++++ .../testdata/cmk_list_network_none.json | 5 + .../testdata/cmk_list_template_multiple.json | 89 +++ .../testdata/cmk_list_template_none.json | 6 + .../testdata/cmk_list_zone_multiple.json | 27 + .../testdata/cmk_list_zone_none.json | 5 + pkg/providers/cloudstack/cloudstack.go | 75 +-- pkg/providers/cloudstack/cloudstack_test.go | 147 ++++- pkg/providers/cloudstack/decoder/decoder.go | 76 ++- .../cloudstack/decoder/decoder_test.go | 251 ++++--- pkg/providers/cloudstack/mocks/client.go | 93 +-- .../cloudstack_config_invalid_format.ini | 5 + .../cloudstack_config_invalid_verifyssl.ini | 5 + .../cloudstack_config_missing_apikey.ini | 4 + .../cloudstack_config_missing_apiurl.ini | 4 + .../cloudstack_config_missing_secretkey.ini | 4 + .../cloudstack_config_missing_verifyssl.ini | 4 + .../cloudstack_config_multiple_profiles.ini | 11 + .../cloudstack_config_no_sections.ini | 4 + .../testdata/cloudstack_config_valid.ini | 5 + .../cloudstack/testdata/cluster_invalid.yaml | 123 ++++ .../cluster_main_with_availability_zones.yaml | 139 ++++ pkg/providers/cloudstack/validator.go | 179 +++-- pkg/providers/cloudstack/validator_test.go | 246 ++++--- release/go.sum | 1 - release/pkg/generate_spec.go | 1 - .../test/testdata/main-bundle-release.yaml | 122 ++-- test/e2e/tools/eks-anywhere-test-tool/go.sum | 4 - 37 files changed, 1818 insertions(+), 849 deletions(-) create mode 100644 pkg/dependencies/testdata/cloudstack_config_multiple_profiles.ini create mode 100644 pkg/executables/testdata/cmk_list_network_multiple.json create mode 100644 pkg/executables/testdata/cmk_list_network_none.json create mode 100644 pkg/executables/testdata/cmk_list_template_multiple.json create mode 100644 pkg/executables/testdata/cmk_list_template_none.json create mode 100644 pkg/executables/testdata/cmk_list_zone_multiple.json create mode 100644 pkg/executables/testdata/cmk_list_zone_none.json create mode 100644 pkg/providers/cloudstack/testdata/cloudstack_config_invalid_format.ini create mode 100644 pkg/providers/cloudstack/testdata/cloudstack_config_invalid_verifyssl.ini create mode 100644 pkg/providers/cloudstack/testdata/cloudstack_config_missing_apikey.ini create mode 100644 pkg/providers/cloudstack/testdata/cloudstack_config_missing_apiurl.ini create mode 100644 pkg/providers/cloudstack/testdata/cloudstack_config_missing_secretkey.ini create mode 100644 pkg/providers/cloudstack/testdata/cloudstack_config_missing_verifyssl.ini create mode 100644 pkg/providers/cloudstack/testdata/cloudstack_config_multiple_profiles.ini create mode 100644 pkg/providers/cloudstack/testdata/cloudstack_config_no_sections.ini create mode 100644 pkg/providers/cloudstack/testdata/cloudstack_config_valid.ini create mode 100644 pkg/providers/cloudstack/testdata/cluster_invalid.yaml create mode 100644 pkg/providers/cloudstack/testdata/cluster_main_with_availability_zones.yaml diff --git a/internal/test/cleanup/cleanup.go b/internal/test/cleanup/cleanup.go index 4bf0b12e7229..fdbb41b91515 100644 --- a/internal/test/cleanup/cleanup.go +++ b/internal/test/cleanup/cleanup.go @@ -99,12 +99,18 @@ func CleanUpCloudstackTestResources(ctx context.Context, clusterName string, dry if err != nil { return fmt.Errorf("building cmk executable: %v", err) } - cmk := executableBuilder.BuildCmkExecutable(tmpWriter, *execConfig) + cmk := executableBuilder.BuildCmkExecutable(tmpWriter, execConfig.Profiles) defer cmk.Close(ctx) - if err := cmk.ValidateCloudStackConnection(ctx); err != nil { - return fmt.Errorf("validating cloudstack connection with cloudmonkey: %v", err) + errorsMap := map[string]error{} + for _, profile := range execConfig.Profiles { + if err := cmk.CleanupVms(ctx, profile.Name, clusterName, dryRun); err != nil { + errorsMap[profile.Name] = err + } } - return cmk.CleanupVms(ctx, clusterName, dryRun) + if len(errorsMap) > 0 { + return fmt.Errorf("cleaning up VMs: %+v", errorsMap) + } + return nil } diff --git a/pkg/dependencies/factory.go b/pkg/dependencies/factory.go index e78c70bb2fad..8c01b4cf683d 100644 --- a/pkg/dependencies/factory.go +++ b/pkg/dependencies/factory.go @@ -418,12 +418,13 @@ func (f *Factory) WithCmk() *Factory { if f.dependencies.Cmk != nil { return nil } + execConfig, err := decoder.ParseCloudStackSecret() if err != nil { return fmt.Errorf("building cmk executable: %v", err) } - f.dependencies.Cmk = f.executableBuilder.BuildCmkExecutable(f.dependencies.Writer, *execConfig) + f.dependencies.Cmk = f.executableBuilder.BuildCmkExecutable(f.dependencies.Writer, execConfig.Profiles) f.dependencies.closers = append(f.dependencies.closers, f.dependencies.Cmk) return nil diff --git a/pkg/dependencies/factory_test.go b/pkg/dependencies/factory_test.go index 0f2b72e55bbd..92272110d642 100644 --- a/pkg/dependencies/factory_test.go +++ b/pkg/dependencies/factory_test.go @@ -2,6 +2,7 @@ package dependencies_test import ( "context" + "encoding/base64" "os" "testing" @@ -13,6 +14,7 @@ import ( "github.com/aws/eks-anywhere/pkg/cluster" "github.com/aws/eks-anywhere/pkg/config" "github.com/aws/eks-anywhere/pkg/dependencies" + "github.com/aws/eks-anywhere/pkg/providers/cloudstack/decoder" "github.com/aws/eks-anywhere/pkg/version" "github.com/aws/eks-anywhere/release/api/v1alpha1" ) @@ -107,6 +109,10 @@ func TestFactoryBuildWithClusterManagerWithoutCliConfig(t *testing.T) { } func TestFactoryBuildWithMultipleDependencies(t *testing.T) { + configString := test.ReadFile(t, "testdata/cloudstack_config_multiple_profiles.ini") + encodedConfig := base64.StdEncoding.EncodeToString([]byte(configString)) + t.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, encodedConfig) + tt := newTest(t, vsphere) deps, err := dependencies.NewFactory(). UseExecutableImage("image:1"). @@ -125,6 +131,7 @@ func TestFactoryBuildWithMultipleDependencies(t *testing.T) { WithCAPIManager(). WithManifestReader(). WithUnAuthKubeClient(). + WithCmk(). Build(context.Background()) tt.Expect(err).To(BeNil()) diff --git a/pkg/dependencies/testdata/cloudstack_config_multiple_profiles.ini b/pkg/dependencies/testdata/cloudstack_config_multiple_profiles.ini new file mode 100644 index 000000000000..ead54c30f37c --- /dev/null +++ b/pkg/dependencies/testdata/cloudstack_config_multiple_profiles.ini @@ -0,0 +1,11 @@ +[Global] +verify-ssl = false +api-key = test-key1 +secret-key = test-secret1 +api-url = http://127.16.0.1:8080/client/api + +[Instance2] +verify-ssl = true +api-key = test-key2 +secret-key = test-secret2 +api-url = http://127.16.0.2:8080/client/api diff --git a/pkg/executables/builder.go b/pkg/executables/builder.go index 5c94c66ef9b7..bd03ab63b20a 100644 --- a/pkg/executables/builder.go +++ b/pkg/executables/builder.go @@ -41,8 +41,8 @@ func (b *ExecutableBuilder) BuildGovcExecutable(writer filewriter.FileWriter, op return NewGovc(b.buildExecutable(govcPath), writer, opts...) } -func (b *ExecutableBuilder) BuildCmkExecutable(writer filewriter.FileWriter, execConfig decoder.CloudStackExecConfig) *Cmk { - return NewCmk(b.buildExecutable(cmkPath), writer, execConfig) +func (b *ExecutableBuilder) BuildCmkExecutable(writer filewriter.FileWriter, configs []decoder.CloudStackProfileConfig) *Cmk { + return NewCmk(b.buildExecutable(cmkPath), writer, configs) } func (b *ExecutableBuilder) BuildAwsCli() *AwsCli { diff --git a/pkg/executables/cmk.go b/pkg/executables/cmk.go index 53cce0553f89..49648875481f 100644 --- a/pkg/executables/cmk.go +++ b/pkg/executables/cmk.go @@ -23,7 +23,7 @@ var cmkConfigTemplate string const ( cmkPath = "cmk" - cmkConfigFileName = "cmk_tmp.ini" + cmkConfigFileNameTemplate = "cmk_%s.ini" Shared = "Shared" defaultCloudStackPreflightTimeout = "30" rootDomain = "ROOT" @@ -34,22 +34,14 @@ const ( type Cmk struct { writer filewriter.FileWriter executable Executable - config decoder.CloudStackExecConfig -} - -type cmkExecConfig struct { - CloudStackApiKey string - CloudStackSecretKey string - CloudStackManagementUrl string - CloudMonkeyVerifyCert string - CloudMonkeyTimeout string + configMap map[string]decoder.CloudStackProfileConfig } func (c *Cmk) Close(ctx context.Context) error { return nil } -func (c *Cmk) ValidateTemplatePresent(ctx context.Context, domainId string, zoneId string, account string, template v1alpha1.CloudStackResourceIdentifier) error { +func (c *Cmk) ValidateTemplatePresent(ctx context.Context, profile string, domainId string, zoneId string, account string, template v1alpha1.CloudStackResourceIdentifier) error { command := newCmkCommand("list templates") applyCmkArgs(&command, appendArgs("templatefilter=all"), appendArgs("listall=true")) if len(template.Id) > 0 { @@ -65,7 +57,7 @@ func (c *Cmk) ValidateTemplatePresent(ctx context.Context, domainId string, zone applyCmkArgs(&command, withCloudStackAccount(account)) } } - result, err := c.exec(ctx, command...) + result, err := c.exec(ctx, profile, command...) if err != nil { return fmt.Errorf("getting templates info - %s: %v", result.String(), err) } @@ -88,7 +80,7 @@ func (c *Cmk) ValidateTemplatePresent(ctx context.Context, domainId string, zone return nil } -func (c *Cmk) ValidateServiceOfferingPresent(ctx context.Context, zoneId string, serviceOffering v1alpha1.CloudStackResourceIdentifier) error { +func (c *Cmk) ValidateServiceOfferingPresent(ctx context.Context, profile string, zoneId string, serviceOffering v1alpha1.CloudStackResourceIdentifier) error { command := newCmkCommand("list serviceofferings") if len(serviceOffering.Id) > 0 { applyCmkArgs(&command, withCloudStackId(serviceOffering.Id)) @@ -96,7 +88,7 @@ func (c *Cmk) ValidateServiceOfferingPresent(ctx context.Context, zoneId string, applyCmkArgs(&command, withCloudStackName(serviceOffering.Name)) } applyCmkArgs(&command, withCloudStackZoneId(zoneId)) - result, err := c.exec(ctx, command...) + result, err := c.exec(ctx, profile, command...) if err != nil { return fmt.Errorf("getting service offerings info - %s: %v", result.String(), err) } @@ -120,7 +112,7 @@ func (c *Cmk) ValidateServiceOfferingPresent(ctx context.Context, zoneId string, return nil } -func (c *Cmk) ValidateDiskOfferingPresent(ctx context.Context, zoneId string, diskOffering v1alpha1.CloudStackResourceDiskOffering) error { +func (c *Cmk) ValidateDiskOfferingPresent(ctx context.Context, profile string, zoneId string, diskOffering v1alpha1.CloudStackResourceDiskOffering) error { command := newCmkCommand("list diskofferings") if len(diskOffering.Id) > 0 { applyCmkArgs(&command, withCloudStackId(diskOffering.Id)) @@ -128,7 +120,7 @@ func (c *Cmk) ValidateDiskOfferingPresent(ctx context.Context, zoneId string, di applyCmkArgs(&command, withCloudStackName(diskOffering.Name)) } applyCmkArgs(&command, withCloudStackZoneId(zoneId)) - result, err := c.exec(ctx, command...) + result, err := c.exec(ctx, profile, command...) if err != nil { return fmt.Errorf("getting disk offerings info - %s: %v", result.String(), err) } @@ -158,7 +150,7 @@ func (c *Cmk) ValidateDiskOfferingPresent(ctx context.Context, zoneId string, di return nil } -func (c *Cmk) ValidateAffinityGroupsPresent(ctx context.Context, domainId string, account string, affinityGroupIds []string) error { +func (c *Cmk) ValidateAffinityGroupsPresent(ctx context.Context, profile string, domainId string, account string, affinityGroupIds []string) error { for _, affinityGroupId := range affinityGroupIds { command := newCmkCommand("list affinitygroups") applyCmkArgs(&command, withCloudStackId(affinityGroupId)) @@ -171,7 +163,7 @@ func (c *Cmk) ValidateAffinityGroupsPresent(ctx context.Context, domainId string } } - result, err := c.exec(ctx, command...) + result, err := c.exec(ctx, profile, command...) if err != nil { return fmt.Errorf("getting affinity group info - %s: %v", result.String(), err) } @@ -195,62 +187,57 @@ func (c *Cmk) ValidateAffinityGroupsPresent(ctx context.Context, domainId string return nil } -func (c *Cmk) ValidateZonesPresent(ctx context.Context, zones []v1alpha1.CloudStackZone) ([]v1alpha1.CloudStackResourceIdentifier, error) { - var zoneIdentifiers []v1alpha1.CloudStackResourceIdentifier - for _, zone := range zones { - command := newCmkCommand("list zones") - if len(zone.Id) > 0 { - applyCmkArgs(&command, withCloudStackId(zone.Id)) - } else { - applyCmkArgs(&command, withCloudStackName(zone.Name)) - } - result, err := c.exec(ctx, command...) - if err != nil { - return nil, fmt.Errorf("getting zones info - %s: %v", result.String(), err) - } - if result.Len() == 0 { - return nil, fmt.Errorf("zone %s not found", zone) - } +func (c *Cmk) ValidateZoneAndGetId(ctx context.Context, profile string, zone v1alpha1.CloudStackZone) (string, error) { + command := newCmkCommand("list zones") + if len(zone.Id) > 0 { + applyCmkArgs(&command, withCloudStackId(zone.Id)) + } else { + applyCmkArgs(&command, withCloudStackName(zone.Name)) + } + result, err := c.exec(ctx, profile, command...) + if err != nil { + return "", fmt.Errorf("getting zones info - %s: %v", result.String(), err) + } + if result.Len() == 0 { + return "", fmt.Errorf("zone %s not found", zone) + } - response := struct { - CmkZones []cmkResourceIdentifier `json:"zone"` - }{} - if err = json.Unmarshal(result.Bytes(), &response); err != nil { - return nil, fmt.Errorf("parsing response into json: %v", err) - } - cmkZones := response.CmkZones - if len(cmkZones) > 1 { - return nil, fmt.Errorf("duplicate zone %s found", zone) - } else if len(zones) == 0 { - return nil, fmt.Errorf("zone %s not found", zone) - } else { - zoneIdentifiers = append(zoneIdentifiers, v1alpha1.CloudStackResourceIdentifier{Name: cmkZones[0].Name, Id: cmkZones[0].Id}) - } + response := struct { + CmkZones []cmkResourceIdentifier `json:"zone"` + }{} + if err = json.Unmarshal(result.Bytes(), &response); err != nil { + return "", fmt.Errorf("parsing response into json: %v", err) + } + cmkZones := response.CmkZones + if len(cmkZones) > 1 { + return "", fmt.Errorf("duplicate zone %s found", zone) + } else if len(cmkZones) == 0 { + return "", fmt.Errorf("zone %s not found", zone) } - return zoneIdentifiers, nil + return cmkZones[0].Id, nil } -func (c *Cmk) ValidateDomainPresent(ctx context.Context, domain string) (v1alpha1.CloudStackResourceIdentifier, error) { - domainIdentifier := v1alpha1.CloudStackResourceIdentifier{Name: domain, Id: ""} +func (c *Cmk) ValidateDomainAndGetId(ctx context.Context, profile string, domain string) (string, error) { + domainId := "" command := newCmkCommand("list domains") // "list domains" API does not support querying by domain path, so here we extract the domain name which is the last part of the input domain tokens := strings.Split(domain, domainDelimiter) domainName := tokens[len(tokens)-1] applyCmkArgs(&command, withCloudStackName(domainName), appendArgs("listall=true")) - result, err := c.exec(ctx, command...) + result, err := c.exec(ctx, profile, command...) if err != nil { - return domainIdentifier, fmt.Errorf("getting domain info - %s: %v", result.String(), err) + return domainId, fmt.Errorf("getting domain info - %s: %v", result.String(), err) } if result.Len() == 0 { - return domainIdentifier, fmt.Errorf("domain %s not found", domain) + return domainId, fmt.Errorf("domain %s not found", domain) } response := struct { CmkDomains []cmkDomain `json:"domain"` }{} if err = json.Unmarshal(result.Bytes(), &response); err != nil { - return domainIdentifier, fmt.Errorf("parsing response into json: %v", err) + return domainId, fmt.Errorf("parsing response into json: %v", err) } domains := response.CmkDomains var domainPath string @@ -261,26 +248,19 @@ func (c *Cmk) ValidateDomainPresent(ctx context.Context, domain string) (v1alpha } for _, d := range domains { if d.Path == domainPath { - domainIdentifier.Id = d.Id - domainIdentifier.Name = d.Name + domainId = d.Id break } } - if domainIdentifier.Id == "" { - return domainIdentifier, fmt.Errorf("domain(s) found for domain name %s, but not found a domain with domain path %s", domain, domainPath) + if domainId == "" { + return domainId, fmt.Errorf("domain(s) found for domain name %s, but not found a domain with domain path %s", domain, domainPath) } - return domainIdentifier, nil + return domainId, nil } -func (c *Cmk) ValidateNetworkPresent(ctx context.Context, domainId string, zone v1alpha1.CloudStackZone, zones []v1alpha1.CloudStackResourceIdentifier, account string, multipleZone bool) error { +func (c *Cmk) ValidateNetworkPresent(ctx context.Context, profile string, domainId string, network v1alpha1.CloudStackResourceIdentifier, zoneId string, account string) error { command := newCmkCommand("list networks") - if len(zone.Network.Id) > 0 { - applyCmkArgs(&command, withCloudStackId(zone.Network.Id)) - } - if multipleZone { - applyCmkArgs(&command, withCloudStackNetworkType(Shared)) - } // account must be specified within a domainId // domainId can be specified without account if len(domainId) > 0 { @@ -289,27 +269,13 @@ func (c *Cmk) ValidateNetworkPresent(ctx context.Context, domainId string, zone applyCmkArgs(&command, withCloudStackAccount(account)) } } - var zoneId string - var err error - if len(zone.Id) > 0 { - zoneId = zone.Id - } else { - zoneId, err = getZoneIdByName(zones, zone.Name) - if err != nil { - return fmt.Errorf("getting zone id by name %s: %v", zone.Name, err) - } - } applyCmkArgs(&command, withCloudStackZoneId(zoneId)) - result, err := c.exec(ctx, command...) + result, err := c.exec(ctx, profile, command...) if err != nil { return fmt.Errorf("getting network info - %s: %v", result.String(), err) } if result.Len() == 0 { - if multipleZone { - return fmt.Errorf("%s network %s not found in zone %s", Shared, zone.Network, zone) - } else { - return fmt.Errorf("network %s not found in zone %s", zone.Network, zone) - } + return fmt.Errorf("network %s not found in zone %s", network, zoneId) } response := struct { @@ -324,40 +290,32 @@ func (c *Cmk) ValidateNetworkPresent(ctx context.Context, domainId string, zone // if network id and name are both provided, the following code is to confirm name matches return value retrieved by id. // if only name is provided, the following code is to only get networks with specified name. - if len(zone.Network.Name) > 0 { + if len(network.Name) > 0 { networks = []cmkResourceIdentifier{} for _, net := range response.CmkNetworks { - if net.Name == zone.Network.Name { + if net.Name == network.Name { networks = append(networks, net) } } } if len(networks) > 1 { - return fmt.Errorf("duplicate network %s found", zone.Network) + return fmt.Errorf("duplicate network %s found", network) } else if len(networks) == 0 { - if multipleZone { - return fmt.Errorf("%s network %s not found in zoneRef %s", Shared, zone.Network, zone) - } else { - return fmt.Errorf("network %s not found in zoneRef %s", zone.Network, zone) - } + return fmt.Errorf("network %s not found in zoneRef %s", network, zoneId) } return nil } -func getZoneIdByName(zones []v1alpha1.CloudStackResourceIdentifier, zoneName string) (string, error) { - for _, zoneIdentifier := range zones { - if zoneName == zoneIdentifier.Name { - return zoneIdentifier.Id, nil - } +func (c *Cmk) ValidateAccountPresent(ctx context.Context, profile string, account string, domainId string) error { + // If account is not specified then no need to check its presence + if len(account) == 0 { + return nil } - return "", fmt.Errorf("zoneId not found for zone %s", zoneName) -} -func (c *Cmk) ValidateAccountPresent(ctx context.Context, account string, domainId string) error { command := newCmkCommand("list accounts") applyCmkArgs(&command, withCloudStackName(account), withCloudStackDomainId(domainId)) - result, err := c.exec(ctx, command...) + result, err := c.exec(ctx, profile, command...) if err != nil { return fmt.Errorf("getting accounts info - %s: %v", result.String(), err) } @@ -380,18 +338,31 @@ func (c *Cmk) ValidateAccountPresent(ctx context.Context, account string, domain return nil } -func NewCmk(executable Executable, writer filewriter.FileWriter, config decoder.CloudStackExecConfig) *Cmk { +func NewCmk(executable Executable, writer filewriter.FileWriter, configs []decoder.CloudStackProfileConfig) *Cmk { + configMap := map[string]decoder.CloudStackProfileConfig{} + for _, config := range configs { + configMap[config.Name] = config + } + return &Cmk{ writer: writer, executable: executable, - config: config, + configMap: configMap, + } +} + +func (c *Cmk) GetManagementApiEndpoint(profile string) (string, error) { + config, exist := c.configMap[profile] + if exist { + return config.ManagementUrl, nil } + return "", fmt.Errorf("profile %s does not exist", profile) } // ValidateCloudStackConnection Calls `cmk sync` to ensure that the endpoint and credentials + domain are valid -func (c *Cmk) ValidateCloudStackConnection(ctx context.Context) error { +func (c *Cmk) ValidateCloudStackConnection(ctx context.Context, profile string) error { command := newCmkCommand("sync") - buffer, err := c.exec(ctx, command...) + buffer, err := c.exec(ctx, profile, command...) if err != nil { return fmt.Errorf("validating cloudstack connection for cmk: %s: %v", buffer.String(), err) } @@ -399,10 +370,10 @@ func (c *Cmk) ValidateCloudStackConnection(ctx context.Context) error { return nil } -func (c *Cmk) CleanupVms(ctx context.Context, clusterName string, dryRun bool) error { +func (c *Cmk) CleanupVms(ctx context.Context, profile string, clusterName string, dryRun bool) error { command := newCmkCommand("list virtualmachines") applyCmkArgs(&command, withCloudStackKeyword(clusterName), appendArgs("listall=true")) - result, err := c.exec(ctx, command...) + result, err := c.exec(ctx, profile, command...) if err != nil { return fmt.Errorf("listing virtual machines in cluster %s: %s: %v", clusterName, result.String(), err) } @@ -422,13 +393,13 @@ func (c *Cmk) CleanupVms(ctx context.Context, clusterName string, dryRun bool) e } stopCommand := newCmkCommand("stop virtualmachine") applyCmkArgs(&stopCommand, withCloudStackId(vm.Id), appendArgs("forced=true")) - stopResult, err := c.exec(ctx, stopCommand...) + stopResult, err := c.exec(ctx, profile, stopCommand...) if err != nil { return fmt.Errorf("stopping virtual machine with name %s and id %s: %s: %v", vm.Name, vm.Id, stopResult.String(), err) } destroyCommand := newCmkCommand("destroy virtualmachine") applyCmkArgs(&destroyCommand, withCloudStackId(vm.Id), appendArgs("expunge=true")) - destroyResult, err := c.exec(ctx, destroyCommand...) + destroyResult, err := c.exec(ctx, profile, destroyCommand...) if err != nil { return fmt.Errorf("destroying virtual machine with name %s and id %s: %s: %v", vm.Name, vm.Id, destroyResult.String(), err) } @@ -438,38 +409,36 @@ func (c *Cmk) CleanupVms(ctx context.Context, clusterName string, dryRun bool) e return nil } -func (c *Cmk) exec(ctx context.Context, args ...string) (stdout bytes.Buffer, err error) { +func (c *Cmk) exec(ctx context.Context, profile string, args ...string) (stdout bytes.Buffer, err error) { if err != nil { return bytes.Buffer{}, fmt.Errorf("failed get environment map: %v", err) } - configFile, err := c.buildCmkConfigFile() + + configFile, err := c.buildCmkConfigFile(profile) if err != nil { return bytes.Buffer{}, fmt.Errorf("failed cmk validations: %v", err) } - argsWithConfigFile := append([]string{"-c", configFile}, args...) + argsWithConfigFile := append([]string{"-c", configFile}, args...) return c.executable.Execute(ctx, argsWithConfigFile...) } -func (c *Cmk) buildCmkConfigFile() (configFile string, err error) { +func (c *Cmk) buildCmkConfigFile(profile string) (configFile string, err error) { + config, exist := c.configMap[profile] + if !exist { + return "", fmt.Errorf("profile %s does not exist", profile) + } + t := templater.New(c.writer) - cloudstackPreflightTimeout := defaultCloudStackPreflightTimeout + config.Timeout = defaultCloudStackPreflightTimeout if timeout, isSet := os.LookupEnv("CLOUDSTACK_PREFLIGHT_TIMEOUT"); isSet { if _, err := strconv.ParseUint(timeout, 10, 16); err != nil { return "", fmt.Errorf("CLOUDSTACK_PREFLIGHT_TIMEOUT must be a number: %v", err) } - cloudstackPreflightTimeout = timeout - } - - cmkConfig := &cmkExecConfig{ - CloudStackApiKey: c.config.ApiKey, - CloudStackSecretKey: c.config.SecretKey, - CloudStackManagementUrl: c.config.ManagementUrl, - CloudMonkeyVerifyCert: c.config.VerifySsl, - CloudMonkeyTimeout: cloudstackPreflightTimeout, + config.Timeout = timeout } - writtenFileName, err := t.WriteToFile(cmkConfigTemplate, cmkConfig, cmkConfigFileName) + writtenFileName, err := t.WriteToFile(cmkConfigTemplate, config, fmt.Sprintf(cmkConfigFileNameTemplate, profile)) if err != nil { return "", fmt.Errorf("creating file for cmk config: %v", err) } diff --git a/pkg/executables/cmk_cmd_builder.go b/pkg/executables/cmk_cmd_builder.go index 289bd148ad0a..672d2b1dc859 100644 --- a/pkg/executables/cmk_cmd_builder.go +++ b/pkg/executables/cmk_cmd_builder.go @@ -35,10 +35,6 @@ func withCloudStackZoneId(zoneId string) cmkCommandArgs { return appendArgs(fmt.Sprintf("zoneid=\"%s\"", zoneId)) } -func withCloudStackNetworkType(networkType string) cmkCommandArgs { - return appendArgs(fmt.Sprintf("type=\"%s\"", networkType)) -} - func withCloudStackId(id string) cmkCommandArgs { return appendArgs(fmt.Sprintf("id=\"%s\"", id)) } diff --git a/pkg/executables/cmk_test.go b/pkg/executables/cmk_test.go index 05eb31d73c28..852a04cac0fc 100644 --- a/pkg/executables/cmk_test.go +++ b/pkg/executables/cmk_test.go @@ -10,6 +10,7 @@ import ( "testing" "github.com/golang/mock/gomock" + . "github.com/onsi/gomega" "github.com/aws/eks-anywhere/internal/test" "github.com/aws/eks-anywhere/pkg/api/v1alpha1" @@ -19,23 +20,41 @@ import ( ) const ( - cmkConfigFileName = "cmk_tmp.ini" - accountName = "account1" - rootDomain = "ROOT" - rootDomainId = "5300cdac-74d5-11ec-8696-c81f66d3e965" - domain = "foo/domain1" - domainName = "domain1" - domainId = "7700cdac-74d5-11ec-8696-c81f66d3e965" - domain2 = "foo/bar/domain1" - domain2Name = "domain1" - domain2Id = "8800cdac-74d5-11ec-8696-c81f66d3e965" - zoneId = "4e3b338d-87a6-4189-b931-a1747edeea8f" + cmkConfigFileName = "cmk_test_name.ini" + cmkConfigFileName2 = "cmk_test_name_2.ini" + accountName = "account1" + rootDomain = "ROOT" + rootDomainId = "5300cdac-74d5-11ec-8696-c81f66d3e965" + domain = "foo/domain1" + domainName = "domain1" + domainId = "7700cdac-74d5-11ec-8696-c81f66d3e965" + domain2 = "foo/bar/domain1" + domain2Name = "domain1" + domain2Id = "8800cdac-74d5-11ec-8696-c81f66d3e965" + zoneId = "4e3b338d-87a6-4189-b931-a1747edeea8f" ) var execConfig = decoder.CloudStackExecConfig{ - ApiKey: "test", - SecretKey: "test", - ManagementUrl: "http://1.1.1.1:8080/client/api", + Profiles: []decoder.CloudStackProfileConfig{ + { + Name: "test_name", + ApiKey: "test", + SecretKey: "test", + ManagementUrl: "http://1.1.1.1:8080/client/api", + }, + }, +} + +var execConfigWithMultipleProfiles = decoder.CloudStackExecConfig{ + Profiles: []decoder.CloudStackProfileConfig{ + execConfig.Profiles[0], + { + Name: "test_name_2", + ApiKey: "test_2", + SecretKey: "test_2", + ManagementUrl: "http://1.1.1.1:8080/client/api_2", + }, + }, } var zones = []v1alpha1.CloudStackZone{ @@ -93,8 +112,32 @@ func TestValidateCloudStackConnectionSuccess(t *testing.T) { configFilePath, _ := filepath.Abs(filepath.Join(writer.Dir(), "generated", cmkConfigFileName)) expectedArgs := []string{"-c", configFilePath, "sync"} executable.EXPECT().Execute(ctx, expectedArgs).Return(bytes.Buffer{}, nil) - c := executables.NewCmk(executable, writer, execConfig) - err := c.ValidateCloudStackConnection(ctx) + c := executables.NewCmk(executable, writer, execConfig.Profiles) + err := c.ValidateCloudStackConnection(ctx, execConfig.Profiles[0].Name) + if err != nil { + t.Fatalf("Cmk.ValidateCloudStackConnection() error = %v, want nil", err) + } +} + +func TestValidateMultipleCloudStackProfiles(t *testing.T) { + _, writer := test.NewWriter(t) + ctx := context.Background() + mockCtrl := gomock.NewController(t) + + executable := mockexecutables.NewMockExecutable(mockCtrl) + configFilePath, _ := filepath.Abs(filepath.Join(writer.Dir(), "generated", cmkConfigFileName)) + expectedArgs := []string{"-c", configFilePath, "sync"} + executable.EXPECT().Execute(ctx, expectedArgs).Return(bytes.Buffer{}, nil) + configFilePath2, _ := filepath.Abs(filepath.Join(writer.Dir(), "generated", cmkConfigFileName2)) + expectedArgs2 := []string{"-c", configFilePath2, "sync"} + executable.EXPECT().Execute(ctx, expectedArgs2).Return(bytes.Buffer{}, nil) + + c := executables.NewCmk(executable, writer, execConfigWithMultipleProfiles.Profiles) + err := c.ValidateCloudStackConnection(ctx, execConfigWithMultipleProfiles.Profiles[0].Name) + if err != nil { + t.Fatalf("Cmk.ValidateCloudStackConnection() error = %v, want nil", err) + } + err = c.ValidateCloudStackConnection(ctx, execConfigWithMultipleProfiles.Profiles[1].Name) if err != nil { t.Fatalf("Cmk.ValidateCloudStackConnection() error = %v, want nil", err) } @@ -109,8 +152,8 @@ func TestValidateCloudStackConnectionError(t *testing.T) { configFilePath, _ := filepath.Abs(filepath.Join(writer.Dir(), "generated", cmkConfigFileName)) expectedArgs := []string{"-c", configFilePath, "sync"} executable.EXPECT().Execute(ctx, expectedArgs).Return(bytes.Buffer{}, errors.New("cmk test error")) - c := executables.NewCmk(executable, writer, execConfig) - err := c.ValidateCloudStackConnection(ctx) + c := executables.NewCmk(executable, writer, execConfig.Profiles) + err := c.ValidateCloudStackConnection(ctx, execConfig.Profiles[0].Name) if err == nil { t.Fatalf("Cmk.ValidateCloudStackConnection() didn't throw expected error") } @@ -136,7 +179,7 @@ func TestCmkCleanupVms(t *testing.T) { "list", "virtualmachines", fmt.Sprintf("keyword=\"%s\"", clusterName), "listall=true", }}, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.CleanupVms(ctx, clusterName, false) + return cmk.CleanupVms(ctx, execConfig.Profiles[0].Name, clusterName, false) }, cmkResponseError: nil, wantErr: true, @@ -151,7 +194,7 @@ func TestCmkCleanupVms(t *testing.T) { }, }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.CleanupVms(ctx, clusterName, true) + return cmk.CleanupVms(ctx, execConfig.Profiles[0].Name, clusterName, true) }, cmkResponseError: nil, wantErr: false, @@ -164,7 +207,7 @@ func TestCmkCleanupVms(t *testing.T) { "list", "virtualmachines", fmt.Sprintf("keyword=\"%s\"", clusterName), "listall=true", }}, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.CleanupVms(ctx, clusterName, false) + return cmk.CleanupVms(ctx, execConfig.Profiles[0].Name, clusterName, false) }, cmkResponseError: nil, wantErr: true, @@ -177,7 +220,7 @@ func TestCmkCleanupVms(t *testing.T) { "list", "virtualmachines", fmt.Sprintf("keyword=\"%s\"", clusterName), "listall=true", }}, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.CleanupVms(ctx, clusterName, false) + return cmk.CleanupVms(ctx, execConfig.Profiles[0].Name, clusterName, false) }, cmkResponseError: nil, wantErr: true, @@ -200,7 +243,7 @@ func TestCmkCleanupVms(t *testing.T) { }, }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.CleanupVms(ctx, clusterName, false) + return cmk.CleanupVms(ctx, execConfig.Profiles[0].Name, clusterName, false) }, cmkResponseError: nil, wantErr: false, @@ -223,7 +266,7 @@ func TestCmkCleanupVms(t *testing.T) { executable.EXPECT().Execute(ctx, argsList). Return(*bytes.NewBufferString(fileContent), tt.cmkResponseError) } - cmk := executables.NewCmk(executable, writer, execConfig) + cmk := executables.NewCmk(executable, writer, execConfig.Profiles) err := tt.cmkFunc(*cmk, ctx) if tt.wantErr && err != nil || !tt.wantErr && err == nil { return @@ -237,14 +280,13 @@ func TestCmkListOperations(t *testing.T) { _, writer := test.NewWriter(t) configFilePath, _ := filepath.Abs(filepath.Join(writer.Dir(), "generated", cmkConfigFileName)) tests := []struct { - testName string - argumentsExecCall []string - jsonResponseFile string - cmkFunc func(cmk executables.Cmk, ctx context.Context) error - cmkResponseError error - wantErr bool - shouldSecondCallOccur bool - wantResultCount int + testName string + argumentsExecCall []string + jsonResponseFile string + cmkFunc func(cmk executables.Cmk, ctx context.Context) error + cmkResponseError error + wantErr bool + wantResultCount int }{ { testName: "listdomain success on name root", @@ -254,16 +296,15 @@ func TestCmkListOperations(t *testing.T) { "list", "domains", fmt.Sprintf("name=\"%s\"", rootDomain), "listall=true", }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - domain, err := cmk.ValidateDomainPresent(ctx, rootDomain) - if domain.Id != rootDomainId { - t.Fatalf("Expected domain id: %s, actual domain id: %s", rootDomainId, domain.Id) + domainId, err := cmk.ValidateDomainAndGetId(ctx, execConfig.Profiles[0].Name, rootDomain) + if domainId != rootDomainId { + t.Fatalf("Expected domain id: %s, actual domain id: %s", rootDomainId, domainId) } return err }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 0, }, { testName: "listdomain success on name filter", @@ -273,16 +314,15 @@ func TestCmkListOperations(t *testing.T) { "list", "domains", fmt.Sprintf("name=\"%s\"", domainName), "listall=true", }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - domain, err := cmk.ValidateDomainPresent(ctx, domain) - if domain.Id != domainId { - t.Fatalf("Expected domain id: %s, actual domain id: %s", domainId, domain.Id) + actualDomainId, err := cmk.ValidateDomainAndGetId(ctx, execConfig.Profiles[0].Name, domain) + if actualDomainId != domainId { + t.Fatalf("Expected domain id: %s, actual domain id: %s", domainId, actualDomainId) } return err }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 0, }, { testName: "listdomain failure on multiple returns", @@ -292,13 +332,12 @@ func TestCmkListOperations(t *testing.T) { "list", "domains", fmt.Sprintf("name=\"%s\"", domainName), "listall=true", }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - _, err := cmk.ValidateDomainPresent(ctx, domainName) + _, err := cmk.ValidateDomainAndGetId(ctx, execConfig.Profiles[0].Name, domainName) return err }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listdomain success on multiple returns", @@ -308,16 +347,15 @@ func TestCmkListOperations(t *testing.T) { "list", "domains", fmt.Sprintf("name=\"%s\"", domain2Name), "listall=true", }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - domain, err := cmk.ValidateDomainPresent(ctx, domain2) - if domain.Id != domain2Id { - t.Fatalf("Expected domain id: %s, actual domain id: %s", domain2Id, domain.Id) + domainId, err := cmk.ValidateDomainAndGetId(ctx, execConfig.Profiles[0].Name, domain2) + if domainId != domain2Id { + t.Fatalf("Expected domain id: %s, actual domain id: %s", domain2Id, domainId) } return err }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 0, }, { testName: "listdomains json parse exception", @@ -327,13 +365,12 @@ func TestCmkListOperations(t *testing.T) { "list", "domains", fmt.Sprintf("name=\"%s\"", domainName), "listall=true", }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - _, err := cmk.ValidateDomainPresent(ctx, domain) + _, err := cmk.ValidateDomainAndGetId(ctx, execConfig.Profiles[0].Name, domain) return err }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: false, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listdomains no results", @@ -343,13 +380,12 @@ func TestCmkListOperations(t *testing.T) { "list", "domains", fmt.Sprintf("name=\"%s\"", domainName), "listall=true", }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - _, err := cmk.ValidateDomainPresent(ctx, domain) + _, err := cmk.ValidateDomainAndGetId(ctx, execConfig.Profiles[0].Name, domain) return err }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listaccounts success on name filter", @@ -359,12 +395,11 @@ func TestCmkListOperations(t *testing.T) { "list", "accounts", fmt.Sprintf("name=\"%s\"", accountName), fmt.Sprintf("domainid=\"%s\"", domainId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateAccountPresent(ctx, accountName, domainId) + return cmk.ValidateAccountPresent(ctx, execConfig.Profiles[0].Name, accountName, domainId) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 0, }, { testName: "listaccounts json parse exception", @@ -374,12 +409,11 @@ func TestCmkListOperations(t *testing.T) { "list", "accounts", fmt.Sprintf("name=\"%s\"", accountName), fmt.Sprintf("domainid=\"%s\"", domainId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateAccountPresent(ctx, accountName, domainId) + return cmk.ValidateAccountPresent(ctx, execConfig.Profiles[0].Name, accountName, domainId) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: false, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listaccounts no results", @@ -389,12 +423,11 @@ func TestCmkListOperations(t *testing.T) { "list", "accounts", fmt.Sprintf("name=\"%s\"", accountName), fmt.Sprintf("domainid=\"%s\"", domainId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateAccountPresent(ctx, accountName, domainId) + return cmk.ValidateAccountPresent(ctx, execConfig.Profiles[0].Name, accountName, domainId) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listzones success on name filter", @@ -404,13 +437,12 @@ func TestCmkListOperations(t *testing.T) { "list", "zones", fmt.Sprintf("name=\"%s\"", resourceName.Name), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - _, err := cmk.ValidateZonesPresent(ctx, []v1alpha1.CloudStackZone{zones[0]}) + _, err := cmk.ValidateZoneAndGetId(ctx, execConfig.Profiles[0].Name, zones[0]) return err }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: false, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, }, { testName: "listzones success on id filter", @@ -420,13 +452,57 @@ func TestCmkListOperations(t *testing.T) { "list", "zones", fmt.Sprintf("id=\"%s\"", resourceId.Id), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - _, err := cmk.ValidateZonesPresent(ctx, []v1alpha1.CloudStackZone{zones[2]}) + _, err := cmk.ValidateZoneAndGetId(ctx, execConfig.Profiles[0].Name, zones[2]) return err }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, + }, + { + testName: "listzones failure on multple results", + jsonResponseFile: "testdata/cmk_list_zone_multiple.json", + argumentsExecCall: []string{ + "-c", configFilePath, + "list", "zones", fmt.Sprintf("id=\"%s\"", resourceId.Id), + }, + cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { + _, err := cmk.ValidateZoneAndGetId(ctx, execConfig.Profiles[0].Name, zones[2]) + return err + }, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 1, + }, + { + testName: "listzones failure on none results", + jsonResponseFile: "testdata/cmk_list_zone_none.json", + argumentsExecCall: []string{ + "-c", configFilePath, + "list", "zones", fmt.Sprintf("id=\"%s\"", resourceId.Id), + }, + cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { + _, err := cmk.ValidateZoneAndGetId(ctx, execConfig.Profiles[0].Name, zones[2]) + return err + }, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 1, + }, + { + testName: "listzones failure on cmk failure", + jsonResponseFile: "testdata/cmk_list_empty_response.json", + argumentsExecCall: []string{ + "-c", configFilePath, + "list", "zones", fmt.Sprintf("name=\"%s\"", resourceName.Name), + }, + cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { + _, err := cmk.ValidateZoneAndGetId(ctx, execConfig.Profiles[0].Name, zones[0]) + return err + }, + cmkResponseError: errors.New("cmk calling return exception"), + wantErr: true, + wantResultCount: 0, }, { testName: "listzones no results", @@ -436,13 +512,12 @@ func TestCmkListOperations(t *testing.T) { "list", "zones", fmt.Sprintf("name=\"%s\"", resourceName.Name), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - _, err := cmk.ValidateZonesPresent(ctx, zones) + _, err := cmk.ValidateZoneAndGetId(ctx, execConfig.Profiles[0].Name, zones[0]) return err }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listzones json parse exception", @@ -452,13 +527,12 @@ func TestCmkListOperations(t *testing.T) { "list", "zones", fmt.Sprintf("name=\"%s\"", resourceName.Name), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - _, err := cmk.ValidateZonesPresent(ctx, zones) + _, err := cmk.ValidateZoneAndGetId(ctx, execConfig.Profiles[0].Name, zones[0]) return err }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: false, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listnetworks success on name filter", @@ -468,27 +542,53 @@ func TestCmkListOperations(t *testing.T) { "list", "networks", fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), fmt.Sprintf("zoneid=\"%s\"", "TEST_RESOURCE"), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateNetworkPresent(ctx, domainId, zones[2], []v1alpha1.CloudStackResourceIdentifier{}, accountName, false) + return cmk.ValidateNetworkPresent(ctx, execConfig.Profiles[0].Name, domainId, zones[2].Network, zones[2].Id, accountName) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: false, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, }, { - testName: "listnetworks success on id filter", - jsonResponseFile: "testdata/cmk_list_network_singular.json", + testName: "listnetworks failure on multiple results", + jsonResponseFile: "testdata/cmk_list_network_multiple.json", argumentsExecCall: []string{ "-c", configFilePath, - "list", "networks", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), fmt.Sprintf("zoneid=\"%s\"", "TEST_RESOURCE"), + "list", "networks", fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), fmt.Sprintf("zoneid=\"%s\"", "TEST_RESOURCE"), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateNetworkPresent(ctx, domainId, zones[3], []v1alpha1.CloudStackResourceIdentifier{}, accountName, false) + return cmk.ValidateNetworkPresent(ctx, execConfig.Profiles[0].Name, domainId, zones[2].Network, zones[2].Id, accountName) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 1, + }, + { + testName: "listnetworks failure on none results", + jsonResponseFile: "testdata/cmk_list_network_none.json", + argumentsExecCall: []string{ + "-c", configFilePath, + "list", "networks", fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), fmt.Sprintf("zoneid=\"%s\"", "TEST_RESOURCE"), + }, + cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { + return cmk.ValidateNetworkPresent(ctx, execConfig.Profiles[0].Name, domainId, zones[2].Network, zones[2].Id, accountName) + }, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 1, + }, + { + testName: "listnetworks failure on cmk failure", + jsonResponseFile: "testdata/cmk_list_network_multiple.json", + argumentsExecCall: []string{ + "-c", configFilePath, + "list", "networks", fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), fmt.Sprintf("zoneid=\"%s\"", "TEST_RESOURCE"), + }, + cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { + return cmk.ValidateNetworkPresent(ctx, execConfig.Profiles[0].Name, domainId, zones[2].Network, zones[2].Id, accountName) + }, + cmkResponseError: errors.New("cmk calling return exception"), + wantErr: true, + wantResultCount: 1, }, { testName: "listnetworks no results", @@ -498,12 +598,11 @@ func TestCmkListOperations(t *testing.T) { "list", "networks", fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), fmt.Sprintf("zoneid=\"%s\"", "TEST_RESOURCE"), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateNetworkPresent(ctx, domainId, zones[2], []v1alpha1.CloudStackResourceIdentifier{}, accountName, false) + return cmk.ValidateNetworkPresent(ctx, execConfig.Profiles[0].Name, domainId, zones[2].Network, zones[2].Id, accountName) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listnetworks json parse exception", @@ -513,12 +612,11 @@ func TestCmkListOperations(t *testing.T) { "list", "networks", fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), fmt.Sprintf("zoneid=\"%s\"", "TEST_RESOURCE"), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateNetworkPresent(ctx, domainId, zones[2], []v1alpha1.CloudStackResourceIdentifier{}, accountName, false) + return cmk.ValidateNetworkPresent(ctx, execConfig.Profiles[0].Name, domainId, zones[2].Network, zones[2].Id, accountName) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: false, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listserviceofferings success on name filter", @@ -528,12 +626,11 @@ func TestCmkListOperations(t *testing.T) { "list", "serviceofferings", fmt.Sprintf("name=\"%s\"", resourceName.Name), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateServiceOfferingPresent(ctx, zoneId, resourceName) + return cmk.ValidateServiceOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, resourceName) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: false, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, }, { testName: "listserviceofferings success on id filter", @@ -543,12 +640,11 @@ func TestCmkListOperations(t *testing.T) { "list", "serviceofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateServiceOfferingPresent(ctx, zoneId, resourceId) + return cmk.ValidateServiceOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, resourceId) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, }, { testName: "listserviceofferings no results", @@ -558,12 +654,11 @@ func TestCmkListOperations(t *testing.T) { "list", "serviceofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateServiceOfferingPresent(ctx, zoneId, resourceId) + return cmk.ValidateServiceOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, resourceId) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listserviceofferings json parse exception", @@ -573,12 +668,11 @@ func TestCmkListOperations(t *testing.T) { "list", "serviceofferings", fmt.Sprintf("name=\"%s\"", resourceName.Name), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateServiceOfferingPresent(ctx, zoneId, resourceName) + return cmk.ValidateServiceOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, resourceName) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: false, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listdiskofferings success on name filter", @@ -588,12 +682,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("name=\"%s\"", resourceName.Name), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingResourceName) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingResourceName) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: false, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, }, { testName: "listdiskofferings success on id filter", @@ -603,12 +696,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingResourceID) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingResourceID) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, }, { testName: "listdiskofferings no results", @@ -618,12 +710,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingResourceID) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingResourceID) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listdiskofferings no results", @@ -633,12 +724,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingResourceID) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingResourceID) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listdiskofferings multiple results", @@ -648,12 +738,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingResourceID) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingResourceID) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 4, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 4, }, { testName: "listdiskofferings customized results with customSizeInGB > 0", @@ -663,12 +752,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingCustomSizeInGB) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingCustomSizeInGB) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, }, { testName: "listdiskofferings non-customized results with customSizeInGB > 0", @@ -678,12 +766,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingCustomSizeInGB) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingCustomSizeInGB) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 1, }, { testName: "listdiskofferings non-customized results with customSizeInGB > 0", @@ -693,12 +780,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingResourceID) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingResourceID) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 1, }, { testName: "listdiskofferings throw exception", @@ -708,12 +794,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingResourceID) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingResourceID) }, - cmkResponseError: errors.New("cmk calling return exception"), - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: errors.New("cmk calling return exception"), + wantErr: true, + wantResultCount: 0, }, { testName: "listdiskofferings json parse exception", @@ -723,12 +808,11 @@ func TestCmkListOperations(t *testing.T) { "list", "diskofferings", fmt.Sprintf("name=\"%s\"", resourceName.Name), fmt.Sprintf("zoneid=\"%s\"", zoneId), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateDiskOfferingPresent(ctx, zoneId, diskOfferingResourceName) + return cmk.ValidateDiskOfferingPresent(ctx, execConfig.Profiles[0].Name, zoneId, diskOfferingResourceName) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: false, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "validatetemplate success on name filter", @@ -738,12 +822,22 @@ func TestCmkListOperations(t *testing.T) { "list", "templates", "templatefilter=all", "listall=true", fmt.Sprintf("name=\"%s\"", resourceName.Name), fmt.Sprintf("zoneid=\"%s\"", zoneId), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateTemplatePresent(ctx, domainId, zoneId, accountName, resourceName) + return cmk.ValidateTemplatePresent(ctx, execConfig.Profiles[0].Name, domainId, zoneId, accountName, resourceName) + }, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, + }, + { + testName: "validatetemplate failure when passing invalid profile", + jsonResponseFile: "testdata/cmk_list_template_singular.json", + argumentsExecCall: nil, + cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { + return cmk.ValidateTemplatePresent(ctx, "xxx", domainId, zoneId, accountName, resourceName) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: false, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 1, }, { testName: "validatetemplate success on id filter", @@ -753,12 +847,53 @@ func TestCmkListOperations(t *testing.T) { "list", "templates", "templatefilter=all", "listall=true", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateTemplatePresent(ctx, domainId, zoneId, accountName, resourceId) + return cmk.ValidateTemplatePresent(ctx, execConfig.Profiles[0].Name, domainId, zoneId, accountName, resourceId) + }, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, + }, + { + testName: "validatetemplate failure on multiple results", + jsonResponseFile: "testdata/cmk_list_template_multiple.json", + argumentsExecCall: []string{ + "-c", configFilePath, + "list", "templates", "templatefilter=all", "listall=true", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), + }, + cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { + return cmk.ValidateTemplatePresent(ctx, execConfig.Profiles[0].Name, domainId, zoneId, accountName, resourceId) + }, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 1, + }, + { + testName: "validatetemplate failure on none results", + jsonResponseFile: "testdata/cmk_list_template_none.json", + argumentsExecCall: []string{ + "-c", configFilePath, + "list", "templates", "templatefilter=all", "listall=true", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), + }, + cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { + return cmk.ValidateTemplatePresent(ctx, execConfig.Profiles[0].Name, domainId, zoneId, accountName, resourceId) + }, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 1, + }, + { + testName: "validatetemplate failure on cmk failure", + jsonResponseFile: "testdata/cmk_list_template_none.json", + argumentsExecCall: []string{ + "-c", configFilePath, + "list", "templates", "templatefilter=all", "listall=true", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("zoneid=\"%s\"", zoneId), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), + }, + cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { + return cmk.ValidateTemplatePresent(ctx, execConfig.Profiles[0].Name, domainId, zoneId, accountName, resourceId) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: true, - wantResultCount: 1, + cmkResponseError: errors.New("cmk calling return exception"), + wantErr: true, + wantResultCount: 1, }, { testName: "validatetemplate no results", @@ -768,12 +903,11 @@ func TestCmkListOperations(t *testing.T) { "list", "templates", "templatefilter=all", "listall=true", fmt.Sprintf("name=\"%s\"", resourceName.Name), fmt.Sprintf("zoneid=\"%s\"", zoneId), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateTemplatePresent(ctx, domainId, zoneId, accountName, resourceName) + return cmk.ValidateTemplatePresent(ctx, execConfig.Profiles[0].Name, domainId, zoneId, accountName, resourceName) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: true, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "validatetemplate json parse exception", @@ -783,12 +917,11 @@ func TestCmkListOperations(t *testing.T) { "list", "templates", "templatefilter=all", "listall=true", fmt.Sprintf("name=\"%s\"", resourceName.Name), fmt.Sprintf("zoneid=\"%s\"", zoneId), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateTemplatePresent(ctx, domainId, zoneId, accountName, resourceName) + return cmk.ValidateTemplatePresent(ctx, execConfig.Profiles[0].Name, domainId, zoneId, accountName, resourceName) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: false, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listaffinitygroups success on id filter", @@ -798,12 +931,11 @@ func TestCmkListOperations(t *testing.T) { "list", "affinitygroups", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateAffinityGroupsPresent(ctx, domainId, accountName, []string{resourceId.Id}) + return cmk.ValidateAffinityGroupsPresent(ctx, execConfig.Profiles[0].Name, domainId, accountName, []string{resourceId.Id}) }, - cmkResponseError: nil, - wantErr: false, - shouldSecondCallOccur: false, - wantResultCount: 1, + cmkResponseError: nil, + wantErr: false, + wantResultCount: 1, }, { testName: "listaffinitygroups no results", @@ -813,12 +945,11 @@ func TestCmkListOperations(t *testing.T) { "list", "affinitygroups", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateAffinityGroupsPresent(ctx, domainId, accountName, []string{resourceId.Id}) + return cmk.ValidateAffinityGroupsPresent(ctx, execConfig.Profiles[0].Name, domainId, accountName, []string{resourceId.Id}) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: false, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, { testName: "listaffinitygroups json parse exception", @@ -828,12 +959,11 @@ func TestCmkListOperations(t *testing.T) { "list", "affinitygroups", fmt.Sprintf("id=\"%s\"", resourceId.Id), fmt.Sprintf("domainid=\"%s\"", domainId), fmt.Sprintf("account=\"%s\"", accountName), }, cmkFunc: func(cmk executables.Cmk, ctx context.Context) error { - return cmk.ValidateAffinityGroupsPresent(ctx, domainId, accountName, []string{resourceId.Id}) + return cmk.ValidateAffinityGroupsPresent(ctx, execConfig.Profiles[0].Name, domainId, accountName, []string{resourceId.Id}) }, - cmkResponseError: nil, - wantErr: true, - shouldSecondCallOccur: false, - wantResultCount: 0, + cmkResponseError: nil, + wantErr: true, + wantResultCount: 0, }, } @@ -849,9 +979,11 @@ func TestCmkListOperations(t *testing.T) { defer tctx.RestoreContext() executable := mockexecutables.NewMockExecutable(mockCtrl) - executable.EXPECT().Execute(ctx, tt.argumentsExecCall). - Return(*bytes.NewBufferString(fileContent), tt.cmkResponseError) - cmk := executables.NewCmk(executable, writer, execConfig) + if tt.argumentsExecCall != nil { + executable.EXPECT().Execute(ctx, tt.argumentsExecCall). + Return(*bytes.NewBufferString(fileContent), tt.cmkResponseError) + } + cmk := executables.NewCmk(executable, writer, execConfig.Profiles) err := tt.cmkFunc(*cmk, ctx) if tt.wantErr && err != nil || !tt.wantErr && err == nil { return @@ -860,3 +992,27 @@ func TestCmkListOperations(t *testing.T) { }) } } + +func TestCmkGetManagementApiEndpoint(t *testing.T) { + _, writer := test.NewWriter(t) + mockCtrl := gomock.NewController(t) + tt := NewWithT(t) + + var tctx testContext + tctx.SaveContext() + defer tctx.RestoreContext() + + executable := mockexecutables.NewMockExecutable(mockCtrl) + cmk := executables.NewCmk(executable, writer, execConfigWithMultipleProfiles.Profiles) + + endpoint, err := cmk.GetManagementApiEndpoint("test_name") + tt.Expect(err).To(BeNil()) + tt.Expect(endpoint).To(Equal("http://1.1.1.1:8080/client/api")) + + endpoint, err = cmk.GetManagementApiEndpoint("test_name_2") + tt.Expect(err).To(BeNil()) + tt.Expect(endpoint).To(Equal("http://1.1.1.1:8080/client/api_2")) + + _, err = cmk.GetManagementApiEndpoint("xxx") + tt.Expect(err).NotTo(BeNil()) +} diff --git a/pkg/executables/config/cmk.ini b/pkg/executables/config/cmk.ini index ce1dc8326209..86fe4af55cfa 100644 --- a/pkg/executables/config/cmk.ini +++ b/pkg/executables/config/cmk.ini @@ -1,13 +1,12 @@ prompt = CMK asyncblock = true -timeout = {{ .CloudMonkeyTimeout }} +timeout = {{ .Timeout }} output = json -verifycert = {{ .CloudMonkeyVerifyCert }} -profile = cluster +verifycert = {{ .VerifySsl }} +profile = {{ .Name }} autocomplete = true - -[cluster] -url = {{ .CloudStackManagementUrl }} -apikey = {{ .CloudStackApiKey }} -secretkey = {{ .CloudStackSecretKey }} +[{{ .Name }}] +url = {{ .ManagementUrl }} +apikey = {{ .ApiKey }} +secretkey = {{ .SecretKey }} \ No newline at end of file diff --git a/pkg/executables/testdata/cmk_list_network_multiple.json b/pkg/executables/testdata/cmk_list_network_multiple.json new file mode 100644 index 000000000000..d249f79fce7e --- /dev/null +++ b/pkg/executables/testdata/cmk_list_network_multiple.json @@ -0,0 +1,135 @@ +{ + "count": 1, + "network": [ + { + "acltype": "Domain", + "broadcastdomaintype": "Native", + "broadcasturi": "vlan://untagged", + "canusefordeploy": true, + "cidr": "192.168.1.0/24", + "details": {}, + "displaynetwork": true, + "displaytext": "for zone 1, shared", + "dns1": "8.8.8.8", + "dns2": "8.8.4.4", + "domain": "ROOT", + "domainid": "5300cdac-74d5-11ec-8696-c81f66d3e965", + "gateway": "192.168.1.1", + "id": "aeb3a5e6-2e80-4a73-900f-d01bbd4874b5", + "ispersistent": false, + "issystem": false, + "name": "TEST_RESOURCE", + "netmask": "255.255.255.0", + "networkdomain": "cs1cloud.internal", + "networkofferingavailability": "Optional", + "networkofferingconservemode": true, + "networkofferingdisplaytext": "Offering for Shared networks", + "networkofferingid": "928c228c-c5c3-4f26-92ca-d611d5ebda49", + "networkofferingname": "DefaultSharedNetworkOffering", + "physicalnetworkid": "67ad3d0a-5a2f-4557-8563-1bb6577eebcf", + "redundantrouter": false, + "related": "aeb3a5e6-2e80-4a73-900f-d01bbd4874b5", + "restartrequired": false, + "service": [ + { + "capability": [ + { + "canchooseservicecapability": false, + "name": "DhcpAccrossMultipleSubnets", + "value": "true" + } + ], + "name": "Dhcp" + }, + { + "name": "UserData" + }, + { + "capability": [ + { + "canchooseservicecapability": false, + "name": "AllowDnsSuffixModification", + "value": "true" + } + ], + "name": "Dns" + } + ], + "specifyipranges": true, + "state": "Setup", + "strechedl2subnet": false, + "subdomainaccess": true, + "tags": [], + "traffictype": "Guest", + "type": "Shared", + "vlan": "untagged", + "zoneid": "151e4c35-7ba4-4f74-b35d-f3d8627118cc", + "zonename": "zone1" + }, + { + "acltype": "Domain", + "broadcastdomaintype": "Native", + "broadcasturi": "vlan://untagged", + "canusefordeploy": true, + "cidr": "192.168.1.0/24", + "details": {}, + "displaynetwork": true, + "displaytext": "for zone 1, shared", + "dns1": "8.8.8.8", + "dns2": "8.8.4.4", + "domain": "ROOT", + "domainid": "5300cdac-74d5-11ec-8696-c81f66d3e966", + "gateway": "192.168.1.1", + "id": "aeb3a5e6-2e80-4a73-900f-d01bbd4874b5", + "ispersistent": false, + "issystem": false, + "name": "TEST_RESOURCE", + "netmask": "255.255.255.0", + "networkdomain": "cs1cloud.internal", + "networkofferingavailability": "Optional", + "networkofferingconservemode": true, + "networkofferingdisplaytext": "Offering for Shared networks", + "networkofferingid": "928c228c-c5c3-4f26-92ca-d611d5ebda49", + "networkofferingname": "DefaultSharedNetworkOffering", + "physicalnetworkid": "67ad3d0a-5a2f-4557-8563-1bb6577eebcf", + "redundantrouter": false, + "related": "aeb3a5e6-2e80-4a73-900f-d01bbd4874b5", + "restartrequired": false, + "service": [ + { + "capability": [ + { + "canchooseservicecapability": false, + "name": "DhcpAccrossMultipleSubnets", + "value": "true" + } + ], + "name": "Dhcp" + }, + { + "name": "UserData" + }, + { + "capability": [ + { + "canchooseservicecapability": false, + "name": "AllowDnsSuffixModification", + "value": "true" + } + ], + "name": "Dns" + } + ], + "specifyipranges": true, + "state": "Setup", + "strechedl2subnet": false, + "subdomainaccess": true, + "tags": [], + "traffictype": "Guest", + "type": "Shared", + "vlan": "untagged", + "zoneid": "151e4c35-7ba4-4f74-b35d-f3d8627118cc", + "zonename": "zone1" + } + ] +} diff --git a/pkg/executables/testdata/cmk_list_network_none.json b/pkg/executables/testdata/cmk_list_network_none.json new file mode 100644 index 000000000000..d7e24648fabf --- /dev/null +++ b/pkg/executables/testdata/cmk_list_network_none.json @@ -0,0 +1,5 @@ +{ + "count": 0, + "network": [ + ] +} diff --git a/pkg/executables/testdata/cmk_list_template_multiple.json b/pkg/executables/testdata/cmk_list_template_multiple.json new file mode 100644 index 000000000000..e3b28e7c9983 --- /dev/null +++ b/pkg/executables/testdata/cmk_list_template_multiple.json @@ -0,0 +1,89 @@ +{ + "count": 2, + "template": [ + { + "account": "system", + "bits": 0, + "checksum": "ed0e788280ff2912ea40f7f91ca7a249", + "created": "2021-11-01T15:02:10-0400", + "crossZones": true, + "deployasis": false, + "details": {}, + "directdownload": false, + "displaytext": "CentOS 5.5(64-bit) no GUI (KVM)", + "domain": "ROOT", + "domainid": "4ab50296-3b45-11ec-a097-a8a15983abb5", + "downloaddetails": [ + { + "datastore": "nfs://192.168.1.88:/export/secondary", + "downloadPercent": "100", + "downloadState": "DOWNLOADED" + } + ], + "format": "QCOW2", + "hypervisor": "KVM", + "id": "4ab79b52-3b45-11ec-a097-a8a15983abb5", + "isdynamicallyscalable": false, + "isextractable": true, + "isfeatured": true, + "ispublic": true, + "isready": true, + "name": "CentOS 5.5(64-bit) no GUI (KVM)", + "ostypeid": "4ac797d4-3b45-11ec-a097-a8a15983abb5", + "ostypename": "CentOS 5.5 (64-bit)", + "passwordenabled": false, + "physicalsize": 1769537536, + "requireshvm": false, + "size": 8589934592, + "sshkeyenabled": false, + "status": "Download Complete", + "tags": [], + "templatetype": "BUILTIN", + "url": "http://download.cloudstack.org/releases/2.2.0/eec2209b-9875-3c8d-92be-c001bd8a0faf.qcow2.bz2", + "zoneid": "4e3b338d-87a6-4189-b931-a1747edeea8f", + "zonename": "zone1" + }, + { + "account": "system", + "bits": 0, + "checksum": "ed0e788280ff2912ea40f7f91ca7a249", + "created": "2021-11-01T15:02:10-0400", + "crossZones": true, + "deployasis": false, + "details": {}, + "directdownload": false, + "displaytext": "CentOS 5.5(64-bit) no GUI (KVM)", + "domain": "ROOT", + "domainid": "4ab50296-3b45-11ec-a097-a8a15983abb5", + "downloaddetails": [ + { + "datastore": "nfs://192.168.1.88:/export/secondary", + "downloadPercent": "100", + "downloadState": "DOWNLOADED" + } + ], + "format": "QCOW2", + "hypervisor": "KVM", + "id": "4ab79b52-3b45-11ec-a097-a8a15983abb6", + "isdynamicallyscalable": false, + "isextractable": true, + "isfeatured": true, + "ispublic": true, + "isready": true, + "name": "CentOS 5.5(64-bit) no GUI (KVM)", + "ostypeid": "4ac797d4-3b45-11ec-a097-a8a15983abb5", + "ostypename": "CentOS 5.5 (64-bit)", + "passwordenabled": false, + "physicalsize": 1769537536, + "requireshvm": false, + "size": 8589934592, + "sshkeyenabled": false, + "status": "Download Complete", + "tags": [], + "templatetype": "BUILTIN", + "url": "http://download.cloudstack.org/releases/2.2.0/eec2209b-9875-3c8d-92be-c001bd8a0faf.qcow2.bz2", + "zoneid": "4e3b338d-87a6-4189-b931-a1747edeea8f", + "zonename": "zone1" + } + ] +} diff --git a/pkg/executables/testdata/cmk_list_template_none.json b/pkg/executables/testdata/cmk_list_template_none.json new file mode 100644 index 000000000000..9cde2ad853f6 --- /dev/null +++ b/pkg/executables/testdata/cmk_list_template_none.json @@ -0,0 +1,6 @@ +{ + "count": 0, + "template": [ + + ] +} diff --git a/pkg/executables/testdata/cmk_list_zone_multiple.json b/pkg/executables/testdata/cmk_list_zone_multiple.json new file mode 100644 index 000000000000..74384d8696cd --- /dev/null +++ b/pkg/executables/testdata/cmk_list_zone_multiple.json @@ -0,0 +1,27 @@ +{ + "count": 2, + "zone": [ + { + "allocationstate": "Enabled", + "dhcpprovider": "VirtualRouter", + "id": "4e3b338d-87a6-4189-b931-a1747edeea8f", + "localstorageenabled": false, + "name": "zone1", + "networktype": "Advanced", + "securitygroupsenabled": false, + "tags": [], + "zonetoken": "1f999599-dbf0-3ba6-9091-1ba87c4f91f0" + }, + { + "allocationstate": "Enabled", + "dhcpprovider": "VirtualRouter", + "id": "4e3b338d-87a6-4189-b931-a1747edeea8g", + "localstorageenabled": false, + "name": "zone1", + "networktype": "Advanced", + "securitygroupsenabled": false, + "tags": [], + "zonetoken": "1f999599-dbf0-3ba6-9091-1ba87c4f91f0" + }, + ] +} diff --git a/pkg/executables/testdata/cmk_list_zone_none.json b/pkg/executables/testdata/cmk_list_zone_none.json new file mode 100644 index 000000000000..3326fff5d6d4 --- /dev/null +++ b/pkg/executables/testdata/cmk_list_zone_none.json @@ -0,0 +1,5 @@ +{ + "count": 0, + "zone": [ + ] +} diff --git a/pkg/providers/cloudstack/cloudstack.go b/pkg/providers/cloudstack/cloudstack.go index 8dcb12c7d6bb..f80625da63b9 100644 --- a/pkg/providers/cloudstack/cloudstack.go +++ b/pkg/providers/cloudstack/cloudstack.go @@ -202,19 +202,6 @@ type ProviderKubectlClient interface { } func NewProvider(datacenterConfig *v1alpha1.CloudStackDatacenterConfig, machineConfigs map[string]*v1alpha1.CloudStackMachineConfig, clusterConfig *v1alpha1.Cluster, providerKubectlClient ProviderKubectlClient, providerCmkClient ProviderCmkClient, writer filewriter.FileWriter, now types.NowFunc, skipIpCheck bool) *cloudstackProvider { - return NewProviderCustomNet( - datacenterConfig, - machineConfigs, - clusterConfig, - providerKubectlClient, - providerCmkClient, - writer, - now, - skipIpCheck, - ) -} - -func NewProviderCustomNet(datacenterConfig *v1alpha1.CloudStackDatacenterConfig, machineConfigs map[string]*v1alpha1.CloudStackMachineConfig, clusterConfig *v1alpha1.Cluster, providerKubectlClient ProviderKubectlClient, providerCmkClient ProviderCmkClient, writer filewriter.FileWriter, now types.NowFunc, skipIpCheck bool) *cloudstackProvider { var controlPlaneMachineSpec, etcdMachineSpec *v1alpha1.CloudStackMachineConfigSpec workerNodeGroupMachineSpecs := make(map[string]v1alpha1.CloudStackMachineConfigSpec, len(machineConfigs)) if clusterConfig.Spec.ControlPlaneConfiguration.MachineGroupRef != nil && machineConfigs[clusterConfig.Spec.ControlPlaneConfiguration.MachineGroupRef.Name] != nil { @@ -361,12 +348,17 @@ func (p *cloudstackProvider) validateEnv(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to parse environment variable exec config: %v", err) } - if len(execConfig.ManagementUrl) <= 0 { - return errors.New("cloudstack management api url is not set or is empty") + if len(execConfig.Profiles) <= 0 { + return errors.New("cloudstack instances are not defined") } - if err := p.validateManagementApiEndpoint(execConfig.ManagementUrl); err != nil { - return errors.New("CloudStackDatacenterConfig managementApiEndpoint is invalid") + + for _, instance := range execConfig.Profiles { + if err := p.validateManagementApiEndpoint(instance.ManagementUrl); err != nil { + return fmt.Errorf("CloudStack instance %s's managementApiEndpoint %s is invalid: %v", + instance.Name, instance.ManagementUrl, err) + } } + if _, ok := os.LookupEnv(eksaLicense); !ok { if err := os.Setenv(eksaLicense, ""); err != nil { return fmt.Errorf("unable to set %s: %v", eksaLicense, err) @@ -375,26 +367,30 @@ func (p *cloudstackProvider) validateEnv(ctx context.Context) error { return nil } -func (p *cloudstackProvider) SetupAndValidateCreateCluster(ctx context.Context, clusterSpec *cluster.Spec) error { - err := p.validateEnv(ctx) - if err != nil { - return fmt.Errorf("failed setup and validations: %v", err) - } - - cloudStackClusterSpec := NewSpec(clusterSpec, p.machineConfigs, p.datacenterConfig) - - if err := p.validator.validateCloudStackAccess(ctx); err != nil { +func (p *cloudstackProvider) validateClusterSpec(ctx context.Context, clusterSpec *cluster.Spec) (err error) { + if err := p.validator.validateCloudStackAccess(ctx, p.datacenterConfig); err != nil { return err } if err := p.validator.ValidateCloudStackDatacenterConfig(ctx, p.datacenterConfig); err != nil { return err } - if err := p.validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec); err != nil { + if err := p.validator.ValidateClusterMachineConfigs(ctx, NewSpec(clusterSpec, p.machineConfigs, p.datacenterConfig)); err != nil { return err } + return nil +} + +func (p *cloudstackProvider) SetupAndValidateCreateCluster(ctx context.Context, clusterSpec *cluster.Spec) error { + if err := p.validateEnv(ctx); err != nil { + return fmt.Errorf("validating environment variables: %v", err) + } + + if err := p.validateClusterSpec(ctx, clusterSpec); err != nil { + return fmt.Errorf("validating cluster spec: %v", err) + } if err := p.setupSSHAuthKeysForCreate(); err != nil { - return fmt.Errorf("failed setup and validations: %v", err) + return fmt.Errorf("setting up SSH keys: %v", err) } if clusterSpec.Cluster.IsManaged() { @@ -424,28 +420,19 @@ func (p *cloudstackProvider) SetupAndValidateCreateCluster(ctx context.Context, } func (p *cloudstackProvider) SetupAndValidateUpgradeCluster(ctx context.Context, cluster *types.Cluster, clusterSpec *cluster.Spec) error { - err := p.validateEnv(ctx) - if err != nil { - return fmt.Errorf("failed setup and validations: %v", err) + if err := p.validateEnv(ctx); err != nil { + return fmt.Errorf("validating environment variables: %v", err) } - cloudStackClusterSpec := NewSpec(clusterSpec, p.machineConfigs, p.datacenterConfig) - if err := p.validator.validateCloudStackAccess(ctx); err != nil { - return err - } - if err := p.validator.ValidateCloudStackDatacenterConfig(ctx, p.datacenterConfig); err != nil { - return err - } - if err := p.validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec); err != nil { - return err + if err := p.validateClusterSpec(ctx, clusterSpec); err != nil { + return fmt.Errorf("validating cluster spec: %v", err) } if err := p.setupSSHAuthKeysForUpgrade(); err != nil { - return fmt.Errorf("failed setup and validations: %v", err) + return fmt.Errorf("setting up SSH keys: %v", err) } - err = p.validateMachineConfigsNameUniqueness(ctx, cluster, clusterSpec) - if err != nil { + if err := p.validateMachineConfigsNameUniqueness(ctx, cluster, clusterSpec); err != nil { return fmt.Errorf("failed validate machineconfig uniqueness: %v", err) } return nil @@ -454,7 +441,7 @@ func (p *cloudstackProvider) SetupAndValidateUpgradeCluster(ctx context.Context, func (p *cloudstackProvider) SetupAndValidateDeleteCluster(ctx context.Context, _ *types.Cluster) error { err := p.validateEnv(ctx) if err != nil { - return fmt.Errorf("failed setup and validations: %v", err) + return fmt.Errorf("validating environment variables: %v", err) } return nil } diff --git a/pkg/providers/cloudstack/cloudstack_test.go b/pkg/providers/cloudstack/cloudstack_test.go index e51875e904f5..9ffaaba695e8 100644 --- a/pkg/providers/cloudstack/cloudstack_test.go +++ b/pkg/providers/cloudstack/cloudstack_test.go @@ -10,6 +10,7 @@ import ( "github.com/golang/mock/gomock" etcdv1 "github.com/mrajashree/etcdadm-controller/api/v1beta1" + . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" @@ -33,12 +34,13 @@ const ( /* Generated from ini file (like the following) then b64 encoded: `cat fake-cloud-config.ini | base64 | tr -d '\n'` [Global] - api-key = test-key - secret-key = test-secret - api-url = http://127.16.0.1:8080/client/api - verify-ssl = true + verify-ssl = false + api-key = test-key1 + secret-key = test-secret1 + api-url = http://127.16.0.1:8080/client/api */ - expectedCloudStackCloudConfig = "W0dsb2JhbF0KYXBpLWtleSAgICA9IHRlc3Qta2V5CnNlY3JldC1rZXkgPSB0ZXN0LXNlY3JldAphcGktdXJsICAgID0gaHR0cDovLzEyNy4xNi4wLjE6ODA4MC9jbGllbnQvYXBpCnZlcmlmeS1zc2wgPSB0cnVlCg==" + expectedCloudStackCloudConfig = "W0dsb2JhbF0KdmVyaWZ5LXNzbCA9IGZhbHNlCmFwaS1rZXkgPSB0ZXN0LWtleTEKc2VjcmV0LWtleSA9IHRlc3Qtc2VjcmV0MQphcGktdXJsID0gaHR0cDovLzEyNy4xNi4wLjE6ODA4MC9jbGllbnQvYXBpCg==" + cloudStackCloudConfigWithInvalidUrl = "W0dsb2JhbF0KdmVyaWZ5LXNzbCA9IGZhbHNlCmFwaS1rZXkgPSB0ZXN0LWtleTEKc2VjcmV0LWtleSA9IHRlc3Qtc2VjcmV0MQphcGktdXJsID0geHh4Cg==" ) func givenClusterConfig(t *testing.T, fileName string) *v1alpha1.Cluster { @@ -59,15 +61,16 @@ func givenEmptyClusterSpec() *cluster.Spec { func givenWildcardCmk(mockCtrl *gomock.Controller) ProviderCmkClient { cmk := mocks.NewMockProviderCmkClient(mockCtrl) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateCloudStackConnection(gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateDomainPresent(gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateAccountPresent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateZoneAndGetId(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateCloudStackConnection(gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateDomainAndGetId(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateAccountPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() cmk.EXPECT().ValidateNetworkPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().GetManagementApiEndpoint(gomock.Any()).AnyTimes().Return("http://127.16.0.1:8080/client/api", nil) return cmk } @@ -193,7 +196,8 @@ func newProviderWithKubectl(t *testing.T, datacenterConfig *v1alpha1.CloudStackD func newProvider(t *testing.T, datacenterConfig *v1alpha1.CloudStackDatacenterConfig, machineConfigs map[string]*v1alpha1.CloudStackMachineConfig, clusterConfig *v1alpha1.Cluster, kubectl ProviderKubectlClient, cmk ProviderCmkClient) *cloudstackProvider { _, writer := test.NewWriter(t) - return NewProviderCustomNet( + + return NewProvider( datacenterConfig, machineConfigs, clusterConfig, @@ -237,6 +241,109 @@ func TestProviderGenerateCAPISpecForCreate(t *testing.T) { test.AssertContentToFile(t, string(md), "testdata/expected_results_main_md.yaml") } +func TestProviderSetupAndValidateCreateClusterFailureOnInvalidUrl(t *testing.T) { + tt := NewWithT(t) + mockCtrl := gomock.NewController(t) + var tctx testContext + tctx.SaveContext() + ctx := context.Background() + kubectl := mocks.NewMockProviderKubectlClient(mockCtrl) + clusterSpec := givenClusterSpec(t, testClusterConfigMainFilename) + + datacenterConfig := givenDatacenterConfig(t, testClusterConfigMainFilename) + machineConfigs := givenMachineConfigs(t, testClusterConfigMainFilename) + cmk := givenWildcardCmk(mockCtrl) + provider := newProviderWithKubectl(t, datacenterConfig, machineConfigs, clusterSpec.Cluster, kubectl, cmk) + if provider == nil { + t.Fatalf("provider object is nil") + } + + os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, cloudStackCloudConfigWithInvalidUrl) + err := provider.SetupAndValidateCreateCluster(ctx, clusterSpec) + tt.Expect(err).NotTo(BeNil()) +} + +func TestProviderSetupAndValidateUpgradeClusterFailureOnInvalidUrl(t *testing.T) { + tt := NewWithT(t) + mockCtrl := gomock.NewController(t) + var tctx testContext + tctx.SaveContext() + ctx := context.Background() + kubectl := mocks.NewMockProviderKubectlClient(mockCtrl) + cluster := &types.Cluster{Name: "test"} + clusterSpec := givenClusterSpec(t, testClusterConfigMainFilename) + + datacenterConfig := givenDatacenterConfig(t, testClusterConfigMainFilename) + machineConfigs := givenMachineConfigs(t, testClusterConfigMainFilename) + cmk := givenWildcardCmk(mockCtrl) + provider := newProviderWithKubectl(t, datacenterConfig, machineConfigs, clusterSpec.Cluster, kubectl, cmk) + if provider == nil { + t.Fatalf("provider object is nil") + } + + os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, cloudStackCloudConfigWithInvalidUrl) + err := provider.SetupAndValidateUpgradeCluster(ctx, cluster, clusterSpec) + tt.Expect(err).NotTo(BeNil()) +} + +func TestProviderSetupAndValidateDeleteClusterFailureOnInvalidUrl(t *testing.T) { + tt := NewWithT(t) + mockCtrl := gomock.NewController(t) + var tctx testContext + tctx.SaveContext() + ctx := context.Background() + kubectl := mocks.NewMockProviderKubectlClient(mockCtrl) + cluster := &types.Cluster{Name: "test"} + clusterSpec := givenClusterSpec(t, testClusterConfigMainFilename) + + datacenterConfig := givenDatacenterConfig(t, testClusterConfigMainFilename) + machineConfigs := givenMachineConfigs(t, testClusterConfigMainFilename) + cmk := givenWildcardCmk(mockCtrl) + provider := newProviderWithKubectl(t, datacenterConfig, machineConfigs, clusterSpec.Cluster, kubectl, cmk) + if provider == nil { + t.Fatalf("provider object is nil") + } + + os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, cloudStackCloudConfigWithInvalidUrl) + err := provider.SetupAndValidateDeleteCluster(ctx, cluster) + tt.Expect(err).NotTo(BeNil()) +} + +func TestProviderSetupAndValidateCreateClusterFailureOnInvalidClusterSpec(t *testing.T) { + tt := NewWithT(t) + clusterSpecManifest := "cluster_invalid.yaml" + mockCtrl := gomock.NewController(t) + setupContext() + kubectl := mocks.NewMockProviderKubectlClient(mockCtrl) + clusterSpec := givenClusterSpec(t, clusterSpecManifest) + datacenterConfig := givenDatacenterConfig(t, clusterSpecManifest) + machineConfigs := givenMachineConfigs(t, clusterSpecManifest) + ctx := context.Background() + cmk := givenWildcardCmk(mockCtrl) + provider := newProviderWithKubectl(t, datacenterConfig, machineConfigs, clusterSpec.Cluster, kubectl, cmk) + + err := provider.SetupAndValidateCreateCluster(ctx, clusterSpec) + tt.Expect(err).NotTo(BeNil()) +} + +func TestProviderSetupAndValidateUpgradeClusterFailureOnInvalidClusterSpec(t *testing.T) { + tt := NewWithT(t) + clusterSpecManifest := "cluster_invalid.yaml" + mockCtrl := gomock.NewController(t) + setupContext() + kubectl := mocks.NewMockProviderKubectlClient(mockCtrl) + cluster := &types.Cluster{Name: "test"} + clusterSpec := givenClusterSpec(t, clusterSpecManifest) + datacenterConfig := givenDatacenterConfig(t, clusterSpecManifest) + machineConfigs := givenMachineConfigs(t, clusterSpecManifest) + ctx := context.Background() + cmk := givenWildcardCmk(mockCtrl) + provider := newProviderWithKubectl(t, datacenterConfig, machineConfigs, clusterSpec.Cluster, kubectl, cmk) + + err := provider.SetupAndValidateUpgradeCluster(ctx, cluster, clusterSpec) + tt.Expect(err).NotTo(BeNil()) +} + func TestProviderGenerateCAPISpecForCreateWithAffinity(t *testing.T) { clusterSpecManifest := "cluster_affinity.yaml" mockCtrl := gomock.NewController(t) @@ -653,7 +760,7 @@ func TestSetupAndValidateForCreateSSHAuthorizedKeyInvalidCP(t *testing.T) { tctx.SaveContext() err := provider.SetupAndValidateCreateCluster(ctx, clusterSpec) - thenErrorExpected(t, "failed setup and validations: ssh: no key found", err) + thenErrorExpected(t, "setting up SSH keys: ssh: no key found", err) } func TestSetupAndValidateForCreateSSHAuthorizedKeyInvalidWorker(t *testing.T) { @@ -668,7 +775,7 @@ func TestSetupAndValidateForCreateSSHAuthorizedKeyInvalidWorker(t *testing.T) { tctx.SaveContext() err := provider.SetupAndValidateCreateCluster(ctx, clusterSpec) - thenErrorExpected(t, "failed setup and validations: ssh: no key found", err) + thenErrorExpected(t, "setting up SSH keys: ssh: no key found", err) } func TestSetupAndValidateForCreateSSHAuthorizedKeyInvalidEtcd(t *testing.T) { @@ -683,7 +790,7 @@ func TestSetupAndValidateForCreateSSHAuthorizedKeyInvalidEtcd(t *testing.T) { tctx.SaveContext() err := provider.SetupAndValidateCreateCluster(ctx, clusterSpec) - thenErrorExpected(t, "failed setup and validations: ssh: no key found", err) + thenErrorExpected(t, "setting up SSH keys: ssh: no key found", err) } func TestSetupAndValidateSSHAuthorizedKeyEmptyCP(t *testing.T) { @@ -1329,7 +1436,7 @@ func TestSetupAndValidateForUpgradeSSHAuthorizedKeyInvalidCP(t *testing.T) { cluster := &types.Cluster{} err := provider.SetupAndValidateUpgradeCluster(ctx, cluster, clusterSpec) - thenErrorExpected(t, "failed setup and validations: ssh: no key found", err) + thenErrorExpected(t, "setting up SSH keys: ssh: no key found", err) } func TestSetupAndValidateForUpgradeSSHAuthorizedKeyInvalidWorker(t *testing.T) { @@ -1345,7 +1452,7 @@ func TestSetupAndValidateForUpgradeSSHAuthorizedKeyInvalidWorker(t *testing.T) { cluster := &types.Cluster{} err := provider.SetupAndValidateUpgradeCluster(ctx, cluster, clusterSpec) - thenErrorExpected(t, "failed setup and validations: ssh: no key found", err) + thenErrorExpected(t, "setting up SSH keys: ssh: no key found", err) } func TestSetupAndValidateForUpgradeSSHAuthorizedKeyInvalidEtcd(t *testing.T) { @@ -1361,7 +1468,7 @@ func TestSetupAndValidateForUpgradeSSHAuthorizedKeyInvalidEtcd(t *testing.T) { cluster := &types.Cluster{} err := provider.SetupAndValidateUpgradeCluster(ctx, cluster, clusterSpec) - thenErrorExpected(t, "failed setup and validations: ssh: no key found", err) + thenErrorExpected(t, "setting up SSH keys: ssh: no key found", err) } func TestClusterUpgradeNeededNoChanges(t *testing.T) { diff --git a/pkg/providers/cloudstack/decoder/decoder.go b/pkg/providers/cloudstack/decoder/decoder.go index 4bcab1c6c21b..b912a822aa4d 100644 --- a/pkg/providers/cloudstack/decoder/decoder.go +++ b/pkg/providers/cloudstack/decoder/decoder.go @@ -13,6 +13,8 @@ const ( EksacloudStackCloudConfigB64SecretKey = "EKSA_CLOUDSTACK_B64ENCODED_SECRET" CloudStackCloudConfigB64SecretKey = "CLOUDSTACK_B64ENCODED_SECRET" EksaCloudStackHostPathToMount = "EKSA_CLOUDSTACK_HOST_PATHS_TO_MOUNT" + CloudStackGlobalAZ = "Global" + defaultVerifySslValue = "true" ) // ParseCloudStackSecret parses the input b64 string into the ini object to extract out the api key, secret key, and url @@ -23,45 +25,63 @@ func ParseCloudStackSecret() (*CloudStackExecConfig, error) { } decodedString, err := b64.StdEncoding.DecodeString(cloudStackB64EncodedSecret) if err != nil { - return nil, fmt.Errorf("failed to decode value for %s with base64: %v", EksacloudStackCloudConfigB64SecretKey, err) + return nil, fmt.Errorf("decoding value for %s with base64: %v", EksacloudStackCloudConfigB64SecretKey, err) } cfg, err := ini.Load(decodedString) if err != nil { - return nil, fmt.Errorf("failed to extract values from %s with ini: %v", EksacloudStackCloudConfigB64SecretKey, err) + return nil, fmt.Errorf("extracting values from %s with ini: %v", EksacloudStackCloudConfigB64SecretKey, err) } - section, err := cfg.GetSection("Global") - if err != nil { - return nil, fmt.Errorf("failed to extract section 'Global' from %s: %v", EksacloudStackCloudConfigB64SecretKey, err) - } - apiKey, err := section.GetKey("api-key") - if err != nil { - return nil, fmt.Errorf("failed to extract value of 'api-key' from %s: %v", EksacloudStackCloudConfigB64SecretKey, err) - } - secretKey, err := section.GetKey("secret-key") - if err != nil { - return nil, fmt.Errorf("failed to extract value of 'secret-key' from %s: %v", EksacloudStackCloudConfigB64SecretKey, err) - } - apiUrl, err := section.GetKey("api-url") - if err != nil { - return nil, fmt.Errorf("failed to extract value of 'api-url' from %s: %v", EksacloudStackCloudConfigB64SecretKey, err) - } - verifySsl, err := section.GetKey("verify-ssl") - verifySslValue := "true" - if err == nil { - verifySslValue = verifySsl.Value() - if _, err := strconv.ParseBool(verifySslValue); err != nil { - return nil, fmt.Errorf("'verify-ssl' has invalid boolean string %s: %v", verifySslValue, err) + + cloudstackProfiles := []CloudStackProfileConfig{} + sections := cfg.Sections() + for _, section := range sections { + if section.Name() == "DEFAULT" { + continue } + + apiKey, err := section.GetKey("api-key") + if err != nil { + return nil, fmt.Errorf("extracting value of 'api-key' from %s: %v", section.Name(), err) + } + secretKey, err := section.GetKey("secret-key") + if err != nil { + return nil, fmt.Errorf("extracting value of 'secret-key' from %s: %v", EksacloudStackCloudConfigB64SecretKey, err) + } + apiUrl, err := section.GetKey("api-url") + if err != nil { + return nil, fmt.Errorf("extracting value of 'api-url' from %s: %v", EksacloudStackCloudConfigB64SecretKey, err) + } + verifySslValue := defaultVerifySslValue + if verifySsl, err := section.GetKey("verify-ssl"); err == nil { + verifySslValue = verifySsl.Value() + if _, err := strconv.ParseBool(verifySslValue); err != nil { + return nil, fmt.Errorf("'verify-ssl' has invalid boolean string %s: %v", verifySslValue, err) + } + } + cloudstackProfiles = append(cloudstackProfiles, CloudStackProfileConfig{ + Name: section.Name(), + ApiKey: apiKey.Value(), + SecretKey: secretKey.Value(), + ManagementUrl: apiUrl.Value(), + VerifySsl: verifySslValue, + }) } + + if len(cloudstackProfiles) == 0 { + return nil, fmt.Errorf("no instance found from %s", EksacloudStackCloudConfigB64SecretKey) + } + return &CloudStackExecConfig{ - ApiKey: apiKey.Value(), - SecretKey: secretKey.Value(), - ManagementUrl: apiUrl.Value(), - VerifySsl: verifySslValue, + Profiles: cloudstackProfiles, }, nil } type CloudStackExecConfig struct { + Profiles []CloudStackProfileConfig +} + +type CloudStackProfileConfig struct { + Name string ApiKey string SecretKey string ManagementUrl string diff --git a/pkg/providers/cloudstack/decoder/decoder_test.go b/pkg/providers/cloudstack/decoder/decoder_test.go index b57945be23d1..2dca15dc66d1 100644 --- a/pkg/providers/cloudstack/decoder/decoder_test.go +++ b/pkg/providers/cloudstack/decoder/decoder_test.go @@ -2,31 +2,17 @@ package decoder_test import ( _ "embed" + "encoding/base64" "os" + "reflect" "testing" . "github.com/onsi/gomega" + "github.com/aws/eks-anywhere/internal/test" "github.com/aws/eks-anywhere/pkg/providers/cloudstack/decoder" ) -const ( - apiKey = "test-key" - secretKey = "test-secret" - apiUrl = "http://127.16.0.1:8080/client/api" - verifySsl = "false" - defaultVerifySsl = "true" - validCloudStackCloudConfig = "W0dsb2JhbF0KYXBpLWtleSA9IHRlc3Qta2V5CnNlY3JldC1rZXkgPSB0ZXN0LXNlY3JldAphcGktdXJsID0gaHR0cDovLzEyNy4xNi4wLjE6ODA4MC9jbGllbnQvYXBpCnZlcmlmeS1zc2wgPSBmYWxzZQo=" - missingApiKey = "W0dsb2JhbF0Kc2VjcmV0LWtleSA9IHRlc3Qtc2VjcmV0CmFwaS11cmwgPSBodHRwOi8vMTI3LjE2LjAuMTo4MDgwL2NsaWVudC9hcGkKdmVyaWZ5LXNzbCA9IGZhbHNlCg==" - missingSecretKey = "W0dsb2JhbF0KYXBpLWtleSA9IHRlc3Qta2V5CmFwaS11cmwgPSBodHRwOi8vMTI3LjE2LjAuMTo4MDgwL2NsaWVudC9hcGkKdmVyaWZ5LXNzbCA9IGZhbHNlCg==" - missingApiUrl = "W0dsb2JhbF0KYXBpLWtleSA9IHRlc3Qta2V5CnNlY3JldC1rZXkgPSB0ZXN0LXNlY3JldAp2ZXJpZnktc3NsID0gZmFsc2UK" - missingVerifySsl = "W0dsb2JhbF0KYXBpLWtleSA9IHRlc3Qta2V5CnNlY3JldC1rZXkgPSB0ZXN0LXNlY3JldAphcGktdXJsID0gaHR0cDovLzEyNy4xNi4wLjE6ODA4MC9jbGllbnQvYXBpCg==" - invalidVerifySslValue = "W0dsb2JhbF0KYXBpLWtleSA9IHRlc3Qta2V5CnNlY3JldC1rZXkgPSB0ZXN0LXNlY3JldAphcGktdXJsID0gaHR0cDovLzEyNy4xNi4wLjE6ODA4MC9jbGllbnQvYXBpCnZlcmlmeS1zc2wgPSBUVFRUVAo=" - missingGlobalSection = "YXBpLWtleSA9IHRlc3Qta2V5CnNlY3JldC1rZXkgPSB0ZXN0LXNlY3JldAphcGktdXJsID0gaHR0cDovLzEyNy4xNi4wLjE6ODA4MC9jbGllbnQvYXBpCnZlcmlmeS1zc2wgPSBmYWxzZQo=" - invalidINI = "W0dsb2JhbF0KYXBpLWtleSA7IHRlc3Qta2V5CnNlY3JldC1rZXkgOyB0ZXN0LXNlY3JldAphcGktdXJsIDsgaHR0cDovLzEyNy4xNi4wLjE6ODA4MC9jbGllbnQvYXBpCnZlcmlmeS1zc2wgOyBmYWxzZQo=" - invalidEncoding = "=====W0dsb2JhbF0KYXBpLWtleSA7IHRlc3Qta2V5CnNlY3JldC1rZXkgOyB0ZXN0LXNlY3JldAphcGktdXJsIDsgaHR0cDovLzEyNy4xNi4wLjE6ODA4MC9jbGllbnQvYXBpCnZlcmlmeS1zc2wgOyBmYWxzZQo======" -) - type testContext struct { oldCloudStackCloudConfigSecret string isCloudStackCloudConfigSecretSet bool @@ -42,130 +28,139 @@ func (tctx *testContext) restoreContext() { } } -func TestValidConfigShouldSucceedtoParse(t *testing.T) { - var tctx testContext - tctx.backupContext() - - g := NewWithT(t) - os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, validCloudStackCloudConfig) - execConfig, err := decoder.ParseCloudStackSecret() - g.Expect(err).To(BeNil(), "An error occurred when parsing a valid secret") - g.Expect(execConfig.ApiKey).To(Equal(apiKey)) - g.Expect(execConfig.SecretKey).To(Equal(secretKey)) - g.Expect(execConfig.ManagementUrl).To(Equal(apiUrl)) - g.Expect(execConfig.VerifySsl).To(Equal(verifySsl)) - - tctx.restoreContext() -} - -func TestMissingApiKeyShouldFailToParse(t *testing.T) { - var tctx testContext - tctx.backupContext() - - g := NewWithT(t) - os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, missingApiKey) - _, err := decoder.ParseCloudStackSecret() - g.Expect(err).ToNot(BeNil()) - - tctx.restoreContext() -} - -func TestMissingSecretKeyShouldFailToParse(t *testing.T) { - var tctx testContext - tctx.backupContext() - - g := NewWithT(t) - os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, missingSecretKey) - _, err := decoder.ParseCloudStackSecret() - g.Expect(err).ToNot(BeNil()) - - tctx.restoreContext() -} - -func TestMissingApiUrlShouldFailToParse(t *testing.T) { - var tctx testContext - tctx.backupContext() - - g := NewWithT(t) - os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, missingApiUrl) - _, err := decoder.ParseCloudStackSecret() - g.Expect(err).ToNot(BeNil()) - - tctx.restoreContext() -} - -func TestMissingVerifySslShouldSetDefaultValue(t *testing.T) { - var tctx testContext - tctx.backupContext() - - g := NewWithT(t) - os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, missingVerifySsl) - execConfig, err := decoder.ParseCloudStackSecret() - g.Expect(err).To(BeNil(), "An error occurred when parsing a valid secret") - g.Expect(execConfig.ApiKey).To(Equal(apiKey)) - g.Expect(execConfig.SecretKey).To(Equal(secretKey)) - g.Expect(execConfig.ManagementUrl).To(Equal(apiUrl)) - g.Expect(execConfig.VerifySsl).To(Equal(defaultVerifySsl)) +func TestCloudStackConfigDecoder(t *testing.T) { + tests := []struct { + name string + configFile string + wantErr bool + wantConfig *decoder.CloudStackExecConfig + }{ + { + name: "Valid config", + configFile: "../testdata/cloudstack_config_valid.ini", + wantErr: false, + wantConfig: &decoder.CloudStackExecConfig{ + Profiles: []decoder.CloudStackProfileConfig{ + { + Name: decoder.CloudStackGlobalAZ, + ApiKey: "test-key1", + SecretKey: "test-secret1", + ManagementUrl: "http://127.16.0.1:8080/client/api", + VerifySsl: "false", + Timeout: "", + }, + }, + }, + }, + { + name: "Multiple profiles config", + configFile: "../testdata/cloudstack_config_multiple_profiles.ini", + wantErr: false, + wantConfig: &decoder.CloudStackExecConfig{ + Profiles: []decoder.CloudStackProfileConfig{ + { + Name: decoder.CloudStackGlobalAZ, + ApiKey: "test-key1", + SecretKey: "test-secret1", + ManagementUrl: "http://127.16.0.1:8080/client/api", + VerifySsl: "false", + }, + { + Name: "Instance2", + ApiKey: "test-key2", + SecretKey: "test-secret2", + ManagementUrl: "http://127.16.0.2:8080/client/api", + VerifySsl: "true", + Timeout: "", + }, + }, + }, + }, + { + name: "Missing apikey", + configFile: "../testdata/cloudstack_config_missing_apikey.ini", + wantErr: true, + }, + { + name: "Missing secretkey", + configFile: "../testdata/cloudstack_config_missing_secretkey.ini", + wantErr: true, + }, + { + name: "Missing apiurl", + configFile: "../testdata/cloudstack_config_missing_apiurl.ini", + wantErr: true, + }, + { + name: "Missing verifyssl", + configFile: "../testdata/cloudstack_config_missing_verifyssl.ini", + wantErr: false, + wantConfig: &decoder.CloudStackExecConfig{ + Profiles: []decoder.CloudStackProfileConfig{ + { + Name: decoder.CloudStackGlobalAZ, + ApiKey: "test-key1", + SecretKey: "test-secret1", + ManagementUrl: "http://127.16.0.1:8080/client/api", + VerifySsl: "true", + Timeout: "", + }, + }, + }, + }, + { + name: "Invalid INI format", + configFile: "../testdata/cloudstack_config_invalid_format.ini", + wantErr: true, + }, + { + name: "Invalid veryfyssl value", + configFile: "../testdata/cloudstack_config_invalid_verifyssl.ini", + wantErr: true, + }, + { + name: "No sections", + configFile: "../testdata/cloudstack_config_no_sections.ini", + wantErr: true, + }, + } - tctx.restoreContext() + for _, tc := range tests { + t.Run(tc.name, func(tt *testing.T) { + g := NewWithT(t) + configString := test.ReadFile(t, tc.configFile) + encodedConfig := base64.StdEncoding.EncodeToString([]byte(configString)) + tt.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, encodedConfig) + + gotConfig, err := decoder.ParseCloudStackSecret() + if tc.wantErr { + g.Expect(err).NotTo(BeNil()) + } else { + g.Expect(err).To(BeNil()) + if !reflect.DeepEqual(tc.wantConfig, gotConfig) { + t.Errorf("%v got = %v, want %v", tc.name, gotConfig, tc.wantConfig) + } + } + }) + } } -func TestInvalidVerifySslShouldFailToParse(t *testing.T) { - var tctx testContext - tctx.backupContext() - +func TestCloudStackConfigDecoderInvalidEncoding(t *testing.T) { g := NewWithT(t) - os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, invalidVerifySslValue) - _, err := decoder.ParseCloudStackSecret() - g.Expect(err).ToNot(BeNil()) + t.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, "xxx") - tctx.restoreContext() -} - -func TestMissingGlobalSectionShouldFailToParse(t *testing.T) { - var tctx testContext - tctx.backupContext() - - g := NewWithT(t) - os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, missingGlobalSection) _, err := decoder.ParseCloudStackSecret() - g.Expect(err).ToNot(BeNil()) - - tctx.restoreContext() + g.Expect(err).NotTo(BeNil()) } -func TestInvalidINIShouldFailToParse(t *testing.T) { +func TestCloudStackConfigDecoderNoEnvVariable(t *testing.T) { var tctx testContext tctx.backupContext() + os.Clearenv() g := NewWithT(t) - os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, invalidINI) - _, err := decoder.ParseCloudStackSecret() - g.Expect(err).ToNot(BeNil()) - - tctx.restoreContext() -} - -func TestMissingEnvVariableShouldFailToParse(t *testing.T) { - var tctx testContext - tctx.backupContext() - - g := NewWithT(t) - os.Unsetenv(decoder.EksacloudStackCloudConfigB64SecretKey) - _, err := decoder.ParseCloudStackSecret() - g.Expect(err).ToNot(BeNil()) - tctx.restoreContext() -} - -func TestInvalidEncodingShouldFailToParse(t *testing.T) { - var tctx testContext - tctx.backupContext() - - g := NewWithT(t) - os.Setenv(decoder.EksacloudStackCloudConfigB64SecretKey, invalidEncoding) _, err := decoder.ParseCloudStackSecret() - g.Expect(err).ToNot(BeNil()) - + g.Expect(err).NotTo(BeNil()) tctx.restoreContext() } diff --git a/pkg/providers/cloudstack/mocks/client.go b/pkg/providers/cloudstack/mocks/client.go index afd957431e58..61411535977d 100644 --- a/pkg/providers/cloudstack/mocks/client.go +++ b/pkg/providers/cloudstack/mocks/client.go @@ -41,79 +41,94 @@ func (m *MockProviderCmkClient) EXPECT() *MockProviderCmkClientMockRecorder { return m.recorder } +// GetManagementApiEndpoint mocks base method. +func (m *MockProviderCmkClient) GetManagementApiEndpoint(arg0 string) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetManagementApiEndpoint", arg0) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetManagementApiEndpoint indicates an expected call of GetManagementApiEndpoint. +func (mr *MockProviderCmkClientMockRecorder) GetManagementApiEndpoint(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetManagementApiEndpoint", reflect.TypeOf((*MockProviderCmkClient)(nil).GetManagementApiEndpoint), arg0) +} + // ValidateAccountPresent mocks base method. -func (m *MockProviderCmkClient) ValidateAccountPresent(arg0 context.Context, arg1, arg2 string) error { +func (m *MockProviderCmkClient) ValidateAccountPresent(arg0 context.Context, arg1, arg2, arg3 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateAccountPresent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "ValidateAccountPresent", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // ValidateAccountPresent indicates an expected call of ValidateAccountPresent. -func (mr *MockProviderCmkClientMockRecorder) ValidateAccountPresent(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockProviderCmkClientMockRecorder) ValidateAccountPresent(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAccountPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateAccountPresent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAccountPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateAccountPresent), arg0, arg1, arg2, arg3) } // ValidateAffinityGroupsPresent mocks base method. -func (m *MockProviderCmkClient) ValidateAffinityGroupsPresent(arg0 context.Context, arg1, arg2 string, arg3 []string) error { +func (m *MockProviderCmkClient) ValidateAffinityGroupsPresent(arg0 context.Context, arg1, arg2, arg3 string, arg4 []string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateAffinityGroupsPresent", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "ValidateAffinityGroupsPresent", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(error) return ret0 } // ValidateAffinityGroupsPresent indicates an expected call of ValidateAffinityGroupsPresent. -func (mr *MockProviderCmkClientMockRecorder) ValidateAffinityGroupsPresent(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockProviderCmkClientMockRecorder) ValidateAffinityGroupsPresent(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAffinityGroupsPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateAffinityGroupsPresent), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAffinityGroupsPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateAffinityGroupsPresent), arg0, arg1, arg2, arg3, arg4) } // ValidateCloudStackConnection mocks base method. -func (m *MockProviderCmkClient) ValidateCloudStackConnection(arg0 context.Context) error { +func (m *MockProviderCmkClient) ValidateCloudStackConnection(arg0 context.Context, arg1 string) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateCloudStackConnection", arg0) + ret := m.ctrl.Call(m, "ValidateCloudStackConnection", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // ValidateCloudStackConnection indicates an expected call of ValidateCloudStackConnection. -func (mr *MockProviderCmkClientMockRecorder) ValidateCloudStackConnection(arg0 interface{}) *gomock.Call { +func (mr *MockProviderCmkClientMockRecorder) ValidateCloudStackConnection(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateCloudStackConnection", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateCloudStackConnection), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateCloudStackConnection", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateCloudStackConnection), arg0, arg1) } // ValidateDiskOfferingPresent mocks base method. -func (m *MockProviderCmkClient) ValidateDiskOfferingPresent(arg0 context.Context, arg1 string, arg2 v1alpha1.CloudStackResourceDiskOffering) error { +func (m *MockProviderCmkClient) ValidateDiskOfferingPresent(arg0 context.Context, arg1, arg2 string, arg3 v1alpha1.CloudStackResourceDiskOffering) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateDiskOfferingPresent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "ValidateDiskOfferingPresent", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // ValidateDiskOfferingPresent indicates an expected call of ValidateDiskOfferingPresent. -func (mr *MockProviderCmkClientMockRecorder) ValidateDiskOfferingPresent(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockProviderCmkClientMockRecorder) ValidateDiskOfferingPresent(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDiskOfferingPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateDiskOfferingPresent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDiskOfferingPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateDiskOfferingPresent), arg0, arg1, arg2, arg3) } -// ValidateDomainPresent mocks base method. -func (m *MockProviderCmkClient) ValidateDomainPresent(arg0 context.Context, arg1 string) (v1alpha1.CloudStackResourceIdentifier, error) { +// ValidateDomainAndGetId mocks base method. +func (m *MockProviderCmkClient) ValidateDomainAndGetId(arg0 context.Context, arg1, arg2 string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateDomainPresent", arg0, arg1) - ret0, _ := ret[0].(v1alpha1.CloudStackResourceIdentifier) + ret := m.ctrl.Call(m, "ValidateDomainAndGetId", arg0, arg1, arg2) + ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } -// ValidateDomainPresent indicates an expected call of ValidateDomainPresent. -func (mr *MockProviderCmkClientMockRecorder) ValidateDomainPresent(arg0, arg1 interface{}) *gomock.Call { +// ValidateDomainAndGetId indicates an expected call of ValidateDomainAndGetId. +func (mr *MockProviderCmkClientMockRecorder) ValidateDomainAndGetId(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDomainPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateDomainPresent), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateDomainAndGetId", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateDomainAndGetId), arg0, arg1, arg2) } // ValidateNetworkPresent mocks base method. -func (m *MockProviderCmkClient) ValidateNetworkPresent(arg0 context.Context, arg1 string, arg2 v1alpha1.CloudStackZone, arg3 []v1alpha1.CloudStackResourceIdentifier, arg4 string, arg5 bool) error { +func (m *MockProviderCmkClient) ValidateNetworkPresent(arg0 context.Context, arg1, arg2 string, arg3 v1alpha1.CloudStackResourceIdentifier, arg4, arg5 string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ValidateNetworkPresent", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(error) @@ -127,46 +142,46 @@ func (mr *MockProviderCmkClientMockRecorder) ValidateNetworkPresent(arg0, arg1, } // ValidateServiceOfferingPresent mocks base method. -func (m *MockProviderCmkClient) ValidateServiceOfferingPresent(arg0 context.Context, arg1 string, arg2 v1alpha1.CloudStackResourceIdentifier) error { +func (m *MockProviderCmkClient) ValidateServiceOfferingPresent(arg0 context.Context, arg1, arg2 string, arg3 v1alpha1.CloudStackResourceIdentifier) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateServiceOfferingPresent", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "ValidateServiceOfferingPresent", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(error) return ret0 } // ValidateServiceOfferingPresent indicates an expected call of ValidateServiceOfferingPresent. -func (mr *MockProviderCmkClientMockRecorder) ValidateServiceOfferingPresent(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockProviderCmkClientMockRecorder) ValidateServiceOfferingPresent(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateServiceOfferingPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateServiceOfferingPresent), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateServiceOfferingPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateServiceOfferingPresent), arg0, arg1, arg2, arg3) } // ValidateTemplatePresent mocks base method. -func (m *MockProviderCmkClient) ValidateTemplatePresent(arg0 context.Context, arg1, arg2, arg3 string, arg4 v1alpha1.CloudStackResourceIdentifier) error { +func (m *MockProviderCmkClient) ValidateTemplatePresent(arg0 context.Context, arg1, arg2, arg3, arg4 string, arg5 v1alpha1.CloudStackResourceIdentifier) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateTemplatePresent", arg0, arg1, arg2, arg3, arg4) + ret := m.ctrl.Call(m, "ValidateTemplatePresent", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].(error) return ret0 } // ValidateTemplatePresent indicates an expected call of ValidateTemplatePresent. -func (mr *MockProviderCmkClientMockRecorder) ValidateTemplatePresent(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockProviderCmkClientMockRecorder) ValidateTemplatePresent(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateTemplatePresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateTemplatePresent), arg0, arg1, arg2, arg3, arg4) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateTemplatePresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateTemplatePresent), arg0, arg1, arg2, arg3, arg4, arg5) } -// ValidateZonesPresent mocks base method. -func (m *MockProviderCmkClient) ValidateZonesPresent(arg0 context.Context, arg1 []v1alpha1.CloudStackZone) ([]v1alpha1.CloudStackResourceIdentifier, error) { +// ValidateZoneAndGetId mocks base method. +func (m *MockProviderCmkClient) ValidateZoneAndGetId(arg0 context.Context, arg1 string, arg2 v1alpha1.CloudStackZone) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateZonesPresent", arg0, arg1) - ret0, _ := ret[0].([]v1alpha1.CloudStackResourceIdentifier) + ret := m.ctrl.Call(m, "ValidateZoneAndGetId", arg0, arg1, arg2) + ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } -// ValidateZonesPresent indicates an expected call of ValidateZonesPresent. -func (mr *MockProviderCmkClientMockRecorder) ValidateZonesPresent(arg0, arg1 interface{}) *gomock.Call { +// ValidateZoneAndGetId indicates an expected call of ValidateZoneAndGetId. +func (mr *MockProviderCmkClientMockRecorder) ValidateZoneAndGetId(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateZonesPresent", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateZonesPresent), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateZoneAndGetId", reflect.TypeOf((*MockProviderCmkClient)(nil).ValidateZoneAndGetId), arg0, arg1, arg2) } // MockProviderKubectlClient is a mock of ProviderKubectlClient interface. diff --git a/pkg/providers/cloudstack/testdata/cloudstack_config_invalid_format.ini b/pkg/providers/cloudstack/testdata/cloudstack_config_invalid_format.ini new file mode 100644 index 000000000000..39efacf46d27 --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cloudstack_config_invalid_format.ini @@ -0,0 +1,5 @@ +[Global] +verify-ssl ; false +api-key ; test-key1 +secret-key ; test-secret1 +api-url ; http://127.16.0.1:8080/client/api diff --git a/pkg/providers/cloudstack/testdata/cloudstack_config_invalid_verifyssl.ini b/pkg/providers/cloudstack/testdata/cloudstack_config_invalid_verifyssl.ini new file mode 100644 index 000000000000..f4b16eafe5a9 --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cloudstack_config_invalid_verifyssl.ini @@ -0,0 +1,5 @@ +[Global] +verify-ssl = xxx +api-key = test-key1 +secret-key = test-secret1 +api-url = http://127.16.0.1:8080/client/api diff --git a/pkg/providers/cloudstack/testdata/cloudstack_config_missing_apikey.ini b/pkg/providers/cloudstack/testdata/cloudstack_config_missing_apikey.ini new file mode 100644 index 000000000000..b87524efbe0c --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cloudstack_config_missing_apikey.ini @@ -0,0 +1,4 @@ +[Global] +verify-ssl = false +secret-key = test-secret1 +api-url = http://127.16.0.1:8080/client/api diff --git a/pkg/providers/cloudstack/testdata/cloudstack_config_missing_apiurl.ini b/pkg/providers/cloudstack/testdata/cloudstack_config_missing_apiurl.ini new file mode 100644 index 000000000000..97944ad77d9c --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cloudstack_config_missing_apiurl.ini @@ -0,0 +1,4 @@ +[Global] +verify-ssl = false +api-key = test-key1 +secret-key = test-secret1 diff --git a/pkg/providers/cloudstack/testdata/cloudstack_config_missing_secretkey.ini b/pkg/providers/cloudstack/testdata/cloudstack_config_missing_secretkey.ini new file mode 100644 index 000000000000..30f825c18049 --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cloudstack_config_missing_secretkey.ini @@ -0,0 +1,4 @@ +[Global] +verify-ssl = false +api-key = test-key1 +api-url = http://127.16.0.1:8080/client/api diff --git a/pkg/providers/cloudstack/testdata/cloudstack_config_missing_verifyssl.ini b/pkg/providers/cloudstack/testdata/cloudstack_config_missing_verifyssl.ini new file mode 100644 index 000000000000..8be593bb22df --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cloudstack_config_missing_verifyssl.ini @@ -0,0 +1,4 @@ +[Global] +api-key = test-key1 +secret-key = test-secret1 +api-url = http://127.16.0.1:8080/client/api diff --git a/pkg/providers/cloudstack/testdata/cloudstack_config_multiple_profiles.ini b/pkg/providers/cloudstack/testdata/cloudstack_config_multiple_profiles.ini new file mode 100644 index 000000000000..ead54c30f37c --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cloudstack_config_multiple_profiles.ini @@ -0,0 +1,11 @@ +[Global] +verify-ssl = false +api-key = test-key1 +secret-key = test-secret1 +api-url = http://127.16.0.1:8080/client/api + +[Instance2] +verify-ssl = true +api-key = test-key2 +secret-key = test-secret2 +api-url = http://127.16.0.2:8080/client/api diff --git a/pkg/providers/cloudstack/testdata/cloudstack_config_no_sections.ini b/pkg/providers/cloudstack/testdata/cloudstack_config_no_sections.ini new file mode 100644 index 000000000000..3266d4477b71 --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cloudstack_config_no_sections.ini @@ -0,0 +1,4 @@ +verify-ssl = false +api-key = test-key1 +secret-key = test-secret1 +api-url = http://127.16.0.1:8080/client/api diff --git a/pkg/providers/cloudstack/testdata/cloudstack_config_valid.ini b/pkg/providers/cloudstack/testdata/cloudstack_config_valid.ini new file mode 100644 index 000000000000..87243f4b0d30 --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cloudstack_config_valid.ini @@ -0,0 +1,5 @@ +[Global] +verify-ssl = false +api-key = test-key1 +secret-key = test-secret1 +api-url = http://127.16.0.1:8080/client/api diff --git a/pkg/providers/cloudstack/testdata/cluster_invalid.yaml b/pkg/providers/cloudstack/testdata/cluster_invalid.yaml new file mode 100644 index 000000000000..7b2463a6a604 --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cluster_invalid.yaml @@ -0,0 +1,123 @@ +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: Cluster +metadata: + name: test + namespace: test-namespace +spec: + clusterNetwork: + cni: cilium + pods: + cidrBlocks: + - 192.168.0.0/16 + services: + cidrBlocks: + - 10.96.0.0/12 + controlPlaneConfiguration: + count: 3 + endpoint: + host: 1.2.3.4 + machineGroupRef: + kind: CloudStackMachineConfig + name: test-cp + datacenterRef: + kind: CloudStackDatacenterConfig + name: test + externalEtcdConfiguration: + count: 3 + machineGroupRef: + kind: CloudStackMachineConfig + name: test-etcd + kubernetesVersion: "1.21" + workerNodeGroupConfigurations: + - count: 3 + machineGroupRef: + kind: CloudStackMachineConfig + name: test +--- +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: CloudStackDatacenterConfig +metadata: + name: test + namespace: test-namespace +spec: + {} +--- +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: CloudStackMachineConfig +metadata: + name: test-cp + namespace: test-namespace +spec: + computeOffering: + name: "m4-large" + users: + - name: "mySshUsername" + sshAuthorizedKeys: # The key below was manually generated and not used in any production systems + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1BK73XhIzjX+meUr7pIYh6RHbvI3tmHeQIXY5lv7aztN1UoX+bhPo3dwo2sfSQn5kuxgQdnxIZ/CTzy0p0GkEYVv3gwspCeurjmu0XmrdmaSGcGxCEWT/65NtvYrQtUE5ELxJ+N/aeZNlK2B7IWANnw/82913asXH4VksV1NYNduP0o1/G4XcwLLSyVFB078q/oEnmvdNIoS61j4/o36HVtENJgYr0idcBvwJdvcGxGnPaqOhx477t+kfJAa5n5dSA5wilIaoXH5i1Tf/HsTCM52L+iNCARvQzJYZhzbWI1MDQwzILtIBEQCJsl2XSqIupleY8CxqQ6jCXt2mhae+wPc3YmbO5rFvr2/EvC57kh3yDs1Nsuj8KOvD78KeeujbR8n8pScm3WDp62HFQ8lEKNdeRNj6kB8WnuaJvPnyZfvzOhwG65/9w13IBl7B1sWxbFnq2rMpm5uHVK7mAmjL0Tt8zoDhcE1YJEnp9xte3/pvmKPkST5Q/9ZtR9P5sI+02jY0fvPkPyC03j2gsPixG7rpOCwpOdbny4dcj0TDeeXJX8er+oVfJuLYz0pNWJcT2raDdFfcqvYA0B0IyNYlj5nWX4RuEcyT3qocLReWPnZojetvAG/H8XwOh7fEVGqHAKOVSnPXCSQJPl6s0H12jPJBDJMTydtYPEszl4/CeQ== testemail@test.com" + template: + name: "centos7-k8s-118" + diskOffering: + name: "Small" + mountPath: "/data-small" + device: "/dev/vdb" + filesystem: "ext4" + label: "data_disk" + symlinks: + /var/log/kubernetes: /data-small/var/log/kubernetes + affinityGroupIds: + - control-plane-anti-affinity +--- +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: CloudStackMachineConfig +metadata: + name: test + namespace: test-namespace +spec: + computeOffering: + name: "m4-large" + users: + - name: "mySshUsername" + sshAuthorizedKeys: # The key below was manually generated and not used in any production systems + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1BK73XhIzjX+meUr7pIYh6RHbvI3tmHeQIXY5lv7aztN1UoX+bhPo3dwo2sfSQn5kuxgQdnxIZ/CTzy0p0GkEYVv3gwspCeurjmu0XmrdmaSGcGxCEWT/65NtvYrQtUE5ELxJ+N/aeZNlK2B7IWANnw/82913asXH4VksV1NYNduP0o1/G4XcwLLSyVFB078q/oEnmvdNIoS61j4/o36HVtENJgYr0idcBvwJdvcGxGnPaqOhx477t+kfJAa5n5dSA5wilIaoXH5i1Tf/HsTCM52L+iNCARvQzJYZhzbWI1MDQwzILtIBEQCJsl2XSqIupleY8CxqQ6jCXt2mhae+wPc3YmbO5rFvr2/EvC57kh3yDs1Nsuj8KOvD78KeeujbR8n8pScm3WDp62HFQ8lEKNdeRNj6kB8WnuaJvPnyZfvzOhwG65/9w13IBl7B1sWxbFnq2rMpm5uHVK7mAmjL0Tt8zoDhcE1YJEnp9xte3/pvmKPkST5Q/9ZtR9P5sI+02jY0fvPkPyC03j2gsPixG7rpOCwpOdbny4dcj0TDeeXJX8er+oVfJuLYz0pNWJcT2raDdFfcqvYA0B0IyNYlj5nWX4RuEcyT3qocLReWPnZojetvAG/H8XwOh7fEVGqHAKOVSnPXCSQJPl6s0H12jPJBDJMTydtYPEszl4/CeQ== testemail@test.com" + template: + name: "centos7-k8s-118" + diskOffering: + name: "Small" + mountPath: "/data-small" + device: "/dev/vdb" + filesystem: "ext4" + label: "data_disk" + symlinks: + /var/log/pods: /data-small/var/log/pods + /var/log/containers: /data-small/var/log/containers + affinityGroupIds: + - worker-affinity + userCustomDetails: + foo: bar +--- +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: CloudStackMachineConfig +metadata: + name: test-etcd + namespace: test-namespace +spec: + computeOffering: + name: "m4-large" + users: + - name: "mySshUsername" + sshAuthorizedKeys: # The key below was manually generated and not used in any production systems + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1BK73XhIzjX+meUr7pIYh6RHbvI3tmHeQIXY5lv7aztN1UoX+bhPo3dwo2sfSQn5kuxgQdnxIZ/CTzy0p0GkEYVv3gwspCeurjmu0XmrdmaSGcGxCEWT/65NtvYrQtUE5ELxJ+N/aeZNlK2B7IWANnw/82913asXH4VksV1NYNduP0o1/G4XcwLLSyVFB078q/oEnmvdNIoS61j4/o36HVtENJgYr0idcBvwJdvcGxGnPaqOhx477t+kfJAa5n5dSA5wilIaoXH5i1Tf/HsTCM52L+iNCARvQzJYZhzbWI1MDQwzILtIBEQCJsl2XSqIupleY8CxqQ6jCXt2mhae+wPc3YmbO5rFvr2/EvC57kh3yDs1Nsuj8KOvD78KeeujbR8n8pScm3WDp62HFQ8lEKNdeRNj6kB8WnuaJvPnyZfvzOhwG65/9w13IBl7B1sWxbFnq2rMpm5uHVK7mAmjL0Tt8zoDhcE1YJEnp9xte3/pvmKPkST5Q/9ZtR9P5sI+02jY0fvPkPyC03j2gsPixG7rpOCwpOdbny4dcj0TDeeXJX8er+oVfJuLYz0pNWJcT2raDdFfcqvYA0B0IyNYlj5nWX4RuEcyT3qocLReWPnZojetvAG/H8XwOh7fEVGqHAKOVSnPXCSQJPl6s0H12jPJBDJMTydtYPEszl4/CeQ== testemail@test.com" + template: + name: "centos7-k8s-118" + diskOffering: + name: "Small" + mountPath: "/data-small" + device: "/dev/vdb" + filesystem: "ext4" + label: "data_disk" + symlinks: + /var/lib/: /data-small/var/lib + affinityGroupIds: + - etcd-affinity + +--- diff --git a/pkg/providers/cloudstack/testdata/cluster_main_with_availability_zones.yaml b/pkg/providers/cloudstack/testdata/cluster_main_with_availability_zones.yaml new file mode 100644 index 000000000000..48a983213f9b --- /dev/null +++ b/pkg/providers/cloudstack/testdata/cluster_main_with_availability_zones.yaml @@ -0,0 +1,139 @@ +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: Cluster +metadata: + name: test + namespace: test-namespace +spec: + clusterNetwork: + cni: cilium + pods: + cidrBlocks: + - 192.168.0.0/16 + services: + cidrBlocks: + - 10.96.0.0/12 + controlPlaneConfiguration: + count: 3 + endpoint: + host: 1.2.3.4 + machineGroupRef: + kind: CloudStackMachineConfig + name: test-cp + datacenterRef: + kind: CloudStackDatacenterConfig + name: test + externalEtcdConfiguration: + count: 3 + machineGroupRef: + kind: CloudStackMachineConfig + name: test-etcd + kubernetesVersion: "1.21" + workerNodeGroupConfigurations: + - count: 3 + machineGroupRef: + kind: CloudStackMachineConfig + name: test +--- +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: CloudStackDatacenterConfig +metadata: + name: test + namespace: test-namespace +spec: + account: "admin" + domain: "domain1" + zones: + - name: "zone1" + network: + name: "net1" + managementApiEndpoint: "http://127.16.0.1:8080/client/api" + availabilityZones: + - credentialsRef: "zone2" + account: "admin" + domain: "domain2" + managementApiEndpoint: "http://127.16.0.2:8080/client/api" + zone: + name: "zone2" + network: + name: "net2" + +--- +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: CloudStackMachineConfig +metadata: + name: test-cp + namespace: test-namespace +spec: + computeOffering: + name: "m4-large" + users: + - name: "mySshUsername" + sshAuthorizedKeys: # The key below was manually generated and not used in any production systems + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1BK73XhIzjX+meUr7pIYh6RHbvI3tmHeQIXY5lv7aztN1UoX+bhPo3dwo2sfSQn5kuxgQdnxIZ/CTzy0p0GkEYVv3gwspCeurjmu0XmrdmaSGcGxCEWT/65NtvYrQtUE5ELxJ+N/aeZNlK2B7IWANnw/82913asXH4VksV1NYNduP0o1/G4XcwLLSyVFB078q/oEnmvdNIoS61j4/o36HVtENJgYr0idcBvwJdvcGxGnPaqOhx477t+kfJAa5n5dSA5wilIaoXH5i1Tf/HsTCM52L+iNCARvQzJYZhzbWI1MDQwzILtIBEQCJsl2XSqIupleY8CxqQ6jCXt2mhae+wPc3YmbO5rFvr2/EvC57kh3yDs1Nsuj8KOvD78KeeujbR8n8pScm3WDp62HFQ8lEKNdeRNj6kB8WnuaJvPnyZfvzOhwG65/9w13IBl7B1sWxbFnq2rMpm5uHVK7mAmjL0Tt8zoDhcE1YJEnp9xte3/pvmKPkST5Q/9ZtR9P5sI+02jY0fvPkPyC03j2gsPixG7rpOCwpOdbny4dcj0TDeeXJX8er+oVfJuLYz0pNWJcT2raDdFfcqvYA0B0IyNYlj5nWX4RuEcyT3qocLReWPnZojetvAG/H8XwOh7fEVGqHAKOVSnPXCSQJPl6s0H12jPJBDJMTydtYPEszl4/CeQ== testemail@test.com" + template: + name: "centos7-k8s-118" + diskOffering: + name: "Small" + mountPath: "/data-small" + device: "/dev/vdb" + filesystem: "ext4" + label: "data_disk" + symlinks: + /var/log/kubernetes: /data-small/var/log/kubernetes + affinityGroupIds: + - control-plane-anti-affinity +--- +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: CloudStackMachineConfig +metadata: + name: test + namespace: test-namespace +spec: + computeOffering: + name: "m4-large" + users: + - name: "mySshUsername" + sshAuthorizedKeys: # The key below was manually generated and not used in any production systems + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1BK73XhIzjX+meUr7pIYh6RHbvI3tmHeQIXY5lv7aztN1UoX+bhPo3dwo2sfSQn5kuxgQdnxIZ/CTzy0p0GkEYVv3gwspCeurjmu0XmrdmaSGcGxCEWT/65NtvYrQtUE5ELxJ+N/aeZNlK2B7IWANnw/82913asXH4VksV1NYNduP0o1/G4XcwLLSyVFB078q/oEnmvdNIoS61j4/o36HVtENJgYr0idcBvwJdvcGxGnPaqOhx477t+kfJAa5n5dSA5wilIaoXH5i1Tf/HsTCM52L+iNCARvQzJYZhzbWI1MDQwzILtIBEQCJsl2XSqIupleY8CxqQ6jCXt2mhae+wPc3YmbO5rFvr2/EvC57kh3yDs1Nsuj8KOvD78KeeujbR8n8pScm3WDp62HFQ8lEKNdeRNj6kB8WnuaJvPnyZfvzOhwG65/9w13IBl7B1sWxbFnq2rMpm5uHVK7mAmjL0Tt8zoDhcE1YJEnp9xte3/pvmKPkST5Q/9ZtR9P5sI+02jY0fvPkPyC03j2gsPixG7rpOCwpOdbny4dcj0TDeeXJX8er+oVfJuLYz0pNWJcT2raDdFfcqvYA0B0IyNYlj5nWX4RuEcyT3qocLReWPnZojetvAG/H8XwOh7fEVGqHAKOVSnPXCSQJPl6s0H12jPJBDJMTydtYPEszl4/CeQ== testemail@test.com" + template: + name: "centos7-k8s-118" + diskOffering: + name: "Small" + mountPath: "/data-small" + device: "/dev/vdb" + filesystem: "ext4" + label: "data_disk" + symlinks: + /var/log/pods: /data-small/var/log/pods + /var/log/containers: /data-small/var/log/containers + affinityGroupIds: + - worker-affinity + userCustomDetails: + foo: bar +--- +apiVersion: anywhere.eks.amazonaws.com/v1alpha1 +kind: CloudStackMachineConfig +metadata: + name: test-etcd + namespace: test-namespace +spec: + computeOffering: + name: "m4-large" + users: + - name: "mySshUsername" + sshAuthorizedKeys: # The key below was manually generated and not used in any production systems + - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1BK73XhIzjX+meUr7pIYh6RHbvI3tmHeQIXY5lv7aztN1UoX+bhPo3dwo2sfSQn5kuxgQdnxIZ/CTzy0p0GkEYVv3gwspCeurjmu0XmrdmaSGcGxCEWT/65NtvYrQtUE5ELxJ+N/aeZNlK2B7IWANnw/82913asXH4VksV1NYNduP0o1/G4XcwLLSyVFB078q/oEnmvdNIoS61j4/o36HVtENJgYr0idcBvwJdvcGxGnPaqOhx477t+kfJAa5n5dSA5wilIaoXH5i1Tf/HsTCM52L+iNCARvQzJYZhzbWI1MDQwzILtIBEQCJsl2XSqIupleY8CxqQ6jCXt2mhae+wPc3YmbO5rFvr2/EvC57kh3yDs1Nsuj8KOvD78KeeujbR8n8pScm3WDp62HFQ8lEKNdeRNj6kB8WnuaJvPnyZfvzOhwG65/9w13IBl7B1sWxbFnq2rMpm5uHVK7mAmjL0Tt8zoDhcE1YJEnp9xte3/pvmKPkST5Q/9ZtR9P5sI+02jY0fvPkPyC03j2gsPixG7rpOCwpOdbny4dcj0TDeeXJX8er+oVfJuLYz0pNWJcT2raDdFfcqvYA0B0IyNYlj5nWX4RuEcyT3qocLReWPnZojetvAG/H8XwOh7fEVGqHAKOVSnPXCSQJPl6s0H12jPJBDJMTydtYPEszl4/CeQ== testemail@test.com" + template: + name: "centos7-k8s-118" + diskOffering: + name: "Small" + mountPath: "/data-small" + device: "/dev/vdb" + filesystem: "ext4" + label: "data_disk" + symlinks: + /var/lib/: /data-small/var/lib + affinityGroupIds: + - etcd-affinity + +--- diff --git a/pkg/providers/cloudstack/validator.go b/pkg/providers/cloudstack/validator.go index c4b59e37e9ee..511b158e6a9f 100644 --- a/pkg/providers/cloudstack/validator.go +++ b/pkg/providers/cloudstack/validator.go @@ -2,6 +2,7 @@ package cloudstack import ( "context" + "errors" "fmt" "net" "strconv" @@ -28,71 +29,90 @@ var restrictedUserCustomDetails = [...]string{ "keypairnames", "controlNodeLoginUser", } -var domainId string - func NewValidator(cmk ProviderCmkClient) *Validator { return &Validator{ cmk: cmk, } } +type localAvailabilityZone struct { + *anywherev1.CloudStackAvailabilityZone + ZoneId string + DomainId string +} + type ProviderCmkClient interface { - ValidateCloudStackConnection(ctx context.Context) error - ValidateServiceOfferingPresent(ctx context.Context, zoneId string, serviceOffering anywherev1.CloudStackResourceIdentifier) error - ValidateDiskOfferingPresent(ctx context.Context, zoneId string, diskOffering anywherev1.CloudStackResourceDiskOffering) error - ValidateTemplatePresent(ctx context.Context, domainId string, zoneId string, account string, template anywherev1.CloudStackResourceIdentifier) error - ValidateAffinityGroupsPresent(ctx context.Context, domainId string, account string, affinityGroupIds []string) error - ValidateZonesPresent(ctx context.Context, zones []anywherev1.CloudStackZone) ([]anywherev1.CloudStackResourceIdentifier, error) - ValidateNetworkPresent(ctx context.Context, domainId string, zoneRef anywherev1.CloudStackZone, zones []anywherev1.CloudStackResourceIdentifier, account string, multipleZone bool) error - ValidateDomainPresent(ctx context.Context, domain string) (anywherev1.CloudStackResourceIdentifier, error) - ValidateAccountPresent(ctx context.Context, account string, domainId string) error + GetManagementApiEndpoint(profile string) (string, error) + ValidateCloudStackConnection(ctx context.Context, profile string) error + ValidateServiceOfferingPresent(ctx context.Context, profile string, zoneId string, serviceOffering anywherev1.CloudStackResourceIdentifier) error + ValidateDiskOfferingPresent(ctx context.Context, profile string, zoneId string, diskOffering anywherev1.CloudStackResourceDiskOffering) error + ValidateTemplatePresent(ctx context.Context, profile string, domainId string, zoneId string, account string, template anywherev1.CloudStackResourceIdentifier) error + ValidateAffinityGroupsPresent(ctx context.Context, profile string, domainId string, account string, affinityGroupIds []string) error + ValidateZoneAndGetId(ctx context.Context, profile string, zone anywherev1.CloudStackZone) (string, error) + ValidateNetworkPresent(ctx context.Context, profile string, domainId string, network anywherev1.CloudStackResourceIdentifier, zoneId string, account string) error + ValidateDomainAndGetId(ctx context.Context, profile string, domain string) (string, error) + ValidateAccountPresent(ctx context.Context, profile string, account string, domainId string) error } -func (v *Validator) validateCloudStackAccess(ctx context.Context) error { - if err := v.cmk.ValidateCloudStackConnection(ctx); err != nil { - return fmt.Errorf("failed validating connection to cloudstack: %v", err) +func (v *Validator) validateCloudStackAccess(ctx context.Context, datacenterConfig *anywherev1.CloudStackDatacenterConfig) error { + refNamesToCheck := []string{} + if len(datacenterConfig.Spec.Domain) > 0 { + refNamesToCheck = append(refNamesToCheck, decoder.CloudStackGlobalAZ) + } + for _, az := range datacenterConfig.Spec.AvailabilityZones { + refNamesToCheck = append(refNamesToCheck, az.CredentialsRef) + } + + for _, refName := range refNamesToCheck { + if err := v.cmk.ValidateCloudStackConnection(ctx, refName); err != nil { + return fmt.Errorf("validating connection to cloudstack %s: %v", refName, err) + } } - logger.MarkPass("Connected to server") + logger.MarkPass(fmt.Sprintf("Connected to servers: %s", strings.Join(refNamesToCheck, ", "))) return nil } func (v *Validator) ValidateCloudStackDatacenterConfig(ctx context.Context, datacenterConfig *anywherev1.CloudStackDatacenterConfig) error { - if len(datacenterConfig.Spec.Domain) <= 0 { - return fmt.Errorf("CloudStackDatacenterConfig domain is not set or is empty") - } - if datacenterConfig.Spec.ManagementApiEndpoint == "" { - return fmt.Errorf("CloudStackDatacenterConfig managementApiEndpoint is not set or is empty") - } - _, err := getHostnameFromUrl(datacenterConfig.Spec.ManagementApiEndpoint) - if err != nil { - return fmt.Errorf("checking management api endpoint: %v", err) - } - execConfig, err := decoder.ParseCloudStackSecret() + localAvailabilityZones, err := generateLocalAvailabilityZones(ctx, datacenterConfig) if err != nil { - return fmt.Errorf("parsing cloudstack secret: %v", err) - } - if execConfig.ManagementUrl != datacenterConfig.Spec.ManagementApiEndpoint { - return fmt.Errorf("cloudstack secret management url (%s) differs from cluster spec management url (%s)", - execConfig.ManagementUrl, datacenterConfig.Spec.ManagementApiEndpoint) - } - - if err := v.validateDomainAndAccount(ctx, datacenterConfig); err != nil { return err } - zones, errZone := v.cmk.ValidateZonesPresent(ctx, datacenterConfig.Spec.Zones) - if errZone != nil { - return fmt.Errorf("checking zones %v", errZone) - } + for _, az := range localAvailabilityZones { + _, err := getHostnameFromUrl(az.ManagementApiEndpoint) + if err != nil { + return fmt.Errorf("checking management api endpoint: %v", err) + } - for _, zone := range datacenterConfig.Spec.Zones { - if len(zone.Network.Id) == 0 && len(zone.Network.Name) == 0 { - return fmt.Errorf("zone network is not set or is empty") + endpoint, err := v.cmk.GetManagementApiEndpoint(az.CredentialsRef) + if err != nil { + return err + } + if endpoint != az.ManagementApiEndpoint { + return fmt.Errorf("cloudstack secret management url (%s) differs from cluster spec management url (%s)", + endpoint, az.ManagementApiEndpoint) + } + + domainId, err := v.cmk.ValidateDomainAndGetId(ctx, az.CredentialsRef, az.Domain) + if err != nil { + return err } - err := v.cmk.ValidateNetworkPresent(ctx, domainId, zone, zones, datacenterConfig.Spec.Account, len(zones) > 1) + az.DomainId = domainId + + if err := v.cmk.ValidateAccountPresent(ctx, az.CredentialsRef, az.Account, az.DomainId); err != nil { + return err + } + + zoneId, err := v.cmk.ValidateZoneAndGetId(ctx, az.CredentialsRef, az.CloudStackAvailabilityZone.Zone) if err != nil { - return fmt.Errorf("checking network %v", err) + return err + } + if len(az.CloudStackAvailabilityZone.Zone.Network.Id) == 0 && len(az.CloudStackAvailabilityZone.Zone.Network.Name) == 0 { + return fmt.Errorf("zone network is not set or is empty") + } + if err := v.cmk.ValidateNetworkPresent(ctx, az.CredentialsRef, az.DomainId, az.CloudStackAvailabilityZone.Zone.Network, zoneId, az.Account); err != nil { + return err } } @@ -100,32 +120,43 @@ func (v *Validator) ValidateCloudStackDatacenterConfig(ctx context.Context, data return nil } -func (v *Validator) validateDomainAndAccount(ctx context.Context, datacenterConfig *anywherev1.CloudStackDatacenterConfig) error { - if (datacenterConfig.Spec.Domain != "" && datacenterConfig.Spec.Account == "") || - (datacenterConfig.Spec.Domain == "" && datacenterConfig.Spec.Account != "") { - return fmt.Errorf("both domain and account must be specified or none of them must be specified") +func generateLocalAvailabilityZones(ctx context.Context, datacenterConfig *anywherev1.CloudStackDatacenterConfig) ([]localAvailabilityZone, error) { + localAvailabilityZones := []localAvailabilityZone{} + + if datacenterConfig == nil { + return nil, errors.New("CloudStack Datacenter Config is null") } - if datacenterConfig.Spec.Domain != "" && datacenterConfig.Spec.Account != "" { - domain, errDomain := v.cmk.ValidateDomainPresent(ctx, datacenterConfig.Spec.Domain) - if errDomain != nil { - return fmt.Errorf("checking domain: %v", errDomain) + if len(datacenterConfig.Spec.Domain) > 0 { + for index, zone := range datacenterConfig.Spec.Zones { + availabilityZone := localAvailabilityZone{ + CloudStackAvailabilityZone: &anywherev1.CloudStackAvailabilityZone{ + Name: fmt.Sprintf("availability-zone-%d", index), + CredentialsRef: decoder.CloudStackGlobalAZ, + Domain: datacenterConfig.Spec.Domain, + Account: datacenterConfig.Spec.Account, + ManagementApiEndpoint: datacenterConfig.Spec.ManagementApiEndpoint, + Zone: zone, + }, + } + localAvailabilityZones = append(localAvailabilityZones, availabilityZone) } - - errAccount := v.cmk.ValidateAccountPresent(ctx, datacenterConfig.Spec.Account, domain.Id) - if errAccount != nil { - return fmt.Errorf("checking account: %v", errAccount) + } + for _, az := range datacenterConfig.Spec.AvailabilityZones { + availabilityZone := localAvailabilityZone{ + CloudStackAvailabilityZone: &az, } + localAvailabilityZones = append(localAvailabilityZones, availabilityZone) + } - domainId = domain.Id + if len(localAvailabilityZones) <= 0 { + return nil, fmt.Errorf("CloudStackDatacenterConfig domain or availabilityZones is not set or is empty") } - return nil + return localAvailabilityZones, nil } // TODO: dry out machine configs validations func (v *Validator) ValidateClusterMachineConfigs(ctx context.Context, cloudStackClusterSpec *Spec) error { - var etcdMachineConfig *anywherev1.CloudStackMachineConfig - if len(cloudStackClusterSpec.Cluster.Spec.ControlPlaneConfiguration.Endpoint.Host) <= 0 { return fmt.Errorf("cluster controlPlaneConfiguration.Endpoint.Host is not set or is empty") } @@ -142,7 +173,7 @@ func (v *Validator) ValidateClusterMachineConfigs(ctx context.Context, cloudStac if cloudStackClusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef == nil { return fmt.Errorf("must specify machineGroupRef for etcd machines") } - etcdMachineConfig = cloudStackClusterSpec.etcdMachineConfig() + etcdMachineConfig := cloudStackClusterSpec.etcdMachineConfig() if etcdMachineConfig == nil { return fmt.Errorf("cannot find CloudStackMachineConfig %v for etcd machines", cloudStackClusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name) } @@ -234,29 +265,33 @@ func (v *Validator) validateMachineConfig(ctx context.Context, datacenterConfig return fmt.Errorf("restricted key %s found in custom user details", restrictedKey) } } - zones, err := v.cmk.ValidateZonesPresent(ctx, datacenterConfig.Spec.Zones) + + localAvailabilityZones, err := generateLocalAvailabilityZones(ctx, datacenterConfig) if err != nil { - return fmt.Errorf("checking zones %v", err) + return err } - account := datacenterConfig.Spec.Account - for _, zone := range zones { - if err = v.cmk.ValidateTemplatePresent(ctx, domainId, zone.Id, account, machineConfig.Spec.Template); err != nil { + for _, az := range localAvailabilityZones { + zoneId, err := v.cmk.ValidateZoneAndGetId(ctx, az.CredentialsRef, az.CloudStackAvailabilityZone.Zone) + if err != nil { + return err + } + + if err := v.cmk.ValidateTemplatePresent(ctx, az.CredentialsRef, az.DomainId, zoneId, az.Account, machineConfig.Spec.Template); err != nil { return fmt.Errorf("validating template: %v", err) } - if err = v.cmk.ValidateServiceOfferingPresent(ctx, zone.Id, machineConfig.Spec.ComputeOffering); err != nil { + if err := v.cmk.ValidateServiceOfferingPresent(ctx, az.CredentialsRef, zoneId, machineConfig.Spec.ComputeOffering); err != nil { return fmt.Errorf("validating service offering: %v", err) } if len(machineConfig.Spec.DiskOffering.Id) > 0 || len(machineConfig.Spec.DiskOffering.Name) > 0 { - if err = v.cmk.ValidateDiskOfferingPresent(ctx, zone.Id, machineConfig.Spec.DiskOffering); err != nil { + if err := v.cmk.ValidateDiskOfferingPresent(ctx, az.CredentialsRef, zoneId, machineConfig.Spec.DiskOffering); err != nil { return fmt.Errorf("validating disk offering: %v", err) } } - } - - if len(machineConfig.Spec.AffinityGroupIds) > 0 { - if err = v.cmk.ValidateAffinityGroupsPresent(ctx, domainId, account, machineConfig.Spec.AffinityGroupIds); err != nil { - return fmt.Errorf("validating affinity group ids: %v", err) + if len(machineConfig.Spec.AffinityGroupIds) > 0 { + if err := v.cmk.ValidateAffinityGroupsPresent(ctx, az.CredentialsRef, az.DomainId, az.Account, machineConfig.Spec.AffinityGroupIds); err != nil { + return fmt.Errorf("validating affinity group ids: %v", err) + } } } diff --git a/pkg/providers/cloudstack/validator_test.go b/pkg/providers/cloudstack/validator_test.go index 1c63ea892532..1f393028eb1c 100644 --- a/pkg/providers/cloudstack/validator_test.go +++ b/pkg/providers/cloudstack/validator_test.go @@ -16,8 +16,9 @@ import ( ) const ( - testClusterConfigMainFilename = "cluster_main.yaml" - testDataDir = "testdata" + testClusterConfigMainFilename = "cluster_main.yaml" + testClusterConfigMainWithAZsFilename = "cluster_main_with_availability_zones.yaml" + testDataDir = "testdata" ) var testTemplate = v1alpha1.CloudStackResourceIdentifier{ @@ -44,33 +45,72 @@ func TestValidateCloudStackDatacenterConfig(t *testing.T) { cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t)) validator := NewValidator(cmk) - cloudstackDatacenter, err := v1alpha1.GetCloudStackDatacenterConfig(path.Join(testDataDir, testClusterConfigMainFilename)) + datacenterConfig, err := v1alpha1.GetCloudStackDatacenterConfig(path.Join(testDataDir, testClusterConfigMainFilename)) if err != nil { t.Fatalf("unable to get datacenter config from file") } - cmk.EXPECT().ValidateZonesPresent(ctx, cloudstackDatacenter.Spec.Zones).Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateDomainPresent(ctx, cloudstackDatacenter.Spec.Domain).Return(v1alpha1.CloudStackResourceIdentifier{Id: "5300cdac-74d5-11ec-8696-c81f66d3e965", Name: cloudstackDatacenter.Spec.Domain}, nil) - cmk.EXPECT().ValidateAccountPresent(ctx, gomock.Any(), gomock.Any()).Return(nil) - cmk.EXPECT().ValidateNetworkPresent(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), false).Return(nil) - err = validator.ValidateCloudStackDatacenterConfig(ctx, cloudstackDatacenter) + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + + err = validator.ValidateCloudStackDatacenterConfig(ctx, datacenterConfig) if err != nil { t.Fatalf("failed to validate CloudStackDataCenterConfig: %v", err) } } -func TestValidateCloudStackConnection(t *testing.T) { +func TestValidateCloudStackDatacenterConfigWithAZ(t *testing.T) { ctx := context.Background() + setupContext() cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t)) validator := NewValidator(cmk) - cmk.EXPECT().ValidateCloudStackConnection(ctx).Return(nil) - err := validator.validateCloudStackAccess(ctx) + datacenterConfig, err := v1alpha1.GetCloudStackDatacenterConfig(path.Join(testDataDir, testClusterConfigMainWithAZsFilename)) + if err != nil { + t.Fatalf("unable to get datacenter config from file") + } + + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + setupMockForAvailabilityZonesValidation(cmk, ctx, datacenterConfig.Spec.AvailabilityZones) + + for _, az := range datacenterConfig.Spec.AvailabilityZones { + cmk.EXPECT().GetManagementApiEndpoint(gomock.Any()).Times(1).Return(az.ManagementApiEndpoint, nil) + } + + err = validator.ValidateCloudStackDatacenterConfig(ctx, datacenterConfig) if err != nil { t.Fatalf("failed to validate CloudStackDataCenterConfig: %v", err) } } +func TestValidateCloudStackConnection(t *testing.T) { + ctx := context.Background() + cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t)) + validator := NewValidator(cmk) + datacenterConfig, err := v1alpha1.GetCloudStackDatacenterConfig(path.Join(testDataDir, testClusterConfigMainFilename)) + if err != nil { + t.Fatalf("unable to get datacenter config from file") + } + + cmk.EXPECT().ValidateCloudStackConnection(ctx, "Global").Return(nil) + if err := validator.validateCloudStackAccess(ctx, datacenterConfig); err != nil { + t.Fatalf("failed to validate CloudStackDataCenterConfig: %v", err) + } +} + +func TestValidateCloudStackConnectionFailure(t *testing.T) { + ctx := context.Background() + cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t)) + validator := NewValidator(cmk) + datacenterConfig, err := v1alpha1.GetCloudStackDatacenterConfig(path.Join(testDataDir, testClusterConfigMainFilename)) + if err != nil { + t.Fatalf("unable to get datacenter config from file") + } + + cmk.EXPECT().ValidateCloudStackConnection(ctx, "Global").Return(errors.New("exception")) + err = validator.validateCloudStackAccess(ctx, datacenterConfig) + thenErrorExpected(t, "validating connection to cloudstack Global: exception", err) +} + func TestValidateMachineConfigsNoControlPlaneEndpointIP(t *testing.T) { ctx := context.Background() cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t)) @@ -86,6 +126,8 @@ func TestValidateMachineConfigsNoControlPlaneEndpointIP(t *testing.T) { } clusterSpec.Cluster.Spec.ControlPlaneConfiguration.Endpoint.Host = "" + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) thenErrorExpected(t, "cluster controlPlaneConfiguration.Endpoint.Host is not set or is empty", err) @@ -106,12 +148,10 @@ func TestValidateDatacenterConfigsNoNetwork(t *testing.T) { machineConfigsLookup: nil, } validator := NewValidator(cmk) - cmk.EXPECT().ValidateZonesPresent(ctx, gomock.Any()).Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateDomainPresent(ctx, gomock.Any()).Return(v1alpha1.CloudStackResourceIdentifier{Id: "5300cdac-74d5-11ec-8696-c81f66d3e965", Name: "ROOT"}, nil) - cmk.EXPECT().ValidateAccountPresent(ctx, gomock.Any(), gomock.Any()).Return(nil) - datacenterConfig.Spec.Zones[0].Network.Id = "" datacenterConfig.Spec.Zones[0].Network.Name = "" + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateCloudStackDatacenterConfig(ctx, cloudStackClusterSpec.datacenterConfig) thenErrorExpected(t, "zone network is not set or is empty", err) @@ -131,6 +171,7 @@ func TestValidateDatacenterBadManagementEndpoint(t *testing.T) { machineConfigsLookup: nil, } validator := NewValidator(cmk) + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) datacenterConfig.Spec.ManagementApiEndpoint = ":1234.5234" err = validator.ValidateCloudStackDatacenterConfig(ctx, cloudStackClusterSpec.datacenterConfig) @@ -153,6 +194,7 @@ func TestValidateDatacenterInconsistentManagementEndpoints(t *testing.T) { machineConfigsLookup: nil, } validator := NewValidator(cmk) + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) datacenterConfig.Spec.ManagementApiEndpoint = "abcefg.com" err = validator.ValidateCloudStackDatacenterConfig(ctx, cloudStackClusterSpec.datacenterConfig) @@ -185,12 +227,14 @@ func TestSetupAndValidateDiskOfferingEmpty(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.DiskOffering = v1alpha1.CloudStackResourceDiskOffering{} - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).Times(3).Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(0) - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0) + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + + _ = validator.ValidateCloudStackDatacenterConfig(ctx, datacenterConfig) err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) if err != nil { t.Fatalf("validator.ValidateClusterMachineConfigs() err = %v, want err = nil", err) @@ -230,12 +274,14 @@ func TestSetupAndValidateValidDiskOffering(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.DiskOffering = v1alpha1.CloudStackResourceDiskOffering{} - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).Times(3).Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(1) - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + _ = validator.ValidateCloudStackDatacenterConfig(ctx, datacenterConfig) err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) if err != nil { t.Fatalf("validator.ValidateClusterMachineConfigs() err = %v, want err = nil", err) @@ -275,12 +321,14 @@ func TestSetupAndValidateInvalidDiskOfferingNotPresent(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.DiskOffering = v1alpha1.CloudStackResourceDiskOffering{} - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).AnyTimes().Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(errors.New("match me")) - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(errors.New("match me")) + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + + _ = validator.ValidateCloudStackDatacenterConfig(ctx, datacenterConfig) err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) wantErrMsg := "validating disk offering: match me" assert.Contains(t, err.Error(), wantErrMsg, "expected error containing %q, got %v", wantErrMsg, err) @@ -319,10 +367,11 @@ func TestSetupAndValidateInValidDiskOfferingBadMountPath(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.DiskOffering = v1alpha1.CloudStackResourceDiskOffering{} - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).AnyTimes().Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) wantErrMsg := "machine config test validation failed: mountPath: / invalid, must be non-empty and starts with /" @@ -362,10 +411,11 @@ func TestSetupAndValidateInValidDiskOfferingEmptyDevice(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.DiskOffering = v1alpha1.CloudStackResourceDiskOffering{} - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).AnyTimes().Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) wantErrMsg := "machine config test validation failed: device: invalid, empty device" @@ -405,10 +455,11 @@ func TestSetupAndValidateInValidDiskOfferingEmptyFilesystem(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.DiskOffering = v1alpha1.CloudStackResourceDiskOffering{} - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).AnyTimes().Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) wantErrMsg := "machine config test validation failed: filesystem: invalid, empty filesystem" @@ -448,10 +499,11 @@ func TestSetupAndValidateInValidDiskOfferingEmptyLabel(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.DiskOffering = v1alpha1.CloudStackResourceDiskOffering{} - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).AnyTimes().Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) wantErrMsg := "machine config test validation failed: label: invalid, empty label" @@ -483,12 +535,14 @@ func TestSetupAndValidateUsersNil(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.Users = nil - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).Times(3).Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + _ = validator.ValidateCloudStackDatacenterConfig(ctx, datacenterConfig) err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) if err != nil { t.Fatalf("validator.ValidateClusterMachineConfigs() err = %v, want err = nil", err) @@ -520,6 +574,8 @@ func TestSetupAndValidateRestrictedUserDetails(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.UserCustomDetails = map[string]string{"keyboard": "test"} + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) if err == nil { t.Fatalf("expected error like 'validation failed: restricted key keyboard found in custom user details' but no error was thrown") @@ -551,17 +607,37 @@ func TestSetupAndValidateSshAuthorizedKeysNil(t *testing.T) { etcdMachineConfigName := clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[etcdMachineConfigName].Spec.Users[0].SshAuthorizedKeys = nil - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).Times(3).Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + + cmk.EXPECT().ValidateTemplatePresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateServiceOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateAffinityGroupsPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + + _ = validator.ValidateCloudStackDatacenterConfig(ctx, datacenterConfig) err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) if err != nil { t.Fatalf("validator.ValidateClusterMachineConfigs() err = %v, want err = nil", err) } } +func setupMockForDatacenterConfigValidation(cmk *mocks.MockProviderCmkClient, ctx context.Context, datacenterConfig *v1alpha1.CloudStackDatacenterConfig) { + cmk.EXPECT().ValidateZoneAndGetId(ctx, gomock.Any(), datacenterConfig.Spec.Zones[0]).AnyTimes().Return("4e3b338d-87a6-4189-b931-a1747edeea8f", nil) + cmk.EXPECT().ValidateDomainAndGetId(ctx, gomock.Any(), datacenterConfig.Spec.Domain).AnyTimes().Return("5300cdac-74d5-11ec-8696-c81f66d3e965", nil) + cmk.EXPECT().ValidateAccountPresent(ctx, gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) + cmk.EXPECT().ValidateNetworkPresent(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) + cmk.EXPECT().GetManagementApiEndpoint(gomock.Any()).AnyTimes().MaxTimes(1).Return("http://127.16.0.1:8080/client/api", nil) +} + +func setupMockForAvailabilityZonesValidation(cmk *mocks.MockProviderCmkClient, ctx context.Context, azs []v1alpha1.CloudStackAvailabilityZone) { + for _, az := range azs { + cmk.EXPECT().ValidateZoneAndGetId(ctx, gomock.Any(), az.Zone).AnyTimes().Return("4e3b338d-87a6-4189-b931-a1747edeea82", nil) + cmk.EXPECT().ValidateDomainAndGetId(ctx, gomock.Any(), az.Domain).AnyTimes().Return("5300cdac-74d5-11ec-8696-c81f66d3e962", nil) + cmk.EXPECT().ValidateAccountPresent(ctx, gomock.Any(), az.Account, gomock.Any()).AnyTimes().Return(nil) + cmk.EXPECT().ValidateNetworkPresent(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) + } +} + func TestSetupAndValidateCreateClusterCPMachineGroupRefNil(t *testing.T) { ctx := context.Background() cmk := mocks.NewMockProviderCmkClient(gomock.NewController(t)) @@ -582,6 +658,8 @@ func TestSetupAndValidateCreateClusterCPMachineGroupRefNil(t *testing.T) { } clusterSpec.Cluster.Spec.ControlPlaneConfiguration.MachineGroupRef = nil + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) thenErrorExpected(t, "must specify machineGroupRef for control plane", err) } @@ -606,6 +684,8 @@ func TestSetupAndValidateCreateClusterWorkerMachineGroupRefNil(t *testing.T) { } clusterSpec.Cluster.Spec.WorkerNodeGroupConfigurations[0].MachineGroupRef = nil + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) thenErrorExpected(t, "must specify machineGroupRef for worker nodes", err) } @@ -630,6 +710,8 @@ func TestSetupAndValidateCreateClusterEtcdMachineGroupRefNil(t *testing.T) { } clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef = nil + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) thenErrorExpected(t, "must specify machineGroupRef for etcd machines", err) } @@ -654,6 +736,8 @@ func TestSetupAndValidateCreateClusterCPMachineGroupRefNonexistent(t *testing.T) } clusterSpec.Cluster.Spec.ControlPlaneConfiguration.MachineGroupRef.Name = "nonexistent" + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) thenErrorExpected(t, "cannot find CloudStackMachineConfig nonexistent for control plane", err) } @@ -678,6 +762,8 @@ func TestSetupAndValidateCreateClusterWorkerMachineGroupRefNonexistent(t *testin } clusterSpec.Cluster.Spec.WorkerNodeGroupConfigurations[0].MachineGroupRef.Name = "nonexistent" + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) thenErrorExpected(t, "cannot find CloudStackMachineConfig nonexistent for worker nodes", err) } @@ -702,6 +788,8 @@ func TestSetupAndValidateCreateClusterEtcdMachineGroupRefNonexistent(t *testing. } clusterSpec.Cluster.Spec.ExternalEtcdConfiguration.MachineGroupRef.Name = "nonexistent" + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) thenErrorExpected(t, "cannot find CloudStackMachineConfig nonexistent for etcd machines", err) } @@ -727,6 +815,8 @@ func TestSetupAndValidateCreateClusterTemplateDifferent(t *testing.T) { controlPlaneMachineConfigName := clusterSpec.Cluster.Spec.ControlPlaneConfiguration.MachineGroupRef.Name cloudStackClusterSpec.machineConfigsLookup[controlPlaneMachineConfigName].Spec.Template = v1alpha1.CloudStackResourceIdentifier{Name: "different"} + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) thenErrorExpected(t, "control plane and etcd machines must have the same template specified", err) } @@ -749,12 +839,15 @@ func TestValidateMachineConfigsHappyCase(t *testing.T) { machineConfigsLookup: machineConfigs, } validator := NewValidator(cmk) - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).Times(3).Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(ctx, gomock.Any(), + setupMockForDatacenterConfigValidation(cmk, ctx, datacenterConfig) + + cmk.EXPECT().ValidateTemplatePresent(ctx, gomock.Any(), gomock.Any(), gomock.Any(), datacenterConfig.Spec.Account, testTemplate).Times(3) - cmk.EXPECT().ValidateServiceOfferingPresent(ctx, gomock.Any(), testOffering).Times(3) - cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Times(3) - cmk.EXPECT().ValidateAffinityGroupsPresent(ctx, gomock.Any(), datacenterConfig.Spec.Account, gomock.Any()).Times(3) + cmk.EXPECT().ValidateServiceOfferingPresent(ctx, gomock.Any(), gomock.Any(), testOffering).Times(3) + cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateAffinityGroupsPresent(ctx, gomock.Any(), gomock.Any(), datacenterConfig.Spec.Account, gomock.Any()).Times(3) + + _ = validator.ValidateCloudStackDatacenterConfig(ctx, datacenterConfig) err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) assert.Nil(t, err) assert.Equal(t, "1.2.3.4:6443", clusterSpec.Cluster.Spec.ControlPlaneConfiguration.Endpoint.Host) @@ -773,14 +866,14 @@ func TestValidateCloudStackMachineConfig(t *testing.T) { } validator := NewValidator(cmk) + cmk.EXPECT().ValidateZoneAndGetId(ctx, gomock.Any(), gomock.Any()).Times(3).Return("4e3b338d-87a6-4189-b931-a1747edeea82", nil) + cmk.EXPECT().ValidateTemplatePresent(ctx, gomock.Any(), gomock.Any(), + gomock.Any(), datacenterConfig.Spec.Account, testTemplate).Times(3) + cmk.EXPECT().ValidateServiceOfferingPresent(ctx, gomock.Any(), gomock.Any(), testOffering).Times(3) + cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) + cmk.EXPECT().ValidateAffinityGroupsPresent(ctx, gomock.Any(), gomock.Any(), datacenterConfig.Spec.Account, gomock.Any()).Times(3) + for _, machineConfig := range machineConfigs { - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(ctx, gomock.Any(), gomock.Any(), "admin", machineConfig.Spec.Template).Return(nil) - cmk.EXPECT().ValidateServiceOfferingPresent(ctx, gomock.Any(), machineConfig.Spec.ComputeOffering).Return(nil) - cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) - if len(machineConfig.Spec.AffinityGroupIds) > 0 { - cmk.EXPECT().ValidateAffinityGroupsPresent(ctx, gomock.Any(), "admin", machineConfig.Spec.AffinityGroupIds).Return(nil) - } err := validator.validateMachineConfig(ctx, datacenterConfig, machineConfig) if err != nil { t.Fatalf("failed to validate CloudStackMachineConfig: %v", err) @@ -815,13 +908,16 @@ func TestValidateMachineConfigsWithAffinity(t *testing.T) { } validator := NewValidator(cmk) - cmk.EXPECT().ValidateDomainPresent(gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateZonesPresent(gomock.Any(), gomock.Any()).AnyTimes().Return([]v1alpha1.CloudStackResourceIdentifier{{Name: "zone1", Id: "4e3b338d-87a6-4189-b931-a1747edeea8f"}}, nil) - cmk.EXPECT().ValidateTemplatePresent(ctx, gomock.Any(), - gomock.Any(), datacenterConfig.Spec.Account, testTemplate).AnyTimes() - cmk.EXPECT().ValidateServiceOfferingPresent(ctx, gomock.Any(), testOffering).AnyTimes() - cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() - cmk.EXPECT().ValidateAffinityGroupsPresent(ctx, gomock.Any(), datacenterConfig.Spec.Account, gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateZoneAndGetId(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return("4e3b338d-87a6-4189-b931-a1747edeea8f", nil) + cmk.EXPECT().ValidateDomainAndGetId(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateAccountPresent(ctx, gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) + cmk.EXPECT().ValidateNetworkPresent(ctx, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil) + cmk.EXPECT().GetManagementApiEndpoint(gomock.Any()).AnyTimes().Return("http://127.16.0.1:8080/client/api", nil) + + cmk.EXPECT().ValidateTemplatePresent(ctx, gomock.Any(), gomock.Any(), gomock.Any(), datacenterConfig.Spec.Account, testTemplate).AnyTimes() + cmk.EXPECT().ValidateServiceOfferingPresent(ctx, gomock.Any(), gomock.Any(), testOffering).AnyTimes() + cmk.EXPECT().ValidateDiskOfferingPresent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() + cmk.EXPECT().ValidateAffinityGroupsPresent(ctx, gomock.Any(), gomock.Any(), datacenterConfig.Spec.Account, gomock.Any()).AnyTimes() // Valid affinity types err = validator.ValidateClusterMachineConfigs(ctx, cloudStackClusterSpec) diff --git a/release/go.sum b/release/go.sum index c56a4340b555..6a7863ca1ea0 100644 --- a/release/go.sum +++ b/release/go.sum @@ -462,7 +462,6 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= diff --git a/release/pkg/generate_spec.go b/release/pkg/generate_spec.go index 5712a728ea0f..9d9815b1295a 100644 --- a/release/pkg/generate_spec.go +++ b/release/pkg/generate_spec.go @@ -301,7 +301,6 @@ func (r *ReleaseConfig) GenerateBundleArtifactsTable() (map[string][]Artifact, e return nil, errors.Wrapf(err, "Error converting branch minor version to integer") } } - // TODO: change logic when we update major version to 1 if r.BuildRepoBranchName == "main" || branchMinorVersion > 9 { eksAArtifactsFuncs["cluster-api-provider-tinkerbell"] = r.GetCaptAssets diff --git a/release/pkg/test/testdata/main-bundle-release.yaml b/release/pkg/test/testdata/main-bundle-release.yaml index 14e78d8da0ad..186589b5d923 100644 --- a/release/pkg/test/testdata/main-bundle-release.yaml +++ b/release/pkg/test/testdata/main-bundle-release.yaml @@ -71,7 +71,7 @@ spec: imageDigest: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef name: bottlerocket-bootstrap os: linux - uri: public.ecr.aws/release-container-registry/bottlerocket-bootstrap:v1-20-17-eks-a-v0.0.0-dev-build.1 + uri: public.ecr.aws/release-container-registry/bottlerocket-bootstrap:v1-20-18-eks-a-v0.0.0-dev-build.1 certManager: acmesolver: arch: @@ -242,23 +242,23 @@ spec: imageDigest: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef name: kind-node os: linux - uri: public.ecr.aws/release-container-registry/kubernetes-sigs/kind/node:v1.20.15-eks-d-1-20-17-eks-a-v0.0.0-dev-build.1 + uri: public.ecr.aws/release-container-registry/kubernetes-sigs/kind/node:v1.20.15-eks-d-1-20-18-eks-a-v0.0.0-dev-build.1 kubeVersion: v1.20.15 - manifestUrl: https://distro.eks.amazonaws.com/kubernetes-1-20/kubernetes-1-20-eks-17.yaml - name: kubernetes-1-20-eks-17 + manifestUrl: https://distro.eks.amazonaws.com/kubernetes-1-20/kubernetes-1-20-eks-18.yaml + name: kubernetes-1-20-eks-18 ova: bottlerocket: arch: - amd64 crictl: {} - description: Bottlerocket Ova image for EKS-D 1-20-17 release + description: Bottlerocket Ova image for EKS-D 1-20-18 release etcdadm: {} - name: bottlerocket-v1.20.15-eks-d-1-20-17-eks-a-v0.0.0-dev-build.0-amd64.ova + name: bottlerocket-v1.20.15-eks-d-1-20-18-eks-a-v0.0.0-dev-build.0-amd64.ova os: linux osName: bottlerocket sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-20/1-20-17/bottlerocket-v1.20.15-eks-d-1-20-17-eks-a-v0.0.0-dev-build.0-amd64.ova + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-20/1-20-18/bottlerocket-v1.20.15-eks-d-1-20-18-eks-a-v0.0.0-dev-build.0-amd64.ova ubuntu: arch: - amd64 @@ -271,7 +271,7 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/cri-tools/v1.20.0/cri-tools-v0.0.0-dev+build.0-linux-amd64.tar.gz - description: Ubuntu Ova image for EKS-D 1-20-17 release + description: Ubuntu Ova image for EKS-D 1-20-18 release etcdadm: arch: - amd64 @@ -281,12 +281,12 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/etcdadm/5b496a72af3d80d64a16a650c85ce9a5882bc014/etcdadm-v0.0.0-dev+build.0-linux-amd64.tar.gz - name: ubuntu-v1.20.15-eks-d-1-20-17-eks-a-v0.0.0-dev-build.0-amd64.ova + name: ubuntu-v1.20.15-eks-d-1-20-18-eks-a-v0.0.0-dev-build.0-amd64.ova os: linux osName: ubuntu sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-20/1-20-17/ubuntu-v1.20.15-eks-d-1-20-17-eks-a-v0.0.0-dev-build.0-amd64.ova + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-20/1-20-18/ubuntu-v1.20.15-eks-d-1-20-18-eks-a-v0.0.0-dev-build.0-amd64.ova raw: bottlerocket: crictl: {} @@ -303,7 +303,7 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/cri-tools/v1.20.0/cri-tools-v0.0.0-dev+build.0-linux-amd64.tar.gz - description: Ubuntu Raw image for EKS-D 1-20-17 release + description: Ubuntu Raw image for EKS-D 1-20-18 release etcdadm: arch: - amd64 @@ -313,12 +313,12 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/etcdadm/5b496a72af3d80d64a16a650c85ce9a5882bc014/etcdadm-v0.0.0-dev+build.0-linux-amd64.tar.gz - name: ubuntu-v1.20.15-eks-d-1-20-17-eks-a-v0.0.0-dev-build.0-amd64.gz + name: ubuntu-v1.20.15-eks-d-1-20-18-eks-a-v0.0.0-dev-build.0-amd64.gz os: linux osName: ubuntu sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-20/1-20-17/ubuntu-v1.20.15-eks-d-1-20-17-eks-a-v0.0.0-dev-build.0-amd64.gz + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-20/1-20-18/ubuntu-v1.20.15-eks-d-1-20-18-eks-a-v0.0.0-dev-build.0-amd64.gz eksa: cliTools: arch: @@ -781,7 +781,7 @@ spec: imageDigest: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef name: bottlerocket-bootstrap os: linux - uri: public.ecr.aws/release-container-registry/bottlerocket-bootstrap:v1-21-15-eks-a-v0.0.0-dev-build.1 + uri: public.ecr.aws/release-container-registry/bottlerocket-bootstrap:v1-21-16-eks-a-v0.0.0-dev-build.1 certManager: acmesolver: arch: @@ -952,23 +952,23 @@ spec: imageDigest: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef name: kind-node os: linux - uri: public.ecr.aws/release-container-registry/kubernetes-sigs/kind/node:v1.21.13-eks-d-1-21-15-eks-a-v0.0.0-dev-build.1 + uri: public.ecr.aws/release-container-registry/kubernetes-sigs/kind/node:v1.21.13-eks-d-1-21-16-eks-a-v0.0.0-dev-build.1 kubeVersion: v1.21.13 - manifestUrl: https://distro.eks.amazonaws.com/kubernetes-1-21/kubernetes-1-21-eks-15.yaml - name: kubernetes-1-21-eks-15 + manifestUrl: https://distro.eks.amazonaws.com/kubernetes-1-21/kubernetes-1-21-eks-16.yaml + name: kubernetes-1-21-eks-16 ova: bottlerocket: arch: - amd64 crictl: {} - description: Bottlerocket Ova image for EKS-D 1-21-15 release + description: Bottlerocket Ova image for EKS-D 1-21-16 release etcdadm: {} - name: bottlerocket-v1.21.13-eks-d-1-21-15-eks-a-v0.0.0-dev-build.0-amd64.ova + name: bottlerocket-v1.21.13-eks-d-1-21-16-eks-a-v0.0.0-dev-build.0-amd64.ova os: linux osName: bottlerocket sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-21/1-21-15/bottlerocket-v1.21.13-eks-d-1-21-15-eks-a-v0.0.0-dev-build.0-amd64.ova + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-21/1-21-16/bottlerocket-v1.21.13-eks-d-1-21-16-eks-a-v0.0.0-dev-build.0-amd64.ova ubuntu: arch: - amd64 @@ -981,7 +981,7 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/cri-tools/v1.20.0/cri-tools-v0.0.0-dev+build.0-linux-amd64.tar.gz - description: Ubuntu Ova image for EKS-D 1-21-15 release + description: Ubuntu Ova image for EKS-D 1-21-16 release etcdadm: arch: - amd64 @@ -991,25 +991,25 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/etcdadm/5b496a72af3d80d64a16a650c85ce9a5882bc014/etcdadm-v0.0.0-dev+build.0-linux-amd64.tar.gz - name: ubuntu-v1.21.13-eks-d-1-21-15-eks-a-v0.0.0-dev-build.0-amd64.ova + name: ubuntu-v1.21.13-eks-d-1-21-16-eks-a-v0.0.0-dev-build.0-amd64.ova os: linux osName: ubuntu sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-21/1-21-15/ubuntu-v1.21.13-eks-d-1-21-15-eks-a-v0.0.0-dev-build.0-amd64.ova + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-21/1-21-16/ubuntu-v1.21.13-eks-d-1-21-16-eks-a-v0.0.0-dev-build.0-amd64.ova raw: bottlerocket: arch: - amd64 crictl: {} - description: Bottlerocket Raw image for EKS-D 1-21-15 release + description: Bottlerocket Raw image for EKS-D 1-21-16 release etcdadm: {} - name: bottlerocket-v1.21.13-eks-d-1-21-15-eks-a-v0.0.0-dev-build.0-amd64.img.gz + name: bottlerocket-v1.21.13-eks-d-1-21-16-eks-a-v0.0.0-dev-build.0-amd64.img.gz os: linux osName: bottlerocket sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-21/1-21-15/bottlerocket-v1.21.13-eks-d-1-21-15-eks-a-v0.0.0-dev-build.0-amd64.img.gz + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-21/1-21-16/bottlerocket-v1.21.13-eks-d-1-21-16-eks-a-v0.0.0-dev-build.0-amd64.img.gz ubuntu: arch: - amd64 @@ -1022,7 +1022,7 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/cri-tools/v1.20.0/cri-tools-v0.0.0-dev+build.0-linux-amd64.tar.gz - description: Ubuntu Raw image for EKS-D 1-21-15 release + description: Ubuntu Raw image for EKS-D 1-21-16 release etcdadm: arch: - amd64 @@ -1032,12 +1032,12 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/etcdadm/5b496a72af3d80d64a16a650c85ce9a5882bc014/etcdadm-v0.0.0-dev+build.0-linux-amd64.tar.gz - name: ubuntu-v1.21.13-eks-d-1-21-15-eks-a-v0.0.0-dev-build.0-amd64.gz + name: ubuntu-v1.21.13-eks-d-1-21-16-eks-a-v0.0.0-dev-build.0-amd64.gz os: linux osName: ubuntu sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-21/1-21-15/ubuntu-v1.21.13-eks-d-1-21-15-eks-a-v0.0.0-dev-build.0-amd64.gz + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-21/1-21-16/ubuntu-v1.21.13-eks-d-1-21-16-eks-a-v0.0.0-dev-build.0-amd64.gz eksa: cliTools: arch: @@ -1500,7 +1500,7 @@ spec: imageDigest: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef name: bottlerocket-bootstrap os: linux - uri: public.ecr.aws/release-container-registry/bottlerocket-bootstrap:v1-22-8-eks-a-v0.0.0-dev-build.1 + uri: public.ecr.aws/release-container-registry/bottlerocket-bootstrap:v1-22-9-eks-a-v0.0.0-dev-build.1 certManager: acmesolver: arch: @@ -1671,23 +1671,23 @@ spec: imageDigest: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef name: kind-node os: linux - uri: public.ecr.aws/release-container-registry/kubernetes-sigs/kind/node:v1.22.10-eks-d-1-22-8-eks-a-v0.0.0-dev-build.1 + uri: public.ecr.aws/release-container-registry/kubernetes-sigs/kind/node:v1.22.10-eks-d-1-22-9-eks-a-v0.0.0-dev-build.1 kubeVersion: v1.22.10 - manifestUrl: https://distro.eks.amazonaws.com/kubernetes-1-22/kubernetes-1-22-eks-8.yaml - name: kubernetes-1-22-eks-8 + manifestUrl: https://distro.eks.amazonaws.com/kubernetes-1-22/kubernetes-1-22-eks-9.yaml + name: kubernetes-1-22-eks-9 ova: bottlerocket: arch: - amd64 crictl: {} - description: Bottlerocket Ova image for EKS-D 1-22-8 release + description: Bottlerocket Ova image for EKS-D 1-22-9 release etcdadm: {} - name: bottlerocket-v1.22.10-eks-d-1-22-8-eks-a-v0.0.0-dev-build.0-amd64.ova + name: bottlerocket-v1.22.10-eks-d-1-22-9-eks-a-v0.0.0-dev-build.0-amd64.ova os: linux osName: bottlerocket sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-22/1-22-8/bottlerocket-v1.22.10-eks-d-1-22-8-eks-a-v0.0.0-dev-build.0-amd64.ova + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-22/1-22-9/bottlerocket-v1.22.10-eks-d-1-22-9-eks-a-v0.0.0-dev-build.0-amd64.ova ubuntu: arch: - amd64 @@ -1700,7 +1700,7 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/cri-tools/v1.20.0/cri-tools-v0.0.0-dev+build.0-linux-amd64.tar.gz - description: Ubuntu Ova image for EKS-D 1-22-8 release + description: Ubuntu Ova image for EKS-D 1-22-9 release etcdadm: arch: - amd64 @@ -1710,25 +1710,25 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/etcdadm/5b496a72af3d80d64a16a650c85ce9a5882bc014/etcdadm-v0.0.0-dev+build.0-linux-amd64.tar.gz - name: ubuntu-v1.22.10-eks-d-1-22-8-eks-a-v0.0.0-dev-build.0-amd64.ova + name: ubuntu-v1.22.10-eks-d-1-22-9-eks-a-v0.0.0-dev-build.0-amd64.ova os: linux osName: ubuntu sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-22/1-22-8/ubuntu-v1.22.10-eks-d-1-22-8-eks-a-v0.0.0-dev-build.0-amd64.ova + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-22/1-22-9/ubuntu-v1.22.10-eks-d-1-22-9-eks-a-v0.0.0-dev-build.0-amd64.ova raw: bottlerocket: arch: - amd64 crictl: {} - description: Bottlerocket Raw image for EKS-D 1-22-8 release + description: Bottlerocket Raw image for EKS-D 1-22-9 release etcdadm: {} - name: bottlerocket-v1.22.10-eks-d-1-22-8-eks-a-v0.0.0-dev-build.0-amd64.img.gz + name: bottlerocket-v1.22.10-eks-d-1-22-9-eks-a-v0.0.0-dev-build.0-amd64.img.gz os: linux osName: bottlerocket sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-22/1-22-8/bottlerocket-v1.22.10-eks-d-1-22-8-eks-a-v0.0.0-dev-build.0-amd64.img.gz + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-22/1-22-9/bottlerocket-v1.22.10-eks-d-1-22-9-eks-a-v0.0.0-dev-build.0-amd64.img.gz ubuntu: arch: - amd64 @@ -1741,7 +1741,7 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/cri-tools/v1.20.0/cri-tools-v0.0.0-dev+build.0-linux-amd64.tar.gz - description: Ubuntu Raw image for EKS-D 1-22-8 release + description: Ubuntu Raw image for EKS-D 1-22-9 release etcdadm: arch: - amd64 @@ -1751,12 +1751,12 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/etcdadm/5b496a72af3d80d64a16a650c85ce9a5882bc014/etcdadm-v0.0.0-dev+build.0-linux-amd64.tar.gz - name: ubuntu-v1.22.10-eks-d-1-22-8-eks-a-v0.0.0-dev-build.0-amd64.gz + name: ubuntu-v1.22.10-eks-d-1-22-9-eks-a-v0.0.0-dev-build.0-amd64.gz os: linux osName: ubuntu sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-22/1-22-8/ubuntu-v1.22.10-eks-d-1-22-8-eks-a-v0.0.0-dev-build.0-amd64.gz + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-22/1-22-9/ubuntu-v1.22.10-eks-d-1-22-9-eks-a-v0.0.0-dev-build.0-amd64.gz eksa: cliTools: arch: @@ -2219,7 +2219,7 @@ spec: imageDigest: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef name: bottlerocket-bootstrap os: linux - uri: public.ecr.aws/release-container-registry/bottlerocket-bootstrap:v1-23-2-eks-a-v0.0.0-dev-build.1 + uri: public.ecr.aws/release-container-registry/bottlerocket-bootstrap:v1-23-3-eks-a-v0.0.0-dev-build.1 certManager: acmesolver: arch: @@ -2390,23 +2390,23 @@ spec: imageDigest: sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef name: kind-node os: linux - uri: public.ecr.aws/release-container-registry/kubernetes-sigs/kind/node:v1.23.7-eks-d-1-23-2-eks-a-v0.0.0-dev-build.1 + uri: public.ecr.aws/release-container-registry/kubernetes-sigs/kind/node:v1.23.7-eks-d-1-23-3-eks-a-v0.0.0-dev-build.1 kubeVersion: v1.23.7 - manifestUrl: https://distro.eks.amazonaws.com/kubernetes-1-23/kubernetes-1-23-eks-2.yaml - name: kubernetes-1-23-eks-2 + manifestUrl: https://distro.eks.amazonaws.com/kubernetes-1-23/kubernetes-1-23-eks-3.yaml + name: kubernetes-1-23-eks-3 ova: bottlerocket: arch: - amd64 crictl: {} - description: Bottlerocket Ova image for EKS-D 1-23-2 release + description: Bottlerocket Ova image for EKS-D 1-23-3 release etcdadm: {} - name: bottlerocket-v1.23.7-eks-d-1-23-2-eks-a-v0.0.0-dev-build.0-amd64.ova + name: bottlerocket-v1.23.7-eks-d-1-23-3-eks-a-v0.0.0-dev-build.0-amd64.ova os: linux osName: bottlerocket sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-23/1-23-2/bottlerocket-v1.23.7-eks-d-1-23-2-eks-a-v0.0.0-dev-build.0-amd64.ova + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-23/1-23-3/bottlerocket-v1.23.7-eks-d-1-23-3-eks-a-v0.0.0-dev-build.0-amd64.ova ubuntu: arch: - amd64 @@ -2419,7 +2419,7 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/cri-tools/v1.20.0/cri-tools-v0.0.0-dev+build.0-linux-amd64.tar.gz - description: Ubuntu Ova image for EKS-D 1-23-2 release + description: Ubuntu Ova image for EKS-D 1-23-3 release etcdadm: arch: - amd64 @@ -2429,25 +2429,25 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/etcdadm/5b496a72af3d80d64a16a650c85ce9a5882bc014/etcdadm-v0.0.0-dev+build.0-linux-amd64.tar.gz - name: ubuntu-v1.23.7-eks-d-1-23-2-eks-a-v0.0.0-dev-build.0-amd64.ova + name: ubuntu-v1.23.7-eks-d-1-23-3-eks-a-v0.0.0-dev-build.0-amd64.ova os: linux osName: ubuntu sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-23/1-23-2/ubuntu-v1.23.7-eks-d-1-23-2-eks-a-v0.0.0-dev-build.0-amd64.ova + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/ova/1-23/1-23-3/ubuntu-v1.23.7-eks-d-1-23-3-eks-a-v0.0.0-dev-build.0-amd64.ova raw: bottlerocket: arch: - amd64 crictl: {} - description: Bottlerocket Raw image for EKS-D 1-23-2 release + description: Bottlerocket Raw image for EKS-D 1-23-3 release etcdadm: {} - name: bottlerocket-v1.23.7-eks-d-1-23-2-eks-a-v0.0.0-dev-build.0-amd64.img.gz + name: bottlerocket-v1.23.7-eks-d-1-23-3-eks-a-v0.0.0-dev-build.0-amd64.img.gz os: linux osName: bottlerocket sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-23/1-23-2/bottlerocket-v1.23.7-eks-d-1-23-2-eks-a-v0.0.0-dev-build.0-amd64.img.gz + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-23/1-23-3/bottlerocket-v1.23.7-eks-d-1-23-3-eks-a-v0.0.0-dev-build.0-amd64.img.gz ubuntu: arch: - amd64 @@ -2460,7 +2460,7 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/cri-tools/v1.20.0/cri-tools-v0.0.0-dev+build.0-linux-amd64.tar.gz - description: Ubuntu Raw image for EKS-D 1-23-2 release + description: Ubuntu Raw image for EKS-D 1-23-3 release etcdadm: arch: - amd64 @@ -2470,12 +2470,12 @@ spec: sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/etcdadm/5b496a72af3d80d64a16a650c85ce9a5882bc014/etcdadm-v0.0.0-dev+build.0-linux-amd64.tar.gz - name: ubuntu-v1.23.7-eks-d-1-23-2-eks-a-v0.0.0-dev-build.0-amd64.gz + name: ubuntu-v1.23.7-eks-d-1-23-3-eks-a-v0.0.0-dev-build.0-amd64.gz os: linux osName: ubuntu sha256: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef sha512: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef - uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-23/1-23-2/ubuntu-v1.23.7-eks-d-1-23-2-eks-a-v0.0.0-dev-build.0-amd64.gz + uri: https://release-bucket/artifacts/v0.0.0-dev-build.0/eks-distro/raw/1-23/1-23-3/ubuntu-v1.23.7-eks-d-1-23-3-eks-a-v0.0.0-dev-build.0-amd64.gz eksa: cliTools: arch: diff --git a/test/e2e/tools/eks-anywhere-test-tool/go.sum b/test/e2e/tools/eks-anywhere-test-tool/go.sum index 4fcace3fab94..6bfcb1e860a5 100644 --- a/test/e2e/tools/eks-anywhere-test-tool/go.sum +++ b/test/e2e/tools/eks-anywhere-test-tool/go.sum @@ -553,10 +553,6 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=