1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
17
jc "github.com/juju/testing/checkers"
18
"github.com/juju/utils"
19
"github.com/juju/version"
20
gc "gopkg.in/check.v1"
21
"gopkg.in/juju/charm.v6-unstable"
22
"gopkg.in/juju/names.v2"
23
goyaml "gopkg.in/yaml.v2"
25
"github.com/juju/juju/apiserver/params"
26
"github.com/juju/juju/cmd/modelcmd"
27
"github.com/juju/juju/constraints"
28
"github.com/juju/juju/core/migration"
29
"github.com/juju/juju/environs"
30
"github.com/juju/juju/instance"
31
"github.com/juju/juju/juju/osenv"
32
"github.com/juju/juju/juju/testing"
33
"github.com/juju/juju/network"
34
"github.com/juju/juju/state"
35
"github.com/juju/juju/state/multiwatcher"
36
"github.com/juju/juju/state/presence"
37
"github.com/juju/juju/status"
38
"github.com/juju/juju/testcharms"
39
coretesting "github.com/juju/juju/testing"
40
"github.com/juju/juju/testing/factory"
41
coreversion "github.com/juju/juju/version"
45
currentVersion = version.Number{Major: 1, Minor: 2, Patch: 3}
46
nextVersion = version.Number{Major: 1, Minor: 2, Patch: 4}
49
func runStatus(c *gc.C, args ...string) (code int, stdout, stderr []byte) {
50
ctx := coretesting.Context(c)
51
code = cmd.Main(NewStatusCommand(), ctx, args)
52
stdout = ctx.Stdout.(*bytes.Buffer).Bytes()
53
stderr = ctx.Stderr.(*bytes.Buffer).Bytes()
57
type StatusSuite struct {
61
var _ = gc.Suite(&StatusSuite{})
63
func (s *StatusSuite) SetUpSuite(c *gc.C) {
64
s.JujuConnSuite.SetUpSuite(c)
65
s.PatchValue(&coreversion.Current, currentVersion)
68
func (s *StatusSuite) SetUpTest(c *gc.C) {
69
s.ConfigAttrs = map[string]interface{}{
70
"agent-version": currentVersion.String(),
72
s.JujuConnSuite.SetUpTest(c)
75
type M map[string]interface{}
79
type testCase struct {
84
func test(summary string, steps ...stepper) testCase {
85
return testCase{summary, steps}
88
type stepper interface {
89
step(c *gc.C, ctx *context)
96
func newContext(st *state.State, env environs.Environ, adminUserTag string) *context {
97
// We make changes in the API server's state so that
98
// our changes to presence are immediately noticed
103
charms: make(map[string]*state.Charm),
104
pingers: make(map[string]*presence.Pinger),
105
adminUserTag: adminUserTag,
109
type context struct {
112
charms map[string]*state.Charm
113
pingers map[string]*presence.Pinger
114
adminUserTag string // A string repr of the tag.
118
func (ctx *context) reset(c *gc.C) {
119
for _, up := range ctx.pingers {
120
err := up.KillForTesting()
121
c.Check(err, jc.ErrorIsNil)
125
func (ctx *context) run(c *gc.C, steps []stepper) {
126
for i, s := range steps {
133
func (ctx *context) setAgentPresence(c *gc.C, p presence.Agent) *presence.Pinger {
134
pinger, err := p.SetAgentPresence()
135
c.Assert(err, jc.ErrorIsNil)
137
err = p.WaitAgentPresence(coretesting.LongWait)
138
c.Assert(err, jc.ErrorIsNil)
139
agentPresence, err := p.AgentPresence()
140
c.Assert(err, jc.ErrorIsNil)
141
c.Assert(agentPresence, jc.IsTrue)
145
func (s *StatusSuite) newContext(c *gc.C) *context {
146
st := s.Environ.(testing.GetStater).GetStateInAPIServer()
148
// We make changes in the API server's state so that
149
// our changes to presence are immediately noticed
151
return newContext(st, s.Environ, s.AdminUserTag(c).String())
154
func (s *StatusSuite) resetContext(c *gc.C, ctx *context) {
156
s.JujuConnSuite.Reset(c)
159
// shortcuts for expected output.
162
"name": "controller",
163
"controller": "kontroll",
170
"current": "started",
171
"since": "01 Apr 15 01:23+10:00",
173
"dns-name": "controller-0.dns",
174
"instance-id": "controller-0",
176
"current": "pending",
177
"since": "01 Apr 15 01:23+10:00",
180
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
181
"controller-member-status": "adding-vote",
185
"current": "started",
186
"since": "01 Apr 15 01:23+10:00",
188
"dns-name": "controller-1.dns",
189
"instance-id": "controller-1",
191
"current": "pending",
192
"since": "01 Apr 15 01:23+10:00",
195
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
199
"current": "started",
200
"since": "01 Apr 15 01:23+10:00",
202
"dns-name": "controller-2.dns",
203
"instance-id": "controller-2",
205
"current": "pending",
206
"since": "01 Apr 15 01:23+10:00",
209
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
213
"current": "started",
214
"since": "01 Apr 15 01:23+10:00",
216
"dns-name": "controller-3.dns",
217
"instance-id": "controller-3",
219
"current": "pending",
220
"since": "01 Apr 15 01:23+10:00",
223
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
227
"current": "started",
228
"since": "01 Apr 15 01:23+10:00",
230
"dns-name": "controller-4.dns",
231
"instance-id": "controller-4",
233
"current": "pending",
234
"since": "01 Apr 15 01:23+10:00",
237
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
239
machine1WithContainers = M{
241
"current": "started",
242
"since": "01 Apr 15 01:23+10:00",
247
"current": "started",
248
"since": "01 Apr 15 01:23+10:00",
253
"current": "started",
254
"since": "01 Apr 15 01:23+10:00",
256
"dns-name": "controller-3.dns",
257
"instance-id": "controller-3",
259
"current": "pending",
260
"since": "01 Apr 15 01:23+10:00",
265
"dns-name": "controller-2.dns",
266
"instance-id": "controller-2",
268
"current": "pending",
269
"since": "01 Apr 15 01:23+10:00",
275
"current": "pending",
276
"since": "01 Apr 15 01:23+10:00",
278
"instance-id": "pending",
280
"current": "pending",
281
"since": "01 Apr 15 01:23+10:00",
286
"dns-name": "controller-1.dns",
287
"instance-id": "controller-1",
289
"current": "pending",
290
"since": "01 Apr 15 01:23+10:00",
294
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
296
unexposedService = dummyCharm(M{
297
"application-status": M{
298
"current": "unknown",
299
"message": "Waiting for agent initialization to finish",
300
"since": "01 Apr 15 01:23+10:00",
303
exposedService = dummyCharm(M{
304
"application-status": M{
305
"current": "unknown",
306
"message": "Waiting for agent initialization to finish",
307
"since": "01 Apr 15 01:23+10:00",
312
"charm": "cs:quantal/logging-1",
313
"charm-origin": "jujucharms",
314
"charm-name": "logging",
319
"application-status": M{},
321
"logging-directory": L{"wordpress"},
324
"subordinate-to": L{"mysql", "wordpress"},
328
type outputFormat struct {
330
marshal func(v interface{}) ([]byte, error)
331
unmarshal func(data []byte, v interface{}) error
334
// statusFormats list all output formats that can be marshalled as structured data,
335
// supported by status command.
336
var statusFormats = []outputFormat{
337
{"yaml", goyaml.Marshal, goyaml.Unmarshal},
338
{"json", json.Marshal, json.Unmarshal},
341
var machineCons = constraints.MustParse("cpu-cores=2 mem=8G root-disk=8G")
343
var statusTests = []testCase{
346
"bootstrap and starting a single instance",
348
addMachine{machineId: "0", job: state.JobManageModel},
350
"simulate juju bootstrap by adding machine/0 to the state",
356
"current": "pending",
357
"since": "01 Apr 15 01:23+10:00",
359
"instance-id": "pending",
361
"current": "pending",
362
"since": "01 Apr 15 01:23+10:00",
365
"controller-member-status": "adding-vote",
372
startAliveMachine{"0"},
373
setAddresses{"0", []network.Address{
374
network.NewAddress("10.0.0.1"),
375
network.NewScopedAddress("controller-0.dns", network.ScopePublic),
378
"simulate the PA starting an instance in response to the state change",
384
"current": "pending",
385
"since": "01 Apr 15 01:23+10:00",
387
"dns-name": "controller-0.dns",
388
"instance-id": "controller-0",
390
"current": "pending",
391
"since": "01 Apr 15 01:23+10:00",
394
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
395
"controller-member-status": "adding-vote",
402
setMachineStatus{"0", status.StatusStarted, ""},
404
"simulate the MA started and set the machine status",
414
setTools{"0", version.MustParseBinary("1.2.3-trusty-ppc")},
416
"simulate the MA setting the version",
421
"dns-name": "controller-0.dns",
422
"instance-id": "controller-0",
424
"current": "pending",
425
"since": "01 Apr 15 01:23+10:00",
428
"current": "started",
429
"since": "01 Apr 15 01:23+10:00",
433
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
434
"controller-member-status": "adding-vote",
442
"instance with different hardware characteristics",
443
addMachine{machineId: "0", cons: machineCons, job: state.JobManageModel},
444
setAddresses{"0", []network.Address{
445
network.NewAddress("10.0.0.1"),
446
network.NewScopedAddress("controller-0.dns", network.ScopePublic),
448
startAliveMachine{"0"},
449
setMachineStatus{"0", status.StatusStarted, ""},
451
"machine 0 has specific hardware characteristics",
457
"current": "started",
458
"since": "01 Apr 15 01:23+10:00",
460
"dns-name": "controller-0.dns",
461
"instance-id": "controller-0",
463
"current": "pending",
464
"since": "01 Apr 15 01:23+10:00",
467
"hardware": "arch=amd64 cpu-cores=2 mem=8192M root-disk=8192M",
468
"controller-member-status": "adding-vote",
476
"instance without addresses",
477
addMachine{machineId: "0", cons: machineCons, job: state.JobManageModel},
478
startAliveMachine{"0"},
479
setMachineStatus{"0", status.StatusStarted, ""},
481
"machine 0 has no dns-name",
487
"current": "started",
488
"since": "01 Apr 15 01:23+10:00",
490
"instance-id": "controller-0",
492
"current": "pending",
493
"since": "01 Apr 15 01:23+10:00",
496
"hardware": "arch=amd64 cpu-cores=2 mem=8192M root-disk=8192M",
497
"controller-member-status": "adding-vote",
505
"test pending and missing machines",
506
addMachine{machineId: "0", job: state.JobManageModel},
508
"machine 0 reports pending",
514
"current": "pending",
515
"since": "01 Apr 15 01:23+10:00",
517
"instance-id": "pending",
519
"current": "pending",
520
"since": "01 Apr 15 01:23+10:00",
523
"controller-member-status": "adding-vote",
530
startMissingMachine{"0"},
532
"machine 0 reports missing",
537
"instance-id": "i-missing",
539
"current": "pending",
540
"since": "01 Apr 15 01:23+10:00",
543
"current": "unknown",
544
"message": "missing",
545
"since": "01 Apr 15 01:23+10:00",
548
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
549
"controller-member-status": "adding-vote",
557
"add two services and expose one, then add 2 more machines and some units",
559
addMachine{machineId: "0", job: state.JobManageModel},
560
setAddresses{"0", network.NewAddresses("controller-0.dns")},
561
startAliveMachine{"0"},
562
setMachineStatus{"0", status.StatusStarted, ""},
564
addService{name: "dummy-application", charm: "dummy"},
565
addService{name: "exposed-application", charm: "dummy"},
567
"no applications exposed yet",
574
"dummy-application": unexposedService,
575
"exposed-application": unexposedService,
581
setServiceExposed{"exposed-application", true},
583
"one exposed application",
590
"dummy-application": unexposedService,
591
"exposed-application": exposedService,
597
addMachine{machineId: "1", job: state.JobHostUnits},
598
setAddresses{"1", network.NewAddresses("controller-1.dns")},
599
startAliveMachine{"1"},
600
setMachineStatus{"1", status.StatusStarted, ""},
601
addMachine{machineId: "2", job: state.JobHostUnits},
602
setAddresses{"2", network.NewAddresses("controller-2.dns")},
603
startAliveMachine{"2"},
604
setMachineStatus{"2", status.StatusStarted, ""},
606
"two more machines added",
615
"dummy-application": unexposedService,
616
"exposed-application": exposedService,
622
addAliveUnit{"dummy-application", "1"},
623
addAliveUnit{"exposed-application", "2"},
624
setAgentStatus{"exposed-application/0", status.StatusError, "You Require More Vespene Gas", nil},
625
// Open multiple ports with different protocols,
626
// ensure they're sorted on protocol, then number.
627
openUnitPort{"exposed-application/0", "udp", 10},
628
openUnitPort{"exposed-application/0", "udp", 2},
629
openUnitPort{"exposed-application/0", "tcp", 3},
630
openUnitPort{"exposed-application/0", "tcp", 2},
631
// Simulate some status with no info, while the agent is down.
632
// Status used to be down, we no longer support said state.
633
// now is one of: pending, started, error.
634
setUnitStatus{"dummy-application/0", status.StatusTerminated, "", nil},
635
setAgentStatus{"dummy-application/0", status.StatusIdle, "", nil},
638
"add two units, one alive (in error state), one started",
647
"exposed-application": dummyCharm(M{
649
"application-status": M{
651
"message": "You Require More Vespene Gas",
652
"since": "01 Apr 15 01:23+10:00",
655
"exposed-application/0": M{
657
"workload-status": M{
659
"message": "You Require More Vespene Gas",
660
"since": "01 Apr 15 01:23+10:00",
664
"since": "01 Apr 15 01:23+10:00",
667
"2/tcp", "3/tcp", "2/udp", "10/udp",
669
"public-address": "controller-2.dns",
673
"dummy-application": dummyCharm(M{
674
"application-status": M{
675
"current": "terminated",
676
"since": "01 Apr 15 01:23+10:00",
679
"dummy-application/0": M{
681
"workload-status": M{
682
"current": "terminated",
683
"since": "01 Apr 15 01:23+10:00",
687
"since": "01 Apr 15 01:23+10:00",
689
"public-address": "controller-1.dns",
698
addMachine{machineId: "3", job: state.JobHostUnits},
700
// Simulate some status with info, while the agent is down.
701
setAddresses{"3", network.NewAddresses("controller-3.dns")},
702
setMachineStatus{"3", status.StatusStopped, "Really?"},
703
addMachine{machineId: "4", job: state.JobHostUnits},
704
setAddresses{"4", network.NewAddresses("controller-4.dns")},
705
startAliveMachine{"4"},
706
setMachineStatus{"4", status.StatusError, "Beware the red toys"},
707
ensureDyingUnit{"dummy-application/0"},
708
addMachine{machineId: "5", job: state.JobHostUnits},
709
ensureDeadMachine{"5"},
711
"add three more machine, one with a dead agent, one in error state and one dead itself; also one dying unit",
719
"dns-name": "controller-3.dns",
720
"instance-id": "controller-3",
722
"current": "pending",
723
"since": "01 Apr 15 01:23+10:00",
726
"current": "stopped",
727
"message": "Really?",
728
"since": "01 Apr 15 01:23+10:00",
731
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
734
"dns-name": "controller-4.dns",
735
"instance-id": "controller-4",
737
"current": "pending",
738
"since": "01 Apr 15 01:23+10:00",
742
"message": "Beware the red toys",
743
"since": "01 Apr 15 01:23+10:00",
746
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
750
"current": "pending",
751
"since": "01 Apr 15 01:23+10:00",
754
"instance-id": "pending",
756
"current": "pending",
757
"since": "01 Apr 15 01:23+10:00",
763
"exposed-application": dummyCharm(M{
765
"application-status": M{
767
"message": "You Require More Vespene Gas",
768
"since": "01 Apr 15 01:23+10:00",
771
"exposed-application/0": M{
773
"workload-status": M{
775
"message": "You Require More Vespene Gas",
776
"since": "01 Apr 15 01:23+10:00",
780
"since": "01 Apr 15 01:23+10:00",
783
"2/tcp", "3/tcp", "2/udp", "10/udp",
785
"public-address": "controller-2.dns",
789
"dummy-application": dummyCharm(M{
790
"application-status": M{
791
"current": "terminated",
792
"since": "01 Apr 15 01:23+10:00",
795
"dummy-application/0": M{
797
"workload-status": M{
798
"current": "terminated",
799
"since": "01 Apr 15 01:23+10:00",
803
"since": "01 Apr 15 01:23+10:00",
805
"public-address": "controller-1.dns",
815
"scope status on dummy-application/0 unit",
816
[]string{"dummy-application/0"},
823
"dummy-application": dummyCharm(M{
824
"application-status": M{
825
"current": "terminated",
826
"since": "01 Apr 15 01:23+10:00",
829
"dummy-application/0": M{
831
"workload-status": M{
832
"current": "terminated",
833
"since": "01 Apr 15 01:23+10:00",
837
"since": "01 Apr 15 01:23+10:00",
839
"public-address": "controller-1.dns",
847
"scope status on exposed-application application",
848
[]string{"exposed-application"},
855
"exposed-application": dummyCharm(M{
857
"application-status": M{
859
"message": "You Require More Vespene Gas",
860
"since": "01 Apr 15 01:23+10:00",
863
"exposed-application/0": M{
865
"workload-status": M{
867
"message": "You Require More Vespene Gas",
868
"since": "01 Apr 15 01:23+10:00",
872
"since": "01 Apr 15 01:23+10:00",
875
"2/tcp", "3/tcp", "2/udp", "10/udp",
877
"public-address": "controller-2.dns",
885
"scope status on application pattern",
886
[]string{"d*-application"},
893
"dummy-application": dummyCharm(M{
894
"application-status": M{
895
"current": "terminated",
896
"since": "01 Apr 15 01:23+10:00",
899
"dummy-application/0": M{
901
"workload-status": M{
902
"current": "terminated",
903
"since": "01 Apr 15 01:23+10:00",
907
"since": "01 Apr 15 01:23+10:00",
909
"public-address": "controller-1.dns",
917
"scope status on unit pattern",
918
[]string{"e*posed-application/*"},
925
"exposed-application": dummyCharm(M{
927
"application-status": M{
929
"message": "You Require More Vespene Gas",
930
"since": "01 Apr 15 01:23+10:00",
933
"exposed-application/0": M{
935
"workload-status": M{
937
"message": "You Require More Vespene Gas",
938
"since": "01 Apr 15 01:23+10:00",
942
"since": "01 Apr 15 01:23+10:00",
945
"2/tcp", "3/tcp", "2/udp", "10/udp",
947
"public-address": "controller-2.dns",
955
"scope status on combination of application and unit patterns",
956
[]string{"exposed-application", "dummy-application", "e*posed-application/*", "dummy-application/*"},
964
"dummy-application": dummyCharm(M{
965
"application-status": M{
966
"current": "terminated",
967
"since": "01 Apr 15 01:23+10:00",
970
"dummy-application/0": M{
972
"workload-status": M{
973
"current": "terminated",
974
"since": "01 Apr 15 01:23+10:00",
978
"since": "01 Apr 15 01:23+10:00",
980
"public-address": "controller-1.dns",
984
"exposed-application": dummyCharm(M{
986
"application-status": M{
988
"message": "You Require More Vespene Gas",
989
"since": "01 Apr 15 01:23+10:00",
992
"exposed-application/0": M{
994
"workload-status": M{
996
"message": "You Require More Vespene Gas",
997
"since": "01 Apr 15 01:23+10:00",
1001
"since": "01 Apr 15 01:23+10:00",
1004
"2/tcp", "3/tcp", "2/udp", "10/udp",
1006
"public-address": "controller-2.dns",
1015
"a unit with a hook relation error",
1016
addMachine{machineId: "0", job: state.JobManageModel},
1017
setAddresses{"0", network.NewAddresses("controller-0.dns")},
1018
startAliveMachine{"0"},
1019
setMachineStatus{"0", status.StatusStarted, ""},
1021
addMachine{machineId: "1", job: state.JobHostUnits},
1022
setAddresses{"1", network.NewAddresses("controller-1.dns")},
1023
startAliveMachine{"1"},
1024
setMachineStatus{"1", status.StatusStarted, ""},
1026
addCharm{"wordpress"},
1027
addService{name: "wordpress", charm: "wordpress"},
1028
addAliveUnit{"wordpress", "1"},
1031
addService{name: "mysql", charm: "mysql"},
1032
addAliveUnit{"mysql", "1"},
1034
relateServices{"wordpress", "mysql"},
1036
setAgentStatus{"wordpress/0", status.StatusError,
1037
"hook failed: some-relation-changed",
1038
map[string]interface{}{"relation-id": 0}},
1041
"a unit with a hook relation error",
1049
"wordpress": wordpressCharm(M{
1053
"application-status": M{
1055
"message": "hook failed: some-relation-changed",
1056
"since": "01 Apr 15 01:23+10:00",
1061
"workload-status": M{
1063
"message": "hook failed: some-relation-changed for mysql:server",
1064
"since": "01 Apr 15 01:23+10:00",
1068
"since": "01 Apr 15 01:23+10:00",
1070
"public-address": "controller-1.dns",
1074
"mysql": mysqlCharm(M{
1076
"server": L{"wordpress"},
1078
"application-status": M{
1079
"current": "unknown",
1080
"message": "Waiting for agent initialization to finish",
1081
"since": "01 Apr 15 01:23+10:00",
1086
"workload-status": M{
1087
"current": "unknown",
1088
"message": "Waiting for agent initialization to finish",
1089
"since": "01 Apr 15 01:23+10:00",
1092
"current": "allocating",
1093
"since": "01 Apr 15 01:23+10:00",
1095
"public-address": "controller-1.dns",
1104
"a unit with a hook relation error when the agent is down",
1105
addMachine{machineId: "0", job: state.JobManageModel},
1106
setAddresses{"0", network.NewAddresses("controller-0.dns")},
1107
startAliveMachine{"0"},
1108
setMachineStatus{"0", status.StatusStarted, ""},
1110
addMachine{machineId: "1", job: state.JobHostUnits},
1111
setAddresses{"1", network.NewAddresses("controller-1.dns")},
1112
startAliveMachine{"1"},
1113
setMachineStatus{"1", status.StatusStarted, ""},
1115
addCharm{"wordpress"},
1116
addService{name: "wordpress", charm: "wordpress"},
1117
addAliveUnit{"wordpress", "1"},
1120
addService{name: "mysql", charm: "mysql"},
1121
addAliveUnit{"mysql", "1"},
1123
relateServices{"wordpress", "mysql"},
1125
setAgentStatus{"wordpress/0", status.StatusError,
1126
"hook failed: some-relation-changed",
1127
map[string]interface{}{"relation-id": 0}},
1130
"a unit with a hook relation error when the agent is down",
1138
"wordpress": wordpressCharm(M{
1142
"application-status": M{
1144
"message": "hook failed: some-relation-changed",
1145
"since": "01 Apr 15 01:23+10:00",
1150
"workload-status": M{
1152
"message": "hook failed: some-relation-changed for mysql:server",
1153
"since": "01 Apr 15 01:23+10:00",
1157
"since": "01 Apr 15 01:23+10:00",
1159
"public-address": "controller-1.dns",
1163
"mysql": mysqlCharm(M{
1165
"server": L{"wordpress"},
1167
"application-status": M{
1168
"current": "unknown",
1169
"message": "Waiting for agent initialization to finish",
1170
"since": "01 Apr 15 01:23+10:00",
1175
"workload-status": M{
1176
"current": "unknown",
1177
"message": "Waiting for agent initialization to finish",
1178
"since": "01 Apr 15 01:23+10:00",
1181
"current": "allocating",
1182
"since": "01 Apr 15 01:23+10:00",
1184
"public-address": "controller-1.dns",
1193
"add a dying application",
1195
addService{name: "dummy-application", charm: "dummy"},
1196
addMachine{machineId: "0", job: state.JobHostUnits},
1197
addAliveUnit{"dummy-application", "0"},
1198
ensureDyingService{"dummy-application"},
1200
"application shows life==dying",
1206
"current": "pending",
1207
"since": "01 Apr 15 01:23+10:00",
1209
"instance-id": "pending",
1210
"machine-status": M{
1211
"current": "pending",
1212
"since": "01 Apr 15 01:23+10:00",
1215
"series": "quantal",
1219
"dummy-application": dummyCharm(M{
1221
"application-status": M{
1222
"current": "unknown",
1223
"message": "Waiting for agent initialization to finish",
1224
"since": "01 Apr 15 01:23+10:00",
1227
"dummy-application/0": M{
1229
"workload-status": M{
1230
"current": "unknown",
1231
"message": "Waiting for agent initialization to finish",
1232
"since": "01 Apr 15 01:23+10:00",
1235
"current": "allocating",
1236
"since": "01 Apr 15 01:23+10:00",
1246
"a unit where the agent is down shows as lost",
1248
addService{name: "dummy-application", charm: "dummy"},
1249
addMachine{machineId: "0", job: state.JobHostUnits},
1250
startAliveMachine{"0"},
1251
setMachineStatus{"0", status.StatusStarted, ""},
1252
addUnit{"dummy-application", "0"},
1253
setAgentStatus{"dummy-application/0", status.StatusIdle, "", nil},
1254
setUnitStatus{"dummy-application/0", status.StatusActive, "", nil},
1256
"unit shows that agent is lost",
1262
"current": "started",
1263
"since": "01 Apr 15 01:23+10:00",
1265
"instance-id": "controller-0",
1266
"machine-status": M{
1267
"current": "pending",
1268
"since": "01 Apr 15 01:23+10:00",
1271
"series": "quantal",
1272
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
1276
"dummy-application": dummyCharm(M{
1277
"application-status": M{
1278
"current": "active",
1279
"since": "01 Apr 15 01:23+10:00",
1282
"dummy-application/0": M{
1284
"workload-status": M{
1285
"current": "unknown",
1286
"message": "agent is lost, sorry! See 'juju status-history dummy-application/0'",
1287
"since": "01 Apr 15 01:23+10:00",
1291
"message": "agent is not communicating with the server",
1292
"since": "01 Apr 15 01:23+10:00",
1304
"complex scenario with multiple related services",
1305
addMachine{machineId: "0", job: state.JobManageModel},
1306
setAddresses{"0", network.NewAddresses("controller-0.dns")},
1307
startAliveMachine{"0"},
1308
setMachineStatus{"0", status.StatusStarted, ""},
1309
addCharm{"wordpress"},
1311
addCharm{"varnish"},
1313
addService{name: "project", charm: "wordpress"},
1314
setServiceExposed{"project", true},
1315
addMachine{machineId: "1", job: state.JobHostUnits},
1316
setAddresses{"1", network.NewAddresses("controller-1.dns")},
1317
startAliveMachine{"1"},
1318
setMachineStatus{"1", status.StatusStarted, ""},
1319
addAliveUnit{"project", "1"},
1320
setAgentStatus{"project/0", status.StatusIdle, "", nil},
1321
setUnitStatus{"project/0", status.StatusActive, "", nil},
1323
addService{name: "mysql", charm: "mysql"},
1324
setServiceExposed{"mysql", true},
1325
addMachine{machineId: "2", job: state.JobHostUnits},
1326
setAddresses{"2", network.NewAddresses("controller-2.dns")},
1327
startAliveMachine{"2"},
1328
setMachineStatus{"2", status.StatusStarted, ""},
1329
addAliveUnit{"mysql", "2"},
1330
setAgentStatus{"mysql/0", status.StatusIdle, "", nil},
1331
setUnitStatus{"mysql/0", status.StatusActive, "", nil},
1333
addService{name: "varnish", charm: "varnish"},
1334
setServiceExposed{"varnish", true},
1335
addMachine{machineId: "3", job: state.JobHostUnits},
1336
setAddresses{"3", network.NewAddresses("controller-3.dns")},
1337
startAliveMachine{"3"},
1338
setMachineStatus{"3", status.StatusStarted, ""},
1339
addAliveUnit{"varnish", "3"},
1341
addService{name: "private", charm: "wordpress"},
1342
setServiceExposed{"private", true},
1343
addMachine{machineId: "4", job: state.JobHostUnits},
1344
setAddresses{"4", network.NewAddresses("controller-4.dns")},
1345
startAliveMachine{"4"},
1346
setMachineStatus{"4", status.StatusStarted, ""},
1347
addAliveUnit{"private", "4"},
1349
relateServices{"project", "mysql"},
1350
relateServices{"project", "varnish"},
1351
relateServices{"private", "mysql"},
1354
"multiples services with relations between some of them",
1365
"project": wordpressCharm(M{
1367
"application-status": M{
1368
"current": "active",
1369
"since": "01 Apr 15 01:23+10:00",
1374
"workload-status": M{
1375
"current": "active",
1376
"since": "01 Apr 15 01:23+10:00",
1380
"since": "01 Apr 15 01:23+10:00",
1382
"public-address": "controller-1.dns",
1387
"cache": L{"varnish"},
1390
"mysql": mysqlCharm(M{
1392
"application-status": M{
1393
"current": "active",
1394
"since": "01 Apr 15 01:23+10:00",
1399
"workload-status": M{
1400
"current": "active",
1401
"since": "01 Apr 15 01:23+10:00",
1405
"since": "01 Apr 15 01:23+10:00",
1407
"public-address": "controller-2.dns",
1411
"server": L{"private", "project"},
1415
"charm": "cs:quantal/varnish-1",
1416
"charm-origin": "jujucharms",
1417
"charm-name": "varnish",
1419
"series": "quantal",
1422
"application-status": M{
1423
"current": "unknown",
1424
"message": "Waiting for agent initialization to finish",
1425
"since": "01 Apr 15 01:23+10:00",
1430
"workload-status": M{
1431
"current": "unknown",
1432
"message": "Waiting for agent initialization to finish",
1433
"since": "01 Apr 15 01:23+10:00",
1436
"current": "allocating",
1437
"since": "01 Apr 15 01:23+10:00",
1439
"public-address": "controller-3.dns",
1443
"webcache": L{"project"},
1446
"private": wordpressCharm(M{
1448
"application-status": M{
1449
"current": "unknown",
1450
"message": "Waiting for agent initialization to finish",
1451
"since": "01 Apr 15 01:23+10:00",
1456
"workload-status": M{
1457
"current": "unknown",
1458
"message": "Waiting for agent initialization to finish",
1459
"since": "01 Apr 15 01:23+10:00",
1462
"current": "allocating",
1463
"since": "01 Apr 15 01:23+10:00",
1465
"public-address": "controller-4.dns",
1477
"simple peer scenario",
1478
addMachine{machineId: "0", job: state.JobManageModel},
1479
setAddresses{"0", network.NewAddresses("controller-0.dns")},
1480
startAliveMachine{"0"},
1481
setMachineStatus{"0", status.StatusStarted, ""},
1483
addCharm{"wordpress"},
1485
addService{name: "riak", charm: "riak"},
1486
setServiceExposed{"riak", true},
1487
addMachine{machineId: "1", job: state.JobHostUnits},
1488
setAddresses{"1", network.NewAddresses("controller-1.dns")},
1489
startAliveMachine{"1"},
1490
setMachineStatus{"1", status.StatusStarted, ""},
1491
addAliveUnit{"riak", "1"},
1492
setAgentStatus{"riak/0", status.StatusIdle, "", nil},
1493
setUnitStatus{"riak/0", status.StatusActive, "", nil},
1494
addMachine{machineId: "2", job: state.JobHostUnits},
1495
setAddresses{"2", network.NewAddresses("controller-2.dns")},
1496
startAliveMachine{"2"},
1497
setMachineStatus{"2", status.StatusStarted, ""},
1498
addAliveUnit{"riak", "2"},
1499
setAgentStatus{"riak/1", status.StatusIdle, "", nil},
1500
setUnitStatus{"riak/1", status.StatusActive, "", nil},
1501
addMachine{machineId: "3", job: state.JobHostUnits},
1502
setAddresses{"3", network.NewAddresses("controller-3.dns")},
1503
startAliveMachine{"3"},
1504
setMachineStatus{"3", status.StatusStarted, ""},
1505
addAliveUnit{"riak", "3"},
1506
setAgentStatus{"riak/2", status.StatusIdle, "", nil},
1507
setUnitStatus{"riak/2", status.StatusActive, "", nil},
1510
"multiples related peer units",
1521
"charm": "cs:quantal/riak-7",
1522
"charm-origin": "jujucharms",
1523
"charm-name": "riak",
1525
"series": "quantal",
1528
"application-status": M{
1529
"current": "active",
1530
"since": "01 Apr 15 01:23+10:00",
1535
"workload-status": M{
1536
"current": "active",
1537
"since": "01 Apr 15 01:23+10:00",
1541
"since": "01 Apr 15 01:23+10:00",
1543
"public-address": "controller-1.dns",
1547
"workload-status": M{
1548
"current": "active",
1549
"since": "01 Apr 15 01:23+10:00",
1553
"since": "01 Apr 15 01:23+10:00",
1555
"public-address": "controller-2.dns",
1559
"workload-status": M{
1560
"current": "active",
1561
"since": "01 Apr 15 01:23+10:00",
1565
"since": "01 Apr 15 01:23+10:00",
1567
"public-address": "controller-3.dns",
1579
// Subordinate tests
1581
"one application with one subordinate application",
1582
addMachine{machineId: "0", job: state.JobManageModel},
1583
setAddresses{"0", network.NewAddresses("controller-0.dns")},
1584
startAliveMachine{"0"},
1585
setMachineStatus{"0", status.StatusStarted, ""},
1586
addCharm{"wordpress"},
1588
addCharm{"logging"},
1590
addService{name: "wordpress", charm: "wordpress"},
1591
setServiceExposed{"wordpress", true},
1592
addMachine{machineId: "1", job: state.JobHostUnits},
1593
setAddresses{"1", network.NewAddresses("controller-1.dns")},
1594
startAliveMachine{"1"},
1595
setMachineStatus{"1", status.StatusStarted, ""},
1596
addAliveUnit{"wordpress", "1"},
1597
setAgentStatus{"wordpress/0", status.StatusIdle, "", nil},
1598
setUnitStatus{"wordpress/0", status.StatusActive, "", nil},
1600
addService{name: "mysql", charm: "mysql"},
1601
setServiceExposed{"mysql", true},
1602
addMachine{machineId: "2", job: state.JobHostUnits},
1603
setAddresses{"2", network.NewAddresses("controller-2.dns")},
1604
startAliveMachine{"2"},
1605
setMachineStatus{"2", status.StatusStarted, ""},
1606
addAliveUnit{"mysql", "2"},
1607
setAgentStatus{"mysql/0", status.StatusIdle, "", nil},
1608
setUnitStatus{"mysql/0", status.StatusActive, "", nil},
1610
addService{name: "logging", charm: "logging"},
1611
setServiceExposed{"logging", true},
1613
relateServices{"wordpress", "mysql"},
1614
relateServices{"wordpress", "logging"},
1615
relateServices{"mysql", "logging"},
1617
addSubordinate{"wordpress/0", "logging"},
1618
addSubordinate{"mysql/0", "logging"},
1620
setUnitsAlive{"logging"},
1621
setAgentStatus{"logging/0", status.StatusIdle, "", nil},
1622
setUnitStatus{"logging/0", status.StatusActive, "", nil},
1623
setAgentStatus{"logging/1", status.StatusError, "somehow lost in all those logs", nil},
1626
"multiples related peer units",
1635
"wordpress": wordpressCharm(M{
1637
"application-status": M{
1638
"current": "active",
1639
"since": "01 Apr 15 01:23+10:00",
1644
"workload-status": M{
1645
"current": "active",
1646
"since": "01 Apr 15 01:23+10:00",
1650
"since": "01 Apr 15 01:23+10:00",
1654
"workload-status": M{
1655
"current": "active",
1656
"since": "01 Apr 15 01:23+10:00",
1660
"since": "01 Apr 15 01:23+10:00",
1662
"public-address": "controller-1.dns",
1665
"public-address": "controller-1.dns",
1670
"logging-dir": L{"logging"},
1673
"mysql": mysqlCharm(M{
1675
"application-status": M{
1676
"current": "active",
1677
"since": "01 Apr 15 01:23+10:00",
1682
"workload-status": M{
1683
"current": "active",
1684
"since": "01 Apr 15 01:23+10:00",
1688
"since": "01 Apr 15 01:23+10:00",
1692
"workload-status": M{
1694
"message": "somehow lost in all those logs",
1695
"since": "01 Apr 15 01:23+10:00",
1699
"since": "01 Apr 15 01:23+10:00",
1701
"public-address": "controller-2.dns",
1704
"public-address": "controller-2.dns",
1708
"server": L{"wordpress"},
1709
"juju-info": L{"logging"},
1712
"logging": loggingCharm,
1717
// scoped on 'logging'
1719
"subordinates scoped on logging",
1720
[]string{"logging"},
1728
"wordpress": wordpressCharm(M{
1730
"application-status": M{
1731
"current": "active",
1732
"since": "01 Apr 15 01:23+10:00",
1737
"workload-status": M{
1738
"current": "active",
1739
"since": "01 Apr 15 01:23+10:00",
1743
"since": "01 Apr 15 01:23+10:00",
1747
"workload-status": M{
1748
"current": "active",
1749
"since": "01 Apr 15 01:23+10:00",
1753
"since": "01 Apr 15 01:23+10:00",
1755
"public-address": "controller-1.dns",
1758
"public-address": "controller-1.dns",
1763
"logging-dir": L{"logging"},
1766
"mysql": mysqlCharm(M{
1768
"application-status": M{
1769
"current": "active",
1770
"since": "01 Apr 15 01:23+10:00",
1775
"workload-status": M{
1776
"current": "active",
1777
"since": "01 Apr 15 01:23+10:00",
1781
"since": "01 Apr 15 01:23+10:00",
1785
"workload-status": M{
1787
"message": "somehow lost in all those logs",
1788
"since": "01 Apr 15 01:23+10:00",
1792
"since": "01 Apr 15 01:23+10:00",
1794
"public-address": "controller-2.dns",
1797
"public-address": "controller-2.dns",
1801
"server": L{"wordpress"},
1802
"juju-info": L{"logging"},
1805
"logging": loggingCharm,
1810
// scoped on wordpress/0
1812
"subordinates scoped on logging",
1813
[]string{"wordpress/0"},
1820
"wordpress": wordpressCharm(M{
1822
"application-status": M{
1823
"current": "active",
1824
"since": "01 Apr 15 01:23+10:00",
1829
"workload-status": M{
1830
"current": "active",
1831
"since": "01 Apr 15 01:23+10:00",
1835
"since": "01 Apr 15 01:23+10:00",
1839
"workload-status": M{
1840
"current": "active",
1841
"since": "01 Apr 15 01:23+10:00",
1845
"since": "01 Apr 15 01:23+10:00",
1847
"public-address": "controller-1.dns",
1850
"public-address": "controller-1.dns",
1855
"logging-dir": L{"logging"},
1858
"logging": loggingCharm,
1864
"machines with containers",
1866
addMachine{machineId: "0", job: state.JobManageModel},
1867
setAddresses{"0", network.NewAddresses("controller-0.dns")},
1868
startAliveMachine{"0"},
1869
setMachineStatus{"0", status.StatusStarted, ""},
1871
addService{name: "mysql", charm: "mysql"},
1872
setServiceExposed{"mysql", true},
1875
addMachine{machineId: "1", job: state.JobHostUnits},
1876
setAddresses{"1", network.NewAddresses("controller-1.dns")},
1877
startAliveMachine{"1"},
1878
setMachineStatus{"1", status.StatusStarted, ""},
1879
addAliveUnit{"mysql", "1"},
1880
setAgentStatus{"mysql/0", status.StatusIdle, "", nil},
1881
setUnitStatus{"mysql/0", status.StatusActive, "", nil},
1883
// step 14: A container on machine 1.
1884
addContainer{"1", "1/lxd/0", state.JobHostUnits},
1885
setAddresses{"1/lxd/0", network.NewAddresses("controller-2.dns")},
1886
startAliveMachine{"1/lxd/0"},
1887
setMachineStatus{"1/lxd/0", status.StatusStarted, ""},
1888
addAliveUnit{"mysql", "1/lxd/0"},
1889
setAgentStatus{"mysql/1", status.StatusIdle, "", nil},
1890
setUnitStatus{"mysql/1", status.StatusActive, "", nil},
1891
addContainer{"1", "1/lxd/1", state.JobHostUnits},
1893
// step 22: A nested container.
1894
addContainer{"1/lxd/0", "1/lxd/0/lxd/0", state.JobHostUnits},
1895
setAddresses{"1/lxd/0/lxd/0", network.NewAddresses("controller-3.dns")},
1896
startAliveMachine{"1/lxd/0/lxd/0"},
1897
setMachineStatus{"1/lxd/0/lxd/0", status.StatusStarted, ""},
1900
"machines with nested containers",
1905
"1": machine1WithContainers,
1908
"mysql": mysqlCharm(M{
1910
"application-status": M{
1911
"current": "active",
1912
"since": "01 Apr 15 01:23+10:00",
1917
"workload-status": M{
1918
"current": "active",
1919
"since": "01 Apr 15 01:23+10:00",
1923
"since": "01 Apr 15 01:23+10:00",
1925
"public-address": "controller-1.dns",
1928
"machine": "1/lxd/0",
1929
"workload-status": M{
1930
"current": "active",
1931
"since": "01 Apr 15 01:23+10:00",
1935
"since": "01 Apr 15 01:23+10:00",
1937
"public-address": "controller-2.dns",
1945
// step 27: once again, with a scope on mysql/1
1947
"machines with nested containers 2",
1948
[]string{"mysql/1"},
1954
"current": "started",
1955
"since": "01 Apr 15 01:23+10:00",
1960
"current": "started",
1961
"since": "01 Apr 15 01:23+10:00",
1963
"dns-name": "controller-2.dns",
1964
"instance-id": "controller-2",
1965
"machine-status": M{
1966
"current": "pending",
1967
"since": "01 Apr 15 01:23+10:00",
1970
"series": "quantal",
1973
"dns-name": "controller-1.dns",
1974
"instance-id": "controller-1",
1975
"machine-status": M{
1976
"current": "pending",
1977
"since": "01 Apr 15 01:23+10:00",
1980
"series": "quantal",
1981
"hardware": "arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M",
1985
"mysql": mysqlCharm(M{
1987
"application-status": M{
1988
"current": "active",
1989
"since": "01 Apr 15 01:23+10:00",
1993
"machine": "1/lxd/0",
1994
"workload-status": M{
1995
"current": "active",
1996
"since": "01 Apr 15 01:23+10:00",
2000
"since": "01 Apr 15 01:23+10:00",
2002
"public-address": "controller-2.dns",
2011
"application with out of date charm",
2012
addMachine{machineId: "0", job: state.JobManageModel},
2013
setAddresses{"0", network.NewAddresses("controller-0.dns")},
2014
startAliveMachine{"0"},
2015
setMachineStatus{"0", status.StatusStarted, ""},
2016
addMachine{machineId: "1", job: state.JobHostUnits},
2017
setAddresses{"1", network.NewAddresses("controller-1.dns")},
2018
startAliveMachine{"1"},
2019
setMachineStatus{"1", status.StatusStarted, ""},
2021
addService{name: "mysql", charm: "mysql"},
2022
setServiceExposed{"mysql", true},
2023
addCharmPlaceholder{"mysql", 23},
2024
addAliveUnit{"mysql", "1"},
2027
"services and units with correct charm status",
2035
"mysql": mysqlCharm(M{
2036
"can-upgrade-to": "cs:quantal/mysql-23",
2038
"application-status": M{
2039
"current": "unknown",
2040
"message": "Waiting for agent initialization to finish",
2041
"since": "01 Apr 15 01:23+10:00",
2046
"workload-status": M{
2047
"current": "unknown",
2048
"message": "Waiting for agent initialization to finish",
2049
"since": "01 Apr 15 01:23+10:00",
2052
"current": "allocating",
2053
"since": "01 Apr 15 01:23+10:00",
2055
"public-address": "controller-1.dns",
2064
"unit with out of date charm",
2065
addMachine{machineId: "0", job: state.JobManageModel},
2066
setAddresses{"0", network.NewAddresses("controller-0.dns")},
2067
startAliveMachine{"0"},
2068
setMachineStatus{"0", status.StatusStarted, ""},
2069
addMachine{machineId: "1", job: state.JobHostUnits},
2070
setAddresses{"1", network.NewAddresses("controller-1.dns")},
2071
startAliveMachine{"1"},
2072
setMachineStatus{"1", status.StatusStarted, ""},
2074
addService{name: "mysql", charm: "mysql"},
2075
setServiceExposed{"mysql", true},
2076
addAliveUnit{"mysql", "1"},
2077
setUnitCharmURL{"mysql/0", "cs:quantal/mysql-1"},
2078
addCharmWithRevision{addCharm{"mysql"}, "local", 1},
2079
setServiceCharm{"mysql", "local:quantal/mysql-1"},
2082
"services and units with correct charm status",
2090
"mysql": mysqlCharm(M{
2091
"charm": "local:quantal/mysql-1",
2092
"charm-origin": "local",
2094
"application-status": M{
2095
"current": "active",
2096
"since": "01 Apr 15 01:23+10:00",
2101
"workload-status": M{
2102
"current": "active",
2103
"since": "01 Apr 15 01:23+10:00",
2107
"since": "01 Apr 15 01:23+10:00",
2109
"upgrading-from": "cs:quantal/mysql-1",
2110
"public-address": "controller-1.dns",
2119
"application and unit with out of date charms",
2120
addMachine{machineId: "0", job: state.JobManageModel},
2121
setAddresses{"0", network.NewAddresses("controller-0.dns")},
2122
startAliveMachine{"0"},
2123
setMachineStatus{"0", status.StatusStarted, ""},
2124
addMachine{machineId: "1", job: state.JobHostUnits},
2125
setAddresses{"1", network.NewAddresses("controller-1.dns")},
2126
startAliveMachine{"1"},
2127
setMachineStatus{"1", status.StatusStarted, ""},
2129
addService{name: "mysql", charm: "mysql"},
2130
setServiceExposed{"mysql", true},
2131
addAliveUnit{"mysql", "1"},
2132
setUnitCharmURL{"mysql/0", "cs:quantal/mysql-1"},
2133
addCharmWithRevision{addCharm{"mysql"}, "cs", 2},
2134
setServiceCharm{"mysql", "cs:quantal/mysql-2"},
2135
addCharmPlaceholder{"mysql", 23},
2138
"services and units with correct charm status",
2146
"mysql": mysqlCharm(M{
2147
"charm": "cs:quantal/mysql-2",
2149
"can-upgrade-to": "cs:quantal/mysql-23",
2151
"application-status": M{
2152
"current": "active",
2153
"since": "01 Apr 15 01:23+10:00",
2158
"workload-status": M{
2159
"current": "active",
2160
"since": "01 Apr 15 01:23+10:00",
2164
"since": "01 Apr 15 01:23+10:00",
2166
"upgrading-from": "cs:quantal/mysql-1",
2167
"public-address": "controller-1.dns",
2176
"application with local charm not shown as out of date",
2177
addMachine{machineId: "0", job: state.JobManageModel},
2178
setAddresses{"0", network.NewAddresses("controller-0.dns")},
2179
startAliveMachine{"0"},
2180
setMachineStatus{"0", status.StatusStarted, ""},
2181
addMachine{machineId: "1", job: state.JobHostUnits},
2182
setAddresses{"1", network.NewAddresses("controller-1.dns")},
2183
startAliveMachine{"1"},
2184
setMachineStatus{"1", status.StatusStarted, ""},
2186
addService{name: "mysql", charm: "mysql"},
2187
setServiceExposed{"mysql", true},
2188
addAliveUnit{"mysql", "1"},
2189
setUnitCharmURL{"mysql/0", "cs:quantal/mysql-1"},
2190
addCharmWithRevision{addCharm{"mysql"}, "local", 1},
2191
setServiceCharm{"mysql", "local:quantal/mysql-1"},
2192
addCharmPlaceholder{"mysql", 23},
2195
"services and units with correct charm status",
2203
"mysql": mysqlCharm(M{
2204
"charm": "local:quantal/mysql-1",
2205
"charm-origin": "local",
2207
"application-status": M{
2208
"current": "active",
2209
"since": "01 Apr 15 01:23+10:00",
2214
"workload-status": M{
2215
"current": "active",
2216
"since": "01 Apr 15 01:23+10:00",
2220
"since": "01 Apr 15 01:23+10:00",
2222
"upgrading-from": "cs:quantal/mysql-1",
2223
"public-address": "controller-1.dns",
2232
"deploy two services; set meter statuses on one",
2233
addMachine{machineId: "0", job: state.JobManageModel},
2234
setAddresses{"0", network.NewAddresses("controller-0.dns")},
2235
startAliveMachine{"0"},
2236
setMachineStatus{"0", status.StatusStarted, ""},
2238
addMachine{machineId: "1", job: state.JobHostUnits},
2239
setAddresses{"1", network.NewAddresses("controller-1.dns")},
2240
startAliveMachine{"1"},
2241
setMachineStatus{"1", status.StatusStarted, ""},
2243
addMachine{machineId: "2", job: state.JobHostUnits},
2244
setAddresses{"2", network.NewAddresses("controller-2.dns")},
2245
startAliveMachine{"2"},
2246
setMachineStatus{"2", status.StatusStarted, ""},
2248
addMachine{machineId: "3", job: state.JobHostUnits},
2249
setAddresses{"3", network.NewAddresses("controller-3.dns")},
2250
startAliveMachine{"3"},
2251
setMachineStatus{"3", status.StatusStarted, ""},
2253
addMachine{machineId: "4", job: state.JobHostUnits},
2254
setAddresses{"4", network.NewAddresses("controller-4.dns")},
2255
startAliveMachine{"4"},
2256
setMachineStatus{"4", status.StatusStarted, ""},
2259
addService{name: "mysql", charm: "mysql"},
2260
setServiceExposed{"mysql", true},
2262
addService{name: "servicewithmeterstatus", charm: "mysql"},
2264
addAliveUnit{"mysql", "1"},
2265
addAliveUnit{"servicewithmeterstatus", "2"},
2266
addAliveUnit{"servicewithmeterstatus", "3"},
2267
addAliveUnit{"servicewithmeterstatus", "4"},
2269
setServiceExposed{"mysql", true},
2271
setAgentStatus{"mysql/0", status.StatusIdle, "", nil},
2272
setUnitStatus{"mysql/0", status.StatusActive, "", nil},
2273
setAgentStatus{"servicewithmeterstatus/0", status.StatusIdle, "", nil},
2274
setUnitStatus{"servicewithmeterstatus/0", status.StatusActive, "", nil},
2275
setAgentStatus{"servicewithmeterstatus/1", status.StatusIdle, "", nil},
2276
setUnitStatus{"servicewithmeterstatus/1", status.StatusActive, "", nil},
2277
setAgentStatus{"servicewithmeterstatus/2", status.StatusIdle, "", nil},
2278
setUnitStatus{"servicewithmeterstatus/2", status.StatusActive, "", nil},
2280
setUnitMeterStatus{"servicewithmeterstatus/1", "GREEN", "test green status"},
2281
setUnitMeterStatus{"servicewithmeterstatus/2", "RED", "test red status"},
2284
"simulate just the two services and a bootstrap node",
2295
"mysql": mysqlCharm(M{
2297
"application-status": M{
2298
"current": "active",
2299
"since": "01 Apr 15 01:23+10:00",
2304
"workload-status": M{
2305
"current": "active",
2306
"since": "01 Apr 15 01:23+10:00",
2310
"since": "01 Apr 15 01:23+10:00",
2312
"public-address": "controller-1.dns",
2317
"servicewithmeterstatus": mysqlCharm(M{
2318
"application-status": M{
2319
"current": "active",
2320
"since": "01 Apr 15 01:23+10:00",
2323
"servicewithmeterstatus/0": M{
2325
"workload-status": M{
2326
"current": "active",
2327
"since": "01 Apr 15 01:23+10:00",
2331
"since": "01 Apr 15 01:23+10:00",
2333
"public-address": "controller-2.dns",
2335
"servicewithmeterstatus/1": M{
2337
"workload-status": M{
2338
"current": "active",
2339
"since": "01 Apr 15 01:23+10:00",
2343
"since": "01 Apr 15 01:23+10:00",
2347
"message": "test green status",
2349
"public-address": "controller-3.dns",
2351
"servicewithmeterstatus/2": M{
2353
"workload-status": M{
2354
"current": "active",
2355
"since": "01 Apr 15 01:23+10:00",
2359
"since": "01 Apr 15 01:23+10:00",
2363
"message": "test red status",
2365
"public-address": "controller-4.dns",
2374
"upgrade available",
2375
setToolsUpgradeAvailable{},
2377
"upgrade availability should be shown in model-status",
2380
"name": "controller",
2381
"controller": "kontroll",
2384
"upgrade-available": "1.2.4",
2387
"applications": M{},
2392
"consistent workload version",
2393
addMachine{machineId: "0", job: state.JobManageModel},
2394
setAddresses{"0", network.NewAddresses("controller-0.dns")},
2395
startAliveMachine{"0"},
2396
setMachineStatus{"0", status.StatusStarted, ""},
2399
addService{name: "mysql", charm: "mysql"},
2401
addMachine{machineId: "1", job: state.JobHostUnits},
2402
setAddresses{"1", network.NewAddresses("controller-1.dns")},
2403
startAliveMachine{"1"},
2404
setMachineStatus{"1", status.StatusStarted, ""},
2405
addAliveUnit{"mysql", "1"},
2406
setUnitWorkloadVersion{"mysql/0", "the best!"},
2409
"application and unit with correct workload version",
2417
"mysql": mysqlCharm(M{
2418
"version": "the best!",
2419
"application-status": M{
2420
"current": "unknown",
2421
"message": "Waiting for agent initialization to finish",
2422
"since": "01 Apr 15 01:23+10:00",
2427
"workload-status": M{
2428
"current": "unknown",
2429
"message": "Waiting for agent initialization to finish",
2430
"since": "01 Apr 15 01:23+10:00",
2433
"current": "allocating",
2434
"since": "01 Apr 15 01:23+10:00",
2436
"public-address": "controller-1.dns",
2445
"mixed workload version",
2446
addMachine{machineId: "0", job: state.JobManageModel},
2447
setAddresses{"0", network.NewAddresses("controller-0.dns")},
2448
startAliveMachine{"0"},
2449
setMachineStatus{"0", status.StatusStarted, ""},
2452
addService{name: "mysql", charm: "mysql"},
2454
addMachine{machineId: "1", job: state.JobHostUnits},
2455
setAddresses{"1", network.NewAddresses("controller-1.dns")},
2456
startAliveMachine{"1"},
2457
setMachineStatus{"1", status.StatusStarted, ""},
2458
addAliveUnit{"mysql", "1"},
2459
setUnitWorkloadVersion{"mysql/0", "the best!"},
2461
addMachine{machineId: "2", job: state.JobHostUnits},
2462
setAddresses{"2", network.NewAddresses("controller-2.dns")},
2463
startAliveMachine{"2"},
2464
setMachineStatus{"2", status.StatusStarted, ""},
2465
addAliveUnit{"mysql", "2"},
2466
setUnitWorkloadVersion{"mysql/1", "not as good"},
2469
"application and unit with correct workload version",
2478
"mysql": mysqlCharm(M{
2479
"version": "not as good",
2480
"application-status": M{
2481
"current": "unknown",
2482
"message": "Waiting for agent initialization to finish",
2483
"since": "01 Apr 15 01:23+10:00",
2488
"workload-status": M{
2489
"current": "unknown",
2490
"message": "Waiting for agent initialization to finish",
2491
"since": "01 Apr 15 01:23+10:00",
2494
"current": "allocating",
2495
"since": "01 Apr 15 01:23+10:00",
2497
"public-address": "controller-1.dns",
2501
"workload-status": M{
2502
"current": "unknown",
2503
"message": "Waiting for agent initialization to finish",
2504
"since": "01 Apr 15 01:23+10:00",
2507
"current": "allocating",
2508
"since": "01 Apr 15 01:23+10:00",
2510
"public-address": "controller-2.dns",
2520
func mysqlCharm(extras M) M {
2522
"charm": "cs:quantal/mysql-1",
2523
"charm-origin": "jujucharms",
2524
"charm-name": "mysql",
2526
"series": "quantal",
2530
for key, value := range extras {
2536
func dummyCharm(extras M) M {
2538
"charm": "cs:quantal/dummy-1",
2539
"charm-origin": "jujucharms",
2540
"charm-name": "dummy",
2542
"series": "quantal",
2546
for key, value := range extras {
2552
func wordpressCharm(extras M) M {
2554
"charm": "cs:quantal/wordpress-3",
2555
"charm-origin": "jujucharms",
2556
"charm-name": "wordpress",
2558
"series": "quantal",
2562
for key, value := range extras {
2568
// TODO(dfc) test failing components by destructively mutating the state under the hood
2570
type addMachine struct {
2572
cons constraints.Value
2573
job state.MachineJob
2576
func (am addMachine) step(c *gc.C, ctx *context) {
2577
m, err := ctx.st.AddOneMachine(state.MachineTemplate{
2579
Constraints: am.cons,
2580
Jobs: []state.MachineJob{am.job},
2582
c.Assert(err, jc.ErrorIsNil)
2583
c.Assert(m.Id(), gc.Equals, am.machineId)
2586
type addContainer struct {
2589
job state.MachineJob
2592
func (ac addContainer) step(c *gc.C, ctx *context) {
2593
template := state.MachineTemplate{
2595
Jobs: []state.MachineJob{ac.job},
2597
m, err := ctx.st.AddMachineInsideMachine(template, ac.parentId, instance.LXD)
2598
c.Assert(err, jc.ErrorIsNil)
2599
c.Assert(m.Id(), gc.Equals, ac.machineId)
2602
type startMachine struct {
2606
func (sm startMachine) step(c *gc.C, ctx *context) {
2607
m, err := ctx.st.Machine(sm.machineId)
2608
c.Assert(err, jc.ErrorIsNil)
2609
cons, err := m.Constraints()
2610
c.Assert(err, jc.ErrorIsNil)
2611
cfg, err := ctx.st.ControllerConfig()
2612
c.Assert(err, jc.ErrorIsNil)
2613
inst, hc := testing.AssertStartInstanceWithConstraints(c, ctx.env, cfg.ControllerUUID(), m.Id(), cons)
2614
err = m.SetProvisioned(inst.Id(), "fake_nonce", hc)
2615
c.Assert(err, jc.ErrorIsNil)
2618
type startMissingMachine struct {
2622
func (sm startMissingMachine) step(c *gc.C, ctx *context) {
2623
m, err := ctx.st.Machine(sm.machineId)
2624
c.Assert(err, jc.ErrorIsNil)
2625
cons, err := m.Constraints()
2626
c.Assert(err, jc.ErrorIsNil)
2627
cfg, err := ctx.st.ControllerConfig()
2628
c.Assert(err, jc.ErrorIsNil)
2629
_, hc := testing.AssertStartInstanceWithConstraints(c, ctx.env, cfg.ControllerUUID(), m.Id(), cons)
2630
err = m.SetProvisioned("i-missing", "fake_nonce", hc)
2631
c.Assert(err, jc.ErrorIsNil)
2634
s := status.StatusInfo{
2635
Status: status.StatusUnknown,
2639
err = m.SetInstanceStatus(s)
2640
c.Assert(err, jc.ErrorIsNil)
2643
type startAliveMachine struct {
2647
func (sam startAliveMachine) step(c *gc.C, ctx *context) {
2648
m, err := ctx.st.Machine(sam.machineId)
2649
c.Assert(err, jc.ErrorIsNil)
2650
pinger := ctx.setAgentPresence(c, m)
2651
cons, err := m.Constraints()
2652
c.Assert(err, jc.ErrorIsNil)
2653
cfg, err := ctx.st.ControllerConfig()
2654
c.Assert(err, jc.ErrorIsNil)
2655
inst, hc := testing.AssertStartInstanceWithConstraints(c, ctx.env, cfg.ControllerUUID(), m.Id(), cons)
2656
err = m.SetProvisioned(inst.Id(), "fake_nonce", hc)
2657
c.Assert(err, jc.ErrorIsNil)
2658
ctx.pingers[m.Id()] = pinger
2661
type startMachineWithHardware struct {
2663
hc instance.HardwareCharacteristics
2666
func (sm startMachineWithHardware) step(c *gc.C, ctx *context) {
2667
m, err := ctx.st.Machine(sm.machineId)
2668
c.Assert(err, jc.ErrorIsNil)
2669
pinger := ctx.setAgentPresence(c, m)
2670
cons, err := m.Constraints()
2671
c.Assert(err, jc.ErrorIsNil)
2672
cfg, err := ctx.st.ControllerConfig()
2673
c.Assert(err, jc.ErrorIsNil)
2674
inst, _ := testing.AssertStartInstanceWithConstraints(c, ctx.env, cfg.ControllerUUID(), m.Id(), cons)
2675
err = m.SetProvisioned(inst.Id(), "fake_nonce", &sm.hc)
2676
c.Assert(err, jc.ErrorIsNil)
2677
ctx.pingers[m.Id()] = pinger
2680
type setAddresses struct {
2682
addresses []network.Address
2685
func (sa setAddresses) step(c *gc.C, ctx *context) {
2686
m, err := ctx.st.Machine(sa.machineId)
2687
c.Assert(err, jc.ErrorIsNil)
2688
err = m.SetProviderAddresses(sa.addresses...)
2689
c.Assert(err, jc.ErrorIsNil)
2692
type setTools struct {
2694
version version.Binary
2697
func (st setTools) step(c *gc.C, ctx *context) {
2698
m, err := ctx.st.Machine(st.machineId)
2699
c.Assert(err, jc.ErrorIsNil)
2700
err = m.SetAgentVersion(st.version)
2701
c.Assert(err, jc.ErrorIsNil)
2704
type setUnitTools struct {
2706
version version.Binary
2709
func (st setUnitTools) step(c *gc.C, ctx *context) {
2710
m, err := ctx.st.Unit(st.unitName)
2711
c.Assert(err, jc.ErrorIsNil)
2712
err = m.SetAgentVersion(st.version)
2713
c.Assert(err, jc.ErrorIsNil)
2716
type addCharm struct {
2720
func (ac addCharm) addCharmStep(c *gc.C, ctx *context, scheme string, rev int) {
2721
ch := testcharms.Repo.CharmDir(ac.name)
2722
name := ch.Meta().Name
2723
curl := charm.MustParseURL(fmt.Sprintf("%s:quantal/%s-%d", scheme, name, rev))
2724
info := state.CharmInfo{
2727
StoragePath: "dummy-path",
2728
SHA256: fmt.Sprintf("%s-%d-sha256", name, rev),
2730
dummy, err := ctx.st.AddCharm(info)
2731
c.Assert(err, jc.ErrorIsNil)
2732
ctx.charms[ac.name] = dummy
2735
func (ac addCharm) step(c *gc.C, ctx *context) {
2736
ch := testcharms.Repo.CharmDir(ac.name)
2737
ac.addCharmStep(c, ctx, "cs", ch.Revision())
2740
type addCharmWithRevision struct {
2746
func (ac addCharmWithRevision) step(c *gc.C, ctx *context) {
2747
ac.addCharmStep(c, ctx, ac.scheme, ac.rev)
2750
type addService struct {
2753
cons constraints.Value
2756
func (as addService) step(c *gc.C, ctx *context) {
2757
ch, ok := ctx.charms[as.charm]
2758
c.Assert(ok, jc.IsTrue)
2759
svc, err := ctx.st.AddApplication(state.AddApplicationArgs{Name: as.name, Charm: ch})
2760
c.Assert(err, jc.ErrorIsNil)
2761
if svc.IsPrincipal() {
2762
err = svc.SetConstraints(as.cons)
2763
c.Assert(err, jc.ErrorIsNil)
2767
type setServiceExposed struct {
2772
func (sse setServiceExposed) step(c *gc.C, ctx *context) {
2773
s, err := ctx.st.Application(sse.name)
2774
c.Assert(err, jc.ErrorIsNil)
2775
err = s.ClearExposed()
2776
c.Assert(err, jc.ErrorIsNil)
2778
err = s.SetExposed()
2779
c.Assert(err, jc.ErrorIsNil)
2783
type setServiceCharm struct {
2788
func (ssc setServiceCharm) step(c *gc.C, ctx *context) {
2789
ch, err := ctx.st.Charm(charm.MustParseURL(ssc.charm))
2790
c.Assert(err, jc.ErrorIsNil)
2791
s, err := ctx.st.Application(ssc.name)
2792
c.Assert(err, jc.ErrorIsNil)
2793
cfg := state.SetCharmConfig{Charm: ch}
2794
err = s.SetCharm(cfg)
2795
c.Assert(err, jc.ErrorIsNil)
2798
type addCharmPlaceholder struct {
2803
func (ac addCharmPlaceholder) step(c *gc.C, ctx *context) {
2804
ch := testcharms.Repo.CharmDir(ac.name)
2805
name := ch.Meta().Name
2806
curl := charm.MustParseURL(fmt.Sprintf("cs:quantal/%s-%d", name, ac.rev))
2807
err := ctx.st.AddStoreCharmPlaceholder(curl)
2808
c.Assert(err, jc.ErrorIsNil)
2811
type addUnit struct {
2816
func (au addUnit) step(c *gc.C, ctx *context) {
2817
s, err := ctx.st.Application(au.serviceName)
2818
c.Assert(err, jc.ErrorIsNil)
2819
u, err := s.AddUnit()
2820
c.Assert(err, jc.ErrorIsNil)
2821
m, err := ctx.st.Machine(au.machineId)
2822
c.Assert(err, jc.ErrorIsNil)
2823
err = u.AssignToMachine(m)
2824
c.Assert(err, jc.ErrorIsNil)
2827
type addAliveUnit struct {
2832
func (aau addAliveUnit) step(c *gc.C, ctx *context) {
2833
s, err := ctx.st.Application(aau.serviceName)
2834
c.Assert(err, jc.ErrorIsNil)
2835
u, err := s.AddUnit()
2836
c.Assert(err, jc.ErrorIsNil)
2837
pinger := ctx.setAgentPresence(c, u)
2838
m, err := ctx.st.Machine(aau.machineId)
2839
c.Assert(err, jc.ErrorIsNil)
2840
err = u.AssignToMachine(m)
2841
c.Assert(err, jc.ErrorIsNil)
2842
ctx.pingers[u.Name()] = pinger
2845
type setUnitsAlive struct {
2849
func (sua setUnitsAlive) step(c *gc.C, ctx *context) {
2850
s, err := ctx.st.Application(sua.serviceName)
2851
c.Assert(err, jc.ErrorIsNil)
2852
us, err := s.AllUnits()
2853
c.Assert(err, jc.ErrorIsNil)
2854
for _, u := range us {
2855
ctx.pingers[u.Name()] = ctx.setAgentPresence(c, u)
2859
type setUnitMeterStatus struct {
2865
func (s setUnitMeterStatus) step(c *gc.C, ctx *context) {
2866
u, err := ctx.st.Unit(s.unitName)
2867
c.Assert(err, jc.ErrorIsNil)
2868
err = u.SetMeterStatus(s.color, s.message)
2869
c.Assert(err, jc.ErrorIsNil)
2872
type setUnitStatus struct {
2874
status status.Status
2876
statusData map[string]interface{}
2879
func (sus setUnitStatus) step(c *gc.C, ctx *context) {
2880
u, err := ctx.st.Unit(sus.unitName)
2881
c.Assert(err, jc.ErrorIsNil)
2884
s := status.StatusInfo{
2886
Message: sus.statusInfo,
2887
Data: sus.statusData,
2890
err = u.SetStatus(s)
2891
c.Assert(err, jc.ErrorIsNil)
2894
type setAgentStatus struct {
2896
status status.Status
2898
statusData map[string]interface{}
2901
func (sus setAgentStatus) step(c *gc.C, ctx *context) {
2902
u, err := ctx.st.Unit(sus.unitName)
2903
c.Assert(err, jc.ErrorIsNil)
2906
sInfo := status.StatusInfo{
2908
Message: sus.statusInfo,
2909
Data: sus.statusData,
2912
err = u.SetAgentStatus(sInfo)
2913
c.Assert(err, jc.ErrorIsNil)
2916
type setUnitCharmURL struct {
2921
func (uc setUnitCharmURL) step(c *gc.C, ctx *context) {
2922
u, err := ctx.st.Unit(uc.unitName)
2923
c.Assert(err, jc.ErrorIsNil)
2924
curl := charm.MustParseURL(uc.charm)
2925
err = u.SetCharmURL(curl)
2926
c.Assert(err, jc.ErrorIsNil)
2929
s := status.StatusInfo{
2930
Status: status.StatusActive,
2934
err = u.SetStatus(s)
2935
c.Assert(err, jc.ErrorIsNil)
2936
sInfo := status.StatusInfo{
2937
Status: status.StatusIdle,
2941
err = u.SetAgentStatus(sInfo)
2942
c.Assert(err, jc.ErrorIsNil)
2946
type setUnitWorkloadVersion struct {
2951
func (wv setUnitWorkloadVersion) step(c *gc.C, ctx *context) {
2952
u, err := ctx.st.Unit(wv.unitName)
2953
c.Assert(err, jc.ErrorIsNil)
2954
err = u.SetWorkloadVersion(wv.version)
2955
c.Assert(err, jc.ErrorIsNil)
2958
type openUnitPort struct {
2964
func (oup openUnitPort) step(c *gc.C, ctx *context) {
2965
u, err := ctx.st.Unit(oup.unitName)
2966
c.Assert(err, jc.ErrorIsNil)
2967
err = u.OpenPort(oup.protocol, oup.number)
2968
c.Assert(err, jc.ErrorIsNil)
2971
type ensureDyingUnit struct {
2975
func (e ensureDyingUnit) step(c *gc.C, ctx *context) {
2976
u, err := ctx.st.Unit(e.unitName)
2977
c.Assert(err, jc.ErrorIsNil)
2979
c.Assert(err, jc.ErrorIsNil)
2980
c.Assert(u.Life(), gc.Equals, state.Dying)
2983
type ensureDyingService struct {
2987
func (e ensureDyingService) step(c *gc.C, ctx *context) {
2988
svc, err := ctx.st.Application(e.serviceName)
2989
c.Assert(err, jc.ErrorIsNil)
2991
c.Assert(err, jc.ErrorIsNil)
2993
c.Assert(err, jc.ErrorIsNil)
2994
c.Assert(svc.Life(), gc.Equals, state.Dying)
2997
type ensureDeadMachine struct {
3001
func (e ensureDeadMachine) step(c *gc.C, ctx *context) {
3002
m, err := ctx.st.Machine(e.machineId)
3003
c.Assert(err, jc.ErrorIsNil)
3004
err = m.EnsureDead()
3005
c.Assert(err, jc.ErrorIsNil)
3006
c.Assert(m.Life(), gc.Equals, state.Dead)
3009
type setMachineStatus struct {
3011
status status.Status
3015
func (sms setMachineStatus) step(c *gc.C, ctx *context) {
3018
m, err := ctx.st.Machine(sms.machineId)
3019
c.Assert(err, jc.ErrorIsNil)
3020
sInfo := status.StatusInfo{
3022
Message: sms.statusInfo,
3025
err = m.SetStatus(sInfo)
3026
c.Assert(err, jc.ErrorIsNil)
3029
type relateServices struct {
3033
func (rs relateServices) step(c *gc.C, ctx *context) {
3034
eps, err := ctx.st.InferEndpoints(rs.ep1, rs.ep2)
3035
c.Assert(err, jc.ErrorIsNil)
3036
_, err = ctx.st.AddRelation(eps...)
3037
c.Assert(err, jc.ErrorIsNil)
3040
type addSubordinate struct {
3045
func (as addSubordinate) step(c *gc.C, ctx *context) {
3046
u, err := ctx.st.Unit(as.prinUnit)
3047
c.Assert(err, jc.ErrorIsNil)
3048
eps, err := ctx.st.InferEndpoints(u.ApplicationName(), as.subService)
3049
c.Assert(err, jc.ErrorIsNil)
3050
rel, err := ctx.st.EndpointsRelation(eps...)
3051
c.Assert(err, jc.ErrorIsNil)
3052
ru, err := rel.Unit(u)
3053
c.Assert(err, jc.ErrorIsNil)
3054
err = ru.EnterScope(nil)
3055
c.Assert(err, jc.ErrorIsNil)
3058
type scopedExpect struct {
3064
type expect struct {
3069
// substituteFakeTime replaces all "since" values
3070
// in actual status output with a known fake value.
3071
func substituteFakeSinceTime(c *gc.C, in []byte, expectIsoTime bool) []byte {
3072
// This regexp will work for yaml and json.
3073
exp := regexp.MustCompile(`(?P<since>"?since"?:\ ?)(?P<quote>"?)(?P<timestamp>[^("|\n)]*)*"?`)
3074
// Before the substritution is done, check that the timestamp produced
3075
// by status is in the correct format.
3076
if matches := exp.FindStringSubmatch(string(in)); matches != nil {
3077
for i, name := range exp.SubexpNames() {
3078
if name != "timestamp" {
3081
timeFormat := "02 Jan 2006 15:04:05Z07:00"
3083
timeFormat = "2006-01-02 15:04:05Z"
3085
_, err := time.Parse(timeFormat, matches[i])
3086
c.Assert(err, jc.ErrorIsNil)
3090
out := exp.ReplaceAllString(string(in), `$since$quote<timestamp>$quote`)
3091
// Substitute a made up time used in our expected output.
3092
out = strings.Replace(out, "<timestamp>", "01 Apr 15 01:23+10:00", -1)
3096
func (e scopedExpect) step(c *gc.C, ctx *context) {
3097
c.Logf("\nexpect: %s %s\n", e.what, strings.Join(e.scope, " "))
3099
// Now execute the command for each format.
3100
for _, format := range statusFormats {
3101
c.Logf("format %q", format.name)
3102
// Run command with the required format.
3103
args := []string{"--format", format.name}
3104
if ctx.expectIsoTime {
3105
args = append(args, "--utc")
3107
args = append(args, e.scope...)
3108
c.Logf("running status %s", strings.Join(args, " "))
3109
code, stdout, stderr := runStatus(c, args...)
3110
c.Assert(code, gc.Equals, 0)
3111
if !c.Check(stderr, gc.HasLen, 0) {
3112
c.Fatalf("status failed: %s", string(stderr))
3115
// Prepare the output in the same format.
3116
buf, err := format.marshal(e.output)
3117
c.Assert(err, jc.ErrorIsNil)
3119
err = format.unmarshal(buf, &expected)
3120
c.Assert(err, jc.ErrorIsNil)
3122
// Check the output is as expected.
3124
out := substituteFakeSinceTime(c, stdout, ctx.expectIsoTime)
3125
err = format.unmarshal(out, &actual)
3126
c.Assert(err, jc.ErrorIsNil)
3127
c.Assert(actual, jc.DeepEquals, expected)
3131
func (e expect) step(c *gc.C, ctx *context) {
3132
scopedExpect{e.what, nil, e.output}.step(c, ctx)
3135
type setToolsUpgradeAvailable struct{}
3137
func (ua setToolsUpgradeAvailable) step(c *gc.C, ctx *context) {
3138
env, err := ctx.st.Model()
3139
c.Assert(err, jc.ErrorIsNil)
3140
err = env.UpdateLatestToolsVersion(nextVersion)
3141
c.Assert(err, jc.ErrorIsNil)
3144
func (s *StatusSuite) TestStatusAllFormats(c *gc.C) {
3145
for i, t := range statusTests {
3146
c.Logf("test %d: %s", i, t.summary)
3148
// Prepare context and run all steps to setup.
3149
ctx := s.newContext(c)
3150
defer s.resetContext(c, ctx)
3156
func (s *StatusSuite) TestMigrationInProgress(c *gc.C) {
3157
// This test isn't part of statusTests because migrations can't be
3158
// run on controller models.
3160
const hostedModelName = "hosted"
3161
const statusText = "foo bar"
3163
f := factory.NewFactory(s.BackingState)
3164
hostedSt := f.MakeModel(c, &factory.ModelParams{
3165
Name: hostedModelName,
3167
defer hostedSt.Close()
3169
mig, err := hostedSt.CreateModelMigration(state.ModelMigrationSpec{
3170
InitiatedBy: names.NewUserTag("admin"),
3171
TargetInfo: migration.TargetInfo{
3172
ControllerTag: names.NewModelTag(utils.MustNewUUID().String()),
3173
Addrs: []string{"1.2.3.4:5555", "4.3.2.1:6666"},
3175
AuthTag: names.NewUserTag("user"),
3176
Password: "password",
3179
c.Assert(err, jc.ErrorIsNil)
3180
err = mig.SetStatusMessage(statusText)
3181
c.Assert(err, jc.ErrorIsNil)
3185
"name": hostedModelName,
3186
"controller": "kontroll",
3189
"migration": statusText,
3192
"applications": M{},
3195
for _, format := range statusFormats {
3196
code, stdout, stderr := runStatus(c, "-m", hostedModelName, "--format", format.name)
3197
c.Check(code, gc.Equals, 0)
3198
c.Assert(stderr, gc.HasLen, 0, gc.Commentf("status failed: %s", stderr))
3200
// Roundtrip expected through format so that types will match.
3201
buf, err := format.marshal(expected)
3202
c.Assert(err, jc.ErrorIsNil)
3203
var expectedForFormat M
3204
err = format.unmarshal(buf, &expectedForFormat)
3205
c.Assert(err, jc.ErrorIsNil)
3208
c.Assert(format.unmarshal(stdout, &actual), jc.ErrorIsNil)
3209
c.Check(actual, jc.DeepEquals, expectedForFormat)
3213
type fakeApiClient struct {
3214
statusReturn *params.FullStatus
3215
patternsUsed []string
3219
func (a *fakeApiClient) Status(patterns []string) (*params.FullStatus, error) {
3220
a.patternsUsed = patterns
3221
return a.statusReturn, nil
3224
func (a *fakeApiClient) Close() error {
3225
a.closeCalled = true
3229
func (s *StatusSuite) TestStatusWithFormatSummary(c *gc.C) {
3230
ctx := s.newContext(c)
3231
defer s.resetContext(c, ctx)
3233
addMachine{machineId: "0", job: state.JobManageModel},
3234
setAddresses{"0", network.NewAddresses("localhost")},
3235
startAliveMachine{"0"},
3236
setMachineStatus{"0", status.StatusStarted, ""},
3237
addCharm{"wordpress"},
3239
addCharm{"logging"},
3240
addService{name: "wordpress", charm: "wordpress"},
3241
setServiceExposed{"wordpress", true},
3242
addMachine{machineId: "1", job: state.JobHostUnits},
3243
setAddresses{"1", network.NewAddresses("localhost")},
3244
startAliveMachine{"1"},
3245
setMachineStatus{"1", status.StatusStarted, ""},
3246
addAliveUnit{"wordpress", "1"},
3247
setAgentStatus{"wordpress/0", status.StatusIdle, "", nil},
3248
setUnitStatus{"wordpress/0", status.StatusActive, "", nil},
3249
addService{name: "mysql", charm: "mysql"},
3250
setServiceExposed{"mysql", true},
3251
addMachine{machineId: "2", job: state.JobHostUnits},
3252
setAddresses{"2", network.NewAddresses("10.0.0.1")},
3253
startAliveMachine{"2"},
3254
setMachineStatus{"2", status.StatusStarted, ""},
3255
addAliveUnit{"mysql", "2"},
3256
setAgentStatus{"mysql/0", status.StatusIdle, "", nil},
3257
setUnitStatus{"mysql/0", status.StatusActive, "", nil},
3258
addService{name: "logging", charm: "logging"},
3259
setServiceExposed{"logging", true},
3260
relateServices{"wordpress", "mysql"},
3261
relateServices{"wordpress", "logging"},
3262
relateServices{"mysql", "logging"},
3263
addSubordinate{"wordpress/0", "logging"},
3264
addSubordinate{"mysql/0", "logging"},
3265
setUnitsAlive{"logging"},
3266
setAgentStatus{"logging/0", status.StatusIdle, "", nil},
3267
setUnitStatus{"logging/0", status.StatusActive, "", nil},
3268
setAgentStatus{"logging/1", status.StatusError, "somehow lost in all those logs", nil},
3270
for _, s := range steps {
3273
code, stdout, stderr := runStatus(c, "--format", "summary")
3274
c.Check(code, gc.Equals, 0)
3275
c.Check(string(stderr), gc.Equals, "")
3276
c.Assert(string(stdout), gc.Equals, `
3277
Running on subnets: 127.0.0.1/8, 10.0.0.1/8
3289
wordpress 1/1 exposed
3293
func (s *StatusSuite) TestStatusWithFormatOneline(c *gc.C) {
3294
ctx := s.newContext(c)
3295
defer s.resetContext(c, ctx)
3297
addMachine{machineId: "0", job: state.JobManageModel},
3298
setAddresses{"0", network.NewAddresses("controller-0.dns")},
3299
startAliveMachine{"0"},
3300
setMachineStatus{"0", status.StatusStarted, ""},
3301
addCharm{"wordpress"},
3303
addCharm{"logging"},
3305
addService{name: "wordpress", charm: "wordpress"},
3306
setServiceExposed{"wordpress", true},
3307
addMachine{machineId: "1", job: state.JobHostUnits},
3308
setAddresses{"1", network.NewAddresses("controller-1.dns")},
3309
startAliveMachine{"1"},
3310
setMachineStatus{"1", status.StatusStarted, ""},
3311
addAliveUnit{"wordpress", "1"},
3312
setAgentStatus{"wordpress/0", status.StatusIdle, "", nil},
3313
setUnitStatus{"wordpress/0", status.StatusActive, "", nil},
3315
addService{name: "mysql", charm: "mysql"},
3316
setServiceExposed{"mysql", true},
3317
addMachine{machineId: "2", job: state.JobHostUnits},
3318
setAddresses{"2", network.NewAddresses("controller-2.dns")},
3319
startAliveMachine{"2"},
3320
setMachineStatus{"2", status.StatusStarted, ""},
3321
addAliveUnit{"mysql", "2"},
3322
setAgentStatus{"mysql/0", status.StatusIdle, "", nil},
3323
setUnitStatus{"mysql/0", status.StatusActive, "", nil},
3325
addService{name: "logging", charm: "logging"},
3326
setServiceExposed{"logging", true},
3328
relateServices{"wordpress", "mysql"},
3329
relateServices{"wordpress", "logging"},
3330
relateServices{"mysql", "logging"},
3332
addSubordinate{"wordpress/0", "logging"},
3333
addSubordinate{"mysql/0", "logging"},
3335
setUnitsAlive{"logging"},
3336
setAgentStatus{"logging/0", status.StatusIdle, "", nil},
3337
setUnitStatus{"logging/0", status.StatusActive, "", nil},
3338
setAgentStatus{"logging/1", status.StatusError, "somehow lost in all those logs", nil},
3344
- mysql/0: controller-2.dns (agent:idle, workload:active)
3345
- logging/1: controller-2.dns (agent:idle, workload:error)
3346
- wordpress/0: controller-1.dns (agent:idle, workload:active)
3347
- logging/0: controller-1.dns (agent:idle, workload:active)
3349
assertOneLineStatus(c, expected)
3352
func assertOneLineStatus(c *gc.C, expected string) {
3353
code, stdout, stderr := runStatus(c, "--format", "oneline")
3354
c.Check(code, gc.Equals, 0)
3355
c.Check(string(stderr), gc.Equals, "")
3356
c.Assert(string(stdout), gc.Equals, expected)
3358
c.Log(`Check that "short" is an alias for oneline.`)
3359
code, stdout, stderr = runStatus(c, "--format", "short")
3360
c.Check(code, gc.Equals, 0)
3361
c.Check(string(stderr), gc.Equals, "")
3362
c.Assert(string(stdout), gc.Equals, expected)
3364
c.Log(`Check that "line" is an alias for oneline.`)
3365
code, stdout, stderr = runStatus(c, "--format", "line")
3366
c.Check(code, gc.Equals, 0)
3367
c.Check(string(stderr), gc.Equals, "")
3368
c.Assert(string(stdout), gc.Equals, expected)
3371
func (s *StatusSuite) prepareTabularData(c *gc.C) *context {
3372
ctx := s.newContext(c)
3374
setToolsUpgradeAvailable{},
3375
addMachine{machineId: "0", job: state.JobManageModel},
3376
setAddresses{"0", network.NewAddresses("controller-0.dns")},
3377
startMachineWithHardware{"0", instance.MustParseHardware("availability-zone=us-east-1a")},
3378
setMachineStatus{"0", status.StatusStarted, ""},
3379
addCharm{"wordpress"},
3381
addCharm{"logging"},
3382
addService{name: "wordpress", charm: "wordpress"},
3383
setServiceExposed{"wordpress", true},
3384
addMachine{machineId: "1", job: state.JobHostUnits},
3385
setAddresses{"1", network.NewAddresses("controller-1.dns")},
3386
startAliveMachine{"1"},
3387
setMachineStatus{"1", status.StatusStarted, ""},
3388
addAliveUnit{"wordpress", "1"},
3389
setAgentStatus{"wordpress/0", status.StatusIdle, "", nil},
3390
setUnitStatus{"wordpress/0", status.StatusActive, "", nil},
3391
setUnitTools{"wordpress/0", version.MustParseBinary("1.2.3-trusty-ppc")},
3392
addService{name: "mysql", charm: "mysql"},
3393
setServiceExposed{"mysql", true},
3394
addMachine{machineId: "2", job: state.JobHostUnits},
3395
setAddresses{"2", network.NewAddresses("controller-2.dns")},
3396
startAliveMachine{"2"},
3397
setMachineStatus{"2", status.StatusStarted, ""},
3398
addAliveUnit{"mysql", "2"},
3399
setAgentStatus{"mysql/0", status.StatusIdle, "", nil},
3402
status.StatusMaintenance,
3403
"installing all the things", nil},
3404
setUnitTools{"mysql/0", version.MustParseBinary("1.2.3-trusty-ppc")},
3405
addService{name: "logging", charm: "logging"},
3406
setServiceExposed{"logging", true},
3407
relateServices{"wordpress", "mysql"},
3408
relateServices{"wordpress", "logging"},
3409
relateServices{"mysql", "logging"},
3410
addSubordinate{"wordpress/0", "logging"},
3411
addSubordinate{"mysql/0", "logging"},
3412
setUnitsAlive{"logging"},
3413
setAgentStatus{"logging/0", status.StatusIdle, "", nil},
3414
setUnitStatus{"logging/0", status.StatusActive, "", nil},
3415
setAgentStatus{"logging/1", status.StatusError, "somehow lost in all those logs", nil},
3416
setUnitWorkloadVersion{"logging/1", "a bit too long, really"},
3417
setUnitWorkloadVersion{"wordpress/0", "4.5.3"},
3418
setUnitWorkloadVersion{"mysql/0", "5.7.13"},
3420
for _, s := range steps {
3426
func (s *StatusSuite) testStatusWithFormatTabular(c *gc.C, useFeatureFlag bool) {
3427
ctx := s.prepareTabularData(c)
3428
defer s.resetContext(c, ctx)
3430
if !useFeatureFlag {
3431
args = []string{"--format", "tabular"}
3433
code, stdout, stderr := runStatus(c, args...)
3434
c.Check(code, gc.Equals, 0)
3435
c.Check(string(stderr), gc.Equals, "")
3437
MODEL CONTROLLER CLOUD/REGION VERSION UPGRADE-AVAILABLE
3438
controller kontroll dummy 1.2.3 1.2.4
3440
APP VERSION STATUS EXPOSED ORIGIN CHARM REV OS
3441
logging a bi... true jujucharms logging 1 ubuntu
3442
mysql 5.7.13 maintenance true jujucharms mysql 1 ubuntu
3443
wordpress 4.5.3 active true jujucharms wordpress 3 ubuntu
3445
RELATION PROVIDES CONSUMES TYPE
3446
juju-info logging mysql regular
3447
logging-dir logging wordpress regular
3448
info mysql logging subordinate
3449
db mysql wordpress regular
3450
logging-directory wordpress logging subordinate
3452
UNIT WORKLOAD AGENT MACHINE PUBLIC-ADDRESS PORTS MESSAGE
3453
mysql/0 maintenance idle 2 controller-2.dns installing all the things
3454
logging/1 error idle controller-2.dns somehow lost in all those logs
3455
wordpress/0 active idle 1 controller-1.dns
3456
logging/0 active idle controller-1.dns
3458
MACHINE STATE DNS INS-ID SERIES AZ
3459
0 started controller-0.dns controller-0 quantal us-east-1a
3460
1 started controller-1.dns controller-1 quantal
3461
2 started controller-2.dns controller-2 quantal
3464
c.Assert(string(stdout), gc.Equals, expected)
3467
func (s *StatusSuite) TestStatusWithFormatTabular(c *gc.C) {
3468
s.testStatusWithFormatTabular(c, false)
3471
func (s *StatusSuite) TestFormatTabularHookActionName(c *gc.C) {
3472
status := formattedStatus{
3473
Applications: map[string]applicationStatus{
3475
Units: map[string]unitStatus{
3477
JujuStatusInfo: statusInfoContents{
3478
Current: status.StatusExecuting,
3479
Message: "running config-changed hook",
3481
WorkloadStatusInfo: statusInfoContents{
3482
Current: status.StatusMaintenance,
3483
Message: "doing some work",
3487
JujuStatusInfo: statusInfoContents{
3488
Current: status.StatusExecuting,
3489
Message: "running action backup database",
3491
WorkloadStatusInfo: statusInfoContents{
3492
Current: status.StatusMaintenance,
3493
Message: "doing some work",
3500
out, err := FormatTabular(status)
3501
c.Assert(err, jc.ErrorIsNil)
3502
c.Assert(string(out), gc.Equals, `
3503
MODEL CONTROLLER CLOUD/REGION VERSION
3506
APP VERSION STATUS EXPOSED ORIGIN CHARM REV OS
3509
UNIT WORKLOAD AGENT MACHINE PUBLIC-ADDRESS PORTS MESSAGE
3510
foo/0 maintenance executing (config-changed) doing some work
3511
foo/1 maintenance executing (backup database) doing some work
3513
MACHINE STATE DNS INS-ID SERIES AZ
3517
func (s *StatusSuite) TestFormatTabularConsistentPeerRelationName(c *gc.C) {
3518
status := formattedStatus{
3519
Applications: map[string]applicationStatus{
3521
Relations: map[string][]string{
3522
"coordinator": {"foo"},
3523
"frobulator": {"foo"},
3524
"encapsulator": {"foo"},
3525
"catchulator": {"foo"},
3526
"perforator": {"foo"},
3527
"deliverator": {"foo"},
3528
"replicator": {"foo"},
3533
out, err := FormatTabular(status)
3534
c.Assert(err, jc.ErrorIsNil)
3535
sections, err := splitTableSections(out)
3536
c.Assert(err, jc.ErrorIsNil)
3537
c.Assert(sections["RELATION"], gc.DeepEquals, []string{
3538
"RELATION PROVIDES CONSUMES TYPE",
3539
"replicator foo foo peer",
3543
func (s *StatusSuite) TestStatusWithNilStatusApi(c *gc.C) {
3544
ctx := s.newContext(c)
3545
defer s.resetContext(c, ctx)
3547
addMachine{machineId: "0", job: state.JobManageModel},
3548
setAddresses{"0", network.NewAddresses("controller-0.dns")},
3549
startAliveMachine{"0"},
3550
setMachineStatus{"0", status.StatusStarted, ""},
3553
for _, s := range steps {
3557
client := fakeApiClient{}
3558
var status = client.Status
3559
s.PatchValue(&status, func(_ []string) (*params.FullStatus, error) {
3562
s.PatchValue(&newApiClientForStatus, func(_ *statusCommand) (statusAPI, error) {
3566
code, _, stderr := runStatus(c, "--format", "tabular")
3567
c.Check(code, gc.Equals, 1)
3568
c.Check(string(stderr), gc.Equals, "error: unable to obtain the current status\n")
3571
func (s *StatusSuite) TestFormatTabularMetering(c *gc.C) {
3572
status := formattedStatus{
3573
Applications: map[string]applicationStatus{
3575
Units: map[string]unitStatus{
3577
MeterStatus: &meterStatus{
3579
Message: "warning: stable strangelets",
3583
MeterStatus: &meterStatus{
3585
Message: "things are looking up",
3592
out, err := FormatTabular(status)
3593
c.Assert(err, jc.ErrorIsNil)
3594
c.Assert(string(out), gc.Equals, `
3595
MODEL CONTROLLER CLOUD/REGION VERSION
3598
APP VERSION STATUS EXPOSED ORIGIN CHARM REV OS
3601
UNIT WORKLOAD AGENT MACHINE PUBLIC-ADDRESS PORTS MESSAGE
3605
METER STATUS MESSAGE
3606
foo/0 strange warning: stable strangelets
3607
foo/1 up things are looking up
3609
MACHINE STATE DNS INS-ID SERIES AZ
3614
// Filtering Feature
3617
func (s *StatusSuite) FilteringTestSetup(c *gc.C) *context {
3618
ctx := s.newContext(c)
3621
// Given a machine is started
3622
// And the machine's ID is "0"
3623
// And the machine's job is to manage the environment
3624
addMachine{machineId: "0", job: state.JobManageModel},
3625
startAliveMachine{"0"},
3626
setMachineStatus{"0", status.StatusStarted, ""},
3627
// And the machine's address is "controller-0.dns"
3628
setAddresses{"0", network.NewAddresses("controller-0.dns")},
3629
// And a container is started
3630
// And the container's ID is "0/lxd/0"
3631
addContainer{"0", "0/lxd/0", state.JobHostUnits},
3633
// And the "wordpress" charm is available
3634
addCharm{"wordpress"},
3635
addService{name: "wordpress", charm: "wordpress"},
3636
// And the "mysql" charm is available
3638
addService{name: "mysql", charm: "mysql"},
3639
// And the "logging" charm is available
3640
addCharm{"logging"},
3642
// And a machine is started
3643
// And the machine's ID is "1"
3644
// And the machine's job is to host units
3645
addMachine{machineId: "1", job: state.JobHostUnits},
3646
startAliveMachine{"1"},
3647
setMachineStatus{"1", status.StatusStarted, ""},
3648
// And the machine's address is "controller-1.dns"
3649
setAddresses{"1", network.NewAddresses("controller-1.dns")},
3650
// And a unit of "wordpress" is deployed to machine "1"
3651
addAliveUnit{"wordpress", "1"},
3652
// And the unit is started
3653
setAgentStatus{"wordpress/0", status.StatusIdle, "", nil},
3654
setUnitStatus{"wordpress/0", status.StatusActive, "", nil},
3655
// And a machine is started
3657
// And the machine's ID is "2"
3658
// And the machine's job is to host units
3659
addMachine{machineId: "2", job: state.JobHostUnits},
3660
startAliveMachine{"2"},
3661
setMachineStatus{"2", status.StatusStarted, ""},
3662
// And the machine's address is "controller-2.dns"
3663
setAddresses{"2", network.NewAddresses("controller-2.dns")},
3664
// And a unit of "mysql" is deployed to machine "2"
3665
addAliveUnit{"mysql", "2"},
3666
// And the unit is started
3667
setAgentStatus{"mysql/0", status.StatusIdle, "", nil},
3668
setUnitStatus{"mysql/0", status.StatusActive, "", nil},
3669
// And the "logging" service is added
3670
addService{name: "logging", charm: "logging"},
3671
// And the service is exposed
3672
setServiceExposed{"logging", true},
3673
// And the "wordpress" service is related to the "mysql" service
3674
relateServices{"wordpress", "mysql"},
3675
// And the "wordpress" service is related to the "logging" service
3676
relateServices{"wordpress", "logging"},
3677
// And the "mysql" service is related to the "logging" service
3678
relateServices{"mysql", "logging"},
3679
// And the "logging" service is a subordinate to unit 0 of the "wordpress" service
3680
addSubordinate{"wordpress/0", "logging"},
3681
setAgentStatus{"logging/0", status.StatusIdle, "", nil},
3682
setUnitStatus{"logging/0", status.StatusActive, "", nil},
3683
// And the "logging" service is a subordinate to unit 0 of the "mysql" service
3684
addSubordinate{"mysql/0", "logging"},
3685
setAgentStatus{"logging/1", status.StatusIdle, "", nil},
3686
setUnitStatus{"logging/1", status.StatusActive, "", nil},
3687
setUnitsAlive{"logging"},
3694
// Scenario: One unit is in an errored state and user filters to active
3695
func (s *StatusSuite) TestFilterToActive(c *gc.C) {
3696
ctx := s.FilteringTestSetup(c)
3697
defer s.resetContext(c, ctx)
3699
// Given unit 1 of the "logging" service has an error
3700
setAgentStatus{"logging/1", status.StatusError, "mock error", nil}.step(c, ctx)
3701
// And unit 0 of the "mysql" service has an error
3702
setAgentStatus{"mysql/0", status.StatusError, "mock error", nil}.step(c, ctx)
3703
// When I run juju status --format oneline started
3704
_, stdout, stderr := runStatus(c, "--format", "oneline", "active")
3705
c.Assert(string(stderr), gc.Equals, "")
3706
// Then I should receive output prefixed with:
3709
- wordpress/0: controller-1.dns (agent:idle, workload:active)
3710
- logging/0: controller-1.dns (agent:idle, workload:active)
3712
c.Assert(string(stdout), gc.Equals, expected[1:])
3715
// Scenario: user filters to a single machine
3716
func (s *StatusSuite) TestFilterToMachine(c *gc.C) {
3717
ctx := s.FilteringTestSetup(c)
3718
defer s.resetContext(c, ctx)
3720
// When I run juju status --format oneline 1
3721
_, stdout, stderr := runStatus(c, "--format", "oneline", "1")
3722
c.Assert(string(stderr), gc.Equals, "")
3723
// Then I should receive output prefixed with:
3726
- wordpress/0: controller-1.dns (agent:idle, workload:active)
3727
- logging/0: controller-1.dns (agent:idle, workload:active)
3729
c.Assert(string(stdout), gc.Equals, expected[1:])
3732
// Scenario: user filters to a machine, shows containers
3733
func (s *StatusSuite) TestFilterToMachineShowsContainer(c *gc.C) {
3734
ctx := s.FilteringTestSetup(c)
3735
defer s.resetContext(c, ctx)
3737
// When I run juju status --format yaml 0
3738
_, stdout, stderr := runStatus(c, "--format", "yaml", "0")
3739
c.Assert(string(stderr), gc.Equals, "")
3740
// Then I should receive output matching:
3741
const expected = "(.|\n)*machines:(.|\n)*\"0\"(.|\n)*0/lxd/0(.|\n)*"
3742
c.Assert(string(stdout), gc.Matches, expected)
3745
// Scenario: user filters to a container
3746
func (s *StatusSuite) TestFilterToContainer(c *gc.C) {
3747
ctx := s.FilteringTestSetup(c)
3748
defer s.resetContext(c, ctx)
3750
// When I run juju status --format yaml 0/lxd/0
3751
_, stdout, stderr := runStatus(c, "--format", "yaml", "0/lxd/0")
3752
c.Assert(string(stderr), gc.Equals, "")
3753
out := substituteFakeSinceTime(c, stdout, ctx.expectIsoTime)
3754
const expected = "" +
3756
" name: controller\n" +
3757
" controller: kontroll\n" +
3759
" version: 1.2.3\n" +
3763
" current: started\n" +
3764
" since: 01 Apr 15 01:23+10:00\n" +
3765
" dns-name: controller-0.dns\n" +
3766
" instance-id: controller-0\n" +
3767
" machine-status:\n" +
3768
" current: pending\n" +
3769
" since: 01 Apr 15 01:23+10:00\n" +
3770
" series: quantal\n" +
3774
" current: pending\n" +
3775
" since: 01 Apr 15 01:23+10:00\n" +
3776
" instance-id: pending\n" +
3777
" machine-status:\n" +
3778
" current: pending\n" +
3779
" since: 01 Apr 15 01:23+10:00\n" +
3780
" series: quantal\n" +
3781
" hardware: arch=amd64 cpu-cores=1 mem=1024M root-disk=8192M\n" +
3782
" controller-member-status: adding-vote\n" +
3783
"applications: {}\n"
3785
c.Assert(string(out), gc.Equals, expected)
3788
// Scenario: One unit is in an errored state and user filters to errored
3789
func (s *StatusSuite) TestFilterToErrored(c *gc.C) {
3790
ctx := s.FilteringTestSetup(c)
3791
defer s.resetContext(c, ctx)
3793
// Given unit 1 of the "logging" service has an error
3794
setAgentStatus{"logging/1", status.StatusError, "mock error", nil}.step(c, ctx)
3795
// When I run juju status --format oneline error
3796
_, stdout, stderr := runStatus(c, "--format", "oneline", "error")
3797
c.Assert(stderr, gc.IsNil)
3798
// Then I should receive output prefixed with:
3801
- mysql/0: controller-2.dns (agent:idle, workload:active)
3802
- logging/1: controller-2.dns (agent:idle, workload:error)
3804
c.Assert(string(stdout), gc.Equals, expected[1:])
3807
// Scenario: User filters to mysql service
3808
func (s *StatusSuite) TestFilterToService(c *gc.C) {
3809
ctx := s.FilteringTestSetup(c)
3810
defer s.resetContext(c, ctx)
3812
// When I run juju status --format oneline error
3813
_, stdout, stderr := runStatus(c, "--format", "oneline", "mysql")
3814
c.Assert(stderr, gc.IsNil)
3815
// Then I should receive output prefixed with:
3818
- mysql/0: controller-2.dns (agent:idle, workload:active)
3819
- logging/1: controller-2.dns (agent:idle, workload:active)
3822
c.Assert(string(stdout), gc.Equals, expected[1:])
3825
// Scenario: User filters to exposed services
3826
func (s *StatusSuite) TestFilterToExposedService(c *gc.C) {
3827
ctx := s.FilteringTestSetup(c)
3828
defer s.resetContext(c, ctx)
3830
// Given unit 1 of the "mysql" service is exposed
3831
setServiceExposed{"mysql", true}.step(c, ctx)
3832
// And the logging service is not exposed
3833
setServiceExposed{"logging", false}.step(c, ctx)
3834
// And the wordpress service is not exposed
3835
setServiceExposed{"wordpress", false}.step(c, ctx)
3836
// When I run juju status --format oneline exposed
3837
_, stdout, stderr := runStatus(c, "--format", "oneline", "exposed")
3838
c.Assert(stderr, gc.IsNil)
3839
// Then I should receive output prefixed with:
3842
- mysql/0: controller-2.dns (agent:idle, workload:active)
3843
- logging/1: controller-2.dns (agent:idle, workload:active)
3845
c.Assert(string(stdout), gc.Equals, expected[1:])
3848
// Scenario: User filters to non-exposed services
3849
func (s *StatusSuite) TestFilterToNotExposedService(c *gc.C) {
3850
ctx := s.FilteringTestSetup(c)
3851
defer s.resetContext(c, ctx)
3853
setServiceExposed{"mysql", true}.step(c, ctx)
3854
// When I run juju status --format oneline not exposed
3855
_, stdout, stderr := runStatus(c, "--format", "oneline", "not", "exposed")
3856
c.Assert(stderr, gc.IsNil)
3857
// Then I should receive output prefixed with:
3860
- wordpress/0: controller-1.dns (agent:idle, workload:active)
3861
- logging/0: controller-1.dns (agent:idle, workload:active)
3863
c.Assert(string(stdout), gc.Equals, expected[1:])
3866
// Scenario: Filtering on Subnets
3867
func (s *StatusSuite) TestFilterOnSubnet(c *gc.C) {
3868
ctx := s.FilteringTestSetup(c)
3869
defer s.resetContext(c, ctx)
3871
// Given the address for machine "1" is "localhost"
3872
setAddresses{"1", network.NewAddresses("localhost", "127.0.0.1")}.step(c, ctx)
3873
// And the address for machine "2" is "10.0.0.1"
3874
setAddresses{"2", network.NewAddresses("10.0.0.1")}.step(c, ctx)
3875
// When I run juju status --format oneline 127.0.0.1
3876
_, stdout, stderr := runStatus(c, "--format", "oneline", "127.0.0.1")
3877
c.Assert(stderr, gc.IsNil)
3878
// Then I should receive output prefixed with:
3881
- wordpress/0: localhost (agent:idle, workload:active)
3882
- logging/0: localhost (agent:idle, workload:active)
3884
c.Assert(string(stdout), gc.Equals, expected[1:])
3887
// Scenario: Filtering on Ports
3888
func (s *StatusSuite) TestFilterOnPorts(c *gc.C) {
3889
ctx := s.FilteringTestSetup(c)
3890
defer s.resetContext(c, ctx)
3892
// Given the address for machine "1" is "localhost"
3893
setAddresses{"1", network.NewAddresses("localhost")}.step(c, ctx)
3894
// And the address for machine "2" is "10.0.0.1"
3895
setAddresses{"2", network.NewAddresses("10.0.0.1")}.step(c, ctx)
3896
openUnitPort{"wordpress/0", "tcp", 80}.step(c, ctx)
3897
// When I run juju status --format oneline 80/tcp
3898
_, stdout, stderr := runStatus(c, "--format", "oneline", "80/tcp")
3899
c.Assert(stderr, gc.IsNil)
3900
// Then I should receive output prefixed with:
3903
- wordpress/0: localhost (agent:idle, workload:active) 80/tcp
3904
- logging/0: localhost (agent:idle, workload:active)
3906
c.Assert(string(stdout), gc.Equals, expected[1:])
3909
// Scenario: User filters out a parent, but not its subordinate
3910
func (s *StatusSuite) TestFilterParentButNotSubordinate(c *gc.C) {
3911
ctx := s.FilteringTestSetup(c)
3912
defer s.resetContext(c, ctx)
3914
// When I run juju status --format oneline 80/tcp
3915
_, stdout, stderr := runStatus(c, "--format", "oneline", "logging")
3916
c.Assert(stderr, gc.IsNil)
3917
// Then I should receive output prefixed with:
3920
- mysql/0: controller-2.dns (agent:idle, workload:active)
3921
- logging/1: controller-2.dns (agent:idle, workload:active)
3922
- wordpress/0: controller-1.dns (agent:idle, workload:active)
3923
- logging/0: controller-1.dns (agent:idle, workload:active)
3925
c.Assert(string(stdout), gc.Equals, expected[1:])
3928
// Scenario: User filters out a subordinate, but not its parent
3929
func (s *StatusSuite) TestFilterSubordinateButNotParent(c *gc.C) {
3930
ctx := s.FilteringTestSetup(c)
3931
defer s.resetContext(c, ctx)
3933
// Given the wordpress service is exposed
3934
setServiceExposed{"wordpress", true}.step(c, ctx)
3935
// When I run juju status --format oneline not exposed
3936
_, stdout, stderr := runStatus(c, "--format", "oneline", "not", "exposed")
3937
c.Assert(stderr, gc.IsNil)
3938
// Then I should receive output prefixed with:
3941
- mysql/0: controller-2.dns (agent:idle, workload:active)
3942
- logging/1: controller-2.dns (agent:idle, workload:active)
3944
c.Assert(string(stdout), gc.Equals, expected[1:])
3947
func (s *StatusSuite) TestFilterMultipleHomogenousPatterns(c *gc.C) {
3948
ctx := s.FilteringTestSetup(c)
3949
defer s.resetContext(c, ctx)
3951
_, stdout, stderr := runStatus(c, "--format", "oneline", "wordpress/0", "mysql/0")
3952
c.Assert(stderr, gc.IsNil)
3953
// Then I should receive output prefixed with:
3956
- mysql/0: controller-2.dns (agent:idle, workload:active)
3957
- logging/1: controller-2.dns (agent:idle, workload:active)
3958
- wordpress/0: controller-1.dns (agent:idle, workload:active)
3959
- logging/0: controller-1.dns (agent:idle, workload:active)
3961
c.Assert(string(stdout), gc.Equals, expected[1:])
3964
func (s *StatusSuite) TestFilterMultipleHeterogenousPatterns(c *gc.C) {
3965
ctx := s.FilteringTestSetup(c)
3966
defer s.resetContext(c, ctx)
3968
_, stdout, stderr := runStatus(c, "--format", "oneline", "wordpress/0", "active")
3969
c.Assert(stderr, gc.IsNil)
3970
// Then I should receive output prefixed with:
3973
- mysql/0: controller-2.dns (agent:idle, workload:active)
3974
- logging/1: controller-2.dns (agent:idle, workload:active)
3975
- wordpress/0: controller-1.dns (agent:idle, workload:active)
3976
- logging/0: controller-1.dns (agent:idle, workload:active)
3978
c.Assert(string(stdout), gc.Equals, expected[1:])
3981
// TestSummaryStatusWithUnresolvableDns is result of bug# 1410320.
3982
func (s *StatusSuite) TestSummaryStatusWithUnresolvableDns(c *gc.C) {
3983
formatter := &summaryFormatter{}
3984
formatter.resolveAndTrackIp("invalidDns")
3985
// Test should not panic.
3988
func initStatusCommand(args ...string) (*statusCommand, error) {
3989
com := &statusCommand{}
3990
return com, coretesting.InitCommand(modelcmd.Wrap(com), args)
3993
var statusInitTests = []struct {
4002
args: []string{"--utc"},
4009
err: "invalid JUJU_STATUS_ISO_TIME env var, expected true|false.*",
4013
func (*StatusSuite) TestStatusCommandInit(c *gc.C) {
4014
defer os.Setenv(osenv.JujuStatusIsoTimeEnvKey, os.Getenv(osenv.JujuStatusIsoTimeEnvKey))
4016
for i, t := range statusInitTests {
4017
c.Logf("test %d", i)
4018
os.Setenv(osenv.JujuStatusIsoTimeEnvKey, t.envVar)
4019
com, err := initStatusCommand(t.args...)
4021
c.Check(err, gc.ErrorMatches, t.err)
4023
c.Check(err, jc.ErrorIsNil)
4025
c.Check(com.isoTime, gc.DeepEquals, t.isoTime)
4029
var statusTimeTest = test(
4030
"status generates timestamps as UTC in ISO format",
4031
addMachine{machineId: "0", job: state.JobManageModel},
4032
setAddresses{"0", network.NewAddresses("controller-0.dns")},
4033
startAliveMachine{"0"},
4034
setMachineStatus{"0", status.StatusStarted, ""},
4036
addService{name: "dummy-application", charm: "dummy"},
4038
addMachine{machineId: "1", job: state.JobHostUnits},
4039
startAliveMachine{"1"},
4040
setAddresses{"1", network.NewAddresses("controller-1.dns")},
4041
setMachineStatus{"1", status.StatusStarted, ""},
4043
addAliveUnit{"dummy-application", "1"},
4045
"add two units, one alive (in error state), one started",
4048
"name": "controller",
4049
"controller": "kontroll",
4058
"dummy-application": dummyCharm(M{
4059
"application-status": M{
4060
"current": "unknown",
4061
"message": "Waiting for agent initialization to finish",
4062
"since": "01 Apr 15 01:23+10:00",
4065
"dummy-application/0": M{
4067
"workload-status": M{
4068
"current": "unknown",
4069
"message": "Waiting for agent initialization to finish",
4070
"since": "01 Apr 15 01:23+10:00",
4073
"current": "allocating",
4074
"since": "01 Apr 15 01:23+10:00",
4076
"public-address": "controller-1.dns",
4085
func (s *StatusSuite) TestIsoTimeFormat(c *gc.C) {
4087
// Prepare context and run all steps to setup.
4088
ctx := s.newContext(c)
4089
ctx.expectIsoTime = true
4090
defer s.resetContext(c, ctx)
4095
func (s *StatusSuite) TestFormatProvisioningError(c *gc.C) {
4096
status := ¶ms.FullStatus{
4097
Machines: map[string]params.MachineStatus{
4099
AgentStatus: params.DetailedStatus{
4101
Info: "<error while provisioning>",
4103
InstanceId: "pending",
4104
InstanceStatus: params.DetailedStatus{},
4107
Jobs: []multiwatcher.MachineJob{"JobHostUnits"},
4111
formatter := NewStatusFormatter(status, true)
4112
formatted := formatter.format()
4114
c.Check(formatted, jc.DeepEquals, formattedStatus{
4115
Machines: map[string]machineStatus{
4117
JujuStatus: statusInfoContents{Current: "error", Message: "<error while provisioning>"},
4118
InstanceId: "pending",
4121
Containers: map[string]machineStatus{},
4124
Applications: map[string]applicationStatus{},
4128
type tableSections map[string][]string
4130
func sectionTitle(lines []string) string {
4131
return strings.SplitN(lines[0], " ", 2)[0]
4134
func splitTableSections(tableData []byte) (tableSections, error) {
4135
scanner := bufio.NewScanner(bytes.NewReader(tableData))
4136
result := make(tableSections)
4137
var current []string
4138
for scanner.Scan() {
4139
if line := scanner.Text(); line == "" && current != nil {
4140
result[sectionTitle(current)] = current
4142
} else if line != "" {
4143
current = append(current, line)
4146
if scanner.Err() != nil {
4147
return nil, scanner.Err()
4150
result[sectionTitle(current)] = current