1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
6
11
"launchpad.net/goyaml"
7
13
"launchpad.net/juju-core/cloudinit"
8
14
"launchpad.net/juju-core/constraints"
9
15
"launchpad.net/juju-core/environs/agent"
10
16
"launchpad.net/juju-core/environs/config"
11
"launchpad.net/juju-core/log"
17
"launchpad.net/juju-core/instance"
12
18
"launchpad.net/juju-core/log/syslog"
13
19
"launchpad.net/juju-core/state"
14
20
"launchpad.net/juju-core/state/api"
15
21
"launchpad.net/juju-core/upstart"
16
22
"launchpad.net/juju-core/utils"
20
25
// MachineConfig represents initialization information for a new juju machine.
67
72
// MachineId identifies the new machine.
75
// MachineContainerType specifies the type of container that the machine
76
// is. If the machine is not a container, then the type is "".
77
MachineContainerType instance.ContainerType
70
79
// AuthorizedKeys specifies the keys that are allowed to
71
80
// connect to the machine (see cloudinit.SSHAddAuthorizedKeys)
72
81
// If no keys are supplied, there can be no ssh access to the node.
106
115
if err := verifyConfig(cfg); err != nil {
109
// TODO(dimitern) this is needed for raring, due to LP bug #1103881
110
if cfg.Tools.Series == "raring" {
111
addScripts(c, "apt-get upgrade -y")
113
c.SetAptUpgrade(true)
115
118
c.AddSSHAuthorizedKeys(cfg.AuthorizedKeys)
116
119
c.AddPackage("git")
120
// Perfectly reasonable to install lxc on environment instances and kvm
122
if cfg.MachineContainerType != instance.LXC {
119
127
"set -xe", // ensure we run all the scripts or abort.
129
137
fmt.Sprintf("echo -n %s > $bin/downloaded-url.txt", shquote(cfg.Tools.URL)),
140
// TODO (thumper): work out how to pass the logging config to the children
133
142
// TODO: disable debug mode by default when the system is stable.
134
if true || log.Debug {
135
144
debugFlag = " --debug"
138
var syslogConfigRenderer syslog.SyslogConfigRenderer
147
if err := cfg.addLogging(c); err != nil {
151
// We add the machine agent's configuration info
152
// before running bootstrap-state so that bootstrap-state
153
// has a chance to rerwrite it to change the password.
154
// It would be cleaner to change bootstrap-state to
155
// be responsible for starting the machine agent itself,
156
// but this would not be backwardly compatible.
157
machineTag := state.MachineTag(cfg.MachineId)
158
_, err := cfg.addAgentInfo(c, machineTag)
139
163
if cfg.StateServer {
140
164
if cfg.NeedMongoPPA() {
141
165
c.AddAptSource("ppa:juju/experimental", "1024R/C8068B11")
143
167
c.AddPackage("mongodb-server")
144
168
certKey := string(cfg.StateServerCert) + string(cfg.StateServerKey)
145
169
addFile(c, cfg.dataFile("server.pem"), certKey, 0600)
146
if err := addMongoToBoot(c, cfg); err != nil {
170
if err := cfg.addMongoToBoot(c); err != nil {
149
173
// We temporarily give bootstrap-state a directory
150
174
// of its own so that it can get the state info via the
151
175
// same mechanism as other jujud commands.
152
acfg, err := addAgentInfo(c, cfg, "bootstrap")
176
acfg, err := cfg.addAgentInfo(c, "bootstrap")
162
186
"rm -rf "+shquote(acfg.Dir()),
164
syslogConfigRenderer = syslog.NewAccumulateConfig(
190
if err := cfg.addMachineAgentToBoot(c, machineTag, cfg.MachineId, debugFlag); err != nil {
195
c.SetAptUpgrade(true)
197
c.SetOutput(cloudinit.OutAll, "| tee -a /var/log/cloud-init-output.log", "")
201
func (cfg *MachineConfig) addLogging(c *cloudinit.Config) error {
202
var configRenderer syslog.SyslogConfigRenderer
204
configRenderer = syslog.NewAccumulateConfig(
165
205
state.MachineTag(cfg.MachineId))
167
syslogConfigRenderer = syslog.NewForwardConfig(
207
configRenderer = syslog.NewForwardConfig(
168
208
state.MachineTag(cfg.MachineId), cfg.stateHostAddrs())
171
content, err := syslogConfigRenderer.Render()
210
content, err := configRenderer.Render()
176
215
fmt.Sprintf("cat > /etc/rsyslog.d/25-juju.conf << 'EOF'\n%sEOF\n", string(content)),
178
217
c.AddRunCmd("restart rsyslog")
180
if _, err := addAgentToBoot(c, cfg, "machine",
181
state.MachineTag(cfg.MachineId),
182
fmt.Sprintf("--machine-id %s "+debugFlag, cfg.MachineId)); err != nil {
188
c.SetOutput(cloudinit.OutAll, "| tee -a /var/log/cloud-init-output.log", "")
193
221
func addFile(c *cloudinit.Config, filename, data string, mode uint) {
211
239
APIInfo: &apiInfo,
212
240
StateServerCert: cfg.StateServerCert,
213
241
StateServerKey: cfg.StateServerKey,
214
MongoPort: cfg.MongoPort,
242
StatePort: cfg.StatePort,
215
243
APIPort: cfg.APIPort,
216
244
MachineNonce: cfg.MachineNonce,
246
c.OldPassword = cfg.StateInfo.Password
218
248
c.StateInfo.Addrs = cfg.stateHostAddrs()
219
249
c.StateInfo.Tag = tag
220
250
c.StateInfo.Password = ""
221
c.OldPassword = cfg.StateInfo.Password
223
252
c.APIInfo.Addrs = cfg.apiHostAddrs()
224
253
c.APIInfo.Tag = tag
230
259
// addAgentInfo adds agent-required information to the agent's directory
231
260
// and returns the agent directory name.
232
func addAgentInfo(c *cloudinit.Config, cfg *MachineConfig, tag string) (*agent.Conf, error) {
261
func (cfg *MachineConfig) addAgentInfo(c *cloudinit.Config, tag string) (*agent.Conf, error) {
233
262
acfg := cfg.agentConfig(tag)
234
263
cmds, err := acfg.WriteCommands()
242
func addAgentToBoot(c *cloudinit.Config, cfg *MachineConfig, kind, tag, args string) (*agent.Conf, error) {
243
acfg, err := addAgentInfo(c, cfg, tag)
271
func (cfg *MachineConfig) addMachineAgentToBoot(c *cloudinit.Config, tag, machineId, logConfig string) error {
248
272
// Make the agent run via a symbolic link to the actual tools
249
273
// directory, so it can upgrade itself without needing to change
250
274
// the upstart script.
252
276
// TODO(dfc) ln -nfs, so it doesn't fail if for some reason that the target already exists
253
277
addScripts(c, fmt.Sprintf("ln -s %v %s", cfg.Tools.Binary, shquote(toolsDir)))
255
svc := upstart.NewService("jujud-" + tag)
256
logPath := fmt.Sprintf("/var/log/juju/%s.log", tag)
267
conf := &upstart.Conf{
269
Desc: fmt.Sprintf("juju %s agent", tag),
279
name := "jujud-" + tag
280
conf := upstart.MachineAgentUpstartService(name, toolsDir, cfg.DataDir, "/var/log/juju/", tag, machineId, logConfig)
273
281
cmds, err := conf.InstallCommands()
275
return nil, fmt.Errorf("cannot make cloud-init upstart script for the %s agent: %v", tag, err)
283
return fmt.Errorf("cannot make cloud-init upstart script for the %s agent: %v", tag, err)
277
285
addScripts(c, cmds...)
281
func addMongoToBoot(c *cloudinit.Config, cfg *MachineConfig) error {
289
func (cfg *MachineConfig) addMongoToBoot(c *cloudinit.Config) error {
290
dbDir := filepath.Join(cfg.DataDir, "db")
283
"mkdir -p /var/lib/juju/db/journal",
292
"mkdir -p "+dbDir+"/journal",
284
293
// Otherwise we get three files with 100M+ each, which takes time.
285
"dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc.0",
286
"dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc.1",
287
"dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc.2",
294
"dd bs=1M count=1 if=/dev/zero of="+dbDir+"/journal/prealloc.0",
295
"dd bs=1M count=1 if=/dev/zero of="+dbDir+"/journal/prealloc.1",
296
"dd bs=1M count=1 if=/dev/zero of="+dbDir+"/journal/prealloc.2",
289
svc := upstart.NewService("juju-db")
290
conf := &upstart.Conf{
292
Desc: "juju state database",
293
Cmd: "/usr/bin/mongod" +
295
" --dbpath=/var/lib/juju/db" +
296
" --sslOnNormalPorts" +
297
" --sslPEMKeyFile " + shquote(cfg.dataFile("server.pem")) +
298
" --sslPEMKeyPassword ignored" +
299
" --bind_ip 0.0.0.0" +
300
" --port " + fmt.Sprint(cfg.MongoPort) +
299
conf := upstart.MongoUpstartService("juju-db", cfg.DataDir, dbDir, cfg.StatePort)
304
300
cmds, err := conf.InstallCommands()
306
302
return fmt.Errorf("cannot make cloud-init upstart script for the state database: %v", err)
313
309
// to use as a directory for storing the tools executables in
314
310
// by using the last element stripped of its extension.
315
311
func versionDir(toolsURL string) string {
316
name := path.Base(toolsURL)
317
ext := path.Ext(name)
312
name := filepath.Base(toolsURL)
313
ext := filepath.Ext(name)
318
314
return name[:len(name)-len(ext)]
325
321
func (cfg *MachineConfig) stateHostAddrs() []string {
326
322
var hosts []string
327
323
if cfg.StateServer {
328
hosts = append(hosts, fmt.Sprintf("localhost:%d", cfg.MongoPort))
324
hosts = append(hosts, fmt.Sprintf("localhost:%d", cfg.StatePort))
330
326
if cfg.StateInfo != nil {
331
327
hosts = append(hosts, cfg.StateInfo.Addrs...)