diff --git a/data/data/manifests/openshift/cloud-creds-secret.yaml.template b/data/data/manifests/openshift/cloud-creds-secret.yaml.template index 20b0908468..3f48ec772b 100644 --- a/data/data/manifests/openshift/cloud-creds-secret.yaml.template +++ b/data/data/manifests/openshift/cloud-creds-secret.yaml.template @@ -36,9 +36,9 @@ data: {{- else if .CloudCreds.OpenStack}} clouds.yaml: {{.CloudCreds.OpenStack.Base64encodeCloudCreds}} clouds.conf: {{.CloudCreds.OpenStack.Base64encodeCloudCredsINI}} -{{- else if .CloudCreds.VSphere}} - {{.CloudCreds.VSphere.VCenter}}.username: {{.CloudCreds.VSphere.Base64encodeUsername}} - {{.CloudCreds.VSphere.VCenter}}.password: {{.CloudCreds.VSphere.Base64encodePassword}} +{{- else if .CloudCreds.VSphere}} {{ range .CloudCreds.VSphere }} + {{.VCenter}}.username: {{.Base64encodeUsername}} + {{.VCenter}}.password: {{.Base64encodePassword}} {{ end }} {{- else if .CloudCreds.Ovirt}} ovirt_url: {{.CloudCreds.Ovirt.Base64encodeURL}} ovirt_username: {{.CloudCreds.Ovirt.Base64encodeUsername}} diff --git a/pkg/asset/manifests/openshift.go b/pkg/asset/manifests/openshift.go index 0bfb3b107e..a6d6026392 100644 --- a/pkg/asset/manifests/openshift.go +++ b/pkg/asset/manifests/openshift.go @@ -181,12 +181,25 @@ func (o *Openshift) Generate(dependencies asset.Parents) error { }, } case vspheretypes.Name: - cloudCreds = cloudCredsSecretData{ - VSphere: &VSphereCredsSecretData{ + vsphereCredentials := make([]*VSphereCredsSecretData,0) + if installConfig.Config.VSphere.VCenter != "" { + vsphereCredentials = append(vsphereCredentials, &VSphereCredsSecretData{ VCenter: installConfig.Config.VSphere.VCenter, Base64encodeUsername: base64.StdEncoding.EncodeToString([]byte(installConfig.Config.VSphere.Username)), Base64encodePassword: base64.StdEncoding.EncodeToString([]byte(installConfig.Config.VSphere.Password)), - }, + }) + } + if len(installConfig.Config.VSphere.VCenters) > 0{ + for _, vcenter := range installConfig.Config.VSphere.VCenters { + vsphereCredentials = append(vsphereCredentials, &VSphereCredsSecretData{ + VCenter: vcenter.Server, + Base64encodeUsername: base64.StdEncoding.EncodeToString([]byte(vcenter.User)), + Base64encodePassword: base64.StdEncoding.EncodeToString([]byte(vcenter.Password)), + }) + } + } + cloudCreds = cloudCredsSecretData{ + VSphere: vsphereCredentials, } case ovirttypes.Name: conf, err := ovirt.NewConfig() diff --git a/pkg/asset/manifests/template.go b/pkg/asset/manifests/template.go index 4e93d3b00d..64b97130c0 100644 --- a/pkg/asset/manifests/template.go +++ b/pkg/asset/manifests/template.go @@ -57,7 +57,7 @@ type cloudCredsSecretData struct { GCP *GCPCredsSecretData IBMCloud *IBMCloudCredsSecretData OpenStack *OpenStackCredsSecretData - VSphere *VSphereCredsSecretData + VSphere [] *VSphereCredsSecretData Ovirt *OvirtCredsSecretData } diff --git a/pkg/asset/manifests/vsphere/cloudproviderconfig.go b/pkg/asset/manifests/vsphere/cloudproviderconfig.go index 5f312abecc..5b51c61ffd 100644 --- a/pkg/asset/manifests/vsphere/cloudproviderconfig.go +++ b/pkg/asset/manifests/vsphere/cloudproviderconfig.go @@ -13,6 +13,24 @@ func printIfNotEmpty(buf *bytes.Buffer, k, v string) { } } +func getDatacenters(vcenter vspheretypes.VCenter) string { + datacenters := make([]string, 0) + for _, region := range vcenter.Regions { + if region.Datacenter != "" { + datacenters = append(datacenters, region.Datacenter) + } + } + datacenterString := "" + for idx, dataCenter := range datacenters { + if idx > 0 { + datacenterString = datacenterString + "," + dataCenter + } else { + datacenterString = dataCenter + } + } + return datacenterString +} + // CloudProviderConfig generates the cloud provider config for the vSphere platform. // folderPath is the absolute path to the VM folder that will be used for installation. // p is the vSphere platform struct. @@ -30,11 +48,51 @@ func CloudProviderConfig(folderPath string, p *vspheretypes.Platform) (string, e printIfNotEmpty(buf, "datacenter", p.Datacenter) printIfNotEmpty(buf, "default-datastore", p.DefaultDatastore) printIfNotEmpty(buf, "folder", folderPath) - printIfNotEmpty(buf, "resourcepool-path", p.ResourcePool) - fmt.Fprintln(buf, "") - fmt.Fprintf(buf, "[VirtualCenter %q]\n", p.VCenter) - printIfNotEmpty(buf, "datacenters", p.Datacenter) + regions := make([]string, 0) + zones := make([]string, 0) + + for _, vcenter := range p.VCenters { + fmt.Fprintln(buf, "") + fmt.Fprintf(buf, "[VirtualCenter %q]\n", vcenter.Server) + printIfNotEmpty(buf, "datacenters", getDatacenters(vcenter)) + for _, region := range vcenter.Regions { + regions = append(regions, region.Name) + for _, zone := range region.Zones { + zones = append(zones, zone.Name) + } + } + } + + if p.VCenter != "" { + fmt.Fprintln(buf, "") + fmt.Fprintf(buf, "[VirtualCenter %q]\n", p.VCenter) + printIfNotEmpty(buf, "datacenters", p.Datacenter) + } + + if len(zones) > 0 { + fmt.Fprintln(buf, "") + fmt.Fprintln(buf, "[Labels]") + regionsString := "" + zonesString := "" + for idx, region := range regions { + if idx > 0 { + regionsString = regionsString + "," + region + } else { + regionsString = region + } + } + printIfNotEmpty(buf, "region", regionsString) + + for idx, zone := range zones { + if idx > 0 { + zonesString = zonesString + "," + zone + } else { + zonesString = zone + } + } + printIfNotEmpty(buf, "zone", zonesString) + } return buf.String(), nil } diff --git a/pkg/asset/manifests/vsphere/cloudproviderconfig_test.go b/pkg/asset/manifests/vsphere/cloudproviderconfig_test.go index 82927367d4..b51b26a634 100644 --- a/pkg/asset/manifests/vsphere/cloudproviderconfig_test.go +++ b/pkg/asset/manifests/vsphere/cloudproviderconfig_test.go @@ -9,15 +9,15 @@ import ( vspheretypes "github.com/openshift/installer/pkg/types/vsphere" ) -func TestCloudProviderConfig(t *testing.T) { - platform := &vspheretypes.Platform{ - VCenter: "test-name", - Username: "test-username", - Password: "test-password", - Datacenter: "test-datacenter", - DefaultDatastore: "test-datastore", - } - expectedConfig := `[Global] +var cloudProviderLegacyPlatform = &vspheretypes.Platform{ + VCenter: "test-name", + Username: "test-username", + Password: "test-password", + Datacenter: "test-datacenter", + DefaultDatastore: "test-datastore", +} + +var cloudProviderLegacyExpectedConfig = `[Global] secret-name = "vsphere-creds" secret-namespace = "kube-system" insecure-flag = "1" @@ -31,8 +31,151 @@ folder = "/test-datacenter/vm/clusterID" [VirtualCenter "test-name"] datacenters = "test-datacenter" ` + +var cloudProviderMultiClusterPlatform = &vspheretypes.Platform{ + VCenters: []vspheretypes.VCenter{ + { + Server: "test-vcenter-1", + User: "test-user", + Password: "test-password", + Port: 443, + Regions: []vspheretypes.Region{ + { + Name: "region-a", + Datacenter: "dc-region-a", + Zones: []vspheretypes.Zone{ + { + Name: "zone-a-1", + ResourcePool: "zone-a-pool-1", + Cluster: "zone-a-cluster-1", + Network: "VM Network", + Datastore: "zone-a-datastore-1", + }, + { + Name: "zone-a-2", + ResourcePool: "zone-a-pool-2", + Cluster: "zone-a-cluster-2", + Network: "VM Network", + Datastore: "zone-a-datastore-2", + }, + }, + }, + { + Name: "region-b", + Datacenter: "dc-region-b", + Zones: []vspheretypes.Zone{ + { + Name: "zone-b-1", + ResourcePool: "zone-b-pool-1", + Cluster: "zone-b-cluster-1", + Network: "VM Network", + Datastore: "zone-b-datastore-1", + }, + { + Name: "zone-b-2", + ResourcePool: "zone-b-pool-2", + Cluster: "zone-b-cluster-2", + Network: "VM Network", + Datastore: "zone-b-datastore-2", + }, + }, + }, + }, + }, + { + Server: "test-vcenter-2", + User: "test-user", + Password: "test-password", + Port: 443, + Regions: []vspheretypes.Region{ + { + Name: "region-c", + Datacenter: "dc-region-c", + Zones: []vspheretypes.Zone{ + { + Name: "zone-c-1", + ResourcePool: "zone-c-pool-1", + Cluster: "zone-c-cluster-1", + Network: "VM Network", + Datastore: "zone-c-datastore-1", + }, + { + Name: "zone-c-2", + ResourcePool: "zone-c-pool-2", + Cluster: "zone-c-cluster-2", + Network: "VM Network", + Datastore: "zone-c-datastore-2", + }, + }, + }, + { + Name: "region-d", + Datacenter: "dc-region-d", + Zones: []vspheretypes.Zone{ + { + Name: "zone-d-1", + ResourcePool: "zone-d-pool-1", + Cluster: "zone-d-cluster-1", + Network: "VM Network", + Datastore: "zone-d-datastore-1", + }, + { + Name: "zone-d-2", + ResourcePool: "zone-d-pool-2", + Cluster: "zone-d-cluster-2", + Network: "VM Network", + Datastore: "zone-d-datastore-2", + }, + }, + }, + }, + }, + }, +} + +var cloudProviderMultiClusteryExpectedConfig = `[Global] +secret-name = "vsphere-creds" +secret-namespace = "kube-system" +insecure-flag = "1" + +[Workspace] +folder = "/test-datacenter/vm/clusterID" + +[VirtualCenter "test-vcenter-1"] +datacenters = "dc-region-a,dc-region-b" + +[VirtualCenter "test-vcenter-2"] +datacenters = "dc-region-c,dc-region-d" + +[Labels] +region = "region-a,region-b,region-c,region-d" +zone = "zone-a-1,zone-a-2,zone-b-1,zone-b-2,zone-c-1,zone-c-2,zone-d-1,zone-d-2" +` + +func TestCloudProviderConfig(t *testing.T) { folderPath := fmt.Sprintf("/%s/vm/%s", "test-datacenter", "clusterID") - actualConfig, err := CloudProviderConfig(folderPath, platform) - assert.NoError(t, err, "failed to create cloud provider config") - assert.Equal(t, expectedConfig, actualConfig, "unexpected cloud provider config") + + testCases := []struct { + testCase string + platform *vspheretypes.Platform + expectedConfig string + }{ + { + testCase: "cloud provider configuration matches legacy platform specification", + platform: cloudProviderLegacyPlatform, + expectedConfig: cloudProviderLegacyExpectedConfig, + }, + { + testCase: "cloud provider configuration matches multi-vcenter platform specification", + platform: cloudProviderMultiClusterPlatform, + expectedConfig: cloudProviderMultiClusteryExpectedConfig, + }, + } + + for _, tc := range testCases { + actualConfig, err := CloudProviderConfig(folderPath, tc.platform) + assert.NoError(t, err, "failed to create cloud provider config") + assert.Equal(t, tc.expectedConfig, actualConfig, "unexpected cloud provider config") + } + }