From f20f614173852f77f354a203cb50f1c9b6c0b9a0 Mon Sep 17 00:00:00 2001 From: Jan Larwig Date: Mon, 4 Nov 2024 10:58:28 +0100 Subject: [PATCH 1/3] add ionoscloud support --- build_library/disk_layout.json | 10 + build_library/vm_image_util.sh | 27 ++ ci-automation/vms.sh | 2 +- sdk_container/.repo/manifests/version.txt | 6 +- .../afterburn/files/coreos-metadata.service | 2 + .../coreos-cloudinit-9999.ebuild | 4 +- .../coreos-init/coreos-init-9999.ebuild | 11 +- .../oem-ionoscloud/files/USER_DATA_INJECTION | 4 + .../coreos-base/oem-ionoscloud/files/grub.cfg | 4 + .../coreos-base/oem-ionoscloud/metadata.xml | 4 + .../oem-ionoscloud/oem-ionoscloud-0.ebuild | 21 ++ .../files/0021-support-ionoscloud.patch | 263 ++++++++++++++++++ .../sys-apps/ignition/ignition-9999.ebuild | 1 + 13 files changed, 347 insertions(+), 12 deletions(-) create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/USER_DATA_INJECTION create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/grub.cfg create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/metadata.xml create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/oem-ionoscloud-0.ebuild create mode 100644 sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/0021-support-ionoscloud.patch diff --git a/build_library/disk_layout.json b/build_library/disk_layout.json index 5d90ccc6c6c..8e25be19ece 100644 --- a/build_library/disk_layout.json +++ b/build_library/disk_layout.json @@ -104,6 +104,16 @@ "fs_type":"ext2" } }, + "ionoscloud":{ + "6":{ + "fs_type":"ext4" + }, + "9":{ + "label":"ROOT", + "fs_label":"ROOT", + "blocks":"12943360" + } + }, "container":{ "1":{ "type":"blank" diff --git a/build_library/vm_image_util.sh b/build_library/vm_image_util.sh index cd44a9d36d6..3f4795f96d3 100644 --- a/build_library/vm_image_util.sh +++ b/build_library/vm_image_util.sh @@ -42,6 +42,8 @@ VALID_IMG_TYPES=( vmware_ova vmware_raw xen + ionoscloud + ionoscloud_raw ) #list of oem package names, minus the oem- prefix @@ -68,6 +70,7 @@ VALID_OEM_PACKAGES=( vagrant-virtualbox virtualbox vmware + ionoscloud ) # Set at runtime to one of the above types @@ -341,6 +344,20 @@ IMG_proxmoxve_OEM_PACKAGE=common-oem-files IMG_proxmoxve_OEM_USE=proxmoxve IMG_proxmoxve_OEM_SYSEXT=oem-proxmoxve +## ionoscloud +IMG_ionoscloud_OEM_USE=ionoscloud +IMG_ionoscloud_OEM_PACKAGE=oem-ionoscloud +IMG_ionoscloud_DISK_LAYOUT=ionoscloud +IMG_ionoscloud_DISK_FORMAT=qcow2 +IMG_ionoscloud_DISK_EXTENSION=qcow2 +IMG_ionoscloud_FS_HOOK=ionoscloud + +## ionoscloud_raw +IMG_ionoscloud_raw_OEM_USE=ionoscloud +IMG_ionoscloud_raw_OEM_PACKAGE=oem-ionoscloud +IMG_ionoscloud_raw_DISK_LAYOUT=ionoscloud +IMG_ionoscloud_raw_FS_HOOK=ionoscloud + ########################################################### # Print the default vm type for the specified board @@ -619,6 +636,16 @@ _run_box_fs_hook() { sudo rm -fr "${VM_TMP_ROOT}/oem/box" } +_run_ionoscloud_fs_hook() { + # Prepare root partition for IONOS Cloud legacy injection + # This is a workaround until the IONOS Cloud introduces a metadata server + sudo mount -o remount,rw "${VM_TMP_ROOT}" + sudo mkdir -p "${VM_TMP_ROOT}/var/lib/cloud/seed/nocloud" + sudo mkdir -p "${VM_TMP_ROOT}/etc/cloud" + sudo touch "${VM_TMP_ROOT}/etc/cloud/cloud.cfg" + sudo mount -o remount,ro "${VM_TMP_ROOT}" +} + # Write the vm disk image to the target directory in the proper format write_vm_disk() { if [[ $(_get_vm_opt PARTITIONED_IMG) -eq 1 ]]; then diff --git a/ci-automation/vms.sh b/ci-automation/vms.sh index dac30bb3b08..ac29d7aeab1 100644 --- a/ci-automation/vms.sh +++ b/ci-automation/vms.sh @@ -138,7 +138,7 @@ function _vm_build_impl() { COMPRESSION_FORMAT="bz2,none" elif [[ "${format}" =~ ^(hyperv|hyperv_vhdx)$ ]];then COMPRESSION_FORMAT="zip" - elif [[ "${format}" =~ ^(scaleway|kubevirt|proxmoxve)$ ]];then + elif [[ "${format}" =~ ^(scaleway|kubevirt|proxmoxve|ionoscloud)$ ]];then COMPRESSION_FORMAT="none" elif [[ "${format}" =~ ^(akamai)$ ]];then COMPRESSION_FORMAT="gz" diff --git a/sdk_container/.repo/manifests/version.txt b/sdk_container/.repo/manifests/version.txt index 4ceebf01e22..499ada8ca21 100644 --- a/sdk_container/.repo/manifests/version.txt +++ b/sdk_container/.repo/manifests/version.txt @@ -1,4 +1,4 @@ -FLATCAR_VERSION=4207.0.0+nightly-20250106-2100 -FLATCAR_VERSION_ID=4207.0.0 -FLATCAR_BUILD_ID="nightly-20250106-2100" +FLATCAR_VERSION=4204.0.0+nightly-20250103-2100-7-g83c4e5a2a4 +FLATCAR_VERSION_ID=4204.0.0 +FLATCAR_BUILD_ID="nightly-20250103-2100-7-g83c4e5a2a4" FLATCAR_SDK_VERSION=4207.0.0+nightly-20250106-2100 diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/coreos-metadata.service b/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/coreos-metadata.service index ad64b983b47..7fc8436338a 100644 --- a/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/coreos-metadata.service +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/afterburn/files/coreos-metadata.service @@ -27,6 +27,8 @@ ConditionKernelCommandLine=|flatcar.oem.id=akamai ConditionKernelCommandLine=|flatcar.oem.id=proxmoxve +ConditionKernelCommandLine=|flatcar.oem.id=ionoscloud + Description=Flatcar Metadata Agent [Service] diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-cloudinit/coreos-cloudinit-9999.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-cloudinit/coreos-cloudinit-9999.ebuild index 834af697934..8a5e9ae588d 100644 --- a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-cloudinit/coreos-cloudinit-9999.ebuild +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-cloudinit/coreos-cloudinit-9999.ebuild @@ -2,7 +2,7 @@ # Distributed under the terms of the GNU General Public License v2 EAPI=7 -EGIT_REPO_URI="https://github.com/flatcar/coreos-cloudinit.git" +EGIT_REPO_URI="https://github.com/tuunit/flatcar-cloudinit.git" COREOS_GO_PACKAGE="github.com/flatcar/coreos-cloudinit" COREOS_GO_GO111MODULE="on" inherit git-r3 systemd toolchain-funcs udev coreos-go @@ -10,7 +10,7 @@ inherit git-r3 systemd toolchain-funcs udev coreos-go if [[ "${PV}" == 9999 ]]; then KEYWORDS="~amd64 ~arm64" else - EGIT_COMMIT="f3aaab923de5075524780716635f25564b5e6934" # flatcar-master + EGIT_COMMIT="a0a73022ef2af493b029b145d4235b5a8360afe1" # feat/ionoscloud-support KEYWORDS="amd64 arm64" fi diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-init/coreos-init-9999.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-init/coreos-init-9999.ebuild index 23b8771e711..a19a3ec8ece 100644 --- a/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-init/coreos-init-9999.ebuild +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/coreos-init/coreos-init-9999.ebuild @@ -3,19 +3,18 @@ # Distributed under the terms of the GNU General Public License v2 EAPI=7 -EGIT_REPO_URI="https://github.com/flatcar/init.git" +EGIT_REPO_URI="https://github.com/tuunit/flatcar-init.git" +PYTHON_COMPAT=( python3_{9..11} ) + +inherit git-r3 systemd python-any-r1 if [[ "${PV}" == 9999 ]]; then KEYWORDS="~amd64 ~arm ~arm64 ~x86" else - EGIT_COMMIT="b5a6cbcfaabe605e28e075b8ac674edaf576a0eb" # flatcar-master + EGIT_COMMIT="5e39ca5d2e5e85217704cefdfa9f2f128ecf822f" # feat/ionoscloud-support KEYWORDS="amd64 arm arm64 x86" fi -PYTHON_COMPAT=( python3_{9..11} ) - -inherit git-r3 systemd python-any-r1 - DESCRIPTION="Init scripts for CoreOS" HOMEPAGE="http://www.coreos.com/" SRC_URI="" diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/USER_DATA_INJECTION b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/USER_DATA_INJECTION new file mode 100644 index 00000000000..aa33c938c6a --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/USER_DATA_INJECTION @@ -0,0 +1,4 @@ +# DO NOT EDIT FILE +# MANAGED BY IONOS CLOUD +# +# INDICATOR FILE FOR USER DATA INJECTION diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/grub.cfg b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/grub.cfg new file mode 100644 index 00000000000..7e8b02ded5f --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/grub.cfg @@ -0,0 +1,4 @@ +# Flatcar GRUB settings + +set oem_id="ionoscloud" +set linux_append="flatcar.autologin" diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/metadata.xml b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/metadata.xml new file mode 100644 index 00000000000..097975e3adc --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/metadata.xml @@ -0,0 +1,4 @@ + + + + diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/oem-ionoscloud-0.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/oem-ionoscloud-0.ebuild new file mode 100644 index 00000000000..c3f53d833fa --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/oem-ionoscloud-0.ebuild @@ -0,0 +1,21 @@ +# Copyright (c) 2013 CoreOS, Inc.. All rights reserved. +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DESCRIPTION="OEM suite for IONOS Cloud" +HOMEPAGE="https://cloud.ionos.com" +SRC_URI="" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="amd64" + +# no source directory +S="${WORKDIR}" + +src_install() { + insinto "/oem" + doins "${FILESDIR}/grub.cfg" + doins "${FILESDIR}/USER_DATA_INJECTION" +} diff --git a/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/0021-support-ionoscloud.patch b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/0021-support-ionoscloud.patch new file mode 100644 index 00000000000..efc0016ceb4 --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/0021-support-ionoscloud.patch @@ -0,0 +1,263 @@ +From 971fc5f34ec13656fe12ae4d698272f8c23a6387 Mon Sep 17 00:00:00 2001 +From: Jan Larwig +Date: Thu, 17 Oct 2024 15:43:10 +0200 +Subject: [PATCH] providers: add support for ionos cloud + +Add support for IONOS Cloud + +Add check to ignore cloud-config +--- + docs/release-notes.md | 2 + + docs/supported-platforms.md | 2 + + internal/providers/ionoscloud/ionoscloud.go | 149 ++++++++++++++++++++ + internal/providers/proxmoxve/proxmoxve.go | 4 +- + internal/providers/util/cloudconfig.go | 13 ++ + internal/register/providers.go | 1 + + 6 files changed, 168 insertions(+), 3 deletions(-) + create mode 100644 internal/providers/ionoscloud/ionoscloud.go + create mode 100644 internal/providers/util/cloudconfig.go + +diff --git a/docs/release-notes.md b/docs/release-notes.md +index 342fb1aa..2f25b609 100644 +--- a/docs/release-notes.md ++++ b/docs/release-notes.md +@@ -10,6 +10,8 @@ nav_order: 9 + + ### Features + ++- Support IONOS Cloud ++ + ### Changes + + ### Bug fixes +diff --git a/docs/supported-platforms.md b/docs/supported-platforms.md +index eef319b2..c6846087 100644 +--- a/docs/supported-platforms.md ++++ b/docs/supported-platforms.md +@@ -20,6 +20,7 @@ Ignition is currently supported for the following platforms: + * [Hetzner Cloud] (`hetzner`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately. + * [Microsoft Hyper-V] (`hyperv`) - Ignition will read its configuration from the `ignition.config` key in pool 0 of the Hyper-V Data Exchange Service (KVP). Values are limited to approximately 1 KiB of text, so Ignition can also read and concatenate multiple keys named `ignition.config.0`, `ignition.config.1`, and so on. + * [IBM Cloud] (`ibmcloud`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately. ++* [IONOS Cloud] (`ionoscloud`) - Ignition will read its configuration from the instance user-data. Per default the user-data is retrieved from a partition or disk with the label `OEM-CONFIG` which can be customized using the environment variable `IGNITION_CONFIG_DISK_LABEL`. + * [KubeVirt] (`kubevirt`) - Ignition will read its configuration from the instance userdata via config drive. Cloud SSH keys are handled separately. + * Bare Metal (`metal`) - Use the `ignition.config.url` kernel parameter to provide a URL to the configuration. The URL can use the `http://`, `https://`, `tftp://`, `s3://`, `arn:`, or `gs://` schemes to specify a remote config. + * [Nutanix] (`nutanix`) - Ignition will read its configuration from the instance userdata via config drive. Cloud SSH keys are handled separately. +@@ -52,6 +53,7 @@ For most cloud providers, cloud SSH keys and custom network configuration are ha + [Hetzner Cloud]: https://www.hetzner.com/cloud + [Microsoft Hyper-V]: https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/ + [IBM Cloud]: https://www.ibm.com/cloud/vpc ++[IONOS Cloud]: https://cloud.ionos.com/ + [KubeVirt]: https://kubevirt.io + [Nutanix]: https://www.nutanix.com/products/ahv + [OpenStack]: https://www.openstack.org/ +diff --git a/internal/providers/ionoscloud/ionoscloud.go b/internal/providers/ionoscloud/ionoscloud.go +new file mode 100644 +index 00000000..cc660998 +--- /dev/null ++++ b/internal/providers/ionoscloud/ionoscloud.go +@@ -0,0 +1,149 @@ ++// Copyright 2024 Red Hat, Inc. ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++// ++// NOTE: This provider is still EXPERIMENTAL. ++// ++// The IONOS Cloud provider fetches the ignition config from the user-data ++// available in an injected file at /var/lib/cloud/seed/nocloud/user-data. ++// This file is created by the IONOS Cloud VM handler before the first boot ++// through the cloud init user data handling. ++// ++// User data with the directive #cloud-config will be ignored ++// See for more: https://docs.ionos.com/cloud/compute-services/compute-engine/how-tos/boot-cloud-init ++ ++package ionoscloud ++ ++import ( ++ "context" ++ "fmt" ++ "os" ++ "os/exec" ++ "path/filepath" ++ "time" ++ ++ "github.com/flatcar/ignition/v2/config/v3_6_experimental/types" ++ "github.com/flatcar/ignition/v2/internal/distro" ++ "github.com/flatcar/ignition/v2/internal/log" ++ "github.com/flatcar/ignition/v2/internal/platform" ++ "github.com/flatcar/ignition/v2/internal/providers/util" ++ "github.com/flatcar/ignition/v2/internal/resource" ++ ut "github.com/flatcar/ignition/v2/internal/util" ++ ++ "github.com/coreos/vcontext/report" ++) ++ ++const ( ++ deviceLabelEnvVar = "IGNITION_CONFIG_DEVICE_LABEL" ++ defaultDeviceLabel = "OEM" ++ userDataPath = "/config/user-data" ++) ++ ++func init() { ++ platform.Register(platform.Provider{ ++ Name: "ionoscloud", ++ Fetch: fetchConfig, ++ }) ++} ++ ++func fetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { ++ var data []byte ++ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ++ ++ dispatch := func(name string, fn func() ([]byte, error)) { ++ raw, err := fn() ++ if err != nil { ++ switch err { ++ case context.Canceled: ++ case context.DeadlineExceeded: ++ f.Logger.Err("timed out while fetching config from %s", name) ++ default: ++ f.Logger.Err("failed to fetch config from %s: %v", name, err) ++ } ++ return ++ } ++ ++ data = raw ++ cancel() ++ } ++ ++ deviceLabel := os.Getenv(deviceLabelEnvVar) ++ if deviceLabel == "" { ++ deviceLabel = defaultDeviceLabel ++ } ++ ++ go dispatch( ++ "load config from disk", func() ([]byte, error) { ++ return fetchConfigFromDevice(f.Logger, ctx, filepath.Join(distro.DiskByLabelDir(), deviceLabel)) ++ }, ++ ) ++ ++ <-ctx.Done() ++ if ctx.Err() == context.DeadlineExceeded { ++ f.Logger.Info("disk was not available in time. Continuing without a config...") ++ } ++ ++ return util.ParseConfig(f.Logger, data) ++} ++ ++func fileExists(path string) bool { ++ _, err := os.Stat(path) ++ return (err == nil) ++} ++ ++func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, device string) ([]byte, error) { ++ for !fileExists(device) { ++ logger.Debug("disk (%q) not found. Waiting...", device) ++ select { ++ case <-time.After(time.Second): ++ case <-ctx.Done(): ++ return nil, ctx.Err() ++ } ++ } ++ ++ logger.Debug("creating temporary mount point") ++ mnt, err := os.MkdirTemp("", "ignition-config") ++ if err != nil { ++ return nil, fmt.Errorf("failed to create temp directory: %v", err) ++ } ++ defer os.Remove(mnt) ++ ++ cmd := exec.Command(distro.MountCmd(), "-o", "ro", "-t", "auto", device, mnt) ++ if _, err := logger.LogCmd(cmd, "mounting disk"); err != nil { ++ return nil, err ++ } ++ defer func() { ++ _ = logger.LogOp( ++ func() error { ++ return ut.UmountPath(mnt) ++ }, ++ "unmounting %q at %q", device, mnt, ++ ) ++ }() ++ ++ if !fileExists(filepath.Join(mnt, userDataPath)) { ++ return nil, nil ++ } ++ ++ contents, err := os.ReadFile(filepath.Join(mnt, userDataPath)) ++ if err != nil { ++ return nil, err ++ } ++ ++ if util.IsCloudConfig(contents) { ++ logger.Debug("disk (%q) contains a cloud-config configuration, ignoring", device) ++ return nil, nil ++ } ++ ++ return contents, nil ++} +diff --git a/internal/providers/proxmoxve/proxmoxve.go b/internal/providers/proxmoxve/proxmoxve.go +index 490bfe30..b0dbb481 100644 +--- a/internal/providers/proxmoxve/proxmoxve.go ++++ b/internal/providers/proxmoxve/proxmoxve.go +@@ -20,7 +20,6 @@ + package proxmoxve + + import ( +- "bytes" + "context" + "fmt" + "os" +@@ -132,8 +131,7 @@ func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, path string) + return nil, err + } + +- header := []byte("#cloud-config\n") +- if bytes.HasPrefix(contents, header) { ++ if util.IsCloudConfig(contents) { + logger.Debug("config drive (%q) contains a cloud-config configuration, ignoring", path) + return nil, nil + } +diff --git a/internal/providers/util/cloudconfig.go b/internal/providers/util/cloudconfig.go +new file mode 100644 +index 00000000..abe9a2b6 +--- /dev/null ++++ b/internal/providers/util/cloudconfig.go +@@ -0,0 +1,13 @@ ++package util ++ ++import ( ++ "bytes" ++) ++ ++func IsCloudConfig(contents []byte) bool { ++ header := []byte("#cloud-config\n") ++ if bytes.HasPrefix(contents, header) { ++ return true ++ } ++ return false ++} +diff --git a/internal/register/providers.go b/internal/register/providers.go +index bda4b7cf..63249c7d 100644 +--- a/internal/register/providers.go ++++ b/internal/register/providers.go +@@ -29,6 +29,7 @@ import ( + _ "github.com/flatcar/ignition/v2/internal/providers/hetzner" + _ "github.com/flatcar/ignition/v2/internal/providers/hyperv" + _ "github.com/flatcar/ignition/v2/internal/providers/ibmcloud" ++ _ "github.com/flatcar/ignition/v2/internal/providers/ionoscloud" + _ "github.com/flatcar/ignition/v2/internal/providers/kubevirt" + _ "github.com/flatcar/ignition/v2/internal/providers/metal" + _ "github.com/flatcar/ignition/v2/internal/providers/nutanix" +-- +2.43.0 + diff --git a/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-9999.ebuild b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-9999.ebuild index c2dc4268365..211f4dcaa8d 100644 --- a/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-9999.ebuild +++ b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/ignition-9999.ebuild @@ -60,6 +60,7 @@ PATCHES=( "${FILESDIR}/0018-docs-Add-re-added-platforms-to-docs-to-pass-tests.patch" "${FILESDIR}/0019-usr-share-oem-oem.patch" "${FILESDIR}/0020-internal-exec-stages-mount-Mount-oem.patch" + "${FILESDIR}/0021-support-ionoscloud.patch" ) src_compile() { From f0a8472faa22003243ed5a502575daf14466675c Mon Sep 17 00:00:00 2001 From: Jan Larwig Date: Thu, 16 Jan 2025 14:33:52 +0100 Subject: [PATCH 2/3] update ignition patch --- .../files/0021-support-ionoscloud.patch | 331 +++++++++++------- 1 file changed, 206 insertions(+), 125 deletions(-) diff --git a/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/0021-support-ionoscloud.patch b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/0021-support-ionoscloud.patch index efc0016ceb4..92c7e59be12 100644 --- a/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/0021-support-ionoscloud.patch +++ b/sdk_container/src/third_party/coreos-overlay/sys-apps/ignition/files/0021-support-ionoscloud.patch @@ -1,19 +1,22 @@ -From 971fc5f34ec13656fe12ae4d698272f8c23a6387 Mon Sep 17 00:00:00 2001 -From: Jan Larwig +From 6bd62809c72ea0507ccc9dfb8353f31ccae04cc0 Mon Sep 17 00:00:00 2001 +From: Mohamed Chiheb Ben Jemaa Date: Thu, 17 Oct 2024 15:43:10 +0200 -Subject: [PATCH] providers: add support for ionos cloud +Subject: [PATCH 21/21] providers: add support for ionos cloud Add support for IONOS Cloud - Add check to ignore cloud-config +Add mounting of root partition +Add better documentation + +Co-authored-by: Jan Larwig --- docs/release-notes.md | 2 + docs/supported-platforms.md | 2 + - internal/providers/ionoscloud/ionoscloud.go | 149 ++++++++++++++++++++ + internal/providers/ionoscloud/ionoscloud.go | 205 ++++++++++++++++++++ internal/providers/proxmoxve/proxmoxve.go | 4 +- - internal/providers/util/cloudconfig.go | 13 ++ + internal/providers/util/cloudconfig.go | 36 ++++ internal/register/providers.go | 1 + - 6 files changed, 168 insertions(+), 3 deletions(-) + 6 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 internal/providers/ionoscloud/ionoscloud.go create mode 100644 internal/providers/util/cloudconfig.go @@ -31,18 +34,18 @@ index 342fb1aa..2f25b609 100644 ### Bug fixes diff --git a/docs/supported-platforms.md b/docs/supported-platforms.md -index eef319b2..c6846087 100644 +index 232482e6..696c9604 100644 --- a/docs/supported-platforms.md +++ b/docs/supported-platforms.md -@@ -20,6 +20,7 @@ Ignition is currently supported for the following platforms: +@@ -22,6 +22,7 @@ Ignition is currently supported for the following platforms: * [Hetzner Cloud] (`hetzner`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately. * [Microsoft Hyper-V] (`hyperv`) - Ignition will read its configuration from the `ignition.config` key in pool 0 of the Hyper-V Data Exchange Service (KVP). Values are limited to approximately 1 KiB of text, so Ignition can also read and concatenate multiple keys named `ignition.config.0`, `ignition.config.1`, and so on. * [IBM Cloud] (`ibmcloud`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately. -+* [IONOS Cloud] (`ionoscloud`) - Ignition will read its configuration from the instance user-data. Per default the user-data is retrieved from a partition or disk with the label `OEM-CONFIG` which can be customized using the environment variable `IGNITION_CONFIG_DISK_LABEL`. ++* [IONOS Cloud] (`ionoscloud`) - Ignition will read its configuration from the instance user-data. Per default the user-data are injected on a disk or partition with the label `OEM` which can be customized using the environment variable `IGNITION_CONFIG_DEVICE_LABEL`. * [KubeVirt] (`kubevirt`) - Ignition will read its configuration from the instance userdata via config drive. Cloud SSH keys are handled separately. * Bare Metal (`metal`) - Use the `ignition.config.url` kernel parameter to provide a URL to the configuration. The URL can use the `http://`, `https://`, `tftp://`, `s3://`, `arn:`, or `gs://` schemes to specify a remote config. * [Nutanix] (`nutanix`) - Ignition will read its configuration from the instance userdata via config drive. Cloud SSH keys are handled separately. -@@ -52,6 +53,7 @@ For most cloud providers, cloud SSH keys and custom network configuration are ha +@@ -57,6 +58,7 @@ For most cloud providers, cloud SSH keys and custom network configuration are ha [Hetzner Cloud]: https://www.hetzner.com/cloud [Microsoft Hyper-V]: https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/ [IBM Cloud]: https://www.ibm.com/cloud/vpc @@ -52,10 +55,10 @@ index eef319b2..c6846087 100644 [OpenStack]: https://www.openstack.org/ diff --git a/internal/providers/ionoscloud/ionoscloud.go b/internal/providers/ionoscloud/ionoscloud.go new file mode 100644 -index 00000000..cc660998 +index 00000000..175d3655 --- /dev/null +++ b/internal/providers/ionoscloud/ionoscloud.go -@@ -0,0 +1,149 @@ +@@ -0,0 +1,205 @@ +// Copyright 2024 Red Hat, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); @@ -83,130 +86,186 @@ index 00000000..cc660998 +package ionoscloud + +import ( -+ "context" -+ "fmt" -+ "os" -+ "os/exec" -+ "path/filepath" -+ "time" -+ -+ "github.com/flatcar/ignition/v2/config/v3_6_experimental/types" -+ "github.com/flatcar/ignition/v2/internal/distro" -+ "github.com/flatcar/ignition/v2/internal/log" -+ "github.com/flatcar/ignition/v2/internal/platform" -+ "github.com/flatcar/ignition/v2/internal/providers/util" -+ "github.com/flatcar/ignition/v2/internal/resource" -+ ut "github.com/flatcar/ignition/v2/internal/util" -+ -+ "github.com/coreos/vcontext/report" ++ "context" ++ "fmt" ++ "os" ++ "os/exec" ++ "path/filepath" ++ "strings" ++ "time" ++ ++ "github.com/flatcar/ignition/v2/config/v3_6_experimental/types" ++ "github.com/flatcar/ignition/v2/internal/distro" ++ "github.com/flatcar/ignition/v2/internal/log" ++ "github.com/flatcar/ignition/v2/internal/platform" ++ "github.com/flatcar/ignition/v2/internal/providers/util" ++ "github.com/flatcar/ignition/v2/internal/resource" ++ ut "github.com/flatcar/ignition/v2/internal/util" ++ ++ "github.com/coreos/vcontext/report" +) + +const ( -+ deviceLabelEnvVar = "IGNITION_CONFIG_DEVICE_LABEL" -+ defaultDeviceLabel = "OEM" -+ userDataPath = "/config/user-data" ++ deviceLabelKernelFlag = "ignition.config.device" ++ defaultDeviceLabel = "OEM" ++ userDataKernelFlag = "ignition.config.path" ++ defaultUserDataPath = "config.ign" +) + +func init() { -+ platform.Register(platform.Provider{ -+ Name: "ionoscloud", -+ Fetch: fetchConfig, -+ }) ++ platform.Register(platform.Provider{ ++ Name: "ionoscloud", ++ Fetch: fetchConfig, ++ }) +} + +func fetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) { -+ var data []byte -+ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) -+ -+ dispatch := func(name string, fn func() ([]byte, error)) { -+ raw, err := fn() -+ if err != nil { -+ switch err { -+ case context.Canceled: -+ case context.DeadlineExceeded: -+ f.Logger.Err("timed out while fetching config from %s", name) -+ default: -+ f.Logger.Err("failed to fetch config from %s: %v", name, err) -+ } -+ return -+ } -+ -+ data = raw -+ cancel() -+ } -+ -+ deviceLabel := os.Getenv(deviceLabelEnvVar) -+ if deviceLabel == "" { -+ deviceLabel = defaultDeviceLabel -+ } -+ -+ go dispatch( -+ "load config from disk", func() ([]byte, error) { -+ return fetchConfigFromDevice(f.Logger, ctx, filepath.Join(distro.DiskByLabelDir(), deviceLabel)) -+ }, -+ ) -+ -+ <-ctx.Done() -+ if ctx.Err() == context.DeadlineExceeded { -+ f.Logger.Info("disk was not available in time. Continuing without a config...") -+ } -+ -+ return util.ParseConfig(f.Logger, data) ++ var data []byte ++ ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ++ ++ dispatch := func(name string, fn func() ([]byte, error)) { ++ raw, err := fn() ++ if err != nil { ++ switch err { ++ case context.Canceled: ++ case context.DeadlineExceeded: ++ f.Logger.Err("timed out while fetching config from %s", name) ++ default: ++ f.Logger.Err("failed to fetch config from %s: %v", name, err) ++ } ++ return ++ } ++ ++ data = raw ++ cancel() ++ } ++ ++ deviceLabel, userDataPath, err := readFromKernelParams(f.Logger) ++ ++ if err != nil { ++ f.Logger.Err("couldn't read kernel parameters: %v", err) ++ return types.Config{}, report.Report{}, err ++ } ++ ++ if deviceLabel == "" { ++ deviceLabel = defaultDeviceLabel ++ } ++ ++ if userDataPath == "" { ++ userDataPath = defaultUserDataPath ++ } ++ ++ go dispatch( ++ "load config from disk", func() ([]byte, error) { ++ return fetchConfigFromDevice(f.Logger, ctx, deviceLabel, userDataPath) ++ }, ++ ) ++ ++ <-ctx.Done() ++ if ctx.Err() == context.DeadlineExceeded { ++ f.Logger.Info("disk was not available in time. Continuing without a config...") ++ } ++ ++ return util.ParseConfig(f.Logger, data) +} + +func fileExists(path string) bool { -+ _, err := os.Stat(path) -+ return (err == nil) ++ _, err := os.Stat(path) ++ return (err == nil) ++} ++ ++func fetchConfigFromDevice(logger *log.Logger, ++ ctx context.Context, ++ deviceLabel string, ++ dataPath string, ++) ([]byte, error) { ++ device := filepath.Join(distro.DiskByLabelDir(), deviceLabel) ++ for !fileExists(device) { ++ logger.Debug("disk (%q) not found. Waiting...", device) ++ select { ++ case <-time.After(time.Second): ++ case <-ctx.Done(): ++ return nil, ctx.Err() ++ } ++ } ++ ++ logger.Debug("creating temporary mount point") ++ mnt, err := os.MkdirTemp("", "ignition-config") ++ if err != nil { ++ return nil, fmt.Errorf("failed to create temp directory: %v", err) ++ } ++ defer os.Remove(mnt) ++ ++ cmd := exec.Command(distro.MountCmd(), "-o", "ro", "-t", "auto", device, mnt) ++ if _, err := logger.LogCmd(cmd, "mounting disk"); err != nil { ++ return nil, err ++ } ++ defer func() { ++ _ = logger.LogOp( ++ func() error { ++ return ut.UmountPath(mnt) ++ }, ++ "unmounting %q at %q", device, mnt, ++ ) ++ }() ++ ++ if !fileExists(filepath.Join(mnt, dataPath)) { ++ return nil, nil ++ } ++ ++ contents, err := os.ReadFile(filepath.Join(mnt, dataPath)) ++ if err != nil { ++ return nil, err ++ } ++ ++ if util.IsCloudConfig(contents) { ++ logger.Debug("disk (%q) contains a cloud-config configuration, ignoring", device) ++ return nil, nil ++ } ++ ++ if util.IsShellScript(contents) { ++ logger.Debug("disk (%q) contains a shell script, ignoring", device) ++ return nil, nil ++ } ++ ++ return contents, nil +} + -+func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, device string) ([]byte, error) { -+ for !fileExists(device) { -+ logger.Debug("disk (%q) not found. Waiting...", device) -+ select { -+ case <-time.After(time.Second): -+ case <-ctx.Done(): -+ return nil, ctx.Err() -+ } -+ } -+ -+ logger.Debug("creating temporary mount point") -+ mnt, err := os.MkdirTemp("", "ignition-config") -+ if err != nil { -+ return nil, fmt.Errorf("failed to create temp directory: %v", err) -+ } -+ defer os.Remove(mnt) -+ -+ cmd := exec.Command(distro.MountCmd(), "-o", "ro", "-t", "auto", device, mnt) -+ if _, err := logger.LogCmd(cmd, "mounting disk"); err != nil { -+ return nil, err -+ } -+ defer func() { -+ _ = logger.LogOp( -+ func() error { -+ return ut.UmountPath(mnt) -+ }, -+ "unmounting %q at %q", device, mnt, -+ ) -+ }() -+ -+ if !fileExists(filepath.Join(mnt, userDataPath)) { -+ return nil, nil -+ } -+ -+ contents, err := os.ReadFile(filepath.Join(mnt, userDataPath)) -+ if err != nil { -+ return nil, err -+ } -+ -+ if util.IsCloudConfig(contents) { -+ logger.Debug("disk (%q) contains a cloud-config configuration, ignoring", device) -+ return nil, nil -+ } -+ -+ return contents, nil ++func readFromKernelParams(logger *log.Logger) (string, string, error) { ++ args, err := os.ReadFile(distro.KernelCmdlinePath()) ++ if err != nil { ++ return "", "", err ++ } ++ ++ deviceLabel, userDataPath := parseParams(args) ++ logger.Debug("parsed device label from parameters: %s", deviceLabel) ++ logger.Debug("parsed user-data path from parameters: %s", userDataPath) ++ return deviceLabel, userDataPath, nil ++} ++ ++func parseParams(args []byte) (deviceLabel, userDataPath string) { ++ for _, arg := range strings.Split(string(args), " ") { ++ parts := strings.SplitN(strings.TrimSpace(arg), "=", 2) ++ if len(parts) != 2 { ++ continue ++ } ++ ++ key := parts[0] ++ value := parts[1] ++ ++ if key == deviceLabelKernelFlag { ++ deviceLabel = value ++ } ++ ++ if key == userDataKernelFlag { ++ userDataPath = value ++ } ++ } ++ ++ return +} diff --git a/internal/providers/proxmoxve/proxmoxve.go b/internal/providers/proxmoxve/proxmoxve.go -index 490bfe30..b0dbb481 100644 +index cbfe7c7d..58525c50 100644 --- a/internal/providers/proxmoxve/proxmoxve.go +++ b/internal/providers/proxmoxve/proxmoxve.go @@ -20,7 +20,6 @@ @@ -229,10 +288,24 @@ index 490bfe30..b0dbb481 100644 } diff --git a/internal/providers/util/cloudconfig.go b/internal/providers/util/cloudconfig.go new file mode 100644 -index 00000000..abe9a2b6 +index 00000000..f3d93777 --- /dev/null +++ b/internal/providers/util/cloudconfig.go -@@ -0,0 +1,13 @@ +@@ -0,0 +1,36 @@ ++// Copyright 2024 Red Hat, Inc. ++// ++// Licensed under the Apache License, Version 2.0 (the "License"); ++// you may not use this file except in compliance with the License. ++// You may obtain a copy of the License at ++// ++// http://www.apache.org/licenses/LICENSE-2.0 ++// ++// Unless required by applicable law or agreed to in writing, software ++// distributed under the License is distributed on an "AS IS" BASIS, ++// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++// See the License for the specific language governing permissions and ++// limitations under the License. ++ +package util + +import ( @@ -246,8 +319,17 @@ index 00000000..abe9a2b6 + } + return false +} ++ ++func IsShellScript(contents []byte) bool { ++ header := []byte("#!/bin/\n") ++ if bytes.HasPrefix(contents, header) { ++ return true ++ } ++ ++ return false ++} diff --git a/internal/register/providers.go b/internal/register/providers.go -index bda4b7cf..63249c7d 100644 +index eb4bd9d2..f37aa906 100644 --- a/internal/register/providers.go +++ b/internal/register/providers.go @@ -29,6 +29,7 @@ import ( @@ -260,4 +342,3 @@ index bda4b7cf..63249c7d 100644 _ "github.com/flatcar/ignition/v2/internal/providers/nutanix" -- 2.43.0 - From f115ff8a22355a36b8b65f226e8b076f1d17b734 Mon Sep 17 00:00:00 2001 From: Jan Larwig Date: Fri, 17 Jan 2025 09:35:04 +0100 Subject: [PATCH 3/3] ionoscloud raw build --- build_library/vm_image_util.sh | 2 +- .../files/USER_DATA_INJECTION | 4 ++++ .../oem-ionoscloud-raw/files/grub.cfg | 4 ++++ .../oem-ionoscloud-raw/metadata.xml | 4 ++++ .../oem-ionoscloud-raw-0.ebuild | 21 +++++++++++++++++++ .../coreos-base/oem-ionoscloud/files/grub.cfg | 2 +- 6 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/files/USER_DATA_INJECTION create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/files/grub.cfg create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/metadata.xml create mode 100644 sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/oem-ionoscloud-raw-0.ebuild diff --git a/build_library/vm_image_util.sh b/build_library/vm_image_util.sh index 3f4795f96d3..891eabda512 100644 --- a/build_library/vm_image_util.sh +++ b/build_library/vm_image_util.sh @@ -354,7 +354,7 @@ IMG_ionoscloud_FS_HOOK=ionoscloud ## ionoscloud_raw IMG_ionoscloud_raw_OEM_USE=ionoscloud -IMG_ionoscloud_raw_OEM_PACKAGE=oem-ionoscloud +IMG_ionoscloud_raw_OEM_PACKAGE=oem-ionoscloud-raw IMG_ionoscloud_raw_DISK_LAYOUT=ionoscloud IMG_ionoscloud_raw_FS_HOOK=ionoscloud diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/files/USER_DATA_INJECTION b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/files/USER_DATA_INJECTION new file mode 100644 index 00000000000..aa33c938c6a --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/files/USER_DATA_INJECTION @@ -0,0 +1,4 @@ +# DO NOT EDIT FILE +# MANAGED BY IONOS CLOUD +# +# INDICATOR FILE FOR USER DATA INJECTION diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/files/grub.cfg b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/files/grub.cfg new file mode 100644 index 00000000000..7e8b02ded5f --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/files/grub.cfg @@ -0,0 +1,4 @@ +# Flatcar GRUB settings + +set oem_id="ionoscloud" +set linux_append="flatcar.autologin" diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/metadata.xml b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/metadata.xml new file mode 100644 index 00000000000..097975e3adc --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/metadata.xml @@ -0,0 +1,4 @@ + + + + diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/oem-ionoscloud-raw-0.ebuild b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/oem-ionoscloud-raw-0.ebuild new file mode 100644 index 00000000000..8a178c418af --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud-raw/oem-ionoscloud-raw-0.ebuild @@ -0,0 +1,21 @@ +# Copyright (c) 2013 CoreOS, Inc.. All rights reserved. +# Distributed under the terms of the GNU General Public License v2 + +EAPI=8 + +DESCRIPTION="OEM suite for raw IONOS Cloud image" +HOMEPAGE="https://cloud.ionos.com" +SRC_URI="" + +LICENSE="Apache-2.0" +SLOT="0" +KEYWORDS="amd64" + +# no source directory +S="${WORKDIR}" + +src_install() { + insinto "/oem" + doins "${FILESDIR}/grub.cfg" + doins "${FILESDIR}/USER_DATA_INJECTION" +} diff --git a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/grub.cfg b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/grub.cfg index 7e8b02ded5f..418c28adaeb 100644 --- a/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/grub.cfg +++ b/sdk_container/src/third_party/coreos-overlay/coreos-base/oem-ionoscloud/files/grub.cfg @@ -1,4 +1,4 @@ # Flatcar GRUB settings set oem_id="ionoscloud" -set linux_append="flatcar.autologin" +set linux_append="flatcar.autologin ignition.config.device=ROOT ignition.config.path=/var/lib/cloud/seed/nocloud/user-data"