~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/worker/machiner/machiner_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package machiner_test
 
5
 
 
6
import (
 
7
        "io/ioutil"
 
8
        "net"
 
9
        "path/filepath"
 
10
        stdtesting "testing"
 
11
        "time"
 
12
 
 
13
        "github.com/juju/errors"
 
14
        gitjujutesting "github.com/juju/testing"
 
15
        jc "github.com/juju/testing/checkers"
 
16
        gc "gopkg.in/check.v1"
 
17
        "gopkg.in/juju/names.v2"
 
18
 
 
19
        "github.com/juju/juju/api"
 
20
        apimachiner "github.com/juju/juju/api/machiner"
 
21
        "github.com/juju/juju/apiserver/params"
 
22
        "github.com/juju/juju/juju/testing"
 
23
        "github.com/juju/juju/network"
 
24
        "github.com/juju/juju/state"
 
25
        "github.com/juju/juju/status"
 
26
        coretesting "github.com/juju/juju/testing"
 
27
        "github.com/juju/juju/worker"
 
28
        "github.com/juju/juju/worker/machiner"
 
29
)
 
30
 
 
31
func TestPackage(t *stdtesting.T) {
 
32
        coretesting.MgoTestPackage(t)
 
33
}
 
34
 
 
35
type MachinerSuite struct {
 
36
        coretesting.BaseSuite
 
37
        accessor   *mockMachineAccessor
 
38
        machineTag names.MachineTag
 
39
        addresses  []net.Addr
 
40
}
 
41
 
 
42
var _ = gc.Suite(&MachinerSuite{})
 
43
 
 
44
func (s *MachinerSuite) SetUpTest(c *gc.C) {
 
45
        s.BaseSuite.SetUpTest(c)
 
46
        s.accessor = &mockMachineAccessor{}
 
47
        s.accessor.machine.watcher.changes = make(chan struct{})
 
48
        s.accessor.machine.life = params.Alive
 
49
        s.machineTag = names.NewMachineTag("123")
 
50
        s.addresses = []net.Addr{ // anything will do
 
51
                &net.IPAddr{IP: net.IPv4bcast},
 
52
                &net.IPAddr{IP: net.IPv4zero},
 
53
        }
 
54
        s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) {
 
55
                return s.addresses, nil
 
56
        })
 
57
        s.PatchValue(machiner.GetObservedNetworkConfig, func() ([]params.NetworkConfig, error) {
 
58
                return nil, nil
 
59
        })
 
60
}
 
61
 
 
62
func (s *MachinerSuite) TestMachinerConfigValidate(c *gc.C) {
 
63
        _, err := machiner.NewMachiner(machiner.Config{})
 
64
        c.Assert(err, gc.ErrorMatches, "validating config: unspecified MachineAccessor not valid")
 
65
        _, err = machiner.NewMachiner(machiner.Config{
 
66
                MachineAccessor: &mockMachineAccessor{},
 
67
        })
 
68
        c.Assert(err, gc.ErrorMatches, "validating config: unspecified Tag not valid")
 
69
 
 
70
        w, err := machiner.NewMachiner(machiner.Config{
 
71
                MachineAccessor: &mockMachineAccessor{},
 
72
                Tag:             names.NewMachineTag("123"),
 
73
        })
 
74
        c.Assert(err, jc.ErrorIsNil)
 
75
 
 
76
        // must stop the worker to prevent a data race when cleanup suite
 
77
        // rolls back the patches
 
78
        err = stopWorker(w)
 
79
        c.Assert(err, jc.ErrorIsNil)
 
80
}
 
81
 
 
82
func (s *MachinerSuite) TestMachinerMachineNotFound(c *gc.C) {
 
83
        // Accessing the machine initially yields "not found or unauthorized".
 
84
        // We don't know which, so we don't report that the machine is dead.
 
85
        var machineDead machineDeathTracker
 
86
        w, err := machiner.NewMachiner(machiner.Config{
 
87
                s.accessor, s.machineTag, false,
 
88
                machineDead.machineDead,
 
89
        })
 
90
        c.Assert(err, jc.ErrorIsNil)
 
91
        s.accessor.machine.SetErrors(
 
92
                nil, // SetMachineAddresses
 
93
                nil, // SetStatus
 
94
                nil, // Watch
 
95
                &params.Error{Code: params.CodeNotFound}, // Refresh
 
96
        )
 
97
        s.accessor.machine.watcher.changes <- struct{}{}
 
98
        err = stopWorker(w)
 
99
        c.Assert(errors.Cause(err), gc.Equals, worker.ErrTerminateAgent)
 
100
        c.Assert(bool(machineDead), jc.IsFalse)
 
101
}
 
102
 
 
103
func (s *MachinerSuite) TestMachinerSetStatusStopped(c *gc.C) {
 
104
        w, err := machiner.NewMachiner(machiner.Config{
 
105
                MachineAccessor: s.accessor,
 
106
                Tag:             s.machineTag,
 
107
        })
 
108
        c.Assert(err, jc.ErrorIsNil)
 
109
        s.accessor.machine.life = params.Dying
 
110
        s.accessor.machine.SetErrors(
 
111
                nil, // SetMachineAddresses
 
112
                nil, // SetStatus (started)
 
113
                nil, // Watch
 
114
                nil, // Refresh
 
115
                errors.New("cannot set status"), // SetStatus (stopped)
 
116
        )
 
117
        s.accessor.machine.watcher.changes <- struct{}{}
 
118
        err = stopWorker(w)
 
119
        c.Assert(
 
120
                err, gc.ErrorMatches,
 
121
                "machine-123 failed to set status stopped: cannot set status",
 
122
        )
 
123
        s.accessor.machine.CheckCallNames(c,
 
124
                "SetMachineAddresses",
 
125
                "SetStatus",
 
126
                "Watch",
 
127
                "Refresh",
 
128
                "Life",
 
129
                "SetStatus",
 
130
        )
 
131
        s.accessor.machine.CheckCall(
 
132
                c, 5, "SetStatus",
 
133
                status.StatusStopped,
 
134
                "",
 
135
                map[string]interface{}(nil),
 
136
        )
 
137
}
 
138
 
 
139
func (s *MachinerSuite) TestMachinerMachineEnsureDeadError(c *gc.C) {
 
140
        w, err := machiner.NewMachiner(machiner.Config{
 
141
                MachineAccessor: s.accessor,
 
142
                Tag:             s.machineTag,
 
143
        })
 
144
        c.Assert(err, jc.ErrorIsNil)
 
145
        s.accessor.machine.life = params.Dying
 
146
        s.accessor.machine.SetErrors(
 
147
                nil, // SetMachineAddresses
 
148
                nil, // SetStatus
 
149
                nil, // Watch
 
150
                nil, // Refresh
 
151
                nil, // SetStatus
 
152
                errors.New("cannot ensure machine is dead"), // EnsureDead
 
153
        )
 
154
        s.accessor.machine.watcher.changes <- struct{}{}
 
155
        err = stopWorker(w)
 
156
        c.Check(
 
157
                err, gc.ErrorMatches,
 
158
                "machine-123 failed to set machine to dead: cannot ensure machine is dead",
 
159
        )
 
160
}
 
161
 
 
162
func (s *MachinerSuite) TestMachinerMachineAssignedUnits(c *gc.C) {
 
163
        w, err := machiner.NewMachiner(machiner.Config{
 
164
                MachineAccessor: s.accessor,
 
165
                Tag:             s.machineTag,
 
166
        })
 
167
        c.Assert(err, jc.ErrorIsNil)
 
168
        s.accessor.machine.life = params.Dying
 
169
        s.accessor.machine.SetErrors(
 
170
                nil, // SetMachineAddresses
 
171
                nil, // SetStatus
 
172
                nil, // Watch
 
173
                nil, // Refresh
 
174
                nil, // SetStatus
 
175
                &params.Error{Code: params.CodeHasAssignedUnits}, // EnsureDead
 
176
        )
 
177
        s.accessor.machine.watcher.changes <- struct{}{}
 
178
        err = stopWorker(w)
 
179
 
 
180
        // If EnsureDead fails with "machine has assigned units", then
 
181
        // the worker will not fail, but will wait for more events.
 
182
        c.Check(err, jc.ErrorIsNil)
 
183
 
 
184
        s.accessor.machine.CheckCallNames(c,
 
185
                "SetMachineAddresses",
 
186
                "SetStatus",
 
187
                "Watch",
 
188
                "Refresh",
 
189
                "Life",
 
190
                "SetStatus",
 
191
                "EnsureDead",
 
192
        )
 
193
}
 
194
 
 
195
func (s *MachinerSuite) TestMachinerStorageAttached(c *gc.C) {
 
196
        // Machine is dying. We'll respond to "EnsureDead" by
 
197
        // saying that there are still storage attachments;
 
198
        // this should not cause an error.
 
199
        s.accessor.machine.life = params.Dying
 
200
        s.accessor.machine.SetErrors(
 
201
                nil, // SetMachineAddresses
 
202
                nil, // SetStatus
 
203
                nil, // Watch
 
204
                nil, // Refresh
 
205
                nil, // SetStatus
 
206
                &params.Error{Code: params.CodeMachineHasAttachedStorage},
 
207
        )
 
208
 
 
209
        worker, err := machiner.NewMachiner(machiner.Config{
 
210
                s.accessor, s.machineTag, false,
 
211
                func() error { return nil },
 
212
        })
 
213
        c.Assert(err, jc.ErrorIsNil)
 
214
        s.accessor.machine.watcher.changes <- struct{}{}
 
215
        err = stopWorker(worker)
 
216
        c.Check(err, jc.ErrorIsNil)
 
217
 
 
218
        s.accessor.CheckCalls(c, []gitjujutesting.StubCall{{
 
219
                FuncName: "Machine",
 
220
                Args:     []interface{}{s.machineTag},
 
221
        }})
 
222
 
 
223
        s.accessor.machine.CheckCalls(c, []gitjujutesting.StubCall{{
 
224
                FuncName: "SetMachineAddresses",
 
225
                Args: []interface{}{
 
226
                        network.NewAddresses(
 
227
                                "255.255.255.255",
 
228
                                "0.0.0.0",
 
229
                        ),
 
230
                },
 
231
        }, {
 
232
                FuncName: "SetStatus",
 
233
                Args: []interface{}{
 
234
                        status.StatusStarted,
 
235
                        "",
 
236
                        map[string]interface{}(nil),
 
237
                },
 
238
        }, {
 
239
                FuncName: "Watch",
 
240
        }, {
 
241
                FuncName: "Refresh",
 
242
        }, {
 
243
                FuncName: "Life",
 
244
        }, {
 
245
                FuncName: "SetStatus",
 
246
                Args: []interface{}{
 
247
                        status.StatusStopped,
 
248
                        "",
 
249
                        map[string]interface{}(nil),
 
250
                },
 
251
        }, {
 
252
                FuncName: "EnsureDead",
 
253
        }})
 
254
}
 
255
 
 
256
// worstCase is used for timeouts when timing out
 
257
// will fail the test. Raising this value should
 
258
// not affect the overall running time of the tests
 
259
// unless they fail.
 
260
const worstCase = 5 * time.Second
 
261
 
 
262
type MachinerStateSuite struct {
 
263
        testing.JujuConnSuite
 
264
 
 
265
        st            api.Connection
 
266
        machinerState *apimachiner.State
 
267
        machine       *state.Machine
 
268
        apiMachine    *apimachiner.Machine
 
269
}
 
270
 
 
271
var _ = gc.Suite(&MachinerStateSuite{})
 
272
 
 
273
func (s *MachinerStateSuite) SetUpTest(c *gc.C) {
 
274
        s.JujuConnSuite.SetUpTest(c)
 
275
        s.st, s.machine = s.OpenAPIAsNewMachine(c)
 
276
 
 
277
        // Create the machiner API facade.
 
278
        s.machinerState = apimachiner.NewState(s.st)
 
279
        c.Assert(s.machinerState, gc.NotNil)
 
280
 
 
281
        // Get the machine through the facade.
 
282
        var err error
 
283
        s.apiMachine, err = s.machinerState.Machine(s.machine.Tag().(names.MachineTag))
 
284
        c.Assert(err, jc.ErrorIsNil)
 
285
        c.Assert(s.apiMachine.Tag(), gc.Equals, s.machine.Tag())
 
286
        // Isolate tests better by not using real interface addresses.
 
287
        s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) {
 
288
                return nil, nil
 
289
        })
 
290
        s.PatchValue(&network.InterfaceByNameAddrs, func(string) ([]net.Addr, error) {
 
291
                return nil, nil
 
292
        })
 
293
        s.PatchValue(&network.LXCNetDefaultConfig, "")
 
294
        s.PatchValue(machiner.GetObservedNetworkConfig, func() ([]params.NetworkConfig, error) {
 
295
                return nil, nil
 
296
        })
 
297
}
 
298
 
 
299
func (s *MachinerStateSuite) waitMachineStatus(c *gc.C, m *state.Machine, expectStatus status.Status) {
 
300
        timeout := time.After(worstCase)
 
301
        for {
 
302
                select {
 
303
                case <-timeout:
 
304
                        c.Fatalf("timeout while waiting for machine status to change")
 
305
                case <-time.After(10 * time.Millisecond):
 
306
                        statusInfo, err := m.Status()
 
307
                        c.Assert(err, jc.ErrorIsNil)
 
308
                        if statusInfo.Status != expectStatus {
 
309
                                c.Logf("machine %q status is %s, still waiting", m, statusInfo.Status)
 
310
                                continue
 
311
                        }
 
312
                        return
 
313
                }
 
314
        }
 
315
}
 
316
 
 
317
func (s *MachinerStateSuite) TestNotFoundOrUnauthorized(c *gc.C) {
 
318
        mr, err := machiner.NewMachiner(machiner.Config{
 
319
                machiner.APIMachineAccessor{s.machinerState},
 
320
                names.NewMachineTag("99"),
 
321
                false,
 
322
                // the "machineDead" callback should not be invoked
 
323
                // because we don't know whether the agent is
 
324
                // legimitately not found or unauthorized; we err on
 
325
                // the side of caution, in case the password got mucked
 
326
                // up, or state got mucked up (e.g. during an upgrade).
 
327
                func() error { return errors.New("should not be called") },
 
328
        })
 
329
        c.Assert(err, jc.ErrorIsNil)
 
330
        c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
 
331
}
 
332
 
 
333
func (s *MachinerStateSuite) makeMachiner(
 
334
        c *gc.C,
 
335
        ignoreAddresses bool,
 
336
        machineDead func() error,
 
337
) worker.Worker {
 
338
        if machineDead == nil {
 
339
                machineDead = func() error { return nil }
 
340
        }
 
341
        w, err := machiner.NewMachiner(machiner.Config{
 
342
                machiner.APIMachineAccessor{s.machinerState},
 
343
                s.apiMachine.Tag().(names.MachineTag),
 
344
                ignoreAddresses,
 
345
                machineDead,
 
346
        })
 
347
        c.Assert(err, jc.ErrorIsNil)
 
348
        return w
 
349
}
 
350
 
 
351
type machineDeathTracker bool
 
352
 
 
353
func (t *machineDeathTracker) machineDead() error {
 
354
        *t = true
 
355
        return nil
 
356
}
 
357
 
 
358
func (s *MachinerStateSuite) TestRunStop(c *gc.C) {
 
359
        var machineDead machineDeathTracker
 
360
        mr := s.makeMachiner(c, false, machineDead.machineDead)
 
361
        c.Assert(worker.Stop(mr), jc.ErrorIsNil)
 
362
        c.Assert(s.apiMachine.Refresh(), jc.ErrorIsNil)
 
363
        c.Assert(s.apiMachine.Life(), gc.Equals, params.Alive)
 
364
        c.Assert(bool(machineDead), jc.IsFalse)
 
365
}
 
366
 
 
367
func (s *MachinerStateSuite) TestStartSetsStatus(c *gc.C) {
 
368
        statusInfo, err := s.machine.Status()
 
369
        c.Assert(err, jc.ErrorIsNil)
 
370
        c.Assert(statusInfo.Status, gc.Equals, status.StatusPending)
 
371
        c.Assert(statusInfo.Message, gc.Equals, "")
 
372
 
 
373
        mr := s.makeMachiner(c, false, nil)
 
374
        defer worker.Stop(mr)
 
375
 
 
376
        s.waitMachineStatus(c, s.machine, status.StatusStarted)
 
377
}
 
378
 
 
379
func (s *MachinerStateSuite) TestSetsStatusWhenDying(c *gc.C) {
 
380
        mr := s.makeMachiner(c, false, nil)
 
381
        defer worker.Stop(mr)
 
382
        c.Assert(s.machine.Destroy(), jc.ErrorIsNil)
 
383
        s.waitMachineStatus(c, s.machine, status.StatusStopped)
 
384
}
 
385
 
 
386
func (s *MachinerStateSuite) TestSetDead(c *gc.C) {
 
387
        var machineDead machineDeathTracker
 
388
        mr := s.makeMachiner(c, false, machineDead.machineDead)
 
389
        defer worker.Stop(mr)
 
390
        c.Assert(s.machine.Destroy(), jc.ErrorIsNil)
 
391
        s.State.StartSync()
 
392
        c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
 
393
        c.Assert(s.machine.Refresh(), jc.ErrorIsNil)
 
394
        c.Assert(s.machine.Life(), gc.Equals, state.Dead)
 
395
        c.Assert(bool(machineDead), jc.IsTrue)
 
396
}
 
397
 
 
398
func (s *MachinerStateSuite) TestSetDeadWithDyingUnit(c *gc.C) {
 
399
        var machineDead machineDeathTracker
 
400
        mr := s.makeMachiner(c, false, machineDead.machineDead)
 
401
        defer worker.Stop(mr)
 
402
 
 
403
        // Add a service, assign to machine.
 
404
        wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
 
405
        unit, err := wordpress.AddUnit()
 
406
        c.Assert(err, jc.ErrorIsNil)
 
407
        err = unit.AssignToMachine(s.machine)
 
408
        c.Assert(err, jc.ErrorIsNil)
 
409
 
 
410
        // Service alive, can't destroy machine.
 
411
        err = s.machine.Destroy()
 
412
        c.Assert(err, jc.Satisfies, state.IsHasAssignedUnitsError)
 
413
 
 
414
        err = wordpress.Destroy()
 
415
        c.Assert(err, jc.ErrorIsNil)
 
416
 
 
417
        // With dying unit, machine can now be marked as dying.
 
418
        c.Assert(s.machine.Destroy(), jc.ErrorIsNil)
 
419
        s.State.StartSync()
 
420
        c.Assert(s.machine.Refresh(), jc.ErrorIsNil)
 
421
        c.Assert(s.machine.Life(), gc.Equals, state.Dying)
 
422
        c.Assert(bool(machineDead), jc.IsFalse)
 
423
 
 
424
        // When the unit is ultimately destroyed, the machine becomes dead.
 
425
        err = unit.Destroy()
 
426
        c.Assert(err, jc.ErrorIsNil)
 
427
        s.State.StartSync()
 
428
        c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
 
429
        c.Assert(bool(machineDead), jc.IsTrue)
 
430
 
 
431
}
 
432
 
 
433
func (s *MachinerStateSuite) setupSetMachineAddresses(c *gc.C, ignore bool) {
 
434
        lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net")
 
435
        netConf := []byte(`
 
436
  # comments ignored
 
437
LXC_BR= ignored
 
438
LXC_ADDR = "fooo"
 
439
LXC_BRIDGE="foobar" # detected
 
440
anything else ignored
 
441
LXC_BRIDGE="ignored"`[1:])
 
442
        err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644)
 
443
        c.Assert(err, jc.ErrorIsNil)
 
444
        s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) {
 
445
                addrs := []net.Addr{
 
446
                        &net.IPAddr{IP: net.IPv4(10, 0, 0, 1)},
 
447
                        &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)},
 
448
                        &net.IPAddr{IP: net.IPv4(10, 0, 3, 1)}, // lxc bridge address ignored
 
449
                        &net.IPAddr{IP: net.IPv6loopback},
 
450
                        &net.UnixAddr{},                        // not IP, ignored
 
451
                        &net.IPAddr{IP: net.IPv4(10, 0, 3, 4)}, // lxc bridge address ignored
 
452
                        &net.IPNet{IP: net.ParseIP("2001:db8::1")},
 
453
                        &net.IPAddr{IP: net.IPv4(169, 254, 1, 20)}, // LinkLocal Ignored
 
454
                        &net.IPNet{IP: net.ParseIP("fe80::1")},     // LinkLocal Ignored
 
455
                }
 
456
                return addrs, nil
 
457
        })
 
458
        s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) {
 
459
                if name == "foobar" {
 
460
                        // The addresses on the LXC bridge
 
461
                        return []net.Addr{
 
462
                                &net.IPAddr{IP: net.IPv4(10, 0, 3, 1)},
 
463
                                &net.IPAddr{IP: net.IPv4(10, 0, 3, 4)},
 
464
                        }, nil
 
465
                } else if name == network.DefaultLXDBridge {
 
466
                        // The addresses on the LXD bridge
 
467
                        return []net.Addr{
 
468
                                &net.IPAddr{IP: net.IPv4(10, 0, 4, 1)},
 
469
                                &net.IPAddr{IP: net.IPv4(10, 0, 4, 4)},
 
470
                        }, nil
 
471
                }
 
472
                c.Fatalf("unknown bridge in testing: %v", name)
 
473
                return nil, nil
 
474
        })
 
475
        s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig)
 
476
 
 
477
        mr := s.makeMachiner(c, ignore, nil)
 
478
        defer worker.Stop(mr)
 
479
        c.Assert(s.machine.Destroy(), jc.ErrorIsNil)
 
480
        s.State.StartSync()
 
481
        c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
 
482
        c.Assert(s.machine.Refresh(), jc.ErrorIsNil)
 
483
}
 
484
 
 
485
func (s *MachinerStateSuite) TestMachineAddresses(c *gc.C) {
 
486
        s.setupSetMachineAddresses(c, false)
 
487
        c.Assert(s.machine.MachineAddresses(), jc.SameContents, []network.Address{
 
488
                network.NewAddress("2001:db8::1"),
 
489
                network.NewScopedAddress("10.0.0.1", network.ScopeCloudLocal),
 
490
                network.NewScopedAddress("::1", network.ScopeMachineLocal),
 
491
                network.NewScopedAddress("127.0.0.1", network.ScopeMachineLocal),
 
492
        })
 
493
}
 
494
 
 
495
func (s *MachinerStateSuite) TestMachineAddressesWithIgnoreFlag(c *gc.C) {
 
496
        s.setupSetMachineAddresses(c, true)
 
497
        c.Assert(s.machine.MachineAddresses(), gc.HasLen, 0)
 
498
}
 
499
 
 
500
func stopWorker(w worker.Worker) error {
 
501
        w.Kill()
 
502
        return w.Wait()
 
503
}