Skip to content

Commit 79d957e

Browse files
authored
Adds support for cloud-config data in machine registration (#61)
Signed-off-by: David Cassany <[email protected]>
1 parent 489d40f commit 79d957e

File tree

6 files changed

+81
-90
lines changed

6 files changed

+81
-90
lines changed

cmd/operator/register/root.go

Lines changed: 42 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ import (
3939
"k8s.io/client-go/tools/clientcmd/api"
4040
)
4141

42-
const stateInstallFile = "/run/initramfs/cos-state/state.yaml"
42+
const (
43+
stateInstallFile = "/run/initramfs/cos-state/state.yaml"
44+
agentStateDir = "/var/lib/elemental/agent"
45+
agentConfDir = "/etc/rancher/elemental/agent"
46+
)
4347

4448
func NewRegisterCommand() *cobra.Command {
4549
var config cfg.Config
@@ -145,17 +149,28 @@ func run(config cfg.Config) {
145149
}
146150

147151
if !isSystemInstalled() {
148-
err = writeCloudInit(data)
149-
if err != nil {
150-
logrus.Fatal("failed to write cloud init: ", err)
152+
cloudInitURLs := config.Elemental.Install.ConfigURLs
153+
if cloudInitURLs == nil {
154+
cloudInitURLs = []string{}
151155
}
152156

153-
cloudInitPath, err := writeYIPConfig(config.Elemental)
157+
agentConfPath, err := writeSystemAgentConfig(config.Elemental)
154158
if err != nil {
155-
logrus.Fatal("failed to write yip config: ", err)
159+
logrus.Fatal("failed to write system agent configuration: ", err)
160+
}
161+
cloudInitURLs = append(cloudInitURLs, agentConfPath)
162+
163+
if len(config.CloudConfig) > 0 {
164+
cloudInitPath, err := writeCloudInit(config.CloudConfig)
165+
if err != nil {
166+
logrus.Fatal("failed to write custom cloud-init file: ", err)
167+
}
168+
cloudInitURLs = append(cloudInitURLs, cloudInitPath)
156169
}
157170

158-
err = callElementalClient(config.Elemental, cloudInitPath)
171+
config.Elemental.Install.ConfigURLs = cloudInitURLs
172+
173+
err = callElementalClient(config.Elemental)
159174
if err != nil {
160175
logrus.Fatal("failed calling elemental client: ", err)
161176
}
@@ -169,23 +184,24 @@ func isSystemInstalled() bool {
169184
return err == nil
170185
}
171186

172-
func writeCloudInit(data []byte) error {
173-
f, err := os.OpenFile("/oem/userdata", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
174-
defer func(f *os.File) {
175-
_ = f.Close()
176-
}(f)
187+
func writeCloudInit(data map[string]interface{}) (string, error) {
188+
f, err := os.CreateTemp(os.TempDir(), "*.yaml")
177189
if err != nil {
178-
return err
190+
return "", err
179191
}
192+
defer f.Close()
180193

181-
if _, err = f.Write(data); err != nil {
182-
return err
194+
bytes, err := yaml.Marshal(data)
195+
if err != nil {
196+
return "", err
183197
}
198+
bytes = append([]byte("#cloud-config\n"), bytes...)
184199

185-
return nil
200+
_, err = f.Write(bytes)
201+
return f.Name(), err
186202
}
187203

188-
func writeYIPConfig(config cfg.Elemental) (string, error) {
204+
func writeSystemAgentConfig(config cfg.Elemental) (string, error) {
189205
kubeConfig := api.Config{
190206
Kind: "Config",
191207
APIVersion: "v1",
@@ -215,12 +231,12 @@ func writeYIPConfig(config cfg.Elemental) (string, error) {
215231
}
216232

217233
agentConfig := agent.AgentConfig{
218-
WorkDir: "/var/lib/elemental/agent/work",
219-
AppliedPlanDir: "/var/lib/elemental/agent/applied",
220-
LocalPlanDir: "/var/lib/elemental/agent/plans",
234+
WorkDir: filepath.Join(agentStateDir, "work"),
235+
AppliedPlanDir: filepath.Join(agentStateDir, "applied"),
236+
LocalPlanDir: filepath.Join(agentStateDir, "plans"),
221237
RemoteEnabled: true,
222238
LocalEnabled: true,
223-
ConnectionInfoFile: "/var/lib/elemental/agent/elemental_connection.json",
239+
ConnectionInfoFile: filepath.Join(agentStateDir, "elemental_connection.json"),
224240
PreserveWorkDir: false,
225241
}
226242

@@ -232,48 +248,23 @@ func writeYIPConfig(config cfg.Elemental) (string, error) {
232248
stages = append(stages, schema.Stage{
233249
Files: []schema.File{
234250
{
235-
Path: "/var/lib/elemental/agent/elemental_connection.json",
251+
Path: filepath.Join(agentStateDir, "elemental_connection.json"),
236252
Content: string(connectionInfoBytes),
237253
Permissions: 0600,
238254
},
239255
{
240-
Path: "/etc/rancher/elemental/agent/config.yaml",
256+
Path: filepath.Join(agentConfDir, "config.yaml"),
241257
Content: string(agentConfigBytes),
242258
Permissions: 0600,
243259
},
244260
},
245261
})
246262

247-
if config.Install.Password != "" {
248-
stages = append(stages, schema.Stage{
249-
Users: map[string]schema.User{
250-
"root": {
251-
Name: "root",
252-
PasswordHash: config.Install.Password,
253-
},
254-
},
255-
})
256-
}
257-
258-
if len(config.Install.SSHKeys) > 0 {
259-
stages = append(stages, schema.Stage{
260-
Users: map[string]schema.User{
261-
"root": {
262-
Name: "root",
263-
SSHAuthorizedKeys: config.Install.SSHKeys,
264-
Homedir: "/root",
265-
},
266-
},
267-
})
268-
}
269-
270-
f, err := os.CreateTemp(os.TempDir(), "*.yip")
271-
defer func(f *os.File) {
272-
_ = f.Close()
273-
}(f)
263+
f, err := os.CreateTemp(os.TempDir(), "*.yaml")
274264
if err != nil {
275265
return "", err
276266
}
267+
defer f.Close()
277268

278269
err = yaml.NewEncoder(f).Encode(schema.YipConfig{
279270
Name: "Elemental System Agent Configuration",
@@ -285,9 +276,7 @@ func writeYIPConfig(config cfg.Elemental) (string, error) {
285276
return f.Name(), err
286277
}
287278

288-
func callElementalClient(conf cfg.Elemental, cloudInitPath string) error {
289-
290-
conf.Install.ConfigURL = cloudInitPath
279+
func callElementalClient(conf cfg.Elemental) error {
291280
ev, err := cfg.ToEnv(conf.Install)
292281
if err != nil {
293282
return err

pkg/config/config.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,15 @@ type Install struct {
2121
Device string `json:"device,omitempty" yaml:"device,omitempty"`
2222
NoFormat bool `json:"no-format,omitempty" yaml:"no-format,omitempty"`
2323

24-
ConfigURL string `json:"config-url,omitempty" yaml:"config-url,omitempty"`
25-
ISO string `json:"iso,omitempty" yaml:"iso,omitempty"`
26-
SystemURI string `json:"system-uri,omitempty" yaml:"system-uri,omitempty"`
24+
ConfigURLs []string `json:"config-urls,omitempty" yaml:"config-urls,omitempty"`
25+
ISO string `json:"iso,omitempty" yaml:"iso,omitempty"`
26+
SystemURI string `json:"system-uri,omitempty" yaml:"system-uri,omitempty"`
2727

2828
Debug bool `json:"debug,omitempty" yaml:"debug,omitempty"`
2929
TTY string `json:"tty,omitempty" yaml:"tty,omitempty"`
3030
PowerOff bool `json:"poweroff,omitempty" yaml:"poweroff,omitempty"`
3131
Reboot bool `json:"reboot,omitempty" yaml:"reboot,omitempty"`
3232
EjectCD bool `json:"eject-cd,omitempty" yaml:"eject-cd,omitempty"`
33-
34-
Password string `json:"password,omitempty" yaml:"password,omitempty"`
35-
SSHKeys []string `json:"ssh-keys,omitempty" yaml:"ssh-keys,omitempty"`
3633
}
3734

3835
func (in *Install) DeepCopy() *Install {
@@ -71,8 +68,8 @@ type Elemental struct {
7168
}
7269

7370
type Config struct {
74-
Elemental Elemental `yaml:"elemental" json:"elemental,omitempty"`
75-
Data map[string]interface{} `yaml:"data,omitempty" json:"data,omitempty"`
71+
Elemental Elemental `yaml:"elemental" json:"elemental,omitempty"`
72+
CloudConfig map[string]interface{} `yaml:"cloud-config,omitempty" json:"cloud-config,omitempty"`
7673
}
7774

7875
func (in *Config) DeepCopyInto(out *Config) {

pkg/config/toenv.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,13 @@ func ToEnv(inst Install) ([]string, error) {
3838

3939
// it's a mapping of how config env option should be transliterated to the elemental CLI
4040
var defaultOverrides = map[string]string{
41-
"ELEMENTAL_INSTALL_CONFIG_URL": "ELEMENTAL_INSTALL_CLOUD_INIT",
42-
"ELEMENTAL_INSTALL_POWEROFF": "ELEMENTAL_POWEROFF",
43-
"ELEMENTAL_INSTALL_REBOOT": "ELEMENTAL_REBOOT",
44-
"ELEMENTAL_INSTALL_EJECT_CD": "ELEMENTAL_EJECT_CD",
45-
"ELEMENTAL_INSTALL_DEVICE": "ELEMENTAL_INSTALL_TARGET",
46-
"ELEMENTAL_INSTALL_SYSTEM_URI": "ELEMENTAL_INSTALL_SYSTEM",
47-
"ELEMENTAL_INSTALL_DEBUG": "ELEMENTAL_DEBUG",
48-
"ELEMENTAL_INSTALL_PASSWORD": "SKIP",
49-
"ELEMENTAL_INSTALL_SSH_KEYS": "SKIP",
41+
"ELEMENTAL_INSTALL_CONFIG_URLS": "ELEMENTAL_INSTALL_CLOUD_INIT",
42+
"ELEMENTAL_INSTALL_POWEROFF": "ELEMENTAL_POWEROFF",
43+
"ELEMENTAL_INSTALL_REBOOT": "ELEMENTAL_REBOOT",
44+
"ELEMENTAL_INSTALL_EJECT_CD": "ELEMENTAL_EJECT_CD",
45+
"ELEMENTAL_INSTALL_DEVICE": "ELEMENTAL_INSTALL_TARGET",
46+
"ELEMENTAL_INSTALL_SYSTEM_URI": "ELEMENTAL_INSTALL_SYSTEM",
47+
"ELEMENTAL_INSTALL_DEBUG": "ELEMENTAL_DEBUG",
5048
}
5149

5250
func envOverrides(keyName string) string {
@@ -66,13 +64,22 @@ func mapToEnv(prefix string, data map[string]interface{}) []string {
6664
keyName = strings.ReplaceAll(keyName, "-", "_")
6765
// Apply overrides needed to convert between configs types
6866
keyName = envOverrides(keyName)
69-
if keyName == "SKIP" {
70-
continue
71-
}
7267

7368
if data, ok := v.(map[string]interface{}); ok {
7469
subResult := mapToEnv(keyName+"_", data)
7570
result = append(result, subResult...)
71+
} else if slice, ok := v.([]interface{}); ok {
72+
// Convert slices into comma separated values, this is
73+
// what viper/cobra support on elemental-cli side
74+
ev := fmt.Sprintf("%s=", keyName)
75+
for i, s := range slice {
76+
if i < len(slice)-1 {
77+
ev += fmt.Sprintf("%v,", s)
78+
} else {
79+
ev += fmt.Sprintf("%v", s)
80+
}
81+
}
82+
result = append(result, ev)
7683
} else {
7784
result = append(result, fmt.Sprintf("%s=%v", keyName, v))
7885
logrus.Debugf("%s=%v\n", keyName, v)

pkg/config/toenv_test.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -121,35 +121,33 @@ var _ = Describe("os2 config unit tests", func() {
121121
})
122122
It("converts to env slice installation parameters", func() {
123123
c = Config{
124-
Data: map[string]interface{}{
124+
CloudConfig: map[string]interface{}{
125125
"random": "data",
126126
},
127127
Elemental: Elemental{
128128
// Those settings below are tied to the
129129
// elemental installer.
130130
Install: Install{
131-
Device: "foob",
132-
ConfigURL: "fooc",
133-
Firmware: "efi",
134-
ISO: "http://foo.bar",
135-
NoFormat: true,
136-
Debug: true,
137-
PowerOff: true,
138-
Reboot: true,
139-
TTY: "foo",
140-
SystemURI: "docker:container",
141-
SSHKeys: []string{"github:mudler"},
131+
Device: "foob",
132+
ConfigURLs: []string{"fooc", "food"},
133+
Firmware: "efi",
134+
ISO: "http://foo.bar",
135+
NoFormat: true,
136+
Debug: true,
137+
PowerOff: true,
138+
Reboot: true,
139+
TTY: "foo",
140+
SystemURI: "docker:container",
142141
},
143142
},
144143
}
145144
e, err := ToEnv(c.Elemental.Install)
146145
Expect(err).ToNot(HaveOccurred())
147-
// We drop SSHKeys from the env vars
148146
Expect(len(e)).To(Equal(10))
149147
Expect(e).To(
150148
ContainElements(
151149
"ELEMENTAL_INSTALL_TARGET=foob",
152-
"ELEMENTAL_INSTALL_CLOUD_INIT=fooc",
150+
"ELEMENTAL_INSTALL_CLOUD_INIT=fooc,food",
153151
"ELEMENTAL_INSTALL_FIRMWARE=efi",
154152
"ELEMENTAL_INSTALL_ISO=http://foo.bar",
155153
"ELEMENTAL_INSTALL_NO_FORMAT=true",

pkg/server/register.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func (i *InventoryServer) unauthenticatedResponse(machineRegistration *elm.Machi
117117
CACert: i.getRancherCACert(),
118118
},
119119
},
120-
Data: machineRegistration.Spec.Config.Data,
120+
CloudConfig: machineRegistration.Spec.Config.CloudConfig,
121121
})
122122
}
123123

@@ -204,7 +204,7 @@ func (i *InventoryServer) writeMachineInventoryCloudConfig(writer io.Writer, inv
204204
},
205205
Install: registration.Spec.Config.Elemental.Install,
206206
},
207-
Data: registration.Spec.Config.Data,
207+
CloudConfig: registration.Spec.Config.CloudConfig,
208208
})
209209
}
210210

tests/e2e/machineregistration_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ var _ = Describe("MachineRegistration e2e tests", func() {
4242
It("creates a machine registration resource and a URL attaching CA certificate", func() {
4343
install := config.Install{Device: "/dev/vda", ISO: "https://something.example.com"}
4444
elemental := config.Elemental{Install: install}
45-
config := &config.Config{Elemental: elemental, Data: map[string]interface{}{
45+
config := &config.Config{Elemental: elemental, CloudConfig: map[string]interface{}{
4646
"write_files": map[string]string{
4747
"content": "V2h5IGFyZSB5b3UgY2hlY2tpbmcgdGhpcz8K",
4848
"encoding": "b64",

0 commit comments

Comments
 (0)