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-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 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/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/hack/update-rhcos-bootimage.py b/hack/update-rhcos-bootimage.py index 3d07a2b23b6..21492edcfc9 100755 --- a/hack/update-rhcos-bootimage.py +++ b/hack/update-rhcos-bootimage.py @@ -1,15 +1,23 @@ -#!/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 +#!/usr/bin/env python3 +# 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 -dn = os.path.abspath(os.path.dirname(sys.argv[0])) +# 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' parser = argparse.ArgumentParser() parser.add_argument("meta", action='store') +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") + +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) @@ -17,13 +25,24 @@ 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(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) 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..e1be51b827d 100644 --- a/pkg/types/machinepools.go +++ b/pkg/types/machinepools.go @@ -20,6 +20,16 @@ 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" + // ArchitectureS390X indicates s390x (IBM System Z). + ArchitectureS390X = "s390x" +) + // MachinePool is a pool of machines to be installed. type MachinePool struct { // Name is the name of the machine pool. @@ -38,6 +48,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..a750f6b829e 100644 --- a/pkg/types/validation/installconfig_test.go +++ b/pkg/types/validation/installconfig_test.go @@ -700,6 +700,15 @@ func TestValidateInstallConfig(t *testing.T) { return c }(), }, + { + 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 33867a09c14..60b33b6e6f9 100644 --- a/pkg/types/validation/machinepools.go +++ b/pkg/types/validation/machinepools.go @@ -31,6 +31,19 @@ var ( } return v }() + + validArchitectures = map[types.Architecture]bool{ + types.ArchitectureAMD64: true, + types.ArchitectureS390X: 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 +59,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, } }