diff --git a/data/data/openstack/bootstrap/main.tf b/data/data/openstack/bootstrap/main.tf index da06ecbe521..7b6d8a0684d 100644 --- a/data/data/openstack/bootstrap/main.tf +++ b/data/data/openstack/bootstrap/main.tf @@ -109,11 +109,6 @@ EOF } } -data "openstack_images_image_v2" "bootstrap_image" { - name = var.image_name - most_recent = true -} - data "openstack_compute_flavor_v2" "bootstrap_flavor" { name = var.flavor_name } @@ -121,7 +116,7 @@ data "openstack_compute_flavor_v2" "bootstrap_flavor" { resource "openstack_compute_instance_v2" "bootstrap" { name = "${var.cluster_id}-bootstrap" flavor_id = data.openstack_compute_flavor_v2.bootstrap_flavor.id - image_id = data.openstack_images_image_v2.bootstrap_image.id + image_id = var.base_image_id user_data = data.ignition_config.redirect.rendered diff --git a/data/data/openstack/bootstrap/variables.tf b/data/data/openstack/bootstrap/variables.tf index abd0113bbf7..76aecd4b398 100644 --- a/data/data/openstack/bootstrap/variables.tf +++ b/data/data/openstack/bootstrap/variables.tf @@ -1,6 +1,6 @@ -variable "image_name" { +variable "base_image_id" { type = string - description = "The name of the Glance image for the bootstrap node." + description = "The identifier of the Glance image for the bootstrap node." } variable "extra_tags" { diff --git a/data/data/openstack/main.tf b/data/data/openstack/main.tf index 8149215e35d..6eb9cbcf061 100644 --- a/data/data/openstack/main.tf +++ b/data/data/openstack/main.tf @@ -27,7 +27,7 @@ module "bootstrap" { cluster_id = var.cluster_id extra_tags = var.openstack_extra_tags - image_name = var.openstack_base_image + base_image_id = data.openstack_images_image_v2.base_image.id flavor_name = var.openstack_master_flavor_name ignition = var.ignition_bootstrap api_int_ip = var.openstack_api_int_ip @@ -42,7 +42,7 @@ module "bootstrap" { module "masters" { source = "./masters" - base_image = var.openstack_base_image + base_image_id = data.openstack_images_image_v2.base_image.id cluster_id = var.cluster_id flavor_name = var.openstack_master_flavor_name instance_count = var.master_count @@ -72,3 +72,21 @@ module "topology" { trunk_support = var.openstack_trunk_support octavia_support = var.openstack_octavia_support } + +resource "openstack_images_image_v2" "base_image" { + // we need to create a new image only if the base image url has been provided, plus base image name is -rhcos + count = var.openstack_base_image_url != "" && var.openstack_base_image_name == "${var.cluster_id}-rhcos" ? 1 : 0 + + name = var.openstack_base_image_name + image_source_url = var.openstack_base_image_url + container_format = "bare" + disk_format = "qcow2" + + tags = ["openshiftClusterID=${var.cluster_id}"] +} + +data "openstack_images_image_v2" "base_image" { + name = var.openstack_base_image_name + + depends_on = ["openstack_images_image_v2.base_image"] +} diff --git a/data/data/openstack/masters/main.tf b/data/data/openstack/masters/main.tf index ea60534791f..c39f450541d 100644 --- a/data/data/openstack/masters/main.tf +++ b/data/data/openstack/masters/main.tf @@ -1,8 +1,3 @@ -data "openstack_images_image_v2" "masters_img" { - name = var.base_image - most_recent = true -} - data "openstack_compute_flavor_v2" "masters_flavor" { name = var.flavor_name } @@ -38,7 +33,7 @@ resource "openstack_blockstorage_volume_v3" "master_volume" { size = var.root_volume_size volume_type = var.root_volume_type - image_id = data.openstack_images_image_v2.masters_img.id + image_id = var.base_image_id } resource "openstack_compute_instance_v2" "master_conf" { @@ -46,7 +41,7 @@ resource "openstack_compute_instance_v2" "master_conf" { count = var.instance_count flavor_id = data.openstack_compute_flavor_v2.masters_flavor.id - image_id = var.root_volume_size == null ? data.openstack_images_image_v2.masters_img.id : null + image_id = var.root_volume_size == null ? var.base_image_id : null security_groups = var.master_sg_ids user_data = element( data.ignition_config.master_ignition_config.*.rendered, diff --git a/data/data/openstack/masters/variables.tf b/data/data/openstack/masters/variables.tf index b8bfada44f4..7fdd06ff2bb 100644 --- a/data/data/openstack/masters/variables.tf +++ b/data/data/openstack/masters/variables.tf @@ -1,5 +1,6 @@ -variable "base_image" { - type = string +variable "base_image_id" { + type = string + description = "The identifier of the Glance image for master nodes." } variable "cluster_id" { diff --git a/data/data/openstack/variables-openstack.tf b/data/data/openstack/variables-openstack.tf index 70fc6246ba0..8d378ddf07c 100644 --- a/data/data/openstack/variables-openstack.tf +++ b/data/data/openstack/variables-openstack.tf @@ -10,12 +10,17 @@ variable "openstack_master_root_volume_size" { description = "The size of the volume in gigabytes for the root block device of master nodes." } -variable "openstack_base_image" { +variable "openstack_base_image_name" { type = string - default = "rhcos" description = "Name of the base image to use for the nodes." } +variable "openstack_base_image_url" { + type = string + default = "" + description = "URL of the base image to use for the nodes." +} + variable "openstack_credentials_auth_url" { type = string default = "" diff --git a/pkg/asset/cluster/tfvars.go b/pkg/asset/cluster/tfvars.go index 27c376df782..ecaacd5b64c 100644 --- a/pkg/asset/cluster/tfvars.go +++ b/pkg/asset/cluster/tfvars.go @@ -280,6 +280,8 @@ func (t *TerraformVariables) Generate(parents asset.Parents) error { ingressVIP.String(), installConfig.Config.Platform.OpenStack.TrunkSupport, installConfig.Config.Platform.OpenStack.OctaviaSupport, + string(*rhcosImage), + clusterID.InfraID, ) if err != nil { return errors.Wrapf(err, "failed to get %s Terraform variables", platform) diff --git a/pkg/asset/machines/master.go b/pkg/asset/machines/master.go index 53d3b731551..c03a38f9b37 100644 --- a/pkg/asset/machines/master.go +++ b/pkg/asset/machines/master.go @@ -35,6 +35,7 @@ import ( "github.com/openshift/installer/pkg/asset/machines/machineconfig" "github.com/openshift/installer/pkg/asset/machines/openstack" "github.com/openshift/installer/pkg/asset/rhcos" + rhcosutils "github.com/openshift/installer/pkg/rhcos" "github.com/openshift/installer/pkg/types" awstypes "github.com/openshift/installer/pkg/types/aws" awsdefaults "github.com/openshift/installer/pkg/types/aws/defaults" @@ -181,7 +182,10 @@ func (m *Master) Generate(dependencies asset.Parents) error { mpool.Set(ic.Platform.OpenStack.DefaultMachinePlatform) mpool.Set(pool.Platform.OpenStack) pool.Platform.OpenStack = &mpool - machines, err = openstack.Machines(clusterID.InfraID, ic, pool, string(*rhcosImage), "master", "master-user-data") + + imageName, _ := rhcosutils.GenerateOpenStackImageName(string(*rhcosImage), clusterID.InfraID) + + machines, err = openstack.Machines(clusterID.InfraID, ic, pool, imageName, "master", "master-user-data") if err != nil { return errors.Wrap(err, "failed to create master machine objects") } diff --git a/pkg/asset/machines/worker.go b/pkg/asset/machines/worker.go index 6344f791397..2ce42ac8ab6 100644 --- a/pkg/asset/machines/worker.go +++ b/pkg/asset/machines/worker.go @@ -35,6 +35,7 @@ import ( "github.com/openshift/installer/pkg/asset/machines/machineconfig" "github.com/openshift/installer/pkg/asset/machines/openstack" "github.com/openshift/installer/pkg/asset/rhcos" + rhcosutils "github.com/openshift/installer/pkg/rhcos" "github.com/openshift/installer/pkg/types" awstypes "github.com/openshift/installer/pkg/types/aws" awsdefaults "github.com/openshift/installer/pkg/types/aws/defaults" @@ -247,7 +248,9 @@ func (w *Worker) Generate(dependencies asset.Parents) error { mpool.Set(pool.Platform.OpenStack) pool.Platform.OpenStack = &mpool - sets, err := openstack.MachineSets(clusterID.InfraID, ic, &pool, string(*rhcosImage), "worker", "worker-user-data") + imageName, _ := rhcosutils.GenerateOpenStackImageName(string(*rhcosImage), clusterID.InfraID) + + sets, err := openstack.MachineSets(clusterID.InfraID, ic, &pool, imageName, "worker", "worker-user-data") if err != nil { return errors.Wrap(err, "failed to create master machine objects") } diff --git a/pkg/asset/rhcos/image.go b/pkg/asset/rhcos/image.go index 06418de4174..34e6035ca36 100644 --- a/pkg/asset/rhcos/image.go +++ b/pkg/asset/rhcos/image.go @@ -78,7 +78,7 @@ func osImage(config *types.InstallConfig) (string, error) { case libvirt.Name: osimage, err = rhcos.QEMU(ctx) case openstack.Name: - osimage = "rhcos" + osimage, err = rhcos.OpenStack(ctx) case azure.Name: osimage, err = rhcos.VHD(ctx) case baremetal.Name: diff --git a/pkg/rhcos/openstack.go b/pkg/rhcos/openstack.go index bf24a302c65..b201ecc6ef5 100644 --- a/pkg/rhcos/openstack.go +++ b/pkg/rhcos/openstack.go @@ -27,3 +27,17 @@ func OpenStack(ctx context.Context) (string, error) { return base.ResolveReference(relOpenStack).String(), nil } + +// GenerateOpenStackImageName returns Glance image name for instances. +func GenerateOpenStackImageName(rhcosImage, infraID string) (imageName string, isURL bool) { + // Here we check whether rhcosImage is a URL or not. If this is the first case, it means that Glance image + // should be created by the installer with the universal name "-rhcos". Otherwise, it means + // that we are given the name of the pre-created Glance image, which the installer should use for node + // provisioning. + _, err := url.ParseRequestURI(rhcosImage) + if err != nil { + return rhcosImage, false + } + + return infraID + "-rhcos", true +} diff --git a/pkg/tfvars/openstack/openstack.go b/pkg/tfvars/openstack/openstack.go index 29123db7fbb..2345fa685d4 100644 --- a/pkg/tfvars/openstack/openstack.go +++ b/pkg/tfvars/openstack/openstack.go @@ -4,11 +4,14 @@ package openstack import ( "encoding/json" + "github.com/openshift/installer/pkg/rhcos" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/apis/openstackproviderconfig/v1alpha1" ) type config struct { - BaseImage string `json:"openstack_base_image,omitempty"` + BaseImageName string `json:"openstack_base_image_name,omitempty"` + BaseImageURL string `json:"openstack_base_image_url,omitempty"` ExternalNetwork string `json:"openstack_external_network,omitempty"` Cloud string `json:"openstack_credentials_cloud,omitempty"` FlavorName string `json:"openstack_master_flavor_name,omitempty"` @@ -23,9 +26,8 @@ type config struct { } // TFVars generates OpenStack-specific Terraform variables. -func TFVars(masterConfig *v1alpha1.OpenstackProviderSpec, cloud string, externalNetwork string, lbFloatingIP string, apiVIP string, dnsVIP string, ingressVIP string, trunkSupport string, octaviaSupport string) ([]byte, error) { +func TFVars(masterConfig *v1alpha1.OpenstackProviderSpec, cloud string, externalNetwork string, lbFloatingIP string, apiVIP string, dnsVIP string, ingressVIP string, trunkSupport string, octaviaSupport string, baseImage string, infraID string) ([]byte, error) { cfg := &config{ - BaseImage: masterConfig.Image, ExternalNetwork: externalNetwork, Cloud: cloud, FlavorName: masterConfig.Flavor, @@ -36,6 +38,23 @@ func TFVars(masterConfig *v1alpha1.OpenstackProviderSpec, cloud string, external TrunkSupport: trunkSupport, OctaviaSupport: octaviaSupport, } + + // Normally baseImage contains a URL that we will use to create a new Glance image, but for testing + // purposes we also allow to set a custom Glance image name to skip the uploading. Here we check + // whether baseImage is a URL or not. If this is the first case, it means that the image should be + // created by the installer from the URL. Otherwise, it means that we are given the name of the pre-created + // Glance image, which we should use for instances. + imageName, isURL := rhcos.GenerateOpenStackImageName(baseImage, infraID) + cfg.BaseImageName = imageName + if isURL { + // Valid URL -> use baseImage as a URL that will be used to create new Glance image with name "-rhcos". + cfg.BaseImageURL = baseImage + } else { + // Not a URL -> use baseImage value as a Glance image name. + + // TODO(mfedosin): add validations that this image exists and there are no other images with this name. + } + if masterConfig.RootVolume != nil { cfg.RootVolumeSize = masterConfig.RootVolume.Size cfg.RootVolumeType = masterConfig.RootVolume.VolumeType