~juju-qa/ubuntu/yakkety/juju/2.0-rc3-again

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-04-24 22:34:47 UTC
  • Revision ID: package-import@ubuntu.com-20130424223447-f0qdji7ubnyo0s71
Tags: upstream-1.10.0.1
ImportĀ upstreamĀ versionĀ 1.10.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package cloudinit_test
 
2
 
 
3
import (
 
4
        "encoding/base64"
 
5
        . "launchpad.net/gocheck"
 
6
        "launchpad.net/goyaml"
 
7
        cloudinit_core "launchpad.net/juju-core/cloudinit"
 
8
        "launchpad.net/juju-core/constraints"
 
9
        "launchpad.net/juju-core/environs/cloudinit"
 
10
        "launchpad.net/juju-core/environs/config"
 
11
        "launchpad.net/juju-core/state"
 
12
        "launchpad.net/juju-core/state/api"
 
13
        "launchpad.net/juju-core/testing"
 
14
        "launchpad.net/juju-core/version"
 
15
        "regexp"
 
16
        "strings"
 
17
)
 
18
 
 
19
// Use local suite since this file lives in the ec2 package
 
20
// for testing internals.
 
21
type cloudinitSuite struct {
 
22
        testing.LoggingSuite
 
23
}
 
24
 
 
25
var _ = Suite(&cloudinitSuite{})
 
26
 
 
27
var envConstraints = constraints.MustParse("mem=2G")
 
28
 
 
29
type cloudinitTest struct {
 
30
        cfg           cloudinit.MachineConfig
 
31
        setEnvConfig  bool
 
32
        expectScripts string
 
33
}
 
34
 
 
35
func minimalConfig(c *C) *config.Config {
 
36
        cfg, err := config.New(map[string]interface{}{
 
37
                "type":            "test",
 
38
                "name":            "test-name",
 
39
                "default-series":  "test-series",
 
40
                "authorized-keys": "test-keys",
 
41
                "ca-cert":         testing.CACert,
 
42
                "ca-private-key":  "",
 
43
        })
 
44
        c.Assert(err, IsNil)
 
45
        return cfg
 
46
}
 
47
 
 
48
// Each test gives a cloudinit config - we check the
 
49
// output to see if it looks correct.
 
50
var cloudinitTests = []cloudinitTest{
 
51
        {
 
52
                // precise state server
 
53
                cfg: cloudinit.MachineConfig{
 
54
                        MachineId:      "0",
 
55
                        AuthorizedKeys: "sshkey1",
 
56
                        // precise currently needs mongo from PPA
 
57
                        Tools:           newSimpleTools("1.2.3-precise-amd64"),
 
58
                        StateServer:     true,
 
59
                        StateServerCert: serverCert,
 
60
                        StateServerKey:  serverKey,
 
61
                        MongoPort:       37017,
 
62
                        APIPort:         17070,
 
63
                        MachineNonce:    "FAKE_NONCE",
 
64
                        StateInfo: &state.Info{
 
65
                                Password: "arble",
 
66
                                CACert:   []byte("CA CERT\n" + testing.CACert),
 
67
                        },
 
68
                        APIInfo: &api.Info{
 
69
                                Password: "bletch",
 
70
                                CACert:   []byte("CA CERT\n" + testing.CACert),
 
71
                        },
 
72
                        Constraints: envConstraints,
 
73
                        DataDir:     "/var/lib/juju",
 
74
                },
 
75
                setEnvConfig: true,
 
76
                expectScripts: `
 
77
set -xe
 
78
mkdir -p /var/lib/juju
 
79
mkdir -p /var/log/juju
 
80
bin='/var/lib/juju/tools/1\.2\.3-precise-amd64'
 
81
mkdir -p \$bin
 
82
wget --no-verbose -O - 'http://foo\.com/tools/juju1\.2\.3-precise-amd64\.tgz' \| tar xz -C \$bin
 
83
echo -n 'http://foo\.com/tools/juju1\.2\.3-precise-amd64\.tgz' > \$bin/downloaded-url\.txt
 
84
echo 'SERVER CERT\\n[^']*SERVER KEY\\n[^']*' > '/var/lib/juju/server\.pem'
 
85
chmod 600 '/var/lib/juju/server\.pem'
 
86
mkdir -p /var/lib/juju/db/journal
 
87
dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc\.0
 
88
dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc\.1
 
89
dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc\.2
 
90
cat >> /etc/init/juju-db\.conf << 'EOF'\\ndescription "juju state database"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nexec /usr/bin/mongod --auth --dbpath=/var/lib/juju/db --sslOnNormalPorts --sslPEMKeyFile '/var/lib/juju/server\.pem' --sslPEMKeyPassword ignored --bind_ip 0\.0\.0\.0 --port 37017 --noprealloc --smallfiles\\nEOF\\n
 
91
start juju-db
 
92
mkdir -p '/var/lib/juju/agents/bootstrap'
 
93
echo 'datadir: /var/lib/juju\\nstateservercert:\\n[^']+stateserverkey:\\n[^']+mongoport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n  addrs:\\n  - localhost:37017\\n  cacert:\\n[^']+  tag: bootstrap\\n  password: ""\\noldapipassword: ""\\napiinfo:\\n  addrs:\\n  - localhost:17070\\n  cacert:\\n[^']+  tag: bootstrap\\n  password: ""\\n' > '/var/lib/juju/agents/bootstrap/agent\.conf'
 
94
chmod 600 '/var/lib/juju/agents/bootstrap/agent\.conf'
 
95
/var/lib/juju/tools/1\.2\.3-precise-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --constraints 'mem=2048M' --debug
 
96
rm -rf '/var/lib/juju/agents/bootstrap'
 
97
cat > /etc/rsyslog.d/25-juju.conf << 'EOF'\\n\\n\$ModLoad imfile\\n\\n\$InputFilePollInterval 5\\n\$InputFileName /var/log/juju/machine-0.log\\n\$InputFileTag local-juju-machine-0:\\n\$InputFileStateFile machine-0\\n\$InputRunFileMonitor\\n\\n\$ModLoad imudp\\n\$UDPServerRun 514\\n\\n# Messages received from remote rsyslog machines contain a leading space so we\\n# need to account for that.\\n\$template JujuLogFormatLocal,\"%HOSTNAME%:%msg:::drop-last-lf%\\n\"\\n\$template JujuLogFormat,\"%HOSTNAME%:%msg:2:2048:drop-last-lf%\\n\"\\n\\n:syslogtag, startswith, \"juju-\" /var/log/juju/all-machines.log;JujuLogFormat\\n:syslogtag, startswith, \"local-juju-\" /var/log/juju/all-machines.log;JujuLogFormatLocal\\n& ~\\nEOF\\n
 
98
restart rsyslog
 
99
mkdir -p '/var/lib/juju/agents/machine-0'
 
100
echo 'datadir: /var/lib/juju\\nstateservercert:\\n[^']+stateserverkey:\\n[^']+mongoport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n  addrs:\\n  - localhost:37017\\n  cacert:\\n[^']+  tag: machine-0\\n  password: ""\\noldapipassword: ""\\napiinfo:\\n  addrs:\\n  - localhost:17070\\n  cacert:\\n[^']+  tag: machine-0\\n  password: ""\\n' > '/var/lib/juju/agents/machine-0/agent\.conf'
 
101
chmod 600 '/var/lib/juju/agents/machine-0/agent\.conf'
 
102
ln -s 1\.2\.3-precise-amd64 '/var/lib/juju/tools/machine-0'
 
103
cat >> /etc/init/jujud-machine-0\.conf << 'EOF'\\ndescription "juju machine-0 agent"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nexec /var/lib/juju/tools/machine-0/jujud machine --log-file /var/log/juju/machine-0\.log --data-dir '/var/lib/juju' --machine-id 0  --debug >> /var/log/juju/machine-0\.log 2>&1\\nEOF\\n
 
104
start jujud-machine-0
 
105
`,
 
106
        }, {
 
107
                // raring state server
 
108
                cfg: cloudinit.MachineConfig{
 
109
                        MachineId:      "0",
 
110
                        AuthorizedKeys: "sshkey1",
 
111
                        // raring provides mongo in the archive
 
112
                        Tools:           newSimpleTools("1.2.3-raring-amd64"),
 
113
                        StateServer:     true,
 
114
                        StateServerCert: serverCert,
 
115
                        StateServerKey:  serverKey,
 
116
                        MongoPort:       37017,
 
117
                        APIPort:         17070,
 
118
                        MachineNonce:    "FAKE_NONCE",
 
119
                        StateInfo: &state.Info{
 
120
                                Password: "arble",
 
121
                                CACert:   []byte("CA CERT\n" + testing.CACert),
 
122
                        },
 
123
                        APIInfo: &api.Info{
 
124
                                Password: "bletch",
 
125
                                CACert:   []byte("CA CERT\n" + testing.CACert),
 
126
                        },
 
127
                        Constraints: envConstraints,
 
128
                        DataDir:     "/var/lib/juju",
 
129
                },
 
130
                setEnvConfig: true,
 
131
                expectScripts: `
 
132
apt-get upgrade -y
 
133
set -xe
 
134
mkdir -p /var/lib/juju
 
135
mkdir -p /var/log/juju
 
136
bin='/var/lib/juju/tools/1\.2\.3-raring-amd64'
 
137
mkdir -p \$bin
 
138
wget --no-verbose -O - 'http://foo\.com/tools/juju1\.2\.3-raring-amd64\.tgz' \| tar xz -C \$bin
 
139
echo -n 'http://foo\.com/tools/juju1\.2\.3-raring-amd64\.tgz' > \$bin/downloaded-url\.txt
 
140
echo 'SERVER CERT\\n[^']*SERVER KEY\\n[^']*' > '/var/lib/juju/server\.pem'
 
141
chmod 600 '/var/lib/juju/server\.pem'
 
142
mkdir -p /var/lib/juju/db/journal
 
143
dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc\.0
 
144
dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc\.1
 
145
dd bs=1M count=1 if=/dev/zero of=/var/lib/juju/db/journal/prealloc\.2
 
146
cat >> /etc/init/juju-db\.conf << 'EOF'\\ndescription "juju state database"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nexec /usr/bin/mongod --auth --dbpath=/var/lib/juju/db --sslOnNormalPorts --sslPEMKeyFile '/var/lib/juju/server\.pem' --sslPEMKeyPassword ignored --bind_ip 0\.0\.0\.0 --port 37017 --noprealloc --smallfiles\\nEOF\\n
 
147
start juju-db
 
148
mkdir -p '/var/lib/juju/agents/bootstrap'
 
149
echo 'datadir: /var/lib/juju\\nstateservercert:\\n[^']+stateserverkey:\\n[^']+mongoport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n  addrs:\\n  - localhost:37017\\n  cacert:\\n[^']+  tag: bootstrap\\n  password: ""\\noldapipassword: ""\\napiinfo:\\n  addrs:\\n  - localhost:17070\\n  cacert:\\n[^']+  tag: bootstrap\\n  password: ""\\n' > '/var/lib/juju/agents/bootstrap/agent\.conf'
 
150
chmod 600 '/var/lib/juju/agents/bootstrap/agent\.conf'
 
151
/var/lib/juju/tools/1\.2\.3-raring-amd64/jujud bootstrap-state --data-dir '/var/lib/juju' --env-config '[^']*' --constraints 'mem=2048M' --debug
 
152
rm -rf '/var/lib/juju/agents/bootstrap'
 
153
cat > /etc/rsyslog.d/25-juju.conf << 'EOF'\\n\\n\$ModLoad imfile\\n\\n\$InputFilePollInterval 5\\n\$InputFileName /var/log/juju/machine-0.log\\n\$InputFileTag local-juju-machine-0:\\n\$InputFileStateFile machine-0\\n\$InputRunFileMonitor\\n\\n\$ModLoad imudp\\n\$UDPServerRun 514\\n\\n# Messages received from remote rsyslog machines contain a leading space so we\\n# need to account for that.\\n\$template JujuLogFormatLocal,\"%HOSTNAME%:%msg:::drop-last-lf%\\n\"\\n\$template JujuLogFormat,\"%HOSTNAME%:%msg:2:2048:drop-last-lf%\\n\"\\n\\n:syslogtag, startswith, \"juju-\" /var/log/juju/all-machines.log;JujuLogFormat\\n:syslogtag, startswith, \"local-juju-\" /var/log/juju/all-machines.log;JujuLogFormatLocal\\n& ~\\nEOF\\n
 
154
restart rsyslog
 
155
mkdir -p '/var/lib/juju/agents/machine-0'
 
156
echo 'datadir: /var/lib/juju\\nstateservercert:\\n[^']+stateserverkey:\\n[^']+mongoport: 37017\\napiport: 17070\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n  addrs:\\n  - localhost:37017\\n  cacert:\\n[^']+  tag: machine-0\\n  password: ""\\noldapipassword: ""\\napiinfo:\\n  addrs:\\n  - localhost:17070\\n  cacert:\\n[^']+  tag: machine-0\\n  password: ""\\n' > '/var/lib/juju/agents/machine-0/agent\.conf'
 
157
chmod 600 '/var/lib/juju/agents/machine-0/agent\.conf'
 
158
ln -s 1\.2\.3-raring-amd64 '/var/lib/juju/tools/machine-0'
 
159
cat >> /etc/init/jujud-machine-0\.conf << 'EOF'\\ndescription "juju machine-0 agent"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nexec /var/lib/juju/tools/machine-0/jujud machine --log-file /var/log/juju/machine-0\.log --data-dir '/var/lib/juju' --machine-id 0  --debug >> /var/log/juju/machine-0\.log 2>&1\\nEOF\\n
 
160
start jujud-machine-0
 
161
`,
 
162
        }, {
 
163
                cfg: cloudinit.MachineConfig{
 
164
                        MachineId:      "99",
 
165
                        AuthorizedKeys: "sshkey1",
 
166
                        DataDir:        "/var/lib/juju",
 
167
                        StateServer:    false,
 
168
                        Tools:          newSimpleTools("1.2.3-linux-amd64"),
 
169
                        MachineNonce:   "FAKE_NONCE",
 
170
                        StateInfo: &state.Info{
 
171
                                Addrs:    []string{"state-addr.example.com:12345"},
 
172
                                Tag:      "machine-99",
 
173
                                Password: "arble",
 
174
                                CACert:   []byte("CA CERT\n" + testing.CACert),
 
175
                        },
 
176
                        APIInfo: &api.Info{
 
177
                                Addrs:    []string{"state-addr.example.com:54321"},
 
178
                                Tag:      "machine-99",
 
179
                                Password: "bletch",
 
180
                                CACert:   []byte("CA CERT\n" + testing.CACert),
 
181
                        },
 
182
                },
 
183
                expectScripts: `
 
184
set -xe
 
185
mkdir -p /var/lib/juju
 
186
mkdir -p /var/log/juju
 
187
bin='/var/lib/juju/tools/1\.2\.3-linux-amd64'
 
188
mkdir -p \$bin
 
189
wget --no-verbose -O - 'http://foo\.com/tools/juju1\.2\.3-linux-amd64\.tgz' \| tar xz -C \$bin
 
190
echo -n 'http://foo\.com/tools/juju1\.2\.3-linux-amd64\.tgz' > \$bin/downloaded-url\.txt
 
191
cat > /etc/rsyslog.d/25-juju.conf << 'EOF'\\n\\n\$ModLoad imfile\\n\\n\$InputFilePollInterval 5\\n\$InputFileName /var/log/juju/machine-99.log\\n\$InputFileTag juju-machine-99:\\n\$InputFileStateFile machine-99\\n\$InputRunFileMonitor\\n\\n:syslogtag, startswith, \"juju-\" @state-addr.example.com:514\\n& ~\\nEOF\\n
 
192
restart rsyslog
 
193
mkdir -p '/var/lib/juju/agents/machine-99'
 
194
echo 'datadir: /var/lib/juju\\noldpassword: arble\\nmachinenonce: FAKE_NONCE\\nstateinfo:\\n  addrs:\\n  - state-addr\.example\.com:12345\\n  cacert:\\n[^']+  tag: machine-99\\n  password: ""\\noldapipassword: ""\\napiinfo:\\n  addrs:\\n  - state-addr\.example\.com:54321\\n  cacert:\\n[^']+  tag: machine-99\\n  password: ""\\n' > '/var/lib/juju/agents/machine-99/agent\.conf'
 
195
chmod 600 '/var/lib/juju/agents/machine-99/agent\.conf'
 
196
ln -s 1\.2\.3-linux-amd64 '/var/lib/juju/tools/machine-99'
 
197
cat >> /etc/init/jujud-machine-99\.conf << 'EOF'\\ndescription "juju machine-99 agent"\\nauthor "Juju Team <juju@lists\.ubuntu\.com>"\\nstart on runlevel \[2345\]\\nstop on runlevel \[!2345\]\\nrespawn\\nnormal exit 0\\n\\nexec /var/lib/juju/tools/machine-99/jujud machine --log-file /var/log/juju/machine-99\.log --data-dir '/var/lib/juju' --machine-id 99  --debug >> /var/log/juju/machine-99\.log 2>&1\\nEOF\\n
 
198
start jujud-machine-99
 
199
`,
 
200
        },
 
201
}
 
202
 
 
203
func newSimpleTools(vers string) *state.Tools {
 
204
        return &state.Tools{
 
205
                URL:    "http://foo.com/tools/juju" + vers + ".tgz",
 
206
                Binary: version.MustParseBinary(vers),
 
207
        }
 
208
}
 
209
 
 
210
// check that any --env-config $base64 is valid and matches t.cfg.Config
 
211
func checkEnvConfig(c *C, cfg *config.Config, x map[interface{}]interface{}, scripts []string) {
 
212
        re := regexp.MustCompile(`--env-config '([\w,=]+)'`)
 
213
        found := false
 
214
        for _, s := range scripts {
 
215
                m := re.FindStringSubmatch(s)
 
216
                if m == nil {
 
217
                        continue
 
218
                }
 
219
                found = true
 
220
                buf, err := base64.StdEncoding.DecodeString(m[1])
 
221
                c.Assert(err, IsNil)
 
222
                var actual map[string]interface{}
 
223
                err = goyaml.Unmarshal(buf, &actual)
 
224
                c.Assert(err, IsNil)
 
225
                c.Assert(cfg.AllAttrs(), DeepEquals, actual)
 
226
        }
 
227
        c.Assert(found, Equals, true)
 
228
}
 
229
 
 
230
// TestCloudInit checks that the output from the various tests
 
231
// in cloudinitTests is well formed.
 
232
func (*cloudinitSuite) TestCloudInit(c *C) {
 
233
        for i, test := range cloudinitTests {
 
234
                c.Logf("test %d", i)
 
235
                if test.setEnvConfig {
 
236
                        test.cfg.Config = minimalConfig(c)
 
237
                }
 
238
                ci, err := cloudinit.New(&test.cfg)
 
239
                c.Assert(err, IsNil)
 
240
                c.Check(ci, NotNil)
 
241
                // render the cloudinit config to bytes, and then
 
242
                // back to a map so we can introspect it without
 
243
                // worrying about internal details of the cloudinit
 
244
                // package.
 
245
                data, err := ci.Render()
 
246
                c.Assert(err, IsNil)
 
247
 
 
248
                x := make(map[interface{}]interface{})
 
249
                err = goyaml.Unmarshal(data, &x)
 
250
                c.Assert(err, IsNil)
 
251
 
 
252
                // TODO(dimitern) raring does apt-get upgrade differently, due to LP bug #1103881
 
253
                if test.cfg.Tools.Series != "raring" {
 
254
                        c.Check(x["apt_upgrade"], Equals, true)
 
255
                }
 
256
                c.Check(x["apt_update"], Equals, true)
 
257
 
 
258
                scripts := getScripts(x)
 
259
                scriptDiff(c, scripts, test.expectScripts)
 
260
                if test.cfg.Config != nil {
 
261
                        checkEnvConfig(c, test.cfg.Config, x, scripts)
 
262
                }
 
263
                checkPackage(c, x, "git", true)
 
264
                if test.cfg.StateServer {
 
265
                        checkPackage(c, x, "mongodb-server", true)
 
266
                        source := struct{ source, key string }{
 
267
                                source: "ppa:juju/experimental",
 
268
                                key:    "1024R/C8068B11",
 
269
                        }
 
270
                        checkAptSource(c, x, source, test.cfg.NeedMongoPPA())
 
271
                }
 
272
        }
 
273
}
 
274
 
 
275
func (*cloudinitSuite) TestCloudInitConfigure(c *C) {
 
276
        for i, test := range cloudinitTests {
 
277
                test.cfg.Config = minimalConfig(c)
 
278
                c.Logf("test %d (Configure)", i)
 
279
                cloudcfg := cloudinit_core.New()
 
280
                ci, err := cloudinit.Configure(&test.cfg, cloudcfg)
 
281
                c.Assert(err, IsNil)
 
282
                c.Check(ci, NotNil)
 
283
        }
 
284
}
 
285
 
 
286
func (*cloudinitSuite) TestCloudInitConfigureUsesGivenConfig(c *C) {
 
287
        // Create a simple cloudinit config with a 'runcmd' statement.
 
288
        cloudcfg := cloudinit_core.New()
 
289
        script := "test script"
 
290
        cloudcfg.AddRunCmd(script)
 
291
        cloudinitTests[0].cfg.Config = minimalConfig(c)
 
292
        ci, err := cloudinit.Configure(&cloudinitTests[0].cfg, cloudcfg)
 
293
        c.Assert(err, IsNil)
 
294
        c.Check(ci, NotNil)
 
295
        data, err := ci.Render()
 
296
        c.Assert(err, IsNil)
 
297
 
 
298
        ciContent := make(map[interface{}]interface{})
 
299
        err = goyaml.Unmarshal(data, &ciContent)
 
300
        c.Assert(err, IsNil)
 
301
        // The 'runcmd' statement is at the beginning of the list
 
302
        // of 'runcmd' statements.
 
303
        runCmd := ciContent["runcmd"].([]interface{})
 
304
        c.Check(runCmd[0], Equals, script)
 
305
}
 
306
 
 
307
func getScripts(x map[interface{}]interface{}) []string {
 
308
        var scripts []string
 
309
        for _, s := range x["runcmd"].([]interface{}) {
 
310
                scripts = append(scripts, s.(string))
 
311
        }
 
312
        return scripts
 
313
}
 
314
 
 
315
func scriptDiff(c *C, got []string, expect string) {
 
316
        for _, s := range got {
 
317
                c.Logf("script: %s", regexp.QuoteMeta(strings.Replace(s, "\n", "\\n", -1)))
 
318
        }
 
319
        pats := strings.Split(strings.Trim(expect, "\n"), "\n")
 
320
        for i := 0; ; i++ {
 
321
                switch {
 
322
                case i == len(got) && i == len(pats):
 
323
                        return
 
324
                case i == len(got):
 
325
                        c.Fatalf("too few scripts found (expected %q at line %d)", pats[i], i)
 
326
                case i == len(pats):
 
327
                        c.Fatalf("too many scripts found (got %q at line %d)", got[i], i)
 
328
                }
 
329
                script := strings.Replace(got[i], "\n", "\\n", -1) // make .* work
 
330
                c.Assert(script, Matches, pats[i], Commentf("line %d", i))
 
331
        }
 
332
}
 
333
 
 
334
// CheckPackage checks that the cloudinit will or won't install the given
 
335
// package, depending on the value of match.
 
336
func checkPackage(c *C, x map[interface{}]interface{}, pkg string, match bool) {
 
337
        pkgs0 := x["packages"]
 
338
        if pkgs0 == nil {
 
339
                if match {
 
340
                        c.Errorf("cloudinit has no entry for packages")
 
341
                }
 
342
                return
 
343
        }
 
344
 
 
345
        pkgs := pkgs0.([]interface{})
 
346
 
 
347
        found := false
 
348
        for _, p0 := range pkgs {
 
349
                p := p0.(string)
 
350
                if p == pkg {
 
351
                        found = true
 
352
                }
 
353
        }
 
354
        switch {
 
355
        case match && !found:
 
356
                c.Errorf("package %q not found in %v", pkg, pkgs)
 
357
        case !match && found:
 
358
                c.Errorf("%q found but not expected in %v", pkg, pkgs)
 
359
        }
 
360
}
 
361
 
 
362
// CheckAptSources checks that the cloudinit will or won't install the given
 
363
// source, depending on the value of match.
 
364
func checkAptSource(c *C, x map[interface{}]interface{}, source struct{ source, key string }, match bool) {
 
365
        sources0 := x["apt_sources"]
 
366
        if sources0 == nil {
 
367
                if match {
 
368
                        c.Errorf("cloudinit has no entry for apt_sources")
 
369
                }
 
370
                return
 
371
        }
 
372
 
 
373
        sources := sources0.([]interface{})
 
374
 
 
375
        found := false
 
376
        for _, s0 := range sources {
 
377
                s := s0.(map[interface{}]interface{})
 
378
                if s["source"] == source.source && s["key"] == source.key {
 
379
                        found = true
 
380
                }
 
381
        }
 
382
        switch {
 
383
        case match && !found:
 
384
                c.Errorf("source %q not found in %v", source, sources)
 
385
        case !match && found:
 
386
                c.Errorf("%q found but not expected in %v", source, sources)
 
387
        }
 
388
}
 
389
 
 
390
// When mutate is called on a known-good MachineConfig,
 
391
// there should be an error complaining about the missing
 
392
// field named by the adjacent err.
 
393
var verifyTests = []struct {
 
394
        err    string
 
395
        mutate func(*cloudinit.MachineConfig)
 
396
}{
 
397
        {"invalid machine id", func(cfg *cloudinit.MachineConfig) {
 
398
                cfg.MachineId = "-1"
 
399
        }},
 
400
        {"missing environment configuration", func(cfg *cloudinit.MachineConfig) {
 
401
                cfg.Config = nil
 
402
        }},
 
403
        {"missing state info", func(cfg *cloudinit.MachineConfig) {
 
404
                cfg.StateInfo = nil
 
405
        }},
 
406
        {"missing API info", func(cfg *cloudinit.MachineConfig) {
 
407
                cfg.APIInfo = nil
 
408
        }},
 
409
        {"missing state hosts", func(cfg *cloudinit.MachineConfig) {
 
410
                cfg.StateServer = false
 
411
                cfg.StateInfo = &state.Info{
 
412
                        Tag:    "machine-99",
 
413
                        CACert: []byte(testing.CACert),
 
414
                }
 
415
                cfg.APIInfo = &api.Info{
 
416
                        Addrs:  []string{"foo:35"},
 
417
                        Tag:    "machine-99",
 
418
                        CACert: []byte(testing.CACert),
 
419
                }
 
420
        }},
 
421
        {"missing API hosts", func(cfg *cloudinit.MachineConfig) {
 
422
                cfg.StateServer = false
 
423
                cfg.StateInfo = &state.Info{
 
424
                        Addrs:  []string{"foo:35"},
 
425
                        Tag:    "machine-99",
 
426
                        CACert: []byte(testing.CACert),
 
427
                }
 
428
                cfg.APIInfo = &api.Info{
 
429
                        Tag:    "machine-99",
 
430
                        CACert: []byte(testing.CACert),
 
431
                }
 
432
        }},
 
433
        {"missing CA certificate", func(cfg *cloudinit.MachineConfig) {
 
434
                cfg.StateInfo = &state.Info{Addrs: []string{"host:98765"}}
 
435
        }},
 
436
        {"missing CA certificate", func(cfg *cloudinit.MachineConfig) {
 
437
                cfg.StateServer = false
 
438
                cfg.StateInfo = &state.Info{
 
439
                        Tag:   "machine-99",
 
440
                        Addrs: []string{"host:98765"},
 
441
                }
 
442
        }},
 
443
        {"missing state server certificate", func(cfg *cloudinit.MachineConfig) {
 
444
                cfg.StateServerCert = []byte{}
 
445
        }},
 
446
        {"missing state server private key", func(cfg *cloudinit.MachineConfig) {
 
447
                cfg.StateServerKey = []byte{}
 
448
        }},
 
449
        {"missing var directory", func(cfg *cloudinit.MachineConfig) {
 
450
                cfg.DataDir = ""
 
451
        }},
 
452
        {"missing tools", func(cfg *cloudinit.MachineConfig) {
 
453
                cfg.Tools = nil
 
454
        }},
 
455
        {"missing tools URL", func(cfg *cloudinit.MachineConfig) {
 
456
                cfg.Tools = &state.Tools{}
 
457
        }},
 
458
        {"entity tag must match started machine", func(cfg *cloudinit.MachineConfig) {
 
459
                cfg.StateServer = false
 
460
                info := *cfg.StateInfo
 
461
                info.Tag = "machine-0"
 
462
                cfg.StateInfo = &info
 
463
        }},
 
464
        {"entity tag must match started machine", func(cfg *cloudinit.MachineConfig) {
 
465
                cfg.StateServer = false
 
466
                info := *cfg.StateInfo
 
467
                info.Tag = ""
 
468
                cfg.StateInfo = &info
 
469
        }},
 
470
        {"entity tag must match started machine", func(cfg *cloudinit.MachineConfig) {
 
471
                cfg.StateServer = false
 
472
                info := *cfg.APIInfo
 
473
                info.Tag = "machine-0"
 
474
                cfg.APIInfo = &info
 
475
        }},
 
476
        {"entity tag must match started machine", func(cfg *cloudinit.MachineConfig) {
 
477
                cfg.StateServer = false
 
478
                info := *cfg.APIInfo
 
479
                info.Tag = ""
 
480
                cfg.APIInfo = &info
 
481
        }},
 
482
        {"entity tag must be blank when starting a state server", func(cfg *cloudinit.MachineConfig) {
 
483
                info := *cfg.StateInfo
 
484
                info.Tag = "machine-0"
 
485
                cfg.StateInfo = &info
 
486
        }},
 
487
        {"entity tag must be blank when starting a state server", func(cfg *cloudinit.MachineConfig) {
 
488
                info := *cfg.APIInfo
 
489
                info.Tag = "machine-0"
 
490
                cfg.APIInfo = &info
 
491
        }},
 
492
        {"missing mongo port", func(cfg *cloudinit.MachineConfig) {
 
493
                cfg.MongoPort = 0
 
494
        }},
 
495
        {"missing API port", func(cfg *cloudinit.MachineConfig) {
 
496
                cfg.APIPort = 0
 
497
        }},
 
498
        {"missing machine nonce", func(cfg *cloudinit.MachineConfig) {
 
499
                cfg.MachineNonce = ""
 
500
        }},
 
501
}
 
502
 
 
503
// TestCloudInitVerify checks that required fields are appropriately
 
504
// checked for by NewCloudInit.
 
505
func (*cloudinitSuite) TestCloudInitVerify(c *C) {
 
506
        cfg := &cloudinit.MachineConfig{
 
507
                StateServer:     true,
 
508
                StateServerCert: serverCert,
 
509
                StateServerKey:  serverKey,
 
510
                MongoPort:       1234,
 
511
                APIPort:         1235,
 
512
                MachineId:       "99",
 
513
                Tools:           newSimpleTools("9.9.9-linux-arble"),
 
514
                AuthorizedKeys:  "sshkey1",
 
515
                StateInfo: &state.Info{
 
516
                        Addrs:  []string{"host:98765"},
 
517
                        CACert: []byte(testing.CACert),
 
518
                },
 
519
                APIInfo: &api.Info{
 
520
                        Addrs:  []string{"host:9999"},
 
521
                        CACert: []byte(testing.CACert),
 
522
                },
 
523
                Config:       minimalConfig(c),
 
524
                DataDir:      "/var/lib/juju",
 
525
                MachineNonce: "FAKE_NONCE",
 
526
        }
 
527
        // check that the base configuration does not give an error
 
528
        _, err := cloudinit.New(cfg)
 
529
        c.Assert(err, IsNil)
 
530
 
 
531
        for i, test := range verifyTests {
 
532
                c.Logf("test %d. %s", i, test.err)
 
533
                cfg1 := *cfg
 
534
                test.mutate(&cfg1)
 
535
                t, err := cloudinit.New(&cfg1)
 
536
                c.Assert(err, ErrorMatches, "invalid machine configuration: "+test.err)
 
537
                c.Assert(t, IsNil)
 
538
        }
 
539
}
 
540
 
 
541
var serverCert = []byte(`
 
542
SERVER CERT
 
543
-----BEGIN CERTIFICATE-----
 
544
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN
 
545
MAsGA1UEAxMEcm9vdDAeFw0xMjExMDgxNjIyMzRaFw0xMzExMDgxNjI3MzRaMBwx
 
546
DDAKBgNVBAoTA2htbTEMMAoGA1UEAxMDYW55MFowCwYJKoZIhvcNAQEBA0sAMEgC
 
547
QQCACqz6JPwM7nbxAWub+APpnNB7myckWJ6nnsPKi9SipP1hyhfzkp8RGMJ5Uv7y
 
548
8CSTtJ8kg/ibka1VV8LvP9tnAgMBAAGjUjBQMA4GA1UdDwEB/wQEAwIAsDAdBgNV
 
549
HQ4EFgQU6G1ERaHCgfAv+yoDMFVpDbLOmIQwHwYDVR0jBBgwFoAUP/mfUdwOlHfk
 
550
fR+gLQjslxf64w0wCwYJKoZIhvcNAQEFA0EAbn0MaxWVgGYBomeLYfDdb8vCq/5/
 
551
G/2iCUQCXsVrBparMLFnor/iKOkJB5n3z3rtu70rFt+DpX6L8uBR3LB3+A==
 
552
-----END CERTIFICATE-----
 
553
`[1:])
 
554
 
 
555
var serverKey = []byte(`
 
556
SERVER KEY
 
557
-----BEGIN RSA PRIVATE KEY-----
 
558
MIIBPAIBAAJBAIAKrPok/AzudvEBa5v4A+mc0HubJyRYnqeew8qL1KKk/WHKF/OS
 
559
nxEYwnlS/vLwJJO0nySD+JuRrVVXwu8/22cCAwEAAQJBAJsk1F0wTRuaIhJ5xxqw
 
560
FIWPFep/n5jhrDOsIs6cSaRbfIBy3rAl956pf/MHKvf/IXh7KlG9p36IW49hjQHK
 
561
7HkCIQD2CqyV1ppNPFSoCI8mSwO8IZppU3i2V4MhpwnqHz3H0wIhAIU5XIlhLJW8
 
562
TNOaFMEia/TuYofdwJnYvi9t0v4UKBWdAiEA76AtvjEoTpi3in/ri0v78zp2/KXD
 
563
JzPMDvZ0fYS30ukCIA1stlJxpFiCXQuFn0nG+jH4Q52FTv8xxBhrbLOFvHRRAiEA
 
564
2Vc9NN09ty+HZgxpwqIA1fHVuYJY9GMPG1LnTnZ9INg=
 
565
-----END RSA PRIVATE KEY-----
 
566
`[1:])