~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/worker/uniter/relation/relationer_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 2012-2015 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package relation_test
 
5
 
 
6
import (
 
7
        "strconv"
 
8
        "strings"
 
9
        "time"
 
10
 
 
11
        "github.com/juju/errors"
 
12
        jc "github.com/juju/testing/checkers"
 
13
        ft "github.com/juju/testing/filetesting"
 
14
        "github.com/juju/utils"
 
15
        gc "gopkg.in/check.v1"
 
16
        "gopkg.in/juju/charm.v6-unstable/hooks"
 
17
        "gopkg.in/juju/names.v2"
 
18
 
 
19
        "github.com/juju/juju/api"
 
20
        apiuniter "github.com/juju/juju/api/uniter"
 
21
        jujutesting "github.com/juju/juju/juju/testing"
 
22
        "github.com/juju/juju/network"
 
23
        "github.com/juju/juju/state"
 
24
        coretesting "github.com/juju/juju/testing"
 
25
        "github.com/juju/juju/worker/uniter/hook"
 
26
        "github.com/juju/juju/worker/uniter/relation"
 
27
)
 
28
 
 
29
type RelationerSuite struct {
 
30
        jujutesting.JujuConnSuite
 
31
        hooks   chan hook.Info
 
32
        app     *state.Application
 
33
        rel     *state.Relation
 
34
        dir     *relation.StateDir
 
35
        dirPath string
 
36
 
 
37
        st         api.Connection
 
38
        uniter     *apiuniter.State
 
39
        apiRelUnit *apiuniter.RelationUnit
 
40
}
 
41
 
 
42
var _ = gc.Suite(&RelationerSuite{})
 
43
 
 
44
func (s *RelationerSuite) SetUpTest(c *gc.C) {
 
45
        s.JujuConnSuite.SetUpTest(c)
 
46
        var err error
 
47
        s.app = s.AddTestingService(c, "u", s.AddTestingCharm(c, "riak"))
 
48
        c.Assert(err, jc.ErrorIsNil)
 
49
        rels, err := s.app.Relations()
 
50
        c.Assert(err, jc.ErrorIsNil)
 
51
        c.Assert(rels, gc.HasLen, 1)
 
52
        s.rel = rels[0]
 
53
        _, unit := s.AddRelationUnit(c, "u/0")
 
54
        s.dirPath = c.MkDir()
 
55
        s.dir, err = relation.ReadStateDir(s.dirPath, s.rel.Id())
 
56
        c.Assert(err, jc.ErrorIsNil)
 
57
        s.hooks = make(chan hook.Info)
 
58
 
 
59
        password, err := utils.RandomPassword()
 
60
        c.Assert(err, jc.ErrorIsNil)
 
61
        err = unit.SetPassword(password)
 
62
        c.Assert(err, jc.ErrorIsNil)
 
63
        s.st = s.OpenAPIAs(c, unit.Tag(), password)
 
64
        s.uniter, err = s.st.Uniter()
 
65
        c.Assert(err, jc.ErrorIsNil)
 
66
        c.Assert(s.uniter, gc.NotNil)
 
67
 
 
68
        apiUnit, err := s.uniter.Unit(unit.Tag().(names.UnitTag))
 
69
        c.Assert(err, jc.ErrorIsNil)
 
70
        apiRel, err := s.uniter.Relation(s.rel.Tag().(names.RelationTag))
 
71
        c.Assert(err, jc.ErrorIsNil)
 
72
        s.apiRelUnit, err = apiRel.Unit(apiUnit)
 
73
        c.Assert(err, jc.ErrorIsNil)
 
74
}
 
75
 
 
76
func (s *RelationerSuite) AddRelationUnit(c *gc.C, name string) (*state.RelationUnit, *state.Unit) {
 
77
        u, err := s.app.AddUnit()
 
78
        c.Assert(err, jc.ErrorIsNil)
 
79
        c.Assert(u.Name(), gc.Equals, name)
 
80
        machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
 
81
        c.Assert(err, jc.ErrorIsNil)
 
82
        err = u.AssignToMachine(machine)
 
83
        c.Assert(err, jc.ErrorIsNil)
 
84
        privateAddr := network.NewScopedAddress(
 
85
                strings.Replace(name, "/", "-", 1)+".testing.invalid", network.ScopeCloudLocal,
 
86
        )
 
87
        err = machine.SetProviderAddresses(privateAddr)
 
88
        c.Assert(err, jc.ErrorIsNil)
 
89
        ru, err := s.rel.Unit(u)
 
90
        c.Assert(err, jc.ErrorIsNil)
 
91
        return ru, u
 
92
}
 
93
 
 
94
func (s *RelationerSuite) TestStateDir(c *gc.C) {
 
95
        // Create the relationer; check its state dir is not created.
 
96
        r := relation.NewRelationer(s.apiRelUnit, s.dir)
 
97
        path := strconv.Itoa(s.rel.Id())
 
98
        ft.Removed{path}.Check(c, s.dirPath)
 
99
 
 
100
        // Join the relation; check the dir was created.
 
101
        err := r.Join()
 
102
        c.Assert(err, jc.ErrorIsNil)
 
103
        ft.Dir{path, 0755}.Check(c, s.dirPath)
 
104
 
 
105
        // Prepare to depart the relation; check the dir is still there.
 
106
        hi := hook.Info{Kind: hooks.RelationBroken}
 
107
        _, err = r.PrepareHook(hi)
 
108
        c.Assert(err, jc.ErrorIsNil)
 
109
        ft.Dir{path, 0755}.Check(c, s.dirPath)
 
110
 
 
111
        // Actually depart it; check the dir is removed.
 
112
        err = r.CommitHook(hi)
 
113
        c.Assert(err, jc.ErrorIsNil)
 
114
        ft.Removed{path}.Check(c, s.dirPath)
 
115
}
 
116
 
 
117
func (s *RelationerSuite) TestEnterLeaveScope(c *gc.C) {
 
118
        ru1, _ := s.AddRelationUnit(c, "u/1")
 
119
        r := relation.NewRelationer(s.apiRelUnit, s.dir)
 
120
 
 
121
        // u/1 does not consider u/0 to be alive.
 
122
        w := ru1.Watch()
 
123
        defer stop(c, w)
 
124
        s.State.StartSync()
 
125
        ch, ok := <-w.Changes()
 
126
        c.Assert(ok, jc.IsTrue)
 
127
        c.Assert(ch.Changed, gc.HasLen, 0)
 
128
        c.Assert(ch.Departed, gc.HasLen, 0)
 
129
 
 
130
        // u/0 enters scope; u/1 observes it.
 
131
        err := r.Join()
 
132
        c.Assert(err, jc.ErrorIsNil)
 
133
        s.State.StartSync()
 
134
        select {
 
135
        case ch, ok := <-w.Changes():
 
136
                c.Assert(ok, jc.IsTrue)
 
137
                c.Assert(ch.Changed, gc.HasLen, 1)
 
138
                _, found := ch.Changed["u/0"]
 
139
                c.Assert(found, jc.IsTrue)
 
140
                c.Assert(ch.Departed, gc.HasLen, 0)
 
141
        case <-time.After(coretesting.LongWait):
 
142
                c.Fatalf("timed out waiting for presence detection")
 
143
        }
 
144
 
 
145
        // re-Join is no-op.
 
146
        err = r.Join()
 
147
        c.Assert(err, jc.ErrorIsNil)
 
148
        // TODO(jam): This would be a great to replace with statetesting.NotifyWatcherC
 
149
        s.State.StartSync()
 
150
        select {
 
151
        case ch, ok := <-w.Changes():
 
152
                c.Fatalf("got unexpected change: %#v, %#v", ch, ok)
 
153
        case <-time.After(coretesting.ShortWait):
 
154
        }
 
155
 
 
156
        // u/0 leaves scope; u/1 observes it.
 
157
        hi := hook.Info{Kind: hooks.RelationBroken}
 
158
        _, err = r.PrepareHook(hi)
 
159
        c.Assert(err, jc.ErrorIsNil)
 
160
 
 
161
        err = r.CommitHook(hi)
 
162
        c.Assert(err, jc.ErrorIsNil)
 
163
        s.State.StartSync()
 
164
        select {
 
165
        case ch, ok := <-w.Changes():
 
166
                c.Assert(ok, jc.IsTrue)
 
167
                c.Assert(ch.Changed, gc.HasLen, 0)
 
168
                c.Assert(ch.Departed, gc.DeepEquals, []string{"u/0"})
 
169
        case <-time.After(coretesting.LongWait):
 
170
                c.Fatalf("timed out waiting for absence detection")
 
171
        }
 
172
}
 
173
 
 
174
func (s *RelationerSuite) TestPrepareCommitHooks(c *gc.C) {
 
175
        r := relation.NewRelationer(s.apiRelUnit, s.dir)
 
176
        err := r.Join()
 
177
        c.Assert(err, jc.ErrorIsNil)
 
178
 
 
179
        assertMembers := func(expect map[string]int64) {
 
180
                c.Assert(s.dir.State().Members, jc.DeepEquals, expect)
 
181
                expectNames := make([]string, 0, len(expect))
 
182
                for name := range expect {
 
183
                        expectNames = append(expectNames, name)
 
184
                }
 
185
                c.Assert(r.ContextInfo().MemberNames, jc.SameContents, expectNames)
 
186
        }
 
187
        assertMembers(map[string]int64{})
 
188
 
 
189
        // Check preparing an invalid hook changes nothing.
 
190
        changed := hook.Info{
 
191
                Kind:          hooks.RelationChanged,
 
192
                RemoteUnit:    "u/1",
 
193
                ChangeVersion: 7,
 
194
        }
 
195
        _, err = r.PrepareHook(changed)
 
196
        c.Assert(err, gc.ErrorMatches, `inappropriate "relation-changed" for "u/1": unit has not joined`)
 
197
        assertMembers(map[string]int64{})
 
198
 
 
199
        // Check preparing a valid hook updates neither the context nor persistent
 
200
        // relation state.
 
201
        joined := hook.Info{
 
202
                Kind:       hooks.RelationJoined,
 
203
                RemoteUnit: "u/1",
 
204
        }
 
205
        name, err := r.PrepareHook(joined)
 
206
        c.Assert(err, jc.ErrorIsNil)
 
207
        c.Assert(name, gc.Equals, "ring-relation-joined")
 
208
        assertMembers(map[string]int64{})
 
209
 
 
210
        // Check that preparing the following hook fails as before...
 
211
        _, err = r.PrepareHook(changed)
 
212
        c.Assert(err, gc.ErrorMatches, `inappropriate "relation-changed" for "u/1": unit has not joined`)
 
213
        assertMembers(map[string]int64{})
 
214
 
 
215
        // ...but that committing the previous hook updates the persistent
 
216
        // relation state...
 
217
        err = r.CommitHook(joined)
 
218
        c.Assert(err, jc.ErrorIsNil)
 
219
        assertMembers(map[string]int64{"u/1": 0})
 
220
 
 
221
        // ...and allows us to prepare the next hook...
 
222
        name, err = r.PrepareHook(changed)
 
223
        c.Assert(err, jc.ErrorIsNil)
 
224
        c.Assert(name, gc.Equals, "ring-relation-changed")
 
225
        assertMembers(map[string]int64{"u/1": 0})
 
226
 
 
227
        // ...and commit it.
 
228
        err = r.CommitHook(changed)
 
229
        c.Assert(err, jc.ErrorIsNil)
 
230
        assertMembers(map[string]int64{"u/1": 7})
 
231
 
 
232
        // To verify implied behaviour above, prepare a new joined hook with
 
233
        // missing membership information, and check relation context
 
234
        // membership is stil not updated...
 
235
        joined.RemoteUnit = "u/2"
 
236
        joined.ChangeVersion = 3
 
237
        name, err = r.PrepareHook(joined)
 
238
        c.Assert(err, jc.ErrorIsNil)
 
239
        c.Assert(name, gc.Equals, "ring-relation-joined")
 
240
        assertMembers(map[string]int64{"u/1": 7})
 
241
 
 
242
        // ...until commit, at which point so is relation state.
 
243
        err = r.CommitHook(joined)
 
244
        c.Assert(err, jc.ErrorIsNil)
 
245
        assertMembers(map[string]int64{"u/1": 7, "u/2": 3})
 
246
}
 
247
 
 
248
func (s *RelationerSuite) TestSetDying(c *gc.C) {
 
249
        ru1, u := s.AddRelationUnit(c, "u/1")
 
250
        settings := map[string]interface{}{"unit": "settings"}
 
251
        err := ru1.EnterScope(settings)
 
252
        c.Assert(err, jc.ErrorIsNil)
 
253
        r := relation.NewRelationer(s.apiRelUnit, s.dir)
 
254
        err = r.Join()
 
255
        c.Assert(err, jc.ErrorIsNil)
 
256
 
 
257
        // Change Life to Dying check the results.
 
258
        err = r.SetDying()
 
259
        c.Assert(err, jc.ErrorIsNil)
 
260
 
 
261
        // Check that we cannot rejoin the relation.
 
262
        f := func() { r.Join() }
 
263
        c.Assert(f, gc.PanicMatches, "dying relationer must not join!")
 
264
 
 
265
        // Simulate a RelationBroken hook.
 
266
        err = r.CommitHook(hook.Info{Kind: hooks.RelationBroken})
 
267
        c.Assert(err, jc.ErrorIsNil)
 
268
 
 
269
        // Check that the relation state has been broken.
 
270
        err = s.dir.State().Validate(hook.Info{Kind: hooks.RelationBroken})
 
271
        c.Assert(err, gc.ErrorMatches, ".*: relation is broken and cannot be changed further")
 
272
 
 
273
        // Check that it left scope, by leaving scope on the other side and destroying
 
274
        // the relation.
 
275
        err = ru1.LeaveScope()
 
276
        c.Assert(err, jc.ErrorIsNil)
 
277
        err = u.Destroy()
 
278
        c.Assert(err, jc.ErrorIsNil)
 
279
        err = u.Refresh()
 
280
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
 
281
}
 
282
 
 
283
type stopper interface {
 
284
        Stop() error
 
285
}
 
286
 
 
287
func stop(c *gc.C, s stopper) {
 
288
        c.Assert(s.Stop(), gc.IsNil)
 
289
}
 
290
 
 
291
type RelationerImplicitSuite struct {
 
292
        jujutesting.JujuConnSuite
 
293
}
 
294
 
 
295
var _ = gc.Suite(&RelationerImplicitSuite{})
 
296
 
 
297
func (s *RelationerImplicitSuite) TestImplicitRelationer(c *gc.C) {
 
298
        // Create a relationer for an implicit endpoint (mysql:juju-info).
 
299
        mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
 
300
        u, err := mysql.AddUnit()
 
301
        c.Assert(err, jc.ErrorIsNil)
 
302
        machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
 
303
        c.Assert(err, jc.ErrorIsNil)
 
304
        err = u.AssignToMachine(machine)
 
305
        c.Assert(err, jc.ErrorIsNil)
 
306
        err = machine.SetProviderAddresses(network.NewScopedAddress("blah", network.ScopeCloudLocal))
 
307
        c.Assert(err, jc.ErrorIsNil)
 
308
        s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
 
309
        eps, err := s.State.InferEndpoints("logging", "mysql")
 
310
        c.Assert(err, jc.ErrorIsNil)
 
311
        rel, err := s.State.AddRelation(eps...)
 
312
        c.Assert(err, jc.ErrorIsNil)
 
313
        relsDir := c.MkDir()
 
314
        dir, err := relation.ReadStateDir(relsDir, rel.Id())
 
315
        c.Assert(err, jc.ErrorIsNil)
 
316
 
 
317
        password, err := utils.RandomPassword()
 
318
        c.Assert(err, jc.ErrorIsNil)
 
319
        err = u.SetPassword(password)
 
320
        c.Assert(err, jc.ErrorIsNil)
 
321
        st := s.OpenAPIAs(c, u.Tag(), password)
 
322
        uniterState, err := st.Uniter()
 
323
        c.Assert(err, jc.ErrorIsNil)
 
324
        c.Assert(uniterState, gc.NotNil)
 
325
 
 
326
        apiUnit, err := uniterState.Unit(u.Tag().(names.UnitTag))
 
327
        c.Assert(err, jc.ErrorIsNil)
 
328
        apiRel, err := uniterState.Relation(rel.Tag().(names.RelationTag))
 
329
        c.Assert(err, jc.ErrorIsNil)
 
330
        apiRelUnit, err := apiRel.Unit(apiUnit)
 
331
        c.Assert(err, jc.ErrorIsNil)
 
332
 
 
333
        r := relation.NewRelationer(apiRelUnit, dir)
 
334
        c.Assert(r, jc.Satisfies, (*relation.Relationer).IsImplicit)
 
335
 
 
336
        // Hooks are not allowed.
 
337
        f := func() { r.PrepareHook(hook.Info{}) }
 
338
        c.Assert(f, gc.PanicMatches, "implicit relations must not run hooks")
 
339
        f = func() { r.CommitHook(hook.Info{}) }
 
340
        c.Assert(f, gc.PanicMatches, "implicit relations must not run hooks")
 
341
 
 
342
        // Set it to Dying; check that the dir is removed immediately.
 
343
        err = r.SetDying()
 
344
        c.Assert(err, jc.ErrorIsNil)
 
345
        path := strconv.Itoa(rel.Id())
 
346
        ft.Removed{path}.Check(c, relsDir)
 
347
 
 
348
        err = rel.Destroy()
 
349
        c.Assert(err, jc.ErrorIsNil)
 
350
        err = rel.Refresh()
 
351
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
 
352
}