~juju-qa/ubuntu/trusty/juju/juju-1.25.8

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/worker/uniter/runner/factory_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-12-02 18:01:10 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161202180110-dl1helep8qfebmhx
ImportĀ upstreamĀ 1.25.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2014 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package runner_test
 
5
 
 
6
import (
 
7
        "os"
 
8
        "strings"
 
9
        "time"
 
10
 
 
11
        "github.com/juju/errors"
 
12
        "github.com/juju/names"
 
13
        jc "github.com/juju/testing/checkers"
 
14
        "github.com/juju/utils"
 
15
        "github.com/juju/utils/fs"
 
16
        gc "gopkg.in/check.v1"
 
17
        "gopkg.in/juju/charm.v5/hooks"
 
18
 
 
19
        "github.com/juju/juju/apiserver/params"
 
20
        "github.com/juju/juju/state"
 
21
        "github.com/juju/juju/storage"
 
22
        "github.com/juju/juju/testcharms"
 
23
        "github.com/juju/juju/worker/uniter/hook"
 
24
        "github.com/juju/juju/worker/uniter/runner"
 
25
)
 
26
 
 
27
type FactorySuite struct {
 
28
        HookContextSuite
 
29
        paths      RealPaths
 
30
        factory    runner.Factory
 
31
        membership map[int][]string
 
32
}
 
33
 
 
34
var _ = gc.Suite(&FactorySuite{})
 
35
 
 
36
func (s *FactorySuite) SetUpTest(c *gc.C) {
 
37
        s.HookContextSuite.SetUpTest(c)
 
38
        s.paths = NewRealPaths(c)
 
39
        s.membership = map[int][]string{}
 
40
 
 
41
        contextFactory, err := runner.NewContextFactory(
 
42
                s.uniter,
 
43
                s.unit.Tag().(names.UnitTag),
 
44
                fakeTracker{},
 
45
                s.getRelationInfos,
 
46
                s.storage,
 
47
                s.paths,
 
48
        )
 
49
        c.Assert(err, jc.ErrorIsNil)
 
50
 
 
51
        factory, err := runner.NewFactory(
 
52
                s.uniter,
 
53
                s.paths,
 
54
                contextFactory,
 
55
        )
 
56
        c.Assert(err, jc.ErrorIsNil)
 
57
        s.factory = factory
 
58
}
 
59
 
 
60
func (s *FactorySuite) SetCharm(c *gc.C, name string) {
 
61
        err := os.RemoveAll(s.paths.charm)
 
62
        c.Assert(err, jc.ErrorIsNil)
 
63
        err = fs.Copy(testcharms.Repo.CharmDirPath(name), s.paths.charm)
 
64
        c.Assert(err, jc.ErrorIsNil)
 
65
}
 
66
 
 
67
func (s *FactorySuite) getRelationInfos() map[int]*runner.RelationInfo {
 
68
        info := map[int]*runner.RelationInfo{}
 
69
        for relId, relUnit := range s.apiRelunits {
 
70
                info[relId] = &runner.RelationInfo{
 
71
                        RelationUnit: relUnit,
 
72
                        MemberNames:  s.membership[relId],
 
73
                }
 
74
        }
 
75
        return info
 
76
}
 
77
 
 
78
func (s *FactorySuite) setUpCacheMethods(c *gc.C) {
 
79
        // The factory's caches are created lazily, so it doesn't have any at all to
 
80
        // begin with. Creating and discarding a context lets us call updateCache
 
81
        // without panicking. (IMO this is less invasive that making updateCache
 
82
        // responsible for creating missing caches etc.)
 
83
        _, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.Install})
 
84
        c.Assert(err, jc.ErrorIsNil)
 
85
}
 
86
 
 
87
func (s *FactorySuite) updateCache(relId int, unitName string, settings params.Settings) {
 
88
        runner.UpdateCachedSettings(s.factory, relId, unitName, settings)
 
89
}
 
90
 
 
91
func (s *FactorySuite) getCache(relId int, unitName string) (params.Settings, bool) {
 
92
        return runner.CachedSettings(s.factory, relId, unitName)
 
93
}
 
94
 
 
95
func (s *FactorySuite) AssertPaths(c *gc.C, rnr runner.Runner) {
 
96
        c.Assert(runner.RunnerPaths(rnr), gc.DeepEquals, s.paths)
 
97
}
 
98
 
 
99
func (s *FactorySuite) TestNewCommandRunnerNoRelation(c *gc.C) {
 
100
        rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: -1})
 
101
        c.Assert(err, jc.ErrorIsNil)
 
102
        s.AssertPaths(c, rnr)
 
103
        ctx := rnr.Context()
 
104
        s.AssertCoreContext(c, ctx)
 
105
        s.AssertNotActionContext(c, ctx)
 
106
        s.AssertNotRelationContext(c, ctx)
 
107
        s.AssertNotStorageContext(c, ctx)
 
108
}
 
109
 
 
110
func (s *FactorySuite) TestNewCommandRunnerRelationIdDoesNotExist(c *gc.C) {
 
111
        for _, value := range []bool{true, false} {
 
112
                _, err := s.factory.NewCommandRunner(runner.CommandInfo{
 
113
                        RelationId: 12, ForceRemoteUnit: value,
 
114
                })
 
115
                c.Check(err, gc.ErrorMatches, `unknown relation id: 12`)
 
116
        }
 
117
}
 
118
 
 
119
func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInvalid(c *gc.C) {
 
120
        for _, value := range []bool{true, false} {
 
121
                _, err := s.factory.NewCommandRunner(runner.CommandInfo{
 
122
                        RelationId: 0, RemoteUnitName: "blah", ForceRemoteUnit: value,
 
123
                })
 
124
                c.Check(err, gc.ErrorMatches, `invalid remote unit: blah`)
 
125
        }
 
126
}
 
127
 
 
128
func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInappropriate(c *gc.C) {
 
129
        for _, value := range []bool{true, false} {
 
130
                _, err := s.factory.NewCommandRunner(runner.CommandInfo{
 
131
                        RelationId: -1, RemoteUnitName: "blah/123", ForceRemoteUnit: value,
 
132
                })
 
133
                c.Check(err, gc.ErrorMatches, `remote unit provided without a relation: blah/123`)
 
134
        }
 
135
}
 
136
 
 
137
func (s *FactorySuite) TestNewCommandRunnerEmptyRelation(c *gc.C) {
 
138
        _, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: 1})
 
139
        c.Check(err, gc.ErrorMatches, `cannot infer remote unit in empty relation 1`)
 
140
}
 
141
 
 
142
func (s *FactorySuite) TestNewCommandRunnerRemoteUnitAmbiguous(c *gc.C) {
 
143
        s.membership[1] = []string{"foo/0", "foo/1"}
 
144
        _, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: 1})
 
145
        c.Check(err, gc.ErrorMatches, `ambiguous remote unit; possibilities are \[foo/0 foo/1\]`)
 
146
}
 
147
 
 
148
func (s *FactorySuite) TestNewCommandRunnerRemoteUnitMissing(c *gc.C) {
 
149
        s.membership[0] = []string{"foo/0", "foo/1"}
 
150
        _, err := s.factory.NewCommandRunner(runner.CommandInfo{
 
151
                RelationId: 0, RemoteUnitName: "blah/123",
 
152
        })
 
153
        c.Check(err, gc.ErrorMatches, `unknown remote unit blah/123; possibilities are \[foo/0 foo/1\]`)
 
154
}
 
155
 
 
156
func (s *FactorySuite) TestNewCommandRunnerForceNoRemoteUnit(c *gc.C) {
 
157
        rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{
 
158
                RelationId: 0, ForceRemoteUnit: true,
 
159
        })
 
160
        c.Assert(err, jc.ErrorIsNil)
 
161
        s.AssertPaths(c, rnr)
 
162
        ctx := rnr.Context()
 
163
        s.AssertCoreContext(c, ctx)
 
164
        s.AssertNotActionContext(c, ctx)
 
165
        s.AssertRelationContext(c, ctx, 0, "")
 
166
        s.AssertNotStorageContext(c, ctx)
 
167
}
 
168
 
 
169
func (s *FactorySuite) TestNewCommandRunnerForceRemoteUnitMissing(c *gc.C) {
 
170
        rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{
 
171
                RelationId: 0, RemoteUnitName: "blah/123", ForceRemoteUnit: true,
 
172
        })
 
173
        c.Assert(err, gc.IsNil)
 
174
        ctx := rnr.Context()
 
175
        s.AssertCoreContext(c, ctx)
 
176
        s.AssertNotActionContext(c, ctx)
 
177
        s.AssertRelationContext(c, ctx, 0, "blah/123")
 
178
        s.AssertNotStorageContext(c, ctx)
 
179
}
 
180
 
 
181
func (s *FactorySuite) TestNewCommandRunnerInferRemoteUnit(c *gc.C) {
 
182
        s.membership[0] = []string{"foo/2"}
 
183
        rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: 0})
 
184
        c.Assert(err, jc.ErrorIsNil)
 
185
        s.AssertPaths(c, rnr)
 
186
        ctx := rnr.Context()
 
187
        s.AssertCoreContext(c, ctx)
 
188
        s.AssertNotActionContext(c, ctx)
 
189
        s.AssertRelationContext(c, ctx, 0, "foo/2")
 
190
        s.AssertNotStorageContext(c, ctx)
 
191
}
 
192
 
 
193
func (s *FactorySuite) TestNewHookRunner(c *gc.C) {
 
194
        rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.ConfigChanged})
 
195
        c.Assert(err, jc.ErrorIsNil)
 
196
        s.AssertPaths(c, rnr)
 
197
        ctx := rnr.Context()
 
198
        s.AssertCoreContext(c, ctx)
 
199
        s.AssertNotActionContext(c, ctx)
 
200
        s.AssertNotRelationContext(c, ctx)
 
201
        s.AssertNotStorageContext(c, ctx)
 
202
}
 
203
 
 
204
func (s *FactorySuite) TestNewHookRunnerWithBadHook(c *gc.C) {
 
205
        rnr, err := s.factory.NewHookRunner(hook.Info{})
 
206
        c.Assert(rnr, gc.IsNil)
 
207
        c.Assert(err, gc.ErrorMatches, `unknown hook kind ""`)
 
208
}
 
209
 
 
210
func (s *FactorySuite) TestNewHookRunnerWithStorage(c *gc.C) {
 
211
        // We need to set up a unit that has storage metadata defined.
 
212
        ch := s.AddTestingCharm(c, "storage-block")
 
213
        sCons := map[string]state.StorageConstraints{
 
214
                "data": {Pool: "", Size: 1024, Count: 1},
 
215
        }
 
216
        service := s.AddTestingServiceWithStorage(c, "storage-block", ch, sCons)
 
217
        s.machine = nil // allocate a new machine
 
218
        unit := s.AddUnit(c, service)
 
219
 
 
220
        storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag())
 
221
        c.Assert(err, jc.ErrorIsNil)
 
222
        c.Assert(storageAttachments, gc.HasLen, 1)
 
223
        storageTag := storageAttachments[0].StorageInstance()
 
224
 
 
225
        volume, err := s.State.StorageInstanceVolume(storageTag)
 
226
        c.Assert(err, jc.ErrorIsNil)
 
227
        volumeTag := volume.VolumeTag()
 
228
        machineTag := s.machine.MachineTag()
 
229
 
 
230
        err = s.State.SetVolumeInfo(
 
231
                volumeTag, state.VolumeInfo{
 
232
                        VolumeId: "vol-123",
 
233
                        Size:     456,
 
234
                },
 
235
        )
 
236
        c.Assert(err, jc.ErrorIsNil)
 
237
        err = s.State.SetVolumeAttachmentInfo(
 
238
                machineTag, volumeTag, state.VolumeAttachmentInfo{
 
239
                        DeviceName: "sdb",
 
240
                },
 
241
        )
 
242
        c.Assert(err, jc.ErrorIsNil)
 
243
 
 
244
        password, err := utils.RandomPassword()
 
245
        err = unit.SetPassword(password)
 
246
        c.Assert(err, jc.ErrorIsNil)
 
247
        st := s.OpenAPIAs(c, unit.Tag(), password)
 
248
        uniter, err := st.Uniter()
 
249
        c.Assert(err, jc.ErrorIsNil)
 
250
 
 
251
        contextFactory, err := runner.NewContextFactory(
 
252
                uniter,
 
253
                unit.Tag().(names.UnitTag),
 
254
                fakeTracker{},
 
255
                s.getRelationInfos,
 
256
                s.storage,
 
257
                s.paths,
 
258
        )
 
259
        c.Assert(err, jc.ErrorIsNil)
 
260
        factory, err := runner.NewFactory(
 
261
                uniter,
 
262
                s.paths,
 
263
                contextFactory,
 
264
        )
 
265
        c.Assert(err, jc.ErrorIsNil)
 
266
 
 
267
        rnr, err := factory.NewHookRunner(hook.Info{
 
268
                Kind:      hooks.StorageAttached,
 
269
                StorageId: "data/0",
 
270
        })
 
271
        c.Assert(err, jc.ErrorIsNil)
 
272
        s.AssertPaths(c, rnr)
 
273
        ctx := rnr.Context()
 
274
        c.Assert(ctx.UnitName(), gc.Equals, "storage-block/0")
 
275
        s.AssertStorageContext(c, ctx, "data/0", storage.StorageAttachmentInfo{
 
276
                Kind:     storage.StorageKindBlock,
 
277
                Location: "/dev/sdb",
 
278
        })
 
279
        s.AssertNotActionContext(c, ctx)
 
280
        s.AssertNotRelationContext(c, ctx)
 
281
}
 
282
 
 
283
func (s *FactorySuite) TestNewHookRunnerWithRelation(c *gc.C) {
 
284
        rnr, err := s.factory.NewHookRunner(hook.Info{
 
285
                Kind:       hooks.RelationBroken,
 
286
                RelationId: 1,
 
287
        })
 
288
        c.Assert(err, jc.ErrorIsNil)
 
289
        s.AssertPaths(c, rnr)
 
290
        ctx := rnr.Context()
 
291
        s.AssertCoreContext(c, ctx)
 
292
        s.AssertNotActionContext(c, ctx)
 
293
        s.AssertRelationContext(c, ctx, 1, "")
 
294
        s.AssertNotStorageContext(c, ctx)
 
295
}
 
296
 
 
297
func (s *FactorySuite) TestNewHookRunnerPrunesNonMemberCaches(c *gc.C) {
 
298
 
 
299
        // Write cached member settings for a member and a non-member.
 
300
        s.setUpCacheMethods(c)
 
301
        s.membership[0] = []string{"rel0/0"}
 
302
        s.updateCache(0, "rel0/0", params.Settings{"keep": "me"})
 
303
        s.updateCache(0, "rel0/1", params.Settings{"drop": "me"})
 
304
 
 
305
        rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.Install})
 
306
        c.Assert(err, jc.ErrorIsNil)
 
307
        s.AssertPaths(c, rnr)
 
308
        ctx := rnr.Context()
 
309
 
 
310
        settings0, found := s.getCache(0, "rel0/0")
 
311
        c.Assert(found, jc.IsTrue)
 
312
        c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"})
 
313
 
 
314
        settings1, found := s.getCache(0, "rel0/1")
 
315
        c.Assert(found, jc.IsFalse)
 
316
        c.Assert(settings1, gc.IsNil)
 
317
 
 
318
        // Check the caches are being used by the context relations.
 
319
        relCtx, found := ctx.Relation(0)
 
320
        c.Assert(found, jc.IsTrue)
 
321
 
 
322
        // Verify that the settings really were cached by trying to look them up.
 
323
        // Nothing's really in scope, so the call would fail if they weren't.
 
324
        settings0, err = relCtx.ReadSettings("rel0/0")
 
325
        c.Assert(err, jc.ErrorIsNil)
 
326
        c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"})
 
327
 
 
328
        // Verify that the non-member settings were purged by looking them up and
 
329
        // checking for the expected error.
 
330
        settings1, err = relCtx.ReadSettings("rel0/1")
 
331
        c.Assert(settings1, gc.IsNil)
 
332
        c.Assert(err, gc.ErrorMatches, "permission denied")
 
333
}
 
334
 
 
335
func (s *FactorySuite) TestNewHookRunnerRelationJoinedUpdatesRelationContextAndCaches(c *gc.C) {
 
336
        // Write some cached settings for r/0, so we can verify the cache gets cleared.
 
337
        s.setUpCacheMethods(c)
 
338
        s.membership[1] = []string{"r/0"}
 
339
        s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
 
340
 
 
341
        rnr, err := s.factory.NewHookRunner(hook.Info{
 
342
                Kind:       hooks.RelationJoined,
 
343
                RelationId: 1,
 
344
                RemoteUnit: "r/0",
 
345
        })
 
346
        c.Assert(err, jc.ErrorIsNil)
 
347
        s.AssertPaths(c, rnr)
 
348
        ctx := rnr.Context()
 
349
        s.AssertCoreContext(c, ctx)
 
350
        s.AssertNotActionContext(c, ctx)
 
351
        s.AssertNotStorageContext(c, ctx)
 
352
        rel := s.AssertRelationContext(c, ctx, 1, "r/0")
 
353
        c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0"})
 
354
        cached0, member := s.getCache(1, "r/0")
 
355
        c.Assert(cached0, gc.IsNil)
 
356
        c.Assert(member, jc.IsTrue)
 
357
}
 
358
 
 
359
func (s *FactorySuite) TestNewHookRunnerRelationChangedUpdatesRelationContextAndCaches(c *gc.C) {
 
360
        // Update member settings to have actual values, so we can check that
 
361
        // the change for r/4 clears its cache but leaves r/0's alone.
 
362
        s.setUpCacheMethods(c)
 
363
        s.membership[1] = []string{"r/0", "r/4"}
 
364
        s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
 
365
        s.updateCache(1, "r/4", params.Settings{"baz": "qux"})
 
366
 
 
367
        rnr, err := s.factory.NewHookRunner(hook.Info{
 
368
                Kind:       hooks.RelationChanged,
 
369
                RelationId: 1,
 
370
                RemoteUnit: "r/4",
 
371
        })
 
372
        c.Assert(err, jc.ErrorIsNil)
 
373
        s.AssertPaths(c, rnr)
 
374
        ctx := rnr.Context()
 
375
        s.AssertCoreContext(c, ctx)
 
376
        s.AssertNotActionContext(c, ctx)
 
377
        s.AssertNotStorageContext(c, ctx)
 
378
        rel := s.AssertRelationContext(c, ctx, 1, "r/4")
 
379
        c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"})
 
380
        cached0, member := s.getCache(1, "r/0")
 
381
        c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"})
 
382
        c.Assert(member, jc.IsTrue)
 
383
        cached4, member := s.getCache(1, "r/4")
 
384
        c.Assert(cached4, gc.IsNil)
 
385
        c.Assert(member, jc.IsTrue)
 
386
}
 
387
 
 
388
func (s *FactorySuite) TestNewHookRunnerRelationDepartedUpdatesRelationContextAndCaches(c *gc.C) {
 
389
        // Update member settings to have actual values, so we can check that
 
390
        // the depart for r/0 leaves r/4's cache alone (while discarding r/0's).
 
391
        s.setUpCacheMethods(c)
 
392
        s.membership[1] = []string{"r/0", "r/4"}
 
393
        s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
 
394
        s.updateCache(1, "r/4", params.Settings{"baz": "qux"})
 
395
 
 
396
        rnr, err := s.factory.NewHookRunner(hook.Info{
 
397
                Kind:       hooks.RelationDeparted,
 
398
                RelationId: 1,
 
399
                RemoteUnit: "r/0",
 
400
        })
 
401
        c.Assert(err, jc.ErrorIsNil)
 
402
        s.AssertPaths(c, rnr)
 
403
        ctx := rnr.Context()
 
404
        s.AssertCoreContext(c, ctx)
 
405
        s.AssertNotActionContext(c, ctx)
 
406
        s.AssertNotStorageContext(c, ctx)
 
407
        rel := s.AssertRelationContext(c, ctx, 1, "r/0")
 
408
        c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/4"})
 
409
        cached0, member := s.getCache(1, "r/0")
 
410
        c.Assert(cached0, gc.IsNil)
 
411
        c.Assert(member, jc.IsFalse)
 
412
        cached4, member := s.getCache(1, "r/4")
 
413
        c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"})
 
414
        c.Assert(member, jc.IsTrue)
 
415
}
 
416
 
 
417
func (s *FactorySuite) TestNewHookRunnerRelationBrokenRetainsCaches(c *gc.C) {
 
418
        // Note that this is bizarre and unrealistic, because we would never usually
 
419
        // run relation-broken on a non-empty relation. But verfying that the settings
 
420
        // stick around allows us to verify that there's no special handling for that
 
421
        // hook -- as there should not be, because the relation caches will be discarded
 
422
        // for the *next* hook, which will be constructed with the current set of known
 
423
        // relations and ignore everything else.
 
424
        s.setUpCacheMethods(c)
 
425
        s.membership[1] = []string{"r/0", "r/4"}
 
426
        s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
 
427
        s.updateCache(1, "r/4", params.Settings{"baz": "qux"})
 
428
 
 
429
        rnr, err := s.factory.NewHookRunner(hook.Info{
 
430
                Kind:       hooks.RelationBroken,
 
431
                RelationId: 1,
 
432
        })
 
433
        c.Assert(err, jc.ErrorIsNil)
 
434
        s.AssertPaths(c, rnr)
 
435
        ctx := rnr.Context()
 
436
        rel := s.AssertRelationContext(c, ctx, 1, "")
 
437
        c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"})
 
438
        cached0, member := s.getCache(1, "r/0")
 
439
        c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"})
 
440
        c.Assert(member, jc.IsTrue)
 
441
        cached4, member := s.getCache(1, "r/4")
 
442
        c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"})
 
443
        c.Assert(member, jc.IsTrue)
 
444
}
 
445
 
 
446
func (s *FactorySuite) TestNewHookRunnerWithBadRelation(c *gc.C) {
 
447
        rnr, err := s.factory.NewHookRunner(hook.Info{
 
448
                Kind:       hooks.RelationBroken,
 
449
                RelationId: 12345,
 
450
        })
 
451
        c.Assert(rnr, gc.IsNil)
 
452
        c.Assert(err, gc.ErrorMatches, `unknown relation id: 12345`)
 
453
}
 
454
 
 
455
func (s *FactorySuite) TestNewHookRunnerMetricsDisabledHook(c *gc.C) {
 
456
        s.SetCharm(c, "metered")
 
457
        rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.Install})
 
458
        c.Assert(err, jc.ErrorIsNil)
 
459
        s.AssertPaths(c, rnr)
 
460
        ctx := rnr.Context()
 
461
        err = ctx.AddMetric("key", "value", time.Now())
 
462
        c.Assert(err, gc.ErrorMatches, "metrics disabled")
 
463
}
 
464
 
 
465
func (s *FactorySuite) TestNewHookRunnerMetricsDisabledUndeclared(c *gc.C) {
 
466
        s.SetCharm(c, "mysql")
 
467
        rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.CollectMetrics})
 
468
        c.Assert(err, jc.ErrorIsNil)
 
469
        s.AssertPaths(c, rnr)
 
470
        ctx := rnr.Context()
 
471
        err = ctx.AddMetric("key", "value", time.Now())
 
472
        c.Assert(err, gc.ErrorMatches, "metrics disabled")
 
473
}
 
474
 
 
475
func (s *FactorySuite) TestNewHookRunnerMetricsDeclarationError(c *gc.C) {
 
476
        rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.CollectMetrics})
 
477
        c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist)
 
478
        c.Assert(rnr, gc.IsNil)
 
479
}
 
480
 
 
481
func (s *FactorySuite) TestNewHookRunnerMetricsEnabled(c *gc.C) {
 
482
        s.SetCharm(c, "metered")
 
483
 
 
484
        rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.CollectMetrics})
 
485
        c.Assert(err, jc.ErrorIsNil)
 
486
        s.AssertPaths(c, rnr)
 
487
        ctx := rnr.Context()
 
488
        err = ctx.AddMetric("pings", "0.5", time.Now())
 
489
        c.Assert(err, jc.ErrorIsNil)
 
490
}
 
491
 
 
492
func (s *FactorySuite) TestNewActionRunnerGood(c *gc.C) {
 
493
        s.SetCharm(c, "dummy")
 
494
        action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", map[string]interface{}{
 
495
                "outfile": "/some/file.bz2",
 
496
        })
 
497
        c.Assert(err, jc.ErrorIsNil)
 
498
        rnr, err := s.factory.NewActionRunner(action.Id())
 
499
        c.Assert(err, jc.ErrorIsNil)
 
500
        s.AssertPaths(c, rnr)
 
501
        ctx := rnr.Context()
 
502
        data, err := ctx.ActionData()
 
503
        c.Assert(err, jc.ErrorIsNil)
 
504
        c.Assert(data, jc.DeepEquals, &runner.ActionData{
 
505
                Name: "snapshot",
 
506
                Tag:  action.ActionTag(),
 
507
                Params: map[string]interface{}{
 
508
                        "outfile": "/some/file.bz2",
 
509
                },
 
510
                ResultsMap: map[string]interface{}{},
 
511
        })
 
512
        vars := ctx.HookVars(s.paths)
 
513
        c.Assert(len(vars) > 0, jc.IsTrue, gc.Commentf("expected HookVars but found none"))
 
514
        combined := strings.Join(vars, "|")
 
515
        c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_NAME=snapshot(\|.*|$)`)
 
516
        c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_UUID=`+action.Id()+`(\|.*|$)`)
 
517
        c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_TAG=`+action.Tag().String()+`(\|.*|$)`)
 
518
}
 
519
 
 
520
func (s *FactorySuite) TestNewActionRunnerBadCharm(c *gc.C) {
 
521
        rnr, err := s.factory.NewActionRunner("irrelevant")
 
522
        c.Assert(rnr, gc.IsNil)
 
523
        c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist)
 
524
        c.Assert(err, gc.Not(jc.Satisfies), runner.IsBadActionError)
 
525
}
 
526
 
 
527
func (s *FactorySuite) TestNewActionRunnerBadName(c *gc.C) {
 
528
        s.SetCharm(c, "dummy")
 
529
        action, err := s.State.EnqueueAction(s.unit.Tag(), "no-such-action", nil)
 
530
        c.Assert(err, jc.ErrorIsNil) // this will fail when using AddAction on unit
 
531
        rnr, err := s.factory.NewActionRunner(action.Id())
 
532
        c.Check(rnr, gc.IsNil)
 
533
        c.Check(err, gc.ErrorMatches, "cannot run \"no-such-action\" action: not defined")
 
534
        c.Check(err, jc.Satisfies, runner.IsBadActionError)
 
535
}
 
536
 
 
537
func (s *FactorySuite) TestNewActionRunnerBadParams(c *gc.C) {
 
538
        s.SetCharm(c, "dummy")
 
539
        action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", map[string]interface{}{
 
540
                "outfile": 123,
 
541
        })
 
542
        c.Assert(err, jc.ErrorIsNil) // this will fail when state is done right
 
543
        rnr, err := s.factory.NewActionRunner(action.Id())
 
544
        c.Check(rnr, gc.IsNil)
 
545
        c.Check(err, gc.ErrorMatches, "cannot run \"snapshot\" action: .*")
 
546
        c.Check(err, jc.Satisfies, runner.IsBadActionError)
 
547
}
 
548
 
 
549
func (s *FactorySuite) TestNewActionRunnerMissingAction(c *gc.C) {
 
550
        s.SetCharm(c, "dummy")
 
551
        action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil)
 
552
        c.Assert(err, jc.ErrorIsNil)
 
553
        _, err = s.unit.CancelAction(action)
 
554
        c.Assert(err, jc.ErrorIsNil)
 
555
        rnr, err := s.factory.NewActionRunner(action.Id())
 
556
        c.Check(rnr, gc.IsNil)
 
557
        c.Check(err, gc.ErrorMatches, "action no longer available")
 
558
        c.Check(err, gc.Equals, runner.ErrActionNotAvailable)
 
559
}
 
560
 
 
561
func (s *FactorySuite) TestNewActionRunnerUnauthAction(c *gc.C) {
 
562
        s.SetCharm(c, "dummy")
 
563
        otherUnit, err := s.service.AddUnit()
 
564
        c.Assert(err, jc.ErrorIsNil)
 
565
        action, err := s.State.EnqueueAction(otherUnit.Tag(), "snapshot", nil)
 
566
        c.Assert(err, jc.ErrorIsNil)
 
567
        rnr, err := s.factory.NewActionRunner(action.Id())
 
568
        c.Check(rnr, gc.IsNil)
 
569
        c.Check(err, gc.ErrorMatches, "action no longer available")
 
570
        c.Check(err, gc.Equals, runner.ErrActionNotAvailable)
 
571
}