From 1604905af335719d8368585a5c60b4ed1b16a9d9 Mon Sep 17 00:00:00 2001 From: Christy Norman Date: Fri, 4 Feb 2022 16:27:59 -0600 Subject: [PATCH] Add IBM Power VS: types only For more background on IPI on Power VS, refer to the enhancement proposal here: openshift/enhancements#736 Older discussions on some of the code here can be found in https://github.com/openshift/installer/pull/5224. An older unreviewed version of this PR can be found here: https://github.com/openshift/installer/pull/5292 Signed-off-by: Christy Norman --- OWNERS_ALIASES | 9 ++ .../install.openshift.io_installconfigs.yaml | 148 +++++++++++++++++- pkg/explain/printer_test.go | 5 +- pkg/types/clustermetadata.go | 5 + pkg/types/defaults/installconfig.go | 3 + pkg/types/installconfig.go | 9 ++ pkg/types/machinepools.go | 6 + pkg/types/powervs/OWNERS | 7 + pkg/types/powervs/defaults/platform.go | 9 ++ pkg/types/powervs/doc.go | 6 + pkg/types/powervs/machinepools.go | 65 ++++++++ pkg/types/powervs/metadata.go | 8 + pkg/types/powervs/platform.go | 57 +++++++ pkg/types/powervs/powervs_regions.go | 67 ++++++++ pkg/types/powervs/validation/machinepool.go | 69 ++++++++ .../powervs/validation/machinepool_test.go | 132 ++++++++++++++++ pkg/types/powervs/validation/platform.go | 18 +++ pkg/types/powervs/validation/platform_test.go | 12 ++ pkg/types/validation/installconfig.go | 6 + pkg/types/validation/installconfig_test.go | 57 ++++++- pkg/types/validation/machinepools.go | 5 + 21 files changed, 698 insertions(+), 5 deletions(-) create mode 100644 pkg/types/powervs/OWNERS create mode 100644 pkg/types/powervs/defaults/platform.go create mode 100644 pkg/types/powervs/doc.go create mode 100644 pkg/types/powervs/machinepools.go create mode 100644 pkg/types/powervs/metadata.go create mode 100644 pkg/types/powervs/platform.go create mode 100644 pkg/types/powervs/powervs_regions.go create mode 100644 pkg/types/powervs/validation/machinepool.go create mode 100644 pkg/types/powervs/validation/machinepool_test.go create mode 100644 pkg/types/powervs/validation/platform.go create mode 100644 pkg/types/powervs/validation/platform_test.go diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES index 2ef77e0cbc8..fb430da4f85 100644 --- a/OWNERS_ALIASES +++ b/OWNERS_ALIASES @@ -33,6 +33,15 @@ aliases: - mdbooth - pierreprinetti - stephenfin + powervs-approvers: + - clnperez + - mkumatag + - Prashanth684 + powervs-reviewers: + - clnperez + - mkumatag + - Prashanth684 + - mjturek vsphere-approvers: - dav1x - jcpowermac diff --git a/data/data/install.openshift.io_installconfigs.yaml b/data/data/install.openshift.io_installconfigs.yaml index 6120e22021b..290811dafa5 100644 --- a/data/data/install.openshift.io_installconfigs.yaml +++ b/data/data/install.openshift.io_installconfigs.yaml @@ -469,6 +469,36 @@ spec: - high_performance type: string type: object + powervs: + description: PowerVS is the configuration used when installing + on IBM Power VS. + properties: + memory: + description: Memory defines the memory in GB for the instance. + type: string + procType: + description: ProcType defines the processor sharing model + for the instance. Must be one of {capped, dedicated, shared}. + enum: + - "" + - capped + - dedicated + - shared + type: string + processors: + description: Processors defines the processing units for + the instance. + type: string + sysType: + description: SysType defines the system type for instance. + type: string + volumeIDs: + description: VolumeIDs is the list of volumes attached to + the instance. + items: + type: string + type: array + type: object vsphere: description: VSphere is the configuration used when installing on vSphere. @@ -929,6 +959,36 @@ spec: - high_performance type: string type: object + powervs: + description: PowerVS is the configuration used when installing + on IBM Power VS. + properties: + memory: + description: Memory defines the memory in GB for the instance. + type: string + procType: + description: ProcType defines the processor sharing model + for the instance. Must be one of {capped, dedicated, shared}. + enum: + - "" + - capped + - dedicated + - shared + type: string + processors: + description: Processors defines the processing units for the + instance. + type: string + sysType: + description: SysType defines the system type for instance. + type: string + volumeIDs: + description: VolumeIDs is the list of volumes attached to + the instance. + items: + type: string + type: array + type: object vsphere: description: VSphere is the configuration used when installing on vSphere. @@ -981,7 +1041,7 @@ spec: values. For all other platforms, the field must not be set. AWS: \"Mint\", \"Passthrough\", \"Manual\" Azure: \"Mint\", \"Passthrough\", \"Manual\" AzureStack: \"Manual\" GCP: \"Mint\", \"Passthrough\", \"Manual\" IBMCloud: - \"Manual\" AlibabaCloud: \"Manual\"" + \"Manual\" AlibabaCloud: \"Manual\" PowerVS: \"Manual\"" enum: - "" - Mint @@ -2159,6 +2219,92 @@ spec: - ovirt_cluster_id - ovirt_storage_domain_id type: object + powervs: + description: PowerVS is the configuration used when installing on + Power VS. + properties: + clusterOSImage: + description: ClusterOSImage is a pre-created Power VS boot image + that overrides the default image for cluster nodes. + type: string + defaultMachinePlatform: + description: DefaultMachinePlatform is the default configuration + used when installing on Power VS for machine pools which do + not define their own platform configuration. + properties: + memory: + description: Memory defines the memory in GB for the instance. + type: string + procType: + description: ProcType defines the processor sharing model + for the instance. Must be one of {capped, dedicated, shared}. + enum: + - "" + - capped + - dedicated + - shared + type: string + processors: + description: Processors defines the processing units for the + instance. + type: string + sysType: + description: SysType defines the system type for instance. + type: string + volumeIDs: + description: VolumeIDs is the list of volumes attached to + the instance. + items: + type: string + type: array + type: object + powervsResourceGroup: + description: PowerVSResourceGroup is the resource group in which + Power VS resources will be created. + type: string + pvsNetworkName: + description: PVSNetworkName specifies an existing network within + the Power VS Service Instance. + type: string + region: + description: Region specifies the IBM Cloud colo region where + the cluster will be created. + type: string + serviceInstanceID: + description: ServiceInstanceID is the ID of the Power IAAS instance + created from the IBM Cloud Catalog + type: string + subnets: + description: Subnets specifies existing subnets (by ID) where + cluster resources will be created. Leave unset to have the + installer create subnets in a new VPC on your behalf. + items: + type: string + type: array + userID: + description: UserID is the login for the user's IBM Cloud account. + type: string + vpc: + description: VPC is a VPC inside IBM Cloud. Needed in order to + create VPC Load Balancers. + type: string + vpcRegion: + description: VPCRegion specifies the IBM Cloud region in which + to create VPC resources. Leave unset to allow installer to select + the closest VPC region. + type: string + zone: + description: Zone specifies the IBM Cloud colo region where the + cluster will be created. At this time, only single-zone clusters + are supported. + type: string + required: + - powervsResourceGroup + - region + - serviceInstanceID + - userID + - zone + type: object vsphere: description: VSphere is the configuration used when installing on vSphere. diff --git a/pkg/explain/printer_test.go b/pkg/explain/printer_test.go index 487af8f8fe8..14db69acade 100644 --- a/pkg/explain/printer_test.go +++ b/pkg/explain/printer_test.go @@ -43,7 +43,7 @@ func Test_PrintFields(t *testing.T) { CredentialsMode is used to explicitly set the mode with which CredentialRequests are satisfied. If this field is set, then the installer will not attempt to query the cloud permissions before attempting installation. If the field is not set or empty, then the installer will perform its normal verification that the credentials provided are sufficient to perform an installation. There are three possible values for this field, but the valid values are dependent upon the platform being used. "Mint": create new credentials with a subset of the overall permissions for each CredentialsRequest "Passthrough": copy the credentials with all of the overall permissions for each CredentialsRequest "Manual": CredentialsRequests must be handled manually by the user - For each of the following platforms, the field can set to the specified values. For all other platforms, the field must not be set. AWS: "Mint", "Passthrough", "Manual" Azure: "Mint", "Passthrough", "Manual" AzureStack: "Manual" GCP: "Mint", "Passthrough", "Manual" IBMCloud: "Manual" AlibabaCloud: "Manual" + For each of the following platforms, the field can set to the specified values. For all other platforms, the field must not be set. AWS: "Mint", "Passthrough", "Manual" Azure: "Mint", "Passthrough", "Manual" AzureStack: "Manual" GCP: "Mint", "Passthrough", "Manual" IBMCloud: "Manual" AlibabaCloud: "Manual" PowerVS: "Manual" fips Default: false @@ -114,6 +114,9 @@ func Test_PrintFields(t *testing.T) { ovirt Ovirt is the configuration used when installing on oVirt. + powervs + PowerVS is the configuration used when installing on Power VS. + vsphere VSphere is the configuration used when installing on vSphere.`, }, { diff --git a/pkg/types/clustermetadata.go b/pkg/types/clustermetadata.go index e0ab9fa696e..5c6dcc2d03f 100644 --- a/pkg/types/clustermetadata.go +++ b/pkg/types/clustermetadata.go @@ -10,6 +10,7 @@ import ( "github.com/openshift/installer/pkg/types/libvirt" "github.com/openshift/installer/pkg/types/openstack" "github.com/openshift/installer/pkg/types/ovirt" + "github.com/openshift/installer/pkg/types/powervs" "github.com/openshift/installer/pkg/types/vsphere" ) @@ -36,6 +37,7 @@ type ClusterPlatformMetadata struct { IBMCloud *ibmcloud.Metadata `json:"ibmcloud,omitempty"` BareMetal *baremetal.Metadata `json:"baremetal,omitempty"` Ovirt *ovirt.Metadata `json:"ovirt,omitempty"` + PowerVS *powervs.Metadata `json:"powervs,omitempty"` VSphere *vsphere.Metadata `json:"vsphere,omitempty"` } @@ -73,6 +75,9 @@ func (cpm *ClusterPlatformMetadata) Platform() string { if cpm.Ovirt != nil { return ovirt.Name } + if cpm.PowerVS != nil { + return powervs.Name + } if cpm.VSphere != nil { return vsphere.Name } diff --git a/pkg/types/defaults/installconfig.go b/pkg/types/defaults/installconfig.go index ace0565aa01..720b82b98d2 100644 --- a/pkg/types/defaults/installconfig.go +++ b/pkg/types/defaults/installconfig.go @@ -14,6 +14,7 @@ import ( nonedefaults "github.com/openshift/installer/pkg/types/none/defaults" openstackdefaults "github.com/openshift/installer/pkg/types/openstack/defaults" ovirtdefaults "github.com/openshift/installer/pkg/types/ovirt/defaults" + powervsdefaults "github.com/openshift/installer/pkg/types/powervs/defaults" vspheredefaults "github.com/openshift/installer/pkg/types/vsphere/defaults" ) @@ -105,6 +106,8 @@ func SetInstallConfigDefaults(c *types.InstallConfig) { for i := range c.Compute { ovirtdefaults.SetComputeDefaults(c.Platform.Ovirt, &c.Compute[i]) } + case c.Platform.PowerVS != nil: + powervsdefaults.SetPlatformDefaults(c.Platform.PowerVS) case c.Platform.None != nil: nonedefaults.SetPlatformDefaults(c.Platform.None) } diff --git a/pkg/types/installconfig.go b/pkg/types/installconfig.go index a3ee2651029..90142fb2d8a 100644 --- a/pkg/types/installconfig.go +++ b/pkg/types/installconfig.go @@ -15,6 +15,7 @@ import ( "github.com/openshift/installer/pkg/types/none" "github.com/openshift/installer/pkg/types/openstack" "github.com/openshift/installer/pkg/types/ovirt" + "github.com/openshift/installer/pkg/types/powervs" "github.com/openshift/installer/pkg/types/vsphere" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -39,6 +40,7 @@ var ( ibmcloud.Name, openstack.Name, ovirt.Name, + powervs.Name, vsphere.Name, } // HiddenPlatformNames is a slice with all the @@ -148,6 +150,7 @@ type InstallConfig struct { // GCP: "Mint", "Passthrough", "Manual" // IBMCloud: "Manual" // AlibabaCloud: "Manual" + // PowerVS: "Manual" // +optional CredentialsMode CredentialsMode `json:"credentialsMode,omitempty"` @@ -205,6 +208,10 @@ type Platform struct { // +optional OpenStack *openstack.Platform `json:"openstack,omitempty"` + // PowerVS is the configuration used when installing on Power VS. + // +optional + PowerVS *powervs.Platform `json:"powervs,omitempty"` + // VSphere is the configuration used when installing on vSphere. // +optional VSphere *vsphere.Platform `json:"vsphere,omitempty"` @@ -243,6 +250,8 @@ func (p *Platform) Name() string { return vsphere.Name case p.Ovirt != nil: return ovirt.Name + case p.PowerVS != nil: + return powervs.Name default: return "" } diff --git a/pkg/types/machinepools.go b/pkg/types/machinepools.go index e37ee8eba4f..f3ea6602293 100644 --- a/pkg/types/machinepools.go +++ b/pkg/types/machinepools.go @@ -10,6 +10,7 @@ import ( "github.com/openshift/installer/pkg/types/libvirt" "github.com/openshift/installer/pkg/types/openstack" "github.com/openshift/installer/pkg/types/ovirt" + "github.com/openshift/installer/pkg/types/powervs" "github.com/openshift/installer/pkg/types/vsphere" ) @@ -100,6 +101,9 @@ type MachinePoolPlatform struct { // Ovirt is the configuration used when installing on oVirt. Ovirt *ovirt.MachinePool `json:"ovirt,omitempty"` + + // PowerVS is the configuration used when installing on IBM Power VS. + PowerVS *powervs.MachinePool `json:"powervs,omitempty"` } // Name returns a string representation of the platform (e.g. "aws" if @@ -129,6 +133,8 @@ func (p *MachinePoolPlatform) Name() string { return vsphere.Name case p.Ovirt != nil: return ovirt.Name + case p.PowerVS != nil: + return powervs.Name default: return "" } diff --git a/pkg/types/powervs/OWNERS b/pkg/types/powervs/OWNERS new file mode 100644 index 00000000000..4f3d2491746 --- /dev/null +++ b/pkg/types/powervs/OWNERS @@ -0,0 +1,7 @@ +# See the OWNERS docs: https://git.k8s.io/community/contributors/guide/owners.md +# This file just uses aliases defined in OWNERS_ALIASES. + +approvers: + - powervs-approvers +reviewers: + - powervs-reviewers diff --git a/pkg/types/powervs/defaults/platform.go b/pkg/types/powervs/defaults/platform.go new file mode 100644 index 00000000000..86d8cda6730 --- /dev/null +++ b/pkg/types/powervs/defaults/platform.go @@ -0,0 +1,9 @@ +package defaults + +import ( + "github.com/openshift/installer/pkg/types/powervs" +) + +// SetPlatformDefaults sets the defaults for the platform. +func SetPlatformDefaults(p *powervs.Platform) { +} diff --git a/pkg/types/powervs/doc.go b/pkg/types/powervs/doc.go new file mode 100644 index 00000000000..7f0864590c0 --- /dev/null +++ b/pkg/types/powervs/doc.go @@ -0,0 +1,6 @@ +// Package powervs contains Power VS-specific structures for installer +// configuration and management. +package powervs + +// Name is name for the Power VS platform. +const Name string = "powervs" diff --git a/pkg/types/powervs/machinepools.go b/pkg/types/powervs/machinepools.go new file mode 100644 index 00000000000..ce8ef6d22eb --- /dev/null +++ b/pkg/types/powervs/machinepools.go @@ -0,0 +1,65 @@ +package powervs + +// ProcType defines valid types for a ppc64le processor in Power VS +// +kubebuilder:validation:Enum="";capped;dedicated;shared +type ProcType string + +// Capped type for capped processor consumption +const Capped ProcType = "capped" + +// Dedicated type for dedicated processor(s) +const Dedicated ProcType = "dedicated" + +// Shared type shared type for shared processor(s) +const Shared ProcType = "shared" + +// MachinePool stores the configuration for a machine pool installed on IBM Power VS. +type MachinePool struct { + // VolumeIDs is the list of volumes attached to the instance. + // + // +optional + VolumeIDs []string `json:"volumeIDs,omitempty"` + + // Memory defines the memory in GB for the instance. + // + // +optional + Memory string `json:"memory,omitempty"` + + // Processors defines the processing units for the instance. + // + // +optional + Processors string `json:"processors,omitempty"` + + // ProcType defines the processor sharing model for the instance. + // Must be one of {capped, dedicated, shared}. + // + // +optional + ProcType ProcType `json:"procType,omitempty"` + + // SysType defines the system type for instance. + // + // +optional + SysType string `json:"sysType,omitempty"` +} + +// Set stores values from required into a +func (a *MachinePool) Set(required *MachinePool) { + if required == nil || a == nil { + return + } + if len(required.VolumeIDs) != 0 { + a.VolumeIDs = required.VolumeIDs + } + if required.Memory != "" { + a.Memory = required.Memory + } + if required.Processors != "" { + a.Processors = required.Processors + } + if required.ProcType != "" { + a.ProcType = required.ProcType + } + if required.SysType != "" { + a.SysType = required.SysType + } +} diff --git a/pkg/types/powervs/metadata.go b/pkg/types/powervs/metadata.go new file mode 100644 index 00000000000..ef67610ab97 --- /dev/null +++ b/pkg/types/powervs/metadata.go @@ -0,0 +1,8 @@ +package powervs + +// Metadata contains Power VS metadata (e.g. for uninstalling the cluster). +type Metadata struct { + CISInstanceCRN string `json:"cisInstanceCRN"` + Region string `json:"region"` + Zone string `json:"zone"` +} diff --git a/pkg/types/powervs/platform.go b/pkg/types/powervs/platform.go new file mode 100644 index 00000000000..1b2df39777b --- /dev/null +++ b/pkg/types/powervs/platform.go @@ -0,0 +1,57 @@ +package powervs + +// Platform stores all the global configuration that all machinesets +// use. +type Platform struct { + + // ServiceInstanceID is the ID of the Power IAAS instance created from the IBM Cloud Catalog + ServiceInstanceID string `json:"serviceInstanceID"` + + // PowerVSResourceGroup is the resource group in which Power VS resources will be created. + PowerVSResourceGroup string `json:"powervsResourceGroup"` + + // Region specifies the IBM Cloud colo region where the cluster will be created. + Region string `json:"region"` + + // Zone specifies the IBM Cloud colo region where the cluster will be created. + // At this time, only single-zone clusters are supported. + Zone string `json:"zone"` + + // VPCRegion specifies the IBM Cloud region in which to create VPC resources. + // Leave unset to allow installer to select the closest VPC region. + // + // +optional + VPCRegion string `json:"vpcRegion,omitempty"` + + // UserID is the login for the user's IBM Cloud account. + UserID string `json:"userID"` + + // VPC is a VPC inside IBM Cloud. Needed in order to create VPC Load Balancers. + // + // +optional + VPC string `json:"vpc,omitempty"` + + // Subnets specifies existing subnets (by ID) where cluster + // resources will be created. Leave unset to have the installer + // create subnets in a new VPC on your behalf. + // + // +optional + Subnets []string `json:"subnets,omitempty"` + + // PVSNetworkName specifies an existing network within the Power VS Service Instance. + // + // +optional + PVSNetworkName string `json:"pvsNetworkName,omitempty"` + + // ClusterOSImage is a pre-created Power VS boot image that overrides the + // default image for cluster nodes. + // + // +optional + ClusterOSImage string `json:"clusterOSImage,omitempty"` + + // DefaultMachinePlatform is the default configuration used when + // installing on Power VS for machine pools which do not define their own + // platform configuration. + // +optional + DefaultMachinePlatform *MachinePool `json:"defaultMachinePlatform,omitempty"` +} diff --git a/pkg/types/powervs/powervs_regions.go b/pkg/types/powervs/powervs_regions.go new file mode 100644 index 00000000000..9652865371a --- /dev/null +++ b/pkg/types/powervs/powervs_regions.go @@ -0,0 +1,67 @@ +package powervs + +// Since there is no API to query these, we have to hard-code them here. + +// Region describes resources associated with a region in Power VS. +// We're using a few items from the IBM Cloud VPC offering. The region names +// for VPC are different so another function of this is to correlate those. +type Region struct { + Description string + VPCRegion string + Zones []string +} + +// Regions holds the regions for IBM Power VS, and descriptions used during the survey +var Regions = map[string]Region{ + "dal": { + Description: "Dallas, USA", + VPCRegion: "us-south", + Zones: []string{"dal12"}, + }, + "eu-de": { + Description: "Frankfurt, Germany", + VPCRegion: "eu-de", + Zones: []string{ + "eu-de-1", + "eu-de-2", + }, + }, + "lon": { + Description: "London, UK.", + VPCRegion: "eu-gb", + Zones: []string{ + "lon04", + "lon06", + }, + }, + "osa": { + Description: "Osaka, Japan", + VPCRegion: "jp-osa", + Zones: []string{"osa21"}, + }, + "syd": { + Description: "Sydney, Australia", + VPCRegion: "au-syd", + Zones: []string{"syd04"}, + }, + "sao": { + Description: "São Paulo, Brazil", + VPCRegion: "br-sao", + Zones: []string{"sao01"}, + }, + "tor": { + Description: "Toronto, Canada", + VPCRegion: "ca-tor", + Zones: []string{"tor01"}, + }, + "tok": { + Description: "Tokyo, Japan", + VPCRegion: "jp-tok", + Zones: []string{"tok04"}, + }, + "us-east": { + Description: "Washington DC, USA", + VPCRegion: "us-east", + Zones: []string{"us-east"}, + }, +} diff --git a/pkg/types/powervs/validation/machinepool.go b/pkg/types/powervs/validation/machinepool.go new file mode 100644 index 00000000000..cb445c90151 --- /dev/null +++ b/pkg/types/powervs/validation/machinepool.go @@ -0,0 +1,69 @@ +package validation + +import ( + "math" + "regexp" + "strconv" + + "github.com/google/uuid" + "github.com/openshift/installer/pkg/types/powervs" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +// ValidateMachinePool checks that the specified machine pool is valid. +func ValidateMachinePool(p *powervs.MachinePool, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + // Validate VolumeIDs + for i, volumeID := range p.VolumeIDs { + _, err := uuid.Parse(volumeID) + if err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("volumeIDs").Index(i), volumeID, "volume ID must be a valid UUID")) + } + } + + // Validate Memory + if p.Memory != "" { + memory, err := strconv.ParseInt(p.Memory, 10, 64) + if err == nil { + if memory < 2 || memory > 64 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("memory"), p.Memory, "memory must be an integer number of GB that is at least 2 and no more than 64")) + } + } else { + allErrs = append(allErrs, field.Invalid(fldPath.Child("memory"), p.Memory, "memory must be an integer number of GB that is at least 2 and no more than 64")) + } + } + + // Validate Processors + if p.Processors != "" { + processors, err := strconv.ParseFloat(p.Processors, 64) + if err == nil { + if processors < 0.25 || processors > 32 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("processors"), p.Processors, "number of processors must be from .25 to 32 cores")) + } + if math.Mod(processors*1000, 2) != 0 || math.Mod(processors*100, 25) != 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("processors"), p.Processors, "processors must be in increments of .25")) + } + } else { + allErrs = append(allErrs, field.Invalid(fldPath.Child("processors"), p.Processors, "processors must be a valid floating point number")) + } + } + // Validate ProcType + if p.ProcType != "" { + procTypes := sets.NewString("shared", "dedicated", "capped") + if !procTypes.Has(string(p.ProcType)) { + allErrs = append(allErrs, field.NotSupported(fldPath.Child("procType"), p.ProcType, procTypes.List())) + } + } + + // Validate SysType + if p.SysType != "" { + const sysTypeRegex = `^(?:e980|s922(-.*|))$` + // Allowing for a staging-only pattern of s922-* but not exposing here + if !regexp.MustCompile(sysTypeRegex).MatchString(p.SysType) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("sysType"), p.SysType, "system type must be one of {e980,s922}")) + } + } + return allErrs +} diff --git a/pkg/types/powervs/validation/machinepool_test.go b/pkg/types/powervs/validation/machinepool_test.go new file mode 100644 index 00000000000..2eaed9ca4e6 --- /dev/null +++ b/pkg/types/powervs/validation/machinepool_test.go @@ -0,0 +1,132 @@ +package validation + +import ( + "testing" + + "github.com/openshift/installer/pkg/types/powervs" + "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +func TestValidateMachinePool(t *testing.T) { + cases := []struct { + name string + pool *powervs.MachinePool + expected string + }{ + { + name: "empty", + pool: &powervs.MachinePool{}, + }, + { + name: "valid volumeIDs", + pool: &powervs.MachinePool{ + VolumeIDs: []string{"c8b709c4-93f1-499e-915e-0820bcc51406", "587c5788-107f-4351-aabc-1652c54c4491"}, + }, + }, + { + name: "invalid volumeIDs", + pool: &powervs.MachinePool{ + VolumeIDs: []string{"c8b709c4-93f1-499e-915e-0820bcc51406", "abc123"}, + }, + expected: `^test-path\.volumeIDs\[1]: Invalid value: "abc123": volume ID must be a valid UUID$`, + }, + { + name: "valid memory", + pool: &powervs.MachinePool{ + Memory: "5", + }, + }, + { + name: "invalid memory under", + pool: &powervs.MachinePool{ + Memory: "1", + }, + expected: `^test-path\.memory: Invalid value: "1": memory must be an integer number of GB that is at least 2 and no more than 64$`, + }, + { + name: "invalid memory over", + pool: &powervs.MachinePool{ + Memory: "65", + }, + expected: `^test-path\.memory: Invalid value: "65": memory must be an integer number of GB that is at least 2 and no more than 64$`, + }, + { + name: "invalid memory string", + pool: &powervs.MachinePool{ + Memory: "all", + }, + expected: `^test-path\.memory: Invalid value: "all": memory must be an integer number of GB that is at least 2 and no more than 64$`, + }, + { + name: "valid processors", + pool: &powervs.MachinePool{ + Processors: "1.25", + }, + }, + { + name: "invalid processors under", + pool: &powervs.MachinePool{ + Processors: "0", + }, + expected: `^test-path\.processors: Invalid value: "0": number of processors must be from \.25 to 32 cores$`, + }, + { + name: "invalid processors over", + pool: &powervs.MachinePool{ + Processors: "33", + }, + expected: `^test-path\.processors: Invalid value: "33": number of processors must be from \.25 to 32 cores$`, + }, + { + name: "invalid processors string", + pool: &powervs.MachinePool{ + Processors: "all", + }, + expected: `^test-path\.processors: Invalid value: "all": processors must be a valid floating point number$`, + }, + { + name: "invalid processors increment", + pool: &powervs.MachinePool{ + Processors: "1.33", + }, + expected: `^test-path\.processors: Invalid value: "1\.33": processors must be in increments of \.25$`, + }, + { + name: "valid procType", + pool: &powervs.MachinePool{ + ProcType: "shared", + }, + }, + { + name: "invalid procType", + pool: &powervs.MachinePool{ + ProcType: "none", + }, + expected: `^test-path\.procType: Unsupported value: "none": supported values: "capped", "dedicated", "shared"$`, + }, + { + name: "valid sysType", + pool: &powervs.MachinePool{ + SysType: "s922", + }, + }, + { + name: "invalid sysType", + pool: &powervs.MachinePool{ + SysType: "p922", + }, + expected: `^test-path\.sysType: Invalid value: "p922": system type must be one of {e980,s922}$`, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + err := ValidateMachinePool(tc.pool, field.NewPath("test-path")).ToAggregate() + if tc.expected == "" { + assert.NoError(t, err) + } else { + assert.Regexp(t, tc.expected, err) + } + }) + } +} diff --git a/pkg/types/powervs/validation/platform.go b/pkg/types/powervs/validation/platform.go new file mode 100644 index 00000000000..14fd8406bb5 --- /dev/null +++ b/pkg/types/powervs/validation/platform.go @@ -0,0 +1,18 @@ +package validation + +import ( + "k8s.io/apimachinery/pkg/util/validation/field" + + "github.com/openshift/installer/pkg/types/powervs" +) + +// ValidatePlatform checks that the specified platform is valid. +func ValidatePlatform(p *powervs.Platform, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if p.Region == "" { + allErrs = append(allErrs, field.Required(fldPath.Child("region"), "region must be specified")) + } + + return allErrs +} diff --git a/pkg/types/powervs/validation/platform_test.go b/pkg/types/powervs/validation/platform_test.go new file mode 100644 index 00000000000..4cda1e09111 --- /dev/null +++ b/pkg/types/powervs/validation/platform_test.go @@ -0,0 +1,12 @@ +package validation + +import ( + "testing" + + _ "github.com/stretchr/testify/assert" + _ "k8s.io/apimachinery/pkg/util/validation/field" + + _ "github.com/openshift/installer/pkg/types/powervs" +) + +func TestValidatePlatform(t *testing.T) {} diff --git a/pkg/types/validation/installconfig.go b/pkg/types/validation/installconfig.go index 3ebabed8611..f16241ffec2 100644 --- a/pkg/types/validation/installconfig.go +++ b/pkg/types/validation/installconfig.go @@ -39,6 +39,8 @@ import ( openstackvalidation "github.com/openshift/installer/pkg/types/openstack/validation" "github.com/openshift/installer/pkg/types/ovirt" ovirtvalidation "github.com/openshift/installer/pkg/types/ovirt/validation" + "github.com/openshift/installer/pkg/types/powervs" + powervsvalidation "github.com/openshift/installer/pkg/types/powervs/validation" "github.com/openshift/installer/pkg/types/vsphere" vspherevalidation "github.com/openshift/installer/pkg/types/vsphere/validation" "github.com/openshift/installer/pkg/validate" @@ -487,6 +489,9 @@ func validatePlatform(platform *types.Platform, fldPath *field.Path, network *ty return openstackvalidation.ValidatePlatform(platform.OpenStack, network, f, c) }) } + if platform.PowerVS != nil { + validate(powervs.Name, platform.PowerVS, func(f *field.Path) field.ErrorList { return powervsvalidation.ValidatePlatform(platform.PowerVS, f) }) + } if platform.VSphere != nil { validate(vsphere.Name, platform.VSphere, func(f *field.Path) field.ErrorList { return vspherevalidation.ValidatePlatform(platform.VSphere, f) }) } @@ -620,6 +625,7 @@ func validateCloudCredentialsMode(mode types.CredentialsMode, fldPath *field.Pat azure.Name: allowedAzureModes, gcp.Name: {types.MintCredentialsMode, types.PassthroughCredentialsMode, types.ManualCredentialsMode}, ibmcloud.Name: {types.ManualCredentialsMode}, + powervs.Name: {types.ManualCredentialsMode}, } if validModes, ok := validPlatformCredentialsModes[platform.Name()]; ok { validModesSet := sets.NewString() diff --git a/pkg/types/validation/installconfig_test.go b/pkg/types/validation/installconfig_test.go index 848982f402d..d70c1b61fcc 100644 --- a/pkg/types/validation/installconfig_test.go +++ b/pkg/types/validation/installconfig_test.go @@ -22,6 +22,7 @@ import ( "github.com/openshift/installer/pkg/types/none" "github.com/openshift/installer/pkg/types/openstack" "github.com/openshift/installer/pkg/types/ovirt" + "github.com/openshift/installer/pkg/types/powervs" "github.com/openshift/installer/pkg/types/vsphere" ) @@ -86,6 +87,12 @@ func validIBMCloudPlatform() *ibmcloud.Platform { } } +func validPowerVSPlatform() *powervs.Platform { + return &powervs.Platform{ + Region: "us-south", + } +} + func validLibvirtPlatform() *libvirt.Platform { return &libvirt.Platform{ URI: "qemu+tcp://192.168.122.1/system", @@ -524,7 +531,7 @@ func TestValidateInstallConfig(t *testing.T) { c.Platform = types.Platform{} return c }(), - expectedError: `^platform: Invalid value: "": must specify one of the platforms \(alibabacloud, aws, azure, baremetal, gcp, ibmcloud, none, openstack, ovirt, vsphere\)$`, + expectedError: `^platform: Invalid value: "": must specify one of the platforms \(alibabacloud, aws, azure, baremetal, gcp, ibmcloud, none, openstack, ovirt, powervs, vsphere\)$`, }, { name: "multiple platforms", @@ -555,7 +562,7 @@ func TestValidateInstallConfig(t *testing.T) { } return c }(), - expectedError: `^platform: Invalid value: "libvirt": must specify one of the platforms \(alibabacloud, aws, azure, baremetal, gcp, ibmcloud, none, openstack, ovirt, vsphere\)$`, + expectedError: `^platform: Invalid value: "libvirt": must specify one of the platforms \(alibabacloud, aws, azure, baremetal, gcp, ibmcloud, none, openstack, ovirt, powervs, vsphere\)$`, }, { name: "invalid libvirt platform", @@ -567,7 +574,7 @@ func TestValidateInstallConfig(t *testing.T) { c.Platform.Libvirt.URI = "" return c }(), - expectedError: `^\[platform: Invalid value: "libvirt": must specify one of the platforms \(alibabacloud, aws, azure, baremetal, gcp, ibmcloud, none, openstack, ovirt, vsphere\), platform\.libvirt\.uri: Invalid value: "": invalid URI "" \(no scheme\)]$`, + expectedError: `^\[platform: Invalid value: "libvirt": must specify one of the platforms \(alibabacloud, aws, azure, baremetal, gcp, ibmcloud, none, openstack, ovirt, powervs, vsphere\), platform\.libvirt\.uri: Invalid value: "": invalid URI "" \(no scheme\)]$`, }, { name: "valid none platform", @@ -1073,6 +1080,50 @@ func TestValidateInstallConfig(t *testing.T) { }(), expectedError: `^\Qplatform.ibmcloud.region: Required value: region must be specified\E$`, }, + { + name: "valid powervs platform", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.Platform = types.Platform{ + PowerVS: validPowerVSPlatform(), + } + return c + }(), + }, + { + name: "valid powervs platform manual credential mod", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.Platform = types.Platform{ + PowerVS: validPowerVSPlatform(), + } + c.CredentialsMode = types.ManualCredentialsMode + return c + }(), + }, + { + name: "invalid powervs platform mint credential mod", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.Platform = types.Platform{ + PowerVS: validPowerVSPlatform(), + } + c.CredentialsMode = types.MintCredentialsMode + return c + }(), + expectedError: `^credentialsMode: Unsupported value: "Mint": supported values: "Manual"$`, + }, + { + name: "invalid powervs platform", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.Platform = types.Platform{ + PowerVS: &powervs.Platform{}, + } + return c + }(), + expectedError: `^\Qplatform.powervs.region: Required value: region must be specified\E$`, + }, { name: "valid azurestack platform", installConfig: func() *types.InstallConfig { diff --git a/pkg/types/validation/machinepools.go b/pkg/types/validation/machinepools.go index 48ec1ad2d6e..b826841781b 100644 --- a/pkg/types/validation/machinepools.go +++ b/pkg/types/validation/machinepools.go @@ -22,6 +22,8 @@ import ( openstackvalidation "github.com/openshift/installer/pkg/types/openstack/validation" "github.com/openshift/installer/pkg/types/ovirt" ovirtvalidation "github.com/openshift/installer/pkg/types/ovirt/validation" + "github.com/openshift/installer/pkg/types/powervs" + powervsvalidation "github.com/openshift/installer/pkg/types/powervs/validation" "github.com/openshift/installer/pkg/types/vsphere" vspherevalidation "github.com/openshift/installer/pkg/types/vsphere/validation" ) @@ -119,6 +121,9 @@ func validateMachinePoolPlatform(platform *types.Platform, p *types.MachinePoolP if p.Ovirt != nil { validate(ovirt.Name, p.Ovirt, func(f *field.Path) field.ErrorList { return ovirtvalidation.ValidateMachinePool(p.Ovirt, f) }) } + if p.PowerVS != nil { + validate(powervs.Name, p.PowerVS, func(f *field.Path) field.ErrorList { return powervsvalidation.ValidateMachinePool(p.PowerVS, f) }) + } if p.OpenStack != nil { validate(openstack.Name, p.OpenStack, func(f *field.Path) field.ErrorList { return openstackvalidation.ValidateMachinePool(platform.OpenStack, p.OpenStack, pool.Name, f)