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

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/state/service_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 state_test
 
2
 
 
3
import (
 
4
        "fmt"
 
5
        "labix.org/v2/mgo"
 
6
        . "launchpad.net/gocheck"
 
7
        "launchpad.net/juju-core/charm"
 
8
        "launchpad.net/juju-core/constraints"
 
9
        "launchpad.net/juju-core/state"
 
10
        "sort"
 
11
        "time"
 
12
)
 
13
 
 
14
type ServiceSuite struct {
 
15
        ConnSuite
 
16
        charm *state.Charm
 
17
        mysql *state.Service
 
18
}
 
19
 
 
20
var _ = Suite(&ServiceSuite{})
 
21
 
 
22
func (s *ServiceSuite) SetUpTest(c *C) {
 
23
        s.ConnSuite.SetUpTest(c)
 
24
        s.charm = s.AddTestingCharm(c, "mysql")
 
25
        var err error
 
26
        s.mysql, err = s.State.AddService("mysql", s.charm)
 
27
        c.Assert(err, IsNil)
 
28
}
 
29
 
 
30
func (s *ServiceSuite) TestSetCharm(c *C) {
 
31
        ch, force, err := s.mysql.Charm()
 
32
        c.Assert(err, IsNil)
 
33
        c.Assert(ch.URL(), DeepEquals, s.charm.URL())
 
34
        c.Assert(force, Equals, false)
 
35
        url, force := s.mysql.CharmURL()
 
36
        c.Assert(url, DeepEquals, s.charm.URL())
 
37
        c.Assert(force, Equals, false)
 
38
 
 
39
        wp := s.AddTestingCharm(c, "wordpress")
 
40
        err = s.mysql.SetCharm(wp, true)
 
41
        ch, force, err1 := s.mysql.Charm()
 
42
        c.Assert(err1, IsNil)
 
43
        c.Assert(ch.URL(), DeepEquals, wp.URL())
 
44
        c.Assert(force, Equals, true)
 
45
        url, force = s.mysql.CharmURL()
 
46
        c.Assert(url, DeepEquals, wp.URL())
 
47
        c.Assert(force, Equals, true)
 
48
 
 
49
        // SetCharm fails when the service is Dying.
 
50
        _, err = s.mysql.AddUnit()
 
51
        c.Assert(err, IsNil)
 
52
        err = s.mysql.Destroy()
 
53
        c.Assert(err, IsNil)
 
54
        err = s.mysql.SetCharm(wp, true)
 
55
        c.Assert(err, ErrorMatches, `service "mysql" is not alive`)
 
56
}
 
57
 
 
58
func (s *ServiceSuite) TestSetCharmErrors(c *C) {
 
59
        logging := s.AddTestingCharm(c, "logging")
 
60
        err := s.mysql.SetCharm(logging, false)
 
61
        c.Assert(err, ErrorMatches, "cannot change a service's subordinacy")
 
62
 
 
63
        othermysql := s.AddSeriesCharm(c, "mysql", "otherseries")
 
64
        err = s.mysql.SetCharm(othermysql, false)
 
65
        c.Assert(err, ErrorMatches, "cannot change a service's series")
 
66
}
 
67
 
 
68
var stringConfig = `
 
69
options:
 
70
  key: {default: My Key, description: Desc, type: string}
 
71
`
 
72
var emptyConfig = `
 
73
options: {}
 
74
`
 
75
var floatConfig = `
 
76
options:
 
77
  key: {default: 0.42, description: Float key, type: float}
 
78
`
 
79
var newStringConfig = `
 
80
options:
 
81
  key: {default: My Key, description: Desc, type: string}
 
82
  other: {default: None, description: My Other, type: string}
 
83
`
 
84
 
 
85
var setCharmConfigTests = []struct {
 
86
        summary     string
 
87
        startconfig string
 
88
        startvalues map[string]interface{}
 
89
        endconfig   string
 
90
        endvalues   map[string]interface{}
 
91
        err         string
 
92
}{
 
93
        {
 
94
                summary:     "add float key to empty config",
 
95
                startconfig: emptyConfig,
 
96
                endconfig:   floatConfig,
 
97
        }, {
 
98
                summary:     "add string key to empty config",
 
99
                startconfig: emptyConfig,
 
100
                endconfig:   stringConfig,
 
101
        }, {
 
102
                summary:     "add string key and preserve existing values",
 
103
                startconfig: stringConfig,
 
104
                startvalues: map[string]interface{}{"key": "foo", "other": "bar"},
 
105
                endconfig:   newStringConfig,
 
106
                endvalues:   map[string]interface{}{"key": "foo", "other": "bar"},
 
107
        }, {
 
108
                summary:     "remove string key",
 
109
                startconfig: stringConfig,
 
110
                startvalues: map[string]interface{}{"key": "value"},
 
111
                endconfig:   emptyConfig,
 
112
        }, {
 
113
                summary:     "remove float key",
 
114
                startconfig: floatConfig,
 
115
                startvalues: map[string]interface{}{"key": 123.45},
 
116
                endconfig:   emptyConfig,
 
117
        }, {
 
118
                summary:     "change key type without values",
 
119
                startconfig: stringConfig,
 
120
                endconfig:   floatConfig,
 
121
        }, {
 
122
                summary:     "change key type with values",
 
123
                startconfig: stringConfig,
 
124
                startvalues: map[string]interface{}{"key": "value"},
 
125
                endconfig:   floatConfig,
 
126
                err:         `unexpected type in service configuration "key"="value"; expected float`,
 
127
        },
 
128
}
 
129
 
 
130
func (s *ServiceSuite) TestSetCharmConfig(c *C) {
 
131
        charms := map[string]*state.Charm{
 
132
                stringConfig:    s.AddConfigCharm(c, "wordpress", stringConfig, 1),
 
133
                emptyConfig:     s.AddConfigCharm(c, "wordpress", emptyConfig, 2),
 
134
                floatConfig:     s.AddConfigCharm(c, "wordpress", floatConfig, 3),
 
135
                newStringConfig: s.AddConfigCharm(c, "wordpress", newStringConfig, 4),
 
136
        }
 
137
 
 
138
        for i, t := range setCharmConfigTests {
 
139
                c.Logf("test %d: %s", i, t.summary)
 
140
 
 
141
                origCh := charms[t.startconfig]
 
142
                svc, err := s.State.AddService("wordpress", origCh)
 
143
                c.Assert(err, IsNil)
 
144
                cfg, err := svc.Config()
 
145
                c.Assert(err, IsNil)
 
146
                cfg.Update(t.startvalues)
 
147
                _, err = cfg.Write()
 
148
                c.Assert(err, IsNil)
 
149
 
 
150
                newCh := charms[t.endconfig]
 
151
                err = svc.SetCharm(newCh, false)
 
152
                var expectVals map[string]interface{}
 
153
                var expectCh *state.Charm
 
154
                if t.err != "" {
 
155
                        c.Assert(err, ErrorMatches, t.err)
 
156
                        expectCh = origCh
 
157
                        expectVals = t.startvalues
 
158
                } else {
 
159
                        c.Assert(err, IsNil)
 
160
                        expectCh = newCh
 
161
                        expectVals = t.endvalues
 
162
                }
 
163
 
 
164
                sch, _, err := svc.Charm()
 
165
                c.Assert(err, IsNil)
 
166
                c.Assert(sch.URL(), DeepEquals, expectCh.URL())
 
167
                cfg, err = svc.Config()
 
168
                c.Assert(err, IsNil)
 
169
                if len(expectVals) == 0 {
 
170
                        c.Assert(cfg.Map(), HasLen, 0)
 
171
                } else {
 
172
                        c.Assert(cfg.Map(), DeepEquals, expectVals)
 
173
                }
 
174
 
 
175
                err = svc.Destroy()
 
176
                c.Assert(err, IsNil)
 
177
        }
 
178
}
 
179
 
 
180
func serviceSet(options map[string]string) func(svc *state.Service) error {
 
181
        return func(svc *state.Service) error {
 
182
                return svc.SetConfig(options)
 
183
        }
 
184
}
 
185
 
 
186
func serviceSetYAML(yaml string) func(svc *state.Service) error {
 
187
        return func(svc *state.Service) error {
 
188
                return svc.SetConfigYAML([]byte(yaml))
 
189
        }
 
190
}
 
191
 
 
192
var serviceSetTests = []struct {
 
193
        about   string
 
194
        initial map[string]interface{}
 
195
        set     func(st *state.Service) error
 
196
        expect  map[string]interface{} // resulting configuration of the dummy service.
 
197
        err     string                 // error regex
 
198
}{{
 
199
        about: "unknown option",
 
200
        set:   serviceSet(map[string]string{"foo": "bar"}),
 
201
        err:   `Unknown configuration option: "foo"`,
 
202
}, {
 
203
        about: "set outlook",
 
204
        set:   serviceSet(map[string]string{"outlook": "positive"}),
 
205
        expect: map[string]interface{}{
 
206
                "outlook": "positive",
 
207
        },
 
208
}, {
 
209
        about: "unset outlook and set title",
 
210
        initial: map[string]interface{}{
 
211
                "outlook": "positive",
 
212
        },
 
213
        set: serviceSet(map[string]string{
 
214
                "outlook": "",
 
215
                "title":   "sir",
 
216
        },
 
217
        ),
 
218
        expect: map[string]interface{}{
 
219
                "title": "sir",
 
220
        },
 
221
}, {
 
222
        about: "set a default value",
 
223
        initial: map[string]interface{}{
 
224
                "title": "sir",
 
225
        },
 
226
        set: serviceSet(map[string]string{"username": "admin001"}),
 
227
        expect: map[string]interface{}{
 
228
                "username": "admin001",
 
229
                "title":    "sir",
 
230
        },
 
231
}, {
 
232
        about: "unset a default value, set a different default",
 
233
        initial: map[string]interface{}{
 
234
                "username": "admin001",
 
235
                "title":    "sir",
 
236
        },
 
237
        set: serviceSet(map[string]string{
 
238
                "username": "",
 
239
                "title":    "My Title",
 
240
        },
 
241
        ),
 
242
        expect: map[string]interface{}{
 
243
                "title": "My Title",
 
244
        },
 
245
}, {
 
246
        about: "bad configuration",
 
247
        set:   serviceSetYAML("345"),
 
248
        err:   "malformed YAML data",
 
249
}, {
 
250
        about:  "config with no options",
 
251
        set:    serviceSetYAML("{}"),
 
252
        expect: map[string]interface{}{},
 
253
}, {
 
254
        about: "set some attributes",
 
255
        initial: map[string]interface{}{
 
256
                "title": "sir",
 
257
        },
 
258
        set: serviceSetYAML("skill-level: 9000\nusername: admin001\n\n"),
 
259
        expect: map[string]interface{}{
 
260
                "title":       "sir",
 
261
                "username":    "admin001",
 
262
                "skill-level": int64(9000), // yaml int types are int64
 
263
        },
 
264
}, {
 
265
        about: "remove an attribute by setting to empty string",
 
266
        initial: map[string]interface{}{
 
267
                "title":    "sir",
 
268
                "username": "foo",
 
269
        },
 
270
        set: serviceSetYAML("title: ''\n"),
 
271
        expect: map[string]interface{}{
 
272
                "username": "foo",
 
273
        },
 
274
},
 
275
}
 
276
 
 
277
func (s *ServiceSuite) TestSet(c *C) {
 
278
        sch := s.AddTestingCharm(c, "dummy")
 
279
        for i, t := range serviceSetTests {
 
280
                c.Logf("test %d. %s", i, t.about)
 
281
                svc, err := s.State.AddService("dummy-service", sch)
 
282
                c.Assert(err, IsNil)
 
283
                if t.initial != nil {
 
284
                        cfg, err := svc.Config()
 
285
                        c.Assert(err, IsNil)
 
286
                        cfg.Update(t.initial)
 
287
                        _, err = cfg.Write()
 
288
                        c.Assert(err, IsNil)
 
289
                }
 
290
                err = t.set(svc)
 
291
                if t.err != "" {
 
292
                        c.Assert(err, ErrorMatches, t.err)
 
293
                } else {
 
294
                        c.Assert(err, IsNil)
 
295
                        cfg, err := svc.Config()
 
296
                        c.Assert(err, IsNil)
 
297
                        c.Assert(cfg.Map(), DeepEquals, t.expect)
 
298
                }
 
299
                err = svc.Destroy()
 
300
                c.Assert(err, IsNil)
 
301
        }
 
302
}
 
303
 
 
304
func (s *ServiceSuite) TestSettingsRefCountWorks(c *C) {
 
305
        oldCh := s.AddConfigCharm(c, "wordpress", emptyConfig, 1)
 
306
        newCh := s.AddConfigCharm(c, "wordpress", emptyConfig, 2)
 
307
        svcName := "mywp"
 
308
 
 
309
        assertNoRef := func(sch *state.Charm) {
 
310
                _, err := state.ServiceSettingsRefCount(s.State, svcName, sch.URL())
 
311
                c.Assert(err, Equals, mgo.ErrNotFound)
 
312
        }
 
313
        assertRef := func(sch *state.Charm, refcount int) {
 
314
                rc, err := state.ServiceSettingsRefCount(s.State, svcName, sch.URL())
 
315
                c.Assert(err, IsNil)
 
316
                c.Assert(rc, Equals, refcount)
 
317
        }
 
318
 
 
319
        assertNoRef(oldCh)
 
320
        assertNoRef(newCh)
 
321
 
 
322
        svc, err := s.State.AddService(svcName, oldCh)
 
323
        c.Assert(err, IsNil)
 
324
        assertRef(oldCh, 1)
 
325
        assertNoRef(newCh)
 
326
 
 
327
        err = svc.SetCharm(oldCh, false)
 
328
        c.Assert(err, IsNil)
 
329
        assertRef(oldCh, 1)
 
330
        assertNoRef(newCh)
 
331
 
 
332
        err = svc.SetCharm(newCh, false)
 
333
        c.Assert(err, IsNil)
 
334
        assertNoRef(oldCh)
 
335
        assertRef(newCh, 1)
 
336
 
 
337
        err = svc.SetCharm(oldCh, false)
 
338
        c.Assert(err, IsNil)
 
339
        assertRef(oldCh, 1)
 
340
        assertNoRef(newCh)
 
341
 
 
342
        u, err := svc.AddUnit()
 
343
        c.Assert(err, IsNil)
 
344
        curl, ok := u.CharmURL()
 
345
        c.Assert(ok, Equals, false)
 
346
        assertRef(oldCh, 1)
 
347
        assertNoRef(newCh)
 
348
 
 
349
        err = u.SetCharmURL(oldCh.URL())
 
350
        c.Assert(err, IsNil)
 
351
        curl, ok = u.CharmURL()
 
352
        c.Assert(ok, Equals, true)
 
353
        c.Assert(curl, DeepEquals, oldCh.URL())
 
354
        assertRef(oldCh, 2)
 
355
        assertNoRef(newCh)
 
356
 
 
357
        err = u.EnsureDead()
 
358
        c.Assert(err, IsNil)
 
359
        assertRef(oldCh, 2)
 
360
        assertNoRef(newCh)
 
361
 
 
362
        err = u.Remove()
 
363
        c.Assert(err, IsNil)
 
364
        assertRef(oldCh, 1)
 
365
        assertNoRef(newCh)
 
366
 
 
367
        err = svc.Destroy()
 
368
        c.Assert(err, IsNil)
 
369
        assertNoRef(oldCh)
 
370
        assertNoRef(newCh)
 
371
}
 
372
 
 
373
const mysqlBaseMeta = `
 
374
name: mysql
 
375
summary: "Database engine"
 
376
description: "A pretty popular database"
 
377
provides:
 
378
  server: mysql
 
379
`
 
380
const onePeerMeta = `
 
381
peers:
 
382
  cluster: mysql
 
383
`
 
384
const twoPeersMeta = `
 
385
peers:
 
386
  cluster: mysql
 
387
  loadbalancer: phony
 
388
`
 
389
 
 
390
func (s *ServiceSuite) assertServiceRelations(c *C, svc *state.Service, expectedKeys ...string) []*state.Relation {
 
391
        rels, err := svc.Relations()
 
392
        c.Assert(err, IsNil)
 
393
        if len(rels) == 0 {
 
394
                return nil
 
395
        }
 
396
        relKeys := make([]string, len(expectedKeys))
 
397
        for i, rel := range rels {
 
398
                relKeys[i] = rel.String()
 
399
        }
 
400
        sort.Strings(relKeys)
 
401
        c.Assert(relKeys, DeepEquals, expectedKeys)
 
402
        return rels
 
403
}
 
404
 
 
405
func (s *ServiceSuite) TestNewPeerRelationsAddedOnUpgrade(c *C) {
 
406
        // Original mysql charm has no peer relations.
 
407
        oldCh := s.AddMetaCharm(c, "mysql", mysqlBaseMeta+onePeerMeta, 2)
 
408
        newCh := s.AddMetaCharm(c, "mysql", mysqlBaseMeta+twoPeersMeta, 3)
 
409
 
 
410
        // No relations joined yet.
 
411
        s.assertServiceRelations(c, s.mysql)
 
412
 
 
413
        err := s.mysql.SetCharm(oldCh, false)
 
414
        c.Assert(err, IsNil)
 
415
        s.assertServiceRelations(c, s.mysql, "mysql:cluster")
 
416
 
 
417
        err = s.mysql.SetCharm(newCh, false)
 
418
        c.Assert(err, IsNil)
 
419
        rels := s.assertServiceRelations(c, s.mysql, "mysql:cluster", "mysql:loadbalancer")
 
420
 
 
421
        // Check state consistency by attempting to destroy the service.
 
422
        err = s.mysql.Destroy()
 
423
        c.Assert(err, IsNil)
 
424
 
 
425
        // Check the peer relations got destroyed as well.
 
426
        for _, rel := range rels {
 
427
                err = rel.Refresh()
 
428
                c.Assert(state.IsNotFound(err), Equals, true)
 
429
        }
 
430
}
 
431
 
 
432
func jujuInfoEp(serviceName string) state.Endpoint {
 
433
        return state.Endpoint{
 
434
                ServiceName: serviceName,
 
435
                Relation: charm.Relation{
 
436
                        Interface: "juju-info",
 
437
                        Name:      "juju-info",
 
438
                        Role:      charm.RoleProvider,
 
439
                        Scope:     charm.ScopeGlobal,
 
440
                },
 
441
        }
 
442
}
 
443
 
 
444
func (s *ServiceSuite) TestTag(c *C) {
 
445
        c.Assert(s.mysql.Tag(), Equals, "service-mysql")
 
446
}
 
447
 
 
448
func (s *ServiceSuite) TestMysqlEndpoints(c *C) {
 
449
        _, err := s.mysql.Endpoint("mysql")
 
450
        c.Assert(err, ErrorMatches, `service "mysql" has no "mysql" relation`)
 
451
 
 
452
        jiEP, err := s.mysql.Endpoint("juju-info")
 
453
        c.Assert(err, IsNil)
 
454
        c.Assert(jiEP, DeepEquals, jujuInfoEp("mysql"))
 
455
 
 
456
        serverEP, err := s.mysql.Endpoint("server")
 
457
        c.Assert(err, IsNil)
 
458
        c.Assert(serverEP, DeepEquals, state.Endpoint{
 
459
                ServiceName: "mysql",
 
460
                Relation: charm.Relation{
 
461
                        Interface: "mysql",
 
462
                        Name:      "server",
 
463
                        Role:      charm.RoleProvider,
 
464
                        Scope:     charm.ScopeGlobal,
 
465
                },
 
466
        })
 
467
 
 
468
        eps, err := s.mysql.Endpoints()
 
469
        c.Assert(err, IsNil)
 
470
        c.Assert(eps, DeepEquals, []state.Endpoint{jiEP, serverEP})
 
471
}
 
472
 
 
473
func (s *ServiceSuite) TestRiakEndpoints(c *C) {
 
474
        riak, err := s.State.AddService("myriak", s.AddTestingCharm(c, "riak"))
 
475
        c.Assert(err, IsNil)
 
476
 
 
477
        _, err = riak.Endpoint("garble")
 
478
        c.Assert(err, ErrorMatches, `service "myriak" has no "garble" relation`)
 
479
 
 
480
        jiEP, err := riak.Endpoint("juju-info")
 
481
        c.Assert(err, IsNil)
 
482
        c.Assert(jiEP, DeepEquals, jujuInfoEp("myriak"))
 
483
 
 
484
        ringEP, err := riak.Endpoint("ring")
 
485
        c.Assert(err, IsNil)
 
486
        c.Assert(ringEP, DeepEquals, state.Endpoint{
 
487
                ServiceName: "myriak",
 
488
                Relation: charm.Relation{
 
489
                        Interface: "riak",
 
490
                        Name:      "ring",
 
491
                        Role:      charm.RolePeer,
 
492
                        Scope:     charm.ScopeGlobal,
 
493
                        Limit:     1,
 
494
                },
 
495
        })
 
496
 
 
497
        adminEP, err := riak.Endpoint("admin")
 
498
        c.Assert(err, IsNil)
 
499
        c.Assert(adminEP, DeepEquals, state.Endpoint{
 
500
                ServiceName: "myriak",
 
501
                Relation: charm.Relation{
 
502
                        Interface: "http",
 
503
                        Name:      "admin",
 
504
                        Role:      charm.RoleProvider,
 
505
                        Scope:     charm.ScopeGlobal,
 
506
                },
 
507
        })
 
508
 
 
509
        endpointEP, err := riak.Endpoint("endpoint")
 
510
        c.Assert(err, IsNil)
 
511
        c.Assert(endpointEP, DeepEquals, state.Endpoint{
 
512
                ServiceName: "myriak",
 
513
                Relation: charm.Relation{
 
514
                        Interface: "http",
 
515
                        Name:      "endpoint",
 
516
                        Role:      charm.RoleProvider,
 
517
                        Scope:     charm.ScopeGlobal,
 
518
                },
 
519
        })
 
520
 
 
521
        eps, err := riak.Endpoints()
 
522
        c.Assert(err, IsNil)
 
523
        c.Assert(eps, DeepEquals, []state.Endpoint{adminEP, endpointEP, jiEP, ringEP})
 
524
}
 
525
 
 
526
func (s *ServiceSuite) TestWordpressEndpoints(c *C) {
 
527
        wordpress, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress"))
 
528
        c.Assert(err, IsNil)
 
529
 
 
530
        _, err = wordpress.Endpoint("nonsense")
 
531
        c.Assert(err, ErrorMatches, `service "wordpress" has no "nonsense" relation`)
 
532
 
 
533
        jiEP, err := wordpress.Endpoint("juju-info")
 
534
        c.Assert(err, IsNil)
 
535
        c.Assert(jiEP, DeepEquals, jujuInfoEp("wordpress"))
 
536
 
 
537
        urlEP, err := wordpress.Endpoint("url")
 
538
        c.Assert(err, IsNil)
 
539
        c.Assert(urlEP, DeepEquals, state.Endpoint{
 
540
                ServiceName: "wordpress",
 
541
                Relation: charm.Relation{
 
542
                        Interface: "http",
 
543
                        Name:      "url",
 
544
                        Role:      charm.RoleProvider,
 
545
                        Scope:     charm.ScopeGlobal,
 
546
                },
 
547
        })
 
548
 
 
549
        ldEP, err := wordpress.Endpoint("logging-dir")
 
550
        c.Assert(err, IsNil)
 
551
        c.Assert(ldEP, DeepEquals, state.Endpoint{
 
552
                ServiceName: "wordpress",
 
553
                Relation: charm.Relation{
 
554
                        Interface: "logging",
 
555
                        Name:      "logging-dir",
 
556
                        Role:      charm.RoleProvider,
 
557
                        Scope:     charm.ScopeContainer,
 
558
                },
 
559
        })
 
560
 
 
561
        dbEP, err := wordpress.Endpoint("db")
 
562
        c.Assert(err, IsNil)
 
563
        c.Assert(dbEP, DeepEquals, state.Endpoint{
 
564
                ServiceName: "wordpress",
 
565
                Relation: charm.Relation{
 
566
                        Interface: "mysql",
 
567
                        Name:      "db",
 
568
                        Role:      charm.RoleRequirer,
 
569
                        Scope:     charm.ScopeGlobal,
 
570
                        Limit:     1,
 
571
                },
 
572
        })
 
573
 
 
574
        cacheEP, err := wordpress.Endpoint("cache")
 
575
        c.Assert(err, IsNil)
 
576
        c.Assert(cacheEP, DeepEquals, state.Endpoint{
 
577
                ServiceName: "wordpress",
 
578
                Relation: charm.Relation{
 
579
                        Interface: "varnish",
 
580
                        Name:      "cache",
 
581
                        Role:      charm.RoleRequirer,
 
582
                        Scope:     charm.ScopeGlobal,
 
583
                        Limit:     2,
 
584
                        Optional:  true,
 
585
                },
 
586
        })
 
587
 
 
588
        eps, err := wordpress.Endpoints()
 
589
        c.Assert(err, IsNil)
 
590
        c.Assert(eps, DeepEquals, []state.Endpoint{cacheEP, dbEP, jiEP, ldEP, urlEP})
 
591
}
 
592
 
 
593
func (s *ServiceSuite) TestServiceRefresh(c *C) {
 
594
        s1, err := s.State.Service(s.mysql.Name())
 
595
        c.Assert(err, IsNil)
 
596
 
 
597
        err = s.mysql.SetCharm(s.charm, true)
 
598
        c.Assert(err, IsNil)
 
599
 
 
600
        testch, force, err := s1.Charm()
 
601
        c.Assert(err, IsNil)
 
602
        c.Assert(force, Equals, false)
 
603
        c.Assert(testch.URL(), DeepEquals, s.charm.URL())
 
604
 
 
605
        err = s1.Refresh()
 
606
        c.Assert(err, IsNil)
 
607
        testch, force, err = s1.Charm()
 
608
        c.Assert(err, IsNil)
 
609
        c.Assert(force, Equals, true)
 
610
        c.Assert(testch.URL(), DeepEquals, s.charm.URL())
 
611
 
 
612
        err = s.mysql.Destroy()
 
613
        c.Assert(err, IsNil)
 
614
        err = s.mysql.Refresh()
 
615
        c.Assert(state.IsNotFound(err), Equals, true)
 
616
}
 
617
 
 
618
func (s *ServiceSuite) TestServiceExposed(c *C) {
 
619
        // Check that querying for the exposed flag works correctly.
 
620
        c.Assert(s.mysql.IsExposed(), Equals, false)
 
621
 
 
622
        // Check that setting and clearing the exposed flag works correctly.
 
623
        err := s.mysql.SetExposed()
 
624
        c.Assert(err, IsNil)
 
625
        c.Assert(s.mysql.IsExposed(), Equals, true)
 
626
        err = s.mysql.ClearExposed()
 
627
        c.Assert(err, IsNil)
 
628
        c.Assert(s.mysql.IsExposed(), Equals, false)
 
629
 
 
630
        // Check that setting and clearing the exposed flag repeatedly does not fail.
 
631
        err = s.mysql.SetExposed()
 
632
        c.Assert(err, IsNil)
 
633
        err = s.mysql.SetExposed()
 
634
        c.Assert(err, IsNil)
 
635
        err = s.mysql.ClearExposed()
 
636
        c.Assert(err, IsNil)
 
637
        err = s.mysql.ClearExposed()
 
638
        c.Assert(err, IsNil)
 
639
        err = s.mysql.SetExposed()
 
640
        c.Assert(err, IsNil)
 
641
        c.Assert(s.mysql.IsExposed(), Equals, true)
 
642
 
 
643
        // Make the service Dying and check that ClearExposed and SetExposed fail.
 
644
        // TODO(fwereade): maybe service destruction should always unexpose?
 
645
        u, err := s.mysql.AddUnit()
 
646
        c.Assert(err, IsNil)
 
647
        err = s.mysql.Destroy()
 
648
        c.Assert(err, IsNil)
 
649
        err = s.mysql.ClearExposed()
 
650
        c.Assert(err, ErrorMatches, notAliveErr)
 
651
        err = s.mysql.SetExposed()
 
652
        c.Assert(err, ErrorMatches, notAliveErr)
 
653
 
 
654
        // Remove the service and check that both fail.
 
655
        err = u.EnsureDead()
 
656
        c.Assert(err, IsNil)
 
657
        err = u.Remove()
 
658
        c.Assert(err, IsNil)
 
659
        err = s.mysql.SetExposed()
 
660
        c.Assert(err, ErrorMatches, notAliveErr)
 
661
        err = s.mysql.ClearExposed()
 
662
        c.Assert(err, ErrorMatches, notAliveErr)
 
663
}
 
664
 
 
665
func (s *ServiceSuite) TestAddUnit(c *C) {
 
666
        // Check that principal units can be added on their own.
 
667
        unitZero, err := s.mysql.AddUnit()
 
668
        c.Assert(err, IsNil)
 
669
        c.Assert(unitZero.Name(), Equals, "mysql/0")
 
670
        c.Assert(unitZero.IsPrincipal(), Equals, true)
 
671
        c.Assert(unitZero.SubordinateNames(), HasLen, 0)
 
672
        unitOne, err := s.mysql.AddUnit()
 
673
        c.Assert(err, IsNil)
 
674
        c.Assert(unitOne.Name(), Equals, "mysql/1")
 
675
        c.Assert(unitOne.IsPrincipal(), Equals, true)
 
676
        c.Assert(unitOne.SubordinateNames(), HasLen, 0)
 
677
 
 
678
        // Assign the principal unit to a machine.
 
679
        m, err := s.State.AddMachine("series", state.JobHostUnits)
 
680
        c.Assert(err, IsNil)
 
681
        err = unitZero.AssignToMachine(m)
 
682
        c.Assert(err, IsNil)
 
683
 
 
684
        // Add a subordinate service and check that units cannot be added directly.
 
685
        // to add a subordinate unit.
 
686
        subCharm := s.AddTestingCharm(c, "logging")
 
687
        logging, err := s.State.AddService("logging", subCharm)
 
688
        c.Assert(err, IsNil)
 
689
        _, err = logging.AddUnit()
 
690
        c.Assert(err, ErrorMatches, `cannot add unit to service "logging": service is a subordinate`)
 
691
 
 
692
        // Indirectly create a subordinate unit by adding a relation and entering
 
693
        // scope as a principal.
 
694
        eps, err := s.State.InferEndpoints([]string{"logging", "mysql"})
 
695
        c.Assert(err, IsNil)
 
696
        rel, err := s.State.AddRelation(eps...)
 
697
        c.Assert(err, IsNil)
 
698
        ru, err := rel.Unit(unitZero)
 
699
        c.Assert(err, IsNil)
 
700
        err = ru.EnterScope(nil)
 
701
        c.Assert(err, IsNil)
 
702
        subZero, err := s.State.Unit("logging/0")
 
703
        c.Assert(err, IsNil)
 
704
 
 
705
        // Check that once it's refreshed unitZero has subordinates.
 
706
        err = unitZero.Refresh()
 
707
        c.Assert(err, IsNil)
 
708
        c.Assert(unitZero.SubordinateNames(), DeepEquals, []string{"logging/0"})
 
709
 
 
710
        // Check the subordinate unit has been assigned its principal's machine.
 
711
        id, err := subZero.AssignedMachineId()
 
712
        c.Assert(err, IsNil)
 
713
        c.Assert(id, Equals, m.Id())
 
714
}
 
715
 
 
716
func (s *ServiceSuite) TestAddUnitWhenNotAlive(c *C) {
 
717
        u, err := s.mysql.AddUnit()
 
718
        c.Assert(err, IsNil)
 
719
        err = s.mysql.Destroy()
 
720
        c.Assert(err, IsNil)
 
721
        _, err = s.mysql.AddUnit()
 
722
        c.Assert(err, ErrorMatches, `cannot add unit to service "mysql": service is not alive`)
 
723
        err = u.EnsureDead()
 
724
        c.Assert(err, IsNil)
 
725
        err = u.Remove()
 
726
        c.Assert(err, IsNil)
 
727
        _, err = s.mysql.AddUnit()
 
728
        c.Assert(err, ErrorMatches, `cannot add unit to service "mysql": service "mysql" not found`)
 
729
}
 
730
 
 
731
func (s *ServiceSuite) TestReadUnit(c *C) {
 
732
        _, err := s.mysql.AddUnit()
 
733
        c.Assert(err, IsNil)
 
734
        _, err = s.mysql.AddUnit()
 
735
        c.Assert(err, IsNil)
 
736
 
 
737
        // Check that retrieving a unit from the service works correctly.
 
738
        unit, err := s.mysql.Unit("mysql/0")
 
739
        c.Assert(err, IsNil)
 
740
        c.Assert(unit.Name(), Equals, "mysql/0")
 
741
 
 
742
        // Check that retrieving a unit from state works correctly.
 
743
        unit, err = s.State.Unit("mysql/0")
 
744
        c.Assert(err, IsNil)
 
745
        c.Assert(unit.Name(), Equals, "mysql/0")
 
746
 
 
747
        // Check that retrieving a non-existent or an invalidly
 
748
        // named unit fail nicely.
 
749
        unit, err = s.mysql.Unit("mysql")
 
750
        c.Assert(err, ErrorMatches, `"mysql" is not a valid unit name`)
 
751
        unit, err = s.mysql.Unit("mysql/0/0")
 
752
        c.Assert(err, ErrorMatches, `"mysql/0/0" is not a valid unit name`)
 
753
        unit, err = s.mysql.Unit("pressword/0")
 
754
        c.Assert(err, ErrorMatches, `cannot get unit "pressword/0" from service "mysql": .*`)
 
755
 
 
756
        // Check direct state retrieval also fails nicely.
 
757
        unit, err = s.State.Unit("mysql")
 
758
        c.Assert(err, ErrorMatches, `"mysql" is not a valid unit name`)
 
759
        unit, err = s.State.Unit("mysql/0/0")
 
760
        c.Assert(err, ErrorMatches, `"mysql/0/0" is not a valid unit name`)
 
761
        unit, err = s.State.Unit("pressword/0")
 
762
        c.Assert(err, ErrorMatches, `unit "pressword/0" not found`)
 
763
 
 
764
        // Add another service to check units are not misattributed.
 
765
        mysql, err := s.State.AddService("wordpress", s.charm)
 
766
        c.Assert(err, IsNil)
 
767
        _, err = mysql.AddUnit()
 
768
        c.Assert(err, IsNil)
 
769
 
 
770
        // BUG(aram): use error strings from state.
 
771
        unit, err = s.mysql.Unit("wordpress/0")
 
772
        c.Assert(err, ErrorMatches, `cannot get unit "wordpress/0" from service "mysql": .*`)
 
773
 
 
774
        units, err := s.mysql.AllUnits()
 
775
        c.Assert(err, IsNil)
 
776
        c.Assert(sortedUnitNames(units), DeepEquals, []string{"mysql/0", "mysql/1"})
 
777
}
 
778
 
 
779
func (s *ServiceSuite) TestReadUnitWhenDying(c *C) {
 
780
        // Test that we can still read units when the service is Dying...
 
781
        unit, err := s.mysql.AddUnit()
 
782
        c.Assert(err, IsNil)
 
783
        preventUnitDestroyRemove(c, s.State, unit)
 
784
        err = s.mysql.Destroy()
 
785
        c.Assert(err, IsNil)
 
786
        _, err = s.mysql.AllUnits()
 
787
        c.Assert(err, IsNil)
 
788
        _, err = s.mysql.Unit("mysql/0")
 
789
        c.Assert(err, IsNil)
 
790
 
 
791
        // ...and when those units are Dying or Dead...
 
792
        testWhenDying(c, unit, noErr, noErr, func() error {
 
793
                _, err := s.mysql.AllUnits()
 
794
                return err
 
795
        }, func() error {
 
796
                _, err := s.mysql.Unit("mysql/0")
 
797
                return err
 
798
        })
 
799
 
 
800
        // ...and even, in a very limited way, when the service itself is removed.
 
801
        removeAllUnits(c, s.mysql)
 
802
        _, err = s.mysql.AllUnits()
 
803
        c.Assert(err, IsNil)
 
804
}
 
805
 
 
806
func (s *ServiceSuite) TestDestroySimple(c *C) {
 
807
        err := s.mysql.Destroy()
 
808
        c.Assert(err, IsNil)
 
809
        c.Assert(s.mysql.Life(), Equals, state.Dying)
 
810
        err = s.mysql.Refresh()
 
811
        c.Assert(state.IsNotFound(err), Equals, true)
 
812
}
 
813
 
 
814
func (s *ServiceSuite) TestDestroyStillHasUnits(c *C) {
 
815
        unit, err := s.mysql.AddUnit()
 
816
        c.Assert(err, IsNil)
 
817
        err = s.mysql.Destroy()
 
818
        c.Assert(err, IsNil)
 
819
        c.Assert(s.mysql.Life(), Equals, state.Dying)
 
820
 
 
821
        err = unit.EnsureDead()
 
822
        c.Assert(err, IsNil)
 
823
        err = s.mysql.Refresh()
 
824
        c.Assert(err, IsNil)
 
825
        c.Assert(s.mysql.Life(), Equals, state.Dying)
 
826
 
 
827
        err = unit.Remove()
 
828
        c.Assert(err, IsNil)
 
829
        err = s.mysql.Refresh()
 
830
        c.Assert(state.IsNotFound(err), Equals, true)
 
831
}
 
832
 
 
833
func (s *ServiceSuite) TestDestroyOnceHadUnits(c *C) {
 
834
        unit, err := s.mysql.AddUnit()
 
835
        c.Assert(err, IsNil)
 
836
        err = unit.EnsureDead()
 
837
        c.Assert(err, IsNil)
 
838
        err = unit.Remove()
 
839
        c.Assert(err, IsNil)
 
840
 
 
841
        err = s.mysql.Destroy()
 
842
        c.Assert(err, IsNil)
 
843
        c.Assert(s.mysql.Life(), Equals, state.Dying)
 
844
        err = s.mysql.Refresh()
 
845
        c.Assert(state.IsNotFound(err), Equals, true)
 
846
}
 
847
 
 
848
func (s *ServiceSuite) TestDestroyStaleNonZeroUnitCount(c *C) {
 
849
        unit, err := s.mysql.AddUnit()
 
850
        c.Assert(err, IsNil)
 
851
        err = s.mysql.Refresh()
 
852
        c.Assert(err, IsNil)
 
853
        err = unit.EnsureDead()
 
854
        c.Assert(err, IsNil)
 
855
        err = unit.Remove()
 
856
        c.Assert(err, IsNil)
 
857
 
 
858
        err = s.mysql.Destroy()
 
859
        c.Assert(err, IsNil)
 
860
        c.Assert(s.mysql.Life(), Equals, state.Dying)
 
861
        err = s.mysql.Refresh()
 
862
        c.Assert(state.IsNotFound(err), Equals, true)
 
863
}
 
864
 
 
865
func (s *ServiceSuite) TestDestroyStaleZeroUnitCount(c *C) {
 
866
        unit, err := s.mysql.AddUnit()
 
867
        c.Assert(err, IsNil)
 
868
 
 
869
        err = s.mysql.Destroy()
 
870
        c.Assert(err, IsNil)
 
871
        c.Assert(s.mysql.Life(), Equals, state.Dying)
 
872
 
 
873
        err = s.mysql.Refresh()
 
874
        c.Assert(err, IsNil)
 
875
        c.Assert(s.mysql.Life(), Equals, state.Dying)
 
876
 
 
877
        err = unit.EnsureDead()
 
878
        c.Assert(err, IsNil)
 
879
        err = s.mysql.Refresh()
 
880
        c.Assert(err, IsNil)
 
881
        c.Assert(s.mysql.Life(), Equals, state.Dying)
 
882
 
 
883
        err = unit.Remove()
 
884
        c.Assert(err, IsNil)
 
885
        err = s.mysql.Refresh()
 
886
        c.Assert(state.IsNotFound(err), Equals, true)
 
887
}
 
888
 
 
889
func (s *ServiceSuite) TestDestroyWithRemovableRelation(c *C) {
 
890
        wordpress, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress"))
 
891
        c.Assert(err, IsNil)
 
892
        eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"})
 
893
        c.Assert(err, IsNil)
 
894
        rel, err := s.State.AddRelation(eps...)
 
895
        c.Assert(err, IsNil)
 
896
 
 
897
        // Destroy a service with no units in relation scope; check service and
 
898
        // unit removed.
 
899
        err = wordpress.Destroy()
 
900
        c.Assert(err, IsNil)
 
901
        err = wordpress.Refresh()
 
902
        c.Assert(state.IsNotFound(err), Equals, true)
 
903
        err = rel.Refresh()
 
904
        c.Assert(state.IsNotFound(err), Equals, true)
 
905
}
 
906
 
 
907
func (s *ServiceSuite) TestDestroyWithReferencedRelation(c *C) {
 
908
        s.assertDestroyWithReferencedRelation(c, true)
 
909
}
 
910
 
 
911
func (s *ServiceSuite) TestDestroyWithreferencedRelationStaleCount(c *C) {
 
912
        s.assertDestroyWithReferencedRelation(c, false)
 
913
}
 
914
 
 
915
func (s *ServiceSuite) assertDestroyWithReferencedRelation(c *C, refresh bool) {
 
916
        wordpress, err := s.State.AddService("wordpress", s.AddTestingCharm(c, "wordpress"))
 
917
        c.Assert(err, IsNil)
 
918
        eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"})
 
919
        c.Assert(err, IsNil)
 
920
        rel0, err := s.State.AddRelation(eps...)
 
921
        c.Assert(err, IsNil)
 
922
 
 
923
        _, err = s.State.AddService("logging", s.AddTestingCharm(c, "logging"))
 
924
        c.Assert(err, IsNil)
 
925
        eps, err = s.State.InferEndpoints([]string{"logging", "mysql"})
 
926
        c.Assert(err, IsNil)
 
927
        rel1, err := s.State.AddRelation(eps...)
 
928
        c.Assert(err, IsNil)
 
929
 
 
930
        // Add a separate reference to the first relation.
 
931
        unit, err := wordpress.AddUnit()
 
932
        c.Assert(err, IsNil)
 
933
        ru, err := rel0.Unit(unit)
 
934
        c.Assert(err, IsNil)
 
935
        err = ru.EnterScope(nil)
 
936
        c.Assert(err, IsNil)
 
937
 
 
938
        // Optionally update the service document to get correct relation counts.
 
939
        if refresh {
 
940
                err = s.mysql.Destroy()
 
941
                c.Assert(err, IsNil)
 
942
        }
 
943
 
 
944
        // Destroy, and check that the first relation becomes Dying...
 
945
        err = s.mysql.Destroy()
 
946
        c.Assert(err, IsNil)
 
947
        err = rel0.Refresh()
 
948
        c.Assert(err, IsNil)
 
949
        c.Assert(rel0.Life(), Equals, state.Dying)
 
950
 
 
951
        // ...while the second is removed directly.
 
952
        err = rel1.Refresh()
 
953
        c.Assert(state.IsNotFound(err), Equals, true)
 
954
 
 
955
        // Drop the last reference to the first relation; check the relation and
 
956
        // the service are are both removed.
 
957
        err = ru.LeaveScope()
 
958
        c.Assert(err, IsNil)
 
959
        err = s.mysql.Refresh()
 
960
        c.Assert(state.IsNotFound(err), Equals, true)
 
961
        err = rel0.Refresh()
 
962
        c.Assert(state.IsNotFound(err), Equals, true)
 
963
}
 
964
 
 
965
func (s *ServiceSuite) TestReadUnitWithChangingState(c *C) {
 
966
        // Check that reading a unit after removing the service
 
967
        // fails nicely.
 
968
        err := s.mysql.Destroy()
 
969
        c.Assert(err, IsNil)
 
970
        err = s.mysql.Refresh()
 
971
        c.Assert(state.IsNotFound(err), Equals, true)
 
972
        _, err = s.State.Unit("mysql/0")
 
973
        c.Assert(err, ErrorMatches, `unit "mysql/0" not found`)
 
974
}
 
975
 
 
976
func (s *ServiceSuite) TestServiceConfig(c *C) {
 
977
        env, err := s.mysql.Config()
 
978
        c.Assert(err, IsNil)
 
979
        err = env.Read()
 
980
        c.Assert(err, IsNil)
 
981
        c.Assert(env.Map(), DeepEquals, map[string]interface{}{})
 
982
 
 
983
        env.Update(map[string]interface{}{"spam": "eggs", "eggs": "spam"})
 
984
        env.Update(map[string]interface{}{"spam": "spam", "chaos": "emeralds"})
 
985
        _, err = env.Write()
 
986
        c.Assert(err, IsNil)
 
987
 
 
988
        env, err = s.mysql.Config()
 
989
        c.Assert(err, IsNil)
 
990
        err = env.Read()
 
991
        c.Assert(err, IsNil)
 
992
        c.Assert(env.Map(), DeepEquals, map[string]interface{}{"spam": "spam", "eggs": "spam", "chaos": "emeralds"})
 
993
}
 
994
 
 
995
func uint64p(val uint64) *uint64 {
 
996
        return &val
 
997
}
 
998
 
 
999
func (s *ServiceSuite) TestConstraints(c *C) {
 
1000
        // Constraints are initially empty (for now).
 
1001
        cons0 := constraints.Value{}
 
1002
        cons1, err := s.mysql.Constraints()
 
1003
        c.Assert(err, IsNil)
 
1004
        c.Assert(cons1, DeepEquals, cons0)
 
1005
 
 
1006
        // Constraints can be set.
 
1007
        cons2 := constraints.Value{Mem: uint64p(4096)}
 
1008
        err = s.mysql.SetConstraints(cons2)
 
1009
        cons3, err := s.mysql.Constraints()
 
1010
        c.Assert(err, IsNil)
 
1011
        c.Assert(cons3, DeepEquals, cons2)
 
1012
 
 
1013
        // Constraints are completely overwritten when re-set.
 
1014
        cons4 := constraints.Value{CpuPower: uint64p(750)}
 
1015
        err = s.mysql.SetConstraints(cons4)
 
1016
        c.Assert(err, IsNil)
 
1017
        cons5, err := s.mysql.Constraints()
 
1018
        c.Assert(err, IsNil)
 
1019
        c.Assert(cons5, DeepEquals, cons4)
 
1020
 
 
1021
        // Destroy the existing service; there's no way to directly assert
 
1022
        // that the constraints are deleted...
 
1023
        err = s.mysql.Destroy()
 
1024
        c.Assert(err, IsNil)
 
1025
        err = s.mysql.Refresh()
 
1026
        c.Assert(state.IsNotFound(err), Equals, true)
 
1027
 
 
1028
        // ...but we can check that old constraints do not affect new services
 
1029
        // with matching names.
 
1030
        ch, _, err := s.mysql.Charm()
 
1031
        c.Assert(err, IsNil)
 
1032
        mysql, err := s.State.AddService(s.mysql.Name(), ch)
 
1033
        c.Assert(err, IsNil)
 
1034
        cons6, err := mysql.Constraints()
 
1035
        c.Assert(err, IsNil)
 
1036
        c.Assert(cons6, DeepEquals, cons0)
 
1037
}
 
1038
 
 
1039
func (s *ServiceSuite) TestConstraintsLifecycle(c *C) {
 
1040
        // Dying.
 
1041
        unit, err := s.mysql.AddUnit()
 
1042
        c.Assert(err, IsNil)
 
1043
        err = s.mysql.Destroy()
 
1044
        c.Assert(err, IsNil)
 
1045
        cons1 := constraints.MustParse("mem=1G")
 
1046
        err = s.mysql.SetConstraints(cons1)
 
1047
        c.Assert(err, ErrorMatches, `cannot set constraints: not found or not alive`)
 
1048
        scons, err := s.mysql.Constraints()
 
1049
        c.Assert(err, IsNil)
 
1050
        c.Assert(scons, DeepEquals, constraints.Value{})
 
1051
 
 
1052
        // Removed (== Dead, for a service).
 
1053
        err = unit.EnsureDead()
 
1054
        c.Assert(err, IsNil)
 
1055
        err = unit.Remove()
 
1056
        c.Assert(err, IsNil)
 
1057
        err = s.mysql.SetConstraints(cons1)
 
1058
        c.Assert(err, ErrorMatches, `cannot set constraints: not found or not alive`)
 
1059
        _, err = s.mysql.Constraints()
 
1060
        c.Assert(err, ErrorMatches, `constraints not found`)
 
1061
}
 
1062
 
 
1063
func (s *ServiceSuite) TestSubordinateConstraints(c *C) {
 
1064
        loggingCh := s.AddTestingCharm(c, "logging")
 
1065
        logging, err := s.State.AddService("logging", loggingCh)
 
1066
        c.Assert(err, IsNil)
 
1067
 
 
1068
        _, err = logging.Constraints()
 
1069
        c.Assert(err, Equals, state.ErrSubordinateConstraints)
 
1070
 
 
1071
        err = logging.SetConstraints(constraints.Value{})
 
1072
        c.Assert(err, Equals, state.ErrSubordinateConstraints)
 
1073
}
 
1074
 
 
1075
type unitSlice []*state.Unit
 
1076
 
 
1077
func (m unitSlice) Len() int           { return len(m) }
 
1078
func (m unitSlice) Swap(i, j int)      { m[i], m[j] = m[j], m[i] }
 
1079
func (m unitSlice) Less(i, j int) bool { return m[i].Name() < m[j].Name() }
 
1080
 
 
1081
var serviceUnitsWatchTests = []struct {
 
1082
        summary string
 
1083
        test    func(*C, *state.State, *state.Service)
 
1084
        changes []string
 
1085
}{
 
1086
        {
 
1087
                "Check initial empty event",
 
1088
                func(_ *C, _ *state.State, _ *state.Service) {},
 
1089
                []string(nil),
 
1090
        }, {
 
1091
                "Add a unit",
 
1092
                func(c *C, s *state.State, service *state.Service) {
 
1093
                        _, err := service.AddUnit()
 
1094
                        c.Assert(err, IsNil)
 
1095
                },
 
1096
                []string{"mysql/0"},
 
1097
        }, {
 
1098
                "Add a unit, ignore unrelated change",
 
1099
                func(c *C, s *state.State, service *state.Service) {
 
1100
                        _, err := service.AddUnit()
 
1101
                        c.Assert(err, IsNil)
 
1102
                        unit0, err := service.Unit("mysql/0")
 
1103
                        c.Assert(err, IsNil)
 
1104
                        err = unit0.SetPublicAddress("what.ever")
 
1105
                        c.Assert(err, IsNil)
 
1106
                },
 
1107
                []string{"mysql/1"},
 
1108
        }, {
 
1109
                "Add two units at once",
 
1110
                func(c *C, s *state.State, service *state.Service) {
 
1111
                        unit2, err := service.AddUnit()
 
1112
                        c.Assert(err, IsNil)
 
1113
                        preventUnitDestroyRemove(c, s, unit2)
 
1114
                        _, err = service.AddUnit()
 
1115
                        c.Assert(err, IsNil)
 
1116
                },
 
1117
                []string{"mysql/2", "mysql/3"},
 
1118
        }, {
 
1119
                "Report dying unit",
 
1120
                func(c *C, s *state.State, service *state.Service) {
 
1121
                        unit0, err := service.Unit("mysql/0")
 
1122
                        c.Assert(err, IsNil)
 
1123
                        preventUnitDestroyRemove(c, s, unit0)
 
1124
                        err = unit0.Destroy()
 
1125
                        c.Assert(err, IsNil)
 
1126
                },
 
1127
                []string{"mysql/0"},
 
1128
        }, {
 
1129
                // I'm preserving these tests in amber, not fixing them.
 
1130
                "Report another dying unit for no clear reason",
 
1131
                func(c *C, s *state.State, service *state.Service) {
 
1132
                        unit2, err := service.Unit("mysql/2")
 
1133
                        c.Assert(err, IsNil)
 
1134
                        err = unit2.Destroy()
 
1135
                        c.Assert(err, IsNil)
 
1136
                },
 
1137
                []string{"mysql/2"},
 
1138
        }, {
 
1139
                "Report multiple dead or dying units",
 
1140
                func(c *C, s *state.State, service *state.Service) {
 
1141
                        unit0, err := service.Unit("mysql/0")
 
1142
                        c.Assert(err, IsNil)
 
1143
                        err = unit0.EnsureDead()
 
1144
                        c.Assert(err, IsNil)
 
1145
                        unit1, err := service.Unit("mysql/1")
 
1146
                        c.Assert(err, IsNil)
 
1147
                        err = unit1.EnsureDead()
 
1148
                        c.Assert(err, IsNil)
 
1149
                },
 
1150
                []string{"mysql/0", "mysql/1"},
 
1151
        }, {
 
1152
                "Report dying unit along with a new, alive unit",
 
1153
                func(c *C, s *state.State, service *state.Service) {
 
1154
                        unit3, err := service.Unit("mysql/3")
 
1155
                        c.Assert(err, IsNil)
 
1156
                        preventUnitDestroyRemove(c, s, unit3)
 
1157
                        err = unit3.Destroy()
 
1158
                        c.Assert(err, IsNil)
 
1159
                        _, err = service.AddUnit()
 
1160
                        c.Assert(err, IsNil)
 
1161
                },
 
1162
                []string{"mysql/3", "mysql/4"},
 
1163
        }, {
 
1164
                "Report multiple dead units and multiple new, alive, units",
 
1165
                func(c *C, s *state.State, service *state.Service) {
 
1166
                        unit3, err := service.Unit("mysql/3")
 
1167
                        c.Assert(err, IsNil)
 
1168
                        err = unit3.EnsureDead()
 
1169
                        c.Assert(err, IsNil)
 
1170
                        unit4, err := service.Unit("mysql/4")
 
1171
                        c.Assert(err, IsNil)
 
1172
                        err = unit4.EnsureDead()
 
1173
                        c.Assert(err, IsNil)
 
1174
                        _, err = service.AddUnit()
 
1175
                        c.Assert(err, IsNil)
 
1176
                        _, err = service.AddUnit()
 
1177
                        c.Assert(err, IsNil)
 
1178
                },
 
1179
                []string{"mysql/3", "mysql/4", "mysql/5", "mysql/6"},
 
1180
        }, {
 
1181
                "Add many, and remove many at once",
 
1182
                func(c *C, s *state.State, service *state.Service) {
 
1183
                        units := [20]*state.Unit{}
 
1184
                        var err error
 
1185
                        for i := 0; i < len(units); i++ {
 
1186
                                units[i], err = service.AddUnit()
 
1187
                                c.Assert(err, IsNil)
 
1188
                        }
 
1189
                        for i := 10; i < len(units); i++ {
 
1190
                                err = units[i].Destroy()
 
1191
                                c.Assert(err, IsNil)
 
1192
                        }
 
1193
                },
 
1194
                []string{"mysql/10", "mysql/11", "mysql/12", "mysql/13", "mysql/14", "mysql/15", "mysql/16", "mysql/7", "mysql/8", "mysql/9"},
 
1195
        }, {
 
1196
                "Change many at once",
 
1197
                func(c *C, s *state.State, service *state.Service) {
 
1198
                        units := [10]*state.Unit{}
 
1199
                        var err error
 
1200
                        for i := 0; i < len(units); i++ {
 
1201
                                units[i], err = service.Unit("mysql/" + fmt.Sprint(i+7))
 
1202
                                c.Assert(err, IsNil)
 
1203
                        }
 
1204
                        for _, unit := range units {
 
1205
                                preventUnitDestroyRemove(c, s, unit)
 
1206
                                err = unit.Destroy()
 
1207
                                c.Assert(err, IsNil)
 
1208
                        }
 
1209
                        err = units[8].EnsureDead()
 
1210
                        c.Assert(err, IsNil)
 
1211
                        err = units[9].EnsureDead()
 
1212
                        c.Assert(err, IsNil)
 
1213
                },
 
1214
                []string{"mysql/10", "mysql/11", "mysql/12", "mysql/13", "mysql/14", "mysql/15", "mysql/16", "mysql/7", "mysql/8", "mysql/9"},
 
1215
        }, {
 
1216
                "Report dead when first seen and also add a new unit",
 
1217
                func(c *C, s *state.State, service *state.Service) {
 
1218
                        unit, err := service.AddUnit()
 
1219
                        c.Assert(err, IsNil)
 
1220
                        err = unit.EnsureDead()
 
1221
                        c.Assert(err, IsNil)
 
1222
                        _, err = service.AddUnit()
 
1223
                        c.Assert(err, IsNil)
 
1224
                },
 
1225
                []string{"mysql/27", "mysql/28"},
 
1226
        }, {
 
1227
                "report only units assigned to this machine",
 
1228
                func(c *C, s *state.State, service *state.Service) {
 
1229
                        _, err := service.AddUnit()
 
1230
                        c.Assert(err, IsNil)
 
1231
                        _, err = service.AddUnit()
 
1232
                        c.Assert(err, IsNil)
 
1233
                        ch, _, err := service.Charm()
 
1234
                        c.Assert(err, IsNil)
 
1235
                        svc, err := s.AddService("bacon", ch)
 
1236
                        c.Assert(err, IsNil)
 
1237
                        _, err = svc.AddUnit()
 
1238
                        c.Assert(err, IsNil)
 
1239
                        _, err = svc.AddUnit()
 
1240
                        c.Assert(err, IsNil)
 
1241
                        unit10, err := service.Unit("mysql/10")
 
1242
                        c.Assert(err, IsNil)
 
1243
                        err = unit10.EnsureDead()
 
1244
                        c.Assert(err, IsNil)
 
1245
                        err = unit10.Remove()
 
1246
                        c.Assert(err, IsNil)
 
1247
                },
 
1248
                []string{"mysql/10", "mysql/29", "mysql/30"},
 
1249
        }, {
 
1250
                "Report previously known machines that are removed",
 
1251
                func(c *C, s *state.State, service *state.Service) {
 
1252
                        unit30, err := service.Unit("mysql/30")
 
1253
                        c.Assert(err, IsNil)
 
1254
                        err = unit30.EnsureDead()
 
1255
                        c.Assert(err, IsNil)
 
1256
                        err = unit30.Remove()
 
1257
                        c.Assert(err, IsNil)
 
1258
                },
 
1259
                []string{"mysql/30"},
 
1260
        },
 
1261
}
 
1262
 
 
1263
func (s *ServiceSuite) TestWatchUnits(c *C) {
 
1264
        unitWatcher := s.mysql.WatchUnits()
 
1265
        defer func() {
 
1266
                c.Assert(unitWatcher.Stop(), IsNil)
 
1267
        }()
 
1268
        all := []string{}
 
1269
        for i, test := range serviceUnitsWatchTests {
 
1270
                c.Logf("test %d: %s", i, test.summary)
 
1271
                test.test(c, s.State, s.mysql)
 
1272
                s.State.StartSync()
 
1273
                all = append(all, test.changes...)
 
1274
                var got []string
 
1275
                want := append([]string(nil), test.changes...)
 
1276
                sort.Strings(want)
 
1277
                for {
 
1278
                        select {
 
1279
                        case new, ok := <-unitWatcher.Changes():
 
1280
                                c.Assert(ok, Equals, true)
 
1281
                                got = append(got, new...)
 
1282
                                if len(got) < len(want) {
 
1283
                                        continue
 
1284
                                }
 
1285
                                sort.Strings(got)
 
1286
                                c.Assert(got, DeepEquals, want)
 
1287
                        case <-time.After(500 * time.Millisecond):
 
1288
                                c.Fatalf("did not get change, want: %#v", want)
 
1289
                        }
 
1290
                        break
 
1291
                }
 
1292
        }
 
1293
 
 
1294
        // Check that removing units for which we already got a Dead event
 
1295
        // does not yield any more events.
 
1296
        for _, uname := range all {
 
1297
                unit, err := s.State.Unit(uname)
 
1298
                if state.IsNotFound(err) || unit.Life() != state.Dead {
 
1299
                        continue
 
1300
                }
 
1301
                c.Assert(err, IsNil)
 
1302
                err = unit.Remove()
 
1303
                c.Assert(err, IsNil)
 
1304
        }
 
1305
        s.State.StartSync()
 
1306
        select {
 
1307
        case got := <-unitWatcher.Changes():
 
1308
                c.Fatalf("got unexpected change: %#v", got)
 
1309
        case <-time.After(100 * time.Millisecond):
 
1310
        }
 
1311
 
 
1312
        // Stop the watcher and restart it, check that it returns non-nil
 
1313
        // initial event.
 
1314
        c.Assert(unitWatcher.Stop(), IsNil)
 
1315
        unitWatcher = s.mysql.WatchUnits()
 
1316
        s.State.StartSync()
 
1317
        want := []string{"mysql/11", "mysql/12", "mysql/13", "mysql/14", "mysql/2", "mysql/28", "mysql/29", "mysql/5", "mysql/6", "mysql/7", "mysql/8", "mysql/9"}
 
1318
        select {
 
1319
        case got, ok := <-unitWatcher.Changes():
 
1320
                c.Assert(ok, Equals, true)
 
1321
                sort.Strings(got)
 
1322
                c.Assert(got, DeepEquals, want)
 
1323
        case <-time.After(500 * time.Millisecond):
 
1324
                c.Fatalf("did not get change, want: %#v", want)
 
1325
        }
 
1326
 
 
1327
        // ignore unrelated change for non-alive unit.
 
1328
        unit2, err := s.mysql.Unit("mysql/2")
 
1329
        c.Assert(err, IsNil)
 
1330
        err = unit2.SetPublicAddress("what.ever")
 
1331
        c.Assert(err, IsNil)
 
1332
        s.State.StartSync()
 
1333
        select {
 
1334
        case got := <-unitWatcher.Changes():
 
1335
                c.Fatalf("got unexpected change: %#v", got)
 
1336
        case <-time.After(100 * time.Millisecond):
 
1337
        }
 
1338
}
 
1339
 
 
1340
func (s *ServiceSuite) TestWatchRelations(c *C) {
 
1341
        w := s.mysql.WatchRelations()
 
1342
        defer func() { c.Assert(w.Stop(), IsNil) }()
 
1343
 
 
1344
        assertNoChange := func() {
 
1345
                s.State.StartSync()
 
1346
                select {
 
1347
                case got := <-w.Changes():
 
1348
                        c.Fatalf("expected nothing, got %#v", got)
 
1349
                case <-time.After(100 * time.Millisecond):
 
1350
                }
 
1351
        }
 
1352
        assertChange := func(want ...int) {
 
1353
                s.State.Sync()
 
1354
                select {
 
1355
                case got, ok := <-w.Changes():
 
1356
                        c.Assert(ok, Equals, true)
 
1357
                        if len(want) == 0 {
 
1358
                                c.Assert(got, HasLen, 0)
 
1359
                        } else {
 
1360
                                sort.Ints(got)
 
1361
                                sort.Ints(want)
 
1362
                                c.Assert(got, DeepEquals, want)
 
1363
                        }
 
1364
                case <-time.After(500 * time.Millisecond):
 
1365
                        c.Fatalf("expected %#v, got nothing", want)
 
1366
                }
 
1367
                assertNoChange()
 
1368
        }
 
1369
 
 
1370
        // Check initial event, and lack of followup.
 
1371
        assertChange()
 
1372
 
 
1373
        // Add a relation; check change.
 
1374
        mysqlep, err := s.mysql.Endpoint("server")
 
1375
        c.Assert(err, IsNil)
 
1376
        wpch := s.AddTestingCharm(c, "wordpress")
 
1377
        wpi := 0
 
1378
        addRelation := func() *state.Relation {
 
1379
                name := fmt.Sprintf("wp%d", wpi)
 
1380
                wpi++
 
1381
                wp, err := s.State.AddService(name, wpch)
 
1382
                c.Assert(err, IsNil)
 
1383
                wpep, err := wp.Endpoint("db")
 
1384
                c.Assert(err, IsNil)
 
1385
                rel, err := s.State.AddRelation(mysqlep, wpep)
 
1386
                c.Assert(err, IsNil)
 
1387
                return rel
 
1388
        }
 
1389
        rel0 := addRelation()
 
1390
        assertChange(0)
 
1391
 
 
1392
        // Add another relation; check change.
 
1393
        addRelation()
 
1394
        assertChange(1)
 
1395
 
 
1396
        // Destroy a relation; check change.
 
1397
        err = rel0.Destroy()
 
1398
        c.Assert(err, IsNil)
 
1399
        assertChange(0)
 
1400
 
 
1401
        // Stop watcher; check change chan is closed.
 
1402
        err = w.Stop()
 
1403
        c.Assert(err, IsNil)
 
1404
        assertClosed := func() {
 
1405
                select {
 
1406
                case _, ok := <-w.Changes():
 
1407
                        c.Assert(ok, Equals, false)
 
1408
                default:
 
1409
                        c.Fatalf("Changes not closed")
 
1410
                }
 
1411
        }
 
1412
        assertClosed()
 
1413
 
 
1414
        // Add a new relation; start a new watcher; check initial event.
 
1415
        rel2 := addRelation()
 
1416
        w = s.mysql.WatchRelations()
 
1417
        assertChange(1, 2)
 
1418
 
 
1419
        // Add a unit to the new relation; check no change.
 
1420
        unit, err := s.mysql.AddUnit()
 
1421
        c.Assert(err, IsNil)
 
1422
        ru2, err := rel2.Unit(unit)
 
1423
        c.Assert(err, IsNil)
 
1424
        err = ru2.EnterScope(nil)
 
1425
        c.Assert(err, IsNil)
 
1426
        assertNoChange()
 
1427
 
 
1428
        // Destroy the relation with the unit in scope, and add another; check
 
1429
        // changes.
 
1430
        err = rel2.Destroy()
 
1431
        c.Assert(err, IsNil)
 
1432
        addRelation()
 
1433
        assertChange(2, 3)
 
1434
 
 
1435
        // Leave scope, destroying the relation, and check that change as well.
 
1436
        err = ru2.LeaveScope()
 
1437
        c.Assert(err, IsNil)
 
1438
        assertChange(2)
 
1439
}
 
1440
 
 
1441
func removeAllUnits(c *C, s *state.Service) {
 
1442
        us, err := s.AllUnits()
 
1443
        c.Assert(err, IsNil)
 
1444
        for _, u := range us {
 
1445
                err = u.EnsureDead()
 
1446
                c.Assert(err, IsNil)
 
1447
                err = u.Remove()
 
1448
                c.Assert(err, IsNil)
 
1449
        }
 
1450
}
 
1451
 
 
1452
var watchServiceTests = []struct {
 
1453
        test    func(m *state.Service) error
 
1454
        Exposed bool
 
1455
        Life    state.Life
 
1456
}{
 
1457
        {
 
1458
                test: func(s *state.Service) error {
 
1459
                        return s.SetExposed()
 
1460
                },
 
1461
                Exposed: true,
 
1462
        }, {
 
1463
                test: func(s *state.Service) error {
 
1464
                        return s.ClearExposed()
 
1465
                },
 
1466
                Exposed: false,
 
1467
        }, {
 
1468
                test: func(s *state.Service) error {
 
1469
                        if _, err := s.AddUnit(); err != nil {
 
1470
                                return err
 
1471
                        }
 
1472
                        return s.Destroy()
 
1473
                },
 
1474
                Life: state.Dying,
 
1475
        },
 
1476
}
 
1477
 
 
1478
func (s *ServiceSuite) TestWatchService(c *C) {
 
1479
        altservice, err := s.State.Service(s.mysql.Name())
 
1480
        c.Assert(err, IsNil)
 
1481
        err = altservice.SetCharm(s.charm, true)
 
1482
        c.Assert(err, IsNil)
 
1483
        _, force, err := s.mysql.Charm()
 
1484
        c.Assert(err, IsNil)
 
1485
        c.Assert(force, Equals, false)
 
1486
 
 
1487
        w := s.mysql.Watch()
 
1488
        defer func() {
 
1489
                c.Assert(w.Stop(), IsNil)
 
1490
        }()
 
1491
        s.State.Sync()
 
1492
        select {
 
1493
        case _, ok := <-w.Changes():
 
1494
                c.Assert(ok, Equals, true)
 
1495
                err := s.mysql.Refresh()
 
1496
                c.Assert(err, IsNil)
 
1497
                _, force, err := s.mysql.Charm()
 
1498
                c.Assert(err, IsNil)
 
1499
                c.Assert(force, Equals, true)
 
1500
        case <-time.After(500 * time.Millisecond):
 
1501
                c.Fatalf("did not get change: %v", s.mysql)
 
1502
        }
 
1503
 
 
1504
        for i, test := range watchServiceTests {
 
1505
                c.Logf("test %d", i)
 
1506
                err := test.test(altservice)
 
1507
                c.Assert(err, IsNil)
 
1508
                s.State.StartSync()
 
1509
                select {
 
1510
                case _, ok := <-w.Changes():
 
1511
                        c.Assert(ok, Equals, true)
 
1512
                        err := s.mysql.Refresh()
 
1513
                        c.Assert(err, IsNil)
 
1514
                        c.Assert(s.mysql.Life(), Equals, test.Life)
 
1515
                        c.Assert(s.mysql.IsExposed(), Equals, test.Exposed)
 
1516
                case <-time.After(500 * time.Millisecond):
 
1517
                        c.Fatalf("did not get change: %v %v", test.Exposed, test.Life)
 
1518
                }
 
1519
        }
 
1520
        s.State.StartSync()
 
1521
        select {
 
1522
        case got, ok := <-w.Changes():
 
1523
                c.Fatalf("got unexpected change: %#v, %v", got, ok)
 
1524
        case <-time.After(100 * time.Millisecond):
 
1525
        }
 
1526
}
 
1527
 
 
1528
func (s *ServiceSuite) TestWatchServiceConfig(c *C) {
 
1529
        config, err := s.mysql.Config()
 
1530
        c.Assert(err, IsNil)
 
1531
        c.Assert(config.Keys(), HasLen, 0)
 
1532
 
 
1533
        u, err := s.mysql.AddUnit()
 
1534
        c.Assert(err, IsNil)
 
1535
 
 
1536
        _, err = u.WatchServiceConfig()
 
1537
        c.Assert(err, ErrorMatches, "unit charm not set")
 
1538
 
 
1539
        err = u.SetCharmURL(s.charm.URL())
 
1540
        c.Assert(err, IsNil)
 
1541
 
 
1542
        configWatcher, err := u.WatchServiceConfig()
 
1543
        c.Assert(err, IsNil)
 
1544
        defer func() {
 
1545
                c.Assert(configWatcher.Stop(), IsNil)
 
1546
        }()
 
1547
 
 
1548
        s.State.StartSync()
 
1549
        select {
 
1550
        case got, ok := <-configWatcher.Changes():
 
1551
                c.Assert(ok, Equals, true)
 
1552
                c.Assert(got.Map(), DeepEquals, map[string]interface{}{})
 
1553
        case <-time.After(500 * time.Millisecond):
 
1554
                c.Fatalf("did not get change")
 
1555
        }
 
1556
 
 
1557
        // Two change events.
 
1558
        config.Set("foo", "bar")
 
1559
        config.Set("baz", "yadda")
 
1560
        changes, err := config.Write()
 
1561
        c.Assert(err, IsNil)
 
1562
        c.Assert(changes, DeepEquals, []state.ItemChange{{
 
1563
                Key:      "baz",
 
1564
                Type:     state.ItemAdded,
 
1565
                NewValue: "yadda",
 
1566
        }, {
 
1567
                Key:      "foo",
 
1568
                Type:     state.ItemAdded,
 
1569
                NewValue: "bar",
 
1570
        }})
 
1571
 
 
1572
        s.State.StartSync()
 
1573
        select {
 
1574
        case got, ok := <-configWatcher.Changes():
 
1575
                c.Assert(ok, Equals, true)
 
1576
                c.Assert(got.Map(), DeepEquals, map[string]interface{}{"baz": "yadda", "foo": "bar"})
 
1577
        case <-time.After(500 * time.Millisecond):
 
1578
                c.Fatalf("did not get change")
 
1579
        }
 
1580
 
 
1581
        config.Delete("foo")
 
1582
        changes, err = config.Write()
 
1583
        c.Assert(err, IsNil)
 
1584
        c.Assert(changes, DeepEquals, []state.ItemChange{{
 
1585
                Key:      "foo",
 
1586
                Type:     state.ItemDeleted,
 
1587
                OldValue: "bar",
 
1588
        }})
 
1589
 
 
1590
        s.State.StartSync()
 
1591
        select {
 
1592
        case got, ok := <-configWatcher.Changes():
 
1593
                c.Assert(ok, Equals, true)
 
1594
                c.Assert(got.Map(), DeepEquals, map[string]interface{}{"baz": "yadda"})
 
1595
        case <-time.After(500 * time.Millisecond):
 
1596
                c.Fatalf("did not get change")
 
1597
        }
 
1598
 
 
1599
        select {
 
1600
        case got := <-configWatcher.Changes():
 
1601
                c.Fatalf("got unexpected change: %#v", got)
 
1602
        case <-time.After(100 * time.Millisecond):
 
1603
        }
 
1604
}
 
1605
 
 
1606
func (s *ServiceSuite) TestAnnotatorForService(c *C) {
 
1607
        testAnnotator(c, func() (state.Annotator, error) {
 
1608
                return s.State.Service("mysql")
 
1609
        })
 
1610
}
 
1611
 
 
1612
func (s *ServiceSuite) TestAnnotationRemovalForService(c *C) {
 
1613
        annotations := map[string]string{"mykey": "myvalue"}
 
1614
        err := s.mysql.SetAnnotations(annotations)
 
1615
        c.Assert(err, IsNil)
 
1616
        err = s.mysql.Destroy()
 
1617
        c.Assert(err, IsNil)
 
1618
        ann, err := s.mysql.Annotations()
 
1619
        c.Assert(err, IsNil)
 
1620
        c.Assert(ann, DeepEquals, make(map[string]string))
 
1621
}