~themue/juju-core/053-env-more-script-friendly

1206.2.1 by Martin Packman
Add copyright statement at the top of all go files bar thirdparty
1
// Copyright 2011, 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
3
25.2.1 by Roger Peppe
add cloudinit package
4
package cloudinit
39.4.28 by Roger Peppe
remove log messages; go fmt
5
39.4.10 by Roger Peppe
SSHAddAuthorizedKey becomes SSHAddAuthorizedKeys and
6
import (
1590.1.1 by Sidnei da Silva
- Move addScripts and addFile from environments/cloudinit.go to cloudinit.Config methods.
7
	"fmt"
39.4.10 by Roger Peppe
SSHAddAuthorizedKey becomes SSHAddAuthorizedKeys and
8
	"strings"
1590.1.1 by Sidnei da Silva
- Move addScripts and addFile from environments/cloudinit.go to cloudinit.Config methods.
9
10
	"launchpad.net/juju-core/utils"
39.4.10 by Roger Peppe
SSHAddAuthorizedKey becomes SSHAddAuthorizedKeys and
11
)
58.1.1 by Roger Peppe
allow SSHAddAuthorizedKeys to take multiple keys
12
39.3.4 by Roger Peppe
add cloudinit.Config.SetAttr.
13
// SetAttr sets an arbitrary attribute in the cloudinit config.
14
// If value is nil the attribute will be deleted; otherwise
15
// the value will be marshalled according to the rules
39.4.11 by Roger Peppe
remove admin-secret; make tests run (but not pass)
16
// of the goyaml Marshal function.
39.3.4 by Roger Peppe
add cloudinit.Config.SetAttr.
17
func (cfg *Config) SetAttr(name string, value interface{}) {
18
	cfg.set(name, value != nil, value)
19
}
39.3.13 by Roger Peppe
gofmt
20
25.2.8 by Roger Peppe
simplified key type.
21
// SetUser sets the user name that will be used for some other options.
22
// The user will be assumed to already exist in the machine image.
23
// The default user is "ubuntu".
25.2.7 by Roger Peppe
changes in response to review
24
func (cfg *Config) SetUser(user string) {
25
	cfg.set("user", user != "", user)
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
26
}
27
25.2.8 by Roger Peppe
simplified key type.
28
// SetAptUpgrade sets whether cloud-init runs "apt-get upgrade"
29
// on first boot.
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
30
func (cfg *Config) SetAptUpgrade(yes bool) {
31
	cfg.set("apt_upgrade", yes, yes)
32
}
33
25.2.8 by Roger Peppe
simplified key type.
34
// SetUpdate sets whether cloud-init runs "apt-get update"
35
// on first boot.
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
36
func (cfg *Config) SetAptUpdate(yes bool) {
37
	cfg.set("apt_update", yes, yes)
38
}
39
1567.3.5 by Sidnei da Silva
- Add AddAptProxy to method
40
// SetAptProxy sets the URL to be used as the apt
41
// proxy.
42
func (cfg *Config) SetAptProxy(url string) {
43
	cfg.set("apt_proxy", url != "", url)
44
}
45
25.2.8 by Roger Peppe
simplified key type.
46
// SetAptMirror sets the URL to be used as the apt
47
// mirror site. If not set, the URL is selected based
48
// on cloud metadata in EC2 - <region>.archive.ubuntu.com
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
49
func (cfg *Config) SetAptMirror(url string) {
50
	cfg.set("apt_mirror", url != "", url)
51
}
52
25.2.8 by Roger Peppe
simplified key type.
53
// SetAptPreserveSourcesList sets whether /etc/apt/sources.list
54
// is overwritten by the mirror. If true, SetAptMirror above
55
// will have no effect.
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
56
func (cfg *Config) SetAptPreserveSourcesList(yes bool) {
57
	cfg.set("apt_mirror", yes, yes)
58
}
59
25.2.8 by Roger Peppe
simplified key type.
60
// AddAptSource adds an apt source. The key holds the
61
// public key of the source, in the form expected by apt-key(8).
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
62
func (cfg *Config) AddAptSource(name, key string) {
63
	src, _ := cfg.attrs["apt_sources"].([]*source)
64
	cfg.attrs["apt_sources"] = append(src,
65
		&source{
66
			Source: name,
25.2.7 by Roger Peppe
changes in response to review
67
			Key:    key,
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
68
		})
69
}
70
25.2.8 by Roger Peppe
simplified key type.
71
// AddAptSource adds an apt source. The public key for the
72
// source is retrieved by fetching the given keyId from the
73
// GPG key server at the given address.
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
74
func (cfg *Config) AddAptSourceWithKeyId(name, keyId, keyServer string) {
75
	src, _ := cfg.attrs["apt_sources"].([]*source)
76
	cfg.attrs["apt_sources"] = append(src,
77
		&source{
78
			Source:    name,
79
			KeyId:     keyId,
80
			KeyServer: keyServer,
81
		})
82
}
83
25.2.8 by Roger Peppe
simplified key type.
84
// SetDebconfSelections provides preseeded debconf answers
85
// for the boot process. The given answers will be used as input
86
// to debconf-set-selections(1).
87
func (cfg *Config) SetDebconfSelections(answers string) {
88
	cfg.set("debconf_selections", answers != "", answers)
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
89
}
90
25.2.8 by Roger Peppe
simplified key type.
91
// AddPackage adds a package to be installed on first boot.
92
// If any packages are specified, "apt-get update"
93
// will be called.
25.2.7 by Roger Peppe
changes in response to review
94
func (cfg *Config) AddPackage(name string) {
1628.11.13 by Andrew Wilkins
Manual provisioning: use cloudinit, check if provisioned
95
	cfg.attrs["packages"] = append(cfg.Packages(), name)
96
}
97
98
// Packages returns a list of packages that will be
99
// installed on first boot.
100
func (cfg *Config) Packages() []string {
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
101
	pkgs, _ := cfg.attrs["packages"].([]string)
1628.11.13 by Andrew Wilkins
Manual provisioning: use cloudinit, check if provisioned
102
	return pkgs
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
103
}
104
25.2.8 by Roger Peppe
simplified key type.
105
func (cfg *Config) addCmd(kind string, c *command) {
1628.11.13 by Andrew Wilkins
Manual provisioning: use cloudinit, check if provisioned
106
	cfg.attrs[kind] = append(cfg.getCmds(kind), c)
107
}
108
109
func (cfg *Config) getCmds(kind string) []*command {
25.2.8 by Roger Peppe
simplified key type.
110
	cmds, _ := cfg.attrs[kind].([]*command)
1628.11.13 by Andrew Wilkins
Manual provisioning: use cloudinit, check if provisioned
111
	return cmds
112
}
113
114
// RunCmds returns a list of commands that will be
115
// run at first boot.
116
//
117
// Each element in the resultant slice is either a
118
// string or []string, corresponding to how the command
119
// was added.
120
func (cfg *Config) RunCmds() []interface{} {
121
	cmds := cfg.getCmds("runcmd")
122
	result := make([]interface{}, len(cmds))
123
	for i, cmd := range cmds {
124
		if cmd.args != nil {
125
			result[i] = append([]string{}, cmd.args...)
126
		} else {
127
			result[i] = cmd.literal
128
		}
129
	}
130
	return result
25.2.8 by Roger Peppe
simplified key type.
131
}
132
25.2.10 by Roger Peppe
updated options comments
133
// AddRunCmd adds a command to be executed
134
// at first boot. The command will be run
135
// by the shell with any metacharacters retaining
136
// their special meaning (that is, no quoting takes place).
25.2.8 by Roger Peppe
simplified key type.
137
func (cfg *Config) AddRunCmd(cmd string) {
138
	cfg.addCmd("runcmd", &command{literal: cmd})
139
}
140
141
// AddRunCmdArgs is like AddRunCmd except that the command
142
// will be executed with the given arguments properly quoted.
143
func (cfg *Config) AddRunCmdArgs(args ...string) {
144
	cfg.addCmd("runcmd", &command{args: args})
145
}
146
147
// AddBootCmd is like AddRunCmd except that the
148
// command will run very early in the boot process,
25.2.10 by Roger Peppe
updated options comments
149
// and it will run on every boot, not just the first time.
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
150
func (cfg *Config) AddBootCmd(cmd string) {
25.2.8 by Roger Peppe
simplified key type.
151
	cfg.addCmd("bootcmd", &command{literal: cmd})
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
152
}
153
25.2.8 by Roger Peppe
simplified key type.
154
// AddBootCmdArgs is like AddBootCmd except that the command
155
// will be executed with the given arguments properly quoted.
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
156
func (cfg *Config) AddBootCmdArgs(args ...string) {
25.2.8 by Roger Peppe
simplified key type.
157
	cfg.addCmd("bootcmd", &command{args: args})
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
158
}
159
25.2.8 by Roger Peppe
simplified key type.
160
// SetDisableEC2Metadata sets whether access to the
161
// EC2 metadata service is disabled early in boot
162
// via a null route ( route del -host 169.254.169.254 reject).
25.2.7 by Roger Peppe
changes in response to review
163
func (cfg *Config) SetDisableEC2Metadata(yes bool) {
164
	cfg.set("disable_ec2_metadata", yes, yes)
165
}
166
25.2.8 by Roger Peppe
simplified key type.
167
// SetFinalMessage sets to message that will be written
168
// when the system has finished booting for the first time.
169
// By default, the message is:
170
// "cloud-init boot finished at $TIMESTAMP. Up $UPTIME seconds".
25.2.7 by Roger Peppe
changes in response to review
171
func (cfg *Config) SetFinalMessage(msg string) {
172
	cfg.set("final_message", msg != "", msg)
173
}
174
25.2.8 by Roger Peppe
simplified key type.
175
// SetLocale sets the locale; it defaults to en_US.UTF-8.
25.2.7 by Roger Peppe
changes in response to review
176
func (cfg *Config) SetLocale(locale string) {
177
	cfg.set("locale", locale != "", locale)
178
}
179
25.2.8 by Roger Peppe
simplified key type.
180
// AddMount adds a mount point. The given
25.2.10 by Roger Peppe
updated options comments
181
// arguments will be used as a line in /etc/fstab.
25.2.8 by Roger Peppe
simplified key type.
182
func (cfg *Config) AddMount(args ...string) {
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
183
	mounts, _ := cfg.attrs["mounts"].([][]string)
25.2.8 by Roger Peppe
simplified key type.
184
	cfg.attrs["mounts"] = append(mounts, args)
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
185
}
186
25.2.8 by Roger Peppe
simplified key type.
187
// OutputKind represents a destination for command output.
25.2.7 by Roger Peppe
changes in response to review
188
type OutputKind string
189
190
const (
191
	OutInit   OutputKind = "init"
192
	OutConfig OutputKind = "config"
193
	OutFinal  OutputKind = "final"
194
	OutAll    OutputKind = "all"
195
)
196
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
197
// SetOutput specifies destination for command output.
198
// Valid values for the kind "init", "config", "final" and "all".
199
// Each of stdout and stderr can take one of the following forms:
25.2.10 by Roger Peppe
updated options comments
200
//   >>file
201
//       appends to file
202
//   >file
203
//       overwrites file
204
//   |command
205
//       pipes to the given command.
25.2.7 by Roger Peppe
changes in response to review
206
func (cfg *Config) SetOutput(kind OutputKind, stdout, stderr string) {
207
	out, _ := cfg.attrs["output"].(map[string]interface{})
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
208
	if out == nil {
209
		out = make(map[string]interface{})
210
	}
211
	if stderr == "" {
25.2.7 by Roger Peppe
changes in response to review
212
		out[string(kind)] = stdout
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
213
	} else {
25.2.7 by Roger Peppe
changes in response to review
214
		out[string(kind)] = []string{stdout, stderr}
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
215
	}
216
	cfg.attrs["output"] = out
217
}
218
25.2.10 by Roger Peppe
updated options comments
219
// AddSSHKey adds a pre-generated ssh key to the
25.2.8 by Roger Peppe
simplified key type.
220
// server keyring. Keys that are added like this will be
221
// written to /etc/ssh and new random keys will not
222
// be generated.
223
func (cfg *Config) AddSSHKey(keyType SSHKeyType, keyData string) {
224
	keys, _ := cfg.attrs["ssh_keys"].(map[SSHKeyType]string)
225
	if keys == nil {
226
		keys = make(map[SSHKeyType]string)
227
		cfg.attrs["ssh_keys"] = keys
228
	}
229
	keys[keyType] = keyData
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
230
}
231
25.2.10 by Roger Peppe
updated options comments
232
// SetDisableRoot sets whether ssh login is disabled to the root account
233
// via the ssh authorized key associated with the instance metadata.
234
// It is true by default.
235
func (cfg *Config) SetDisableRoot(disable bool) {
25.2.1 by Roger Peppe
add cloudinit package
236
	// note that disable_root defaults to true, so we include
25.2.10 by Roger Peppe
updated options comments
237
	// the option only if disable is false.
238
	cfg.set("disable_root", !disable, disable)
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
239
}
240
39.4.10 by Roger Peppe
SSHAddAuthorizedKey becomes SSHAddAuthorizedKeys and
241
// AddSSHAuthorizedKey adds a set of keys in
242
// ssh authorized_keys format (see ssh(8) for details)
243
// that will be added to ~/.ssh/authorized_keys for the
25.2.8 by Roger Peppe
simplified key type.
244
// configured user (see SetUser).
39.4.10 by Roger Peppe
SSHAddAuthorizedKey becomes SSHAddAuthorizedKeys and
245
func (cfg *Config) AddSSHAuthorizedKeys(keys string) {
246
	akeys, _ := cfg.attrs["ssh_authorized_keys"].([]string)
247
	lines := strings.Split(keys, "\n")
248
	for _, line := range lines {
249
		if line == "" || line[0] == '#' {
250
			continue
251
		}
252
		akeys = append(akeys, line)
253
	}
254
	cfg.attrs["ssh_authorized_keys"] = akeys
25.2.3 by Roger Peppe
move to more direct scheme for adding options.
255
}
256
1590.1.1 by Sidnei da Silva
- Move addScripts and addFile from environments/cloudinit.go to cloudinit.Config methods.
257
// AddScripts is a simple shorthand for calling AddRunCmd multiple times.
258
func (cfg *Config) AddScripts(scripts ...string) {
259
	for _, s := range scripts {
260
		cfg.AddRunCmd(s)
261
	}
262
}
263
264
// AddFile will add multiple run_cmd entries to safely set the contents of a
265
// specific file to the requested contents.
266
func (cfg *Config) AddFile(filename, data string, mode uint) {
1682.2.1 by Andrew Wilkins
Use printf instead of echo shell builtin
267
	// Note: recent versions of cloud-init have the "write_files"
268
	// module, which can write arbitrary files. We currently support
269
	// 12.04 LTS, which uses an older version of cloud-init without
270
	// this module.
1590.1.1 by Sidnei da Silva
- Move addScripts and addFile from environments/cloudinit.go to cloudinit.Config methods.
271
	p := shquote(filename)
1682.2.1 by Andrew Wilkins
Use printf instead of echo shell builtin
272
	// Don't use the shell's echo builtin here; the interpretation
273
	// of escape sequences differs between shells, namely bash and
274
	// dash. Instead, we use printf (or we could use /bin/echo).
1590.1.1 by Sidnei da Silva
- Move addScripts and addFile from environments/cloudinit.go to cloudinit.Config methods.
275
	cfg.AddScripts(
276
		fmt.Sprintf("install -m %o /dev/null %s", mode, p),
1682.2.2 by Andrew Wilkins
Single quote printf format
277
		fmt.Sprintf(`printf '%%s\n' %s > %s`, shquote(data), p),
1590.1.1 by Sidnei da Silva
- Move addScripts and addFile from environments/cloudinit.go to cloudinit.Config methods.
278
	)
279
}
280
281
func shquote(p string) string {
282
	return utils.ShQuote(p)
283
}
284
25.2.1 by Roger Peppe
add cloudinit package
285
// TODO
286
// byobu
287
// grub_dpkg
288
// mcollective
289
// phone_home
290
// puppet
291
// resizefs
292
// rightscale_userdata
293
// rsyslog
294
// scripts_per_boot
295
// scripts_per_instance
296
// scripts_per_once
297
// scripts_user
298
// set_hostname
299
// set_passwords
300
// ssh_import_id
301
// timezone
302
// update_etc_hosts
303
// update_hostname