5
. "launchpad.net/gocheck"
6
"launchpad.net/juju-core/charm"
7
"launchpad.net/juju-core/cmd"
8
"launchpad.net/juju-core/environs/agent"
9
"launchpad.net/juju-core/environs/dummy"
10
envtesting "launchpad.net/juju-core/environs/testing"
11
"launchpad.net/juju-core/state"
12
"launchpad.net/juju-core/state/api"
13
"launchpad.net/juju-core/state/watcher"
14
"launchpad.net/juju-core/testing"
15
"launchpad.net/juju-core/version"
21
type MachineSuite struct {
26
var _ = Suite(&MachineSuite{})
28
func (s *MachineSuite) SetUpSuite(c *C) {
29
s.agentSuite.SetUpSuite(c)
30
s.oldCacheDir = charm.CacheDir
33
func (s *MachineSuite) TearDownSuite(c *C) {
34
charm.CacheDir = s.oldCacheDir
35
s.agentSuite.TearDownSuite(c)
38
// primeAgent adds a new Machine to run the given jobs, and sets up the
39
// machine agent's directory. It returns the new machine, the
40
// agent's configuration and the tools currently running.
41
func (s *MachineSuite) primeAgent(c *C, jobs ...state.MachineJob) (*state.Machine, *agent.Conf, *state.Tools) {
42
m, err := s.State.InjectMachine("series", "ardbeg-0", jobs...)
44
err = m.SetMongoPassword("machine-password")
46
err = m.SetPassword("machine-api-password")
48
conf, tools := s.agentSuite.primeAgent(c, state.MachineTag(m.Id()), "machine-password")
49
conf.MachineNonce = state.BootstrapNonce
55
// newAgent returns a new MachineAgent instance
56
func (s *MachineSuite) newAgent(c *C, m *state.Machine) *MachineAgent {
58
s.initAgent(c, a, "--machine-id", m.Id())
62
func (s *MachineSuite) TestParseSuccess(c *C) {
63
create := func() (cmd.Command, *AgentConf) {
67
a := CheckAgentCommand(c, create, []string{"--machine-id", "42"})
68
c.Assert(a.(*MachineAgent).MachineId, Equals, "42")
71
func (s *MachineSuite) TestParseNonsense(c *C) {
72
for _, args := range [][]string{
74
{"--machine-id", "-4004"},
76
err := ParseAgentCommand(&MachineAgent{}, args)
77
c.Assert(err, ErrorMatches, "--machine-id option must be set, and expects a non-negative integer")
81
func (s *MachineSuite) TestParseUnknown(c *C) {
83
err := ParseAgentCommand(a, []string{"--machine-id", "42", "blistering barnacles"})
84
c.Assert(err, ErrorMatches, `unrecognized args: \["blistering barnacles"\]`)
87
func (s *MachineSuite) TestRunInvalidMachineId(c *C) {
88
c.Skip("agents don't yet distinguish between temporary and permanent errors")
89
m, _, _ := s.primeAgent(c, state.JobHostUnits)
90
err := s.newAgent(c, m).Run(nil)
91
c.Assert(err, ErrorMatches, "some error")
94
func (s *MachineSuite) TestRunStop(c *C) {
95
m, ac, _ := s.primeAgent(c, state.JobHostUnits)
97
done := make(chan error)
103
c.Assert(<-done, IsNil)
104
c.Assert(charm.CacheDir, Equals, filepath.Join(ac.DataDir, "charmcache"))
107
func (s *MachineSuite) TestWithDeadMachine(c *C) {
108
m, _, _ := s.primeAgent(c, state.JobHostUnits, state.JobServeAPI)
109
err := m.EnsureDead()
111
a := s.newAgent(c, m)
112
err = runWithTimeout(a)
115
// try again with the machine removed.
119
err = runWithTimeout(a)
123
func (s *MachineSuite) TestDyingMachine(c *C) {
124
m, _, _ := s.primeAgent(c, state.JobHostUnits)
125
a := s.newAgent(c, m)
126
done := make(chan error)
131
c.Check(a.Stop(), IsNil)
133
time.Sleep(1 * time.Second)
139
case <-time.After(watcher.Period * 5 / 4):
140
// TODO(rog) Fix this so it doesn't wait for so long.
141
// https://bugs.launchpad.net/juju-core/+bug/1163983
142
c.Fatalf("timed out waiting for agent to terminate")
146
c.Assert(m.Life(), Equals, state.Dead)
149
func (s *MachineSuite) TestHostUnits(c *C) {
150
m, conf, _ := s.primeAgent(c, state.JobHostUnits)
151
a := s.newAgent(c, m)
152
ctx, reset := patchDeployContext(c, conf.StateInfo, conf.DataDir)
154
go func() { c.Check(a.Run(nil), IsNil) }()
155
defer func() { c.Check(a.Stop(), IsNil) }()
157
svc, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress"))
159
u0, err := svc.AddUnit()
161
u1, err := svc.AddUnit()
165
err = u0.AssignToMachine(m)
167
ctx.waitDeployed(c, u0.Name())
171
ctx.waitDeployed(c, u0.Name())
173
err = u1.AssignToMachine(m)
175
ctx.waitDeployed(c, u0.Name(), u1.Name())
177
err = u0.EnsureDead()
179
ctx.waitDeployed(c, u1.Name())
182
c.Assert(state.IsNotFound(err), Equals, true)
185
func (s *MachineSuite) TestManageEnviron(c *C) {
186
usefulVersion := version.Current
187
usefulVersion.Series = "series" // to match the charm created below
188
envtesting.UploadFakeToolsVersion(c, s.Conn.Environ.Storage(), usefulVersion)
189
m, _, _ := s.primeAgent(c, state.JobManageEnviron)
190
op := make(chan dummy.Operation, 200)
193
a := s.newAgent(c, m)
194
// Make sure the agent is stopped even if the test fails.
196
done := make(chan error)
201
// Check that the provisioner and firewaller are alive by doing
202
// a rudimentary check that it responds to state changes.
204
// Add one unit to a service; it should get allocated a machine
205
// and then its ports should be opened.
206
charm := s.AddTestingCharm(c, "dummy")
207
svc, err := s.State.AddService("test-service", charm)
209
err = svc.SetExposed()
211
units, err := s.Conn.AddUnits(svc, 1, "")
213
c.Check(opRecvTimeout(c, s.State, op, dummy.OpStartInstance{}), NotNil)
215
// Wait for the instance id to show up in the state.
216
id1, err := units[0].AssignedMachineId()
218
m1, err := s.State.Machine(id1)
222
for _ = range w.Changes() {
225
if _, ok := m1.InstanceId(); ok {
229
err = units[0].OpenPort("tcp", 999)
232
c.Check(opRecvTimeout(c, s.State, op, dummy.OpOpenPorts{}), NotNil)
240
case <-time.After(5 * time.Second):
241
c.Fatalf("timed out waiting for agent to terminate")
245
func (s *MachineSuite) TestUpgrade(c *C) {
246
m, conf, currentTools := s.primeAgent(c, state.JobServeAPI, state.JobManageEnviron, state.JobHostUnits)
250
a := s.newAgent(c, m)
251
s.testUpgrade(c, a, currentTools)
254
func addAPIInfo(conf *agent.Conf, m *state.Machine) {
255
port := testing.FindTCPPort()
256
conf.APIInfo = &api.Info{
257
Addrs: []string{fmt.Sprintf("localhost:%d", port)},
258
CACert: []byte(testing.CACert),
260
Password: "machine-api-password",
262
conf.StateServerCert = []byte(testing.ServerCert)
263
conf.StateServerKey = []byte(testing.ServerKey)
267
func (s *MachineSuite) TestServeAPI(c *C) {
268
stm, conf, _ := s.primeAgent(c, state.JobServeAPI)
269
addAPIInfo(conf, stm)
272
a := s.newAgent(c, stm)
273
done := make(chan error)
278
st, err := api.Open(conf.APIInfo)
282
m, err := st.Machine(stm.Id())
285
instId, ok := m.InstanceId()
286
c.Assert(ok, Equals, true)
287
c.Assert(instId, Equals, "ardbeg-0")
295
case <-time.After(5 * time.Second):
296
c.Fatalf("timed out waiting for agent to terminate")
300
var serveAPIWithBadConfTests = []struct {
301
change func(c *agent.Conf)
304
func(c *agent.Conf) {
305
c.StateServerCert = nil
307
"configuration does not have state server cert/key",
309
func(c *agent.Conf) {
310
c.StateServerKey = nil
312
"configuration does not have state server cert/key",
315
func (s *MachineSuite) TestServeAPIWithBadConf(c *C) {
316
m, conf, _ := s.primeAgent(c, state.JobServeAPI)
318
for i, t := range serveAPIWithBadConfTests {
319
c.Logf("test %d: %q", i, t.err)
324
a := s.newAgent(c, m)
325
err = runWithTimeout(a)
326
c.Assert(err, ErrorMatches, t.err)
327
err = refreshConfig(conf)
332
// opRecvTimeout waits for any of the given kinds of operation to
333
// be received from ops, and times out if not.
334
func opRecvTimeout(c *C, st *state.State, opc <-chan dummy.Operation, kinds ...dummy.Operation) dummy.Operation {
339
for _, k := range kinds {
340
if reflect.TypeOf(op) == reflect.TypeOf(k) {
344
c.Logf("discarding unknown event %#v", op)
345
case <-time.After(15 * time.Second):
346
c.Fatalf("time out wating for operation")
352
func (s *MachineSuite) TestChangePasswordChanging(c *C) {
353
m, _, _ := s.primeAgent(c, state.JobHostUnits)
354
newAgent := func() runner {
355
return s.newAgent(c, m)
357
s.testAgentPasswordChanging(c, m, newAgent)