@@ -10,10 +10,11 @@ Add check to ignore cloud-config
1010 docs/release-notes.md | 2 +
1111 docs/supported-platforms.md | 2 +
1212 internal/providers/ionoscloud/ionoscloud.go | 149 ++++++++++++++++++++
13+ internal/providers/ionoscloud/ionoscloud.go | 200 ++++++++++++++++++++
1314 internal/providers/proxmoxve/proxmoxve.go | 4 +-
1415 internal/providers/util/cloudconfig.go | 13 ++
1516 internal/register/providers.go | 1 +
16- 6 files changed, 168 insertions(+), 3 deletions(-)
17+ 6 files changed, 219 insertions(+), 3 deletions(-)
1718 create mode 100644 internal/providers/ionoscloud/ionoscloud.go
1819 create mode 100644 internal/providers/util/cloudconfig.go
1920
@@ -55,7 +56,7 @@ new file mode 100644
5556index 00000000..cc660998
5657--- /dev/null
5758+++ b/internal/providers/ionoscloud/ionoscloud.go
58- @@ -0,0 +1,149 @@
59+ @@ -0,0 +1,200 @@
5960+ // Copyright 2024 Red Hat, Inc.
6061+ //
6162+ // Licensed under the Apache License, Version 2.0 (the "License");
@@ -83,127 +84,178 @@ index 00000000..cc660998
8384+ package ionoscloud
8485+
8586+ 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"
87+ + "context"
88+ + "fmt"
89+ + "os"
90+ + "os/exec"
91+ + "path/filepath"
92+ + "strings"
93+ + "time"
94+ +
95+ + "github.com/flatcar/ignition/v2/config/v3_6_experimental/types"
96+ + "github.com/flatcar/ignition/v2/internal/distro"
97+ + "github.com/flatcar/ignition/v2/internal/log"
98+ + "github.com/flatcar/ignition/v2/internal/platform"
99+ + "github.com/flatcar/ignition/v2/internal/providers/util"
100+ + "github.com/flatcar/ignition/v2/internal/resource"
101+ + ut "github.com/flatcar/ignition/v2/internal/util"
102+ +
103+ + "github.com/coreos/vcontext/report"
102104+ )
103105+
104106+ const (
105- + deviceLabelEnvVar = "IGNITION_CONFIG_DEVICE_LABEL"
106- + defaultDeviceLabel = "OEM"
107- + userDataPath = "/config/user-data"
107+ + deviceLabelKernelFlag = "ignition.config.device"
108+ + defaultDeviceLabel = "OEM"
109+ + userDataKernelFlag = "ignition.config.path"
110+ + defaultUserDataPath = "config.ign"
108111+ )
109112+
110113+ func init() {
111- + platform.Register(platform.Provider{
112- + Name: "ionoscloud",
113- + Fetch: fetchConfig,
114- + })
114+ + platform.Register(platform.Provider{
115+ + Name: "ionoscloud",
116+ + Fetch: fetchConfig,
117+ + })
115118+ }
116119+
117120+ 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)
121+ + var data []byte
122+ + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
123+ +
124+ + dispatch := func(name string, fn func() ([]byte, error)) {
125+ + raw, err := fn()
126+ + if err != nil {
127+ + switch err {
128+ + case context.Canceled:
129+ + case context.DeadlineExceeded:
130+ + f.Logger.Err("timed out while fetching config from %s", name)
131+ + default:
132+ + f.Logger.Err("failed to fetch config from %s: %v", name, err)
133+ + }
134+ + return
135+ + }
136+ +
137+ + data = raw
138+ + cancel()
139+ + }
140+ +
141+ + deviceLabel, userDataPath, err := readFromKernelParams(f.Logger)
142+ +
143+ + if err != nil {
144+ + f.Logger.Err("couldn't read kernel parameters: %v", err)
145+ + return types.Config{}, report.Report{}, err
146+ + }
147+ +
148+ + if deviceLabel == "" {
149+ + deviceLabel = defaultDeviceLabel
150+ + }
151+ +
152+ + if userDataPath == "" {
153+ + userDataPath = defaultUserDataPath
154+ + }
155+ +
156+ + go dispatch(
157+ + "load config from disk", func() ([]byte, error) {
158+ + return fetchConfigFromDevice(f.Logger, ctx, deviceLabel, userDataPath)
159+ + },
160+ + )
161+ +
162+ + <-ctx.Done()
163+ + if ctx.Err() == context.DeadlineExceeded {
164+ + f.Logger.Info("disk was not available in time. Continuing without a config...")
165+ + }
166+ +
167+ + return util.ParseConfig(f.Logger, data)
155168+ }
156169+
157170+ func fileExists(path string) bool {
158- + _, err := os.Stat(path)
159- + return (err == nil)
171+ + _, err := os.Stat(path)
172+ + return (err == nil)
173+ + }
174+ +
175+ + func fetchConfigFromDevice(logger *log.Logger,
176+ + ctx context.Context,
177+ + deviceLabel string,
178+ + dataPath string,
179+ + ) ([]byte, error) {
180+ + device := filepath.Join(distro.DiskByLabelDir(), deviceLabel)
181+ + for !fileExists(device) {
182+ + logger.Debug("disk (%q) not found. Waiting...", device)
183+ + select {
184+ + case <-time.After(time.Second):
185+ + case <-ctx.Done():
186+ + return nil, ctx.Err()
187+ + }
188+ + }
189+ +
190+ + logger.Debug("creating temporary mount point")
191+ + mnt, err := os.MkdirTemp("", "ignition-config")
192+ + if err != nil {
193+ + return nil, fmt.Errorf("failed to create temp directory: %v", err)
194+ + }
195+ + defer os.Remove(mnt)
196+ +
197+ + cmd := exec.Command(distro.MountCmd(), "-o", "ro", "-t", "auto", device, mnt)
198+ + if _, err := logger.LogCmd(cmd, "mounting disk"); err != nil {
199+ + return nil, err
200+ + }
201+ + defer func() {
202+ + _ = logger.LogOp(
203+ + func() error {
204+ + return ut.UmountPath(mnt)
205+ + },
206+ + "unmounting %q at %q", device, mnt,
207+ + )
208+ + }()
209+ +
210+ + if !fileExists(filepath.Join(mnt, dataPath)) {
211+ + return nil, nil
212+ + }
213+ +
214+ + contents, err := os.ReadFile(filepath.Join(mnt, dataPath))
215+ + if err != nil {
216+ + return nil, err
217+ + }
218+ +
219+ + if util.IsCloudConfig(contents) {
220+ + logger.Debug("disk (%q) contains a cloud-config configuration, ignoring", device)
221+ + return nil, nil
222+ + }
223+ +
224+ + return contents, nil
225+ + }
226+ +
227+ + func readFromKernelParams(logger *log.Logger) (string, string, error) {
228+ + args, err := os.ReadFile(distro.KernelCmdlinePath())
229+ + if err != nil {
230+ + return "", "", err
231+ + }
232+ +
233+ + deviceLabel, userDataPath := parseParams(args)
234+ + logger.Debug("parsed device label from parameters: %s", deviceLabel)
235+ + logger.Debug("parsed user-data path from parameters: %s", userDataPath)
236+ + return deviceLabel, userDataPath, nil
160237+ }
161238+
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
239+ + func parseParams(args []byte) (deviceLabel, userDataPath string) {
240+ + for _, arg := range strings.Split(string(args), " ") {
241+ + parts := strings.SplitN(strings.TrimSpace(arg), "=", 2)
242+ + if len(parts) != 2 {
243+ + continue
244+ + }
245+ +
246+ + key := parts[0]
247+ + value := parts[1]
248+ +
249+ + if key == deviceLabelKernelFlag {
250+ + deviceLabel = value
251+ + }
252+ +
253+ + if key == userDataKernelFlag {
254+ + userDataPath = value
255+ + }
256+ + }
257+ +
258+ + return
207259+ }
208260diff --git a/internal/providers/proxmoxve/proxmoxve.go b/internal/providers/proxmoxve/proxmoxve.go
209261index 490bfe30..b0dbb481 100644
0 commit comments