Merge lp:~fwereade/juju-core/uniter-relation-states into lp:~go-bot/juju-core/trunk

Proposed by William Reade
Status: Merged
Approved by: William Reade
Approved revision: no longer in the source branch.
Merged at revision: 2606
Proposed branch: lp:~fwereade/juju-core/uniter-relation-states
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 1184 lines (+411/-116)
29 files modified
state/api/agent/state.go (+1/-1)
state/api/common/life.go (+1/-1)
state/api/deployer/machine.go (+1/-1)
state/api/firewaller/machine.go (+2/-2)
state/api/firewaller/service.go (+2/-2)
state/api/firewaller/unit.go (+3/-3)
state/api/keyupdater/authorisedkeys.go (+2/-2)
state/api/logger/logger.go (+2/-2)
state/api/machiner/machine.go (+1/-1)
state/api/params/params.go (+1/-1)
state/api/provisioner/machine.go (+9/-9)
state/api/provisioner/provisioner.go (+1/-1)
state/api/uniter/charm.go (+2/-2)
state/api/uniter/relationunit.go (+3/-3)
state/api/uniter/service.go (+3/-3)
state/api/uniter/unit.go (+29/-9)
state/api/uniter/unit_test.go (+19/-0)
state/api/uniter/uniter.go (+3/-3)
state/api/upgrader/upgrader.go (+3/-3)
state/apiserver/uniter/uniter.go (+38/-0)
state/apiserver/uniter/uniter_test.go (+31/-0)
state/unit.go (+21/-0)
state/unit_test.go (+41/-0)
testing/filetesting/filetesting_test.go (+2/-0)
worker/uniter/modes.go (+0/-10)
worker/uniter/relationer.go (+6/-4)
worker/uniter/relationer_test.go (+28/-13)
worker/uniter/uniter.go (+83/-38)
worker/uniter/uniter_test.go (+73/-2)
To merge this branch: bzr merge lp:~fwereade/juju-core/uniter-relation-states
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+215003@code.launchpad.net

Commit message

worker/uniter: startup with correct relation state

The uniter's runListener is not started until after relation state is set
up; and relation state setup is fixed to use true relation state from the
API server rather than the local best guess, which is inaccurate when any
joined relation has never seen a remote unit.

This involves a new API, which the unit agent will wait for, in case it
connects to an API server which hasn't yet been upgraded.

https://codereview.appspot.com/85670046/

Description of the change

worker/uniter: startup with correct relation state

The uniter's runListener is not started until after relation state is set
up; and relation state setup is fixed to use true relation state from the
API server rather than the local best guess, which is inaccurate when any
joined relation has never seen a remote unit.

This involves a new API, which the unit agent will wait for, in case it
connects to an API server which hasn't yet been upgraded.

https://codereview.appspot.com/85670046/

To post a comment you must log in.
Revision history for this message
William Reade (fwereade) wrote :

Reviewers: mp+215003_code.launchpad.net,

Message:
Please take a look.

Description:
worker/uniter: startup with correct relation state

The uniter's runListener is not started until after relation state is
set
up; and relation state setup is fixed to use true relation state from
the
API server rather than the local best guess, which is inaccurate when
any
joined relation has never seen a remote unit.

This involves a new API, which the unit agent will wait for, in case it
connects to an API server which hasn't yet been upgraded.

https://code.launchpad.net/~fwereade/juju-core/uniter-relation-states/+merge/215003

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/85670046/

Affected files (+356, -67 lines):
   A [revision details]
   M state/api/uniter/unit.go
   M state/api/uniter/unit_test.go
   M state/api/uniter/uniter.go
   M state/apiserver/uniter/uniter.go
   M state/apiserver/uniter/uniter_test.go
   M state/unit.go
   M state/unit_test.go
   M testing/filetesting/filetesting_test.go
   M worker/uniter/modes.go
   M worker/uniter/relationer.go
   M worker/uniter/relationer_test.go
   M worker/uniter/uniter.go
   M worker/uniter/uniter_test.go

Revision history for this message
Horacio Durán (hduran-8) wrote :

https://codereview.appspot.com/85670046/diff/1/state/api/uniter/unit.go
File state/api/uniter/unit.go (right):

https://codereview.appspot.com/85670046/diff/1/state/api/uniter/unit.go#newcode445
state/api/uniter/unit.go:445: return nil, fmt.Errorf("expected one
result, got %d", len(results.Results))
This is purely aesthetic and not sure if valid for English but if got %d
prints a number, "one" should also be a number.

https://codereview.appspot.com/85670046/

Revision history for this message
John A Meinel (jameinel) wrote :

LGTM, though mostly from matching the code to your description of what
it is doing.

https://codereview.appspot.com/85670046/

Revision history for this message
Go Bot (go-bot) wrote :
Download full text (103.9 KiB)

The attempt to merge lp:~fwereade/juju-core/uniter-relation-states into lp:juju-core failed. Below is the output from the failed tests.

ok launchpad.net/juju-core 0.013s
ok launchpad.net/juju-core/agent 1.167s
ok launchpad.net/juju-core/agent/mongo 1.083s
ok launchpad.net/juju-core/agent/tools 0.183s
ok launchpad.net/juju-core/bzr 5.523s
ok launchpad.net/juju-core/cert 2.996s
ok launchpad.net/juju-core/charm 0.405s
? launchpad.net/juju-core/charm/hooks [no test files]
? launchpad.net/juju-core/charm/testing [no test files]
ok launchpad.net/juju-core/cloudinit 0.031s
ok launchpad.net/juju-core/cloudinit/sshinit 0.877s
ok launchpad.net/juju-core/cmd 0.139s
ok launchpad.net/juju-core/cmd/charm-admin 0.344s
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/envcmd 0.161s
ok launchpad.net/juju-core/cmd/juju 219.393s
ok launchpad.net/juju-core/cmd/jujud 77.496s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 8.898s
? launchpad.net/juju-core/cmd/plugins/juju-restore [no test files]
ok launchpad.net/juju-core/cmd/plugins/local 0.194s
? launchpad.net/juju-core/cmd/plugins/local/juju-local [no test files]
ok launchpad.net/juju-core/constraints 0.024s
ok launchpad.net/juju-core/container 0.032s
ok launchpad.net/juju-core/container/factory 0.064s
ok launchpad.net/juju-core/container/kvm 0.206s
ok launchpad.net/juju-core/container/kvm/mock 0.035s
? launchpad.net/juju-core/container/kvm/testing [no test files]
ok launchpad.net/juju-core/container/lxc 4.294s
? launchpad.net/juju-core/container/lxc/mock [no test files]
? launchpad.net/juju-core/container/lxc/testing [no test files]
? launchpad.net/juju-core/container/testing [no test files]
ok launchpad.net/juju-core/downloader 5.242s
ok launchpad.net/juju-core/environs 2.173s
ok launchpad.net/juju-core/environs/bootstrap 11.376s
ok launchpad.net/juju-core/environs/cloudinit 0.470s
ok launchpad.net/juju-core/environs/config 1.576s
ok launchpad.net/juju-core/environs/configstore 0.037s
ok launchpad.net/juju-core/environs/filestorage 0.031s
ok launchpad.net/juju-core/environs/httpstorage 0.656s
ok launchpad.net/juju-core/environs/imagemetadata 0.440s
? launchpad.net/juju-core/environs/imagemetadata/testing [no test files]
ok launchpad.net/juju-core/environs/instances 0.042s
ok launchpad.net/juju-core/environs/jujutest 0.197s
ok launchpad.net/juju-core/environs/manual 12.586s
ok launchpad.net/juju-core/environs/simplestreams 0.274s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 0.947s
ok launchpad.net/juju-core/environs/storage 0.917s
ok launchpad.net/juju-core/environs/sync 47.949s
ok launchpad.net/juju-core/environs/testing 0.153s
ok launchpad.net/juju-core/environs/tools 4.727s
? launchpad.net/juju-core/environs/tools/testing [no test files]
ok launchpad.net/juju-core/errors 0.012s
ok launchpad.net/juju-core/instance 0.021s
? launchpad.net/juju-core/instance/testing [no test files]
ok launchpad.net/juju-core/juju 19.8...

Revision history for this message
Go Bot (go-bot) wrote :
Download full text (103.9 KiB)

The attempt to merge lp:~fwereade/juju-core/uniter-relation-states into lp:juju-core failed. Below is the output from the failed tests.

ok launchpad.net/juju-core 0.014s
ok launchpad.net/juju-core/agent 1.608s
ok launchpad.net/juju-core/agent/mongo 1.108s
ok launchpad.net/juju-core/agent/tools 0.196s
ok launchpad.net/juju-core/bzr 5.215s
ok launchpad.net/juju-core/cert 2.047s
ok launchpad.net/juju-core/charm 0.405s
? launchpad.net/juju-core/charm/hooks [no test files]
? launchpad.net/juju-core/charm/testing [no test files]
ok launchpad.net/juju-core/cloudinit 0.030s
ok launchpad.net/juju-core/cloudinit/sshinit 0.780s
ok launchpad.net/juju-core/cmd 0.163s
ok launchpad.net/juju-core/cmd/charm-admin 0.765s
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/envcmd 0.206s
ok launchpad.net/juju-core/cmd/juju 220.089s
ok launchpad.net/juju-core/cmd/jujud 78.031s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 8.101s
? launchpad.net/juju-core/cmd/plugins/juju-restore [no test files]
ok launchpad.net/juju-core/cmd/plugins/local 0.185s
? launchpad.net/juju-core/cmd/plugins/local/juju-local [no test files]
ok launchpad.net/juju-core/constraints 0.024s
ok launchpad.net/juju-core/container 0.032s
ok launchpad.net/juju-core/container/factory 0.029s
ok launchpad.net/juju-core/container/kvm 0.209s
ok launchpad.net/juju-core/container/kvm/mock 0.039s
? launchpad.net/juju-core/container/kvm/testing [no test files]
ok launchpad.net/juju-core/container/lxc 4.328s
? launchpad.net/juju-core/container/lxc/mock [no test files]
? launchpad.net/juju-core/container/lxc/testing [no test files]
? launchpad.net/juju-core/container/testing [no test files]
ok launchpad.net/juju-core/downloader 5.231s
ok launchpad.net/juju-core/environs 2.095s
ok launchpad.net/juju-core/environs/bootstrap 11.671s
ok launchpad.net/juju-core/environs/cloudinit 0.450s
ok launchpad.net/juju-core/environs/config 3.438s
ok launchpad.net/juju-core/environs/configstore 0.032s
ok launchpad.net/juju-core/environs/filestorage 0.027s
ok launchpad.net/juju-core/environs/httpstorage 0.708s
ok launchpad.net/juju-core/environs/imagemetadata 0.522s
? launchpad.net/juju-core/environs/imagemetadata/testing [no test files]
ok launchpad.net/juju-core/environs/instances 0.040s
ok launchpad.net/juju-core/environs/jujutest 0.183s
ok launchpad.net/juju-core/environs/manual 15.868s
ok launchpad.net/juju-core/environs/simplestreams 0.249s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 0.912s
ok launchpad.net/juju-core/environs/storage 0.809s
ok launchpad.net/juju-core/environs/sync 48.714s
ok launchpad.net/juju-core/environs/testing 0.148s
ok launchpad.net/juju-core/environs/tools 4.338s
? launchpad.net/juju-core/environs/tools/testing [no test files]
ok launchpad.net/juju-core/errors 0.011s
ok launchpad.net/juju-core/instance 0.019s
? launchpad.net/juju-core/instance/testing [no test files]
ok launchpad.net/juju-core/juju 18.9...

Revision history for this message
Go Bot (go-bot) wrote :
Download full text (577.7 KiB)

The attempt to merge lp:~fwereade/juju-core/uniter-relation-states into lp:juju-core failed. Below is the output from the failed tests.

ok launchpad.net/juju-core 0.014s
ok launchpad.net/juju-core/agent 1.561s
ok launchpad.net/juju-core/agent/mongo 1.125s
ok launchpad.net/juju-core/agent/tools 0.178s
ok launchpad.net/juju-core/bzr 5.134s
ok launchpad.net/juju-core/cert 2.717s
ok launchpad.net/juju-core/charm 0.422s
? launchpad.net/juju-core/charm/hooks [no test files]
? launchpad.net/juju-core/charm/testing [no test files]
ok launchpad.net/juju-core/cloudinit 0.030s
ok launchpad.net/juju-core/cloudinit/sshinit 0.809s
ok launchpad.net/juju-core/cmd 0.178s
ok launchpad.net/juju-core/cmd/charm-admin 0.720s
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/envcmd 0.249s
ok launchpad.net/juju-core/cmd/juju 221.435s
ok launchpad.net/juju-core/cmd/jujud 80.471s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 7.852s
? launchpad.net/juju-core/cmd/plugins/juju-restore [no test files]
ok launchpad.net/juju-core/cmd/plugins/local 0.185s
? launchpad.net/juju-core/cmd/plugins/local/juju-local [no test files]
ok launchpad.net/juju-core/constraints 0.024s
ok launchpad.net/juju-core/container 0.036s
ok launchpad.net/juju-core/container/factory 0.034s
ok launchpad.net/juju-core/container/kvm 0.199s
ok launchpad.net/juju-core/container/kvm/mock 0.033s
? launchpad.net/juju-core/container/kvm/testing [no test files]
ok launchpad.net/juju-core/container/lxc 4.320s
? launchpad.net/juju-core/container/lxc/mock [no test files]
? launchpad.net/juju-core/container/lxc/testing [no test files]
? launchpad.net/juju-core/container/testing [no test files]
ok launchpad.net/juju-core/downloader 5.289s
ok launchpad.net/juju-core/environs 2.403s
ok launchpad.net/juju-core/environs/bootstrap 11.122s
ok launchpad.net/juju-core/environs/cloudinit 0.571s
ok launchpad.net/juju-core/environs/config 2.183s
ok launchpad.net/juju-core/environs/configstore 0.031s
ok launchpad.net/juju-core/environs/filestorage 0.031s
ok launchpad.net/juju-core/environs/httpstorage 0.630s
ok launchpad.net/juju-core/environs/imagemetadata 0.434s
? launchpad.net/juju-core/environs/imagemetadata/testing [no test files]
ok launchpad.net/juju-core/environs/instances 0.036s
ok launchpad.net/juju-core/environs/jujutest 0.163s
ok launchpad.net/juju-core/environs/manual 12.104s
ok launchpad.net/juju-core/environs/simplestreams 0.259s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 0.917s
ok launchpad.net/juju-core/environs/storage 0.945s
ok launchpad.net/juju-core/environs/sync 47.973s
ok launchpad.net/juju-core/environs/testing 0.130s
ok launchpad.net/juju-core/environs/tools 4.830s
? launchpad.net/juju-core/environs/tools/testing [no test files]
ok launchpad.net/juju-core/errors 0.011s
ok launchpad.net/juju-core/instance 0.019s
? launchpad.net/juju-core/instance/testing [no test files]
ok launchpad.net/juju-core/juju 23.5...

Revision history for this message
William Reade (fwereade) wrote :

https://codereview.appspot.com/85670046/diff/1/state/api/uniter/unit.go
File state/api/uniter/unit.go (right):

https://codereview.appspot.com/85670046/diff/1/state/api/uniter/unit.go#newcode445
state/api/uniter/unit.go:445: return nil, fmt.Errorf("expected one
result, got %d", len(results.Results))
On 2014/04/09 17:47:11, hduran wrote:
> This is purely aesthetic and not sure if valid for English but if got
%d prints
> a number, "one" should also be a number.

FWIW, style guides I've seen have suggested that you should always spell
out numbers below N, for varying N. Being consistent works for me
though; done.

https://codereview.appspot.com/85670046/

Revision history for this message
Go Bot (go-bot) wrote :

There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'state/api/agent/state.go'
--- state/api/agent/state.go 2014-04-02 15:46:57 +0000
+++ state/api/agent/state.go 2014-04-10 12:49:05 +0000
@@ -32,7 +32,7 @@
32 return nil, err32 return nil, err
33 }33 }
34 if len(results.Entities) != 1 {34 if len(results.Entities) != 1 {
35 return nil, fmt.Errorf("expected one result, got %d", len(results.Entities))35 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Entities))
36 }36 }
37 if err := results.Entities[0].Error; err != nil {37 if err := results.Entities[0].Error; err != nil {
38 return nil, err38 return nil, err
3939
=== modified file 'state/api/common/life.go'
--- state/api/common/life.go 2014-01-23 00:09:15 +0000
+++ state/api/common/life.go 2014-04-10 12:49:05 +0000
@@ -22,7 +22,7 @@
22 return "", err22 return "", err
23 }23 }
24 if len(result.Results) != 1 {24 if len(result.Results) != 1 {
25 return "", fmt.Errorf("expected one result, got %d", len(result.Results))25 return "", fmt.Errorf("expected 1 result, got %d", len(result.Results))
26 }26 }
27 if err := result.Results[0].Error; err != nil {27 if err := result.Results[0].Error; err != nil {
28 return "", err28 return "", err
2929
=== modified file 'state/api/deployer/machine.go'
--- state/api/deployer/machine.go 2014-03-24 13:48:45 +0000
+++ state/api/deployer/machine.go 2014-04-10 12:49:05 +0000
@@ -29,7 +29,7 @@
29 return nil, err29 return nil, err
30 }30 }
31 if len(results.Results) != 1 {31 if len(results.Results) != 1 {
32 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))32 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
33 }33 }
34 result := results.Results[0]34 result := results.Results[0]
35 if result.Error != nil {35 if result.Error != nil {
3636
=== modified file 'state/api/firewaller/machine.go'
--- state/api/firewaller/machine.go 2014-03-24 13:27:29 +0000
+++ state/api/firewaller/machine.go 2014-04-10 12:49:05 +0000
@@ -29,7 +29,7 @@
29 return nil, err29 return nil, err
30 }30 }
31 if len(results.Results) != 1 {31 if len(results.Results) != 1 {
32 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))32 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
33 }33 }
34 result := results.Results[0]34 result := results.Results[0]
35 if result.Error != nil {35 if result.Error != nil {
@@ -51,7 +51,7 @@
51 return "", err51 return "", err
52 }52 }
53 if len(results.Results) != 1 {53 if len(results.Results) != 1 {
54 return "", fmt.Errorf("expected one result, got %d", len(results.Results))54 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
55 }55 }
56 result := results.Results[0]56 result := results.Results[0]
57 if result.Error != nil {57 if result.Error != nil {
5858
=== modified file 'state/api/firewaller/service.go'
--- state/api/firewaller/service.go 2014-03-24 13:27:29 +0000
+++ state/api/firewaller/service.go 2014-04-10 12:49:05 +0000
@@ -38,7 +38,7 @@
38 return nil, err38 return nil, err
39 }39 }
40 if len(results.Results) != 1 {40 if len(results.Results) != 1 {
41 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))41 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
42 }42 }
43 result := results.Results[0]43 result := results.Results[0]
44 if result.Error != nil {44 if result.Error != nil {
@@ -80,7 +80,7 @@
80 return false, err80 return false, err
81 }81 }
82 if len(results.Results) != 1 {82 if len(results.Results) != 1 {
83 return false, fmt.Errorf("expected one result, got %d", len(results.Results))83 return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
84 }84 }
85 result := results.Results[0]85 result := results.Results[0]
86 if result.Error != nil {86 if result.Error != nil {
8787
=== modified file 'state/api/firewaller/unit.go'
--- state/api/firewaller/unit.go 2014-03-24 13:27:29 +0000
+++ state/api/firewaller/unit.go 2014-04-10 12:49:05 +0000
@@ -54,7 +54,7 @@
54 return nil, err54 return nil, err
55 }55 }
56 if len(results.Results) != 1 {56 if len(results.Results) != 1 {
57 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))57 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
58 }58 }
59 result := results.Results[0]59 result := results.Results[0]
60 if result.Error != nil {60 if result.Error != nil {
@@ -94,7 +94,7 @@
94 return nil, err94 return nil, err
95 }95 }
96 if len(results.Results) != 1 {96 if len(results.Results) != 1 {
97 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))97 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
98 }98 }
99 result := results.Results[0]99 result := results.Results[0]
100 if result.Error != nil {100 if result.Error != nil {
@@ -115,7 +115,7 @@
115 return "", err115 return "", err
116 }116 }
117 if len(results.Results) != 1 {117 if len(results.Results) != 1 {
118 return "", fmt.Errorf("expected one result, got %d", len(results.Results))118 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
119 }119 }
120 result := results.Results[0]120 result := results.Results[0]
121 if result.Error != nil {121 if result.Error != nil {
122122
=== modified file 'state/api/keyupdater/authorisedkeys.go'
--- state/api/keyupdater/authorisedkeys.go 2014-03-24 13:55:21 +0000
+++ state/api/keyupdater/authorisedkeys.go 2014-04-10 12:49:05 +0000
@@ -38,7 +38,7 @@
38 }38 }
39 if len(results.Results) != 1 {39 if len(results.Results) != 1 {
40 // TODO: Not directly tested40 // TODO: Not directly tested
41 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))41 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
42 }42 }
43 result := results.Results[0]43 result := results.Results[0]
44 if err := result.Error; err != nil {44 if err := result.Error; err != nil {
@@ -61,7 +61,7 @@
61 }61 }
62 if len(results.Results) != 1 {62 if len(results.Results) != 1 {
63 // TODO: Not directly tested63 // TODO: Not directly tested
64 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))64 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
65 }65 }
66 result := results.Results[0]66 result := results.Results[0]
67 if result.Error != nil {67 if result.Error != nil {
6868
=== modified file 'state/api/logger/logger.go'
--- state/api/logger/logger.go 2014-03-24 14:10:25 +0000
+++ state/api/logger/logger.go 2014-04-10 12:49:05 +0000
@@ -40,7 +40,7 @@
40 }40 }
41 if len(results.Results) != 1 {41 if len(results.Results) != 1 {
42 // TODO: Not directly tested42 // TODO: Not directly tested
43 return "", fmt.Errorf("expected one result, got %d", len(results.Results))43 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
44 }44 }
45 result := results.Results[0]45 result := results.Results[0]
46 if err := result.Error; err != nil {46 if err := result.Error; err != nil {
@@ -63,7 +63,7 @@
63 }63 }
64 if len(results.Results) != 1 {64 if len(results.Results) != 1 {
65 // TODO: Not directly tested65 // TODO: Not directly tested
66 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))66 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
67 }67 }
68 result := results.Results[0]68 result := results.Results[0]
69 if result.Error != nil {69 if result.Error != nil {
7070
=== modified file 'state/api/machiner/machine.go'
--- state/api/machiner/machine.go 2014-03-26 06:28:38 +0000
+++ state/api/machiner/machine.go 2014-04-10 12:49:05 +0000
@@ -93,7 +93,7 @@
93 return nil, err93 return nil, err
94 }94 }
95 if len(results.Results) != 1 {95 if len(results.Results) != 1 {
96 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))96 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
97 }97 }
98 result := results.Results[0]98 result := results.Results[0]
99 if result.Error != nil {99 if result.Error != nil {
100100
=== modified file 'state/api/params/params.go'
--- state/api/params/params.go 2014-04-10 08:47:38 +0000
+++ state/api/params/params.go 2014-04-10 12:49:05 +0000
@@ -50,7 +50,7 @@
50// of a bulk operation on a single value.50// of a bulk operation on a single value.
51func (result ErrorResults) OneError() error {51func (result ErrorResults) OneError() error {
52 if n := len(result.Results); n != 1 {52 if n := len(result.Results); n != 1 {
53 return fmt.Errorf("expected one result, got %d", n)53 return fmt.Errorf("expected 1 result, got %d", n)
54 }54 }
55 if err := result.Results[0].Error; err != nil {55 if err := result.Results[0].Error; err != nil {
56 return err56 return err
5757
=== modified file 'state/api/provisioner/machine.go'
--- state/api/provisioner/machine.go 2014-04-09 15:08:51 +0000
+++ state/api/provisioner/machine.go 2014-04-10 12:49:05 +0000
@@ -65,7 +65,7 @@
65 return nil, nil, err65 return nil, nil, err
66 }66 }
67 if len(results.Results) != 1 {67 if len(results.Results) != 1 {
68 return nil, nil, fmt.Errorf("expected one result, got %d", len(results.Results))68 return nil, nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
69 }69 }
70 result := results.Results[0]70 result := results.Results[0]
71 if result.Error != nil {71 if result.Error != nil {
@@ -100,7 +100,7 @@
100 return "", "", err100 return "", "", err
101 }101 }
102 if len(results.Results) != 1 {102 if len(results.Results) != 1 {
103 return "", "", fmt.Errorf("expected one result, got %d", len(results.Results))103 return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
104 }104 }
105 result := results.Results[0]105 result := results.Results[0]
106 if result.Error != nil {106 if result.Error != nil {
@@ -122,7 +122,7 @@
122 return nothing, err122 return nothing, err
123 }123 }
124 if len(results.Results) != 1 {124 if len(results.Results) != 1 {
125 return nothing, fmt.Errorf("expected one result, got %d", len(results.Results))125 return nothing, fmt.Errorf("expected 1 result, got %d", len(results.Results))
126 }126 }
127 result := results.Results[0]127 result := results.Results[0]
128 if result.Error != nil {128 if result.Error != nil {
@@ -173,7 +173,7 @@
173 return "", err173 return "", err
174 }174 }
175 if len(results.Results) != 1 {175 if len(results.Results) != 1 {
176 return "", fmt.Errorf("expected one result, got %d", len(results.Results))176 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
177 }177 }
178 result := results.Results[0]178 result := results.Results[0]
179 if result.Error != nil {179 if result.Error != nil {
@@ -196,7 +196,7 @@
196 return nil, err196 return nil, err
197 }197 }
198 if len(results.Results) != 1 {198 if len(results.Results) != 1 {
199 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))199 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
200 }200 }
201 result := results.Results[0]201 result := results.Results[0]
202 if result.Error != nil {202 if result.Error != nil {
@@ -242,7 +242,7 @@
242 return "", err242 return "", err
243 }243 }
244 if len(results.Results) != 1 {244 if len(results.Results) != 1 {
245 return "", fmt.Errorf("expected one result, got %d", len(results.Results))245 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
246 }246 }
247 result := results.Results[0]247 result := results.Results[0]
248 if result.Error != nil {248 if result.Error != nil {
@@ -293,7 +293,7 @@
293 return nil, err293 return nil, err
294 }294 }
295 if len(results.Results) != 1 {295 if len(results.Results) != 1 {
296 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))296 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
297 }297 }
298 result := results.Results[0]298 result := results.Results[0]
299 if result.Error != nil {299 if result.Error != nil {
@@ -317,7 +317,7 @@
317 return nil, err317 return nil, err
318 }318 }
319 if len(results.Results) != 1 {319 if len(results.Results) != 1 {
320 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))320 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
321 }321 }
322 result := results.Results[0]322 result := results.Results[0]
323 if result.Error != nil {323 if result.Error != nil {
@@ -340,7 +340,7 @@
340 return err340 return err
341 }341 }
342 if len(results.Results) != 1 {342 if len(results.Results) != 1 {
343 return fmt.Errorf("expected one result, got %d", len(results.Results))343 return fmt.Errorf("expected 1 result, got %d", len(results.Results))
344 }344 }
345 apiError := results.Results[0].Error345 apiError := results.Results[0].Error
346 if apiError != nil {346 if apiError != nil {
347347
=== modified file 'state/api/provisioner/provisioner.go'
--- state/api/provisioner/provisioner.go 2014-04-08 16:40:04 +0000
+++ state/api/provisioner/provisioner.go 2014-04-10 12:49:05 +0000
@@ -106,7 +106,7 @@
106 }106 }
107 if len(results.Results) != 1 {107 if len(results.Results) != 1 {
108 // TODO: Not directly tested108 // TODO: Not directly tested
109 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))109 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
110 }110 }
111 result := results.Results[0]111 result := results.Results[0]
112 if err := result.Error; err != nil {112 if err := result.Error; err != nil {
113113
=== modified file 'state/api/uniter/charm.go'
--- state/api/uniter/charm.go 2014-03-24 10:48:25 +0000
+++ state/api/uniter/charm.go 2014-04-10 12:49:05 +0000
@@ -41,7 +41,7 @@
41 return "", err41 return "", err
42 }42 }
43 if len(results.Results) != 1 {43 if len(results.Results) != 1 {
44 return "", fmt.Errorf("expected one result, got %d", len(results.Results))44 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
45 }45 }
46 result := results.Results[0]46 result := results.Results[0]
47 if result.Error != nil {47 if result.Error != nil {
@@ -70,7 +70,7 @@
70 return nil, false, err70 return nil, false, err
71 }71 }
72 if len(results.Results) != 1 {72 if len(results.Results) != 1 {
73 return nil, false, fmt.Errorf("expected one result, got %d", len(results.Results))73 return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
74 }74 }
75 result := results.Results[0]75 result := results.Results[0]
76 if result.Error != nil {76 if result.Error != nil {
7777
=== modified file 'state/api/uniter/relationunit.go'
--- state/api/uniter/relationunit.go 2014-03-21 18:40:43 +0000
+++ state/api/uniter/relationunit.go 2014-04-10 12:49:05 +0000
@@ -117,7 +117,7 @@
117 return nil, err117 return nil, err
118 }118 }
119 if len(results.Results) != 1 {119 if len(results.Results) != 1 {
120 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))120 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
121 }121 }
122 result := results.Results[0]122 result := results.Results[0]
123 if result.Error != nil {123 if result.Error != nil {
@@ -148,7 +148,7 @@
148 return nil, err148 return nil, err
149 }149 }
150 if len(results.Results) != 1 {150 if len(results.Results) != 1 {
151 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))151 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
152 }152 }
153 result := results.Results[0]153 result := results.Results[0]
154 if result.Error != nil {154 if result.Error != nil {
@@ -172,7 +172,7 @@
172 return nil, err172 return nil, err
173 }173 }
174 if len(results.Results) != 1 {174 if len(results.Results) != 1 {
175 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))175 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
176 }176 }
177 result := results.Results[0]177 result := results.Results[0]
178 if result.Error != nil {178 if result.Error != nil {
179179
=== modified file 'state/api/uniter/service.go'
--- state/api/uniter/service.go 2014-03-21 18:40:43 +0000
+++ state/api/uniter/service.go 2014-04-10 12:49:05 +0000
@@ -47,7 +47,7 @@
47 return nil, err47 return nil, err
48 }48 }
49 if len(results.Results) != 1 {49 if len(results.Results) != 1 {
50 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))50 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
51 }51 }
52 result := results.Results[0]52 result := results.Results[0]
53 if result.Error != nil {53 if result.Error != nil {
@@ -69,7 +69,7 @@
69 return nil, err69 return nil, err
70 }70 }
71 if len(results.Results) != 1 {71 if len(results.Results) != 1 {
72 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))72 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
73 }73 }
74 result := results.Results[0]74 result := results.Results[0]
75 if result.Error != nil {75 if result.Error != nil {
@@ -111,7 +111,7 @@
111 return nil, false, err111 return nil, false, err
112 }112 }
113 if len(results.Results) != 1 {113 if len(results.Results) != 1 {
114 return nil, false, fmt.Errorf("expected one result, got %d", len(results.Results))114 return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
115 }115 }
116 result := results.Results[0]116 result := results.Results[0]
117 if result.Error != nil {117 if result.Error != nil {
118118
=== modified file 'state/api/uniter/unit.go'
--- state/api/uniter/unit.go 2014-04-01 03:39:51 +0000
+++ state/api/uniter/unit.go 2014-04-10 12:49:05 +0000
@@ -94,7 +94,7 @@
94 return nil, err94 return nil, err
95 }95 }
96 if len(results.Results) != 1 {96 if len(results.Results) != 1 {
97 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))97 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
98 }98 }
99 result := results.Results[0]99 result := results.Results[0]
100 if result.Error != nil {100 if result.Error != nil {
@@ -134,7 +134,7 @@
134 return nil, err134 return nil, err
135 }135 }
136 if len(results.Results) != 1 {136 if len(results.Results) != 1 {
137 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))137 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
138 }138 }
139 result := results.Results[0]139 result := results.Results[0]
140 if result.Error != nil {140 if result.Error != nil {
@@ -197,7 +197,7 @@
197 return "", err197 return "", err
198 }198 }
199 if len(results.Results) != 1 {199 if len(results.Results) != 1 {
200 return "", fmt.Errorf("expected one result, got %d", len(results.Results))200 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
201 }201 }
202 result := results.Results[0]202 result := results.Results[0]
203 if result.Error != nil {203 if result.Error != nil {
@@ -221,7 +221,7 @@
221 return false, err221 return false, err
222 }222 }
223 if len(results.Results) != 1 {223 if len(results.Results) != 1 {
224 return false, fmt.Errorf("expected one result, got %d", len(results.Results))224 return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
225 }225 }
226 result := results.Results[0]226 result := results.Results[0]
227 if result.Error != nil {227 if result.Error != nil {
@@ -242,7 +242,7 @@
242 return false, err242 return false, err
243 }243 }
244 if len(results.Results) != 1 {244 if len(results.Results) != 1 {
245 return false, fmt.Errorf("expected one result, got %d", len(results.Results))245 return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
246 }246 }
247 result := results.Results[0]247 result := results.Results[0]
248 if result.Error != nil {248 if result.Error != nil {
@@ -269,7 +269,7 @@
269 return "", err269 return "", err
270 }270 }
271 if len(results.Results) != 1 {271 if len(results.Results) != 1 {
272 return "", fmt.Errorf("expected one result, got %d", len(results.Results))272 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
273 }273 }
274 result := results.Results[0]274 result := results.Results[0]
275 if result.Error != nil {275 if result.Error != nil {
@@ -296,7 +296,7 @@
296 return "", err296 return "", err
297 }297 }
298 if len(results.Results) != 1 {298 if len(results.Results) != 1 {
299 return "", fmt.Errorf("expected one result, got %d", len(results.Results))299 return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
300 }300 }
301 result := results.Results[0]301 result := results.Results[0]
302 if result.Error != nil {302 if result.Error != nil {
@@ -359,7 +359,7 @@
359 return nil, err359 return nil, err
360 }360 }
361 if len(results.Results) != 1 {361 if len(results.Results) != 1 {
362 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))362 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
363 }363 }
364 result := results.Results[0]364 result := results.Results[0]
365 if result.Error != nil {365 if result.Error != nil {
@@ -421,7 +421,7 @@
421 return nil, err421 return nil, err
422 }422 }
423 if len(results.Results) != 1 {423 if len(results.Results) != 1 {
424 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))424 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
425 }425 }
426 result := results.Results[0]426 result := results.Results[0]
427 if result.Error != nil {427 if result.Error != nil {
@@ -430,3 +430,23 @@
430 w := watcher.NewNotifyWatcher(u.st.caller, result)430 w := watcher.NewNotifyWatcher(u.st.caller, result)
431 return w, nil431 return w, nil
432}432}
433
434// JoinedRelations returns the tags of the relations the unit has joined.
435func (u *Unit) JoinedRelations() ([]string, error) {
436 var results params.StringsResults
437 args := params.Entities{
438 Entities: []params.Entity{{Tag: u.tag}},
439 }
440 err := u.st.call("JoinedRelations", args, &results)
441 if err != nil {
442 return nil, err
443 }
444 if len(results.Results) != 1 {
445 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
446 }
447 result := results.Results[0]
448 if result.Error != nil {
449 return nil, result.Error
450 }
451 return result.Result, nil
452}
433453
=== modified file 'state/api/uniter/unit_test.go'
--- state/api/uniter/unit_test.go 2014-04-07 00:36:36 +0000
+++ state/api/uniter/unit_test.go 2014-04-10 12:49:05 +0000
@@ -4,6 +4,8 @@
4package uniter_test4package uniter_test
55
6import (6import (
7 "sort"
8
7 jc "github.com/juju/testing/checkers"9 jc "github.com/juju/testing/checkers"
8 gc "launchpad.net/gocheck"10 gc "launchpad.net/gocheck"
911
@@ -355,3 +357,20 @@
355 c.Assert(s.apiUnit.ServiceName(), gc.Equals, "wordpress")357 c.Assert(s.apiUnit.ServiceName(), gc.Equals, "wordpress")
356 c.Assert(s.apiUnit.ServiceTag(), gc.Equals, "service-wordpress")358 c.Assert(s.apiUnit.ServiceTag(), gc.Equals, "service-wordpress")
357}359}
360
361func (s *unitSuite) TestJoinedRelations(c *gc.C) {
362 joinedRelations, err := s.apiUnit.JoinedRelations()
363 c.Assert(err, gc.IsNil)
364 c.Assert(joinedRelations, gc.HasLen, 0)
365
366 rel1, _, _ := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
367 joinedRelations, err = s.apiUnit.JoinedRelations()
368 c.Assert(err, gc.IsNil)
369 c.Assert(joinedRelations, gc.DeepEquals, []string{rel1.Tag()})
370
371 rel2, _, _ := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
372 joinedRelations, err = s.apiUnit.JoinedRelations()
373 c.Assert(err, gc.IsNil)
374 sort.Strings(joinedRelations)
375 c.Assert(joinedRelations, gc.DeepEquals, []string{rel2.Tag(), rel1.Tag()})
376}
358377
=== modified file 'state/api/uniter/uniter.go'
--- state/api/uniter/uniter.go 2014-03-21 18:46:48 +0000
+++ state/api/uniter/uniter.go 2014-04-10 12:49:05 +0000
@@ -58,7 +58,7 @@
58 return nothing, err58 return nothing, err
59 }59 }
60 if len(result.Results) != 1 {60 if len(result.Results) != 1 {
61 return nothing, fmt.Errorf("expected one result, got %d", len(result.Results))61 return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results))
62 }62 }
63 if err := result.Results[0].Error; err != nil {63 if err := result.Results[0].Error; err != nil {
64 return nothing, err64 return nothing, err
@@ -134,7 +134,7 @@
134 }, nil134 }, nil
135}135}
136136
137// Relation returns the existing relation with the given tag.137// RelationById returns the existing relation with the given id.
138func (st *State) RelationById(id int) (*Relation, error) {138func (st *State) RelationById(id int) (*Relation, error) {
139 var results params.RelationResults139 var results params.RelationResults
140 args := params.RelationIds{140 args := params.RelationIds{
@@ -145,7 +145,7 @@
145 return nil, err145 return nil, err
146 }146 }
147 if len(results.Results) != 1 {147 if len(results.Results) != 1 {
148 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))148 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
149 }149 }
150 result := results.Results[0]150 result := results.Results[0]
151 if err := result.Error; err != nil {151 if err := result.Error; err != nil {
152152
=== modified file 'state/api/upgrader/upgrader.go'
--- state/api/upgrader/upgrader.go 2014-03-24 13:48:45 +0000
+++ state/api/upgrader/upgrader.go 2014-04-10 12:49:05 +0000
@@ -60,7 +60,7 @@
60 }60 }
61 if len(results.Results) != 1 {61 if len(results.Results) != 1 {
62 // TODO: Not directly tested62 // TODO: Not directly tested
63 return version.Number{}, fmt.Errorf("expected one result, got %d", len(results.Results))63 return version.Number{}, fmt.Errorf("expected 1 result, got %d", len(results.Results))
64 }64 }
65 result := results.Results[0]65 result := results.Results[0]
66 if err := result.Error; err != nil {66 if err := result.Error; err != nil {
@@ -87,7 +87,7 @@
87 }87 }
88 if len(results.Results) != 1 {88 if len(results.Results) != 1 {
89 // TODO: Not directly tested89 // TODO: Not directly tested
90 return nil, false, fmt.Errorf("expected one result, got %d", len(results.Results))90 return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
91 }91 }
92 result := results.Results[0]92 result := results.Results[0]
93 if err := result.Error; err != nil {93 if err := result.Error; err != nil {
@@ -112,7 +112,7 @@
112 }112 }
113 if len(results.Results) != 1 {113 if len(results.Results) != 1 {
114 // TODO: Not directly tested114 // TODO: Not directly tested
115 return nil, fmt.Errorf("expected one result, got %d", len(results.Results))115 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
116 }116 }
117 result := results.Results[0]117 result := results.Results[0]
118 if result.Error != nil {118 if result.Error != nil {
119119
=== modified file 'state/apiserver/uniter/uniter.go'
--- state/apiserver/uniter/uniter.go 2014-04-07 00:36:36 +0000
+++ state/apiserver/uniter/uniter.go 2014-04-10 12:49:05 +0000
@@ -693,6 +693,44 @@
693 return result, nil693 return result, nil
694}694}
695695
696func joinedRelationTags(unit *state.Unit) ([]string, error) {
697 relations, err := unit.JoinedRelations()
698 if err != nil {
699 return nil, err
700 }
701 tags := make([]string, len(relations))
702 for i, relation := range relations {
703 tags[i] = relation.Tag()
704 }
705 return tags, nil
706}
707
708// JoinedRelations returns the tags of all relations each supplied unit has joined.
709func (u *UniterAPI) JoinedRelations(args params.Entities) (params.StringsResults, error) {
710 result := params.StringsResults{
711 Results: make([]params.StringsResult, len(args.Entities)),
712 }
713 if len(args.Entities) == 0 {
714 return result, nil
715 }
716 canRead, err := u.accessUnit()
717 if err != nil {
718 return params.StringsResults{}, err
719 }
720 for i, entity := range args.Entities {
721 err := common.ErrPerm
722 if canRead(entity.Tag) {
723 var unit *state.Unit
724 unit, err = u.getUnit(entity.Tag)
725 if err == nil {
726 result.Results[i].Result, err = joinedRelationTags(unit)
727 }
728 }
729 result.Results[i].Error = common.ServerError(err)
730 }
731 return result, nil
732}
733
696// CurrentEnvironUUID returns the UUID for the current juju environment.734// CurrentEnvironUUID returns the UUID for the current juju environment.
697func (u *UniterAPI) CurrentEnvironUUID() (params.StringResult, error) {735func (u *UniterAPI) CurrentEnvironUUID() (params.StringResult, error) {
698 result := params.StringResult{}736 result := params.StringResult{}
699737
=== modified file 'state/apiserver/uniter/uniter_test.go'
--- state/apiserver/uniter/uniter_test.go 2014-04-07 00:36:36 +0000
+++ state/apiserver/uniter/uniter_test.go 2014-04-10 12:49:05 +0000
@@ -1048,6 +1048,37 @@
1048 c.Assert(readSettings, gc.DeepEquals, settings)1048 c.Assert(readSettings, gc.DeepEquals, settings)
1049}1049}
10501050
1051func (s *uniterSuite) TestJoinedRelations(c *gc.C) {
1052 rel := s.addRelation(c, "wordpress", "mysql")
1053 relUnit, err := rel.Unit(s.wordpressUnit)
1054 c.Assert(err, gc.IsNil)
1055 err = relUnit.EnterScope(nil)
1056 c.Assert(err, gc.IsNil)
1057
1058 args := params.Entities{
1059 Entities: []params.Entity{
1060 {s.wordpressUnit.Tag()},
1061 {s.mysqlUnit.Tag()},
1062 {"unit-unknown-1"},
1063 {"service-wordpress"},
1064 {"machine-0"},
1065 {rel.Tag()},
1066 },
1067 }
1068 result, err := s.uniter.JoinedRelations(args)
1069 c.Assert(err, gc.IsNil)
1070 c.Assert(result, gc.DeepEquals, params.StringsResults{
1071 Results: []params.StringsResult{
1072 {Result: []string{rel.Tag()}},
1073 {Error: apiservertesting.ErrUnauthorized},
1074 {Error: apiservertesting.ErrUnauthorized},
1075 {Error: apiservertesting.ErrUnauthorized},
1076 {Error: apiservertesting.ErrUnauthorized},
1077 {Error: apiservertesting.ErrUnauthorized},
1078 },
1079 })
1080}
1081
1051func (s *uniterSuite) TestReadSettings(c *gc.C) {1082func (s *uniterSuite) TestReadSettings(c *gc.C) {
1052 rel := s.addRelation(c, "wordpress", "mysql")1083 rel := s.addRelation(c, "wordpress", "mysql")
1053 relUnit, err := rel.Unit(s.wordpressUnit)1084 relUnit, err := rel.Unit(s.wordpressUnit)
10541085
=== modified file 'state/unit.go'
--- state/unit.go 2014-04-07 00:36:36 +0000
+++ state/unit.go 2014-04-10 12:49:05 +0000
@@ -481,6 +481,27 @@
481 return names481 return names
482}482}
483483
484// JoinedRelations returns the relations for which the unit is in scope.
485func (u *Unit) JoinedRelations() ([]*Relation, error) {
486 candidates, err := serviceRelations(u.st, u.doc.Service)
487 if err != nil {
488 return nil, err
489 }
490 var joinedRelations []*Relation
491 for _, relation := range candidates {
492 relationUnit, err := relation.Unit(u)
493 if err != nil {
494 return nil, err
495 }
496 if inScope, err := relationUnit.InScope(); err != nil {
497 return nil, err
498 } else if inScope {
499 joinedRelations = append(joinedRelations, relation)
500 }
501 }
502 return joinedRelations, nil
503}
504
484// DeployerTag returns the tag of the agent responsible for deploying505// DeployerTag returns the tag of the agent responsible for deploying
485// the unit. If no such entity can be determined, false is returned.506// the unit. If no such entity can be determined, false is returned.
486func (u *Unit) DeployerTag() (string, bool) {507func (u *Unit) DeployerTag() (string, bool) {
487508
=== modified file 'state/unit_test.go'
--- state/unit_test.go 2014-04-07 00:36:36 +0000
+++ state/unit_test.go 2014-04-10 12:49:05 +0000
@@ -993,6 +993,47 @@
993 c.Assert(principal, gc.Equals, "")993 c.Assert(principal, gc.Equals, "")
994}994}
995995
996func (s *UnitSuite) TestJoinedRelations(c *gc.C) {
997 wordpress0 := s.unit
998 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
999 mysql0, err := mysql.AddUnit()
1000 c.Assert(err, gc.IsNil)
1001 eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"})
1002 c.Assert(err, gc.IsNil)
1003 rel, err := s.State.AddRelation(eps...)
1004 c.Assert(err, gc.IsNil)
1005
1006 assertJoinedRelations := func(unit *state.Unit, expect ...*state.Relation) {
1007 actual, err := unit.JoinedRelations()
1008 c.Assert(err, gc.IsNil)
1009 c.Assert(actual, gc.HasLen, len(expect))
1010 for i, a := range actual {
1011 c.Assert(a.Id(), gc.Equals, expect[i].Id())
1012 }
1013 }
1014 assertJoinedRelations(wordpress0)
1015 assertJoinedRelations(mysql0)
1016
1017 mysql0ru, err := rel.Unit(mysql0)
1018 c.Assert(err, gc.IsNil)
1019 err = mysql0ru.EnterScope(nil)
1020 c.Assert(err, gc.IsNil)
1021 assertJoinedRelations(wordpress0)
1022 assertJoinedRelations(mysql0, rel)
1023
1024 wordpress0ru, err := rel.Unit(wordpress0)
1025 c.Assert(err, gc.IsNil)
1026 err = wordpress0ru.EnterScope(nil)
1027 c.Assert(err, gc.IsNil)
1028 assertJoinedRelations(wordpress0, rel)
1029 assertJoinedRelations(mysql0, rel)
1030
1031 err = mysql0ru.LeaveScope()
1032 c.Assert(err, gc.IsNil)
1033 assertJoinedRelations(wordpress0, rel)
1034 assertJoinedRelations(mysql0)
1035}
1036
996func (s *UnitSuite) TestRemove(c *gc.C) {1037func (s *UnitSuite) TestRemove(c *gc.C) {
997 err := s.unit.Remove()1038 err := s.unit.Remove()
998 c.Assert(err, gc.ErrorMatches, `cannot remove unit "wordpress/0": unit is not dead`)1039 c.Assert(err, gc.ErrorMatches, `cannot remove unit "wordpress/0": unit is not dead`)
9991040
=== modified file 'testing/filetesting/filetesting_test.go'
--- testing/filetesting/filetesting_test.go 2014-04-02 07:54:19 +0000
+++ testing/filetesting/filetesting_test.go 2014-04-10 12:49:05 +0000
@@ -105,6 +105,7 @@
105105
106func (s *EntrySuite) TestDirCreateFailure(c *gc.C) {106func (s *EntrySuite) TestDirCreateFailure(c *gc.C) {
107 os.Chmod(s.basePath, 0444)107 os.Chmod(s.basePath, 0444)
108 defer os.Chmod(s.basePath, 0777)
108 c.ExpectFailure("should fail to create file")109 c.ExpectFailure("should fail to create file")
109 ft.Dir{"foobar", 0750}.Create(c, s.basePath)110 ft.Dir{"foobar", 0750}.Create(c, s.basePath)
110}111}
@@ -194,6 +195,7 @@
194func (s *EntrySuite) TestRemovedCreateFailure(c *gc.C) {195func (s *EntrySuite) TestRemovedCreateFailure(c *gc.C) {
195 ft.File{"some-file", "content", 0644}.Create(c, s.basePath)196 ft.File{"some-file", "content", 0644}.Create(c, s.basePath)
196 os.Chmod(s.basePath, 0444)197 os.Chmod(s.basePath, 0444)
198 defer os.Chmod(s.basePath, 0777)
197 c.ExpectFailure("should fail to remove file")199 c.ExpectFailure("should fail to remove file")
198 ft.Removed{"some-file"}.Create(c, s.basePath)200 ft.Removed{"some-file"}.Create(c, s.basePath)
199}201}
200202
=== modified file 'worker/uniter/modes.go'
--- worker/uniter/modes.go 2014-04-02 11:35:49 +0000
+++ worker/uniter/modes.go 2014-04-10 12:49:05 +0000
@@ -22,16 +22,6 @@
22// states of a running Uniter.22// states of a running Uniter.
23type Mode func(u *Uniter) (Mode, error)23type Mode func(u *Uniter) (Mode, error)
2424
25// ModeInit is the initial Uniter mode.
26func ModeInit(u *Uniter) (next Mode, err error) {
27 defer modeContext("ModeInit", &err)()
28 logger.Infof("reconciling relation state")
29 if err := u.restoreRelations(); err != nil {
30 return nil, err
31 }
32 return ModeContinue, nil
33}
34
35// ModeContinue determines what action to take based on persistent uniter state.25// ModeContinue determines what action to take based on persistent uniter state.
36func ModeContinue(u *Uniter) (next Mode, err error) {26func ModeContinue(u *Uniter) (next Mode, err error) {
37 defer modeContext("ModeContinue", &err)()27 defer modeContext("ModeContinue", &err)()
3828
=== modified file 'worker/uniter/relationer.go'
--- worker/uniter/relationer.go 2013-09-12 16:29:52 +0000
+++ worker/uniter/relationer.go 2014-04-10 12:49:05 +0000
@@ -51,6 +51,12 @@
51 if r.dying {51 if r.dying {
52 panic("dying relationer must not join!")52 panic("dying relationer must not join!")
53 }53 }
54 // We need to make sure the state directory exists before we join the
55 // relation, lest a subsequent ReadAllStateDirs report local state that
56 // doesn't include relations recorded in remote state.
57 if err := r.dir.Ensure(); err != nil {
58 return err
59 }
54 // uniter.RelationUnit.EnterScope() sets the unit's private address60 // uniter.RelationUnit.EnterScope() sets the unit's private address
55 // internally automatically, so no need to set it here.61 // internally automatically, so no need to set it here.
56 return r.ru.EnterScope()62 return r.ru.EnterScope()
@@ -127,10 +133,6 @@
127 if err = r.dir.State().Validate(hi); err != nil {133 if err = r.dir.State().Validate(hi); err != nil {
128 return134 return
129 }135 }
130 // We are about to use the dir, ensure it's there.
131 if err = r.dir.Ensure(); err != nil {
132 return
133 }
134 if hi.Kind == hooks.RelationDeparted {136 if hi.Kind == hooks.RelationDeparted {
135 r.ctx.DeleteMember(hi.RemoteUnit)137 r.ctx.DeleteMember(hi.RemoteUnit)
136 } else if hi.RemoteUnit != "" {138 } else if hi.RemoteUnit != "" {
137139
=== modified file 'worker/uniter/relationer_test.go'
--- worker/uniter/relationer_test.go 2014-04-07 00:36:36 +0000
+++ worker/uniter/relationer_test.go 2014-04-10 12:49:05 +0000
@@ -4,8 +4,6 @@
4package uniter_test4package uniter_test
55
6import (6import (
7 "os"
8 "path/filepath"
9 "strconv"7 "strconv"
10 "strings"8 "strings"
11 "time"9 "time"
@@ -21,6 +19,7 @@
21 "launchpad.net/juju-core/state/api"19 "launchpad.net/juju-core/state/api"
22 apiuniter "launchpad.net/juju-core/state/api/uniter"20 apiuniter "launchpad.net/juju-core/state/api/uniter"
23 coretesting "launchpad.net/juju-core/testing"21 coretesting "launchpad.net/juju-core/testing"
22 ft "launchpad.net/juju-core/testing/filetesting"
24 "launchpad.net/juju-core/utils"23 "launchpad.net/juju-core/utils"
25 "launchpad.net/juju-core/worker/uniter"24 "launchpad.net/juju-core/worker/uniter"
26 "launchpad.net/juju-core/worker/uniter/hook"25 "launchpad.net/juju-core/worker/uniter/hook"
@@ -90,6 +89,29 @@
90 return ru, u89 return ru, u
91}90}
9291
92func (s *RelationerSuite) TestStateDir(c *gc.C) {
93 // Create the relationer; check its state dir is not created.
94 r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks)
95 path := strconv.Itoa(s.rel.Id())
96 ft.Removed{path}.Check(c, s.dirPath)
97
98 // Join the relation; check the dir was created.
99 err := r.Join()
100 c.Assert(err, gc.IsNil)
101 ft.Dir{path, 0755}.Check(c, s.dirPath)
102
103 // Prepare to depart the relation; check the dir is still there.
104 hi := hook.Info{Kind: hooks.RelationBroken}
105 _, err = r.PrepareHook(hi)
106 c.Assert(err, gc.IsNil)
107 ft.Dir{path, 0755}.Check(c, s.dirPath)
108
109 // Actually depart it; check the dir is removed.
110 err = r.CommitHook(hi)
111 c.Assert(err, gc.IsNil)
112 ft.Removed{path}.Check(c, s.dirPath)
113}
114
93func (s *RelationerSuite) TestEnterLeaveScope(c *gc.C) {115func (s *RelationerSuite) TestEnterLeaveScope(c *gc.C) {
94 ru1, _ := s.AddRelationUnit(c, "u/1")116 ru1, _ := s.AddRelationUnit(c, "u/1")
95 r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks)117 r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks)
@@ -134,11 +156,6 @@
134 _, err = r.PrepareHook(hi)156 _, err = r.PrepareHook(hi)
135 c.Assert(err, gc.IsNil)157 c.Assert(err, gc.IsNil)
136158
137 // Verify PrepareHook created the dir.
138 fi, err := os.Stat(filepath.Join(s.dirPath, strconv.Itoa(s.rel.Id())))
139 c.Assert(err, gc.IsNil)
140 c.Assert(fi, jc.Satisfies, os.FileInfo.IsDir)
141
142 err = r.CommitHook(hi)159 err = r.CommitHook(hi)
143 c.Assert(err, gc.IsNil)160 c.Assert(err, gc.IsNil)
144 s.State.StartSync()161 s.State.StartSync()
@@ -422,11 +439,9 @@
422 r := uniter.NewRelationer(apiRelUnit, dir, hooks)439 r := uniter.NewRelationer(apiRelUnit, dir, hooks)
423 c.Assert(r, jc.Satisfies, (*uniter.Relationer).IsImplicit)440 c.Assert(r, jc.Satisfies, (*uniter.Relationer).IsImplicit)
424441
425 // Join the relationer; the dir won't be created until necessary442 // Join the relation.
426 err = r.Join()443 err = r.Join()
427 c.Assert(err, gc.IsNil)444 c.Assert(err, gc.IsNil)
428 _, err = os.Stat(filepath.Join(relsDir, strconv.Itoa(rel.Id())))
429 c.Assert(err, gc.NotNil)
430 sub, err := logging.Unit("logging/0")445 sub, err := logging.Unit("logging/0")
431 c.Assert(err, gc.IsNil)446 c.Assert(err, gc.IsNil)
432447
@@ -444,11 +459,11 @@
444 c.Fatalf("unexpected hook generated")459 c.Fatalf("unexpected hook generated")
445 }460 }
446461
447 // Set it to Dying; check that the dir is removed.462 // Set it to Dying; check that the dir is removed immediately.
448 err = r.SetDying()463 err = r.SetDying()
449 c.Assert(err, gc.IsNil)464 c.Assert(err, gc.IsNil)
450 _, err = os.Stat(filepath.Join(relsDir, strconv.Itoa(rel.Id())))465 path := strconv.Itoa(rel.Id())
451 c.Assert(err, jc.Satisfies, os.IsNotExist)466 ft.Removed{path}.Check(c, relsDir)
452467
453 // Check that it left scope, by leaving scope on the other side and destroying468 // Check that it left scope, by leaving scope on the other side and destroying
454 // the relation.469 // the relation.
455470
=== modified file 'worker/uniter/uniter.go'
--- worker/uniter/uniter.go 2014-03-08 17:44:59 +0000
+++ worker/uniter/uniter.go 2014-04-10 12:49:05 +0000
@@ -29,6 +29,7 @@
29 "launchpad.net/juju-core/utils"29 "launchpad.net/juju-core/utils"
30 "launchpad.net/juju-core/utils/exec"30 "launchpad.net/juju-core/utils/exec"
31 "launchpad.net/juju-core/utils/fslock"31 "launchpad.net/juju-core/utils/fslock"
32 "launchpad.net/juju-core/worker"
32 "launchpad.net/juju-core/worker/uniter/charm"33 "launchpad.net/juju-core/worker/uniter/charm"
33 "launchpad.net/juju-core/worker/uniter/hook"34 "launchpad.net/juju-core/worker/uniter/hook"
34 "launchpad.net/juju-core/worker/uniter/jujuc"35 "launchpad.net/juju-core/worker/uniter/jujuc"
@@ -128,7 +129,7 @@
128 }()129 }()
129130
130 // Run modes until we encounter an error.131 // Run modes until we encounter an error.
131 mode := ModeInit132 mode := ModeContinue
132 for err == nil {133 for err == nil {
133 select {134 select {
134 case <-u.tomb.Dying():135 case <-u.tomb.Dying():
@@ -167,6 +168,13 @@
167 if err != nil {168 if err != nil {
168 return err169 return err
169 }170 }
171 if u.unit.Life() == params.Dead {
172 // If we started up already dead, we should not progress further. If we
173 // become Dead immediately after starting up, we may well complete any
174 // operations in progress before detecting it; but that race is fundamental
175 // and inescapable, whereas this one is not.
176 return worker.ErrTerminateAgent
177 }
170 if err = u.setupLocks(); err != nil {178 if err = u.setupLocks(); err != nil {
171 return err179 return err
172 }180 }
@@ -191,16 +199,6 @@
191 u.uuid = env.UUID()199 u.uuid = env.UUID()
192 u.envName = env.Name()200 u.envName = env.Name()
193201
194 runListenerSocketPath := filepath.Join(u.baseDir, RunListenerFile)
195 logger.Debugf("starting juju-run listener on unix:%s", runListenerSocketPath)
196 u.runListener, err = NewRunListener(u, runListenerSocketPath)
197 if err != nil {
198 return err
199 }
200 // The socket needs to have permissions 777 in order for other users to use it.
201 if err := os.Chmod(runListenerSocketPath, 0777); err != nil {
202 return err
203 }
204 u.relationers = map[int]*Relationer{}202 u.relationers = map[int]*Relationer{}
205 u.relationHooks = make(chan hook.Info)203 u.relationHooks = make(chan hook.Info)
206 u.charm = charm.NewGitDir(filepath.Join(u.baseDir, "charm"))204 u.charm = charm.NewGitDir(filepath.Join(u.baseDir, "charm"))
@@ -209,7 +207,20 @@
209 u.deployer = charm.NewGitDeployer(u.charm.Path(), deployerPath, bundles)207 u.deployer = charm.NewGitDeployer(u.charm.Path(), deployerPath, bundles)
210 u.sf = NewStateFile(filepath.Join(u.baseDir, "state", "uniter"))208 u.sf = NewStateFile(filepath.Join(u.baseDir, "state", "uniter"))
211 u.rand = rand.New(rand.NewSource(time.Now().Unix()))209 u.rand = rand.New(rand.NewSource(time.Now().Unix()))
212 return nil210
211 // If we start trying to listen for juju-run commands before we have valid
212 // relation state, surprising things will come to pass.
213 if err := u.restoreRelations(); err != nil {
214 return err
215 }
216 runListenerSocketPath := filepath.Join(u.baseDir, RunListenerFile)
217 logger.Debugf("starting juju-run listener on unix:%s", runListenerSocketPath)
218 u.runListener, err = NewRunListener(u, runListenerSocketPath)
219 if err != nil {
220 return err
221 }
222 // The socket needs to have permissions 777 in order for other users to use it.
223 return os.Chmod(runListenerSocketPath, 0777)
213}224}
214225
215func (u *Uniter) Kill() {226func (u *Uniter) Kill() {
@@ -522,35 +533,69 @@
522 return hookName533 return hookName
523}534}
524535
525// restoreRelations reconciles the supplied relation state dirs with the536// getJoinedRelations finds out what relations the unit is *really* part of,
537// working around the fact that pre-1.19 (1.18.1?) unit agents don't write a
538// state dir for a relation until a remote unit joins.
539func (u *Uniter) getJoinedRelations() (map[int]*uniter.Relation, error) {
540 var joinedRelationTags []string
541 for {
542 var err error
543 joinedRelationTags, err = u.unit.JoinedRelations()
544 if err == nil {
545 break
546 }
547 if params.IsCodeNotImplemented(err) {
548 logger.Infof("waiting for state server to be upgraded")
549 select {
550 case <-u.tomb.Dying():
551 return nil, tomb.ErrDying
552 case <-time.After(15 * time.Second):
553 continue
554 }
555 }
556 return nil, err
557 }
558 joinedRelations := make(map[int]*uniter.Relation)
559 for _, tag := range joinedRelationTags {
560 relation, err := u.st.Relation(tag)
561 if err != nil {
562 return nil, err
563 }
564 joinedRelations[relation.Id()] = relation
565 }
566 return joinedRelations, nil
567}
568
569// restoreRelations reconciles the local relation state dirs with the
526// remote state of the corresponding relations.570// remote state of the corresponding relations.
527func (u *Uniter) restoreRelations() error {571func (u *Uniter) restoreRelations() error {
528 // TODO(dimitern): Get these from state, not from disk.572 joinedRelations, err := u.getJoinedRelations()
529 dirs, err := relation.ReadAllStateDirs(u.relationsDir)573 if err != nil {
530 if err != nil {574 return err
531 return err575 }
532 }576 knownDirs, err := relation.ReadAllStateDirs(u.relationsDir)
533 for id, dir := range dirs {577 if err != nil {
534 remove := false578 return err
535 rel, err := u.st.RelationById(id)579 }
536 if params.IsCodeNotFoundOrCodeUnauthorized(err) {580 for id, dir := range knownDirs {
537 remove = true581 if rel, ok := joinedRelations[id]; ok {
538 } else if err != nil {582 if err := u.addRelation(rel, dir); err != nil {
539 return err583 return err
540 }
541 err = u.addRelation(rel, dir)
542 if params.IsCodeCannotEnterScope(err) {
543 remove = true
544 } else if err != nil {
545 return err
546 }
547 if remove {
548 // If the previous execution was interrupted in the process of
549 // joining or departing the relation, the directory will be empty
550 // and the state is sane.
551 if err := dir.Remove(); err != nil {
552 return fmt.Errorf("cannot synchronize relation state: %v", err)
553 }584 }
585 } else if err := dir.Remove(); err != nil {
586 return err
587 }
588 }
589 for id, rel := range joinedRelations {
590 if _, ok := knownDirs[id]; ok {
591 continue
592 }
593 dir, err := relation.ReadStateDir(u.relationsDir, id)
594 if err != nil {
595 return err
596 }
597 if err := u.addRelation(rel, dir); err != nil {
598 return err
554 }599 }
555 }600 }
556 return nil601 return nil
557602
=== modified file 'worker/uniter/uniter_test.go'
--- worker/uniter/uniter_test.go 2014-04-02 11:35:49 +0000
+++ worker/uniter/uniter_test.go 2014-04-10 12:49:05 +0000
@@ -32,6 +32,7 @@
32 "launchpad.net/juju-core/state/api/params"32 "launchpad.net/juju-core/state/api/params"
33 apiuniter "launchpad.net/juju-core/state/api/uniter"33 apiuniter "launchpad.net/juju-core/state/api/uniter"
34 coretesting "launchpad.net/juju-core/testing"34 coretesting "launchpad.net/juju-core/testing"
35 ft "launchpad.net/juju-core/testing/filetesting"
35 "launchpad.net/juju-core/utils"36 "launchpad.net/juju-core/utils"
36 utilexec "launchpad.net/juju-core/utils/exec"37 utilexec "launchpad.net/juju-core/utils/exec"
37 "launchpad.net/juju-core/utils/fslock"38 "launchpad.net/juju-core/utils/fslock"
@@ -966,9 +967,58 @@
966 waitHooks{},967 waitHooks{},
967 // TODO BUG(?): the unit doesn't leave the scope, leaving the relation968 // TODO BUG(?): the unit doesn't leave the scope, leaving the relation
968 // unkillable without direct intervention. I'm pretty sure it's not a969 // unkillable without direct intervention. I'm pretty sure it's not a
969 // uniter bug -- it should be the responisbility of `juju remove-unit970 // uniter bug -- it should be the responsibility of `juju remove-unit
970 // --force` to cause the unit to leave any relation scopes it may be971 // --force` to cause the unit to leave any relation scopes it may be
971 // in -- but it's worth noting here all the same.972 // in -- but it's worth noting here all the same.
973 ), ut(
974 "unknown local relation dir is removed",
975 quickStartRelation{},
976 stopUniter{},
977 custom{func(c *gc.C, ctx *context) {
978 ft.Dir{"state/relations/90210", 0755}.Create(c, ctx.path)
979 }},
980 startUniter{},
981 waitHooks{"config-changed"},
982 custom{func(c *gc.C, ctx *context) {
983 ft.Removed{"state/relations/90210"}.Check(c, ctx.path)
984 }},
985 ), ut(
986 "all relations are available to config-changed on bounce, even if state dir is missing",
987 createCharm{
988 customize: func(c *gc.C, ctx *context, path string) {
989 script := "relation-ids db > relations.out && chmod 644 relations.out"
990 appendHook(c, path, "config-changed", script)
991 },
992 },
993 serveCharm{},
994 createUniter{},
995 waitUnit{
996 status: params.StatusStarted,
997 },
998 waitHooks{"install", "config-changed", "start"},
999 addRelation{waitJoin: true},
1000 stopUniter{},
1001 custom{func(c *gc.C, ctx *context) {
1002 // Check the state dir was created, and remove it.
1003 path := fmt.Sprintf("state/relations/%d", ctx.relation.Id())
1004 ft.Dir{path, 0755}.Check(c, ctx.path)
1005 ft.Removed{path}.Create(c, ctx.path)
1006
1007 // Check that config-changed didn't record any relations, because
1008 // they shouldn't been available until after the start hook.
1009 ft.File{"charm/relations.out", "", 0644}.Check(c, ctx.path)
1010 }},
1011 startUniter{},
1012 waitHooks{"config-changed"},
1013 custom{func(c *gc.C, ctx *context) {
1014 // Check the state dir was recreated.
1015 path := fmt.Sprintf("state/relations/%d", ctx.relation.Id())
1016 ft.Dir{path, 0755}.Check(c, ctx.path)
1017
1018 // Check that config-changed did record the joined relations.
1019 data := fmt.Sprintf("db:%d\n", ctx.relation.Id())
1020 ft.File{"charm/relations.out", data, 0644}.Check(c, ctx.path)
1021 }},
972 ),1022 ),
973}1023}
9741024
@@ -1671,7 +1721,7 @@
1671}1721}
16721722
1673type addRelation struct {1723type addRelation struct {
1674 testing.JujuConnSuite1724 waitJoin bool
1675}1725}
16761726
1677func (s addRelation) step(c *gc.C, ctx *context) {1727func (s addRelation) step(c *gc.C, ctx *context) {
@@ -1686,6 +1736,27 @@
1686 ctx.relation, err = ctx.st.AddRelation(eps...)1736 ctx.relation, err = ctx.st.AddRelation(eps...)
1687 c.Assert(err, gc.IsNil)1737 c.Assert(err, gc.IsNil)
1688 ctx.relationUnits = map[string]*state.RelationUnit{}1738 ctx.relationUnits = map[string]*state.RelationUnit{}
1739 if !s.waitJoin {
1740 return
1741 }
1742
1743 // It's hard to do this properly (watching scope) without perturbing other tests.
1744 ru, err := ctx.relation.Unit(ctx.unit)
1745 c.Assert(err, gc.IsNil)
1746 timeout := time.After(worstCase)
1747 for {
1748 c.Logf("waiting to join relation")
1749 select {
1750 case <-timeout:
1751 c.Fatalf("failed to join relation")
1752 case <-time.After(coretesting.ShortWait):
1753 inScope, err := ru.InScope()
1754 c.Assert(err, gc.IsNil)
1755 if inScope {
1756 return
1757 }
1758 }
1759 }
1689}1760}
16901761
1691type addRelationUnit struct{}1762type addRelationUnit struct{}

Subscribers

People subscribed via source and target branches

to status/vote changes: