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  }