~ubuntu-branches/ubuntu/saucy/juju-core/saucy-proposed

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/environs/cloudinit/cloudinit.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-07-11 17:18:27 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20130711171827-vjqkg40r0dlf7ys2
Tags: 1.11.2-0ubuntu1
* New upstream release.
* Make juju-core the default juju (LP: #1190634):
  - d/control: Add virtual package juju -> juju-core.
  - d/juju-core.postinst.in: Bump priority of alternatives over that of
    python juju packages.
* Enable for all architectures (LP: #1172505):
  - d/control: Version BD on golang-go to >= 2:1.1.1 to ensure CGO
    support for non-x86 archs, make juju-core Arch: any.
  - d/README.source: Dropped - no longer required.
* d/watch: Updated for new upstream tarball naming.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2012, 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
1
4
package cloudinit
2
5
 
3
6
import (
4
7
        "encoding/base64"
5
8
        "fmt"
 
9
        "path/filepath"
 
10
 
6
11
        "launchpad.net/goyaml"
 
12
 
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"
17
 
        "path"
18
23
)
19
24
 
20
25
// MachineConfig represents initialization information for a new juju machine.
29
34
        StateServerCert []byte
30
35
        StateServerKey  []byte
31
36
 
32
 
        // MongoPort specifies the TCP port that will be used
 
37
        // StatePort specifies the TCP port that will be used
33
38
        // by the MongoDB server. It must be non-zero
34
39
        // if StateServer is true.
35
 
        MongoPort int
 
40
        StatePort int
36
41
 
37
42
        // APIPort specifies the TCP port that will be used
38
43
        // by the API server. It must be non-zero
67
72
        // MachineId identifies the new machine.
68
73
        MachineId string
69
74
 
 
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
 
78
 
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 {
107
116
                return nil, err
108
117
        }
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")
112
 
        } else {
113
 
                c.SetAptUpgrade(true)
114
 
        }
115
118
        c.AddSSHAuthorizedKeys(cfg.AuthorizedKeys)
116
119
        c.AddPackage("git")
 
120
        // Perfectly reasonable to install lxc on environment instances and kvm
 
121
        // containers.
 
122
        if cfg.MachineContainerType != instance.LXC {
 
123
                c.AddPackage("lxc")
 
124
        }
117
125
 
118
126
        addScripts(c,
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)),
130
138
        )
131
139
 
 
140
        // TODO (thumper): work out how to pass the logging config to the children
132
141
        debugFlag := ""
133
142
        // TODO: disable debug mode by default when the system is stable.
134
 
        if true || log.Debug {
 
143
        if true {
135
144
                debugFlag = " --debug"
136
145
        }
137
146
 
138
 
        var syslogConfigRenderer syslog.SyslogConfigRenderer
 
147
        if err := cfg.addLogging(c); err != nil {
 
148
                return nil, err
 
149
        }
 
150
 
 
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)
 
159
        if err != nil {
 
160
                return nil, err
 
161
        }
 
162
 
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 {
147
171
                        return nil, err
148
172
                }
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")
153
177
                if err != nil {
154
178
                        return nil, err
155
179
                }
161
185
                                debugFlag,
162
186
                        "rm -rf "+shquote(acfg.Dir()),
163
187
                )
164
 
                syslogConfigRenderer = syslog.NewAccumulateConfig(
 
188
        }
 
189
 
 
190
        if err := cfg.addMachineAgentToBoot(c, machineTag, cfg.MachineId, debugFlag); err != nil {
 
191
                return nil, err
 
192
        }
 
193
 
 
194
        // general options
 
195
        c.SetAptUpgrade(true)
 
196
        c.SetAptUpdate(true)
 
197
        c.SetOutput(cloudinit.OutAll, "| tee -a /var/log/cloud-init-output.log", "")
 
198
        return c, nil
 
199
}
 
200
 
 
201
func (cfg *MachineConfig) addLogging(c *cloudinit.Config) error {
 
202
        var configRenderer syslog.SyslogConfigRenderer
 
203
        if cfg.StateServer {
 
204
                configRenderer = syslog.NewAccumulateConfig(
165
205
                        state.MachineTag(cfg.MachineId))
166
206
        } else {
167
 
                syslogConfigRenderer = syslog.NewForwardConfig(
 
207
                configRenderer = syslog.NewForwardConfig(
168
208
                        state.MachineTag(cfg.MachineId), cfg.stateHostAddrs())
169
209
        }
170
 
 
171
 
        content, err := syslogConfigRenderer.Render()
 
210
        content, err := configRenderer.Render()
172
211
        if err != nil {
173
 
                return nil, err
 
212
                return err
174
213
        }
175
214
        addScripts(c,
176
215
                fmt.Sprintf("cat > /etc/rsyslog.d/25-juju.conf << 'EOF'\n%sEOF\n", string(content)),
177
216
        )
178
217
        c.AddRunCmd("restart rsyslog")
179
 
 
180
 
        if _, err := addAgentToBoot(c, cfg, "machine",
181
 
                state.MachineTag(cfg.MachineId),
182
 
                fmt.Sprintf("--machine-id %s "+debugFlag, cfg.MachineId)); err != nil {
183
 
                return nil, err
184
 
        }
185
 
 
186
 
        // general options
187
 
        c.SetAptUpdate(true)
188
 
        c.SetOutput(cloudinit.OutAll, "| tee -a /var/log/cloud-init-output.log", "")
189
 
 
190
 
        return c, nil
 
218
        return nil
191
219
}
192
220
 
193
221
func addFile(c *cloudinit.Config, filename, data string, mode uint) {
199
227
}
200
228
 
201
229
func (cfg *MachineConfig) dataFile(name string) string {
202
 
        return path.Join(cfg.DataDir, name)
 
230
        return filepath.Join(cfg.DataDir, name)
203
231
}
204
232
 
205
233
func (cfg *MachineConfig) agentConfig(tag string) *agent.Conf {
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,
217
245
        }
 
246
        c.OldPassword = cfg.StateInfo.Password
 
247
 
218
248
        c.StateInfo.Addrs = cfg.stateHostAddrs()
219
249
        c.StateInfo.Tag = tag
220
250
        c.StateInfo.Password = ""
221
 
        c.OldPassword = cfg.StateInfo.Password
222
251
 
223
252
        c.APIInfo.Addrs = cfg.apiHostAddrs()
224
253
        c.APIInfo.Tag = tag
229
258
 
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()
235
264
        if err != nil {
239
268
        return acfg, nil
240
269
}
241
270
 
242
 
func addAgentToBoot(c *cloudinit.Config, cfg *MachineConfig, kind, tag, args string) (*agent.Conf, error) {
243
 
        acfg, err := addAgentInfo(c, cfg, tag)
244
 
        if err != nil {
245
 
                return nil, err
246
 
        }
247
 
 
 
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)))
254
278
 
255
 
        svc := upstart.NewService("jujud-" + tag)
256
 
        logPath := fmt.Sprintf("/var/log/juju/%s.log", tag)
257
 
        cmd := fmt.Sprintf(
258
 
                "%s/jujud %s"+
259
 
                        " --log-file %s"+
260
 
                        " --data-dir '%s'"+
261
 
                        " %s",
262
 
                toolsDir, kind,
263
 
                logPath,
264
 
                cfg.DataDir,
265
 
                args,
266
 
        )
267
 
        conf := &upstart.Conf{
268
 
                Service: *svc,
269
 
                Desc:    fmt.Sprintf("juju %s agent", tag),
270
 
                Cmd:     cmd,
271
 
                Out:     logPath,
272
 
        }
 
279
        name := "jujud-" + tag
 
280
        conf := upstart.MachineAgentUpstartService(name, toolsDir, cfg.DataDir, "/var/log/juju/", tag, machineId, logConfig)
273
281
        cmds, err := conf.InstallCommands()
274
282
        if err != nil {
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)
276
284
        }
277
285
        addScripts(c, cmds...)
278
 
        return acfg, nil
 
286
        return nil
279
287
}
280
288
 
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")
282
291
        addScripts(c,
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",
288
297
        )
289
 
        svc := upstart.NewService("juju-db")
290
 
        conf := &upstart.Conf{
291
 
                Service: *svc,
292
 
                Desc:    "juju state database",
293
 
                Cmd: "/usr/bin/mongod" +
294
 
                        " --auth" +
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) +
301
 
                        " --noprealloc" +
302
 
                        " --smallfiles",
303
 
        }
 
298
 
 
299
        conf := upstart.MongoUpstartService("juju-db", cfg.DataDir, dbDir, cfg.StatePort)
304
300
        cmds, err := conf.InstallCommands()
305
301
        if err != nil {
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)]
319
315
}
320
316
 
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))
329
325
        }
330
326
        if cfg.StateInfo != nil {
331
327
                hosts = append(hosts, cfg.StateInfo.Addrs...)
403
399
                if len(cfg.StateServerKey) == 0 {
404
400
                        return fmt.Errorf("missing state server private key")
405
401
                }
406
 
                if cfg.MongoPort == 0 {
407
 
                        return fmt.Errorf("missing mongo port")
 
402
                if cfg.StatePort == 0 {
 
403
                        return fmt.Errorf("missing state port")
408
404
                }
409
405
                if cfg.APIPort == 0 {
410
406
                        return fmt.Errorf("missing API port")