Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OEM: IONOS Cloud Images #2389

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions build_library/vm_image_util.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ VALID_IMG_TYPES=(
vmware_ova
vmware_raw
xen
ionoscloud
)

#list of oem package names, minus the oem- prefix
Expand All @@ -66,6 +67,7 @@ VALID_OEM_PACKAGES=(
vagrant-virtualbox
virtualbox
vmware
ionoscloud
)

# Set at runtime to one of the above types
Expand Down Expand Up @@ -332,6 +334,15 @@ IMG_akamai_OEM_PACKAGE=common-oem-files
IMG_akamai_OEM_USE=akamai
IMG_akamai_OEM_SYSEXT=oem-akamai

## ionoscloud
IMG_ionoscloud_OEM_USE=ionoscloud
IMG_ionoscloud_OEM_SYSEXT=oem-ionoscloud
IMG_ionoscloud_OEM_PACKAGE=common-oem-files
IMG_ionoscloud_DISK_LAYOUT=vm
IMG_ionoscloud_DISK_FORMAT=qcow2
IMG_ionoscloud_DISK_EXTENSION=qcow2
IMG_ionoscloud_FS_HOOK=ionoscloud

###########################################################

# Print the default vm type for the specified board
Expand Down Expand Up @@ -610,6 +621,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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tuunit can you put a date on when a metadata server will be introduced by IONOS Cloud? I wouldn't want this workaround to stay around forever, as it is likely to interfere with evolutions to the rootfs.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately not, I can ask again internally but it will most definitely stay like this for a long time. Up to a year or longer.

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
Expand Down
2 changes: 1 addition & 1 deletion ci-automation/vms.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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)$ ]];then
elif [[ "${format}" =~ ^(scaleway|kubevirt|ionoscloud)$ ]];then
COMPRESSION_FORMAT="none"
elif [[ "${format}" =~ ^(akamai)$ ]];then
COMPRESSION_FORMAT="gz"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ ConditionKernelCommandLine=|flatcar.oem.id=kubevirt

ConditionKernelCommandLine=|flatcar.oem.id=akamai

ConditionKernelCommandLine=|flatcar.oem.id=ionoscloud

Description=Flatcar Metadata Agent

[Service]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ AMD64_ONLY_OEMIDS=(
digitalocean
gce
vmware
ionoscloud
)

OEMIDS=(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
set linux_append="flatcar.autologin"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think this is a good idea to enable autologin

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on the cloud provider actually - if on Ionos you have access to the instance console you can still reboot the instance and inject the flatcar.autologin from the Grub menu. See here for more details: #1866 (comment)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not intended to be kept. Just helps me with debugging things while I finish the setup. Sorry I shouldn't have opened the PRs yet.

Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
# 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

if [[ "${PV}" == 9999 ]]; then
KEYWORDS="~amd64 ~arm64"
else
EGIT_COMMIT="f3aaab923de5075524780716635f25564b5e6934" # flatcar-master
EGIT_COMMIT="57fac09cb2f4c13b89f6baec46569a3e66a3b29b" # feat/ionoscloud-support
KEYWORDS="amd64 arm64"
fi

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
# 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"

if [[ "${PV}" == 9999 ]]; then
KEYWORDS="~amd64 ~arm ~arm64 ~x86"
else
EGIT_COMMIT="05b4b2aafbe706bdd65265c7a7103ed75fee14d2" # flatcar-master
EGIT_COMMIT="bfee73ca7420eb071556584e47736dbdb9c9a87e" # flatcar-master
KEYWORDS="amd64 arm arm64 x86"
fi

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
<pkgmetadata>
</pkgmetadata>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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="GPL-2"
SLOT="0"
KEYWORDS="amd64"
IUSE=""

OEM_NAME="IONOS Cloud"
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
From 971fc5f34ec13656fe12ae4d698272f8c23a6387 Mon Sep 17 00:00:00 2001
From: Jan Larwig <jan.larwig@ionos.com>
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. Cloud SSH keys are handled separately. Per default the user-data is looked up on the root partition in `/var/lib/cloud/seed/nocloud/user-data`. The root partition is detected by the label `ROOT` which can be customized using the environment variable `IGNITION_CONFIG_ROOT_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 (
+ rootLabelEnvVar = "IGNITION_CONFIG_ROOT_LABEL"
+ defaultRootLabel = "ROOT"
+ userDataPath = "/var/lib/cloud/seed/nocloud/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(rootLabelEnvVar)
+ if deviceLabel == "" {
+ deviceLabel = defaultRootLabel
+ }
+
+ go dispatch(
+ "load config from root partition", func() ([]byte, error) {
+ return fetchConfigFromDevice(f.Logger, ctx, filepath.Join(distro.DiskByLabelDir(), deviceLabel))
+ },
+ )
+
+ <-ctx.Done()
+ if ctx.Err() == context.DeadlineExceeded {
+ f.Logger.Info("root partition 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("root partition (%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 root partition"); 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("root partition (%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

Loading