Merge lp:~wallyworld/juju-core/machineswithtransienterrors-api into lp:~go-bot/juju-core/trunk
- machineswithtransienterrors-api
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Ian Booth |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2483 |
Proposed branch: | lp:~wallyworld/juju-core/machineswithtransienterrors-api |
Merge into: | lp:~go-bot/juju-core/trunk |
Diff against target: |
707 lines (+361/-34) 17 files modified
state/api/client.go (+14/-0) state/api/machiner/machine.go (+1/-1) state/api/params/internal.go (+7/-4) state/api/provisioner/machine.go (+3/-3) state/api/provisioner/provisioner.go (+23/-0) state/api/provisioner/provisioner_test.go (+50/-2) state/api/uniter/unit.go (+1/-1) state/apiserver/client/client.go (+18/-4) state/apiserver/client/client_test.go (+15/-0) state/apiserver/common/setstatus.go (+50/-0) state/apiserver/common/setstatus_test.go (+66/-2) state/apiserver/machine/machiner_test.go (+1/-1) state/apiserver/provisioner/provisioner.go (+36/-1) state/apiserver/provisioner/provisioner_test.go (+68/-13) state/apiserver/uniter/uniter_test.go (+1/-1) state/interface.go (+6/-0) worker/provisioner/provisioner_task.go (+1/-1) |
To merge this branch: | bzr merge lp:~wallyworld/juju-core/machineswithtransienterrors-api |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+212749@code.launchpad.net |
Commit message
New APIs for provisioner retries.
Add new APIs to support allowing the provisioner to retry
machines with errors. A machine in error status can be
retried if its status data has transient=true.
1. MachinesWithTra
to find machines which can be retried.
2. ResolveProvisio
the user to manually mark a machine as resolved.
Description of the change
New APIs for provisioner retries.
Add new APIs to support allowing the provisioner to retry
machines with errors. A machine in error status can be
retried if its status data has transient=true.
1. MachinesWithTra
to find machines which can be retried.
2. ResolveProvisio
the user to manually mark a machine as resolved.
Existing param structs - SetEntityStatus and SetStatus - were
moved from internal.go to params.go and renamed since they
are now used by the client.
Ian Booth (wallyworld) wrote : | # |
Andrew Wilkins (axwalk) wrote : | # |
https:/
File state/api/client.go (right):
https:/
state/api/
Data: params.
This is a bit too leaky. The client API should be taking (machine) tags
and setting the transient=true status on the server side, otherwise
we're stuck with this implementation detail forever.
https:/
File state/apiserver
https:/
state/apiserver
canAccessFunc(
Should do the access check first to avoid an unnecessary mongo trip.
https:/
state/apiserver
!canAccess {
This is confusing, but I think you just want
if err == nil && !canAccess {
err = common.ErrPerm
}
... right?
Ian Booth (wallyworld) wrote : | # |
Please take a look.
https:/
File state/api/client.go (right):
https:/
state/api/
Data: params.
On 2014/03/26 02:54:08, axw wrote:
> This is a bit too leaky. The client API should be taking (machine)
tags and
> setting the transient=true status on the server side, otherwise we're
stuck with
> this implementation detail forever.
I didn't want to encode the ResolveProvisio
case we decided not to go with it long term, and thought a generic
UpdateMachineStatus API would be generically useful. The implementation
detail is behind a client fasçade. But it is leaky I agree in terms of
being stuck supporting this client version. I'll change it.
https:/
File state/apiserver
https:/
state/apiserver
canAccessFunc(
On 2014/03/26 02:54:08, axw wrote:
> Should do the access check first to avoid an unnecessary mongo trip.
Done.
https:/
state/apiserver
!canAccess {
On 2014/03/26 02:54:08, axw wrote:
> This is confusing, but I think you just want
> if err == nil && !canAccess {
> err = common.ErrPerm
> }
> ... right?
Changed to due above refactoring
Andrew Wilkins (axwalk) wrote : | # |
Thanks for the Client change. Sorry, I missed a couple of things the
first time round.
https:/
File state/api/
https:/
state/api/
gc.ErrorMatches, "permission denied")
This seems like a weird situation to be in. If there's a machine that
the caller is not allowed to read, I think it should just be excluded.
https:/
File state/apiserver
https:/
state/apiserver
nil {
err == nil implies canAccess (otherwise it'd be ErrPerm), so just "if
err == nil"
https:/
state/apiserver
result.
There's a potential type assertion panic here, if something sets
transient to something other than a bool. Not sure if we want to be
bothered working around that.
https:/
state/apiserver
It doesn't seem right to me that MachinesWithTra
returning errors for individual machines. Machine that are inaccessible
simply shouldn't show up, I think. The caller isn't asking about them
specifically.
Ian Booth (wallyworld) wrote : | # |
Please take a look.
https:/
File state/api/
https:/
state/api/
gc.ErrorMatches, "permission denied")
On 2014/03/26 04:57:01, axw wrote:
> This seems like a weird situation to be in. If there's a machine that
the caller
> is not allowed to read, I think it should just be excluded.
Done.
https:/
File state/apiserver
https:/
state/apiserver
nil {
On 2014/03/26 04:57:01, axw wrote:
> err == nil implies canAccess (otherwise it'd be ErrPerm), so just "if
err ==
> nil"
Done.
https:/
state/apiserver
result.
On 2014/03/26 04:57:01, axw wrote:
> There's a potential type assertion panic here, if something sets
transient to
> something other than a bool. Not sure if we want to be bothered
working around
> that.
I thought about it but forgot. Fixed.
https:/
state/apiserver
On 2014/03/26 04:57:01, axw wrote:
> It doesn't seem right to me that MachinesWithTra
returning
> errors for individual machines. Machine that are inaccessible simply
shouldn't
> show up, I think. The caller isn't asking about them specifically.
Hmmmm. I think that's a valid point. I've changed it.
Andrew Wilkins (axwalk) wrote : | # |
On 2014/03/26 05:17:59, wallyworld wrote:
> Please take a look.
https:/
> File state/api/
https:/
> state/api/
> gc.ErrorMatches, "permission denied")
> On 2014/03/26 04:57:01, axw wrote:
> > This seems like a weird situation to be in. If there's a machine
that the
> caller
> > is not allowed to read, I think it should just be excluded.
> Done.
https:/
> File state/apiserver
https:/
> state/apiserver
nil {
> On 2014/03/26 04:57:01, axw wrote:
> > err == nil implies canAccess (otherwise it'd be ErrPerm), so just
"if err ==
> > nil"
> Done.
https:/
> state/apiserver
> result.
> On 2014/03/26 04:57:01, axw wrote:
> > There's a potential type assertion panic here, if something sets
transient to
> > something other than a bool. Not sure if we want to be bothered
working around
> > that.
> I thought about it but forgot. Fixed.
https:/
> state/apiserver
> On 2014/03/26 04:57:01, axw wrote:
> > It doesn't seem right to me that MachinesWithTra
be
> returning
> > errors for individual machines. Machine that are inaccessible simply
shouldn't
> > show up, I think. The caller isn't asking about them specifically.
> Hmmmm. I think that's a valid point. I've changed it.
Thanks, LGTM
Go Bot (go-bot) wrote : | # |
The attempt to merge lp:~wallyworld/juju-core/machineswithtransienterrors-api 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.
? 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.
ok launchpad.
Preview Diff
1 | === modified file 'state/api/client.go' | |||
2 | --- state/api/client.go 2014-03-24 13:27:29 +0000 | |||
3 | +++ state/api/client.go 2014-03-26 05:17:40 +0000 | |||
4 | @@ -138,6 +138,20 @@ | |||
5 | 138 | return c.call("Resolved", p, nil) | 138 | return c.call("Resolved", p, nil) |
6 | 139 | } | 139 | } |
7 | 140 | 140 | ||
8 | 141 | // ResolveProvisioningError updates the provisioning status of a machine allowing the | ||
9 | 142 | // provisioner to retry. | ||
10 | 143 | func (c *Client) ResolveProvisioningError(machine string) error { | ||
11 | 144 | p := params.Entities{ | ||
12 | 145 | Entities: []params.Entity{{Tag: machine}}, | ||
13 | 146 | } | ||
14 | 147 | var results params.ErrorResults | ||
15 | 148 | err := c.st.Call("Client", "", "ResolveProvisioningError", p, &results) | ||
16 | 149 | if err != nil { | ||
17 | 150 | return err | ||
18 | 151 | } | ||
19 | 152 | return results.OneError() | ||
20 | 153 | } | ||
21 | 154 | |||
22 | 141 | // PublicAddress returns the public address of the specified | 155 | // PublicAddress returns the public address of the specified |
23 | 142 | // machine or unit. | 156 | // machine or unit. |
24 | 143 | func (c *Client) PublicAddress(target string) (string, error) { | 157 | func (c *Client) PublicAddress(target string) (string, error) { |
25 | 144 | 158 | ||
26 | === modified file 'state/api/machiner/machine.go' | |||
27 | --- state/api/machiner/machine.go 2014-03-24 13:44:31 +0000 | |||
28 | +++ state/api/machiner/machine.go 2014-03-26 05:17:40 +0000 | |||
29 | @@ -42,7 +42,7 @@ | |||
30 | 42 | func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error { | 42 | func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error { |
31 | 43 | var result params.ErrorResults | 43 | var result params.ErrorResults |
32 | 44 | args := params.SetStatus{ | 44 | args := params.SetStatus{ |
34 | 45 | Entities: []params.SetEntityStatus{ | 45 | Entities: []params.EntityStatus{ |
35 | 46 | {Tag: m.tag, Status: status, Info: info, Data: data}, | 46 | {Tag: m.tag, Status: status, Info: info, Data: data}, |
36 | 47 | }, | 47 | }, |
37 | 48 | } | 48 | } |
38 | 49 | 49 | ||
39 | === modified file 'state/api/params/internal.go' | |||
40 | --- state/api/params/internal.go 2014-03-21 11:52:30 +0000 | |||
41 | +++ state/api/params/internal.go 2014-03-26 05:17:40 +0000 | |||
42 | @@ -315,25 +315,28 @@ | |||
43 | 315 | Machines []MachineSetProvisioned | 315 | Machines []MachineSetProvisioned |
44 | 316 | } | 316 | } |
45 | 317 | 317 | ||
48 | 318 | // SetEntityStatus holds an entity tag, status and extra info. | 318 | // EntityStatus holds an entity tag, status and extra info. |
49 | 319 | type SetEntityStatus struct { | 319 | type EntityStatus struct { |
50 | 320 | Tag string | 320 | Tag string |
51 | 321 | Status Status | 321 | Status Status |
52 | 322 | Info string | 322 | Info string |
53 | 323 | Data StatusData | 323 | Data StatusData |
54 | 324 | } | 324 | } |
55 | 325 | 325 | ||
57 | 326 | // SetStatus holds the parameters for making a SetStatus call. | 326 | // SetStatus holds the parameters for making a SetStatus/UpdateStatus call. |
58 | 327 | type SetStatus struct { | 327 | type SetStatus struct { |
60 | 328 | Entities []SetEntityStatus | 328 | Entities []EntityStatus |
61 | 329 | } | 329 | } |
62 | 330 | 330 | ||
63 | 331 | // StatusResult holds an entity status, extra information, or an | 331 | // StatusResult holds an entity status, extra information, or an |
64 | 332 | // error. | 332 | // error. |
65 | 333 | type StatusResult struct { | 333 | type StatusResult struct { |
66 | 334 | Error *Error | 334 | Error *Error |
67 | 335 | Id string | ||
68 | 336 | Life Life | ||
69 | 335 | Status Status | 337 | Status Status |
70 | 336 | Info string | 338 | Info string |
71 | 339 | Data StatusData | ||
72 | 337 | } | 340 | } |
73 | 338 | 341 | ||
74 | 339 | // StatusResults holds multiple status results. | 342 | // StatusResults holds multiple status results. |
75 | 340 | 343 | ||
76 | === modified file 'state/api/provisioner/machine.go' | |||
77 | --- state/api/provisioner/machine.go 2014-03-24 13:27:29 +0000 | |||
78 | +++ state/api/provisioner/machine.go 2014-03-26 05:17:40 +0000 | |||
79 | @@ -56,11 +56,11 @@ | |||
80 | 56 | } | 56 | } |
81 | 57 | 57 | ||
82 | 58 | // SetStatus sets the status of the machine. | 58 | // SetStatus sets the status of the machine. |
84 | 59 | func (m *Machine) SetStatus(status params.Status, info string) error { | 59 | func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error { |
85 | 60 | var result params.ErrorResults | 60 | var result params.ErrorResults |
86 | 61 | args := params.SetStatus{ | 61 | args := params.SetStatus{ |
89 | 62 | Entities: []params.SetEntityStatus{ | 62 | Entities: []params.EntityStatus{ |
90 | 63 | {Tag: m.tag, Status: status, Info: info}, | 63 | {Tag: m.tag, Status: status, Info: info, Data: data}, |
91 | 64 | }, | 64 | }, |
92 | 65 | } | 65 | } |
93 | 66 | err := m.st.call("SetStatus", args, &result) | 66 | err := m.st.call("SetStatus", args, &result) |
94 | 67 | 67 | ||
95 | === modified file 'state/api/provisioner/provisioner.go' | |||
96 | --- state/api/provisioner/provisioner.go 2014-03-21 18:40:43 +0000 | |||
97 | +++ state/api/provisioner/provisioner.go 2014-03-26 05:17:40 +0000 | |||
98 | @@ -6,6 +6,7 @@ | |||
99 | 6 | import ( | 6 | import ( |
100 | 7 | "fmt" | 7 | "fmt" |
101 | 8 | 8 | ||
102 | 9 | "launchpad.net/juju-core/names" | ||
103 | 9 | "launchpad.net/juju-core/state/api/base" | 10 | "launchpad.net/juju-core/state/api/base" |
104 | 10 | "launchpad.net/juju-core/state/api/common" | 11 | "launchpad.net/juju-core/state/api/common" |
105 | 11 | "launchpad.net/juju-core/state/api/params" | 12 | "launchpad.net/juju-core/state/api/params" |
106 | @@ -107,3 +108,25 @@ | |||
107 | 107 | err = st.call("ContainerConfig", nil, &result) | 108 | err = st.call("ContainerConfig", nil, &result) |
108 | 108 | return result, err | 109 | return result, err |
109 | 109 | } | 110 | } |
110 | 111 | |||
111 | 112 | // MachinesWithTransientErrors returns a slice of machines and corresponding status information | ||
112 | 113 | // for those machines which have transient provisioning errors. | ||
113 | 114 | func (st *State) MachinesWithTransientErrors() ([]*Machine, []params.StatusResult, error) { | ||
114 | 115 | var results params.StatusResults | ||
115 | 116 | err := st.call("MachinesWithTransientErrors", nil, &results) | ||
116 | 117 | if err != nil { | ||
117 | 118 | return nil, nil, err | ||
118 | 119 | } | ||
119 | 120 | machines := make([]*Machine, len(results.Results)) | ||
120 | 121 | for i, status := range results.Results { | ||
121 | 122 | if status.Error != nil { | ||
122 | 123 | continue | ||
123 | 124 | } | ||
124 | 125 | machines[i] = &Machine{ | ||
125 | 126 | tag: names.MachineTag(status.Id), | ||
126 | 127 | life: status.Life, | ||
127 | 128 | st: st, | ||
128 | 129 | } | ||
129 | 130 | } | ||
130 | 131 | return machines, results.Results, nil | ||
131 | 132 | } | ||
132 | 110 | 133 | ||
133 | === modified file 'state/api/provisioner/provisioner_test.go' | |||
134 | --- state/api/provisioner/provisioner_test.go 2014-03-21 18:18:08 +0000 | |||
135 | +++ state/api/provisioner/provisioner_test.go 2014-03-26 05:17:40 +0000 | |||
136 | @@ -88,13 +88,61 @@ | |||
137 | 88 | c.Assert(status, gc.Equals, params.StatusPending) | 88 | c.Assert(status, gc.Equals, params.StatusPending) |
138 | 89 | c.Assert(info, gc.Equals, "") | 89 | c.Assert(info, gc.Equals, "") |
139 | 90 | 90 | ||
141 | 91 | err = apiMachine.SetStatus(params.StatusStarted, "blah") | 91 | err = apiMachine.SetStatus(params.StatusStarted, "blah", nil) |
142 | 92 | c.Assert(err, gc.IsNil) | 92 | c.Assert(err, gc.IsNil) |
143 | 93 | 93 | ||
144 | 94 | status, info, err = apiMachine.Status() | 94 | status, info, err = apiMachine.Status() |
145 | 95 | c.Assert(err, gc.IsNil) | 95 | c.Assert(err, gc.IsNil) |
146 | 96 | c.Assert(status, gc.Equals, params.StatusStarted) | 96 | c.Assert(status, gc.Equals, params.StatusStarted) |
147 | 97 | c.Assert(info, gc.Equals, "blah") | 97 | c.Assert(info, gc.Equals, "blah") |
148 | 98 | _, _, data, err := s.machine.Status() | ||
149 | 99 | c.Assert(err, gc.IsNil) | ||
150 | 100 | c.Assert(data, gc.HasLen, 0) | ||
151 | 101 | } | ||
152 | 102 | |||
153 | 103 | func (s *provisionerSuite) TestGetSetStatusWithData(c *gc.C) { | ||
154 | 104 | apiMachine, err := s.provisioner.Machine(s.machine.Tag()) | ||
155 | 105 | c.Assert(err, gc.IsNil) | ||
156 | 106 | |||
157 | 107 | err = apiMachine.SetStatus(params.StatusError, "blah", params.StatusData{"foo": "bar"}) | ||
158 | 108 | c.Assert(err, gc.IsNil) | ||
159 | 109 | |||
160 | 110 | status, info, err := apiMachine.Status() | ||
161 | 111 | c.Assert(err, gc.IsNil) | ||
162 | 112 | c.Assert(status, gc.Equals, params.StatusError) | ||
163 | 113 | c.Assert(info, gc.Equals, "blah") | ||
164 | 114 | _, _, data, err := s.machine.Status() | ||
165 | 115 | c.Assert(err, gc.IsNil) | ||
166 | 116 | c.Assert(data, gc.DeepEquals, params.StatusData{"foo": "bar"}) | ||
167 | 117 | } | ||
168 | 118 | |||
169 | 119 | func (s *provisionerSuite) TestMachinesWithTransientErrors(c *gc.C) { | ||
170 | 120 | machine, err := s.State.AddMachine("quantal", state.JobHostUnits) | ||
171 | 121 | c.Assert(err, gc.IsNil) | ||
172 | 122 | err = machine.SetStatus(params.StatusError, "blah", params.StatusData{"transient": true}) | ||
173 | 123 | c.Assert(err, gc.IsNil) | ||
174 | 124 | password, err := utils.RandomPassword() | ||
175 | 125 | c.Assert(err, gc.IsNil) | ||
176 | 126 | err = machine.SetPassword(password) | ||
177 | 127 | c.Assert(err, gc.IsNil) | ||
178 | 128 | err = machine.SetProvisioned("i-manager", "fake_nonce", nil) | ||
179 | 129 | c.Assert(err, gc.IsNil) | ||
180 | 130 | st := s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce") | ||
181 | 131 | c.Assert(s.st, gc.NotNil) | ||
182 | 132 | p := st.Provisioner() | ||
183 | 133 | |||
184 | 134 | machines, info, err := p.MachinesWithTransientErrors() | ||
185 | 135 | c.Assert(err, gc.IsNil) | ||
186 | 136 | c.Assert(machines, gc.HasLen, 1) | ||
187 | 137 | c.Assert(machines[0].Id(), gc.Equals, "1") | ||
188 | 138 | c.Assert(info, gc.HasLen, 1) | ||
189 | 139 | c.Assert(info[0], gc.DeepEquals, params.StatusResult{ | ||
190 | 140 | Id: "1", | ||
191 | 141 | Life: "alive", | ||
192 | 142 | Status: "error", | ||
193 | 143 | Info: "blah", | ||
194 | 144 | Data: params.StatusData{"transient": true}, | ||
195 | 145 | }) | ||
196 | 98 | } | 146 | } |
197 | 99 | 147 | ||
198 | 100 | func (s *provisionerSuite) TestEnsureDeadAndRemove(c *gc.C) { | 148 | func (s *provisionerSuite) TestEnsureDeadAndRemove(c *gc.C) { |
199 | @@ -254,7 +302,7 @@ | |||
200 | 254 | 302 | ||
201 | 255 | // Change something other than the containers and make sure it's | 303 | // Change something other than the containers and make sure it's |
202 | 256 | // not detected. | 304 | // not detected. |
204 | 257 | err = apiMachine.SetStatus(params.StatusStarted, "not really") | 305 | err = apiMachine.SetStatus(params.StatusStarted, "not really", nil) |
205 | 258 | c.Assert(err, gc.IsNil) | 306 | c.Assert(err, gc.IsNil) |
206 | 259 | wc.AssertNoChange() | 307 | wc.AssertNoChange() |
207 | 260 | 308 | ||
208 | 261 | 309 | ||
209 | === modified file 'state/api/uniter/unit.go' | |||
210 | --- state/api/uniter/unit.go 2014-03-21 18:40:43 +0000 | |||
211 | +++ state/api/uniter/unit.go 2014-03-26 05:17:40 +0000 | |||
212 | @@ -58,7 +58,7 @@ | |||
213 | 58 | func (u *Unit) SetStatus(status params.Status, info string, data params.StatusData) error { | 58 | func (u *Unit) SetStatus(status params.Status, info string, data params.StatusData) error { |
214 | 59 | var result params.ErrorResults | 59 | var result params.ErrorResults |
215 | 60 | args := params.SetStatus{ | 60 | args := params.SetStatus{ |
217 | 61 | Entities: []params.SetEntityStatus{ | 61 | Entities: []params.EntityStatus{ |
218 | 62 | {Tag: u.tag, Status: status, Info: info, Data: data}, | 62 | {Tag: u.tag, Status: status, Info: info, Data: data}, |
219 | 63 | }, | 63 | }, |
220 | 64 | } | 64 | } |
221 | 65 | 65 | ||
222 | === modified file 'state/apiserver/client/client.go' | |||
223 | --- state/apiserver/client/client.go 2014-03-20 22:35:39 +0000 | |||
224 | +++ state/apiserver/client/client.go 2014-03-26 05:17:40 +0000 | |||
225 | @@ -38,6 +38,8 @@ | |||
226 | 38 | resources *common.Resources | 38 | resources *common.Resources |
227 | 39 | client *Client | 39 | client *Client |
228 | 40 | dataDir string | 40 | dataDir string |
229 | 41 | // statusSetter provides common methods for updating an entity's provisioning status. | ||
230 | 42 | statusSetter *common.StatusSetter | ||
231 | 41 | } | 43 | } |
232 | 42 | 44 | ||
233 | 43 | // Client serves client-specific API methods. | 45 | // Client serves client-specific API methods. |
234 | @@ -48,10 +50,11 @@ | |||
235 | 48 | // NewAPI creates a new instance of the Client API. | 50 | // NewAPI creates a new instance of the Client API. |
236 | 49 | func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer, datadir string) *API { | 51 | func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer, datadir string) *API { |
237 | 50 | r := &API{ | 52 | r := &API{ |
242 | 51 | state: st, | 53 | state: st, |
243 | 52 | auth: authorizer, | 54 | auth: authorizer, |
244 | 53 | resources: resources, | 55 | resources: resources, |
245 | 54 | dataDir: datadir, | 56 | dataDir: datadir, |
246 | 57 | statusSetter: common.NewStatusSetter(st, common.AuthAlways(true)), | ||
247 | 55 | } | 58 | } |
248 | 56 | r.client = &Client{ | 59 | r.client = &Client{ |
249 | 57 | api: r, | 60 | api: r, |
250 | @@ -947,3 +950,14 @@ | |||
251 | 947 | } | 950 | } |
252 | 948 | return charm.Quote(fmt.Sprintf("%s-%d-%s", name, revision, uuid)), nil | 951 | return charm.Quote(fmt.Sprintf("%s-%d-%s", name, revision, uuid)), nil |
253 | 949 | } | 952 | } |
254 | 953 | |||
255 | 954 | // ResolveProvisioningError marks a provisioning error as transient on the machines. | ||
256 | 955 | func (c *Client) ResolveProvisioningError(p params.Entities) (params.ErrorResults, error) { | ||
257 | 956 | entityStatus := make([]params.EntityStatus, len(p.Entities)) | ||
258 | 957 | for i, entity := range p.Entities { | ||
259 | 958 | entityStatus[i] = params.EntityStatus{Tag: entity.Tag, Data: params.StatusData{"transient": true}} | ||
260 | 959 | } | ||
261 | 960 | return c.api.statusSetter.UpdateStatus(params.SetStatus{ | ||
262 | 961 | Entities: entityStatus, | ||
263 | 962 | }) | ||
264 | 963 | } | ||
265 | 950 | 964 | ||
266 | === modified file 'state/apiserver/client/client_test.go' | |||
267 | --- state/apiserver/client/client_test.go 2014-03-24 20:08:45 +0000 | |||
268 | +++ state/apiserver/client/client_test.go 2014-03-26 05:17:40 +0000 | |||
269 | @@ -2004,3 +2004,18 @@ | |||
270 | 2004 | func getArchiveName(bundleURL *url.URL) string { | 2004 | func getArchiveName(bundleURL *url.URL) string { |
271 | 2005 | return strings.TrimPrefix(bundleURL.RequestURI(), "/dummyenv/private/") | 2005 | return strings.TrimPrefix(bundleURL.RequestURI(), "/dummyenv/private/") |
272 | 2006 | } | 2006 | } |
273 | 2007 | |||
274 | 2008 | func (s *clientSuite) TestResolveProvisioningError(c *gc.C) { | ||
275 | 2009 | machine, err := s.State.AddMachine("quantal", state.JobHostUnits) | ||
276 | 2010 | c.Assert(err, gc.IsNil) | ||
277 | 2011 | err = machine.SetStatus(params.StatusError, "error", nil) | ||
278 | 2012 | c.Assert(err, gc.IsNil) | ||
279 | 2013 | err = s.APIState.Client().ResolveProvisioningError(machine.Tag()) | ||
280 | 2014 | c.Assert(err, gc.IsNil) | ||
281 | 2015 | |||
282 | 2016 | status, info, data, err := machine.Status() | ||
283 | 2017 | c.Assert(err, gc.IsNil) | ||
284 | 2018 | c.Assert(status, gc.Equals, params.StatusError) | ||
285 | 2019 | c.Assert(info, gc.Equals, "error") | ||
286 | 2020 | c.Assert(data["transient"], gc.Equals, true) | ||
287 | 2021 | } | ||
288 | 2007 | 2022 | ||
289 | === modified file 'state/apiserver/common/setstatus.go' | |||
290 | --- state/apiserver/common/setstatus.go 2014-01-20 21:00:43 +0000 | |||
291 | +++ state/apiserver/common/setstatus.go 2014-03-26 05:17:40 +0000 | |||
292 | @@ -58,3 +58,53 @@ | |||
293 | 58 | } | 58 | } |
294 | 59 | return result, nil | 59 | return result, nil |
295 | 60 | } | 60 | } |
296 | 61 | |||
297 | 62 | func (s *StatusSetter) updateEntityStatusData(tag string, data params.StatusData) error { | ||
298 | 63 | entity0, err := s.st.FindEntity(tag) | ||
299 | 64 | if err != nil { | ||
300 | 65 | return err | ||
301 | 66 | } | ||
302 | 67 | statusGetter, ok := entity0.(state.StatusGetter) | ||
303 | 68 | if !ok { | ||
304 | 69 | return NotSupportedError(tag, "getting status") | ||
305 | 70 | } | ||
306 | 71 | existingStatus, existingInfo, existingData, err := statusGetter.Status() | ||
307 | 72 | if err != nil { | ||
308 | 73 | return err | ||
309 | 74 | } | ||
310 | 75 | newData := existingData | ||
311 | 76 | if newData == nil { | ||
312 | 77 | newData = data | ||
313 | 78 | } else { | ||
314 | 79 | for k, v := range data { | ||
315 | 80 | newData[k] = v | ||
316 | 81 | } | ||
317 | 82 | } | ||
318 | 83 | entity, ok := entity0.(state.StatusSetter) | ||
319 | 84 | if !ok { | ||
320 | 85 | return NotSupportedError(tag, "updating status") | ||
321 | 86 | } | ||
322 | 87 | return entity.SetStatus(existingStatus, existingInfo, newData) | ||
323 | 88 | } | ||
324 | 89 | |||
325 | 90 | // UpdateStatus updates the status data of each given entity. | ||
326 | 91 | func (s *StatusSetter) UpdateStatus(args params.SetStatus) (params.ErrorResults, error) { | ||
327 | 92 | result := params.ErrorResults{ | ||
328 | 93 | Results: make([]params.ErrorResult, len(args.Entities)), | ||
329 | 94 | } | ||
330 | 95 | if len(args.Entities) == 0 { | ||
331 | 96 | return result, nil | ||
332 | 97 | } | ||
333 | 98 | canModify, err := s.getCanModify() | ||
334 | 99 | if err != nil { | ||
335 | 100 | return params.ErrorResults{}, err | ||
336 | 101 | } | ||
337 | 102 | for i, arg := range args.Entities { | ||
338 | 103 | err := ErrPerm | ||
339 | 104 | if canModify(arg.Tag) { | ||
340 | 105 | err = s.updateEntityStatusData(arg.Tag, arg.Data) | ||
341 | 106 | } | ||
342 | 107 | result.Results[i].Error = ServerError(err) | ||
343 | 108 | } | ||
344 | 109 | return result, nil | ||
345 | 110 | } | ||
346 | 61 | 111 | ||
347 | === modified file 'state/apiserver/common/setstatus_test.go' | |||
348 | --- state/apiserver/common/setstatus_test.go 2014-01-20 21:29:08 +0000 | |||
349 | +++ state/apiserver/common/setstatus_test.go 2014-03-26 05:17:40 +0000 | |||
350 | @@ -34,6 +34,17 @@ | |||
351 | 34 | return s.err | 34 | return s.err |
352 | 35 | } | 35 | } |
353 | 36 | 36 | ||
354 | 37 | func (s *fakeStatusSetter) Status() (status params.Status, info string, data params.StatusData, err error) { | ||
355 | 38 | return s.status, s.info, s.data, nil | ||
356 | 39 | } | ||
357 | 40 | |||
358 | 41 | func (s *fakeStatusSetter) UpdateStatus(data params.StatusData) error { | ||
359 | 42 | for k, v := range data { | ||
360 | 43 | s.data[k] = v | ||
361 | 44 | } | ||
362 | 45 | return s.err | ||
363 | 46 | } | ||
364 | 47 | |||
365 | 37 | func (*statusSetterSuite) TestSetStatus(c *gc.C) { | 48 | func (*statusSetterSuite) TestSetStatus(c *gc.C) { |
366 | 38 | st := &fakeState{ | 49 | st := &fakeState{ |
367 | 39 | entities: map[string]entityWithError{ | 50 | entities: map[string]entityWithError{ |
368 | @@ -55,7 +66,7 @@ | |||
369 | 55 | } | 66 | } |
370 | 56 | s := common.NewStatusSetter(st, getCanModify) | 67 | s := common.NewStatusSetter(st, getCanModify) |
371 | 57 | args := params.SetStatus{ | 68 | args := params.SetStatus{ |
373 | 58 | Entities: []params.SetEntityStatus{ | 69 | Entities: []params.EntityStatus{ |
374 | 59 | {"x0", params.StatusStarted, "bar", nil}, | 70 | {"x0", params.StatusStarted, "bar", nil}, |
375 | 60 | {"x1", params.StatusStopped, "", nil}, | 71 | {"x1", params.StatusStopped, "", nil}, |
376 | 61 | {"x2", params.StatusPending, "not really", nil}, | 72 | {"x2", params.StatusPending, "not really", nil}, |
377 | @@ -91,7 +102,7 @@ | |||
378 | 91 | } | 102 | } |
379 | 92 | s := common.NewStatusSetter(&fakeState{}, getCanModify) | 103 | s := common.NewStatusSetter(&fakeState{}, getCanModify) |
380 | 93 | args := params.SetStatus{ | 104 | args := params.SetStatus{ |
382 | 94 | Entities: []params.SetEntityStatus{{"x0", "", "", nil}}, | 105 | Entities: []params.EntityStatus{{"x0", "", "", nil}}, |
383 | 95 | } | 106 | } |
384 | 96 | _, err := s.SetStatus(args) | 107 | _, err := s.SetStatus(args) |
385 | 97 | c.Assert(err, gc.ErrorMatches, "pow") | 108 | c.Assert(err, gc.ErrorMatches, "pow") |
386 | @@ -106,3 +117,56 @@ | |||
387 | 106 | c.Assert(err, gc.IsNil) | 117 | c.Assert(err, gc.IsNil) |
388 | 107 | c.Assert(result.Results, gc.HasLen, 0) | 118 | c.Assert(result.Results, gc.HasLen, 0) |
389 | 108 | } | 119 | } |
390 | 120 | |||
391 | 121 | func (*statusSetterSuite) TestUpdateStatus(c *gc.C) { | ||
392 | 122 | st := &fakeState{ | ||
393 | 123 | entities: map[string]entityWithError{ | ||
394 | 124 | "x0": &fakeStatusSetter{status: params.StatusPending, info: "blah", err: fmt.Errorf("x0 fails")}, | ||
395 | 125 | "x1": &fakeStatusSetter{status: params.StatusStarted, info: "foo", data: params.StatusData{"foo": "blah"}}, | ||
396 | 126 | "x2": &fakeStatusSetter{status: params.StatusError, info: "some info"}, | ||
397 | 127 | "x3": &fakeStatusSetter{fetchError: "x3 error"}, | ||
398 | 128 | "x4": &fakeStatusSetter{status: params.StatusStopped, info: ""}, | ||
399 | 129 | }, | ||
400 | 130 | } | ||
401 | 131 | getCanModify := func() (common.AuthFunc, error) { | ||
402 | 132 | return func(tag string) bool { | ||
403 | 133 | switch tag { | ||
404 | 134 | case "x0", "x1", "x2", "x3": | ||
405 | 135 | return true | ||
406 | 136 | } | ||
407 | 137 | return false | ||
408 | 138 | }, nil | ||
409 | 139 | } | ||
410 | 140 | s := common.NewStatusSetter(st, getCanModify) | ||
411 | 141 | args := params.SetStatus{ | ||
412 | 142 | Entities: []params.EntityStatus{ | ||
413 | 143 | {Tag: "x0", Data: nil}, | ||
414 | 144 | {Tag: "x1", Data: nil}, | ||
415 | 145 | {Tag: "x2", Data: params.StatusData{"foo": "bar"}}, | ||
416 | 146 | {Tag: "x3", Data: params.StatusData{"foo": "bar"}}, | ||
417 | 147 | {Tag: "x4", Data: params.StatusData{"foo": "bar"}}, | ||
418 | 148 | {Tag: "x5", Data: nil}, | ||
419 | 149 | }, | ||
420 | 150 | } | ||
421 | 151 | result, err := s.UpdateStatus(args) | ||
422 | 152 | c.Assert(err, gc.IsNil) | ||
423 | 153 | c.Assert(result, gc.DeepEquals, params.ErrorResults{ | ||
424 | 154 | Results: []params.ErrorResult{ | ||
425 | 155 | {¶ms.Error{Message: "x0 fails"}}, | ||
426 | 156 | {nil}, | ||
427 | 157 | {nil}, | ||
428 | 158 | {¶ms.Error{Message: "x3 error"}}, | ||
429 | 159 | {apiservertesting.ErrUnauthorized}, | ||
430 | 160 | {apiservertesting.ErrUnauthorized}, | ||
431 | 161 | }, | ||
432 | 162 | }) | ||
433 | 163 | get := func(tag string) *fakeStatusSetter { | ||
434 | 164 | return st.entities[tag].(*fakeStatusSetter) | ||
435 | 165 | } | ||
436 | 166 | c.Assert(get("x1").status, gc.Equals, params.StatusStarted) | ||
437 | 167 | c.Assert(get("x1").info, gc.Equals, "foo") | ||
438 | 168 | c.Assert(get("x1").data, gc.DeepEquals, params.StatusData{"foo": "blah"}) | ||
439 | 169 | c.Assert(get("x2").status, gc.Equals, params.StatusError) | ||
440 | 170 | c.Assert(get("x2").info, gc.Equals, "some info") | ||
441 | 171 | c.Assert(get("x2").data, gc.DeepEquals, params.StatusData{"foo": "bar"}) | ||
442 | 172 | } | ||
443 | 109 | 173 | ||
444 | === modified file 'state/apiserver/machine/machiner_test.go' | |||
445 | --- state/apiserver/machine/machiner_test.go 2014-01-20 21:00:43 +0000 | |||
446 | +++ state/apiserver/machine/machiner_test.go 2014-03-26 05:17:40 +0000 | |||
447 | @@ -57,7 +57,7 @@ | |||
448 | 57 | c.Assert(err, gc.IsNil) | 57 | c.Assert(err, gc.IsNil) |
449 | 58 | 58 | ||
450 | 59 | args := params.SetStatus{ | 59 | args := params.SetStatus{ |
452 | 60 | Entities: []params.SetEntityStatus{ | 60 | Entities: []params.EntityStatus{ |
453 | 61 | {Tag: "machine-1", Status: params.StatusError, Info: "not really"}, | 61 | {Tag: "machine-1", Status: params.StatusError, Info: "not really"}, |
454 | 62 | {Tag: "machine-0", Status: params.StatusStopped, Info: "foobar"}, | 62 | {Tag: "machine-0", Status: params.StatusStopped, Info: "foobar"}, |
455 | 63 | {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"}, | 63 | {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"}, |
456 | 64 | 64 | ||
457 | === modified file 'state/apiserver/provisioner/provisioner.go' | |||
458 | --- state/apiserver/provisioner/provisioner.go 2014-03-20 13:57:50 +0000 | |||
459 | +++ state/apiserver/provisioner/provisioner.go 2014-03-26 05:17:40 +0000 | |||
460 | @@ -214,13 +214,48 @@ | |||
461 | 214 | machine, err := p.getMachine(canAccess, entity.Tag) | 214 | machine, err := p.getMachine(canAccess, entity.Tag) |
462 | 215 | if err == nil { | 215 | if err == nil { |
463 | 216 | r := &result.Results[i] | 216 | r := &result.Results[i] |
465 | 217 | r.Status, r.Info, _, err = machine.Status() | 217 | r.Status, r.Info, r.Data, err = machine.Status() |
466 | 218 | } | 218 | } |
467 | 219 | result.Results[i].Error = common.ServerError(err) | 219 | result.Results[i].Error = common.ServerError(err) |
468 | 220 | } | 220 | } |
469 | 221 | return result, nil | 221 | return result, nil |
470 | 222 | } | 222 | } |
471 | 223 | 223 | ||
472 | 224 | // MachinesWithTransientErrors returns status data for machines with provisioning | ||
473 | 225 | // errors which are transient. | ||
474 | 226 | func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) { | ||
475 | 227 | results := params.StatusResults{} | ||
476 | 228 | canAccessFunc, err := p.getAuthFunc() | ||
477 | 229 | if err != nil { | ||
478 | 230 | return results, err | ||
479 | 231 | } | ||
480 | 232 | // TODO (wallyworld) - add state.State API for more efficient machines query | ||
481 | 233 | machines, err := p.st.AllMachines() | ||
482 | 234 | if err != nil { | ||
483 | 235 | return results, err | ||
484 | 236 | } | ||
485 | 237 | for _, machine := range machines { | ||
486 | 238 | if !canAccessFunc(machine.Tag()) { | ||
487 | 239 | continue | ||
488 | 240 | } | ||
489 | 241 | result := params.StatusResult{} | ||
490 | 242 | if result.Status, result.Info, result.Data, err = machine.Status(); err != nil { | ||
491 | 243 | continue | ||
492 | 244 | } | ||
493 | 245 | if result.Status != params.StatusError { | ||
494 | 246 | continue | ||
495 | 247 | } | ||
496 | 248 | // Transient errors are marked as such in the status data. | ||
497 | 249 | if transient, ok := result.Data["transient"].(bool); !ok || !transient { | ||
498 | 250 | continue | ||
499 | 251 | } | ||
500 | 252 | result.Id = machine.Id() | ||
501 | 253 | result.Life = params.Life(machine.Life().String()) | ||
502 | 254 | results.Results = append(results.Results, result) | ||
503 | 255 | } | ||
504 | 256 | return results, nil | ||
505 | 257 | } | ||
506 | 258 | |||
507 | 224 | // Series returns the deployed series for each given machine entity. | 259 | // Series returns the deployed series for each given machine entity. |
508 | 225 | func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) { | 260 | func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) { |
509 | 226 | result := params.StringResults{ | 261 | result := params.StringResults{ |
510 | 227 | 262 | ||
511 | === modified file 'state/apiserver/provisioner/provisioner_test.go' | |||
512 | --- state/apiserver/provisioner/provisioner_test.go 2014-03-18 02:36:58 +0000 | |||
513 | +++ state/apiserver/provisioner/provisioner_test.go 2014-03-26 05:17:40 +0000 | |||
514 | @@ -57,7 +57,7 @@ | |||
515 | 57 | if withStateServer { | 57 | if withStateServer { |
516 | 58 | s.machines = append(s.machines, testing.AddStateServerMachine(c, s.State)) | 58 | s.machines = append(s.machines, testing.AddStateServerMachine(c, s.State)) |
517 | 59 | } | 59 | } |
519 | 60 | for i := 0; i < 3; i++ { | 60 | for i := 0; i < 4; i++ { |
520 | 61 | machine, err := s.State.AddMachine("quantal", state.JobHostUnits) | 61 | machine, err := s.State.AddMachine("quantal", state.JobHostUnits) |
521 | 62 | c.Check(err, gc.IsNil) | 62 | c.Check(err, gc.IsNil) |
522 | 63 | s.machines = append(s.machines, machine) | 63 | s.machines = append(s.machines, machine) |
523 | @@ -119,6 +119,7 @@ | |||
524 | 119 | {Tag: s.machines[0].Tag(), Password: "xxx0-1234567890123457890"}, | 119 | {Tag: s.machines[0].Tag(), Password: "xxx0-1234567890123457890"}, |
525 | 120 | {Tag: s.machines[1].Tag(), Password: "xxx1-1234567890123457890"}, | 120 | {Tag: s.machines[1].Tag(), Password: "xxx1-1234567890123457890"}, |
526 | 121 | {Tag: s.machines[2].Tag(), Password: "xxx2-1234567890123457890"}, | 121 | {Tag: s.machines[2].Tag(), Password: "xxx2-1234567890123457890"}, |
527 | 122 | {Tag: s.machines[3].Tag(), Password: "xxx3-1234567890123457890"}, | ||
528 | 122 | {Tag: "machine-42", Password: "foo"}, | 123 | {Tag: "machine-42", Password: "foo"}, |
529 | 123 | {Tag: "unit-foo-0", Password: "zzz"}, | 124 | {Tag: "unit-foo-0", Password: "zzz"}, |
530 | 124 | {Tag: "service-bar", Password: "abc"}, | 125 | {Tag: "service-bar", Password: "abc"}, |
531 | @@ -131,6 +132,7 @@ | |||
532 | 131 | {nil}, | 132 | {nil}, |
533 | 132 | {nil}, | 133 | {nil}, |
534 | 133 | {nil}, | 134 | {nil}, |
535 | 135 | {nil}, | ||
536 | 134 | {apiservertesting.NotFoundError("machine 42")}, | 136 | {apiservertesting.NotFoundError("machine 42")}, |
537 | 135 | {apiservertesting.ErrUnauthorized}, | 137 | {apiservertesting.ErrUnauthorized}, |
538 | 136 | {apiservertesting.ErrUnauthorized}, | 138 | {apiservertesting.ErrUnauthorized}, |
539 | @@ -317,8 +319,9 @@ | |||
540 | 317 | c.Assert(err, gc.IsNil) | 319 | c.Assert(err, gc.IsNil) |
541 | 318 | 320 | ||
542 | 319 | args := params.SetStatus{ | 321 | args := params.SetStatus{ |
545 | 320 | Entities: []params.SetEntityStatus{ | 322 | Entities: []params.EntityStatus{ |
546 | 321 | {Tag: s.machines[0].Tag(), Status: params.StatusError, Info: "not really"}, | 323 | {Tag: s.machines[0].Tag(), Status: params.StatusError, Info: "not really", |
547 | 324 | Data: params.StatusData{"foo": "bar"}}, | ||
548 | 322 | {Tag: s.machines[1].Tag(), Status: params.StatusStopped, Info: "foobar"}, | 325 | {Tag: s.machines[1].Tag(), Status: params.StatusStopped, Info: "foobar"}, |
549 | 323 | {Tag: s.machines[2].Tag(), Status: params.StatusStarted, Info: "again"}, | 326 | {Tag: s.machines[2].Tag(), Status: params.StatusStarted, Info: "again"}, |
550 | 324 | {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"}, | 327 | {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"}, |
551 | @@ -339,9 +342,58 @@ | |||
552 | 339 | }) | 342 | }) |
553 | 340 | 343 | ||
554 | 341 | // Verify the changes. | 344 | // Verify the changes. |
558 | 342 | s.assertStatus(c, 0, params.StatusError, "not really") | 345 | s.assertStatus(c, 0, params.StatusError, "not really", params.StatusData{"foo": "bar"}) |
559 | 343 | s.assertStatus(c, 1, params.StatusStopped, "foobar") | 346 | s.assertStatus(c, 1, params.StatusStopped, "foobar", params.StatusData{}) |
560 | 344 | s.assertStatus(c, 2, params.StatusStarted, "again") | 347 | s.assertStatus(c, 2, params.StatusStarted, "again", params.StatusData{}) |
561 | 348 | } | ||
562 | 349 | |||
563 | 350 | func (s *withoutStateServerSuite) TestMachinesWithTransientErrors(c *gc.C) { | ||
564 | 351 | err := s.machines[0].SetStatus(params.StatusStarted, "blah", nil) | ||
565 | 352 | c.Assert(err, gc.IsNil) | ||
566 | 353 | err = s.machines[1].SetStatus(params.StatusError, "transient error", | ||
567 | 354 | params.StatusData{"transient": true, "foo": "bar"}) | ||
568 | 355 | c.Assert(err, gc.IsNil) | ||
569 | 356 | err = s.machines[2].SetStatus(params.StatusError, "error", params.StatusData{"transient": false}) | ||
570 | 357 | c.Assert(err, gc.IsNil) | ||
571 | 358 | err = s.machines[3].SetStatus(params.StatusError, "error", nil) | ||
572 | 359 | c.Assert(err, gc.IsNil) | ||
573 | 360 | |||
574 | 361 | result, err := s.provisioner.MachinesWithTransientErrors() | ||
575 | 362 | c.Assert(err, gc.IsNil) | ||
576 | 363 | c.Assert(result, gc.DeepEquals, params.StatusResults{ | ||
577 | 364 | Results: []params.StatusResult{ | ||
578 | 365 | {Id: "1", Life: "alive", Status: "error", Info: "transient error", | ||
579 | 366 | Data: params.StatusData{"transient": true, "foo": "bar"}}, | ||
580 | 367 | }, | ||
581 | 368 | }) | ||
582 | 369 | } | ||
583 | 370 | |||
584 | 371 | func (s *withoutStateServerSuite) TestMachinesWithTransientErrorsPermission(c *gc.C) { | ||
585 | 372 | // Machines where there's permission issues are omitted. | ||
586 | 373 | anAuthorizer := s.authorizer | ||
587 | 374 | anAuthorizer.MachineAgent = true | ||
588 | 375 | anAuthorizer.EnvironManager = false | ||
589 | 376 | anAuthorizer.Tag = "machine-1" | ||
590 | 377 | aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, | ||
591 | 378 | anAuthorizer) | ||
592 | 379 | err = s.machines[0].SetStatus(params.StatusStarted, "blah", nil) | ||
593 | 380 | c.Assert(err, gc.IsNil) | ||
594 | 381 | err = s.machines[1].SetStatus(params.StatusError, "transient error", | ||
595 | 382 | params.StatusData{"transient": true, "foo": "bar"}) | ||
596 | 383 | c.Assert(err, gc.IsNil) | ||
597 | 384 | err = s.machines[2].SetStatus(params.StatusError, "error", params.StatusData{"transient": false}) | ||
598 | 385 | c.Assert(err, gc.IsNil) | ||
599 | 386 | err = s.machines[3].SetStatus(params.StatusError, "error", nil) | ||
600 | 387 | c.Assert(err, gc.IsNil) | ||
601 | 388 | |||
602 | 389 | result, err := aProvisioner.MachinesWithTransientErrors() | ||
603 | 390 | c.Assert(err, gc.IsNil) | ||
604 | 391 | c.Assert(result, gc.DeepEquals, params.StatusResults{ | ||
605 | 392 | Results: []params.StatusResult{ | ||
606 | 393 | {Id: "1", Life: "alive", Status: "error", Info: "transient error", | ||
607 | 394 | Data: params.StatusData{"transient": true, "foo": "bar"}}, | ||
608 | 395 | }, | ||
609 | 396 | }) | ||
610 | 345 | } | 397 | } |
611 | 346 | 398 | ||
612 | 347 | func (s *withoutStateServerSuite) TestEnsureDead(c *gc.C) { | 399 | func (s *withoutStateServerSuite) TestEnsureDead(c *gc.C) { |
613 | @@ -384,11 +436,14 @@ | |||
614 | 384 | c.Assert(s.machines[index].Life(), gc.Equals, expectLife) | 436 | c.Assert(s.machines[index].Life(), gc.Equals, expectLife) |
615 | 385 | } | 437 | } |
616 | 386 | 438 | ||
619 | 387 | func (s *withoutStateServerSuite) assertStatus(c *gc.C, index int, expectStatus params.Status, expectInfo string) { | 439 | func (s *withoutStateServerSuite) assertStatus(c *gc.C, index int, expectStatus params.Status, expectInfo string, |
620 | 388 | status, info, _, err := s.machines[index].Status() | 440 | expectData params.StatusData) { |
621 | 441 | |||
622 | 442 | status, info, data, err := s.machines[index].Status() | ||
623 | 389 | c.Assert(err, gc.IsNil) | 443 | c.Assert(err, gc.IsNil) |
624 | 390 | c.Assert(status, gc.Equals, expectStatus) | 444 | c.Assert(status, gc.Equals, expectStatus) |
625 | 391 | c.Assert(info, gc.Equals, expectInfo) | 445 | c.Assert(info, gc.Equals, expectInfo) |
626 | 446 | c.Assert(data, gc.DeepEquals, expectData) | ||
627 | 392 | } | 447 | } |
628 | 393 | 448 | ||
629 | 394 | func (s *withoutStateServerSuite) TestWatchContainers(c *gc.C) { | 449 | func (s *withoutStateServerSuite) TestWatchContainers(c *gc.C) { |
630 | @@ -482,7 +537,7 @@ | |||
631 | 482 | c.Assert(err, gc.IsNil) | 537 | c.Assert(err, gc.IsNil) |
632 | 483 | err = s.machines[1].SetStatus(params.StatusStopped, "foo", nil) | 538 | err = s.machines[1].SetStatus(params.StatusStopped, "foo", nil) |
633 | 484 | c.Assert(err, gc.IsNil) | 539 | c.Assert(err, gc.IsNil) |
635 | 485 | err = s.machines[2].SetStatus(params.StatusError, "not really", nil) | 540 | err = s.machines[2].SetStatus(params.StatusError, "not really", params.StatusData{"foo": "bar"}) |
636 | 486 | c.Assert(err, gc.IsNil) | 541 | c.Assert(err, gc.IsNil) |
637 | 487 | 542 | ||
638 | 488 | args := params.Entities{Entities: []params.Entity{ | 543 | args := params.Entities{Entities: []params.Entity{ |
639 | @@ -497,9 +552,9 @@ | |||
640 | 497 | c.Assert(err, gc.IsNil) | 552 | c.Assert(err, gc.IsNil) |
641 | 498 | c.Assert(result, gc.DeepEquals, params.StatusResults{ | 553 | c.Assert(result, gc.DeepEquals, params.StatusResults{ |
642 | 499 | Results: []params.StatusResult{ | 554 | Results: []params.StatusResult{ |
646 | 500 | {Status: params.StatusStarted, Info: "blah"}, | 555 | {Status: params.StatusStarted, Info: "blah", Data: params.StatusData{}}, |
647 | 501 | {Status: params.StatusStopped, Info: "foo"}, | 556 | {Status: params.StatusStopped, Info: "foo", Data: params.StatusData{}}, |
648 | 502 | {Status: params.StatusError, Info: "not really"}, | 557 | {Status: params.StatusError, Info: "not really", Data: params.StatusData{"foo": "bar"}}, |
649 | 503 | {Error: apiservertesting.NotFoundError("machine 42")}, | 558 | {Error: apiservertesting.NotFoundError("machine 42")}, |
650 | 504 | {Error: apiservertesting.ErrUnauthorized}, | 559 | {Error: apiservertesting.ErrUnauthorized}, |
651 | 505 | {Error: apiservertesting.ErrUnauthorized}, | 560 | {Error: apiservertesting.ErrUnauthorized}, |
652 | @@ -650,7 +705,7 @@ | |||
653 | 650 | c.Assert(err, gc.IsNil) | 705 | c.Assert(err, gc.IsNil) |
654 | 651 | c.Assert(result, gc.DeepEquals, params.StringsWatchResult{ | 706 | c.Assert(result, gc.DeepEquals, params.StringsWatchResult{ |
655 | 652 | StringsWatcherId: "1", | 707 | StringsWatcherId: "1", |
657 | 653 | Changes: []string{"0", "1", "2"}, | 708 | Changes: []string{"0", "1", "2", "3"}, |
658 | 654 | }) | 709 | }) |
659 | 655 | 710 | ||
660 | 656 | // Verify the resources were registered and stop them when done. | 711 | // Verify the resources were registered and stop them when done. |
661 | 657 | 712 | ||
662 | === modified file 'state/apiserver/uniter/uniter_test.go' | |||
663 | --- state/apiserver/uniter/uniter_test.go 2014-03-17 13:22:55 +0000 | |||
664 | +++ state/apiserver/uniter/uniter_test.go 2014-03-26 05:17:40 +0000 | |||
665 | @@ -109,7 +109,7 @@ | |||
666 | 109 | c.Assert(err, gc.IsNil) | 109 | c.Assert(err, gc.IsNil) |
667 | 110 | 110 | ||
668 | 111 | args := params.SetStatus{ | 111 | args := params.SetStatus{ |
670 | 112 | Entities: []params.SetEntityStatus{ | 112 | Entities: []params.EntityStatus{ |
671 | 113 | {Tag: "unit-mysql-0", Status: params.StatusError, Info: "not really"}, | 113 | {Tag: "unit-mysql-0", Status: params.StatusError, Info: "not really"}, |
672 | 114 | {Tag: "unit-wordpress-0", Status: params.StatusStopped, Info: "foobar"}, | 114 | {Tag: "unit-wordpress-0", Status: params.StatusStopped, Info: "foobar"}, |
673 | 115 | {Tag: "unit-foo-42", Status: params.StatusStarted, Info: "blah"}, | 115 | {Tag: "unit-foo-42", Status: params.StatusStarted, Info: "blah"}, |
674 | 116 | 116 | ||
675 | === modified file 'state/interface.go' | |||
676 | --- state/interface.go 2014-01-20 17:47:59 +0000 | |||
677 | +++ state/interface.go 2014-03-26 05:17:40 +0000 | |||
678 | @@ -37,9 +37,15 @@ | |||
679 | 37 | SetStatus(status params.Status, info string, data params.StatusData) error | 37 | SetStatus(status params.Status, info string, data params.StatusData) error |
680 | 38 | } | 38 | } |
681 | 39 | 39 | ||
682 | 40 | type StatusGetter interface { | ||
683 | 41 | Status() (status params.Status, info string, data params.StatusData, err error) | ||
684 | 42 | } | ||
685 | 43 | |||
686 | 40 | var ( | 44 | var ( |
687 | 41 | _ StatusSetter = (*Machine)(nil) | 45 | _ StatusSetter = (*Machine)(nil) |
688 | 42 | _ StatusSetter = (*Unit)(nil) | 46 | _ StatusSetter = (*Unit)(nil) |
689 | 47 | _ StatusGetter = (*Machine)(nil) | ||
690 | 48 | _ StatusGetter = (*Unit)(nil) | ||
691 | 43 | ) | 49 | ) |
692 | 44 | 50 | ||
693 | 45 | // Lifer represents an entity with a life. | 51 | // Lifer represents an entity with a life. |
694 | 46 | 52 | ||
695 | === modified file 'state/machine_test.go' | |||
696 | === modified file 'worker/provisioner/provisioner_task.go' | |||
697 | --- worker/provisioner/provisioner_task.go 2014-03-13 05:09:14 +0000 | |||
698 | +++ worker/provisioner/provisioner_task.go 2014-03-26 05:17:40 +0000 | |||
699 | @@ -405,7 +405,7 @@ | |||
700 | 405 | // time until the error is resolved, but don't return an | 405 | // time until the error is resolved, but don't return an |
701 | 406 | // error; just keep going with the other machines. | 406 | // error; just keep going with the other machines. |
702 | 407 | logger.Errorf("cannot start instance for machine %q: %v", machine, err) | 407 | logger.Errorf("cannot start instance for machine %q: %v", machine, err) |
704 | 408 | if err1 := machine.SetStatus(params.StatusError, err.Error()); err1 != nil { | 408 | if err1 := machine.SetStatus(params.StatusError, err.Error(), nil); err1 != nil { |
705 | 409 | // Something is wrong with this machine, better report it back. | 409 | // Something is wrong with this machine, better report it back. |
706 | 410 | logger.Errorf("cannot set error status for machine %q: %v", machine, err1) | 410 | logger.Errorf("cannot set error status for machine %q: %v", machine, err1) |
707 | 411 | return err1 | 411 | return err1 |
Reviewers: mp+212749_ code.launchpad. net,
Message:
Please take a look.
Description:
New APIs for provisioner retries.
Add new APIs to support allowing the provisioner to retry
machines with errors. A machine in error status can be
retried if its status data has transient=true.
1. MachinesWithTra nsientErrors is called by the provisioner ningError is a client API which allows
to find machines which can be retried.
2. ResolveProvisio
the user to manually mark a machine as resolved.
Existing param structs - SetEntityStatus and SetStatus - were
moved from internal.go to params.go and renamed since they
are now used by the client.
https:/ /code.launchpad .net/~wallyworl d/juju- core/machineswi thtransienterro rs-api/ +merge/ 212749
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/80230043/
Affected files (+379, -43 lines): machiner/ machine. go params/ internal. go params/ params. go provisioner/ machine. go provisioner/ provisioner. go provisioner/ provisioner_ test.go uniter/ unit.go /client/ client. go /client/ client_ test.go /common/ setstatus. go /common/ setstatus_ test.go /machine/ machiner_ test.go /provisioner/ provisioner. go /provisioner/ provisioner_ test.go /uniter/ uniter_ test.go test.go provisioner/ provisioner_ task.go
A [revision details]
M state/api/client.go
M state/api/
M state/api/
M state/api/
M state/api/
M state/api/
M state/api/
M state/api/
M state/apiserver
M state/apiserver
M state/apiserver
M state/apiserver
M state/apiserver
M state/apiserver
M state/apiserver
M state/apiserver
M state/interface.go
M state/machine_
M worker/