github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cloudconfig/cloudinit/cloudinit.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Copyright 2015 Cloudbase Solutions SRL 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package cloudinit 6 7 import ( 8 "strings" 9 10 "github.com/juju/packaging/commands" 11 "github.com/juju/packaging/config" 12 "github.com/juju/utils/shell" 13 "github.com/juju/utils/ssh" 14 ) 15 16 // cloudConfig represents a set of cloud-init configuration options. 17 type cloudConfig struct { 18 // series is the series for which this cloudConfig is made for. 19 series string 20 21 // paccmder is the PackageCommander for this cloudConfig. 22 paccmder commands.PackageCommander 23 24 // pacconfer is the PackagingConfigurer for this cloudConfig. 25 pacconfer config.PackagingConfigurer 26 27 // renderer is the shell Renderer for this cloudConfig. 28 renderer shell.Renderer 29 30 // attrs is the map of options set on this cloudConfig. 31 // main attributes used in the options map and their corresponding types: 32 // 33 // user string 34 // package_update bool 35 // package_upgrade bool 36 // packages []string 37 // runcmd []string 38 // bootcmd []string 39 // disable_ec2_metadata bool 40 // final_message string 41 // locale string 42 // mounts [][]string 43 // output map[OutputKind]string 44 // shh_keys map[SSHKeyType]string 45 // ssh_authorized_keys []string 46 // disable_root bool 47 // 48 // used only for Ubuntu but implemented as runcmds on CentOS: 49 // apt_proxy string 50 // apt_mirror string/bool 51 // apt_sources []*packaging.Source 52 // 53 // instead, the following corresponding options are used temporarily, 54 // but are translated to runcmds and removed right before rendering: 55 // package_proxy 56 // package_mirror 57 // package_sources 58 // package_preferences 59 // 60 // old TODO's: 61 // byobu 62 // grub_dpkg 63 // mcollective 64 // phone_home 65 // puppet 66 // resizefs 67 // rightscale_userdata 68 // scripts_per_boot 69 // scripts_per_instance 70 // scripts_per_once 71 // scripts_user 72 // set_hostname 73 // set_passwords 74 // ssh_import_id 75 // timezone 76 // update_etc_hosts 77 // update_hostname 78 attrs map[string]interface{} 79 } 80 81 // getPackagingConfigurer is defined on the AdvancedPackagingConfig interface. 82 func (cfg *cloudConfig) getPackagingConfigurer() config.PackagingConfigurer { 83 return cfg.pacconfer 84 } 85 86 // GetSeries is defined on the CloudConfig interface. 87 func (cfg *cloudConfig) GetSeries() string { 88 return cfg.series 89 } 90 91 // SetAttr is defined on the CloudConfig interface. 92 func (cfg *cloudConfig) SetAttr(name string, value interface{}) { 93 cfg.attrs[name] = value 94 } 95 96 // UnsetAttr is defined on the CloudConfig interface. 97 func (cfg *cloudConfig) UnsetAttr(name string) { 98 delete(cfg.attrs, name) 99 } 100 101 func annotateKeys(rawKeys string) []string { 102 cfgKeys := []string{} 103 keys := ssh.SplitAuthorisedKeys(rawKeys) 104 for _, key := range keys { 105 // ensure our keys have "Juju:" prepended to differentiate 106 // Juju-managed keys and externally added ones 107 jujuKey := ssh.EnsureJujuComment(key) 108 cfgKeys = append(cfgKeys, jujuKey) 109 } 110 return cfgKeys 111 } 112 113 // AddUser is defined on the UsersConfig interface. 114 func (cfg *cloudConfig) AddUser(user *User) { 115 users, _ := cfg.attrs["users"].([]map[string]interface{}) 116 newUser := map[string]interface{}{ 117 "name": user.Name, 118 "lock_passwd": true, 119 } 120 if user.Groups != nil { 121 newUser["groups"] = user.Groups 122 } 123 if user.Shell != "" { 124 newUser["shell"] = user.Shell 125 } 126 if user.SSHAuthorizedKeys != "" { 127 newUser["ssh-authorized-keys"] = annotateKeys(user.SSHAuthorizedKeys) 128 } 129 if user.Sudo != nil { 130 newUser["sudo"] = user.Sudo 131 } 132 cfg.SetAttr("users", append(users, newUser)) 133 } 134 135 // UnsetUsers is defined on the UsersConfig interface. 136 func (cfg *cloudConfig) UnsetUsers() { 137 cfg.UnsetAttr("users") 138 } 139 140 // SetSystemUpdate is defined on the SystemUpdateConfig interface. 141 func (cfg *cloudConfig) SetSystemUpdate(yes bool) { 142 cfg.SetAttr("package_update", yes) 143 } 144 145 // UnsetSystemUpdate is defined on the SystemUpdateConfig interface. 146 func (cfg *cloudConfig) UnsetSystemUpdate() { 147 cfg.UnsetAttr("package_update") 148 } 149 150 // SystemUpdate is defined on the SystemUpdateConfig interface. 151 func (cfg *cloudConfig) SystemUpdate() bool { 152 update, _ := cfg.attrs["package_update"].(bool) 153 return update 154 } 155 156 // SetSystemUpgrade is defined on the SystemUpgradeConfig interface. 157 func (cfg *cloudConfig) SetSystemUpgrade(yes bool) { 158 cfg.SetAttr("package_upgrade", yes) 159 } 160 161 // UnsetSystemUpgrade is defined on the SystemUpgradeConfig interface. 162 func (cfg *cloudConfig) UnsetSystemUpgrade() { 163 cfg.UnsetAttr("package_upgrade") 164 } 165 166 // SystemUpgrade is defined on the SystemUpgradeConfig interface. 167 func (cfg *cloudConfig) SystemUpgrade() bool { 168 upgrade, _ := cfg.attrs["package_upgrade"].(bool) 169 return upgrade 170 } 171 172 // AddPackage is defined on the PackagingConfig interface. 173 func (cfg *cloudConfig) AddPackage(pack string) { 174 cfg.attrs["packages"] = append(cfg.Packages(), pack) 175 } 176 177 // RemovePackage is defined on the PackagingConfig interface. 178 func (cfg *cloudConfig) RemovePackage(pack string) { 179 cfg.attrs["packages"] = removeStringFromSlice(cfg.Packages(), pack) 180 } 181 182 // Packages is defined on the PackagingConfig interface. 183 func (cfg *cloudConfig) Packages() []string { 184 packs, _ := cfg.attrs["packages"].([]string) 185 return packs 186 } 187 188 // AddRunCmd is defined on the RunCmdsConfig interface. 189 func (cfg *cloudConfig) AddRunCmd(args ...string) { 190 cfg.attrs["runcmd"] = append(cfg.RunCmds(), strings.Join(args, " ")) 191 } 192 193 // AddScripts is defined on the RunCmdsConfig interface. 194 func (cfg *cloudConfig) AddScripts(script ...string) { 195 for _, line := range script { 196 cfg.AddRunCmd(line) 197 } 198 } 199 200 // PrependRunCmd is defined on the RunCmdsConfig interface. 201 func (cfg *cloudConfig) PrependRunCmd(args ...string) { 202 cfg.attrs["runcmd"] = append([]string{strings.Join(args, " ")}, cfg.RunCmds()...) 203 } 204 205 // RemoveRunCmd is defined on the RunCmdsConfig interface. 206 func (cfg *cloudConfig) RemoveRunCmd(cmd string) { 207 cfg.attrs["runcmd"] = removeStringFromSlice(cfg.RunCmds(), cmd) 208 } 209 210 // RunCmds is defined on the RunCmdsConfig interface. 211 func (cfg *cloudConfig) RunCmds() []string { 212 cmds, _ := cfg.attrs["runcmd"].([]string) 213 return cmds 214 } 215 216 // AddBootCmd is defined on the BootCmdsConfig interface. 217 func (cfg *cloudConfig) AddBootCmd(args ...string) { 218 cfg.attrs["bootcmd"] = append(cfg.BootCmds(), strings.Join(args, " ")) 219 } 220 221 // RemoveBootCmd is defined on the BootCmdsConfig interface. 222 func (cfg *cloudConfig) RemoveBootCmd(cmd string) { 223 cfg.attrs["bootcmd"] = removeStringFromSlice(cfg.BootCmds(), cmd) 224 } 225 226 // BootCmds is defined on the BootCmdsConfig interface. 227 func (cfg *cloudConfig) BootCmds() []string { 228 cmds, _ := cfg.attrs["bootcmd"].([]string) 229 return cmds 230 } 231 232 // SetDisableEC2Metadata is defined on the EC2MetadataConfig interface. 233 func (cfg *cloudConfig) SetDisableEC2Metadata(set bool) { 234 cfg.SetAttr("disable_ec2_metadata", set) 235 } 236 237 // UnsetDisableEC2Metadata is defined on the EC2MetadataConfig interface. 238 func (cfg *cloudConfig) UnsetDisableEC2Metadata() { 239 cfg.UnsetAttr("disable_ec2_metadata") 240 } 241 242 // DisableEC2Metadata is defined on the EC2MetadataConfig interface. 243 func (cfg *cloudConfig) DisableEC2Metadata() bool { 244 disEC2, _ := cfg.attrs["disable_ec2_metadata"].(bool) 245 return disEC2 246 } 247 248 // SetFinalMessage is defined on the FinalMessageConfig interface. 249 func (cfg *cloudConfig) SetFinalMessage(message string) { 250 cfg.SetAttr("final_message", message) 251 } 252 253 // UnsetFinalMessage is defined on the FinalMessageConfig interface. 254 func (cfg *cloudConfig) UnsetFinalMessage() { 255 cfg.UnsetAttr("final_message") 256 } 257 258 // FinalMessage is defined on the FinalMessageConfig interface. 259 func (cfg *cloudConfig) FinalMessage() string { 260 message, _ := cfg.attrs["final_message"].(string) 261 return message 262 } 263 264 // SetLocale is defined on the LocaleConfig interface. 265 func (cfg *cloudConfig) SetLocale(locale string) { 266 cfg.SetAttr("locale", locale) 267 } 268 269 // UnsetLocale is defined on the LocaleConfig interface. 270 func (cfg *cloudConfig) UnsetLocale() { 271 cfg.UnsetAttr("locale") 272 } 273 274 // Locale is defined on the LocaleConfig interface. 275 func (cfg *cloudConfig) Locale() string { 276 locale, _ := cfg.attrs["locale"].(string) 277 return locale 278 } 279 280 // AddMount adds takes arguments for installing a mount point in /etc/fstab 281 // The options are of the order and format specific to fstab entries: 282 // <device> <mountpoint> <filesystem> <options> <backup setting> <fsck priority> 283 func (cfg *cloudConfig) AddMount(mount ...string) { 284 mounts, _ := cfg.attrs["mounts"].([][]string) 285 cfg.SetAttr("mounts", append(mounts, mount)) 286 } 287 288 // SetOutput is defined on the OutputConfig interface. 289 func (cfg *cloudConfig) SetOutput(kind OutputKind, stdout, stderr string) { 290 out, _ := cfg.attrs["output"].(map[string]interface{}) 291 if out == nil { 292 out = make(map[string]interface{}) 293 } 294 295 if stderr == "" { 296 out[string(kind)] = stdout 297 } else { 298 out[string(kind)] = []string{stdout, stderr} 299 } 300 301 cfg.SetAttr("output", out) 302 } 303 304 // Output is defined on the OutputConfig interface. 305 func (cfg *cloudConfig) Output(kind OutputKind) (stdout, stderr string) { 306 if out, ok := cfg.attrs["output"].(map[string]interface{}); ok { 307 switch out := out[string(kind)].(type) { 308 case string: 309 stdout = out 310 case []string: 311 stdout, stderr = out[0], out[1] 312 } 313 } 314 315 return stdout, stderr 316 } 317 318 // SetSSHAuthorizedKeys is defined on the SSHAuthorizedKeysConfig interface. 319 func (cfg *cloudConfig) SetSSHAuthorizedKeys(rawKeys string) { 320 keys := annotateKeys(rawKeys) 321 if len(keys) != 0 { 322 cfg.SetAttr("ssh_authorized_keys", keys) 323 } else { 324 cfg.UnsetAttr("ssh_authorized_keys") 325 } 326 } 327 328 // SetSSHKeys is defined on the SSHKeysConfig interface. 329 func (cfg *cloudConfig) SetSSHKeys(keys SSHKeys) { 330 if keys.RSA != nil { 331 cfg.SetAttr("ssh_keys", map[string]interface{}{ 332 string(RSAPrivate): keys.RSA.Private, 333 string(RSAPublic): keys.RSA.Public, 334 }) 335 } else { 336 cfg.UnsetAttr("ssh_keys") 337 } 338 } 339 340 // SetDisableRoot is defined on the RootUserConfig interface. 341 func (cfg *cloudConfig) SetDisableRoot(disable bool) { 342 cfg.SetAttr("disable_root", disable) 343 } 344 345 // UnsetDisableRoot is defined on the RootUserConfig interface. 346 func (cfg *cloudConfig) UnsetDisableRoot() { 347 cfg.UnsetAttr("disable_root") 348 } 349 350 // DisableRoot is defined on the RootUserConfig interface. 351 func (cfg *cloudConfig) DisableRoot() bool { 352 disable, _ := cfg.attrs["disable_root"].(bool) 353 return disable 354 } 355 356 // ManageEtcHosts enables or disables management of /etc/hosts. 357 func (cfg *cloudConfig) ManageEtcHosts(manage bool) { 358 if manage { 359 cfg.SetAttr("manage_etc_hosts", true) 360 } else { 361 cfg.UnsetAttr("manage_etc_hosts") 362 } 363 } 364 365 // AddRunTextFile is defined on the WrittenFilesConfig interface. 366 func (cfg *cloudConfig) AddRunTextFile(filename, contents string, perm uint) { 367 cfg.AddScripts(addFileCmds(filename, []byte(contents), perm, false)...) 368 } 369 370 // AddBootTextFile is defined on the WrittenFilesConfig interface. 371 func (cfg *cloudConfig) AddBootTextFile(filename, contents string, perm uint) { 372 for _, cmd := range addFileCmds(filename, []byte(contents), perm, false) { 373 cfg.AddBootCmd(cmd) 374 } 375 } 376 377 // AddRunBinaryFile is defined on the WrittenFilesConfig interface. 378 func (cfg *cloudConfig) AddRunBinaryFile(filename string, data []byte, mode uint) { 379 cfg.AddScripts(addFileCmds(filename, data, mode, true)...) 380 } 381 382 // ShellRenderer is defined on the RenderConfig interface. 383 func (cfg *cloudConfig) ShellRenderer() shell.Renderer { 384 return cfg.renderer 385 } 386 387 // RequiresCloudArchiveCloudTools is defined on the AdvancedPackagingConfig 388 // interface. 389 func (cfg *cloudConfig) RequiresCloudArchiveCloudTools() bool { 390 return config.SeriesRequiresCloudArchiveTools(cfg.series) 391 }