1
- From 971fc5f34ec13656fe12ae4d698272f8c23a6387 Mon Sep 17 00:00:00 2001
2
- From: Jan Larwig <jan.larwig@ionos .com>
1
+ From 71503fcd472dcec66bf56576b8907e5fd81b9493 Mon Sep 17 00:00:00 2001
2
+ From: Mohamed Chiheb Ben Jemaa <mc.benjemaa@gmail .com>
3
3
Date: Thu, 17 Oct 2024 15:43:10 +0200
4
- Subject: [PATCH] providers: add support for ionos cloud
4
+ Subject: [PATCH 21/21 ] providers: add support for ionos cloud
5
5
6
6
Add support for IONOS Cloud
7
-
8
7
Add check to ignore cloud-config
8
+ Add mounting of root partition
9
+ Add better documentation
10
+
11
+ Co-authored-by: Jan Larwig <
[email protected] >
9
12
---
10
13
docs/release-notes.md | 2 +
11
14
docs/supported-platforms.md | 2 +
12
- internal/providers/ionoscloud/ionoscloud.go | 149 ++++++++++++++++++++
15
+ internal/providers/ionoscloud/ionoscloud.go | 200 ++++++++++++++++++++
13
16
internal/providers/proxmoxve/proxmoxve.go | 4 +-
14
- internal/providers/util/cloudconfig.go | 13 ++
17
+ internal/providers/util/cloudconfig.go | 27 + ++
15
18
internal/register/providers.go | 1 +
16
- 6 files changed, 168 insertions(+), 3 deletions(-)
19
+ 6 files changed, 233 insertions(+), 3 deletions(-)
17
20
create mode 100644 internal/providers/ionoscloud/ionoscloud.go
18
21
create mode 100644 internal/providers/util/cloudconfig.go
19
22
@@ -31,18 +34,18 @@ index 342fb1aa..2f25b609 100644
31
34
32
35
### Bug fixes
33
36
diff --git a/docs/supported-platforms.md b/docs/supported-platforms.md
34
- index eef319b2..c6846087 100644
37
+ index 232482e6..696c9604 100644
35
38
--- a/docs/supported-platforms.md
36
39
+++ b/docs/supported-platforms.md
37
- @@ -20 ,6 +20 ,7 @@ Ignition is currently supported for the following platforms:
40
+ @@ -22 ,6 +22 ,7 @@ Ignition is currently supported for the following platforms:
38
41
* [Hetzner Cloud] (`hetzner`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately.
39
42
* [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.
40
43
* [IBM Cloud] (`ibmcloud`) - Ignition will read its configuration from the instance userdata. Cloud SSH keys are handled separately.
41
- + * [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`.
44
+ + * [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`.
42
45
* [KubeVirt] (`kubevirt`) - Ignition will read its configuration from the instance userdata via config drive. Cloud SSH keys are handled separately.
43
46
* 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.
44
47
* [Nutanix] (`nutanix`) - Ignition will read its configuration from the instance userdata via config drive. Cloud SSH keys are handled separately.
45
- @@ -52 ,6 +53 ,7 @@ For most cloud providers, cloud SSH keys and custom network configuration are ha
48
+ @@ -57 ,6 +58 ,7 @@ For most cloud providers, cloud SSH keys and custom network configuration are ha
46
49
[Hetzner Cloud]: https://www.hetzner.com/cloud
47
50
[Microsoft Hyper-V]: https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/
48
51
[IBM Cloud]: https://www.ibm.com/cloud/vpc
@@ -52,10 +55,10 @@ index eef319b2..c6846087 100644
52
55
[OpenStack]: https://www.openstack.org/
53
56
diff --git a/internal/providers/ionoscloud/ionoscloud.go b/internal/providers/ionoscloud/ionoscloud.go
54
57
new file mode 100644
55
- index 00000000..cc660998
58
+ index 00000000..69f3ccd5
56
59
--- /dev/null
57
60
+++ b/internal/providers/ionoscloud/ionoscloud.go
58
- @@ -0,0 +1,149 @@
61
+ @@ -0,0 +1,200 @@
59
62
+ // Copyright 2024 Red Hat, Inc.
60
63
+ //
61
64
+ // Licensed under the Apache License, Version 2.0 (the "License");
@@ -83,130 +86,181 @@ index 00000000..cc660998
83
86
+ package ionoscloud
84
87
+
85
88
+ import (
86
- + "context"
87
- + "fmt"
88
- + "os"
89
- + "os/exec"
90
- + "path/filepath"
91
- + "time"
92
- +
93
- + "github.com/flatcar/ignition/v2/config/v3_6_experimental/types"
94
- + "github.com/flatcar/ignition/v2/internal/distro"
95
- + "github.com/flatcar/ignition/v2/internal/log"
96
- + "github.com/flatcar/ignition/v2/internal/platform"
97
- + "github.com/flatcar/ignition/v2/internal/providers/util"
98
- + "github.com/flatcar/ignition/v2/internal/resource"
99
- + ut "github.com/flatcar/ignition/v2/internal/util"
100
- +
101
- + "github.com/coreos/vcontext/report"
89
+ + "context"
90
+ + "fmt"
91
+ + "os"
92
+ + "os/exec"
93
+ + "path/filepath"
94
+ + "strings"
95
+ + "time"
96
+ +
97
+ + "github.com/flatcar/ignition/v2/config/v3_6_experimental/types"
98
+ + "github.com/flatcar/ignition/v2/internal/distro"
99
+ + "github.com/flatcar/ignition/v2/internal/log"
100
+ + "github.com/flatcar/ignition/v2/internal/platform"
101
+ + "github.com/flatcar/ignition/v2/internal/providers/util"
102
+ + "github.com/flatcar/ignition/v2/internal/resource"
103
+ + ut "github.com/flatcar/ignition/v2/internal/util"
104
+ +
105
+ + "github.com/coreos/vcontext/report"
102
106
+ )
103
107
+
104
108
+ const (
105
- + deviceLabelEnvVar = "IGNITION_CONFIG_DEVICE_LABEL"
106
- + defaultDeviceLabel = "OEM"
107
- + userDataPath = "/config/user-data"
109
+ + deviceLabelKernelFlag = "ignition.config.device"
110
+ + defaultDeviceLabel = "OEM"
111
+ + userDataKernelFlag = "ignition.config.path"
112
+ + defaultUserDataPath = "config.ign"
108
113
+ )
109
114
+
110
115
+ func init() {
111
- + platform.Register(platform.Provider{
112
- + Name: "ionoscloud",
113
- + Fetch: fetchConfig,
114
- + })
116
+ + platform.Register(platform.Provider{
117
+ + Name: "ionoscloud",
118
+ + Fetch: fetchConfig,
119
+ + })
115
120
+ }
116
121
+
117
122
+ func fetchConfig(f *resource.Fetcher) (types.Config, report.Report, error) {
118
- + var data []byte
119
- + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
120
- +
121
- + dispatch := func(name string, fn func() ([]byte, error)) {
122
- + raw, err := fn()
123
- + if err != nil {
124
- + switch err {
125
- + case context.Canceled:
126
- + case context.DeadlineExceeded:
127
- + f.Logger.Err("timed out while fetching config from %s", name)
128
- + default:
129
- + f.Logger.Err("failed to fetch config from %s: %v", name, err)
130
- + }
131
- + return
132
- + }
133
- +
134
- + data = raw
135
- + cancel()
136
- + }
137
- +
138
- + deviceLabel := os.Getenv(deviceLabelEnvVar)
139
- + if deviceLabel == "" {
140
- + deviceLabel = defaultDeviceLabel
141
- + }
142
- +
143
- + go dispatch(
144
- + "load config from disk", func() ([]byte, error) {
145
- + return fetchConfigFromDevice(f.Logger, ctx, filepath.Join(distro.DiskByLabelDir(), deviceLabel))
146
- + },
147
- + )
148
- +
149
- + <-ctx.Done()
150
- + if ctx.Err() == context.DeadlineExceeded {
151
- + f.Logger.Info("disk was not available in time. Continuing without a config...")
152
- + }
153
- +
154
- + return util.ParseConfig(f.Logger, data)
123
+ + var data []byte
124
+ + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
125
+ +
126
+ + dispatch := func(name string, fn func() ([]byte, error)) {
127
+ + raw, err := fn()
128
+ + if err != nil {
129
+ + switch err {
130
+ + case context.Canceled:
131
+ + case context.DeadlineExceeded:
132
+ + f.Logger.Err("timed out while fetching config from %s", name)
133
+ + default:
134
+ + f.Logger.Err("failed to fetch config from %s: %v", name, err)
135
+ + }
136
+ + return
137
+ + }
138
+ +
139
+ + data = raw
140
+ + cancel()
141
+ + }
142
+ +
143
+ + deviceLabel, userDataPath, err := readFromKernelParams(f.Logger)
144
+ +
145
+ + if err != nil {
146
+ + f.Logger.Err("couldn't read kernel parameters: %v", err)
147
+ + return types.Config{}, report.Report{}, err
148
+ + }
149
+ +
150
+ + if deviceLabel == "" {
151
+ + deviceLabel = defaultDeviceLabel
152
+ + }
153
+ +
154
+ + if userDataPath == "" {
155
+ + userDataPath = defaultUserDataPath
156
+ + }
157
+ +
158
+ + go dispatch(
159
+ + "load config from disk", func() ([]byte, error) {
160
+ + return fetchConfigFromDevice(f.Logger, ctx, deviceLabel, userDataPath)
161
+ + },
162
+ + )
163
+ +
164
+ + <-ctx.Done()
165
+ + if ctx.Err() == context.DeadlineExceeded {
166
+ + f.Logger.Info("disk was not available in time. Continuing without a config...")
167
+ + }
168
+ +
169
+ + return util.ParseConfig(f.Logger, data)
155
170
+ }
156
171
+
157
172
+ func fileExists(path string) bool {
158
- + _, err := os.Stat(path)
159
- + return (err == nil)
173
+ + _, err := os.Stat(path)
174
+ + return (err == nil)
175
+ + }
176
+ +
177
+ + func fetchConfigFromDevice(logger *log.Logger,
178
+ + ctx context.Context,
179
+ + deviceLabel string,
180
+ + dataPath string,
181
+ + ) ([]byte, error) {
182
+ + device := filepath.Join(distro.DiskByLabelDir(), deviceLabel)
183
+ + for !fileExists(device) {
184
+ + logger.Debug("disk (%q) not found. Waiting...", device)
185
+ + select {
186
+ + case <-time.After(time.Second):
187
+ + case <-ctx.Done():
188
+ + return nil, ctx.Err()
189
+ + }
190
+ + }
191
+ +
192
+ + logger.Debug("creating temporary mount point")
193
+ + mnt, err := os.MkdirTemp("", "ignition-config")
194
+ + if err != nil {
195
+ + return nil, fmt.Errorf("failed to create temp directory: %v", err)
196
+ + }
197
+ + defer os.Remove(mnt)
198
+ +
199
+ + cmd := exec.Command(distro.MountCmd(), "-o", "ro", "-t", "auto", device, mnt)
200
+ + if _, err := logger.LogCmd(cmd, "mounting disk"); err != nil {
201
+ + return nil, err
202
+ + }
203
+ + defer func() {
204
+ + _ = logger.LogOp(
205
+ + func() error {
206
+ + return ut.UmountPath(mnt)
207
+ + },
208
+ + "unmounting %q at %q", device, mnt,
209
+ + )
210
+ + }()
211
+ +
212
+ + if !fileExists(filepath.Join(mnt, dataPath)) {
213
+ + return nil, nil
214
+ + }
215
+ +
216
+ + contents, err := os.ReadFile(filepath.Join(mnt, dataPath))
217
+ + if err != nil {
218
+ + return nil, err
219
+ + }
220
+ +
221
+ + if util.IsCloudConfig(contents) {
222
+ + logger.Debug("disk (%q) contains a cloud-config configuration, ignoring", device)
223
+ + return nil, nil
224
+ + }
225
+ +
226
+ + return contents, nil
227
+ + }
228
+ +
229
+ + func readFromKernelParams(logger *log.Logger) (string, string, error) {
230
+ + args, err := os.ReadFile(distro.KernelCmdlinePath())
231
+ + if err != nil {
232
+ + return "", "", err
233
+ + }
234
+ +
235
+ + deviceLabel, userDataPath := parseParams(args)
236
+ + logger.Debug("parsed device label from parameters: %s", deviceLabel)
237
+ + logger.Debug("parsed user-data path from parameters: %s", userDataPath)
238
+ + return deviceLabel, userDataPath, nil
160
239
+ }
161
240
+
162
- + func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, device string) ([]byte, error) {
163
- + for !fileExists(device) {
164
- + logger.Debug("disk (%q) not found. Waiting...", device)
165
- + select {
166
- + case <-time.After(time.Second):
167
- + case <-ctx.Done():
168
- + return nil, ctx.Err()
169
- + }
170
- + }
171
- +
172
- + logger.Debug("creating temporary mount point")
173
- + mnt, err := os.MkdirTemp("", "ignition-config")
174
- + if err != nil {
175
- + return nil, fmt.Errorf("failed to create temp directory: %v", err)
176
- + }
177
- + defer os.Remove(mnt)
178
- +
179
- + cmd := exec.Command(distro.MountCmd(), "-o", "ro", "-t", "auto", device, mnt)
180
- + if _, err := logger.LogCmd(cmd, "mounting disk"); err != nil {
181
- + return nil, err
182
- + }
183
- + defer func() {
184
- + _ = logger.LogOp(
185
- + func() error {
186
- + return ut.UmountPath(mnt)
187
- + },
188
- + "unmounting %q at %q", device, mnt,
189
- + )
190
- + }()
191
- +
192
- + if !fileExists(filepath.Join(mnt, userDataPath)) {
193
- + return nil, nil
194
- + }
195
- +
196
- + contents, err := os.ReadFile(filepath.Join(mnt, userDataPath))
197
- + if err != nil {
198
- + return nil, err
199
- + }
200
- +
201
- + if util.IsCloudConfig(contents) {
202
- + logger.Debug("disk (%q) contains a cloud-config configuration, ignoring", device)
203
- + return nil, nil
204
- + }
205
- +
206
- + return contents, nil
241
+ + func parseParams(args []byte) (deviceLabel, userDataPath string) {
242
+ + for _, arg := range strings.Split(string(args), " ") {
243
+ + parts := strings.SplitN(strings.TrimSpace(arg), "=", 2)
244
+ + if len(parts) != 2 {
245
+ + continue
246
+ + }
247
+ +
248
+ + key := parts[0]
249
+ + value := parts[1]
250
+ +
251
+ + if key == deviceLabelKernelFlag {
252
+ + deviceLabel = value
253
+ + }
254
+ +
255
+ + if key == userDataKernelFlag {
256
+ + userDataPath = value
257
+ + }
258
+ + }
259
+ +
260
+ + return
207
261
+ }
208
262
diff --git a/internal/providers/proxmoxve/proxmoxve.go b/internal/providers/proxmoxve/proxmoxve.go
209
- index 490bfe30..b0dbb481 100644
263
+ index cbfe7c7d..58525c50 100644
210
264
--- a/internal/providers/proxmoxve/proxmoxve.go
211
265
+++ b/internal/providers/proxmoxve/proxmoxve.go
212
266
@@ -20,7 +20,6 @@
@@ -229,10 +283,24 @@ index 490bfe30..b0dbb481 100644
229
283
}
230
284
diff --git a/internal/providers/util/cloudconfig.go b/internal/providers/util/cloudconfig.go
231
285
new file mode 100644
232
- index 00000000..abe9a2b6
286
+ index 00000000..82ed9f36
233
287
--- /dev/null
234
288
+++ b/internal/providers/util/cloudconfig.go
235
- @@ -0,0 +1,13 @@
289
+ @@ -0,0 +1,27 @@
290
+ + // Copyright 2024 Red Hat, Inc.
291
+ + //
292
+ + // Licensed under the Apache License, Version 2.0 (the "License");
293
+ + // you may not use this file except in compliance with the License.
294
+ + // You may obtain a copy of the License at
295
+ + //
296
+ + // http://www.apache.org/licenses/LICENSE-2.0
297
+ + //
298
+ + // Unless required by applicable law or agreed to in writing, software
299
+ + // distributed under the License is distributed on an "AS IS" BASIS,
300
+ + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
301
+ + // See the License for the specific language governing permissions and
302
+ + // limitations under the License.
303
+ +
236
304
+ package util
237
305
+
238
306
+ import (
@@ -247,7 +315,7 @@ index 00000000..abe9a2b6
247
315
+ return false
248
316
+ }
249
317
diff --git a/internal/register/providers.go b/internal/register/providers.go
250
- index bda4b7cf..63249c7d 100644
318
+ index eb4bd9d2..f37aa906 100644
251
319
--- a/internal/register/providers.go
252
320
+++ b/internal/register/providers.go
253
321
@@ -29,6 +29,7 @@ import (
0 commit comments