Skip to content

Commit 3d4bb8d

Browse files
mcbenjemaatuunit
andcommitted
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 <jan.larwig@ionos.com>
1 parent a204f42 commit 3d4bb8d

File tree

6 files changed

+182
-3
lines changed

6 files changed

+182
-3
lines changed

docs/release-notes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ nav_order: 9
1010

1111
### Features
1212

13+
- Support IONOS Cloud
14+
1315
### Changes
1416

1517
### Bug fixes

docs/supported-platforms.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Ignition is currently supported for the following platforms:
2020
* [Hetzner Cloud] (`hetzner`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately.
2121
* [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.
2222
* [IBM Cloud] (`ibmcloud`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately.
23+
* [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`.
2324
* [KubeVirt] (`kubevirt`) - Ignition will read its configuration from the instance userdata via config drive. Cloud SSH keys are handled separately.
2425
* 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.
2526
* [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
5253
[Hetzner Cloud]: https://www.hetzner.com/cloud
5354
[Microsoft Hyper-V]: https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/
5455
[IBM Cloud]: https://www.ibm.com/cloud/vpc
56+
[IONOS Cloud]: https://cloud.ionos.com/
5557
[KubeVirt]: https://kubevirt.io
5658
[Nutanix]: https://www.nutanix.com/products/ahv
5759
[OpenStack]: https://www.openstack.org/
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright 2024 Red Hat, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// NOTE: This provider is still EXPERIMENTAL.
16+
//
17+
// The IONOS Cloud provider fetches the ignition config from the user-data
18+
// available in an injected file at /var/lib/cloud/seed/nocloud/user-data.
19+
// This file is created by the IONOS Cloud VM handler before the first boot
20+
// through the cloud init user data handling.
21+
//
22+
// User data with the directive #cloud-config will be ignored
23+
// See for more: https://docs.ionos.com/cloud/compute-services/compute-engine/how-tos/boot-cloud-init
24+
25+
package ionoscloud
26+
27+
import (
28+
"context"
29+
"fmt"
30+
"os"
31+
"os/exec"
32+
"path/filepath"
33+
"time"
34+
35+
"github.com/coreos/ignition/v2/config/v3_6_experimental/types"
36+
"github.com/coreos/ignition/v2/internal/distro"
37+
"github.com/coreos/ignition/v2/internal/log"
38+
"github.com/coreos/ignition/v2/internal/platform"
39+
"github.com/coreos/ignition/v2/internal/providers/util"
40+
"github.com/coreos/ignition/v2/internal/resource"
41+
ut "github.com/coreos/ignition/v2/internal/util"
42+
43+
"github.com/coreos/vcontext/report"
44+
)
45+
46+
const (
47+
rootLabelEnvVar = "IGNITION_CONFIG_ROOT_LABEL"
48+
defaultRootLabel = "ROOT"
49+
userDataPath = "/var/lib/cloud/seed/nocloud/user-data"
50+
)
51+
52+
func init() {
53+
platform.Register(platform.Provider{
54+
Name: "ionoscloud",
55+
Fetch: fetchConfig,
56+
})
57+
}
58+
59+
func fetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) {
60+
var data []byte
61+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
62+
63+
dispatch := func(name string, fn func() ([]byte, error)) {
64+
raw, err := fn()
65+
if err != nil {
66+
switch err {
67+
case context.Canceled:
68+
case context.DeadlineExceeded:
69+
f.Logger.Err("timed out while fetching config from %s", name)
70+
default:
71+
f.Logger.Err("failed to fetch config from %s: %v", name, err)
72+
}
73+
return
74+
}
75+
76+
data = raw
77+
cancel()
78+
}
79+
80+
deviceLabel := os.Getenv(rootLabelEnvVar)
81+
if deviceLabel == "" {
82+
deviceLabel = defaultRootLabel
83+
}
84+
85+
go dispatch(
86+
"load config from root partition", func() ([]byte, error) {
87+
return fetchConfigFromDevice(f.Logger, ctx, filepath.Join(distro.DiskByLabelDir(), deviceLabel))
88+
},
89+
)
90+
91+
<-ctx.Done()
92+
if ctx.Err() == context.DeadlineExceeded {
93+
f.Logger.Info("root partition was not available in time. Continuing without a config...")
94+
}
95+
96+
return util.ParseConfig(f.Logger, data)
97+
}
98+
99+
func fileExists(path string) bool {
100+
_, err := os.Stat(path)
101+
return (err == nil)
102+
}
103+
104+
func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, device string) ([]byte, error) {
105+
for !fileExists(device) {
106+
logger.Debug("root partition (%q) not found. Waiting...", device)
107+
select {
108+
case <-time.After(time.Second):
109+
case <-ctx.Done():
110+
return nil, ctx.Err()
111+
}
112+
}
113+
114+
logger.Debug("creating temporary mount point")
115+
mnt, err := os.MkdirTemp("", "ignition-config")
116+
if err != nil {
117+
return nil, fmt.Errorf("failed to create temp directory: %v", err)
118+
}
119+
defer os.Remove(mnt)
120+
121+
cmd := exec.Command(distro.MountCmd(), "-o", "ro", "-t", "auto", device, mnt)
122+
if _, err := logger.LogCmd(cmd, "mounting root partition"); err != nil {
123+
return nil, err
124+
}
125+
defer func() {
126+
_ = logger.LogOp(
127+
func() error {
128+
return ut.UmountPath(mnt)
129+
},
130+
"unmounting %q at %q", device, mnt,
131+
)
132+
}()
133+
134+
if !fileExists(filepath.Join(mnt, userDataPath)) {
135+
return nil, nil
136+
}
137+
138+
contents, err := os.ReadFile(filepath.Join(mnt, userDataPath))
139+
if err != nil {
140+
return nil, err
141+
}
142+
143+
if util.IsCloudConfig(contents) {
144+
logger.Debug("root partition (%q) contains a cloud-config configuration, ignoring", device)
145+
return nil, nil
146+
}
147+
148+
return contents, nil
149+
}

internal/providers/proxmoxve/proxmoxve.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package proxmoxve
2121

2222
import (
23-
"bytes"
2423
"context"
2524
"fmt"
2625
"os"
@@ -132,8 +131,7 @@ func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, path string)
132131
return nil, err
133132
}
134133

135-
header := []byte("#cloud-config\n")
136-
if bytes.HasPrefix(contents, header) {
134+
if util.IsCloudConfig(contents) {
137135
logger.Debug("config drive (%q) contains a cloud-config configuration, ignoring", path)
138136
return nil, nil
139137
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2024 Red Hat, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package util
16+
17+
import (
18+
"bytes"
19+
)
20+
21+
func IsCloudConfig(contents []byte) bool {
22+
header := []byte("#cloud-config\n")
23+
if bytes.HasPrefix(contents, header) {
24+
return true
25+
}
26+
return false
27+
}

internal/register/providers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
_ "github.com/coreos/ignition/v2/internal/providers/hetzner"
3030
_ "github.com/coreos/ignition/v2/internal/providers/hyperv"
3131
_ "github.com/coreos/ignition/v2/internal/providers/ibmcloud"
32+
_ "github.com/coreos/ignition/v2/internal/providers/ionoscloud"
3233
_ "github.com/coreos/ignition/v2/internal/providers/kubevirt"
3334
_ "github.com/coreos/ignition/v2/internal/providers/metal"
3435
_ "github.com/coreos/ignition/v2/internal/providers/nutanix"

0 commit comments

Comments
 (0)