Skip to content

Commit

Permalink
vsphere platform: Support existing resource pool
Browse files Browse the repository at this point in the history
Some users may want to use IPI on a shared vSphere cluster, where
they do not have full admin privileges. This allows them to use
IPI with privileges mostly confined to an existing resource pool.

Compliments PR openshift#3498.
  • Loading branch information
willhaines committed Sep 29, 2021
1 parent 7cb4d79 commit 4d95df5
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 2 deletions.
28 changes: 28 additions & 0 deletions pkg/asset/installconfig/vsphere/validation.go
Expand Up @@ -36,6 +36,7 @@ func ValidateForProvisioning(ic *types.InstallConfig) error {

allErrs = append(allErrs, validation.ValidateForProvisioning(ic.Platform.VSphere, field.NewPath("platform").Child("vsphere"))...)
allErrs = append(allErrs, folderExists(ic, field.NewPath("platform").Child("vsphere").Child("folder"))...)
allErrs = append(allErrs, resourcePoolExists(ic, field.NewPath("platform").Child("vsphere").Child("resourcePool"))...)

return allErrs.ToAggregate()
}
Expand Down Expand Up @@ -66,3 +67,30 @@ func folderExists(ic *types.InstallConfig, fldPath *field.Path) field.ErrorList
}
return nil
}

// resourcePoolExists returns an error if a resourcePool is specified in the vSphere platform but a resourcePool with that name is not found in the datacenter.
func resourcePoolExists(ic *types.InstallConfig, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
cfg := ic.VSphere

// If no resourcePool is specified, skip this check as the root resourcePool will be used.
if cfg.ResourcePool == "" {
return allErrs
}

vim25Client, _, err := vspheretypes.CreateVSphereClients(context.TODO(), cfg.VCenter, cfg.Username, cfg.Password)
if err != nil {
err = errors.Wrap(err, "unable to connect to vCenter API")
return append(allErrs, field.InternalError(fldPath, err))
}

finder := find.NewFinder(vim25Client)

ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second)
defer cancel()

if _, err = finder.ResourcePool(ctx, cfg.ResourcePool); err != nil {
return append(allErrs, field.Invalid(fldPath, cfg.ResourcePool, err.Error()))
}
return nil
}
3 changes: 3 additions & 0 deletions pkg/asset/machines/vsphere/machines.go
Expand Up @@ -69,6 +69,9 @@ func provider(clusterID string, platform *vsphere.Platform, mpool *vsphere.Machi
if platform.Folder != "" {
folder = platform.Folder
}
if platform.ResourcePool != "" {
resourcePool = platform.ResourcePool
}

return &vsphereapis.VSphereMachineProviderSpec{
TypeMeta: metav1.TypeMeta{
Expand Down
1 change: 1 addition & 0 deletions pkg/asset/manifests/vsphere/cloudproviderconfig.go
Expand Up @@ -30,6 +30,7 @@ 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)
Expand Down
14 changes: 13 additions & 1 deletion pkg/types/validation/installconfig_test.go
Expand Up @@ -692,7 +692,19 @@ func TestValidateInstallConfig(t *testing.T) {
c.Platform.VSphere.Folder = "my-folder"
return c
}(),
expectedError: `^platform\.vsphere.folder: Invalid value: \"my-folder\": folder must be absolute path: expected prefix /test-datacenter/vm/$`,
expectedError: `^platform\.vsphere\.folder: Invalid value: \"my-folder\": folder must be absolute path: expected prefix /test-datacenter/vm/$`,
},
{
name: "invalid vsphere resource pool",
installConfig: func() *types.InstallConfig {
c := validInstallConfig()
c.Platform = types.Platform{
VSphere: validVSpherePlatform(),
}
c.Platform.VSphere.ResourcePool = "my-resource-pool"
return c
}(),
expectedError: `^platform\.vsphere\.resourcePool: Invalid value: \"my-resource-pool\": resourcePool must be absolute path: expected prefix /test-datacenter/host/<cluster>/Resources/$`,
},
{
name: "empty proxy settings",
Expand Down
4 changes: 4 additions & 0 deletions pkg/types/vsphere/platform.go
Expand Up @@ -24,6 +24,10 @@ type Platform struct {
// Cluster is the name of the cluster virtual machines will be cloned into.
Cluster string `json:"cluster,omitempty"`

// ResourcePool is the absolute path of the resource pool where virtual machines will be
// created. The absolute path is of the form /<datacenter>/host/<cluster>/Resources/<resourcepool>.
ResourcePool string `json:"resourcePool,omitempty"`

// ClusterOSImage overrides the url provided in rhcos.json to download the RHCOS OVA
ClusterOSImage string `json:"clusterOSImage,omitempty"`

Expand Down
29 changes: 28 additions & 1 deletion pkg/types/vsphere/validation/platform.go
Expand Up @@ -45,6 +45,11 @@ func ValidatePlatform(p *vsphere.Platform, fldPath *field.Path) field.ErrorList
allErrs = append(allErrs, validateFolder(p, fldPath)...)
}

// resource pool is optional, but if provided should pass validation
if len(p.ResourcePool) != 0 {
allErrs = append(allErrs, validateResourcePool(p, fldPath)...)
}

return allErrs
}

Expand Down Expand Up @@ -87,7 +92,7 @@ func validateVIPs(p *vsphere.Platform, fldPath *field.Path) field.ErrorList {
return allErrs
}

// validateFolder checks that a provided folder is in absolute path in the correct datacenter.
// validateFolder checks that a provided folder is an absolute path in the correct datacenter.
func validateFolder(p *vsphere.Platform, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

Expand All @@ -104,3 +109,25 @@ func validateFolder(p *vsphere.Platform, fldPath *field.Path) field.ErrorList {

return allErrs
}

// validateResourcePool checks that a provided resource pool is an absolute path in the correct cluster.
func validateResourcePool(p *vsphere.Platform, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

dc := p.Datacenter
if len(dc) == 0 {
dc = "<datacenter>"
}
cluster := p.Cluster
if len(cluster) == 0 {
cluster = "<cluster>"
}
expectedPrefix := fmt.Sprintf("/%s/host/%s/Resources/", dc, cluster)

if !strings.HasPrefix(p.ResourcePool, expectedPrefix) {
errMsg := fmt.Sprintf("resourcePool must be absolute path: expected prefix %s", expectedPrefix)
allErrs = append(allErrs, field.Invalid(fldPath.Child("resourcePool"), p.ResourcePool, errMsg))
}

return allErrs
}

0 comments on commit 4d95df5

Please sign in to comment.