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
1=== modified file 'state/api/agent/state.go'
2--- state/api/agent/state.go 2014-04-02 15:46:57 +0000
3+++ state/api/agent/state.go 2014-04-10 12:49:05 +0000
4@@ -32,7 +32,7 @@
5 return nil, err
6 }
7 if len(results.Entities) != 1 {
8- return nil, fmt.Errorf("expected one result, got %d", len(results.Entities))
9+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Entities))
10 }
11 if err := results.Entities[0].Error; err != nil {
12 return nil, err
13
14=== modified file 'state/api/common/life.go'
15--- state/api/common/life.go 2014-01-23 00:09:15 +0000
16+++ state/api/common/life.go 2014-04-10 12:49:05 +0000
17@@ -22,7 +22,7 @@
18 return "", err
19 }
20 if len(result.Results) != 1 {
21- return "", fmt.Errorf("expected one result, got %d", len(result.Results))
22+ return "", fmt.Errorf("expected 1 result, got %d", len(result.Results))
23 }
24 if err := result.Results[0].Error; err != nil {
25 return "", err
26
27=== modified file 'state/api/deployer/machine.go'
28--- state/api/deployer/machine.go 2014-03-24 13:48:45 +0000
29+++ state/api/deployer/machine.go 2014-04-10 12:49:05 +0000
30@@ -29,7 +29,7 @@
31 return nil, err
32 }
33 if len(results.Results) != 1 {
34- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
35+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
36 }
37 result := results.Results[0]
38 if result.Error != nil {
39
40=== modified file 'state/api/firewaller/machine.go'
41--- state/api/firewaller/machine.go 2014-03-24 13:27:29 +0000
42+++ state/api/firewaller/machine.go 2014-04-10 12:49:05 +0000
43@@ -29,7 +29,7 @@
44 return nil, err
45 }
46 if len(results.Results) != 1 {
47- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
48+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
49 }
50 result := results.Results[0]
51 if result.Error != nil {
52@@ -51,7 +51,7 @@
53 return "", err
54 }
55 if len(results.Results) != 1 {
56- return "", fmt.Errorf("expected one result, got %d", len(results.Results))
57+ return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
58 }
59 result := results.Results[0]
60 if result.Error != nil {
61
62=== modified file 'state/api/firewaller/service.go'
63--- state/api/firewaller/service.go 2014-03-24 13:27:29 +0000
64+++ state/api/firewaller/service.go 2014-04-10 12:49:05 +0000
65@@ -38,7 +38,7 @@
66 return nil, err
67 }
68 if len(results.Results) != 1 {
69- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
70+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
71 }
72 result := results.Results[0]
73 if result.Error != nil {
74@@ -80,7 +80,7 @@
75 return false, err
76 }
77 if len(results.Results) != 1 {
78- return false, fmt.Errorf("expected one result, got %d", len(results.Results))
79+ return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
80 }
81 result := results.Results[0]
82 if result.Error != nil {
83
84=== modified file 'state/api/firewaller/unit.go'
85--- state/api/firewaller/unit.go 2014-03-24 13:27:29 +0000
86+++ state/api/firewaller/unit.go 2014-04-10 12:49:05 +0000
87@@ -54,7 +54,7 @@
88 return nil, err
89 }
90 if len(results.Results) != 1 {
91- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
92+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
93 }
94 result := results.Results[0]
95 if result.Error != nil {
96@@ -94,7 +94,7 @@
97 return nil, err
98 }
99 if len(results.Results) != 1 {
100- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
101+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
102 }
103 result := results.Results[0]
104 if result.Error != nil {
105@@ -115,7 +115,7 @@
106 return "", err
107 }
108 if len(results.Results) != 1 {
109- return "", fmt.Errorf("expected one result, got %d", len(results.Results))
110+ return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
111 }
112 result := results.Results[0]
113 if result.Error != nil {
114
115=== modified file 'state/api/keyupdater/authorisedkeys.go'
116--- state/api/keyupdater/authorisedkeys.go 2014-03-24 13:55:21 +0000
117+++ state/api/keyupdater/authorisedkeys.go 2014-04-10 12:49:05 +0000
118@@ -38,7 +38,7 @@
119 }
120 if len(results.Results) != 1 {
121 // TODO: Not directly tested
122- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
123+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
124 }
125 result := results.Results[0]
126 if err := result.Error; err != nil {
127@@ -61,7 +61,7 @@
128 }
129 if len(results.Results) != 1 {
130 // TODO: Not directly tested
131- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
132+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
133 }
134 result := results.Results[0]
135 if result.Error != nil {
136
137=== modified file 'state/api/logger/logger.go'
138--- state/api/logger/logger.go 2014-03-24 14:10:25 +0000
139+++ state/api/logger/logger.go 2014-04-10 12:49:05 +0000
140@@ -40,7 +40,7 @@
141 }
142 if len(results.Results) != 1 {
143 // TODO: Not directly tested
144- return "", fmt.Errorf("expected one result, got %d", len(results.Results))
145+ return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
146 }
147 result := results.Results[0]
148 if err := result.Error; err != nil {
149@@ -63,7 +63,7 @@
150 }
151 if len(results.Results) != 1 {
152 // TODO: Not directly tested
153- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
154+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
155 }
156 result := results.Results[0]
157 if result.Error != nil {
158
159=== modified file 'state/api/machiner/machine.go'
160--- state/api/machiner/machine.go 2014-03-26 06:28:38 +0000
161+++ state/api/machiner/machine.go 2014-04-10 12:49:05 +0000
162@@ -93,7 +93,7 @@
163 return nil, err
164 }
165 if len(results.Results) != 1 {
166- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
167+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
168 }
169 result := results.Results[0]
170 if result.Error != nil {
171
172=== modified file 'state/api/params/params.go'
173--- state/api/params/params.go 2014-04-10 08:47:38 +0000
174+++ state/api/params/params.go 2014-04-10 12:49:05 +0000
175@@ -50,7 +50,7 @@
176 // of a bulk operation on a single value.
177 func (result ErrorResults) OneError() error {
178 if n := len(result.Results); n != 1 {
179- return fmt.Errorf("expected one result, got %d", n)
180+ return fmt.Errorf("expected 1 result, got %d", n)
181 }
182 if err := result.Results[0].Error; err != nil {
183 return err
184
185=== modified file 'state/api/provisioner/machine.go'
186--- state/api/provisioner/machine.go 2014-04-09 15:08:51 +0000
187+++ state/api/provisioner/machine.go 2014-04-10 12:49:05 +0000
188@@ -65,7 +65,7 @@
189 return nil, nil, err
190 }
191 if len(results.Results) != 1 {
192- return nil, nil, fmt.Errorf("expected one result, got %d", len(results.Results))
193+ return nil, nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
194 }
195 result := results.Results[0]
196 if result.Error != nil {
197@@ -100,7 +100,7 @@
198 return "", "", err
199 }
200 if len(results.Results) != 1 {
201- return "", "", fmt.Errorf("expected one result, got %d", len(results.Results))
202+ return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
203 }
204 result := results.Results[0]
205 if result.Error != nil {
206@@ -122,7 +122,7 @@
207 return nothing, err
208 }
209 if len(results.Results) != 1 {
210- return nothing, fmt.Errorf("expected one result, got %d", len(results.Results))
211+ return nothing, fmt.Errorf("expected 1 result, got %d", len(results.Results))
212 }
213 result := results.Results[0]
214 if result.Error != nil {
215@@ -173,7 +173,7 @@
216 return "", err
217 }
218 if len(results.Results) != 1 {
219- return "", fmt.Errorf("expected one result, got %d", len(results.Results))
220+ return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
221 }
222 result := results.Results[0]
223 if result.Error != nil {
224@@ -196,7 +196,7 @@
225 return nil, err
226 }
227 if len(results.Results) != 1 {
228- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
229+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
230 }
231 result := results.Results[0]
232 if result.Error != nil {
233@@ -242,7 +242,7 @@
234 return "", err
235 }
236 if len(results.Results) != 1 {
237- return "", fmt.Errorf("expected one result, got %d", len(results.Results))
238+ return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
239 }
240 result := results.Results[0]
241 if result.Error != nil {
242@@ -293,7 +293,7 @@
243 return nil, err
244 }
245 if len(results.Results) != 1 {
246- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
247+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
248 }
249 result := results.Results[0]
250 if result.Error != nil {
251@@ -317,7 +317,7 @@
252 return nil, err
253 }
254 if len(results.Results) != 1 {
255- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
256+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
257 }
258 result := results.Results[0]
259 if result.Error != nil {
260@@ -340,7 +340,7 @@
261 return err
262 }
263 if len(results.Results) != 1 {
264- return fmt.Errorf("expected one result, got %d", len(results.Results))
265+ return fmt.Errorf("expected 1 result, got %d", len(results.Results))
266 }
267 apiError := results.Results[0].Error
268 if apiError != nil {
269
270=== modified file 'state/api/provisioner/provisioner.go'
271--- state/api/provisioner/provisioner.go 2014-04-08 16:40:04 +0000
272+++ state/api/provisioner/provisioner.go 2014-04-10 12:49:05 +0000
273@@ -106,7 +106,7 @@
274 }
275 if len(results.Results) != 1 {
276 // TODO: Not directly tested
277- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
278+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
279 }
280 result := results.Results[0]
281 if err := result.Error; err != nil {
282
283=== modified file 'state/api/uniter/charm.go'
284--- state/api/uniter/charm.go 2014-03-24 10:48:25 +0000
285+++ state/api/uniter/charm.go 2014-04-10 12:49:05 +0000
286@@ -41,7 +41,7 @@
287 return "", err
288 }
289 if len(results.Results) != 1 {
290- return "", fmt.Errorf("expected one result, got %d", len(results.Results))
291+ return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
292 }
293 result := results.Results[0]
294 if result.Error != nil {
295@@ -70,7 +70,7 @@
296 return nil, false, err
297 }
298 if len(results.Results) != 1 {
299- return nil, false, fmt.Errorf("expected one result, got %d", len(results.Results))
300+ return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
301 }
302 result := results.Results[0]
303 if result.Error != nil {
304
305=== modified file 'state/api/uniter/relationunit.go'
306--- state/api/uniter/relationunit.go 2014-03-21 18:40:43 +0000
307+++ state/api/uniter/relationunit.go 2014-04-10 12:49:05 +0000
308@@ -117,7 +117,7 @@
309 return nil, err
310 }
311 if len(results.Results) != 1 {
312- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
313+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
314 }
315 result := results.Results[0]
316 if result.Error != nil {
317@@ -148,7 +148,7 @@
318 return nil, err
319 }
320 if len(results.Results) != 1 {
321- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
322+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
323 }
324 result := results.Results[0]
325 if result.Error != nil {
326@@ -172,7 +172,7 @@
327 return nil, err
328 }
329 if len(results.Results) != 1 {
330- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
331+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
332 }
333 result := results.Results[0]
334 if result.Error != nil {
335
336=== modified file 'state/api/uniter/service.go'
337--- state/api/uniter/service.go 2014-03-21 18:40:43 +0000
338+++ state/api/uniter/service.go 2014-04-10 12:49:05 +0000
339@@ -47,7 +47,7 @@
340 return nil, err
341 }
342 if len(results.Results) != 1 {
343- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
344+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
345 }
346 result := results.Results[0]
347 if result.Error != nil {
348@@ -69,7 +69,7 @@
349 return nil, err
350 }
351 if len(results.Results) != 1 {
352- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
353+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
354 }
355 result := results.Results[0]
356 if result.Error != nil {
357@@ -111,7 +111,7 @@
358 return nil, false, err
359 }
360 if len(results.Results) != 1 {
361- return nil, false, fmt.Errorf("expected one result, got %d", len(results.Results))
362+ return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
363 }
364 result := results.Results[0]
365 if result.Error != nil {
366
367=== modified file 'state/api/uniter/unit.go'
368--- state/api/uniter/unit.go 2014-04-01 03:39:51 +0000
369+++ state/api/uniter/unit.go 2014-04-10 12:49:05 +0000
370@@ -94,7 +94,7 @@
371 return nil, err
372 }
373 if len(results.Results) != 1 {
374- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
375+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
376 }
377 result := results.Results[0]
378 if result.Error != nil {
379@@ -134,7 +134,7 @@
380 return nil, err
381 }
382 if len(results.Results) != 1 {
383- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
384+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
385 }
386 result := results.Results[0]
387 if result.Error != nil {
388@@ -197,7 +197,7 @@
389 return "", err
390 }
391 if len(results.Results) != 1 {
392- return "", fmt.Errorf("expected one result, got %d", len(results.Results))
393+ return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
394 }
395 result := results.Results[0]
396 if result.Error != nil {
397@@ -221,7 +221,7 @@
398 return false, err
399 }
400 if len(results.Results) != 1 {
401- return false, fmt.Errorf("expected one result, got %d", len(results.Results))
402+ return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
403 }
404 result := results.Results[0]
405 if result.Error != nil {
406@@ -242,7 +242,7 @@
407 return false, err
408 }
409 if len(results.Results) != 1 {
410- return false, fmt.Errorf("expected one result, got %d", len(results.Results))
411+ return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
412 }
413 result := results.Results[0]
414 if result.Error != nil {
415@@ -269,7 +269,7 @@
416 return "", err
417 }
418 if len(results.Results) != 1 {
419- return "", fmt.Errorf("expected one result, got %d", len(results.Results))
420+ return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
421 }
422 result := results.Results[0]
423 if result.Error != nil {
424@@ -296,7 +296,7 @@
425 return "", err
426 }
427 if len(results.Results) != 1 {
428- return "", fmt.Errorf("expected one result, got %d", len(results.Results))
429+ return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
430 }
431 result := results.Results[0]
432 if result.Error != nil {
433@@ -359,7 +359,7 @@
434 return nil, err
435 }
436 if len(results.Results) != 1 {
437- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
438+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
439 }
440 result := results.Results[0]
441 if result.Error != nil {
442@@ -421,7 +421,7 @@
443 return nil, err
444 }
445 if len(results.Results) != 1 {
446- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
447+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
448 }
449 result := results.Results[0]
450 if result.Error != nil {
451@@ -430,3 +430,23 @@
452 w := watcher.NewNotifyWatcher(u.st.caller, result)
453 return w, nil
454 }
455+
456+// JoinedRelations returns the tags of the relations the unit has joined.
457+func (u *Unit) JoinedRelations() ([]string, error) {
458+ var results params.StringsResults
459+ args := params.Entities{
460+ Entities: []params.Entity{{Tag: u.tag}},
461+ }
462+ err := u.st.call("JoinedRelations", args, &results)
463+ if err != nil {
464+ return nil, err
465+ }
466+ if len(results.Results) != 1 {
467+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
468+ }
469+ result := results.Results[0]
470+ if result.Error != nil {
471+ return nil, result.Error
472+ }
473+ return result.Result, nil
474+}
475
476=== modified file 'state/api/uniter/unit_test.go'
477--- state/api/uniter/unit_test.go 2014-04-07 00:36:36 +0000
478+++ state/api/uniter/unit_test.go 2014-04-10 12:49:05 +0000
479@@ -4,6 +4,8 @@
480 package uniter_test
481
482 import (
483+ "sort"
484+
485 jc "github.com/juju/testing/checkers"
486 gc "launchpad.net/gocheck"
487
488@@ -355,3 +357,20 @@
489 c.Assert(s.apiUnit.ServiceName(), gc.Equals, "wordpress")
490 c.Assert(s.apiUnit.ServiceTag(), gc.Equals, "service-wordpress")
491 }
492+
493+func (s *unitSuite) TestJoinedRelations(c *gc.C) {
494+ joinedRelations, err := s.apiUnit.JoinedRelations()
495+ c.Assert(err, gc.IsNil)
496+ c.Assert(joinedRelations, gc.HasLen, 0)
497+
498+ rel1, _, _ := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
499+ joinedRelations, err = s.apiUnit.JoinedRelations()
500+ c.Assert(err, gc.IsNil)
501+ c.Assert(joinedRelations, gc.DeepEquals, []string{rel1.Tag()})
502+
503+ rel2, _, _ := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
504+ joinedRelations, err = s.apiUnit.JoinedRelations()
505+ c.Assert(err, gc.IsNil)
506+ sort.Strings(joinedRelations)
507+ c.Assert(joinedRelations, gc.DeepEquals, []string{rel2.Tag(), rel1.Tag()})
508+}
509
510=== modified file 'state/api/uniter/uniter.go'
511--- state/api/uniter/uniter.go 2014-03-21 18:46:48 +0000
512+++ state/api/uniter/uniter.go 2014-04-10 12:49:05 +0000
513@@ -58,7 +58,7 @@
514 return nothing, err
515 }
516 if len(result.Results) != 1 {
517- return nothing, fmt.Errorf("expected one result, got %d", len(result.Results))
518+ return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results))
519 }
520 if err := result.Results[0].Error; err != nil {
521 return nothing, err
522@@ -134,7 +134,7 @@
523 }, nil
524 }
525
526-// Relation returns the existing relation with the given tag.
527+// RelationById returns the existing relation with the given id.
528 func (st *State) RelationById(id int) (*Relation, error) {
529 var results params.RelationResults
530 args := params.RelationIds{
531@@ -145,7 +145,7 @@
532 return nil, err
533 }
534 if len(results.Results) != 1 {
535- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
536+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
537 }
538 result := results.Results[0]
539 if err := result.Error; err != nil {
540
541=== modified file 'state/api/upgrader/upgrader.go'
542--- state/api/upgrader/upgrader.go 2014-03-24 13:48:45 +0000
543+++ state/api/upgrader/upgrader.go 2014-04-10 12:49:05 +0000
544@@ -60,7 +60,7 @@
545 }
546 if len(results.Results) != 1 {
547 // TODO: Not directly tested
548- return version.Number{}, fmt.Errorf("expected one result, got %d", len(results.Results))
549+ return version.Number{}, fmt.Errorf("expected 1 result, got %d", len(results.Results))
550 }
551 result := results.Results[0]
552 if err := result.Error; err != nil {
553@@ -87,7 +87,7 @@
554 }
555 if len(results.Results) != 1 {
556 // TODO: Not directly tested
557- return nil, false, fmt.Errorf("expected one result, got %d", len(results.Results))
558+ return nil, false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
559 }
560 result := results.Results[0]
561 if err := result.Error; err != nil {
562@@ -112,7 +112,7 @@
563 }
564 if len(results.Results) != 1 {
565 // TODO: Not directly tested
566- return nil, fmt.Errorf("expected one result, got %d", len(results.Results))
567+ return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
568 }
569 result := results.Results[0]
570 if result.Error != nil {
571
572=== modified file 'state/apiserver/uniter/uniter.go'
573--- state/apiserver/uniter/uniter.go 2014-04-07 00:36:36 +0000
574+++ state/apiserver/uniter/uniter.go 2014-04-10 12:49:05 +0000
575@@ -693,6 +693,44 @@
576 return result, nil
577 }
578
579+func joinedRelationTags(unit *state.Unit) ([]string, error) {
580+ relations, err := unit.JoinedRelations()
581+ if err != nil {
582+ return nil, err
583+ }
584+ tags := make([]string, len(relations))
585+ for i, relation := range relations {
586+ tags[i] = relation.Tag()
587+ }
588+ return tags, nil
589+}
590+
591+// JoinedRelations returns the tags of all relations each supplied unit has joined.
592+func (u *UniterAPI) JoinedRelations(args params.Entities) (params.StringsResults, error) {
593+ result := params.StringsResults{
594+ Results: make([]params.StringsResult, len(args.Entities)),
595+ }
596+ if len(args.Entities) == 0 {
597+ return result, nil
598+ }
599+ canRead, err := u.accessUnit()
600+ if err != nil {
601+ return params.StringsResults{}, err
602+ }
603+ for i, entity := range args.Entities {
604+ err := common.ErrPerm
605+ if canRead(entity.Tag) {
606+ var unit *state.Unit
607+ unit, err = u.getUnit(entity.Tag)
608+ if err == nil {
609+ result.Results[i].Result, err = joinedRelationTags(unit)
610+ }
611+ }
612+ result.Results[i].Error = common.ServerError(err)
613+ }
614+ return result, nil
615+}
616+
617 // CurrentEnvironUUID returns the UUID for the current juju environment.
618 func (u *UniterAPI) CurrentEnvironUUID() (params.StringResult, error) {
619 result := params.StringResult{}
620
621=== modified file 'state/apiserver/uniter/uniter_test.go'
622--- state/apiserver/uniter/uniter_test.go 2014-04-07 00:36:36 +0000
623+++ state/apiserver/uniter/uniter_test.go 2014-04-10 12:49:05 +0000
624@@ -1048,6 +1048,37 @@
625 c.Assert(readSettings, gc.DeepEquals, settings)
626 }
627
628+func (s *uniterSuite) TestJoinedRelations(c *gc.C) {
629+ rel := s.addRelation(c, "wordpress", "mysql")
630+ relUnit, err := rel.Unit(s.wordpressUnit)
631+ c.Assert(err, gc.IsNil)
632+ err = relUnit.EnterScope(nil)
633+ c.Assert(err, gc.IsNil)
634+
635+ args := params.Entities{
636+ Entities: []params.Entity{
637+ {s.wordpressUnit.Tag()},
638+ {s.mysqlUnit.Tag()},
639+ {"unit-unknown-1"},
640+ {"service-wordpress"},
641+ {"machine-0"},
642+ {rel.Tag()},
643+ },
644+ }
645+ result, err := s.uniter.JoinedRelations(args)
646+ c.Assert(err, gc.IsNil)
647+ c.Assert(result, gc.DeepEquals, params.StringsResults{
648+ Results: []params.StringsResult{
649+ {Result: []string{rel.Tag()}},
650+ {Error: apiservertesting.ErrUnauthorized},
651+ {Error: apiservertesting.ErrUnauthorized},
652+ {Error: apiservertesting.ErrUnauthorized},
653+ {Error: apiservertesting.ErrUnauthorized},
654+ {Error: apiservertesting.ErrUnauthorized},
655+ },
656+ })
657+}
658+
659 func (s *uniterSuite) TestReadSettings(c *gc.C) {
660 rel := s.addRelation(c, "wordpress", "mysql")
661 relUnit, err := rel.Unit(s.wordpressUnit)
662
663=== modified file 'state/unit.go'
664--- state/unit.go 2014-04-07 00:36:36 +0000
665+++ state/unit.go 2014-04-10 12:49:05 +0000
666@@ -481,6 +481,27 @@
667 return names
668 }
669
670+// JoinedRelations returns the relations for which the unit is in scope.
671+func (u *Unit) JoinedRelations() ([]*Relation, error) {
672+ candidates, err := serviceRelations(u.st, u.doc.Service)
673+ if err != nil {
674+ return nil, err
675+ }
676+ var joinedRelations []*Relation
677+ for _, relation := range candidates {
678+ relationUnit, err := relation.Unit(u)
679+ if err != nil {
680+ return nil, err
681+ }
682+ if inScope, err := relationUnit.InScope(); err != nil {
683+ return nil, err
684+ } else if inScope {
685+ joinedRelations = append(joinedRelations, relation)
686+ }
687+ }
688+ return joinedRelations, nil
689+}
690+
691 // DeployerTag returns the tag of the agent responsible for deploying
692 // the unit. If no such entity can be determined, false is returned.
693 func (u *Unit) DeployerTag() (string, bool) {
694
695=== modified file 'state/unit_test.go'
696--- state/unit_test.go 2014-04-07 00:36:36 +0000
697+++ state/unit_test.go 2014-04-10 12:49:05 +0000
698@@ -993,6 +993,47 @@
699 c.Assert(principal, gc.Equals, "")
700 }
701
702+func (s *UnitSuite) TestJoinedRelations(c *gc.C) {
703+ wordpress0 := s.unit
704+ mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
705+ mysql0, err := mysql.AddUnit()
706+ c.Assert(err, gc.IsNil)
707+ eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"})
708+ c.Assert(err, gc.IsNil)
709+ rel, err := s.State.AddRelation(eps...)
710+ c.Assert(err, gc.IsNil)
711+
712+ assertJoinedRelations := func(unit *state.Unit, expect ...*state.Relation) {
713+ actual, err := unit.JoinedRelations()
714+ c.Assert(err, gc.IsNil)
715+ c.Assert(actual, gc.HasLen, len(expect))
716+ for i, a := range actual {
717+ c.Assert(a.Id(), gc.Equals, expect[i].Id())
718+ }
719+ }
720+ assertJoinedRelations(wordpress0)
721+ assertJoinedRelations(mysql0)
722+
723+ mysql0ru, err := rel.Unit(mysql0)
724+ c.Assert(err, gc.IsNil)
725+ err = mysql0ru.EnterScope(nil)
726+ c.Assert(err, gc.IsNil)
727+ assertJoinedRelations(wordpress0)
728+ assertJoinedRelations(mysql0, rel)
729+
730+ wordpress0ru, err := rel.Unit(wordpress0)
731+ c.Assert(err, gc.IsNil)
732+ err = wordpress0ru.EnterScope(nil)
733+ c.Assert(err, gc.IsNil)
734+ assertJoinedRelations(wordpress0, rel)
735+ assertJoinedRelations(mysql0, rel)
736+
737+ err = mysql0ru.LeaveScope()
738+ c.Assert(err, gc.IsNil)
739+ assertJoinedRelations(wordpress0, rel)
740+ assertJoinedRelations(mysql0)
741+}
742+
743 func (s *UnitSuite) TestRemove(c *gc.C) {
744 err := s.unit.Remove()
745 c.Assert(err, gc.ErrorMatches, `cannot remove unit "wordpress/0": unit is not dead`)
746
747=== modified file 'testing/filetesting/filetesting_test.go'
748--- testing/filetesting/filetesting_test.go 2014-04-02 07:54:19 +0000
749+++ testing/filetesting/filetesting_test.go 2014-04-10 12:49:05 +0000
750@@ -105,6 +105,7 @@
751
752 func (s *EntrySuite) TestDirCreateFailure(c *gc.C) {
753 os.Chmod(s.basePath, 0444)
754+ defer os.Chmod(s.basePath, 0777)
755 c.ExpectFailure("should fail to create file")
756 ft.Dir{"foobar", 0750}.Create(c, s.basePath)
757 }
758@@ -194,6 +195,7 @@
759 func (s *EntrySuite) TestRemovedCreateFailure(c *gc.C) {
760 ft.File{"some-file", "content", 0644}.Create(c, s.basePath)
761 os.Chmod(s.basePath, 0444)
762+ defer os.Chmod(s.basePath, 0777)
763 c.ExpectFailure("should fail to remove file")
764 ft.Removed{"some-file"}.Create(c, s.basePath)
765 }
766
767=== modified file 'worker/uniter/modes.go'
768--- worker/uniter/modes.go 2014-04-02 11:35:49 +0000
769+++ worker/uniter/modes.go 2014-04-10 12:49:05 +0000
770@@ -22,16 +22,6 @@
771 // states of a running Uniter.
772 type Mode func(u *Uniter) (Mode, error)
773
774-// ModeInit is the initial Uniter mode.
775-func ModeInit(u *Uniter) (next Mode, err error) {
776- defer modeContext("ModeInit", &err)()
777- logger.Infof("reconciling relation state")
778- if err := u.restoreRelations(); err != nil {
779- return nil, err
780- }
781- return ModeContinue, nil
782-}
783-
784 // ModeContinue determines what action to take based on persistent uniter state.
785 func ModeContinue(u *Uniter) (next Mode, err error) {
786 defer modeContext("ModeContinue", &err)()
787
788=== modified file 'worker/uniter/relationer.go'
789--- worker/uniter/relationer.go 2013-09-12 16:29:52 +0000
790+++ worker/uniter/relationer.go 2014-04-10 12:49:05 +0000
791@@ -51,6 +51,12 @@
792 if r.dying {
793 panic("dying relationer must not join!")
794 }
795+ // We need to make sure the state directory exists before we join the
796+ // relation, lest a subsequent ReadAllStateDirs report local state that
797+ // doesn't include relations recorded in remote state.
798+ if err := r.dir.Ensure(); err != nil {
799+ return err
800+ }
801 // uniter.RelationUnit.EnterScope() sets the unit's private address
802 // internally automatically, so no need to set it here.
803 return r.ru.EnterScope()
804@@ -127,10 +133,6 @@
805 if err = r.dir.State().Validate(hi); err != nil {
806 return
807 }
808- // We are about to use the dir, ensure it's there.
809- if err = r.dir.Ensure(); err != nil {
810- return
811- }
812 if hi.Kind == hooks.RelationDeparted {
813 r.ctx.DeleteMember(hi.RemoteUnit)
814 } else if hi.RemoteUnit != "" {
815
816=== modified file 'worker/uniter/relationer_test.go'
817--- worker/uniter/relationer_test.go 2014-04-07 00:36:36 +0000
818+++ worker/uniter/relationer_test.go 2014-04-10 12:49:05 +0000
819@@ -4,8 +4,6 @@
820 package uniter_test
821
822 import (
823- "os"
824- "path/filepath"
825 "strconv"
826 "strings"
827 "time"
828@@ -21,6 +19,7 @@
829 "launchpad.net/juju-core/state/api"
830 apiuniter "launchpad.net/juju-core/state/api/uniter"
831 coretesting "launchpad.net/juju-core/testing"
832+ ft "launchpad.net/juju-core/testing/filetesting"
833 "launchpad.net/juju-core/utils"
834 "launchpad.net/juju-core/worker/uniter"
835 "launchpad.net/juju-core/worker/uniter/hook"
836@@ -90,6 +89,29 @@
837 return ru, u
838 }
839
840+func (s *RelationerSuite) TestStateDir(c *gc.C) {
841+ // Create the relationer; check its state dir is not created.
842+ r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks)
843+ path := strconv.Itoa(s.rel.Id())
844+ ft.Removed{path}.Check(c, s.dirPath)
845+
846+ // Join the relation; check the dir was created.
847+ err := r.Join()
848+ c.Assert(err, gc.IsNil)
849+ ft.Dir{path, 0755}.Check(c, s.dirPath)
850+
851+ // Prepare to depart the relation; check the dir is still there.
852+ hi := hook.Info{Kind: hooks.RelationBroken}
853+ _, err = r.PrepareHook(hi)
854+ c.Assert(err, gc.IsNil)
855+ ft.Dir{path, 0755}.Check(c, s.dirPath)
856+
857+ // Actually depart it; check the dir is removed.
858+ err = r.CommitHook(hi)
859+ c.Assert(err, gc.IsNil)
860+ ft.Removed{path}.Check(c, s.dirPath)
861+}
862+
863 func (s *RelationerSuite) TestEnterLeaveScope(c *gc.C) {
864 ru1, _ := s.AddRelationUnit(c, "u/1")
865 r := uniter.NewRelationer(s.apiRelUnit, s.dir, s.hooks)
866@@ -134,11 +156,6 @@
867 _, err = r.PrepareHook(hi)
868 c.Assert(err, gc.IsNil)
869
870- // Verify PrepareHook created the dir.
871- fi, err := os.Stat(filepath.Join(s.dirPath, strconv.Itoa(s.rel.Id())))
872- c.Assert(err, gc.IsNil)
873- c.Assert(fi, jc.Satisfies, os.FileInfo.IsDir)
874-
875 err = r.CommitHook(hi)
876 c.Assert(err, gc.IsNil)
877 s.State.StartSync()
878@@ -422,11 +439,9 @@
879 r := uniter.NewRelationer(apiRelUnit, dir, hooks)
880 c.Assert(r, jc.Satisfies, (*uniter.Relationer).IsImplicit)
881
882- // Join the relationer; the dir won't be created until necessary
883+ // Join the relation.
884 err = r.Join()
885 c.Assert(err, gc.IsNil)
886- _, err = os.Stat(filepath.Join(relsDir, strconv.Itoa(rel.Id())))
887- c.Assert(err, gc.NotNil)
888 sub, err := logging.Unit("logging/0")
889 c.Assert(err, gc.IsNil)
890
891@@ -444,11 +459,11 @@
892 c.Fatalf("unexpected hook generated")
893 }
894
895- // Set it to Dying; check that the dir is removed.
896+ // Set it to Dying; check that the dir is removed immediately.
897 err = r.SetDying()
898 c.Assert(err, gc.IsNil)
899- _, err = os.Stat(filepath.Join(relsDir, strconv.Itoa(rel.Id())))
900- c.Assert(err, jc.Satisfies, os.IsNotExist)
901+ path := strconv.Itoa(rel.Id())
902+ ft.Removed{path}.Check(c, relsDir)
903
904 // Check that it left scope, by leaving scope on the other side and destroying
905 // the relation.
906
907=== modified file 'worker/uniter/uniter.go'
908--- worker/uniter/uniter.go 2014-03-08 17:44:59 +0000
909+++ worker/uniter/uniter.go 2014-04-10 12:49:05 +0000
910@@ -29,6 +29,7 @@
911 "launchpad.net/juju-core/utils"
912 "launchpad.net/juju-core/utils/exec"
913 "launchpad.net/juju-core/utils/fslock"
914+ "launchpad.net/juju-core/worker"
915 "launchpad.net/juju-core/worker/uniter/charm"
916 "launchpad.net/juju-core/worker/uniter/hook"
917 "launchpad.net/juju-core/worker/uniter/jujuc"
918@@ -128,7 +129,7 @@
919 }()
920
921 // Run modes until we encounter an error.
922- mode := ModeInit
923+ mode := ModeContinue
924 for err == nil {
925 select {
926 case <-u.tomb.Dying():
927@@ -167,6 +168,13 @@
928 if err != nil {
929 return err
930 }
931+ if u.unit.Life() == params.Dead {
932+ // If we started up already dead, we should not progress further. If we
933+ // become Dead immediately after starting up, we may well complete any
934+ // operations in progress before detecting it; but that race is fundamental
935+ // and inescapable, whereas this one is not.
936+ return worker.ErrTerminateAgent
937+ }
938 if err = u.setupLocks(); err != nil {
939 return err
940 }
941@@ -191,16 +199,6 @@
942 u.uuid = env.UUID()
943 u.envName = env.Name()
944
945- runListenerSocketPath := filepath.Join(u.baseDir, RunListenerFile)
946- logger.Debugf("starting juju-run listener on unix:%s", runListenerSocketPath)
947- u.runListener, err = NewRunListener(u, runListenerSocketPath)
948- if err != nil {
949- return err
950- }
951- // The socket needs to have permissions 777 in order for other users to use it.
952- if err := os.Chmod(runListenerSocketPath, 0777); err != nil {
953- return err
954- }
955 u.relationers = map[int]*Relationer{}
956 u.relationHooks = make(chan hook.Info)
957 u.charm = charm.NewGitDir(filepath.Join(u.baseDir, "charm"))
958@@ -209,7 +207,20 @@
959 u.deployer = charm.NewGitDeployer(u.charm.Path(), deployerPath, bundles)
960 u.sf = NewStateFile(filepath.Join(u.baseDir, "state", "uniter"))
961 u.rand = rand.New(rand.NewSource(time.Now().Unix()))
962- return nil
963+
964+ // If we start trying to listen for juju-run commands before we have valid
965+ // relation state, surprising things will come to pass.
966+ if err := u.restoreRelations(); err != nil {
967+ return err
968+ }
969+ runListenerSocketPath := filepath.Join(u.baseDir, RunListenerFile)
970+ logger.Debugf("starting juju-run listener on unix:%s", runListenerSocketPath)
971+ u.runListener, err = NewRunListener(u, runListenerSocketPath)
972+ if err != nil {
973+ return err
974+ }
975+ // The socket needs to have permissions 777 in order for other users to use it.
976+ return os.Chmod(runListenerSocketPath, 0777)
977 }
978
979 func (u *Uniter) Kill() {
980@@ -522,35 +533,69 @@
981 return hookName
982 }
983
984-// restoreRelations reconciles the supplied relation state dirs with the
985+// getJoinedRelations finds out what relations the unit is *really* part of,
986+// working around the fact that pre-1.19 (1.18.1?) unit agents don't write a
987+// state dir for a relation until a remote unit joins.
988+func (u *Uniter) getJoinedRelations() (map[int]*uniter.Relation, error) {
989+ var joinedRelationTags []string
990+ for {
991+ var err error
992+ joinedRelationTags, err = u.unit.JoinedRelations()
993+ if err == nil {
994+ break
995+ }
996+ if params.IsCodeNotImplemented(err) {
997+ logger.Infof("waiting for state server to be upgraded")
998+ select {
999+ case <-u.tomb.Dying():
1000+ return nil, tomb.ErrDying
1001+ case <-time.After(15 * time.Second):
1002+ continue
1003+ }
1004+ }
1005+ return nil, err
1006+ }
1007+ joinedRelations := make(map[int]*uniter.Relation)
1008+ for _, tag := range joinedRelationTags {
1009+ relation, err := u.st.Relation(tag)
1010+ if err != nil {
1011+ return nil, err
1012+ }
1013+ joinedRelations[relation.Id()] = relation
1014+ }
1015+ return joinedRelations, nil
1016+}
1017+
1018+// restoreRelations reconciles the local relation state dirs with the
1019 // remote state of the corresponding relations.
1020 func (u *Uniter) restoreRelations() error {
1021- // TODO(dimitern): Get these from state, not from disk.
1022- dirs, err := relation.ReadAllStateDirs(u.relationsDir)
1023- if err != nil {
1024- return err
1025- }
1026- for id, dir := range dirs {
1027- remove := false
1028- rel, err := u.st.RelationById(id)
1029- if params.IsCodeNotFoundOrCodeUnauthorized(err) {
1030- remove = true
1031- } else if err != nil {
1032- return err
1033- }
1034- err = u.addRelation(rel, dir)
1035- if params.IsCodeCannotEnterScope(err) {
1036- remove = true
1037- } else if err != nil {
1038- return err
1039- }
1040- if remove {
1041- // If the previous execution was interrupted in the process of
1042- // joining or departing the relation, the directory will be empty
1043- // and the state is sane.
1044- if err := dir.Remove(); err != nil {
1045- return fmt.Errorf("cannot synchronize relation state: %v", err)
1046+ joinedRelations, err := u.getJoinedRelations()
1047+ if err != nil {
1048+ return err
1049+ }
1050+ knownDirs, err := relation.ReadAllStateDirs(u.relationsDir)
1051+ if err != nil {
1052+ return err
1053+ }
1054+ for id, dir := range knownDirs {
1055+ if rel, ok := joinedRelations[id]; ok {
1056+ if err := u.addRelation(rel, dir); err != nil {
1057+ return err
1058 }
1059+ } else if err := dir.Remove(); err != nil {
1060+ return err
1061+ }
1062+ }
1063+ for id, rel := range joinedRelations {
1064+ if _, ok := knownDirs[id]; ok {
1065+ continue
1066+ }
1067+ dir, err := relation.ReadStateDir(u.relationsDir, id)
1068+ if err != nil {
1069+ return err
1070+ }
1071+ if err := u.addRelation(rel, dir); err != nil {
1072+ return err
1073 }
1074 }
1075 return nil
1076
1077=== modified file 'worker/uniter/uniter_test.go'
1078--- worker/uniter/uniter_test.go 2014-04-02 11:35:49 +0000
1079+++ worker/uniter/uniter_test.go 2014-04-10 12:49:05 +0000
1080@@ -32,6 +32,7 @@
1081 "launchpad.net/juju-core/state/api/params"
1082 apiuniter "launchpad.net/juju-core/state/api/uniter"
1083 coretesting "launchpad.net/juju-core/testing"
1084+ ft "launchpad.net/juju-core/testing/filetesting"
1085 "launchpad.net/juju-core/utils"
1086 utilexec "launchpad.net/juju-core/utils/exec"
1087 "launchpad.net/juju-core/utils/fslock"
1088@@ -966,9 +967,58 @@
1089 waitHooks{},
1090 // TODO BUG(?): the unit doesn't leave the scope, leaving the relation
1091 // unkillable without direct intervention. I'm pretty sure it's not a
1092- // uniter bug -- it should be the responisbility of `juju remove-unit
1093+ // uniter bug -- it should be the responsibility of `juju remove-unit
1094 // --force` to cause the unit to leave any relation scopes it may be
1095 // in -- but it's worth noting here all the same.
1096+ ), ut(
1097+ "unknown local relation dir is removed",
1098+ quickStartRelation{},
1099+ stopUniter{},
1100+ custom{func(c *gc.C, ctx *context) {
1101+ ft.Dir{"state/relations/90210", 0755}.Create(c, ctx.path)
1102+ }},
1103+ startUniter{},
1104+ waitHooks{"config-changed"},
1105+ custom{func(c *gc.C, ctx *context) {
1106+ ft.Removed{"state/relations/90210"}.Check(c, ctx.path)
1107+ }},
1108+ ), ut(
1109+ "all relations are available to config-changed on bounce, even if state dir is missing",
1110+ createCharm{
1111+ customize: func(c *gc.C, ctx *context, path string) {
1112+ script := "relation-ids db > relations.out && chmod 644 relations.out"
1113+ appendHook(c, path, "config-changed", script)
1114+ },
1115+ },
1116+ serveCharm{},
1117+ createUniter{},
1118+ waitUnit{
1119+ status: params.StatusStarted,
1120+ },
1121+ waitHooks{"install", "config-changed", "start"},
1122+ addRelation{waitJoin: true},
1123+ stopUniter{},
1124+ custom{func(c *gc.C, ctx *context) {
1125+ // Check the state dir was created, and remove it.
1126+ path := fmt.Sprintf("state/relations/%d", ctx.relation.Id())
1127+ ft.Dir{path, 0755}.Check(c, ctx.path)
1128+ ft.Removed{path}.Create(c, ctx.path)
1129+
1130+ // Check that config-changed didn't record any relations, because
1131+ // they shouldn't been available until after the start hook.
1132+ ft.File{"charm/relations.out", "", 0644}.Check(c, ctx.path)
1133+ }},
1134+ startUniter{},
1135+ waitHooks{"config-changed"},
1136+ custom{func(c *gc.C, ctx *context) {
1137+ // Check the state dir was recreated.
1138+ path := fmt.Sprintf("state/relations/%d", ctx.relation.Id())
1139+ ft.Dir{path, 0755}.Check(c, ctx.path)
1140+
1141+ // Check that config-changed did record the joined relations.
1142+ data := fmt.Sprintf("db:%d\n", ctx.relation.Id())
1143+ ft.File{"charm/relations.out", data, 0644}.Check(c, ctx.path)
1144+ }},
1145 ),
1146 }
1147
1148@@ -1671,7 +1721,7 @@
1149 }
1150
1151 type addRelation struct {
1152- testing.JujuConnSuite
1153+ waitJoin bool
1154 }
1155
1156 func (s addRelation) step(c *gc.C, ctx *context) {
1157@@ -1686,6 +1736,27 @@
1158 ctx.relation, err = ctx.st.AddRelation(eps...)
1159 c.Assert(err, gc.IsNil)
1160 ctx.relationUnits = map[string]*state.RelationUnit{}
1161+ if !s.waitJoin {
1162+ return
1163+ }
1164+
1165+ // It's hard to do this properly (watching scope) without perturbing other tests.
1166+ ru, err := ctx.relation.Unit(ctx.unit)
1167+ c.Assert(err, gc.IsNil)
1168+ timeout := time.After(worstCase)
1169+ for {
1170+ c.Logf("waiting to join relation")
1171+ select {
1172+ case <-timeout:
1173+ c.Fatalf("failed to join relation")
1174+ case <-time.After(coretesting.ShortWait):
1175+ inScope, err := ru.InScope()
1176+ c.Assert(err, gc.IsNil)
1177+ if inScope {
1178+ return
1179+ }
1180+ }
1181+ }
1182 }
1183
1184 type addRelationUnit struct{}

Subscribers

People subscribed via source and target branches

to status/vote changes: