From 45de9b95c39f91c8949fde3f9a8120a040e4e752 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 7 Oct 2019 14:22:19 +0000 Subject: [PATCH 1/6] hack/update-rhcos-bootimage: Require ART endpoint Don't allow people to point to e.g. an RHT-internal endpoint. See: https://github.com/openshift/installer/pull/2462 --- hack/update-rhcos-bootimage.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hack/update-rhcos-bootimage.py b/hack/update-rhcos-bootimage.py index 3d07a2b23b6..97e84c1845d 100755 --- a/hack/update-rhcos-bootimage.py +++ b/hack/update-rhcos-bootimage.py @@ -1,15 +1,22 @@ #!/usr/bin/python3 -# Usage: ./hack/update-rhcos-bootimage.py https://releases-rhcos.svc.ci.openshift.org/storage/releases/ootpa/410.8.20190401.0/meta.json +# Usage: ./hack/update-rhcos-bootimage.py https://releases-art-rhcos.svc.ci.openshift.org/storage/releases/ootpa/410.8.20190401.0/meta.json import codecs,os,sys,json,argparse import urllib.parse import urllib.request +# An app running in the CI cluster exposes this public endpoint about ART RHCOS +# builds. Do not try to e.g. point to RHT-internal endpoints. +RHCOS_RELEASES_APP = 'https://releases-art-rhcos.svc.ci.openshift.org' + dn = os.path.abspath(os.path.dirname(sys.argv[0])) parser = argparse.ArgumentParser() parser.add_argument("meta", action='store') args = parser.parse_args() +if not args.meta.startswith(RHCOS_RELEASES_APP): + raise SystemExit("URL must start with: " + RHCOS_RELEASES_APP) + with urllib.request.urlopen(args.meta) as f: string_f = codecs.getreader('utf-8')(f) # support for Python < 3.6 meta = json.load(string_f) From efaeb0c06fdf8f9fcd1b8cb4187f22bfb78073dd Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 6 Jan 2020 15:39:38 -0800 Subject: [PATCH 2/6] hack: improve portability of bootimage script Not everyone has python3 installed into /usr/bin/. --- hack/update-rhcos-bootimage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/update-rhcos-bootimage.py b/hack/update-rhcos-bootimage.py index 97e84c1845d..604bbc399b5 100755 --- a/hack/update-rhcos-bootimage.py +++ b/hack/update-rhcos-bootimage.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # Usage: ./hack/update-rhcos-bootimage.py https://releases-art-rhcos.svc.ci.openshift.org/storage/releases/ootpa/410.8.20190401.0/meta.json import codecs,os,sys,json,argparse import urllib.parse From f283ffc566c5879e45a2e4630b03df7748dd214f Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 6 Jan 2020 15:44:23 -0800 Subject: [PATCH 3/6] hack: update format of rhcos build metadata This splits the RHCOS build metadata into architecture-specific files. This will allow the metadata to contain information about bootimages of multiple architectures. In order to preserve backward compatibility (there are a few users, including certain CI jobs, that pull rhcos.json from GitHub directly), I've opted to use separate files for each architecture. Normally, we could have just symlinked the legacy metadata file, but when hosted on raw.githubcontent.com, the symlinks aren't followed. When updating the RHCOS bootimages, this script will need to be run once for each architecture that is being updated. The build metadata was resynced with the following: ./hack/update-rhcos-bootimage.py https://releases-art-rhcos.svc.ci.openshift.org/art/storage/releases/rhcos-4.2/42.80.20191002.0/meta.json amd64 --- data/data/rhcos-amd64.json | 137 +++++++++++++++++++++++++++++++++ data/data/rhcos.json | 3 + hack/update-rhcos-bootimage.py | 18 ++++- 3 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 data/data/rhcos-amd64.json diff --git a/data/data/rhcos-amd64.json b/data/data/rhcos-amd64.json new file mode 100644 index 00000000000..e00293d5c00 --- /dev/null +++ b/data/data/rhcos-amd64.json @@ -0,0 +1,137 @@ +{ + "amis": { + "ap-northeast-1": { + "hvm": "ami-0426ca3481a088c7b" + }, + "ap-northeast-2": { + "hvm": "ami-014514ae47679721b" + }, + "ap-south-1": { + "hvm": "ami-0bd772ba746948d9a" + }, + "ap-southeast-1": { + "hvm": "ami-0d76ac0ebaac29e40" + }, + "ap-southeast-2": { + "hvm": "ami-0391e92574fb09e08" + }, + "ca-central-1": { + "hvm": "ami-04419691da69850cf" + }, + "eu-central-1": { + "hvm": "ami-092b69120ecf915ed" + }, + "eu-north-1": { + "hvm": "ami-0175e9c9d258cc11d" + }, + "eu-west-1": { + "hvm": "ami-04370efd78434697b" + }, + "eu-west-2": { + "hvm": "ami-00c74e593125e0096" + }, + "eu-west-3": { + "hvm": "ami-058ad17da14ff4d0d" + }, + "me-south-1": { + "hvm": "ami-04ab7423e63117ec0" + }, + "sa-east-1": { + "hvm": "ami-03f6b71e93e630dab" + }, + "us-east-1": { + "hvm": "ami-01e7fdcb66157b224" + }, + "us-east-2": { + "hvm": "ami-0bc59aaa7363b805d" + }, + "us-west-1": { + "hvm": "ami-0ba912f53c1fdcdf0" + }, + "us-west-2": { + "hvm": "ami-08e10b201e19fd5e7" + } + }, + "azure": { + "image": "rhcos-42.80.20191002.0.vhd", + "url": "https://rhcos.blob.core.windows.net/imagebucket/rhcos-42.80.20191002.0.vhd" + }, + "baseURI": "https://releases-art-rhcos.svc.ci.openshift.org/art/storage/releases/rhcos-4.2/42.80.20191002.0/", + "buildid": "42.80.20191002.0", + "gcp": { + "image": "rhcos-42-80-20191002-0", + "url": "https://storage.googleapis.com/rhcos/rhcos/42.80.20191002.0.tar.gz" + }, + "images": { + "aws": { + "path": "rhcos-42.80.20191002.0-aws.vmdk", + "sha256": "f5dbf8b4721609214101cda353789557de28caffd496b90c62ace46b9f5e0b9f", + "size": 710720458, + "uncompressed-sha256": "63545c92997f487217541e128eee4aa81ab068840e2f988acbe5848ff711388f", + "uncompressed-size": 725895168 + }, + "azure": { + "path": "rhcos-42.80.20191002.0.vhd", + "sha256": "7336694bcb936b7c95912ba49f9432adbe547837574ccd3e7c4b97a969c95a8c", + "size": 698788031, + "uncompressed-sha256": "1d8c227ec4f22822dfb3a132a764f2e64616e2cb9dd34290e87ff9b805a467fd", + "uncompressed-size": 1900518912 + }, + "gcp": { + "path": "rhcos-42.80.20191002.0-gcp.tar", + "sha256": "15434ee2b7d32368315519357dd0a036f67c7f9e9b987027512099b6b6d66aaf", + "size": 698392984 + }, + "initramfs": { + "path": "rhcos-42.80.20191002.0-installer-initramfs.img", + "sha256": "a975a0e5ebdbbd186a94fd2a2fdb122c88b228fd5a44c3118cf0b29f52f58995" + }, + "iso": { + "path": "rhcos-42.80.20191002.0-installer.iso", + "sha256": "649cdcf265bf39c0386331ec9b100da7c4800eae6c5aab4a8c28514b6efc5289" + }, + "kernel": { + "path": "rhcos-42.80.20191002.0-installer-kernel", + "sha256": "a31a100ad4ad033240ad8547f55a803542f7a6212adb7df1d0fb4618383b9729" + }, + "metal-bios": { + "path": "rhcos-42.80.20191002.0-metal-bios.raw.gz", + "sha256": "4de137e8e3e07f57fa6295b4f95be503f26835a782f9778607e12551669ee6bc", + "size": 700157452, + "uncompressed-sha256": "1ee6d0f9d931396ba089489646ad4a0046014cd59e08f38918ebe500b34e498a", + "uncompressed-size": 3182428160 + }, + "metal-uefi": { + "path": "rhcos-42.80.20191002.0-metal-uefi.raw.gz", + "sha256": "ee6c25c0eeb44e6016ed8604e2c302c7c8c940ef372277f62b23ccdad5a2225e", + "size": 699608952, + "uncompressed-sha256": "c1f2d7e12d5c2f96cd2d373111831e4aae85a6c2a1a9ce9ffb987678a11c39bd", + "uncompressed-size": 3182428160 + }, + "openstack": { + "path": "rhcos-42.80.20191002.0-openstack.qcow2", + "sha256": "b9225f67ae16ef38625c9669d556324d6422b29b5f6bfbd87e960062bfb37f70", + "size": 699870352, + "uncompressed-sha256": "414a90ca4fca60a7d25dee02f63d51ad5afc1a614e6a76135e26a1ec62ef40d1", + "uncompressed-size": 1911160832 + }, + "qemu": { + "path": "rhcos-42.80.20191002.0-qemu.qcow2", + "sha256": "ff8a785d749f5ef771f966e11c498e3eb214f9f82856e5f2d62e81f2f89062c9", + "size": 699873960, + "uncompressed-sha256": "3479ae1cdedc4dd983fba0e356ef2649d2c74bf97b71d818b1c4c31d16d6674b", + "uncompressed-size": 1911095296 + }, + "vmware": { + "path": "rhcos-42.80.20191002.0-vmware.ova", + "sha256": "91a729bde4512dd5d5a7ab060c482aea3ee48d56c7f5d8ce51f8596c8b7827bf", + "size": 725903360 + } + }, + "oscontainer": { + "digest": "sha256:cc71fbd134f063d9fc0ccc78933b89c8dd2b1418b7a7b85bb70de87bc80486d7", + "image": "quay.io/openshift-release-dev/ocp-v4.0-art-dev" + }, + "ostree-commit": "3f772530680edb70ded914f213c07cfa740f1d0ca351941af8e7e5b3e40b8451", + "ostree-version": "42.80.20191002.0" +} \ No newline at end of file diff --git a/data/data/rhcos.json b/data/data/rhcos.json index afe8a92b508..e00293d5c00 100644 --- a/data/data/rhcos.json +++ b/data/data/rhcos.json @@ -33,6 +33,9 @@ "eu-west-3": { "hvm": "ami-058ad17da14ff4d0d" }, + "me-south-1": { + "hvm": "ami-04ab7423e63117ec0" + }, "sa-east-1": { "hvm": "ami-03f6b71e93e630dab" }, diff --git a/hack/update-rhcos-bootimage.py b/hack/update-rhcos-bootimage.py index 604bbc399b5..0a622ed4357 100755 --- a/hack/update-rhcos-bootimage.py +++ b/hack/update-rhcos-bootimage.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Usage: ./hack/update-rhcos-bootimage.py https://releases-art-rhcos.svc.ci.openshift.org/storage/releases/ootpa/410.8.20190401.0/meta.json +# Usage: ./hack/update-rhcos-bootimage.py https://releases-art-rhcos.svc.ci.openshift.org/art/storage/releases/rhcos-4.2/42.80.20191002.0/meta.json amd64 import codecs,os,sys,json,argparse import urllib.parse import urllib.request @@ -8,12 +8,13 @@ # builds. Do not try to e.g. point to RHT-internal endpoints. RHCOS_RELEASES_APP = 'https://releases-art-rhcos.svc.ci.openshift.org' -dn = os.path.abspath(os.path.dirname(sys.argv[0])) - parser = argparse.ArgumentParser() parser.add_argument("meta", action='store') +parser.add_argument("arch", action='store', choices=['amd64']) args = parser.parse_args() +metadata_dir = os.path.join(os.path.dirname(sys.argv[0]), "../data/data") + if not args.meta.startswith(RHCOS_RELEASES_APP): raise SystemExit("URL must start with: " + RHCOS_RELEASES_APP) @@ -32,5 +33,14 @@ for entry in meta['amis'] } newmeta['baseURI'] = urllib.parse.urljoin(args.meta, '.') -with open(os.path.join(dn, "../data/data/rhcos.json"), 'w') as f: + +with open(os.path.join(metadata_dir, f"rhcos-{args.arch}.json"), 'w') as f: json.dump(newmeta, f, sort_keys=True, indent=4) + +# Continue to populate the legacy metadata file because there are still +# processes consuming this file directly. This normally could just be a symlink +# but some of these processes reference raw.githubusercontent.com which doesn't +# follow symlinks. +if args.arch == 'amd64': + with open(os.path.join(metadata_dir, "rhcos.json"), 'w') as f: + json.dump(newmeta, f, sort_keys=True, indent=4) From ceabdbe6731f4d8f43536c8f31b2c8ee5e098576 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 6 Jan 2020 15:42:33 -0800 Subject: [PATCH 4/6] *: add support for multi-arch rhcos image lookup This adds an architecture parameter to the RHCOS image lookup process and a corresponding field to MachinePool. This is a backward-compatible change, defaulting the architecture to AMD64 if none has been specified. This also enforces that the control plane and compute nodes share an architecture, since we don't support heterogeneous clusters today. --- docs/user/customization.md | 2 ++ pkg/asset/installconfig/installconfig_test.go | 6 ++++++ pkg/asset/manifests/operators_test.go | 14 +++++++++----- pkg/asset/rhcos/bootstrap_image.go | 2 +- pkg/asset/rhcos/image.go | 12 +++++++----- pkg/rhcos/ami.go | 6 ++++-- pkg/rhcos/azure.go | 6 ++++-- pkg/rhcos/builds.go | 16 +++++++++++++--- pkg/rhcos/gcp.go | 6 ++++-- pkg/rhcos/openstack.go | 6 ++++-- pkg/rhcos/qemu.go | 6 ++++-- pkg/types/defaults/machinepools.go | 3 +++ pkg/types/defaults/machinepools_test.go | 1 + pkg/types/machinepools.go | 12 ++++++++++++ pkg/types/validation/installconfig.go | 7 +++++-- pkg/types/validation/installconfig_test.go | 1 + pkg/types/validation/machinepools.go | 15 +++++++++++++++ pkg/types/validation/machinepools_test.go | 1 + 18 files changed, 96 insertions(+), 26 deletions(-) diff --git a/docs/user/customization.md b/docs/user/customization.md index 92bdb14d853..5b7367f67e5 100644 --- a/docs/user/customization.md +++ b/docs/user/customization.md @@ -65,6 +65,8 @@ For example, 10.0.0.0/16 represents IP addresses 10.0.0.0 through 10.0.255.255. The following machine-pool properties are available: +* `architecture` (optional string): Determines the instruction set architecture of the machines in the pool. Currently, heteregeneous clusters are not supported, so all pools must specify the same architecture. + Valid values are `amd64` (the default). * `hyperthreading` (optional string): Determines the mode of hyperthreading that machines in the pool will utalize. Valid values are `Enabled` (the default) and `Disabled`. * `name` (required string): The name of the machine pool. diff --git a/pkg/asset/installconfig/installconfig_test.go b/pkg/asset/installconfig/installconfig_test.go index ab9a6d524c0..a8ca17db530 100644 --- a/pkg/asset/installconfig/installconfig_test.go +++ b/pkg/asset/installconfig/installconfig_test.go @@ -79,12 +79,14 @@ func TestInstallConfigGenerate_FillsInDefaults(t *testing.T) { Name: "master", Replicas: pointer.Int64Ptr(3), Hyperthreading: types.HyperthreadingEnabled, + Architecture: types.ArchitectureAMD64, }, Compute: []types.MachinePool{ { Name: "worker", Replicas: pointer.Int64Ptr(3), Hyperthreading: types.HyperthreadingEnabled, + Architecture: types.ArchitectureAMD64, }, }, Platform: types.Platform{ @@ -140,12 +142,14 @@ pullSecret: "{\"auths\":{\"example.com\":{\"auth\":\"authorization value\"}}}" Name: "master", Replicas: pointer.Int64Ptr(3), Hyperthreading: types.HyperthreadingEnabled, + Architecture: types.ArchitectureAMD64, }, Compute: []types.MachinePool{ { Name: "worker", Replicas: pointer.Int64Ptr(3), Hyperthreading: types.HyperthreadingEnabled, + Architecture: types.ArchitectureAMD64, }, }, Platform: types.Platform{ @@ -221,12 +225,14 @@ network: Name: "master", Replicas: pointer.Int64Ptr(3), Hyperthreading: types.HyperthreadingEnabled, + Architecture: types.ArchitectureAMD64, }, Compute: []types.MachinePool{ { Name: "worker", Replicas: pointer.Int64Ptr(3), Hyperthreading: types.HyperthreadingEnabled, + Architecture: types.ArchitectureAMD64, }, }, Platform: types.Platform{ diff --git a/pkg/asset/manifests/operators_test.go b/pkg/asset/manifests/operators_test.go index 24500b54f23..eec7a29cc56 100644 --- a/pkg/asset/manifests/operators_test.go +++ b/pkg/asset/manifests/operators_test.go @@ -33,13 +33,15 @@ func TestRedactedInstallConfig(t *testing.T) { ServiceNetwork: []ipnet.IPNet{*ipnet.MustParseCIDR("1.2.3.4/5")}, }, ControlPlane: &types.MachinePool{ - Name: "control-plane", - Replicas: pointer.Int64Ptr(3), + Name: "control-plane", + Replicas: pointer.Int64Ptr(3), + Architecture: types.ArchitectureAMD64, }, Compute: []types.MachinePool{ { - Name: "compute", - Replicas: pointer.Int64Ptr(3), + Name: "compute", + Replicas: pointer.Int64Ptr(3), + Architecture: types.ArchitectureAMD64, }, }, Platform: types.Platform{ @@ -57,10 +59,12 @@ func TestRedactedInstallConfig(t *testing.T) { expectedConfig := createInstallConfig() expectedYaml := `baseDomain: test-domain compute: -- name: compute +- architecture: amd64 + name: compute platform: {} replicas: 3 controlPlane: + architecture: amd64 name: control-plane platform: {} replicas: 3 diff --git a/pkg/asset/rhcos/bootstrap_image.go b/pkg/asset/rhcos/bootstrap_image.go index c1d48afbdd3..3f7d340a7e0 100644 --- a/pkg/asset/rhcos/bootstrap_image.go +++ b/pkg/asset/rhcos/bootstrap_image.go @@ -45,7 +45,7 @@ func (i *BootstrapImage) Generate(p asset.Parents) error { case baremetal.Name: // Baremetal IPI launches a local VM for the bootstrap node // Hence requires the QEMU image to use the libvirt backend - osimage, err = rhcos.QEMU(ctx) + osimage, err = rhcos.QEMU(ctx, config.ControlPlane.Architecture) default: // other platforms use the same image for all nodes osimage, err = osImage(config) diff --git a/pkg/asset/rhcos/image.go b/pkg/asset/rhcos/image.go index 06418de4174..ebfaae41b6a 100644 --- a/pkg/asset/rhcos/image.go +++ b/pkg/asset/rhcos/image.go @@ -62,6 +62,8 @@ func (i *Image) Generate(p asset.Parents) error { } func osImage(config *types.InstallConfig) (string, error) { + arch := config.ControlPlane.Architecture + var osimage string var err error ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) @@ -72,20 +74,20 @@ func osImage(config *types.InstallConfig) (string, error) { osimage = config.Platform.AWS.AMIID break } - osimage, err = rhcos.AMI(ctx, config.Platform.AWS.Region) + osimage, err = rhcos.AMI(ctx, arch, config.Platform.AWS.Region) case gcp.Name: - osimage, err = rhcos.GCP(ctx) + osimage, err = rhcos.GCP(ctx, arch) case libvirt.Name: - osimage, err = rhcos.QEMU(ctx) + osimage, err = rhcos.QEMU(ctx, arch) case openstack.Name: osimage = "rhcos" case azure.Name: - osimage, err = rhcos.VHD(ctx) + osimage, err = rhcos.VHD(ctx, arch) case baremetal.Name: // Note that baremetal IPI currently uses the OpenStack image // because this contains the necessary ironic config drive // ignition support, which isn't enabled in the UPI BM images - osimage, err = rhcos.OpenStack(ctx) + osimage, err = rhcos.OpenStack(ctx, arch) case none.Name, vsphere.Name: default: return "", errors.New("invalid Platform") diff --git a/pkg/rhcos/ami.go b/pkg/rhcos/ami.go index f6d76767b33..625ba9e80ac 100644 --- a/pkg/rhcos/ami.go +++ b/pkg/rhcos/ami.go @@ -4,11 +4,13 @@ import ( "context" "github.com/pkg/errors" + + "github.com/openshift/installer/pkg/types" ) // AMI fetches the HVM AMI ID of the Red Hat Enterprise Linux CoreOS release. -func AMI(ctx context.Context, region string) (string, error) { - meta, err := fetchRHCOSBuild(ctx) +func AMI(ctx context.Context, arch types.Architecture, region string) (string, error) { + meta, err := fetchRHCOSBuild(ctx, arch) if err != nil { return "", errors.Wrap(err, "failed to fetch RHCOS metadata") } diff --git a/pkg/rhcos/azure.go b/pkg/rhcos/azure.go index 63176489d71..14e8fc30bb2 100644 --- a/pkg/rhcos/azure.go +++ b/pkg/rhcos/azure.go @@ -4,11 +4,13 @@ import ( "context" "github.com/pkg/errors" + + "github.com/openshift/installer/pkg/types" ) // VHD fetches the URL of the public Azure storage bucket containing the RHCOS image -func VHD(ctx context.Context) (string, error) { - meta, err := fetchRHCOSBuild(ctx) +func VHD(ctx context.Context, arch types.Architecture) (string, error) { + meta, err := fetchRHCOSBuild(ctx, arch) if err != nil { return "", errors.Wrap(err, "failed to fetch RHCOS metadata") } diff --git a/pkg/rhcos/builds.go b/pkg/rhcos/builds.go index 68c0863814c..1789eb7bab8 100644 --- a/pkg/rhcos/builds.go +++ b/pkg/rhcos/builds.go @@ -3,10 +3,18 @@ package rhcos import ( "context" "encoding/json" + "fmt" "io/ioutil" + "os" "github.com/openshift/installer/data" "github.com/pkg/errors" + + "github.com/openshift/installer/pkg/types" +) + +var ( + errInvalidArch = fmt.Errorf("no build metadata for given architecture") ) type metadata struct { @@ -35,15 +43,17 @@ type metadata struct { OSTreeVersion string `json:"ostree-version"` } -func fetchRHCOSBuild(ctx context.Context) (*metadata, error) { - file, err := data.Assets.Open("rhcos.json") +func fetchRHCOSBuild(ctx context.Context, arch types.Architecture) (*metadata, error) { + file, err := data.Assets.Open(fmt.Sprintf("rhcos-%s.json", arch)) if err != nil { return nil, err } defer file.Close() body, err := ioutil.ReadAll(file) - if err != nil { + if os.IsNotExist(err) { + return nil, errInvalidArch + } else if err != nil { return nil, err } diff --git a/pkg/rhcos/gcp.go b/pkg/rhcos/gcp.go index 782ceba15a2..e1dc4df66cb 100644 --- a/pkg/rhcos/gcp.go +++ b/pkg/rhcos/gcp.go @@ -4,11 +4,13 @@ import ( "context" "github.com/pkg/errors" + + "github.com/openshift/installer/pkg/types" ) // GCP fetches the URL of the public GCP storage bucket containing the RHCOS image -func GCP(ctx context.Context) (string, error) { - meta, err := fetchRHCOSBuild(ctx) +func GCP(ctx context.Context, arch types.Architecture) (string, error) { + meta, err := fetchRHCOSBuild(ctx, arch) if err != nil { return "", errors.Wrap(err, "failed to fetch RHCOS metadata") } diff --git a/pkg/rhcos/openstack.go b/pkg/rhcos/openstack.go index bf24a302c65..837aa867e93 100644 --- a/pkg/rhcos/openstack.go +++ b/pkg/rhcos/openstack.go @@ -5,12 +5,14 @@ import ( "net/url" "github.com/pkg/errors" + + "github.com/openshift/installer/pkg/types" ) // OpenStack fetches the URL of the Red Hat Enterprise Linux CoreOS release, // for the openstack platform -func OpenStack(ctx context.Context) (string, error) { - meta, err := fetchRHCOSBuild(ctx) +func OpenStack(ctx context.Context, arch types.Architecture) (string, error) { + meta, err := fetchRHCOSBuild(ctx, arch) if err != nil { return "", errors.Wrap(err, "failed to fetch RHCOS metadata") } diff --git a/pkg/rhcos/qemu.go b/pkg/rhcos/qemu.go index e4408447cfa..e30dee4b1d2 100644 --- a/pkg/rhcos/qemu.go +++ b/pkg/rhcos/qemu.go @@ -5,11 +5,13 @@ import ( "net/url" "github.com/pkg/errors" + + "github.com/openshift/installer/pkg/types" ) // QEMU fetches the URL of the Red Hat Enterprise Linux CoreOS release. -func QEMU(ctx context.Context) (string, error) { - meta, err := fetchRHCOSBuild(ctx) +func QEMU(ctx context.Context, arch types.Architecture) (string, error) { + meta, err := fetchRHCOSBuild(ctx, arch) if err != nil { return "", errors.Wrap(err, "failed to fetch RHCOS metadata") } diff --git a/pkg/types/defaults/machinepools.go b/pkg/types/defaults/machinepools.go index e9810c2e9c5..9573e7950b9 100644 --- a/pkg/types/defaults/machinepools.go +++ b/pkg/types/defaults/machinepools.go @@ -17,4 +17,7 @@ func SetMachinePoolDefaults(p *types.MachinePool, platform string) { if p.Hyperthreading == "" { p.Hyperthreading = types.HyperthreadingEnabled } + if p.Architecture == "" { + p.Architecture = types.ArchitectureAMD64 + } } diff --git a/pkg/types/defaults/machinepools_test.go b/pkg/types/defaults/machinepools_test.go index 4746ca72df5..9eb1e43fd16 100644 --- a/pkg/types/defaults/machinepools_test.go +++ b/pkg/types/defaults/machinepools_test.go @@ -14,6 +14,7 @@ func defaultMachinePool(name string) *types.MachinePool { Name: name, Replicas: pointer.Int64Ptr(3), Hyperthreading: types.HyperthreadingEnabled, + Architecture: types.ArchitectureAMD64, } } diff --git a/pkg/types/machinepools.go b/pkg/types/machinepools.go index 16665bdcdf5..331f98792ab 100644 --- a/pkg/types/machinepools.go +++ b/pkg/types/machinepools.go @@ -20,6 +20,14 @@ const ( HyperthreadingDisabled HyperthreadingMode = "Disabled" ) +// Architecture is the instruction set architecture for the machines in a pool. +type Architecture string + +const ( + // ArchitectureAMD64 indicates AMD64 (x86_64). + ArchitectureAMD64 = "amd64" +) + // MachinePool is a pool of machines to be installed. type MachinePool struct { // Name is the name of the machine pool. @@ -38,6 +46,10 @@ type MachinePool struct { // +optional // Default is for hyperthreading to be enabled. Hyperthreading HyperthreadingMode `json:"hyperthreading,omitempty"` + + // Architecture is the instruction set architecture of the machine pool. + // Defaults to amd64. + Architecture Architecture `json:"architecture,omitempty"` } // MachinePoolPlatform is the platform-specific configuration for a machine diff --git a/pkg/types/validation/installconfig.go b/pkg/types/validation/installconfig.go index daeba6de050..ca088f1e740 100644 --- a/pkg/types/validation/installconfig.go +++ b/pkg/types/validation/installconfig.go @@ -85,7 +85,7 @@ func ValidateInstallConfig(c *types.InstallConfig, openStackValidValuesFetcher o } else { allErrs = append(allErrs, field.Required(field.NewPath("controlPlane"), "controlPlane is required")) } - allErrs = append(allErrs, validateCompute(&c.Platform, c.Compute, field.NewPath("compute"))...) + allErrs = append(allErrs, validateCompute(&c.Platform, c.ControlPlane, c.Compute, field.NewPath("compute"))...) if err := validate.ImagePullSecret(c.PullSecret); err != nil { allErrs = append(allErrs, field.Invalid(field.NewPath("pullSecret"), c.PullSecret, err.Error())) } @@ -184,7 +184,7 @@ func validateControlPlane(platform *types.Platform, pool *types.MachinePool, fld return allErrs } -func validateCompute(platform *types.Platform, pools []types.MachinePool, fldPath *field.Path) field.ErrorList { +func validateCompute(platform *types.Platform, control *types.MachinePool, pools []types.MachinePool, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} poolNames := map[string]bool{} for i, p := range pools { @@ -196,6 +196,9 @@ func validateCompute(platform *types.Platform, pools []types.MachinePool, fldPat allErrs = append(allErrs, field.Duplicate(poolFldPath.Child("name"), p.Name)) } poolNames[p.Name] = true + if control != nil && control.Architecture != p.Architecture { + allErrs = append(allErrs, field.Invalid(poolFldPath.Child("architecture"), p.Architecture, "heteregeneous multi-arch is not supported; compute pool architecture must match control plane")) + } allErrs = append(allErrs, ValidateMachinePool(platform, &p, poolFldPath)...) } return allErrs diff --git a/pkg/types/validation/installconfig_test.go b/pkg/types/validation/installconfig_test.go index 09d1170fab9..28675a4087d 100644 --- a/pkg/types/validation/installconfig_test.go +++ b/pkg/types/validation/installconfig_test.go @@ -700,6 +700,7 @@ func TestValidateInstallConfig(t *testing.T) { return c }(), }, + // TODO(crawford): add a test to validate that homogeneous clusters are enforced once an additional architecture is added } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/types/validation/machinepools.go b/pkg/types/validation/machinepools.go index 33867a09c14..6c9ed452458 100644 --- a/pkg/types/validation/machinepools.go +++ b/pkg/types/validation/machinepools.go @@ -31,6 +31,18 @@ var ( } return v }() + + validArchitectures = map[types.Architecture]bool{ + types.ArchitectureAMD64: true, + } + + validArchitectureValues = func() []string { + v := make([]string, 0, len(validArchitectures)) + for m := range validArchitectures { + v = append(v, string(m)) + } + return v + }() ) // ValidateMachinePool checks that the specified machine pool is valid. @@ -46,6 +58,9 @@ func ValidateMachinePool(platform *types.Platform, p *types.MachinePool, fldPath if !validHyperthreadingModes[p.Hyperthreading] { allErrs = append(allErrs, field.NotSupported(fldPath.Child("hyperthreading"), p.Hyperthreading, validHyperthreadingModeValues)) } + if !validArchitectures[p.Architecture] { + allErrs = append(allErrs, field.NotSupported(fldPath.Child("architecture"), p.Architecture, validArchitectureValues)) + } allErrs = append(allErrs, validateMachinePoolPlatform(platform, &p.Platform, fldPath.Child("platform"))...) return allErrs } diff --git a/pkg/types/validation/machinepools_test.go b/pkg/types/validation/machinepools_test.go index 44517d78c01..0aaf503a875 100644 --- a/pkg/types/validation/machinepools_test.go +++ b/pkg/types/validation/machinepools_test.go @@ -19,6 +19,7 @@ func validMachinePool(name string) *types.MachinePool { Name: name, Replicas: pointer.Int64Ptr(1), Hyperthreading: types.HyperthreadingDisabled, + Architecture: types.ArchitectureAMD64, } } From b7d74740800b6f8e754a91d2934ffbfca0dd6b8f Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Mon, 6 Jan 2020 18:17:47 -0800 Subject: [PATCH 5/6] *: enable s390x bootimage support --- hack/update-rhcos-bootimage.py | 16 +++++++++------- pkg/types/machinepools.go | 2 ++ pkg/types/validation/installconfig_test.go | 10 +++++++++- pkg/types/validation/machinepools.go | 1 + 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/hack/update-rhcos-bootimage.py b/hack/update-rhcos-bootimage.py index 0a622ed4357..21492edcfc9 100755 --- a/hack/update-rhcos-bootimage.py +++ b/hack/update-rhcos-bootimage.py @@ -10,7 +10,7 @@ parser = argparse.ArgumentParser() parser.add_argument("meta", action='store') -parser.add_argument("arch", action='store', choices=['amd64']) +parser.add_argument("arch", action='store', choices=['amd64', 's390x']) args = parser.parse_args() metadata_dir = os.path.join(os.path.dirname(sys.argv[0]), "../data/data") @@ -25,13 +25,15 @@ for k in ['images', 'buildid', 'oscontainer', 'ostree-commit', 'ostree-version', 'azure', 'gcp']: - newmeta[k] = meta[k] -newmeta['amis'] = { - entry['name']: { - 'hvm': entry['hvm'], + if meta.get(k): + newmeta[k] = meta[k] +if meta.get(k): + newmeta['amis'] = { + entry['name']: { + 'hvm': entry['hvm'], + } + for entry in meta['amis'] } - for entry in meta['amis'] -} newmeta['baseURI'] = urllib.parse.urljoin(args.meta, '.') with open(os.path.join(metadata_dir, f"rhcos-{args.arch}.json"), 'w') as f: diff --git a/pkg/types/machinepools.go b/pkg/types/machinepools.go index 331f98792ab..e1be51b827d 100644 --- a/pkg/types/machinepools.go +++ b/pkg/types/machinepools.go @@ -26,6 +26,8 @@ type Architecture string const ( // ArchitectureAMD64 indicates AMD64 (x86_64). ArchitectureAMD64 = "amd64" + // ArchitectureS390X indicates s390x (IBM System Z). + ArchitectureS390X = "s390x" ) // MachinePool is a pool of machines to be installed. diff --git a/pkg/types/validation/installconfig_test.go b/pkg/types/validation/installconfig_test.go index 28675a4087d..a750f6b829e 100644 --- a/pkg/types/validation/installconfig_test.go +++ b/pkg/types/validation/installconfig_test.go @@ -700,7 +700,15 @@ func TestValidateInstallConfig(t *testing.T) { return c }(), }, - // TODO(crawford): add a test to validate that homogeneous clusters are enforced once an additional architecture is added + { + name: "cluster is not heteregenous", + installConfig: func() *types.InstallConfig { + c := validInstallConfig() + c.Compute[0].Architecture = types.ArchitectureS390X + return c + }(), + expectedError: `^compute\[0\].architecture: Invalid value: "s390x": heteregeneous multi-arch is not supported; compute pool architecture must match control plane$`, + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/types/validation/machinepools.go b/pkg/types/validation/machinepools.go index 6c9ed452458..60b33b6e6f9 100644 --- a/pkg/types/validation/machinepools.go +++ b/pkg/types/validation/machinepools.go @@ -34,6 +34,7 @@ var ( validArchitectures = map[types.Architecture]bool{ types.ArchitectureAMD64: true, + types.ArchitectureS390X: true, } validArchitectureValues = func() []string { From b1381e5f9ed9c203cd14fd6805a981a2eca5dd73 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Wed, 15 Jan 2020 19:21:25 -0800 Subject: [PATCH 6/6] data/rhcos: add build metadata for s390x These images differ in version from the AMD64 variants because the s390x variants don't exist. Support for s390x was added after 4.2 was released. The version used here has been the one tested in CI. This was generated with the following: ./hack/update-rhcos-bootimage.py https://releases-art-rhcos.svc.ci.openshift.org/art/storage/releases/rhcos-4.2-s390x/42s390x.81.20191224.0/meta.json s390x --- data/data/rhcos-s390x.json | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 data/data/rhcos-s390x.json diff --git a/data/data/rhcos-s390x.json b/data/data/rhcos-s390x.json new file mode 100644 index 00000000000..681e9e32efe --- /dev/null +++ b/data/data/rhcos-s390x.json @@ -0,0 +1,52 @@ +{ + "baseURI": "https://releases-art-rhcos.svc.ci.openshift.org/art/storage/releases/rhcos-4.2-s390x/42s390x.81.20191224.0/", + "buildid": "42s390x.81.20191224.0", + "images": { + "initramfs": { + "path": "rhcos-42s390x.81.20191224.0-installer-initramfs.img", + "sha256": "ff9aa34dd37c43081b469529faf60d4ebc4cde396305634cdf8e480f7793b387" + }, + "iso": { + "path": "rhcos-42s390x.81.20191224.0-installer.iso", + "sha256": "829864fee624cb3e99196b79d3818fa3ba01ce7bb061be82de99349a63732608" + }, + "kernel": { + "path": "rhcos-42s390x.81.20191224.0-installer-kernel", + "sha256": "930921428eb3d815c698fb34a1d6203747558f591992461727a4973fb0f48547" + }, + "metal-dasd": { + "path": "rhcos-42s390x.81.20191224.0-metal-dasd.raw.gz", + "sha256": "308bd932dd84359c92009ccc18eaab33934204aaea1f5dee7ee1e7c3c10b00d5", + "size": 630440448, + "uncompressed-sha256": "2604ff12b0e7bf7e6307c4383928024a8ecaface702a3fafe6650bd632c5e075", + "uncompressed-size": 3155165184 + }, + "metal-zfcp": { + "path": "rhcos-42s390x.81.20191224.0-metal-zfcp.raw.gz", + "sha256": "335d8cc1acb469e3e96f5425199b355afc4596d721abe1ed2b0e8f8cf2e7f78c", + "size": 630418089, + "uncompressed-sha256": "ea08f2bcfdefbbea0e318bc915e933a820951ba67b87b8dc330262f492aec074", + "uncompressed-size": 3155165184 + }, + "openstack": { + "path": "rhcos-42s390x.81.20191224.0-openstack.qcow2", + "sha256": "c28ec6a6c33074fe4f3daee565093dbc855b4d322ac4b03bc6d63e41311bcd64", + "size": 629819070, + "uncompressed-sha256": "2ecd3ab1d600c5a4c2cbd1abc272cfec81ef08deb9ba09d54d9011d8cae59a2d", + "uncompressed-size": 1864564736 + }, + "qemu": { + "path": "rhcos-42s390x.81.20191224.0-qemu.qcow2", + "sha256": "469605b08839339821524385c31022bacddeca67ae0206102910e335471c63f1", + "size": 629819608, + "uncompressed-sha256": "e6747945e805bc627a21a4f2d9ccc104b096410ed0d6256e27e91450a5bbe3d7", + "uncompressed-size": 1864499200 + } + }, + "oscontainer": { + "digest": "sha256:37498b2eae0f317a444ebb01c399e69c6571e845b008e51f52b4b45eeb2ee2df", + "image": "quay.io/openshift-release-dev/ocp-v4.0-art-dev" + }, + "ostree-commit": "018c6d74da09e1bd34281bb2ccaea752f26f08db07af52e036374c13761067a7", + "ostree-version": "42s390x.81.20191224.0" +} \ No newline at end of file