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