Merge lp:~wallyworld/juju-core/machineswithtransienterrors-api into lp:~go-bot/juju-core/trunk

Proposed by Ian Booth
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
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. MachinesWithTransientErrors is called by the provisioner
to find machines which can be retried.
2. ResolveProvisioningError is a client API which allows
the user to manually mark a machine as resolved.

https://codereview.appspot.com/80230043/

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. MachinesWithTransientErrors is called by the provisioner
to find machines which can be retried.
2. ResolveProvisioningError is a client API which allows
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://codereview.appspot.com/80230043/

To post a comment you must log in.
Revision history for this message
Ian Booth (wallyworld) wrote :

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. MachinesWithTransientErrors is called by the provisioner
to find machines which can be retried.
2. ResolveProvisioningError is a client API which allows
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/~wallyworld/juju-core/machineswithtransienterrors-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):
   A [revision details]
   M state/api/client.go
   M state/api/machiner/machine.go
   M state/api/params/internal.go
   M state/api/params/params.go
   M state/api/provisioner/machine.go
   M state/api/provisioner/provisioner.go
   M state/api/provisioner/provisioner_test.go
   M state/api/uniter/unit.go
   M state/apiserver/client/client.go
   M state/apiserver/client/client_test.go
   M state/apiserver/common/setstatus.go
   M state/apiserver/common/setstatus_test.go
   M state/apiserver/machine/machiner_test.go
   M state/apiserver/provisioner/provisioner.go
   M state/apiserver/provisioner/provisioner_test.go
   M state/apiserver/uniter/uniter_test.go
   M state/interface.go
   M state/machine_test.go
   M worker/provisioner/provisioner_task.go

Revision history for this message
Andrew Wilkins (axwalk) wrote :

https://codereview.appspot.com/80230043/diff/1/state/api/client.go
File state/api/client.go (right):

https://codereview.appspot.com/80230043/diff/1/state/api/client.go#newcode141
state/api/client.go:141: Entities: []params.EntityStatus{{Tag: machine,
Data: params.StatusData{"transient": true}}},
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://codereview.appspot.com/80230043/diff/1/state/apiserver/provisioner/provisioner.go
File state/apiserver/provisioner/provisioner.go (right):

https://codereview.appspot.com/80230043/diff/1/state/apiserver/provisioner/provisioner.go#newcode239
state/apiserver/provisioner/provisioner.go:239: canAccess :=
canAccessFunc(machine.Tag())
Should do the access check first to avoid an unnecessary mongo trip.

https://codereview.appspot.com/80230043/diff/1/state/apiserver/provisioner/provisioner.go#newcode243
state/apiserver/provisioner/provisioner.go:243: if err == nil ||
!canAccess {
This is confusing, but I think you just want
     if err == nil && !canAccess {
         err = common.ErrPerm
     }
... right?

https://codereview.appspot.com/80230043/

Revision history for this message
Ian Booth (wallyworld) wrote :

Please take a look.

https://codereview.appspot.com/80230043/diff/1/state/api/client.go
File state/api/client.go (right):

https://codereview.appspot.com/80230043/diff/1/state/api/client.go#newcode141
state/api/client.go:141: Entities: []params.EntityStatus{{Tag: machine,
Data: params.StatusData{"transient": true}}},
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 ResolveProvisioningError API as gospel in
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://codereview.appspot.com/80230043/diff/1/state/apiserver/provisioner/provisioner.go
File state/apiserver/provisioner/provisioner.go (right):

https://codereview.appspot.com/80230043/diff/1/state/apiserver/provisioner/provisioner.go#newcode239
state/apiserver/provisioner/provisioner.go:239: canAccess :=
canAccessFunc(machine.Tag())
On 2014/03/26 02:54:08, axw wrote:
> Should do the access check first to avoid an unnecessary mongo trip.

Done.

https://codereview.appspot.com/80230043/diff/1/state/apiserver/provisioner/provisioner.go#newcode243
state/apiserver/provisioner/provisioner.go:243: if err == nil ||
!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

https://codereview.appspot.com/80230043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

Thanks for the Client change. Sorry, I missed a couple of things the
first time round.

https://codereview.appspot.com/80230043/diff/20001/state/api/provisioner/provisioner_test.go
File state/api/provisioner/provisioner_test.go (right):

https://codereview.appspot.com/80230043/diff/20001/state/api/provisioner/provisioner_test.go#newcode139
state/api/provisioner/provisioner_test.go:139: c.Assert(info[0].Error,
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://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go
File state/apiserver/provisioner/provisioner.go (right):

https://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go#newcode244
state/apiserver/provisioner/provisioner.go:244: if canAccess && err ==
nil {
err == nil implies canAccess (otherwise it'd be ErrPerm), so just "if
err == nil"

https://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go#newcode249
state/apiserver/provisioner/provisioner.go:249: if transient, ok :=
result.Data["transient"]; !ok || !transient.(bool) {
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://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go#newcode254
state/apiserver/provisioner/provisioner.go:254: if err == nil {
It doesn't seem right to me that MachinesWithTransientErrors should 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.

https://codereview.appspot.com/80230043/

Revision history for this message
Ian Booth (wallyworld) wrote :

Please take a look.

https://codereview.appspot.com/80230043/diff/20001/state/api/provisioner/provisioner_test.go
File state/api/provisioner/provisioner_test.go (right):

https://codereview.appspot.com/80230043/diff/20001/state/api/provisioner/provisioner_test.go#newcode139
state/api/provisioner/provisioner_test.go:139: c.Assert(info[0].Error,
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://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go
File state/apiserver/provisioner/provisioner.go (right):

https://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go#newcode244
state/apiserver/provisioner/provisioner.go:244: if canAccess && err ==
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://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go#newcode249
state/apiserver/provisioner/provisioner.go:249: if transient, ok :=
result.Data["transient"]; !ok || !transient.(bool) {
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://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go#newcode254
state/apiserver/provisioner/provisioner.go:254: if err == nil {
On 2014/03/26 04:57:01, axw wrote:
> It doesn't seem right to me that MachinesWithTransientErrors should 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.

https://codereview.appspot.com/80230043/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

On 2014/03/26 05:17:59, wallyworld wrote:
> Please take a look.

https://codereview.appspot.com/80230043/diff/20001/state/api/provisioner/provisioner_test.go
> File state/api/provisioner/provisioner_test.go (right):

https://codereview.appspot.com/80230043/diff/20001/state/api/provisioner/provisioner_test.go#newcode139
> state/api/provisioner/provisioner_test.go:139: c.Assert(info[0].Error,
> 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://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go
> File state/apiserver/provisioner/provisioner.go (right):

https://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go#newcode244
> state/apiserver/provisioner/provisioner.go:244: if canAccess && err ==
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://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go#newcode249
> state/apiserver/provisioner/provisioner.go:249: if transient, ok :=
> result.Data["transient"]; !ok || !transient.(bool) {
> 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://codereview.appspot.com/80230043/diff/20001/state/apiserver/provisioner/provisioner.go#newcode254
> state/apiserver/provisioner/provisioner.go:254: if err == nil {
> On 2014/03/26 04:57:01, axw wrote:
> > It doesn't seem right to me that MachinesWithTransientErrors should
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

https://codereview.appspot.com/80230043/

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

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.net/juju-core 0.013s
ok launchpad.net/juju-core/agent 1.103s
ok launchpad.net/juju-core/agent/mongo 0.536s
ok launchpad.net/juju-core/agent/tools 0.206s
ok launchpad.net/juju-core/bzr 5.054s
ok launchpad.net/juju-core/cert 2.984s
ok launchpad.net/juju-core/charm 0.443s
? launchpad.net/juju-core/charm/hooks [no test files]
? launchpad.net/juju-core/charm/testing [no test files]
ok launchpad.net/juju-core/cloudinit 0.033s
ok launchpad.net/juju-core/cloudinit/sshinit 0.811s
ok launchpad.net/juju-core/cmd 0.155s
ok launchpad.net/juju-core/cmd/charm-admin 0.722s
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/juju 196.023s
ok launchpad.net/juju-core/cmd/jujud 64.041s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 8.457s
? launchpad.net/juju-core/cmd/plugins/juju-restore [no test files]
ok launchpad.net/juju-core/cmd/plugins/local 0.160s
? launchpad.net/juju-core/cmd/plugins/local/juju-local [no test files]
ok launchpad.net/juju-core/constraints 0.021s
ok launchpad.net/juju-core/container 0.044s
ok launchpad.net/juju-core/container/factory 0.050s
ok launchpad.net/juju-core/container/kvm 0.217s
ok launchpad.net/juju-core/container/kvm/mock 0.044s
? launchpad.net/juju-core/container/kvm/testing [no test files]
ok launchpad.net/juju-core/container/lxc 4.290s
? launchpad.net/juju-core/container/lxc/mock [no test files]
? launchpad.net/juju-core/container/lxc/testing [no test files]
? launchpad.net/juju-core/container/testing [no test files]
ok launchpad.net/juju-core/downloader 5.250s
ok launchpad.net/juju-core/environs 2.390s
ok launchpad.net/juju-core/environs/bootstrap 10.380s
ok launchpad.net/juju-core/environs/cloudinit 0.469s
ok launchpad.net/juju-core/environs/config 2.283s
ok launchpad.net/juju-core/environs/configstore 0.033s
ok launchpad.net/juju-core/environs/filestorage 0.027s
ok launchpad.net/juju-core/environs/httpstorage 0.689s
ok launchpad.net/juju-core/environs/imagemetadata 0.431s
? launchpad.net/juju-core/environs/imagemetadata/testing [no test files]
ok launchpad.net/juju-core/environs/instances 0.050s
ok launchpad.net/juju-core/environs/jujutest 0.186s
ok launchpad.net/juju-core/environs/manual 12.385s
ok launchpad.net/juju-core/environs/simplestreams 0.267s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 0.869s
ok launchpad.net/juju-core/environs/storage 0.758s
ok launchpad.net/juju-core/environs/sync 42.734s
ok launchpad.net/juju-core/environs/testing 0.130s
ok launchpad.net/juju-core/environs/tools 4.933s
? launchpad.net/juju-core/environs/tools/testing [no test files]
ok launchpad.net/juju-core/errors 0.011s
ok launchpad.net/juju-core/instance 0.017s
? launchpad.net/juju-core/instance/testing [no test files]
ok launchpad.net/juju-core/juju 19.305s
ok launchpad.net/juju-core/juj...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'state/api/client.go'
--- state/api/client.go 2014-03-24 13:27:29 +0000
+++ state/api/client.go 2014-03-26 05:17:40 +0000
@@ -138,6 +138,20 @@
138 return c.call("Resolved", p, nil)138 return c.call("Resolved", p, nil)
139}139}
140140
141// ResolveProvisioningError updates the provisioning status of a machine allowing the
142// provisioner to retry.
143func (c *Client) ResolveProvisioningError(machine string) error {
144 p := params.Entities{
145 Entities: []params.Entity{{Tag: machine}},
146 }
147 var results params.ErrorResults
148 err := c.st.Call("Client", "", "ResolveProvisioningError", p, &results)
149 if err != nil {
150 return err
151 }
152 return results.OneError()
153}
154
141// PublicAddress returns the public address of the specified155// PublicAddress returns the public address of the specified
142// machine or unit.156// machine or unit.
143func (c *Client) PublicAddress(target string) (string, error) {157func (c *Client) PublicAddress(target string) (string, error) {
144158
=== modified file 'state/api/machiner/machine.go'
--- state/api/machiner/machine.go 2014-03-24 13:44:31 +0000
+++ state/api/machiner/machine.go 2014-03-26 05:17:40 +0000
@@ -42,7 +42,7 @@
42func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error {42func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error {
43 var result params.ErrorResults43 var result params.ErrorResults
44 args := params.SetStatus{44 args := params.SetStatus{
45 Entities: []params.SetEntityStatus{45 Entities: []params.EntityStatus{
46 {Tag: m.tag, Status: status, Info: info, Data: data},46 {Tag: m.tag, Status: status, Info: info, Data: data},
47 },47 },
48 }48 }
4949
=== modified file 'state/api/params/internal.go'
--- state/api/params/internal.go 2014-03-21 11:52:30 +0000
+++ state/api/params/internal.go 2014-03-26 05:17:40 +0000
@@ -315,25 +315,28 @@
315 Machines []MachineSetProvisioned315 Machines []MachineSetProvisioned
316}316}
317317
318// SetEntityStatus holds an entity tag, status and extra info.318// EntityStatus holds an entity tag, status and extra info.
319type SetEntityStatus struct {319type EntityStatus struct {
320 Tag string320 Tag string
321 Status Status321 Status Status
322 Info string322 Info string
323 Data StatusData323 Data StatusData
324}324}
325325
326// SetStatus holds the parameters for making a SetStatus call.326// SetStatus holds the parameters for making a SetStatus/UpdateStatus call.
327type SetStatus struct {327type SetStatus struct {
328 Entities []SetEntityStatus328 Entities []EntityStatus
329}329}
330330
331// StatusResult holds an entity status, extra information, or an331// StatusResult holds an entity status, extra information, or an
332// error.332// error.
333type StatusResult struct {333type StatusResult struct {
334 Error *Error334 Error *Error
335 Id string
336 Life Life
335 Status Status337 Status Status
336 Info string338 Info string
339 Data StatusData
337}340}
338341
339// StatusResults holds multiple status results.342// StatusResults holds multiple status results.
340343
=== modified file 'state/api/provisioner/machine.go'
--- state/api/provisioner/machine.go 2014-03-24 13:27:29 +0000
+++ state/api/provisioner/machine.go 2014-03-26 05:17:40 +0000
@@ -56,11 +56,11 @@
56}56}
5757
58// SetStatus sets the status of the machine.58// SetStatus sets the status of the machine.
59func (m *Machine) SetStatus(status params.Status, info string) error {59func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error {
60 var result params.ErrorResults60 var result params.ErrorResults
61 args := params.SetStatus{61 args := params.SetStatus{
62 Entities: []params.SetEntityStatus{62 Entities: []params.EntityStatus{
63 {Tag: m.tag, Status: status, Info: info},63 {Tag: m.tag, Status: status, Info: info, Data: data},
64 },64 },
65 }65 }
66 err := m.st.call("SetStatus", args, &result)66 err := m.st.call("SetStatus", args, &result)
6767
=== modified file 'state/api/provisioner/provisioner.go'
--- state/api/provisioner/provisioner.go 2014-03-21 18:40:43 +0000
+++ state/api/provisioner/provisioner.go 2014-03-26 05:17:40 +0000
@@ -6,6 +6,7 @@
6import (6import (
7 "fmt"7 "fmt"
88
9 "launchpad.net/juju-core/names"
9 "launchpad.net/juju-core/state/api/base"10 "launchpad.net/juju-core/state/api/base"
10 "launchpad.net/juju-core/state/api/common"11 "launchpad.net/juju-core/state/api/common"
11 "launchpad.net/juju-core/state/api/params"12 "launchpad.net/juju-core/state/api/params"
@@ -107,3 +108,25 @@
107 err = st.call("ContainerConfig", nil, &result)108 err = st.call("ContainerConfig", nil, &result)
108 return result, err109 return result, err
109}110}
111
112// MachinesWithTransientErrors returns a slice of machines and corresponding status information
113// for those machines which have transient provisioning errors.
114func (st *State) MachinesWithTransientErrors() ([]*Machine, []params.StatusResult, error) {
115 var results params.StatusResults
116 err := st.call("MachinesWithTransientErrors", nil, &results)
117 if err != nil {
118 return nil, nil, err
119 }
120 machines := make([]*Machine, len(results.Results))
121 for i, status := range results.Results {
122 if status.Error != nil {
123 continue
124 }
125 machines[i] = &Machine{
126 tag: names.MachineTag(status.Id),
127 life: status.Life,
128 st: st,
129 }
130 }
131 return machines, results.Results, nil
132}
110133
=== modified file 'state/api/provisioner/provisioner_test.go'
--- state/api/provisioner/provisioner_test.go 2014-03-21 18:18:08 +0000
+++ state/api/provisioner/provisioner_test.go 2014-03-26 05:17:40 +0000
@@ -88,13 +88,61 @@
88 c.Assert(status, gc.Equals, params.StatusPending)88 c.Assert(status, gc.Equals, params.StatusPending)
89 c.Assert(info, gc.Equals, "")89 c.Assert(info, gc.Equals, "")
9090
91 err = apiMachine.SetStatus(params.StatusStarted, "blah")91 err = apiMachine.SetStatus(params.StatusStarted, "blah", nil)
92 c.Assert(err, gc.IsNil)92 c.Assert(err, gc.IsNil)
9393
94 status, info, err = apiMachine.Status()94 status, info, err = apiMachine.Status()
95 c.Assert(err, gc.IsNil)95 c.Assert(err, gc.IsNil)
96 c.Assert(status, gc.Equals, params.StatusStarted)96 c.Assert(status, gc.Equals, params.StatusStarted)
97 c.Assert(info, gc.Equals, "blah")97 c.Assert(info, gc.Equals, "blah")
98 _, _, data, err := s.machine.Status()
99 c.Assert(err, gc.IsNil)
100 c.Assert(data, gc.HasLen, 0)
101}
102
103func (s *provisionerSuite) TestGetSetStatusWithData(c *gc.C) {
104 apiMachine, err := s.provisioner.Machine(s.machine.Tag())
105 c.Assert(err, gc.IsNil)
106
107 err = apiMachine.SetStatus(params.StatusError, "blah", params.StatusData{"foo": "bar"})
108 c.Assert(err, gc.IsNil)
109
110 status, info, err := apiMachine.Status()
111 c.Assert(err, gc.IsNil)
112 c.Assert(status, gc.Equals, params.StatusError)
113 c.Assert(info, gc.Equals, "blah")
114 _, _, data, err := s.machine.Status()
115 c.Assert(err, gc.IsNil)
116 c.Assert(data, gc.DeepEquals, params.StatusData{"foo": "bar"})
117}
118
119func (s *provisionerSuite) TestMachinesWithTransientErrors(c *gc.C) {
120 machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
121 c.Assert(err, gc.IsNil)
122 err = machine.SetStatus(params.StatusError, "blah", params.StatusData{"transient": true})
123 c.Assert(err, gc.IsNil)
124 password, err := utils.RandomPassword()
125 c.Assert(err, gc.IsNil)
126 err = machine.SetPassword(password)
127 c.Assert(err, gc.IsNil)
128 err = machine.SetProvisioned("i-manager", "fake_nonce", nil)
129 c.Assert(err, gc.IsNil)
130 st := s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce")
131 c.Assert(s.st, gc.NotNil)
132 p := st.Provisioner()
133
134 machines, info, err := p.MachinesWithTransientErrors()
135 c.Assert(err, gc.IsNil)
136 c.Assert(machines, gc.HasLen, 1)
137 c.Assert(machines[0].Id(), gc.Equals, "1")
138 c.Assert(info, gc.HasLen, 1)
139 c.Assert(info[0], gc.DeepEquals, params.StatusResult{
140 Id: "1",
141 Life: "alive",
142 Status: "error",
143 Info: "blah",
144 Data: params.StatusData{"transient": true},
145 })
98}146}
99147
100func (s *provisionerSuite) TestEnsureDeadAndRemove(c *gc.C) {148func (s *provisionerSuite) TestEnsureDeadAndRemove(c *gc.C) {
@@ -254,7 +302,7 @@
254302
255 // Change something other than the containers and make sure it's303 // Change something other than the containers and make sure it's
256 // not detected.304 // not detected.
257 err = apiMachine.SetStatus(params.StatusStarted, "not really")305 err = apiMachine.SetStatus(params.StatusStarted, "not really", nil)
258 c.Assert(err, gc.IsNil)306 c.Assert(err, gc.IsNil)
259 wc.AssertNoChange()307 wc.AssertNoChange()
260308
261309
=== modified file 'state/api/uniter/unit.go'
--- state/api/uniter/unit.go 2014-03-21 18:40:43 +0000
+++ state/api/uniter/unit.go 2014-03-26 05:17:40 +0000
@@ -58,7 +58,7 @@
58func (u *Unit) SetStatus(status params.Status, info string, data params.StatusData) error {58func (u *Unit) SetStatus(status params.Status, info string, data params.StatusData) error {
59 var result params.ErrorResults59 var result params.ErrorResults
60 args := params.SetStatus{60 args := params.SetStatus{
61 Entities: []params.SetEntityStatus{61 Entities: []params.EntityStatus{
62 {Tag: u.tag, Status: status, Info: info, Data: data},62 {Tag: u.tag, Status: status, Info: info, Data: data},
63 },63 },
64 }64 }
6565
=== modified file 'state/apiserver/client/client.go'
--- state/apiserver/client/client.go 2014-03-20 22:35:39 +0000
+++ state/apiserver/client/client.go 2014-03-26 05:17:40 +0000
@@ -38,6 +38,8 @@
38 resources *common.Resources38 resources *common.Resources
39 client *Client39 client *Client
40 dataDir string40 dataDir string
41 // statusSetter provides common methods for updating an entity's provisioning status.
42 statusSetter *common.StatusSetter
41}43}
4244
43// Client serves client-specific API methods.45// Client serves client-specific API methods.
@@ -48,10 +50,11 @@
48// NewAPI creates a new instance of the Client API.50// NewAPI creates a new instance of the Client API.
49func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer, datadir string) *API {51func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer, datadir string) *API {
50 r := &API{52 r := &API{
51 state: st,53 state: st,
52 auth: authorizer,54 auth: authorizer,
53 resources: resources,55 resources: resources,
54 dataDir: datadir,56 dataDir: datadir,
57 statusSetter: common.NewStatusSetter(st, common.AuthAlways(true)),
55 }58 }
56 r.client = &Client{59 r.client = &Client{
57 api: r,60 api: r,
@@ -947,3 +950,14 @@
947 }950 }
948 return charm.Quote(fmt.Sprintf("%s-%d-%s", name, revision, uuid)), nil951 return charm.Quote(fmt.Sprintf("%s-%d-%s", name, revision, uuid)), nil
949}952}
953
954// ResolveProvisioningError marks a provisioning error as transient on the machines.
955func (c *Client) ResolveProvisioningError(p params.Entities) (params.ErrorResults, error) {
956 entityStatus := make([]params.EntityStatus, len(p.Entities))
957 for i, entity := range p.Entities {
958 entityStatus[i] = params.EntityStatus{Tag: entity.Tag, Data: params.StatusData{"transient": true}}
959 }
960 return c.api.statusSetter.UpdateStatus(params.SetStatus{
961 Entities: entityStatus,
962 })
963}
950964
=== modified file 'state/apiserver/client/client_test.go'
--- state/apiserver/client/client_test.go 2014-03-24 20:08:45 +0000
+++ state/apiserver/client/client_test.go 2014-03-26 05:17:40 +0000
@@ -2004,3 +2004,18 @@
2004func getArchiveName(bundleURL *url.URL) string {2004func getArchiveName(bundleURL *url.URL) string {
2005 return strings.TrimPrefix(bundleURL.RequestURI(), "/dummyenv/private/")2005 return strings.TrimPrefix(bundleURL.RequestURI(), "/dummyenv/private/")
2006}2006}
2007
2008func (s *clientSuite) TestResolveProvisioningError(c *gc.C) {
2009 machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
2010 c.Assert(err, gc.IsNil)
2011 err = machine.SetStatus(params.StatusError, "error", nil)
2012 c.Assert(err, gc.IsNil)
2013 err = s.APIState.Client().ResolveProvisioningError(machine.Tag())
2014 c.Assert(err, gc.IsNil)
2015
2016 status, info, data, err := machine.Status()
2017 c.Assert(err, gc.IsNil)
2018 c.Assert(status, gc.Equals, params.StatusError)
2019 c.Assert(info, gc.Equals, "error")
2020 c.Assert(data["transient"], gc.Equals, true)
2021}
20072022
=== modified file 'state/apiserver/common/setstatus.go'
--- state/apiserver/common/setstatus.go 2014-01-20 21:00:43 +0000
+++ state/apiserver/common/setstatus.go 2014-03-26 05:17:40 +0000
@@ -58,3 +58,53 @@
58 }58 }
59 return result, nil59 return result, nil
60}60}
61
62func (s *StatusSetter) updateEntityStatusData(tag string, data params.StatusData) error {
63 entity0, err := s.st.FindEntity(tag)
64 if err != nil {
65 return err
66 }
67 statusGetter, ok := entity0.(state.StatusGetter)
68 if !ok {
69 return NotSupportedError(tag, "getting status")
70 }
71 existingStatus, existingInfo, existingData, err := statusGetter.Status()
72 if err != nil {
73 return err
74 }
75 newData := existingData
76 if newData == nil {
77 newData = data
78 } else {
79 for k, v := range data {
80 newData[k] = v
81 }
82 }
83 entity, ok := entity0.(state.StatusSetter)
84 if !ok {
85 return NotSupportedError(tag, "updating status")
86 }
87 return entity.SetStatus(existingStatus, existingInfo, newData)
88}
89
90// UpdateStatus updates the status data of each given entity.
91func (s *StatusSetter) UpdateStatus(args params.SetStatus) (params.ErrorResults, error) {
92 result := params.ErrorResults{
93 Results: make([]params.ErrorResult, len(args.Entities)),
94 }
95 if len(args.Entities) == 0 {
96 return result, nil
97 }
98 canModify, err := s.getCanModify()
99 if err != nil {
100 return params.ErrorResults{}, err
101 }
102 for i, arg := range args.Entities {
103 err := ErrPerm
104 if canModify(arg.Tag) {
105 err = s.updateEntityStatusData(arg.Tag, arg.Data)
106 }
107 result.Results[i].Error = ServerError(err)
108 }
109 return result, nil
110}
61111
=== modified file 'state/apiserver/common/setstatus_test.go'
--- state/apiserver/common/setstatus_test.go 2014-01-20 21:29:08 +0000
+++ state/apiserver/common/setstatus_test.go 2014-03-26 05:17:40 +0000
@@ -34,6 +34,17 @@
34 return s.err34 return s.err
35}35}
3636
37func (s *fakeStatusSetter) Status() (status params.Status, info string, data params.StatusData, err error) {
38 return s.status, s.info, s.data, nil
39}
40
41func (s *fakeStatusSetter) UpdateStatus(data params.StatusData) error {
42 for k, v := range data {
43 s.data[k] = v
44 }
45 return s.err
46}
47
37func (*statusSetterSuite) TestSetStatus(c *gc.C) {48func (*statusSetterSuite) TestSetStatus(c *gc.C) {
38 st := &fakeState{49 st := &fakeState{
39 entities: map[string]entityWithError{50 entities: map[string]entityWithError{
@@ -55,7 +66,7 @@
55 }66 }
56 s := common.NewStatusSetter(st, getCanModify)67 s := common.NewStatusSetter(st, getCanModify)
57 args := params.SetStatus{68 args := params.SetStatus{
58 Entities: []params.SetEntityStatus{69 Entities: []params.EntityStatus{
59 {"x0", params.StatusStarted, "bar", nil},70 {"x0", params.StatusStarted, "bar", nil},
60 {"x1", params.StatusStopped, "", nil},71 {"x1", params.StatusStopped, "", nil},
61 {"x2", params.StatusPending, "not really", nil},72 {"x2", params.StatusPending, "not really", nil},
@@ -91,7 +102,7 @@
91 }102 }
92 s := common.NewStatusSetter(&fakeState{}, getCanModify)103 s := common.NewStatusSetter(&fakeState{}, getCanModify)
93 args := params.SetStatus{104 args := params.SetStatus{
94 Entities: []params.SetEntityStatus{{"x0", "", "", nil}},105 Entities: []params.EntityStatus{{"x0", "", "", nil}},
95 }106 }
96 _, err := s.SetStatus(args)107 _, err := s.SetStatus(args)
97 c.Assert(err, gc.ErrorMatches, "pow")108 c.Assert(err, gc.ErrorMatches, "pow")
@@ -106,3 +117,56 @@
106 c.Assert(err, gc.IsNil)117 c.Assert(err, gc.IsNil)
107 c.Assert(result.Results, gc.HasLen, 0)118 c.Assert(result.Results, gc.HasLen, 0)
108}119}
120
121func (*statusSetterSuite) TestUpdateStatus(c *gc.C) {
122 st := &fakeState{
123 entities: map[string]entityWithError{
124 "x0": &fakeStatusSetter{status: params.StatusPending, info: "blah", err: fmt.Errorf("x0 fails")},
125 "x1": &fakeStatusSetter{status: params.StatusStarted, info: "foo", data: params.StatusData{"foo": "blah"}},
126 "x2": &fakeStatusSetter{status: params.StatusError, info: "some info"},
127 "x3": &fakeStatusSetter{fetchError: "x3 error"},
128 "x4": &fakeStatusSetter{status: params.StatusStopped, info: ""},
129 },
130 }
131 getCanModify := func() (common.AuthFunc, error) {
132 return func(tag string) bool {
133 switch tag {
134 case "x0", "x1", "x2", "x3":
135 return true
136 }
137 return false
138 }, nil
139 }
140 s := common.NewStatusSetter(st, getCanModify)
141 args := params.SetStatus{
142 Entities: []params.EntityStatus{
143 {Tag: "x0", Data: nil},
144 {Tag: "x1", Data: nil},
145 {Tag: "x2", Data: params.StatusData{"foo": "bar"}},
146 {Tag: "x3", Data: params.StatusData{"foo": "bar"}},
147 {Tag: "x4", Data: params.StatusData{"foo": "bar"}},
148 {Tag: "x5", Data: nil},
149 },
150 }
151 result, err := s.UpdateStatus(args)
152 c.Assert(err, gc.IsNil)
153 c.Assert(result, gc.DeepEquals, params.ErrorResults{
154 Results: []params.ErrorResult{
155 {&params.Error{Message: "x0 fails"}},
156 {nil},
157 {nil},
158 {&params.Error{Message: "x3 error"}},
159 {apiservertesting.ErrUnauthorized},
160 {apiservertesting.ErrUnauthorized},
161 },
162 })
163 get := func(tag string) *fakeStatusSetter {
164 return st.entities[tag].(*fakeStatusSetter)
165 }
166 c.Assert(get("x1").status, gc.Equals, params.StatusStarted)
167 c.Assert(get("x1").info, gc.Equals, "foo")
168 c.Assert(get("x1").data, gc.DeepEquals, params.StatusData{"foo": "blah"})
169 c.Assert(get("x2").status, gc.Equals, params.StatusError)
170 c.Assert(get("x2").info, gc.Equals, "some info")
171 c.Assert(get("x2").data, gc.DeepEquals, params.StatusData{"foo": "bar"})
172}
109173
=== modified file 'state/apiserver/machine/machiner_test.go'
--- state/apiserver/machine/machiner_test.go 2014-01-20 21:00:43 +0000
+++ state/apiserver/machine/machiner_test.go 2014-03-26 05:17:40 +0000
@@ -57,7 +57,7 @@
57 c.Assert(err, gc.IsNil)57 c.Assert(err, gc.IsNil)
5858
59 args := params.SetStatus{59 args := params.SetStatus{
60 Entities: []params.SetEntityStatus{60 Entities: []params.EntityStatus{
61 {Tag: "machine-1", Status: params.StatusError, Info: "not really"},61 {Tag: "machine-1", Status: params.StatusError, Info: "not really"},
62 {Tag: "machine-0", Status: params.StatusStopped, Info: "foobar"},62 {Tag: "machine-0", Status: params.StatusStopped, Info: "foobar"},
63 {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"},63 {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"},
6464
=== modified file 'state/apiserver/provisioner/provisioner.go'
--- state/apiserver/provisioner/provisioner.go 2014-03-20 13:57:50 +0000
+++ state/apiserver/provisioner/provisioner.go 2014-03-26 05:17:40 +0000
@@ -214,13 +214,48 @@
214 machine, err := p.getMachine(canAccess, entity.Tag)214 machine, err := p.getMachine(canAccess, entity.Tag)
215 if err == nil {215 if err == nil {
216 r := &result.Results[i]216 r := &result.Results[i]
217 r.Status, r.Info, _, err = machine.Status()217 r.Status, r.Info, r.Data, err = machine.Status()
218 }218 }
219 result.Results[i].Error = common.ServerError(err)219 result.Results[i].Error = common.ServerError(err)
220 }220 }
221 return result, nil221 return result, nil
222}222}
223223
224// MachinesWithTransientErrors returns status data for machines with provisioning
225// errors which are transient.
226func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) {
227 results := params.StatusResults{}
228 canAccessFunc, err := p.getAuthFunc()
229 if err != nil {
230 return results, err
231 }
232 // TODO (wallyworld) - add state.State API for more efficient machines query
233 machines, err := p.st.AllMachines()
234 if err != nil {
235 return results, err
236 }
237 for _, machine := range machines {
238 if !canAccessFunc(machine.Tag()) {
239 continue
240 }
241 result := params.StatusResult{}
242 if result.Status, result.Info, result.Data, err = machine.Status(); err != nil {
243 continue
244 }
245 if result.Status != params.StatusError {
246 continue
247 }
248 // Transient errors are marked as such in the status data.
249 if transient, ok := result.Data["transient"].(bool); !ok || !transient {
250 continue
251 }
252 result.Id = machine.Id()
253 result.Life = params.Life(machine.Life().String())
254 results.Results = append(results.Results, result)
255 }
256 return results, nil
257}
258
224// Series returns the deployed series for each given machine entity.259// Series returns the deployed series for each given machine entity.
225func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) {260func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) {
226 result := params.StringResults{261 result := params.StringResults{
227262
=== modified file 'state/apiserver/provisioner/provisioner_test.go'
--- state/apiserver/provisioner/provisioner_test.go 2014-03-18 02:36:58 +0000
+++ state/apiserver/provisioner/provisioner_test.go 2014-03-26 05:17:40 +0000
@@ -57,7 +57,7 @@
57 if withStateServer {57 if withStateServer {
58 s.machines = append(s.machines, testing.AddStateServerMachine(c, s.State))58 s.machines = append(s.machines, testing.AddStateServerMachine(c, s.State))
59 }59 }
60 for i := 0; i < 3; i++ {60 for i := 0; i < 4; i++ {
61 machine, err := s.State.AddMachine("quantal", state.JobHostUnits)61 machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
62 c.Check(err, gc.IsNil)62 c.Check(err, gc.IsNil)
63 s.machines = append(s.machines, machine)63 s.machines = append(s.machines, machine)
@@ -119,6 +119,7 @@
119 {Tag: s.machines[0].Tag(), Password: "xxx0-1234567890123457890"},119 {Tag: s.machines[0].Tag(), Password: "xxx0-1234567890123457890"},
120 {Tag: s.machines[1].Tag(), Password: "xxx1-1234567890123457890"},120 {Tag: s.machines[1].Tag(), Password: "xxx1-1234567890123457890"},
121 {Tag: s.machines[2].Tag(), Password: "xxx2-1234567890123457890"},121 {Tag: s.machines[2].Tag(), Password: "xxx2-1234567890123457890"},
122 {Tag: s.machines[3].Tag(), Password: "xxx3-1234567890123457890"},
122 {Tag: "machine-42", Password: "foo"},123 {Tag: "machine-42", Password: "foo"},
123 {Tag: "unit-foo-0", Password: "zzz"},124 {Tag: "unit-foo-0", Password: "zzz"},
124 {Tag: "service-bar", Password: "abc"},125 {Tag: "service-bar", Password: "abc"},
@@ -131,6 +132,7 @@
131 {nil},132 {nil},
132 {nil},133 {nil},
133 {nil},134 {nil},
135 {nil},
134 {apiservertesting.NotFoundError("machine 42")},136 {apiservertesting.NotFoundError("machine 42")},
135 {apiservertesting.ErrUnauthorized},137 {apiservertesting.ErrUnauthorized},
136 {apiservertesting.ErrUnauthorized},138 {apiservertesting.ErrUnauthorized},
@@ -317,8 +319,9 @@
317 c.Assert(err, gc.IsNil)319 c.Assert(err, gc.IsNil)
318320
319 args := params.SetStatus{321 args := params.SetStatus{
320 Entities: []params.SetEntityStatus{322 Entities: []params.EntityStatus{
321 {Tag: s.machines[0].Tag(), Status: params.StatusError, Info: "not really"},323 {Tag: s.machines[0].Tag(), Status: params.StatusError, Info: "not really",
324 Data: params.StatusData{"foo": "bar"}},
322 {Tag: s.machines[1].Tag(), Status: params.StatusStopped, Info: "foobar"},325 {Tag: s.machines[1].Tag(), Status: params.StatusStopped, Info: "foobar"},
323 {Tag: s.machines[2].Tag(), Status: params.StatusStarted, Info: "again"},326 {Tag: s.machines[2].Tag(), Status: params.StatusStarted, Info: "again"},
324 {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"},327 {Tag: "machine-42", Status: params.StatusStarted, Info: "blah"},
@@ -339,9 +342,58 @@
339 })342 })
340343
341 // Verify the changes.344 // Verify the changes.
342 s.assertStatus(c, 0, params.StatusError, "not really")345 s.assertStatus(c, 0, params.StatusError, "not really", params.StatusData{"foo": "bar"})
343 s.assertStatus(c, 1, params.StatusStopped, "foobar")346 s.assertStatus(c, 1, params.StatusStopped, "foobar", params.StatusData{})
344 s.assertStatus(c, 2, params.StatusStarted, "again")347 s.assertStatus(c, 2, params.StatusStarted, "again", params.StatusData{})
348}
349
350func (s *withoutStateServerSuite) TestMachinesWithTransientErrors(c *gc.C) {
351 err := s.machines[0].SetStatus(params.StatusStarted, "blah", nil)
352 c.Assert(err, gc.IsNil)
353 err = s.machines[1].SetStatus(params.StatusError, "transient error",
354 params.StatusData{"transient": true, "foo": "bar"})
355 c.Assert(err, gc.IsNil)
356 err = s.machines[2].SetStatus(params.StatusError, "error", params.StatusData{"transient": false})
357 c.Assert(err, gc.IsNil)
358 err = s.machines[3].SetStatus(params.StatusError, "error", nil)
359 c.Assert(err, gc.IsNil)
360
361 result, err := s.provisioner.MachinesWithTransientErrors()
362 c.Assert(err, gc.IsNil)
363 c.Assert(result, gc.DeepEquals, params.StatusResults{
364 Results: []params.StatusResult{
365 {Id: "1", Life: "alive", Status: "error", Info: "transient error",
366 Data: params.StatusData{"transient": true, "foo": "bar"}},
367 },
368 })
369}
370
371func (s *withoutStateServerSuite) TestMachinesWithTransientErrorsPermission(c *gc.C) {
372 // Machines where there's permission issues are omitted.
373 anAuthorizer := s.authorizer
374 anAuthorizer.MachineAgent = true
375 anAuthorizer.EnvironManager = false
376 anAuthorizer.Tag = "machine-1"
377 aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources,
378 anAuthorizer)
379 err = s.machines[0].SetStatus(params.StatusStarted, "blah", nil)
380 c.Assert(err, gc.IsNil)
381 err = s.machines[1].SetStatus(params.StatusError, "transient error",
382 params.StatusData{"transient": true, "foo": "bar"})
383 c.Assert(err, gc.IsNil)
384 err = s.machines[2].SetStatus(params.StatusError, "error", params.StatusData{"transient": false})
385 c.Assert(err, gc.IsNil)
386 err = s.machines[3].SetStatus(params.StatusError, "error", nil)
387 c.Assert(err, gc.IsNil)
388
389 result, err := aProvisioner.MachinesWithTransientErrors()
390 c.Assert(err, gc.IsNil)
391 c.Assert(result, gc.DeepEquals, params.StatusResults{
392 Results: []params.StatusResult{
393 {Id: "1", Life: "alive", Status: "error", Info: "transient error",
394 Data: params.StatusData{"transient": true, "foo": "bar"}},
395 },
396 })
345}397}
346398
347func (s *withoutStateServerSuite) TestEnsureDead(c *gc.C) {399func (s *withoutStateServerSuite) TestEnsureDead(c *gc.C) {
@@ -384,11 +436,14 @@
384 c.Assert(s.machines[index].Life(), gc.Equals, expectLife)436 c.Assert(s.machines[index].Life(), gc.Equals, expectLife)
385}437}
386438
387func (s *withoutStateServerSuite) assertStatus(c *gc.C, index int, expectStatus params.Status, expectInfo string) {439func (s *withoutStateServerSuite) assertStatus(c *gc.C, index int, expectStatus params.Status, expectInfo string,
388 status, info, _, err := s.machines[index].Status()440 expectData params.StatusData) {
441
442 status, info, data, err := s.machines[index].Status()
389 c.Assert(err, gc.IsNil)443 c.Assert(err, gc.IsNil)
390 c.Assert(status, gc.Equals, expectStatus)444 c.Assert(status, gc.Equals, expectStatus)
391 c.Assert(info, gc.Equals, expectInfo)445 c.Assert(info, gc.Equals, expectInfo)
446 c.Assert(data, gc.DeepEquals, expectData)
392}447}
393448
394func (s *withoutStateServerSuite) TestWatchContainers(c *gc.C) {449func (s *withoutStateServerSuite) TestWatchContainers(c *gc.C) {
@@ -482,7 +537,7 @@
482 c.Assert(err, gc.IsNil)537 c.Assert(err, gc.IsNil)
483 err = s.machines[1].SetStatus(params.StatusStopped, "foo", nil)538 err = s.machines[1].SetStatus(params.StatusStopped, "foo", nil)
484 c.Assert(err, gc.IsNil)539 c.Assert(err, gc.IsNil)
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"})
486 c.Assert(err, gc.IsNil)541 c.Assert(err, gc.IsNil)
487542
488 args := params.Entities{Entities: []params.Entity{543 args := params.Entities{Entities: []params.Entity{
@@ -497,9 +552,9 @@
497 c.Assert(err, gc.IsNil)552 c.Assert(err, gc.IsNil)
498 c.Assert(result, gc.DeepEquals, params.StatusResults{553 c.Assert(result, gc.DeepEquals, params.StatusResults{
499 Results: []params.StatusResult{554 Results: []params.StatusResult{
500 {Status: params.StatusStarted, Info: "blah"},555 {Status: params.StatusStarted, Info: "blah", Data: params.StatusData{}},
501 {Status: params.StatusStopped, Info: "foo"},556 {Status: params.StatusStopped, Info: "foo", Data: params.StatusData{}},
502 {Status: params.StatusError, Info: "not really"},557 {Status: params.StatusError, Info: "not really", Data: params.StatusData{"foo": "bar"}},
503 {Error: apiservertesting.NotFoundError("machine 42")},558 {Error: apiservertesting.NotFoundError("machine 42")},
504 {Error: apiservertesting.ErrUnauthorized},559 {Error: apiservertesting.ErrUnauthorized},
505 {Error: apiservertesting.ErrUnauthorized},560 {Error: apiservertesting.ErrUnauthorized},
@@ -650,7 +705,7 @@
650 c.Assert(err, gc.IsNil)705 c.Assert(err, gc.IsNil)
651 c.Assert(result, gc.DeepEquals, params.StringsWatchResult{706 c.Assert(result, gc.DeepEquals, params.StringsWatchResult{
652 StringsWatcherId: "1",707 StringsWatcherId: "1",
653 Changes: []string{"0", "1", "2"},708 Changes: []string{"0", "1", "2", "3"},
654 })709 })
655710
656 // Verify the resources were registered and stop them when done.711 // Verify the resources were registered and stop them when done.
657712
=== modified file 'state/apiserver/uniter/uniter_test.go'
--- state/apiserver/uniter/uniter_test.go 2014-03-17 13:22:55 +0000
+++ state/apiserver/uniter/uniter_test.go 2014-03-26 05:17:40 +0000
@@ -109,7 +109,7 @@
109 c.Assert(err, gc.IsNil)109 c.Assert(err, gc.IsNil)
110110
111 args := params.SetStatus{111 args := params.SetStatus{
112 Entities: []params.SetEntityStatus{112 Entities: []params.EntityStatus{
113 {Tag: "unit-mysql-0", Status: params.StatusError, Info: "not really"},113 {Tag: "unit-mysql-0", Status: params.StatusError, Info: "not really"},
114 {Tag: "unit-wordpress-0", Status: params.StatusStopped, Info: "foobar"},114 {Tag: "unit-wordpress-0", Status: params.StatusStopped, Info: "foobar"},
115 {Tag: "unit-foo-42", Status: params.StatusStarted, Info: "blah"},115 {Tag: "unit-foo-42", Status: params.StatusStarted, Info: "blah"},
116116
=== modified file 'state/interface.go'
--- state/interface.go 2014-01-20 17:47:59 +0000
+++ state/interface.go 2014-03-26 05:17:40 +0000
@@ -37,9 +37,15 @@
37 SetStatus(status params.Status, info string, data params.StatusData) error37 SetStatus(status params.Status, info string, data params.StatusData) error
38}38}
3939
40type StatusGetter interface {
41 Status() (status params.Status, info string, data params.StatusData, err error)
42}
43
40var (44var (
41 _ StatusSetter = (*Machine)(nil)45 _ StatusSetter = (*Machine)(nil)
42 _ StatusSetter = (*Unit)(nil)46 _ StatusSetter = (*Unit)(nil)
47 _ StatusGetter = (*Machine)(nil)
48 _ StatusGetter = (*Unit)(nil)
43)49)
4450
45// Lifer represents an entity with a life.51// Lifer represents an entity with a life.
4652
=== modified file 'state/machine_test.go'
=== modified file 'worker/provisioner/provisioner_task.go'
--- worker/provisioner/provisioner_task.go 2014-03-13 05:09:14 +0000
+++ worker/provisioner/provisioner_task.go 2014-03-26 05:17:40 +0000
@@ -405,7 +405,7 @@
405 // time until the error is resolved, but don't return an405 // time until the error is resolved, but don't return an
406 // error; just keep going with the other machines.406 // error; just keep going with the other machines.
407 logger.Errorf("cannot start instance for machine %q: %v", machine, err)407 logger.Errorf("cannot start instance for machine %q: %v", machine, err)
408 if err1 := machine.SetStatus(params.StatusError, err.Error()); err1 != nil {408 if err1 := machine.SetStatus(params.StatusError, err.Error(), nil); err1 != nil {
409 // Something is wrong with this machine, better report it back.409 // Something is wrong with this machine, better report it back.
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)
411 return err1411 return err1

Subscribers

People subscribed via source and target branches

to status/vote changes: