1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
10
"github.com/juju/errors"
11
jc "github.com/juju/testing/checkers"
12
"github.com/juju/utils/set"
13
gc "gopkg.in/check.v1"
14
"gopkg.in/juju/charm.v6-unstable"
15
"gopkg.in/juju/charmrepo.v2-unstable"
17
"github.com/juju/juju/constraints"
18
"github.com/juju/juju/instance"
19
"github.com/juju/juju/juju"
20
"github.com/juju/juju/juju/testing"
21
"github.com/juju/juju/state"
22
"github.com/juju/juju/testcharms"
23
coretesting "github.com/juju/juju/testing"
26
func Test(t *stdtesting.T) {
27
coretesting.MgoTestPackage(t)
30
// DeployLocalSuite uses a fresh copy of the same local dummy charm for each
31
// test, because DeployApplication demands that a charm already exists in state,
32
// and that's is the simplest way to get one in there.
33
type DeployLocalSuite struct {
35
repo charmrepo.Interface
40
var _ = gc.Suite(&DeployLocalSuite{})
42
func (s *DeployLocalSuite) SetUpSuite(c *gc.C) {
43
s.JujuConnSuite.SetUpSuite(c)
44
s.repo = &charmrepo.LocalRepository{Path: testcharms.Repo.Path()}
45
s.PatchValue(&charmrepo.CacheDir, c.MkDir())
48
func (s *DeployLocalSuite) SetUpTest(c *gc.C) {
49
s.JujuConnSuite.SetUpTest(c)
50
curl := charm.MustParseURL("local:quantal/dummy")
51
charm, err := testing.PutCharm(s.State, curl, s.repo, false)
52
c.Assert(err, jc.ErrorIsNil)
56
func (s *DeployLocalSuite) TestDeployMinimal(c *gc.C) {
57
service, err := juju.DeployApplication(s.State,
58
juju.DeployApplicationParams{
59
ApplicationName: "bob",
62
c.Assert(err, jc.ErrorIsNil)
63
s.assertCharm(c, service, s.charm.URL())
64
s.assertSettings(c, service, charm.Settings{})
65
s.assertConstraints(c, service, constraints.Value{})
66
s.assertMachines(c, service, constraints.Value{})
69
func (s *DeployLocalSuite) TestDeploySeries(c *gc.C) {
70
f := &fakeDeployer{State: s.State}
72
_, err := juju.DeployApplication(f,
73
juju.DeployApplicationParams{
74
ApplicationName: "bob",
78
c.Assert(err, jc.ErrorIsNil)
80
c.Assert(f.args.Name, gc.Equals, "bob")
81
c.Assert(f.args.Charm, gc.DeepEquals, s.charm)
82
c.Assert(f.args.Series, gc.Equals, "aseries")
85
func (s *DeployLocalSuite) TestDeployWithImplicitBindings(c *gc.C) {
86
wordpressCharm := s.addWordpressCharmWithExtraBindings(c)
88
service, err := juju.DeployApplication(s.State,
89
juju.DeployApplicationParams{
90
ApplicationName: "bob",
91
Charm: wordpressCharm,
92
EndpointBindings: nil,
94
c.Assert(err, jc.ErrorIsNil)
96
s.assertBindings(c, service, map[string]string{
100
"monitoring-port": "",
104
// extra-bindings names
111
func (s *DeployLocalSuite) addWordpressCharm(c *gc.C) *state.Charm {
112
wordpressCharmURL := charm.MustParseURL("local:quantal/wordpress")
113
return s.addWordpressCharmFromURL(c, wordpressCharmURL)
116
func (s *DeployLocalSuite) addWordpressCharmWithExtraBindings(c *gc.C) *state.Charm {
117
wordpressCharmURL := charm.MustParseURL("local:quantal/wordpress-extra-bindings")
118
return s.addWordpressCharmFromURL(c, wordpressCharmURL)
121
func (s *DeployLocalSuite) addWordpressCharmFromURL(c *gc.C, charmURL *charm.URL) *state.Charm {
122
wordpressCharm, err := testing.PutCharm(s.State, charmURL, s.repo, false)
123
c.Assert(err, jc.ErrorIsNil)
124
return wordpressCharm
127
func (s *DeployLocalSuite) assertBindings(c *gc.C, service *state.Application, expected map[string]string) {
128
bindings, err := service.EndpointBindings()
129
c.Assert(err, jc.ErrorIsNil)
130
c.Assert(bindings, jc.DeepEquals, expected)
133
func (s *DeployLocalSuite) TestDeployWithSomeSpecifiedBindings(c *gc.C) {
134
wordpressCharm := s.addWordpressCharm(c)
135
_, err := s.State.AddSpace("db", "", nil, false)
136
c.Assert(err, jc.ErrorIsNil)
137
_, err = s.State.AddSpace("public", "", nil, false)
138
c.Assert(err, jc.ErrorIsNil)
140
service, err := juju.DeployApplication(s.State,
141
juju.DeployApplicationParams{
142
ApplicationName: "bob",
143
Charm: wordpressCharm,
144
EndpointBindings: map[string]string{
149
c.Assert(err, jc.ErrorIsNil)
151
s.assertBindings(c, service, map[string]string{
154
"logging-dir": "public",
155
"monitoring-port": "public",
158
// extra-bindings names
159
"db-client": "public",
160
"admin-api": "public",
165
func (s *DeployLocalSuite) TestDeployWithBoundRelationNamesAndExtraBindingsNames(c *gc.C) {
166
wordpressCharm := s.addWordpressCharmWithExtraBindings(c)
167
_, err := s.State.AddSpace("db", "", nil, false)
168
c.Assert(err, jc.ErrorIsNil)
169
_, err = s.State.AddSpace("public", "", nil, false)
170
c.Assert(err, jc.ErrorIsNil)
171
_, err = s.State.AddSpace("internal", "", nil, false)
172
c.Assert(err, jc.ErrorIsNil)
174
service, err := juju.DeployApplication(s.State,
175
juju.DeployApplicationParams{
176
ApplicationName: "bob",
177
Charm: wordpressCharm,
178
EndpointBindings: map[string]string{
182
"admin-api": "internal",
185
c.Assert(err, jc.ErrorIsNil)
187
s.assertBindings(c, service, map[string]string{
189
"logging-dir": "public",
190
"monitoring-port": "public",
194
"admin-api": "internal",
196
"foo-bar": "public", // like for relations, uses the application-default.
200
func (s *DeployLocalSuite) TestDeployResources(c *gc.C) {
201
f := &fakeDeployer{State: s.State}
203
_, err := juju.DeployApplication(f,
204
juju.DeployApplicationParams{
205
ApplicationName: "bob",
207
EndpointBindings: map[string]string{
211
Resources: map[string]string{"foo": "bar"},
213
c.Assert(err, jc.ErrorIsNil)
215
c.Assert(f.args.Name, gc.Equals, "bob")
216
c.Assert(f.args.Charm, gc.DeepEquals, s.charm)
217
c.Assert(f.args.Resources, gc.DeepEquals, map[string]string{"foo": "bar"})
220
func (s *DeployLocalSuite) TestDeploySettings(c *gc.C) {
221
service, err := juju.DeployApplication(s.State,
222
juju.DeployApplicationParams{
223
ApplicationName: "bob",
225
ConfigSettings: charm.Settings{
226
"title": "banana cupcakes",
230
c.Assert(err, jc.ErrorIsNil)
231
s.assertSettings(c, service, charm.Settings{
232
"title": "banana cupcakes",
233
"skill-level": int64(9901),
237
func (s *DeployLocalSuite) TestDeploySettingsError(c *gc.C) {
238
_, err := juju.DeployApplication(s.State,
239
juju.DeployApplicationParams{
240
ApplicationName: "bob",
242
ConfigSettings: charm.Settings{
243
"skill-level": 99.01,
246
c.Assert(err, gc.ErrorMatches, `option "skill-level" expected int, got 99.01`)
247
_, err = s.State.Application("bob")
248
c.Assert(err, jc.Satisfies, errors.IsNotFound)
251
func (s *DeployLocalSuite) TestDeployConstraints(c *gc.C) {
252
err := s.State.SetModelConstraints(constraints.MustParse("mem=2G"))
253
c.Assert(err, jc.ErrorIsNil)
254
serviceCons := constraints.MustParse("cpu-cores=2")
255
service, err := juju.DeployApplication(s.State,
256
juju.DeployApplicationParams{
257
ApplicationName: "bob",
259
Constraints: serviceCons,
261
c.Assert(err, jc.ErrorIsNil)
262
s.assertConstraints(c, service, serviceCons)
265
func (s *DeployLocalSuite) TestDeployNumUnits(c *gc.C) {
266
f := &fakeDeployer{State: s.State}
268
serviceCons := constraints.MustParse("cpu-cores=2")
269
_, err := juju.DeployApplication(f,
270
juju.DeployApplicationParams{
271
ApplicationName: "bob",
273
Constraints: serviceCons,
276
c.Assert(err, jc.ErrorIsNil)
278
c.Assert(f.args.Name, gc.Equals, "bob")
279
c.Assert(f.args.Charm, gc.DeepEquals, s.charm)
280
c.Assert(f.args.Constraints, gc.DeepEquals, serviceCons)
281
c.Assert(f.args.NumUnits, gc.Equals, 2)
284
func (s *DeployLocalSuite) TestDeployForceMachineId(c *gc.C) {
285
f := &fakeDeployer{State: s.State}
287
serviceCons := constraints.MustParse("cpu-cores=2")
288
_, err := juju.DeployApplication(f,
289
juju.DeployApplicationParams{
290
ApplicationName: "bob",
292
Constraints: serviceCons,
294
Placement: []*instance.Placement{instance.MustParsePlacement("0")},
296
c.Assert(err, jc.ErrorIsNil)
298
c.Assert(f.args.Name, gc.Equals, "bob")
299
c.Assert(f.args.Charm, gc.DeepEquals, s.charm)
300
c.Assert(f.args.Constraints, gc.DeepEquals, serviceCons)
301
c.Assert(f.args.NumUnits, gc.Equals, 1)
302
c.Assert(f.args.Placement, gc.HasLen, 1)
303
c.Assert(*f.args.Placement[0], gc.Equals, instance.Placement{Scope: instance.MachineScope, Directive: "0"})
306
func (s *DeployLocalSuite) TestDeployForceMachineIdWithContainer(c *gc.C) {
307
f := &fakeDeployer{State: s.State}
309
serviceCons := constraints.MustParse("cpu-cores=2")
310
_, err := juju.DeployApplication(f,
311
juju.DeployApplicationParams{
312
ApplicationName: "bob",
314
Constraints: serviceCons,
316
Placement: []*instance.Placement{instance.MustParsePlacement(fmt.Sprintf("%s:0", instance.LXD))},
318
c.Assert(err, jc.ErrorIsNil)
319
c.Assert(f.args.Name, gc.Equals, "bob")
320
c.Assert(f.args.Charm, gc.DeepEquals, s.charm)
321
c.Assert(f.args.Constraints, gc.DeepEquals, serviceCons)
322
c.Assert(f.args.NumUnits, gc.Equals, 1)
323
c.Assert(f.args.Placement, gc.HasLen, 1)
324
c.Assert(*f.args.Placement[0], gc.Equals, instance.Placement{Scope: string(instance.LXD), Directive: "0"})
327
func (s *DeployLocalSuite) TestDeploy(c *gc.C) {
328
f := &fakeDeployer{State: s.State}
330
serviceCons := constraints.MustParse("cpu-cores=2")
331
placement := []*instance.Placement{
332
{Scope: s.State.ModelUUID(), Directive: "valid"},
333
{Scope: "#", Directive: "0"},
334
{Scope: "lxd", Directive: "1"},
335
{Scope: "lxd", Directive: ""},
337
_, err := juju.DeployApplication(f,
338
juju.DeployApplicationParams{
339
ApplicationName: "bob",
341
Constraints: serviceCons,
343
Placement: placement,
345
c.Assert(err, jc.ErrorIsNil)
347
c.Assert(f.args.Name, gc.Equals, "bob")
348
c.Assert(f.args.Charm, gc.DeepEquals, s.charm)
349
c.Assert(f.args.Constraints, gc.DeepEquals, serviceCons)
350
c.Assert(f.args.NumUnits, gc.Equals, 4)
351
c.Assert(f.args.Placement, gc.DeepEquals, placement)
354
func (s *DeployLocalSuite) TestDeployWithFewerPlacement(c *gc.C) {
355
f := &fakeDeployer{State: s.State}
356
serviceCons := constraints.MustParse("cpu-cores=2")
357
placement := []*instance.Placement{{Scope: s.State.ModelUUID(), Directive: "valid"}}
358
_, err := juju.DeployApplication(f,
359
juju.DeployApplicationParams{
360
ApplicationName: "bob",
362
Constraints: serviceCons,
364
Placement: placement,
366
c.Assert(err, jc.ErrorIsNil)
367
c.Assert(f.args.Name, gc.Equals, "bob")
368
c.Assert(f.args.Charm, gc.DeepEquals, s.charm)
369
c.Assert(f.args.Constraints, gc.DeepEquals, serviceCons)
370
c.Assert(f.args.NumUnits, gc.Equals, 3)
371
c.Assert(f.args.Placement, gc.DeepEquals, placement)
374
func (s *DeployLocalSuite) assertAssignedUnit(c *gc.C, u *state.Unit, mId string, cons constraints.Value) {
375
id, err := u.AssignedMachineId()
376
c.Assert(err, jc.ErrorIsNil)
377
machine, err := s.State.Machine(id)
378
c.Assert(err, jc.ErrorIsNil)
379
machineCons, err := machine.Constraints()
380
c.Assert(err, jc.ErrorIsNil)
381
c.Assert(machineCons, gc.DeepEquals, cons)
384
func (s *DeployLocalSuite) assertCharm(c *gc.C, service *state.Application, expect *charm.URL) {
385
curl, force := service.CharmURL()
386
c.Assert(curl, gc.DeepEquals, expect)
387
c.Assert(force, jc.IsFalse)
390
func (s *DeployLocalSuite) assertSettings(c *gc.C, service *state.Application, expect charm.Settings) {
391
settings, err := service.ConfigSettings()
392
c.Assert(err, jc.ErrorIsNil)
393
c.Assert(settings, gc.DeepEquals, expect)
396
func (s *DeployLocalSuite) assertConstraints(c *gc.C, service *state.Application, expect constraints.Value) {
397
cons, err := service.Constraints()
398
c.Assert(err, jc.ErrorIsNil)
399
c.Assert(cons, gc.DeepEquals, expect)
402
func (s *DeployLocalSuite) assertMachines(c *gc.C, service *state.Application, expectCons constraints.Value, expectIds ...string) {
403
units, err := service.AllUnits()
404
c.Assert(err, jc.ErrorIsNil)
405
c.Assert(units, gc.HasLen, len(expectIds))
406
// first manually tell state to assign all the units
407
for _, unit := range units {
408
id := unit.Tag().Id()
409
res, err := s.State.AssignStagedUnits([]string{id})
410
c.Assert(err, jc.ErrorIsNil)
411
c.Assert(res[0].Error, jc.ErrorIsNil)
412
c.Assert(res[0].Unit, gc.Equals, id)
415
// refresh the list of units from state
416
units, err = service.AllUnits()
417
c.Assert(err, jc.ErrorIsNil)
418
c.Assert(units, gc.HasLen, len(expectIds))
419
unseenIds := set.NewStrings(expectIds...)
420
for _, unit := range units {
421
id, err := unit.AssignedMachineId()
422
c.Assert(err, jc.ErrorIsNil)
424
machine, err := s.State.Machine(id)
425
c.Assert(err, jc.ErrorIsNil)
426
cons, err := machine.Constraints()
427
c.Assert(err, jc.ErrorIsNil)
428
c.Assert(cons, gc.DeepEquals, expectCons)
430
c.Assert(unseenIds, gc.DeepEquals, set.NewStrings())
433
type fakeDeployer struct {
435
args state.AddApplicationArgs
438
func (f *fakeDeployer) AddApplication(args state.AddApplicationArgs) (*state.Application, error) {