diff --git a/data/data/gcp/main.tf b/data/data/gcp/main.tf index 97ac34cb692..3a787c60fca 100644 --- a/data/data/gcp/main.tf +++ b/data/data/gcp/main.tf @@ -104,4 +104,6 @@ resource "google_compute_image" "cluster" { raw_disk { source = var.gcp_image_uri } + + licenses = var.gcp_image_licenses } diff --git a/data/data/gcp/variables-gcp.tf b/data/data/gcp/variables-gcp.tf index 03bd087125c..45262bf25bf 100644 --- a/data/data/gcp/variables-gcp.tf +++ b/data/data/gcp/variables-gcp.tf @@ -97,3 +97,10 @@ variable "gcp_publish_strategy" { type = string description = "The cluster publishing strategy, either Internal or External" } + +variable "gcp_image_licenses" { + type = list(string) + description = "The licenses to use when creating compute instances" + default = [] +} + diff --git a/docs/user/gcp/customization.md b/docs/user/gcp/customization.md index 74bf2467055..a4ebfea2ef9 100644 --- a/docs/user/gcp/customization.md +++ b/docs/user/gcp/customization.md @@ -8,6 +8,7 @@ Beyond the [platform-agnostic `install-config.yaml` properties](../customization * `controlPlaneSubnet` (optional string): The name of an existing GCP subnet which should be used by the cluster control plane. * `computeSubnet` (optional string): The name of an existing GCP subnet which should be used by the cluster nodes. * `defaultMachinePlatform` (optional object): Default [GCP-specific machine pool properties](#machine-pools) which apply to [machine pools](../customization.md#machine-pools) that do not define their own GCP-specific properties. +* `licenses` (optional list of strings): A list of license URLs (https) that should be applied to the compute images (as defined in [the API][compute-images]). The use of this property in combination with any mechanism that results in using pre-built images (such as the current OPENSHIFT_INSTALL_OS_IMAGE_OVERRIDE) is forbidden. Also, note that use of these URLs will force the installer to copy the source image before being used. An example of this license is the one that enables [nested virtualization][gcp-nested]. A full list of available licenses can be retrieved using [the license API][license-api]. ## Machine pools @@ -115,4 +116,22 @@ pullSecret: '{"auths": ...}' sshKey: ssh-ed25519 AAAA... ``` +### Nested virtualization + +An example GCP install config enabling [GCP's nested virtualization license][gcp-nested]: + +```yaml +apiVersion: v1 +baseDomain: example.com +platform: + gcp: + projectID: example-project + region: us-east1 + licenses: + - https://compute.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx +``` + [machine-type]: https://cloud.google.com/compute/docs/machine-types +[compute-images]: https://cloud.google.com/compute/docs/reference/rest/v1/images +[gcp-nested]: https://cloud.google.com/compute/docs/instances/enable-nested-virtualization-vm-instances +[license-api]: https://cloud.google.com/compute/docs/reference/rest/v1/licenses/list diff --git a/pkg/asset/cluster/tfvars.go b/pkg/asset/cluster/tfvars.go index e57f56fcb58..53b5188da16 100644 --- a/pkg/asset/cluster/tfvars.go +++ b/pkg/asset/cluster/tfvars.go @@ -332,6 +332,7 @@ func (t *TerraformVariables) Generate(parents asset.Parents) error { MasterConfigs: masterConfigs, WorkerConfigs: workerConfigs, ImageURI: string(*rhcosImage), + ImageLicenses: installConfig.Config.GCP.Licenses, PublicZoneName: publicZoneName, PublishStrategy: installConfig.Config.Publish, PreexistingNetwork: preexistingnetwork, diff --git a/pkg/tfvars/gcp/gcp.go b/pkg/tfvars/gcp/gcp.go index ae24722f78f..01bd7c55413 100644 --- a/pkg/tfvars/gcp/gcp.go +++ b/pkg/tfvars/gcp/gcp.go @@ -21,6 +21,7 @@ type config struct { MasterInstanceType string `json:"gcp_master_instance_type,omitempty"` MasterAvailabilityZones []string `json:"gcp_master_availability_zones"` ImageURI string `json:"gcp_image_uri,omitempty"` + ImageLicenses []string `json:"gcp_image_licenses,omitempty"` VolumeType string `json:"gcp_master_root_volume_type"` VolumeSize int64 `json:"gcp_master_root_volume_size"` PublicZoneName string `json:"gcp_public_dns_zone_name,omitempty"` @@ -35,6 +36,7 @@ type config struct { type TFVarsSources struct { Auth Auth ImageURI string + ImageLicenses []string `json:"gcp_image_licenses,omitempty"` MasterConfigs []*gcpprovider.GCPMachineProviderSpec WorkerConfigs []*gcpprovider.GCPMachineProviderSpec PublicZoneName string @@ -59,6 +61,7 @@ func TFVars(sources TFVarsSources) ([]byte, error) { VolumeType: masterConfig.Disks[0].Type, VolumeSize: masterConfig.Disks[0].SizeGb, ImageURI: sources.ImageURI, + ImageLicenses: sources.ImageLicenses, PublicZoneName: sources.PublicZoneName, PublishStrategy: string(sources.PublishStrategy), ClusterNetwork: masterConfig.NetworkInterfaces[0].Network, diff --git a/pkg/types/gcp/platform.go b/pkg/types/gcp/platform.go index 3c497b54440..01ece511a0d 100644 --- a/pkg/types/gcp/platform.go +++ b/pkg/types/gcp/platform.go @@ -29,4 +29,12 @@ type Platform struct { // The value should be the name of the subnet. // +optional ComputeSubnet string `json:"computeSubnet,omitempty"` + + // Licenses is a list of licenses to apply to the compute images + // The value should a list of strings (https URLs only) representing the license keys. + // When set, this will cause the installer to copy the image into user's project. + // This option is incompatible with any mechanism that makes use of pre-built images + // such as the current env OPENSHIFT_INSTALL_OS_IMAGE_OVERRIDE + // +optional + Licenses []string `json:"licenses,omitempty"` } diff --git a/pkg/types/gcp/validation/platform.go b/pkg/types/gcp/validation/platform.go index 428f07c3ab3..8136fc5670e 100644 --- a/pkg/types/gcp/validation/platform.go +++ b/pkg/types/gcp/validation/platform.go @@ -1,11 +1,15 @@ package validation import ( + "os" + "sort" "k8s.io/apimachinery/pkg/util/validation/field" "github.com/openshift/installer/pkg/types/gcp" + + "github.com/openshift/installer/pkg/validate" ) var ( @@ -69,5 +73,15 @@ func ValidatePlatform(p *gcp.Platform, fldPath *field.Path) field.ErrorList { allErrs = append(allErrs, field.Required(fldPath.Child("network"), "must provide a VPC network when supplying subnets")) } + if oi, ok := os.LookupEnv("OPENSHIFT_INSTALL_OS_IMAGE_OVERRIDE"); ok && oi != "" && len(p.Licenses) > 0 { + allErrs = append(allErrs, field.Forbidden(fldPath.Child("licenses"), "the use of custom image licenses is forbidden if an OPENSHIFT_INSTALL_OS_IMAGE_OVERRIDE is specified")) + } + + for i, license := range p.Licenses { + if validate.URIWithProtocol(license, "https") != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("licenses").Index(i), license, "licenses must be URLs (https) only")) + } + } + return allErrs }