Merge lp:~axwalk/juju-core/lp1260171-stopinstances-ids into lp:~go-bot/juju-core/trunk

Proposed by Andrew Wilkins
Status: Merged
Approved by: Andrew Wilkins
Approved revision: no longer in the source branch.
Merged at revision: 2728
Proposed branch: lp:~axwalk/juju-core/lp1260171-stopinstances-ids
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 1305 lines (+232/-257)
38 files modified
container/interface.go (+7/-3)
container/kvm/kvm.go (+2/-2)
container/kvm/kvm_test.go (+1/-1)
container/kvm/live_test.go (+1/-1)
container/lxc/lxc.go (+2/-2)
container/lxc/lxc_test.go (+5/-5)
dependencies.tsv (+1/-1)
environs/broker.go (+3/-2)
environs/jujutest/livetests.go (+6/-6)
environs/jujutest/tests.go (+1/-1)
provider/azure/environ.go (+17/-16)
provider/azure/environ_test.go (+11/-12)
provider/common/bootstrap.go (+1/-1)
provider/common/bootstrap_test.go (+7/-7)
provider/common/destroy.go (+6/-1)
provider/common/destroy_test.go (+11/-11)
provider/common/mock_test.go (+3/-3)
provider/dummy/environs.go (+7/-7)
provider/ec2/ec2.go (+1/-5)
provider/ec2/live_test.go (+5/-5)
provider/joyent/environ_instance.go (+39/-6)
provider/joyent/instance.go (+0/-41)
provider/joyent/local_test.go (+3/-3)
provider/local/environ.go (+5/-5)
provider/maas/environ.go (+11/-25)
provider/maas/environ_whitebox_test.go (+17/-13)
provider/maas/util.go (+3/-3)
provider/maas/util_test.go (+1/-1)
provider/manual/environ.go (+1/-1)
provider/openstack/local_test.go (+8/-8)
provider/openstack/provider.go (+23/-19)
state/apiserver/client/destroy.go (+1/-22)
worker/provisioner/kvm-broker.go (+4/-4)
worker/provisioner/kvm-broker_test.go (+3/-3)
worker/provisioner/lxc-broker.go (+4/-4)
worker/provisioner/lxc-broker_test.go (+3/-3)
worker/provisioner/provisioner_task.go (+6/-2)
worker/provisioner/provisioner_test.go (+2/-2)
To merge this branch: bzr merge lp:~axwalk/juju-core/lp1260171-stopinstances-ids
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+219347@code.launchpad.net

Commit message

Change StopInstances to take []instance.Id

StopInstances previously took []instance.Instance,
which is unnecessary for most providers, and imposes
an unnecessary cost for callers that only have IDs
initially. We change StopInstances to

The openstack provider's StopInstances is now slightly
more expensive than before, requiring an additional call
to Instances. This cost will disappear when we fix the
security groups/open-port implementation.

Drive-by: the maas provider now uses a bulk "release"
in StopInstances.

Live tested on ec2, local, virtual MAAS, canonistack, azure.
I lack a Joyent account, but I think it should be fine.

Fixes lp:1260171
Fixes lp:1316272

https://codereview.appspot.com/97400043/

Description of the change

Change StopInstances to take []instance.Id

StopInstances previously took []instance.Instance,
which is unnecessary for most providers, and imposes
an unnecessary cost for callers that only have IDs
initially. We change StopInstances to

The openstack provider's StopInstances is now slightly
more expensive than before, requiring an additional call
to Instances. This cost will disappear when we fix the
security groups/open-port implementation.

Drive-by: the maas provider now uses a bulk "release"
in StopInstances.

Live tested on ec2, local, virtual MAAS, canonistack, azure.
I lack a Joyent account, but I think it should be fine.

Fixes lp:1260171
Fixes lp:1316272

https://codereview.appspot.com/97400043/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :

Reviewers: mp+219347_code.launchpad.net,

Message:
Please take a look.

Description:
Change StopInstances to take []instance.Id

StopInstances previously took []instance.Instance,
which is unnecessary for most providers, and imposes
an unnecessary cost for callers that only have IDs
initially. We change StopInstances to

The openstack provider's StopInstances is now slightly
more expensive than before, requiring an additional call
to Instances. This cost will disappear when we fix the
security groups/open-port implementation.

Drive-by: the maas provider now uses a bulk "release"
in StopInstances.

Live tested on ec2, local, virtual MAAS, canonistack, azure.
I lack a Joyent account, but I think it should be fine.

Fixes lp:1260171
Fixes lp:1316272

https://code.launchpad.net/~axwalk/juju-core/lp1260171-stopinstances-ids/+merge/219347

(do not edit description out of merge proposal)

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

Affected files (+234, -257 lines):
   A [revision details]
   M container/interface.go
   M container/kvm/kvm.go
   M container/kvm/kvm_test.go
   M container/kvm/live_test.go
   M container/lxc/lxc.go
   M container/lxc/lxc_test.go
   M dependencies.tsv
   M environs/broker.go
   M environs/jujutest/livetests.go
   M environs/jujutest/tests.go
   M provider/azure/environ.go
   M provider/azure/environ_test.go
   M provider/common/bootstrap.go
   M provider/common/bootstrap_test.go
   M provider/common/destroy.go
   M provider/common/destroy_test.go
   M provider/common/mock_test.go
   M provider/dummy/environs.go
   M provider/ec2/ec2.go
   M provider/ec2/live_test.go
   M provider/joyent/environ_instance.go
   M provider/joyent/instance.go
   M provider/joyent/local_test.go
   M provider/local/environ.go
   M provider/maas/environ.go
   M provider/maas/environ_whitebox_test.go
   M provider/maas/util.go
   M provider/maas/util_test.go
   M provider/manual/environ.go
   M provider/openstack/local_test.go
   M provider/openstack/provider.go
   M state/apiserver/client/destroy.go
   M worker/provisioner/kvm-broker.go
   M worker/provisioner/kvm-broker_test.go
   M worker/provisioner/lxc-broker.go
   M worker/provisioner/lxc-broker_test.go
   M worker/provisioner/provisioner_task.go
   M worker/provisioner/provisioner_test.go

Revision history for this message
Dave Cheney (dave-cheney) wrote :

On 2014/05/13 11:58:10, axw wrote:
> Please take a look.

SGTM. How do you feel about making StopInstance variadic ?

https://codereview.appspot.com/97400043/

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

LGTM, agree with Dave's comment so would be good to do that before
landing

https://codereview.appspot.com/97400043/diff/1/container/interface.go
File container/interface.go (right):

https://codereview.appspot.com/97400043/diff/1/container/interface.go#newcode31
container/interface.go:31: // Instance.
comment tweak - "by instance id"

https://codereview.appspot.com/97400043/

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

Please take a look.

https://codereview.appspot.com/97400043/diff/1/container/interface.go
File container/interface.go (right):

https://codereview.appspot.com/97400043/diff/1/container/interface.go#newcode31
container/interface.go:31: // Instance.
On 2014/05/14 02:37:16, wallyworld wrote:
> comment tweak - "by instance id"

Done.

https://codereview.appspot.com/97400043/

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

The attempt to merge lp:~axwalk/juju-core/lp1260171-stopinstances-ids into lp:juju-core failed. Below is the output from the failed tests.

godeps: cannot update "/home/tarmac/trees/src/launchpad.net/gomaasapi": bzr: ERROR: branch has no revision <email address hidden>
bzr update --revision only works for a revision in the branch history
mongod: no process found

Revision history for this message
Dave Cheney (dave-cheney) wrote :

On Wed, May 14, 2014 at 12:37 PM, <email address hidden> wrote:
> LGTM, agree with Dave's comment so would be good to do that before
> landing

Making StopInstance variadic would make some callers initially more verbose, ie

var i []instance.Instance
p.StopInstances(i...) // note variadic expansion to pass the contents
of the slice, not the slice itself.

but, as I found with the change that I made to Addresses recently,
most of the callers that are creating those slices are doing so to
pass one item, so they can be refactored to be variadic themselves and
everything becomes a lot simpler to read without so much tedious
boxing of single values into slices.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'container/interface.go'
--- container/interface.go 2014-03-19 21:08:58 +0000
+++ container/interface.go 2014-05-14 03:01:15 +0000
@@ -20,13 +20,17 @@
20// Manager is responsible for starting containers, and stopping and listing20// Manager is responsible for starting containers, and stopping and listing
21// containers that it has started.21// containers that it has started.
22type Manager interface {22type Manager interface {
23 // CreateContainer creates and starts a new container for the specified machine.23 // CreateContainer creates and starts a new container for the specified
24 // machine.
24 CreateContainer(25 CreateContainer(
25 machineConfig *cloudinit.MachineConfig,26 machineConfig *cloudinit.MachineConfig,
26 series string,27 series string,
27 network *NetworkConfig) (instance.Instance, *instance.HardwareCharacteristics, error)28 network *NetworkConfig) (instance.Instance, *instance.HardwareCharacteristics, error)
28 // DestroyContainer stops and destroyes the container identified by Instance.29
29 DestroyContainer(instance.Instance) error30 // DestroyContainer stops and destroyes the container identified by
31 // instance id.
32 DestroyContainer(instance.Id) error
33
30 // ListContainers return a list of containers that have been started by34 // ListContainers return a list of containers that have been started by
31 // this manager.35 // this manager.
32 ListContainers() ([]instance.Instance, error)36 ListContainers() ([]instance.Instance, error)
3337
=== modified file 'container/kvm/kvm.go'
--- container/kvm/kvm.go 2014-04-17 03:41:32 +0000
+++ container/kvm/kvm.go 2014-05-14 03:01:15 +0000
@@ -125,8 +125,8 @@
125 return &kvmInstance{kvmContainer, name}, &hardware, nil125 return &kvmInstance{kvmContainer, name}, &hardware, nil
126}126}
127127
128func (manager *containerManager) DestroyContainer(instance instance.Instance) error {128func (manager *containerManager) DestroyContainer(id instance.Id) error {
129 name := string(instance.Id())129 name := string(id)
130 kvmContainer := KvmObjectFactory.New(name)130 kvmContainer := KvmObjectFactory.New(name)
131 if err := kvmContainer.Stop(); err != nil {131 if err := kvmContainer.Stop(); err != nil {
132 logger.Errorf("failed to stop kvm container: %v", err)132 logger.Errorf("failed to stop kvm container: %v", err)
133133
=== modified file 'container/kvm/kvm_test.go'
--- container/kvm/kvm_test.go 2014-04-09 16:36:12 +0000
+++ container/kvm/kvm_test.go 2014-05-14 03:01:15 +0000
@@ -98,7 +98,7 @@
98func (s *KVMSuite) TestDestroyContainer(c *gc.C) {98func (s *KVMSuite) TestDestroyContainer(c *gc.C) {
99 instance := containertesting.CreateContainer(c, s.manager, "1/lxc/0")99 instance := containertesting.CreateContainer(c, s.manager, "1/lxc/0")
100100
101 err := s.manager.DestroyContainer(instance)101 err := s.manager.DestroyContainer(instance.Id())
102 c.Assert(err, gc.IsNil)102 c.Assert(err, gc.IsNil)
103103
104 name := string(instance.Id())104 name := string(instance.Id())
105105
=== modified file 'container/kvm/live_test.go'
--- container/kvm/live_test.go 2014-04-01 16:27:22 +0000
+++ container/kvm/live_test.go 2014-05-14 03:01:15 +0000
@@ -75,7 +75,7 @@
75 instances, err := manager.ListContainers()75 instances, err := manager.ListContainers()
76 c.Assert(err, gc.IsNil)76 c.Assert(err, gc.IsNil)
77 for _, instance := range instances {77 for _, instance := range instances {
78 err := manager.DestroyContainer(instance)78 err := manager.DestroyContainer(instance.Id())
79 c.Check(err, gc.IsNil)79 c.Check(err, gc.IsNil)
80 }80 }
81 }81 }
8282
=== modified file 'container/lxc/lxc.go'
--- container/lxc/lxc.go 2014-03-19 21:08:58 +0000
+++ container/lxc/lxc.go 2014-05-14 03:01:15 +0000
@@ -269,9 +269,9 @@
269 return appendToContainerConfig(name, line)269 return appendToContainerConfig(name, line)
270}270}
271271
272func (manager *containerManager) DestroyContainer(instance instance.Instance) error {272func (manager *containerManager) DestroyContainer(id instance.Id) error {
273 start := time.Now()273 start := time.Now()
274 name := string(instance.Id())274 name := string(id)
275 lxcContainer := LxcObjectFactory.New(name)275 lxcContainer := LxcObjectFactory.New(name)
276 if useRestartDir() {276 if useRestartDir() {
277 // Remove the autostart link.277 // Remove the autostart link.
278278
=== modified file 'container/lxc/lxc_test.go'
--- container/lxc/lxc_test.go 2014-03-19 21:08:58 +0000
+++ container/lxc/lxc_test.go 2014-05-14 03:01:15 +0000
@@ -274,7 +274,7 @@
274274
275 // DestroyContainer stops and then destroys the container, putting it275 // DestroyContainer stops and then destroys the container, putting it
276 // into "unknown" state.276 // into "unknown" state.
277 err := manager.DestroyContainer(instance)277 err := manager.DestroyContainer(instance.Id())
278 c.Assert(err, gc.IsNil)278 c.Assert(err, gc.IsNil)
279 c.Assert(instance.Status(), gc.Equals, string(golxc.StateUnknown))279 c.Assert(instance.Status(), gc.Equals, string(golxc.StateUnknown))
280}280}
@@ -283,7 +283,7 @@
283 manager := s.makeManager(c, "test")283 manager := s.makeManager(c, "test")
284 instance := containertesting.CreateContainer(c, manager, "1/lxc/0")284 instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
285285
286 err := manager.DestroyContainer(instance)286 err := manager.DestroyContainer(instance.Id())
287 c.Assert(err, gc.IsNil)287 c.Assert(err, gc.IsNil)
288288
289 name := string(instance.Id())289 name := string(instance.Id())
@@ -302,7 +302,7 @@
302 err := os.MkdirAll(targetDir, 0755)302 err := os.MkdirAll(targetDir, 0755)
303 c.Assert(err, gc.IsNil)303 c.Assert(err, gc.IsNil)
304304
305 err = manager.DestroyContainer(instance)305 err = manager.DestroyContainer(instance.Id())
306 c.Assert(err, gc.IsNil)306 c.Assert(err, gc.IsNil)
307307
308 // Check that the container dir is no longer in the container dir308 // Check that the container dir is no longer in the container dir
@@ -368,7 +368,7 @@
368func (s *LxcSuite) TestDestroyContainerRemovesAutostartLink(c *gc.C) {368func (s *LxcSuite) TestDestroyContainerRemovesAutostartLink(c *gc.C) {
369 manager := s.makeManager(c, "test")369 manager := s.makeManager(c, "test")
370 instance := containertesting.CreateContainer(c, manager, "1/lxc/0")370 instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
371 err := manager.DestroyContainer(instance)371 err := manager.DestroyContainer(instance.Id())
372 c.Assert(err, gc.IsNil)372 c.Assert(err, gc.IsNil)
373 autostartLink := lxc.RestartSymlink(string(instance.Id()))373 autostartLink := lxc.RestartSymlink(string(instance.Id()))
374 c.Assert(autostartLink, jc.SymlinkDoesNotExist)374 c.Assert(autostartLink, jc.SymlinkDoesNotExist)
@@ -380,7 +380,7 @@
380380
381 manager := s.makeManager(c, "test")381 manager := s.makeManager(c, "test")
382 instance := containertesting.CreateContainer(c, manager, "1/lxc/0")382 instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
383 err = manager.DestroyContainer(instance)383 err = manager.DestroyContainer(instance.Id())
384 c.Assert(err, gc.IsNil)384 c.Assert(err, gc.IsNil)
385}385}
386386
387387
=== modified file 'dependencies.tsv'
--- dependencies.tsv 2014-04-30 22:17:51 +0000
+++ dependencies.tsv 2014-05-14 03:01:15 +0000
@@ -13,7 +13,7 @@
13launchpad.net/goamz bzr roger.peppe@canonical.com-20131218155244-hbnkvlkkzy3vmlh9 4413launchpad.net/goamz bzr roger.peppe@canonical.com-20131218155244-hbnkvlkkzy3vmlh9 44
14launchpad.net/gocheck bzr gustavo@niemeyer.net-20140225173054-xu9zlkf9kxhvow02 8714launchpad.net/gocheck bzr gustavo@niemeyer.net-20140225173054-xu9zlkf9kxhvow02 87
15launchpad.net/golxc bzr tim.penhey@canonical.com-20140311005930-b14361bwnocu3krh 815launchpad.net/golxc bzr tim.penhey@canonical.com-20140311005930-b14361bwnocu3krh 8
16launchpad.net/gomaasapi bzr martin.packman@canonical.com-20140410114803-rsv7r2a2mgs0z0q4 4916launchpad.net/gomaasapi bzr andrew.wilkins@canonical.com-20140513111813-kstzbs2kx1ujl3m3 50
17launchpad.net/goose bzr tarmac-20140423072524-vxav71c7ko4lgtcu 11917launchpad.net/goose bzr tarmac-20140423072524-vxav71c7ko4lgtcu 119
18launchpad.net/goyaml bzr gustavo@niemeyer.net-20131114120802-abe042syx64z2m7s 5018launchpad.net/goyaml bzr gustavo@niemeyer.net-20131114120802-abe042syx64z2m7s 50
19launchpad.net/gwacl bzr tarmac-20140312041035-ac7gw7kcqjx7db63 23419launchpad.net/gwacl bzr tarmac-20140312041035-ac7gw7kcqjx7db63 234
2020
=== modified file 'environs/broker.go'
--- environs/broker.go 2014-04-22 09:23:39 +0000
+++ environs/broker.go 2014-05-14 03:01:15 +0000
@@ -50,8 +50,9 @@
50 // id.50 // id.
51 StartInstance(args StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, []network.Info, error)51 StartInstance(args StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, []network.Info, error)
5252
53 // StopInstances shuts down the given instances.53 // StopInstances shuts down the instances with the specified IDs.
54 StopInstances([]instance.Instance) error54 // Unknown instance IDs are ignored, to enable idempotency.
55 StopInstances(...instance.Id) error
5556
56 // AllInstances returns all instances currently known to the broker.57 // AllInstances returns all instances currently known to the broker.
57 AllInstances() ([]instance.Instance, error)58 AllInstances() ([]instance.Instance, error)
5859
=== modified file 'environs/jujutest/livetests.go'
--- environs/jujutest/livetests.go 2014-04-24 02:27:38 +0000
+++ environs/jujutest/livetests.go 2014-05-14 03:01:15 +0000
@@ -208,7 +208,7 @@
208 c.Check(insts[0].Id(), gc.Equals, id0)208 c.Check(insts[0].Id(), gc.Equals, id0)
209 c.Check(insts[1], gc.IsNil)209 c.Check(insts[1], gc.IsNil)
210210
211 err = t.Env.StopInstances([]instance.Instance{inst})211 err = t.Env.StopInstances(inst.Id())
212 c.Assert(err, gc.IsNil)212 c.Assert(err, gc.IsNil)
213213
214 // The machine may not be marked as shutting down214 // The machine may not be marked as shutting down
@@ -229,7 +229,7 @@
229229
230 inst1, _ := testing.AssertStartInstance(c, t.Env, "1")230 inst1, _ := testing.AssertStartInstance(c, t.Env, "1")
231 c.Assert(inst1, gc.NotNil)231 c.Assert(inst1, gc.NotNil)
232 defer t.Env.StopInstances([]instance.Instance{inst1})232 defer t.Env.StopInstances(inst1.Id())
233 ports, err := inst1.Ports("1")233 ports, err := inst1.Ports("1")
234 c.Assert(err, gc.IsNil)234 c.Assert(err, gc.IsNil)
235 c.Assert(ports, gc.HasLen, 0)235 c.Assert(ports, gc.HasLen, 0)
@@ -239,7 +239,7 @@
239 ports, err = inst2.Ports("2")239 ports, err = inst2.Ports("2")
240 c.Assert(err, gc.IsNil)240 c.Assert(err, gc.IsNil)
241 c.Assert(ports, gc.HasLen, 0)241 c.Assert(ports, gc.HasLen, 0)
242 defer t.Env.StopInstances([]instance.Instance{inst2})242 defer t.Env.StopInstances(inst2.Id())
243243
244 // Open some ports and check they're there.244 // Open some ports and check they're there.
245 err = inst1.OpenPorts("1", []instance.Port{{"udp", 67}, {"tcp", 45}})245 err = inst1.OpenPorts("1", []instance.Port{{"udp", 67}, {"tcp", 45}})
@@ -332,7 +332,7 @@
332332
333 // Create instances and check open ports on both instances.333 // Create instances and check open ports on both instances.
334 inst1, _ := testing.AssertStartInstance(c, t.Env, "1")334 inst1, _ := testing.AssertStartInstance(c, t.Env, "1")
335 defer t.Env.StopInstances([]instance.Instance{inst1})335 defer t.Env.StopInstances(inst1.Id())
336 ports, err := t.Env.Ports()336 ports, err := t.Env.Ports()
337 c.Assert(err, gc.IsNil)337 c.Assert(err, gc.IsNil)
338 c.Assert(ports, gc.HasLen, 0)338 c.Assert(ports, gc.HasLen, 0)
@@ -341,7 +341,7 @@
341 ports, err = t.Env.Ports()341 ports, err = t.Env.Ports()
342 c.Assert(err, gc.IsNil)342 c.Assert(err, gc.IsNil)
343 c.Assert(ports, gc.HasLen, 0)343 c.Assert(ports, gc.HasLen, 0)
344 defer t.Env.StopInstances([]instance.Instance{inst2})344 defer t.Env.StopInstances(inst2.Id())
345345
346 err = t.Env.OpenPorts([]instance.Port{{"udp", 67}, {"tcp", 45}, {"tcp", 89}, {"tcp", 99}})346 err = t.Env.OpenPorts([]instance.Port{{"udp", 67}, {"tcp", 45}, {"tcp", 89}, {"tcp", 99}})
347 c.Assert(err, gc.IsNil)347 c.Assert(err, gc.IsNil)
@@ -865,7 +865,7 @@
865 MachineConfig: machineConfig,865 MachineConfig: machineConfig,
866 })866 })
867 if inst != nil {867 if inst != nil {
868 err := t.Env.StopInstances([]instance.Instance{inst})868 err := t.Env.StopInstances(inst.Id())
869 c.Check(err, gc.IsNil)869 c.Check(err, gc.IsNil)
870 }870 }
871 c.Assert(inst, gc.IsNil)871 c.Assert(inst, gc.IsNil)
872872
=== modified file 'environs/jujutest/tests.go'
--- environs/jujutest/tests.go 2014-05-06 21:40:29 +0000
+++ environs/jujutest/tests.go 2014-05-14 03:01:15 +0000
@@ -114,7 +114,7 @@
114 c.Assert(insts, gc.HasLen, 2)114 c.Assert(insts, gc.HasLen, 2)
115 c.Assert(insts[0].Id(), gc.Not(gc.Equals), insts[1].Id())115 c.Assert(insts[0].Id(), gc.Not(gc.Equals), insts[1].Id())
116116
117 err = e.StopInstances([]instance.Instance{inst0})117 err = e.StopInstances(inst0.Id())
118 c.Assert(err, gc.IsNil)118 c.Assert(err, gc.IsNil)
119119
120 insts, err = e.Instances([]instance.Id{id0, id1})120 insts, err = e.Instances([]instance.Id{id0, id1})
121121
=== modified file 'provider/azure/environ.go'
--- provider/azure/environ.go 2014-05-06 16:08:51 +0000
+++ provider/azure/environ.go 2014-05-14 03:01:15 +0000
@@ -475,7 +475,7 @@
475 var inst instance.Instance475 var inst instance.Instance
476 defer func() {476 defer func() {
477 if inst != nil && resultErr != nil {477 if inst != nil && resultErr != nil {
478 if err := env.StopInstances([]instance.Instance{inst}); err != nil {478 if err := env.StopInstances(inst.Id()); err != nil {
479 // Failure upon failure. Log it, but return the original error.479 // Failure upon failure. Log it, but return the original error.
480 logger.Errorf("error releasing failed instance: %v", err)480 logger.Errorf("error releasing failed instance: %v", err)
481 }481 }
@@ -760,7 +760,7 @@
760}760}
761761
762// StopInstances is specified in the InstanceBroker interface.762// StopInstances is specified in the InstanceBroker interface.
763func (env *azureEnviron) StopInstances(instances []instance.Instance) error {763func (env *azureEnviron) StopInstances(ids ...instance.Id) error {
764 context, err := env.getManagementAPI()764 context, err := env.getManagementAPI()
765 if err != nil {765 if err != nil {
766 return err766 return err
@@ -769,18 +769,18 @@
769769
770 // Map services to role names we want to delete.770 // Map services to role names we want to delete.
771 serviceInstances := make(map[string]map[string]bool)771 serviceInstances := make(map[string]map[string]bool)
772 for _, instance := range instances {772 for _, id := range ids {
773 instance, ok := instance.(*azureInstance)773 serviceName, roleName := env.splitInstanceId(id)
774 if !ok {774 if roleName == "" {
775 continue775 serviceInstances[serviceName] = nil
776 }776 } else {
777 serviceName := instance.hostedService.ServiceName777 deleteRoleNames, ok := serviceInstances[serviceName]
778 deleteRoleNames, ok := serviceInstances[serviceName]778 if !ok {
779 if !ok {779 deleteRoleNames = make(map[string]bool)
780 deleteRoleNames = make(map[string]bool)780 serviceInstances[serviceName] = deleteRoleNames
781 serviceInstances[serviceName] = deleteRoleNames781 }
782 }782 deleteRoleNames[roleName] = true
783 deleteRoleNames[instance.roleName] = true783 }
784 }784 }
785785
786 // Load the properties of each service, so we know whether to786 // Load the properties of each service, so we know whether to
@@ -806,8 +806,9 @@
806 }806 }
807 }807 }
808 // If we're deleting all the roles, we need to delete the808 // If we're deleting all the roles, we need to delete the
809 // entire cloud service or we'll get an error.809 // entire cloud service or we'll get an error. deleteRoleNames
810 if len(deleteRoleNames) == roleNames.Size() {810 // is nil if we're dealing with a legacy deployment.
811 if deleteRoleNames == nil || len(deleteRoleNames) == roleNames.Size() {
811 if err := context.DeleteHostedService(serviceName); err != nil {812 if err := context.DeleteHostedService(serviceName); err != nil {
812 return err813 return err
813 }814 }
814815
=== modified file 'provider/azure/environ_test.go'
--- provider/azure/environ_test.go 2014-04-30 23:18:40 +0000
+++ provider/azure/environ_test.go 2014-05-14 03:01:15 +0000
@@ -762,10 +762,11 @@
762762
763func (s *environSuite) TestStopInstancesDestroysMachines(c *gc.C) {763func (s *environSuite) TestStopInstancesDestroysMachines(c *gc.C) {
764 env := makeEnviron(c)764 env := makeEnviron(c)
765 prefix := env.getEnvPrefix()
765 service1Name := "service1"766 service1Name := "service1"
766 service1 := makeLegacyDeployment(env, service1Name)767 service1 := makeLegacyDeployment(env, prefix+service1Name)
767 service2Name := "service2"768 service2Name := "service2"
768 service2 := makeDeployment(env, service2Name)769 service2 := makeDeployment(env, prefix+service2Name)
769770
770 inst1, err := env.getInstance(service1, "")771 inst1, err := env.getInstance(service1, "")
771 c.Assert(err, gc.IsNil)772 c.Assert(err, gc.IsNil)
@@ -781,7 +782,7 @@
781 responses = append(responses, buildGetServicePropertiesResponses(c, service2)...)782 responses = append(responses, buildGetServicePropertiesResponses(c, service2)...)
782 responses = append(responses, buildStatusOKResponses(c, 1)...) // DeleteHostedService783 responses = append(responses, buildStatusOKResponses(c, 1)...) // DeleteHostedService
783 requests := gwacl.PatchManagementAPIResponses(responses)784 requests := gwacl.PatchManagementAPIResponses(responses)
784 err = env.StopInstances([]instance.Instance{inst1, inst2, inst3})785 err = env.StopInstances(inst1.Id(), inst2.Id(), inst3.Id())
785 c.Check(err, gc.IsNil)786 c.Check(err, gc.IsNil)
786787
787 // One GET and DELETE per service788 // One GET and DELETE per service
@@ -795,7 +796,7 @@
795796
796func (s *environSuite) TestStopInstancesServiceSubset(c *gc.C) {797func (s *environSuite) TestStopInstancesServiceSubset(c *gc.C) {
797 env := makeEnviron(c)798 env := makeEnviron(c)
798 service := makeDeployment(env, "service")799 service := makeDeployment(env, env.getEnvPrefix()+"service")
799800
800 role1Name := service.Deployments[0].RoleList[0].RoleName801 role1Name := service.Deployments[0].RoleList[0].RoleName
801 inst1, err := env.getInstance(service, role1Name)802 inst1, err := env.getInstance(service, role1Name)
@@ -804,7 +805,7 @@
804 responses := buildGetServicePropertiesResponses(c, service)805 responses := buildGetServicePropertiesResponses(c, service)
805 responses = append(responses, buildStatusOKResponses(c, 1)...) // DeleteRole806 responses = append(responses, buildStatusOKResponses(c, 1)...) // DeleteRole
806 requests := gwacl.PatchManagementAPIResponses(responses)807 requests := gwacl.PatchManagementAPIResponses(responses)
807 err = env.StopInstances([]instance.Instance{inst1})808 err = env.StopInstances(inst1.Id())
808 c.Check(err, gc.IsNil)809 c.Check(err, gc.IsNil)
809810
810 // One GET for the service, and one DELETE for the role.811 // One GET for the service, and one DELETE for the role.
@@ -817,8 +818,9 @@
817818
818func (s *environSuite) TestStopInstancesWhenStoppingMachinesFails(c *gc.C) {819func (s *environSuite) TestStopInstancesWhenStoppingMachinesFails(c *gc.C) {
819 env := makeEnviron(c)820 env := makeEnviron(c)
820 service1 := makeDeployment(env, "service1")821 prefix := env.getEnvPrefix()
821 service2 := makeDeployment(env, "service2")822 service1 := makeDeployment(env, prefix+"service1")
823 service2 := makeDeployment(env, prefix+"service2")
822 service1Role1Name := service1.Deployments[0].RoleList[0].RoleName824 service1Role1Name := service1.Deployments[0].RoleList[0].RoleName
823 inst1, err := env.getInstance(service1, service1Role1Name)825 inst1, err := env.getInstance(service1, service1Role1Name)
824 c.Assert(err, gc.IsNil)826 c.Assert(err, gc.IsNil)
@@ -831,8 +833,7 @@
831 responses = append(responses, gwacl.NewDispatcherResponse(nil, http.StatusConflict, nil))833 responses = append(responses, gwacl.NewDispatcherResponse(nil, http.StatusConflict, nil))
832 requests := gwacl.PatchManagementAPIResponses(responses)834 requests := gwacl.PatchManagementAPIResponses(responses)
833835
834 instances := []instance.Instance{inst1, inst2}836 err = env.StopInstances(inst1.Id(), inst2.Id())
835 err = env.StopInstances(instances)
836 c.Check(err, gc.ErrorMatches, ".*Conflict.*")837 c.Check(err, gc.ErrorMatches, ".*Conflict.*")
837838
838 c.Check(len(*requests), gc.Equals, len(responses))839 c.Check(len(*requests), gc.Equals, len(responses))
@@ -843,9 +844,7 @@
843844
844func (s *environSuite) TestStopInstancesWithZeroInstance(c *gc.C) {845func (s *environSuite) TestStopInstancesWithZeroInstance(c *gc.C) {
845 env := makeEnviron(c)846 env := makeEnviron(c)
846 instances := []instance.Instance{}847 err := env.StopInstances()
847
848 err := env.StopInstances(instances)
849 c.Check(err, gc.IsNil)848 c.Check(err, gc.IsNil)
850}849}
851850
852851
=== modified file 'provider/common/bootstrap.go'
--- provider/common/bootstrap.go 2014-04-30 23:18:40 +0000
+++ provider/common/bootstrap.go 2014-05-14 03:01:15 +0000
@@ -128,7 +128,7 @@
128128
129 if inst != nil {129 if inst != nil {
130 fmt.Fprintln(ctx.GetStderr(), "Stopping instance...")130 fmt.Fprintln(ctx.GetStderr(), "Stopping instance...")
131 if stoperr := env.StopInstances([]instance.Instance{inst}); stoperr != nil {131 if stoperr := env.StopInstances(inst.Id()); stoperr != nil {
132 logger.Errorf("cannot stop failed bootstrap instance %q: %v", inst.Id(), stoperr)132 logger.Errorf("cannot stop failed bootstrap instance %q: %v", inst.Id(), stoperr)
133 } else {133 } else {
134 // set to nil so we know we can safely delete the state file134 // set to nil so we know we can safely delete the state file
135135
=== modified file 'provider/common/bootstrap_test.go'
--- provider/common/bootstrap_test.go 2014-04-24 08:15:45 +0000
+++ provider/common/bootstrap_test.go 2014-05-14 03:01:15 +0000
@@ -117,9 +117,9 @@
117 return &mockInstance{id: "i-blah"}, nil, nil, nil117 return &mockInstance{id: "i-blah"}, nil, nil, nil
118 }118 }
119119
120 var stopped []instance.Instance120 var stopped []instance.Id
121 stopInstances := func(instances []instance.Instance) error {121 stopInstances := func(ids []instance.Id) error {
122 stopped = append(stopped, instances...)122 stopped = append(stopped, ids...)
123 return nil123 return nil
124 }124 }
125125
@@ -134,7 +134,7 @@
134 err := common.Bootstrap(ctx, env, environs.BootstrapParams{})134 err := common.Bootstrap(ctx, env, environs.BootstrapParams{})
135 c.Assert(err, gc.ErrorMatches, "cannot save state: suddenly a wild blah")135 c.Assert(err, gc.ErrorMatches, "cannot save state: suddenly a wild blah")
136 c.Assert(stopped, gc.HasLen, 1)136 c.Assert(stopped, gc.HasLen, 1)
137 c.Assert(stopped[0].Id(), gc.Equals, instance.Id("i-blah"))137 c.Assert(stopped[0], gc.Equals, instance.Id("i-blah"))
138}138}
139139
140func (s *BootstrapSuite) TestCannotRecordThenCannotStop(c *gc.C) {140func (s *BootstrapSuite) TestCannotRecordThenCannotStop(c *gc.C) {
@@ -150,8 +150,8 @@
150 return &mockInstance{id: "i-blah"}, nil, nil, nil150 return &mockInstance{id: "i-blah"}, nil, nil, nil
151 }151 }
152152
153 var stopped []instance.Instance153 var stopped []instance.Id
154 stopInstances := func(instances []instance.Instance) error {154 stopInstances := func(instances []instance.Id) error {
155 stopped = append(stopped, instances...)155 stopped = append(stopped, instances...)
156 return fmt.Errorf("bork bork borken")156 return fmt.Errorf("bork bork borken")
157 }157 }
@@ -171,7 +171,7 @@
171 err := common.Bootstrap(ctx, env, environs.BootstrapParams{})171 err := common.Bootstrap(ctx, env, environs.BootstrapParams{})
172 c.Assert(err, gc.ErrorMatches, "cannot save state: suddenly a wild blah")172 c.Assert(err, gc.ErrorMatches, "cannot save state: suddenly a wild blah")
173 c.Assert(stopped, gc.HasLen, 1)173 c.Assert(stopped, gc.HasLen, 1)
174 c.Assert(stopped[0].Id(), gc.Equals, instance.Id("i-blah"))174 c.Assert(stopped[0], gc.Equals, instance.Id("i-blah"))
175 c.Assert(tw.Log, jc.LogMatches, []jc.SimpleMessage{{175 c.Assert(tw.Log, jc.LogMatches, []jc.SimpleMessage{{
176 loggo.ERROR, `cannot stop failed bootstrap instance "i-blah": bork bork borken`,176 loggo.ERROR, `cannot stop failed bootstrap instance "i-blah": bork bork borken`,
177 }})177 }})
178178
=== modified file 'provider/common/destroy.go'
--- provider/common/destroy.go 2014-03-14 20:38:20 +0000
+++ provider/common/destroy.go 2014-05-14 03:01:15 +0000
@@ -5,6 +5,7 @@
55
6import (6import (
7 "launchpad.net/juju-core/environs"7 "launchpad.net/juju-core/environs"
8 "launchpad.net/juju-core/instance"
8)9)
910
10// Destroy is a common implementation of the Destroy method defined on11// Destroy is a common implementation of the Destroy method defined on
@@ -15,7 +16,11 @@
15 instances, err := env.AllInstances()16 instances, err := env.AllInstances()
16 switch err {17 switch err {
17 case nil:18 case nil:
18 if err := env.StopInstances(instances); err != nil {19 ids := make([]instance.Id, len(instances))
20 for i, inst := range instances {
21 ids[i] = inst.Id()
22 }
23 if err := env.StopInstances(ids...); err != nil {
19 return err24 return err
20 }25 }
21 fallthrough26 fallthrough
2227
=== modified file 'provider/common/destroy_test.go'
--- provider/common/destroy_test.go 2014-04-14 12:36:13 +0000
+++ provider/common/destroy_test.go 2014-05-14 03:01:15 +0000
@@ -41,10 +41,10 @@
41 &mockInstance{id: "another"},41 &mockInstance{id: "another"},
42 }, nil42 }, nil
43 },43 },
44 stopInstances: func(instances []instance.Instance) error {44 stopInstances: func(ids []instance.Id) error {
45 c.Assert(instances, gc.HasLen, 2)45 c.Assert(ids, gc.HasLen, 2)
46 c.Assert(instances[0].Id(), gc.Equals, instance.Id("one"))46 c.Assert(ids[0], gc.Equals, instance.Id("one"))
47 c.Assert(instances[1].Id(), gc.Equals, instance.Id("another"))47 c.Assert(ids[1], gc.Equals, instance.Id("another"))
48 return fmt.Errorf("nah")48 return fmt.Errorf("nah")
49 },49 },
50 }50 }
@@ -61,10 +61,10 @@
61 &mockInstance{id: "another"},61 &mockInstance{id: "another"},
62 }, nil62 }, nil
63 },63 },
64 stopInstances: func(instances []instance.Instance) error {64 stopInstances: func(ids []instance.Id) error {
65 c.Assert(instances, gc.HasLen, 2)65 c.Assert(ids, gc.HasLen, 2)
66 c.Assert(instances[0].Id(), gc.Equals, instance.Id("one"))66 c.Assert(ids[0], gc.Equals, instance.Id("one"))
67 c.Assert(instances[1].Id(), gc.Equals, instance.Id("another"))67 c.Assert(ids[1], gc.Equals, instance.Id("another"))
68 return nil68 return nil
69 },69 },
70 }70 }
@@ -84,9 +84,9 @@
84 &mockInstance{id: "one"},84 &mockInstance{id: "one"},
85 }, nil85 }, nil
86 },86 },
87 stopInstances: func(instances []instance.Instance) error {87 stopInstances: func(ids []instance.Id) error {
88 c.Assert(instances, gc.HasLen, 1)88 c.Assert(ids, gc.HasLen, 1)
89 c.Assert(instances[0].Id(), gc.Equals, instance.Id("one"))89 c.Assert(ids[0], gc.Equals, instance.Id("one"))
90 return nil90 return nil
91 },91 },
92 }92 }
9393
=== modified file 'provider/common/mock_test.go'
--- provider/common/mock_test.go 2014-04-24 02:27:38 +0000
+++ provider/common/mock_test.go 2014-05-14 03:01:15 +0000
@@ -19,7 +19,7 @@
1919
20type allInstancesFunc func() ([]instance.Instance, error)20type allInstancesFunc func() ([]instance.Instance, error)
21type startInstanceFunc func(string, constraints.Value, []string, []string, tools.List, *cloudinit.MachineConfig) (instance.Instance, *instance.HardwareCharacteristics, []network.Info, error)21type startInstanceFunc func(string, constraints.Value, []string, []string, tools.List, *cloudinit.MachineConfig) (instance.Instance, *instance.HardwareCharacteristics, []network.Info, error)
22type stopInstancesFunc func([]instance.Instance) error22type stopInstancesFunc func([]instance.Id) error
23type getToolsSourcesFunc func() ([]simplestreams.DataSource, error)23type getToolsSourcesFunc func() ([]simplestreams.DataSource, error)
24type configFunc func() *config.Config24type configFunc func() *config.Config
25type setConfigFunc func(*config.Config) error25type setConfigFunc func(*config.Config) error
@@ -60,8 +60,8 @@
60 args.MachineConfig)60 args.MachineConfig)
61}61}
6262
63func (env *mockEnviron) StopInstances(instances []instance.Instance) error {63func (env *mockEnviron) StopInstances(ids ...instance.Id) error {
64 return env.stopInstances(instances)64 return env.stopInstances(ids)
65}65}
6666
67func (env *mockEnviron) Config() *config.Config {67func (env *mockEnviron) Config() *config.Config {
6868
=== modified file 'provider/dummy/environs.go'
--- provider/dummy/environs.go 2014-05-06 16:08:51 +0000
+++ provider/dummy/environs.go 2014-05-14 03:01:15 +0000
@@ -130,8 +130,8 @@
130}130}
131131
132type OpStopInstances struct {132type OpStopInstances struct {
133 Env string133 Env string
134 Instances []instance.Instance134 Ids []instance.Id
135}135}
136136
137type OpOpenPorts struct {137type OpOpenPorts struct {
@@ -829,7 +829,7 @@
829 return i, hc, networkInfo, nil829 return i, hc, networkInfo, nil
830}830}
831831
832func (e *environ) StopInstances(is []instance.Instance) error {832func (e *environ) StopInstances(ids ...instance.Id) error {
833 defer delay()833 defer delay()
834 if err := e.checkBroken("StopInstance"); err != nil {834 if err := e.checkBroken("StopInstance"); err != nil {
835 return err835 return err
@@ -840,12 +840,12 @@
840 }840 }
841 estate.mu.Lock()841 estate.mu.Lock()
842 defer estate.mu.Unlock()842 defer estate.mu.Unlock()
843 for _, i := range is {843 for _, id := range ids {
844 delete(estate.insts, i.(*dummyInstance).id)844 delete(estate.insts, id)
845 }845 }
846 estate.ops <- OpStopInstances{846 estate.ops <- OpStopInstances{
847 Env: e.name,847 Env: e.name,
848 Instances: is,848 Ids: ids,
849 }849 }
850 return nil850 return nil
851}851}
852852
=== modified file 'provider/ec2/ec2.go'
--- provider/ec2/ec2.go 2014-05-06 16:08:51 +0000
+++ provider/ec2/ec2.go 2014-05-14 03:01:15 +0000
@@ -537,11 +537,7 @@
537 return inst, &hc, nil, nil537 return inst, &hc, nil, nil
538}538}
539539
540func (e *environ) StopInstances(insts []instance.Instance) error {540func (e *environ) StopInstances(ids ...instance.Id) error {
541 ids := make([]instance.Id, len(insts))
542 for i, inst := range insts {
543 ids[i] = inst.(*ec2Instance).Id()
544 }
545 return e.terminateInstances(ids)541 return e.terminateInstances(ids)
546}542}
547543
548544
=== modified file 'provider/ec2/live_test.go'
--- provider/ec2/live_test.go 2014-04-09 16:36:12 +0000
+++ provider/ec2/live_test.go 2014-05-14 03:01:15 +0000
@@ -116,7 +116,7 @@
116116
117func (t *LiveTests) TestInstanceAttributes(c *gc.C) {117func (t *LiveTests) TestInstanceAttributes(c *gc.C) {
118 inst, hc := testing.AssertStartInstance(c, t.Env, "30")118 inst, hc := testing.AssertStartInstance(c, t.Env, "30")
119 defer t.Env.StopInstances([]instance.Instance{inst})119 defer t.Env.StopInstances(inst.Id())
120 // Sanity check for hardware characteristics.120 // Sanity check for hardware characteristics.
121 c.Assert(hc.Arch, gc.NotNil)121 c.Assert(hc.Arch, gc.NotNil)
122 c.Assert(hc.Mem, gc.NotNil)122 c.Assert(hc.Mem, gc.NotNil)
@@ -140,7 +140,7 @@
140func (t *LiveTests) TestStartInstanceConstraints(c *gc.C) {140func (t *LiveTests) TestStartInstanceConstraints(c *gc.C) {
141 cons := constraints.MustParse("mem=2G")141 cons := constraints.MustParse("mem=2G")
142 inst, hc := testing.AssertStartInstanceWithConstraints(c, t.Env, "30", cons)142 inst, hc := testing.AssertStartInstanceWithConstraints(c, t.Env, "30", cons)
143 defer t.Env.StopInstances([]instance.Instance{inst})143 defer t.Env.StopInstances(inst.Id())
144 ec2inst := ec2.InstanceEC2(inst)144 ec2inst := ec2.InstanceEC2(inst)
145 c.Assert(ec2inst.InstanceType, gc.Equals, "m1.medium")145 c.Assert(ec2inst.InstanceType, gc.Equals, "m1.medium")
146 c.Assert(*hc.Arch, gc.Equals, "amd64")146 c.Assert(*hc.Arch, gc.Equals, "amd64")
@@ -187,14 +187,14 @@
187 c.Assert(err, gc.IsNil)187 c.Assert(err, gc.IsNil)
188188
189 inst0, _ := testing.AssertStartInstance(c, t.Env, "98")189 inst0, _ := testing.AssertStartInstance(c, t.Env, "98")
190 defer t.Env.StopInstances([]instance.Instance{inst0})190 defer t.Env.StopInstances(inst0.Id())
191191
192 // Create a same-named group for the second instance192 // Create a same-named group for the second instance
193 // before starting it, to check that it's reused correctly.193 // before starting it, to check that it's reused correctly.
194 oldMachineGroup := createGroup(c, ec2conn, groups[2].Name, "old machine group")194 oldMachineGroup := createGroup(c, ec2conn, groups[2].Name, "old machine group")
195195
196 inst1, _ := testing.AssertStartInstance(c, t.Env, "99")196 inst1, _ := testing.AssertStartInstance(c, t.Env, "99")
197 defer t.Env.StopInstances([]instance.Instance{inst1})197 defer t.Env.StopInstances(inst1.Id())
198198
199 groupsResp, err := ec2conn.SecurityGroups(groups, nil)199 groupsResp, err := ec2conn.SecurityGroups(groups, nil)
200 c.Assert(err, gc.IsNil)200 c.Assert(err, gc.IsNil)
@@ -343,7 +343,7 @@
343 inst1 := ec2.FabricateInstance(inst0, "i-aaaaaaaa")343 inst1 := ec2.FabricateInstance(inst0, "i-aaaaaaaa")
344 inst2, _ := testing.AssertStartInstance(c, t.Env, "41")344 inst2, _ := testing.AssertStartInstance(c, t.Env, "41")
345345
346 err := t.Env.StopInstances([]instance.Instance{inst0, inst1, inst2})346 err := t.Env.StopInstances(inst0.Id(), inst1.Id(), inst2.Id())
347 c.Check(err, gc.IsNil)347 c.Check(err, gc.IsNil)
348348
349 var insts []instance.Instance349 var insts []instance.Instance
350350
=== modified file 'provider/joyent/environ_instance.go'
--- provider/joyent/environ_instance.go 2014-05-06 16:08:51 +0000
+++ provider/joyent/environ_instance.go 2014-05-14 03:01:15 +0000
@@ -232,17 +232,17 @@
232 return instance.Address{}, errors.NotImplementedf("AllocateAddress")232 return instance.Address{}, errors.NotImplementedf("AllocateAddress")
233}233}
234234
235func (env *joyentEnviron) StopInstances(instances []instance.Instance) error {235func (env *joyentEnviron) StopInstances(ids ...instance.Id) error {
236 // Remove all the instances in parallel so that we incur less round-trips.236 // Remove all the instances in parallel so that we incur less round-trips.
237 var wg sync.WaitGroup237 var wg sync.WaitGroup
238 //var err error238 //var err error
239 wg.Add(len(instances))239 wg.Add(len(ids))
240 errc := make(chan error, len(instances))240 errc := make(chan error, len(ids))
241 for _, inst := range instances {241 for _, id := range ids {
242 inst := inst.(*joyentInstance)242 id := id // copy to new free var for closure
243 go func() {243 go func() {
244 defer wg.Done()244 defer wg.Done()
245 if err := inst.Stop(); err != nil {245 if err := env.stopInstance(string(id)); err != nil {
246 errc <- err246 errc <- err
247 }247 }
248 }()248 }()
@@ -257,6 +257,39 @@
257 return nil257 return nil
258}258}
259259
260func (env *joyentEnviron) stopInstance(id string) error {
261 // wait for machine to be running
262 // if machine is still provisioning stop will fail
263 for !env.pollMachineState(id, "running") {
264 time.Sleep(1 * time.Second)
265 }
266
267 err := env.compute.cloudapi.StopMachine(id)
268 if err != nil {
269 return fmt.Errorf("cannot stop instance %s: %v", id, err)
270 }
271
272 // wait for machine to be stopped
273 for !env.pollMachineState(id, "stopped") {
274 time.Sleep(1 * time.Second)
275 }
276
277 err = env.compute.cloudapi.DeleteMachine(id)
278 if err != nil {
279 return fmt.Errorf("cannot delete instance %s: %v", id, err)
280 }
281
282 return nil
283}
284
285func (env *joyentEnviron) pollMachineState(machineId, state string) bool {
286 machineConfig, err := env.compute.cloudapi.GetMachine(machineId)
287 if err != nil {
288 return false
289 }
290 return strings.EqualFold(machineConfig.State, state)
291}
292
260func (env *joyentEnviron) listInstanceTypes() ([]instances.InstanceType, error) {293func (env *joyentEnviron) listInstanceTypes() ([]instances.InstanceType, error) {
261 packages, err := env.compute.cloudapi.ListPackages(nil)294 packages, err := env.compute.cloudapi.ListPackages(nil)
262 if err != nil {295 if err != nil {
263296
=== modified file 'provider/joyent/instance.go'
--- provider/joyent/instance.go 2014-04-02 11:35:49 +0000
+++ provider/joyent/instance.go 2014-05-14 03:01:15 +0000
@@ -4,10 +4,6 @@
4package joyent4package joyent
55
6import (6import (
7 "fmt"
8 "strings"
9 "time"
10
11 "github.com/joyent/gosdc/cloudapi"7 "github.com/joyent/gosdc/cloudapi"
128
13 "launchpad.net/juju-core/instance"9 "launchpad.net/juju-core/instance"
@@ -63,40 +59,3 @@
63func (inst *joyentInstance) WaitDNSName() (string, error) {59func (inst *joyentInstance) WaitDNSName() (string, error) {
64 return common.WaitDNSName(inst)60 return common.WaitDNSName(inst)
65}61}
66
67// Stop will stop and delete the machine
68// Stopped machines are still billed for in the Joyent Public Cloud
69func (inst *joyentInstance) Stop() error {
70 id := string(inst.Id())
71
72 // wait for machine to be running
73 // if machine is still provisioning stop will fail
74 for !inst.pollMachineState(id, "running") {
75 time.Sleep(1 * time.Second)
76 }
77
78 err := inst.env.compute.cloudapi.StopMachine(id)
79 if err != nil {
80 return fmt.Errorf("cannot stop instance %s: %v", id, err)
81 }
82
83 // wait for machine to be stopped
84 for !inst.pollMachineState(id, "stopped") {
85 time.Sleep(1 * time.Second)
86 }
87
88 err = inst.env.compute.cloudapi.DeleteMachine(id)
89 if err != nil {
90 return fmt.Errorf("cannot delete instance %s: %v", id, err)
91 }
92
93 return nil
94}
95
96func (inst *joyentInstance) pollMachineState(machineId, state string) bool {
97 machineConfig, err := inst.env.compute.cloudapi.GetMachine(machineId)
98 if err != nil {
99 return false
100 }
101 return strings.EqualFold(machineConfig.State, state)
102}
10362
=== modified file 'provider/joyent/local_test.go'
--- provider/joyent/local_test.go 2014-04-24 12:33:19 +0000
+++ provider/joyent/local_test.go 2014-05-14 03:01:15 +0000
@@ -193,7 +193,7 @@
193 err := bootstrap.Bootstrap(bootstrapContext(c), env, environs.BootstrapParams{})193 err := bootstrap.Bootstrap(bootstrapContext(c), env, environs.BootstrapParams{})
194 c.Assert(err, gc.IsNil)194 c.Assert(err, gc.IsNil)
195 inst, _ := testing.AssertStartInstance(c, env, "100")195 inst, _ := testing.AssertStartInstance(c, env, "100")
196 err = env.StopInstances([]instance.Instance{inst})196 err = env.StopInstances(inst.Id())
197 c.Assert(err, gc.IsNil)197 c.Assert(err, gc.IsNil)
198}198}
199199
@@ -261,7 +261,7 @@
261 envtesting.UploadFakeTools(c, env.Storage())261 envtesting.UploadFakeTools(c, env.Storage())
262 inst, _ := testing.AssertStartInstance(c, env, "100")262 inst, _ := testing.AssertStartInstance(c, env, "100")
263 c.Assert(inst.Status(), gc.Equals, "running")263 c.Assert(inst.Status(), gc.Equals, "running")
264 err := env.StopInstances([]instance.Instance{inst})264 err := env.StopInstances(inst.Id())
265 c.Assert(err, gc.IsNil)265 c.Assert(err, gc.IsNil)
266}266}
267267
@@ -274,7 +274,7 @@
274 id1 := inst1.Id()274 id1 := inst1.Id()
275 c.Logf("id0: %s, id1: %s", id0, id1)275 c.Logf("id0: %s, id1: %s", id0, id1)
276 defer func() {276 defer func() {
277 err := env.StopInstances([]instance.Instance{inst0, inst1})277 err := env.StopInstances(inst0.Id(), inst1.Id())
278 c.Assert(err, gc.IsNil)278 c.Assert(err, gc.IsNil)
279 }()279 }()
280280
281281
=== modified file 'provider/local/environ.go'
--- provider/local/environ.go 2014-05-06 16:08:51 +0000
+++ provider/local/environ.go 2014-05-14 03:01:15 +0000
@@ -354,12 +354,12 @@
354}354}
355355
356// StopInstances is specified in the InstanceBroker interface.356// StopInstances is specified in the InstanceBroker interface.
357func (env *localEnviron) StopInstances(instances []instance.Instance) error {357func (env *localEnviron) StopInstances(ids ...instance.Id) error {
358 for _, inst := range instances {358 for _, id := range ids {
359 if inst.Id() == bootstrapInstanceId {359 if id == bootstrapInstanceId {
360 return fmt.Errorf("cannot stop the bootstrap instance")360 return fmt.Errorf("cannot stop the bootstrap instance")
361 }361 }
362 if err := env.containerManager.DestroyContainer(inst); err != nil {362 if err := env.containerManager.DestroyContainer(id); err != nil {
363 return err363 return err
364 }364 }
365 }365 }
@@ -462,7 +462,7 @@
462 return err462 return err
463 }463 }
464 for _, inst := range containers {464 for _, inst := range containers {
465 if err := env.containerManager.DestroyContainer(inst); err != nil {465 if err := env.containerManager.DestroyContainer(inst.Id()); err != nil {
466 return err466 return err
467 }467 }
468 }468 }
469469
=== modified file 'provider/maas/environ.go'
--- provider/maas/environ.go 2014-05-07 19:09:23 +0000
+++ provider/maas/environ.go 2014-05-14 03:01:15 +0000
@@ -434,7 +434,7 @@
434 }434 }
435 defer func() {435 defer func() {
436 if err != nil {436 if err != nil {
437 if err := environ.releaseInstance(inst); err != nil {437 if err := environ.StopInstances(inst.Id()); err != nil {
438 logger.Errorf("error releasing failed instance: %v", err)438 logger.Errorf("error releasing failed instance: %v", err)
439 }439 }
440 }440 }
@@ -546,32 +546,18 @@
546}546}
547547
548// StopInstances is specified in the InstanceBroker interface.548// StopInstances is specified in the InstanceBroker interface.
549func (environ *maasEnviron) StopInstances(instances []instance.Instance) error {549func (environ *maasEnviron) StopInstances(ids ...instance.Id) error {
550 // Shortcut to exit quickly if 'instances' is an empty slice or nil.550 // Shortcut to exit quickly if 'instances' is an empty slice or nil.
551 if len(instances) == 0 {551 if len(ids) == 0 {
552 return nil552 return nil
553 }553 }
554 // Tell MAAS to release each of the instances. If there are errors,554 // TODO(axw) 2014-05-13 #1319016
555 // return only the first one (but release all instances regardless).555 // Nodes that have been removed out of band will cause
556 // Note that releasing instances also turns them off.556 // the release call to fail. We should parse the error
557 var firstErr error557 // returned from MAAS and retry, or otherwise request
558 for _, instance := range instances {558 // an enhancement to MAAS to ignore unknown node IDs.
559 err := environ.releaseInstance(instance)559 nodes := environ.getMAASClient().GetSubObject("nodes")
560 if firstErr == nil {560 _, err := nodes.CallPost("release", getSystemIdValues("nodes", ids))
561 firstErr = err
562 }
563 }
564 return firstErr
565}
566
567// releaseInstance releases a single instance.
568func (environ *maasEnviron) releaseInstance(inst instance.Instance) error {
569 maasInst := inst.(*maasInstance)
570 maasObj := maasInst.maasObject
571 _, err := maasObj.CallPost("release", nil)
572 if err != nil {
573 logger.Debugf("error releasing instance %v", maasInst)
574 }
575 return err561 return err
576}562}
577563
@@ -580,7 +566,7 @@
580// "ids" matches all instances (not none as you might expect).566// "ids" matches all instances (not none as you might expect).
581func (environ *maasEnviron) instances(ids []instance.Id) ([]instance.Instance, error) {567func (environ *maasEnviron) instances(ids []instance.Id) ([]instance.Instance, error) {
582 nodeListing := environ.getMAASClient().GetSubObject("nodes")568 nodeListing := environ.getMAASClient().GetSubObject("nodes")
583 filter := getSystemIdValues(ids)569 filter := getSystemIdValues("id", ids)
584 filter.Add("agent_name", environ.ecfg().maasAgentName())570 filter.Add("agent_name", environ.ecfg().maasAgentName())
585 listNodeObjects, err := nodeListing.CallGet("list", filter)571 listNodeObjects, err := nodeListing.CallGet("list", filter)
586 if err != nil {572 if err != nil {
587573
=== modified file 'provider/maas/environ_whitebox_test.go'
--- provider/maas/environ_whitebox_test.go 2014-04-24 12:33:19 +0000
+++ provider/maas/environ_whitebox_test.go 2014-05-14 03:01:15 +0000
@@ -420,24 +420,27 @@
420func (suite *environSuite) TestStopInstancesReturnsIfParameterEmpty(c *gc.C) {420func (suite *environSuite) TestStopInstancesReturnsIfParameterEmpty(c *gc.C) {
421 suite.getInstance("test1")421 suite.getInstance("test1")
422422
423 err := suite.makeEnviron().StopInstances([]instance.Instance{})423 err := suite.makeEnviron().StopInstances()
424 c.Check(err, gc.IsNil)424 c.Check(err, gc.IsNil)
425 operations := suite.testMAASObject.TestServer.NodeOperations()425 operations := suite.testMAASObject.TestServer.NodeOperations()
426 c.Check(operations, gc.DeepEquals, map[string][]string{})426 c.Check(operations, gc.DeepEquals, map[string][]string{})
427}427}
428428
429func (suite *environSuite) TestStopInstancesStopsAndReleasesInstances(c *gc.C) {429func (suite *environSuite) TestStopInstancesStopsAndReleasesInstances(c *gc.C) {
430 instance1 := suite.getInstance("test1")430 suite.getInstance("test1")
431 instance2 := suite.getInstance("test2")431 suite.getInstance("test2")
432 suite.getInstance("test3")432 suite.getInstance("test3")
433 instances := []instance.Instance{instance1, instance2}433 // mark test1 and test2 as being allocated, but not test3.
434434 // The release operation will ignore test3.
435 err := suite.makeEnviron().StopInstances(instances)435 suite.testMAASObject.TestServer.OwnedNodes()["test1"] = true
436436 suite.testMAASObject.TestServer.OwnedNodes()["test2"] = true
437
438 err := suite.makeEnviron().StopInstances("test1", "test2", "test3")
437 c.Check(err, gc.IsNil)439 c.Check(err, gc.IsNil)
438 operations := suite.testMAASObject.TestServer.NodeOperations()440 operations := suite.testMAASObject.TestServer.NodesOperations()
439 expectedOperations := map[string][]string{"test1": {"release"}, "test2": {"release"}}441 c.Check(operations, gc.DeepEquals, []string{"release"})
440 c.Check(operations, gc.DeepEquals, expectedOperations)442 c.Assert(suite.testMAASObject.TestServer.OwnedNodes()["test1"], jc.IsFalse)
443 c.Assert(suite.testMAASObject.TestServer.OwnedNodes()["test2"], jc.IsFalse)
441}444}
442445
443func (suite *environSuite) TestStateInfo(c *gc.C) {446func (suite *environSuite) TestStateInfo(c *gc.C) {
@@ -472,6 +475,7 @@
472func (suite *environSuite) TestDestroy(c *gc.C) {475func (suite *environSuite) TestDestroy(c *gc.C) {
473 env := suite.makeEnviron()476 env := suite.makeEnviron()
474 suite.getInstance("test1")477 suite.getInstance("test1")
478 suite.testMAASObject.TestServer.OwnedNodes()["test1"] = true // simulate acquire
475 data := makeRandomBytes(10)479 data := makeRandomBytes(10)
476 suite.testMAASObject.TestServer.NewFile("filename", data)480 suite.testMAASObject.TestServer.NewFile("filename", data)
477 stor := env.Storage()481 stor := env.Storage()
@@ -480,9 +484,9 @@
480 c.Check(err, gc.IsNil)484 c.Check(err, gc.IsNil)
481485
482 // Instances have been stopped.486 // Instances have been stopped.
483 operations := suite.testMAASObject.TestServer.NodeOperations()487 operations := suite.testMAASObject.TestServer.NodesOperations()
484 expectedOperations := map[string][]string{"test1": {"release"}}488 c.Check(operations, gc.DeepEquals, []string{"release"})
485 c.Check(operations, gc.DeepEquals, expectedOperations)489 c.Check(suite.testMAASObject.TestServer.OwnedNodes()["test1"], jc.IsFalse)
486 // Files have been cleaned up.490 // Files have been cleaned up.
487 listing, err := storage.List(stor, "")491 listing, err := storage.List(stor, "")
488 c.Assert(err, gc.IsNil)492 c.Assert(err, gc.IsNil)
489493
=== modified file 'provider/maas/util.go'
--- provider/maas/util.go 2013-08-02 05:13:41 +0000
+++ provider/maas/util.go 2014-05-14 03:01:15 +0000
@@ -24,12 +24,12 @@
24}24}
2525
26// getSystemIdValues returns a url.Values object with all the 'system_ids'26// getSystemIdValues returns a url.Values object with all the 'system_ids'
27// from the given instanceIds stored under the key 'id'. This is used27// from the given instanceIds stored under the specified key. This is used
28// to filter out instances when listing the nodes objects.28// to filter out instances when listing the nodes objects.
29func getSystemIdValues(instanceIds []instance.Id) url.Values {29func getSystemIdValues(key string, instanceIds []instance.Id) url.Values {
30 values := url.Values{}30 values := url.Values{}
31 for _, instanceId := range instanceIds {31 for _, instanceId := range instanceIds {
32 values.Add("id", extractSystemId(instanceId))32 values.Add(key, extractSystemId(instanceId))
33 }33 }
34 return values34 return values
35}35}
3636
=== modified file 'provider/maas/util_test.go'
--- provider/maas/util_test.go 2013-08-09 04:58:34 +0000
+++ provider/maas/util_test.go 2014-05-14 03:01:15 +0000
@@ -30,7 +30,7 @@
30 instanceId2 := instance.Id("/MAAS/api/1.0/nodes/system_id2/")30 instanceId2 := instance.Id("/MAAS/api/1.0/nodes/system_id2/")
31 instanceIds := []instance.Id{instanceId1, instanceId2}31 instanceIds := []instance.Id{instanceId1, instanceId2}
3232
33 values := getSystemIdValues(instanceIds)33 values := getSystemIdValues("id", instanceIds)
3434
35 c.Check(values["id"], gc.DeepEquals, []string{"system_id1", "system_id2"})35 c.Check(values["id"], gc.DeepEquals, []string{"system_id1", "system_id2"})
36}36}
3737
=== modified file 'provider/manual/environ.go'
--- provider/manual/environ.go 2014-05-06 16:08:51 +0000
+++ provider/manual/environ.go 2014-05-14 03:01:15 +0000
@@ -70,7 +70,7 @@
70 return nil, nil, nil, errNoStartInstance70 return nil, nil, nil, errNoStartInstance
71}71}
7272
73func (*manualEnviron) StopInstances([]instance.Instance) error {73func (*manualEnviron) StopInstances(...instance.Id) error {
74 return errNoStopInstance74 return errNoStopInstance
75}75}
7676
7777
=== modified file 'provider/openstack/local_test.go'
--- provider/openstack/local_test.go 2014-04-24 12:33:19 +0000
+++ provider/openstack/local_test.go 2014-05-14 03:01:15 +0000
@@ -264,7 +264,7 @@
264 err = bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})264 err = bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
265 c.Assert(err, gc.IsNil)265 c.Assert(err, gc.IsNil)
266 inst, _ := testing.AssertStartInstance(c, env, "100")266 inst, _ := testing.AssertStartInstance(c, env, "100")
267 err = env.StopInstances([]instance.Instance{inst})267 err = env.StopInstances(inst.Id())
268 c.Assert(err, gc.IsNil)268 c.Assert(err, gc.IsNil)
269}269}
270270
@@ -288,7 +288,7 @@
288 env, err := environs.New(cfg)288 env, err := environs.New(cfg)
289 c.Assert(err, gc.IsNil)289 c.Assert(err, gc.IsNil)
290 inst, _ := testing.AssertStartInstance(c, env, "100")290 inst, _ := testing.AssertStartInstance(c, env, "100")
291 err = env.StopInstances([]instance.Instance{inst})291 err = env.StopInstances(inst.Id())
292 c.Assert(err, gc.IsNil)292 c.Assert(err, gc.IsNil)
293}293}
294294
@@ -363,7 +363,7 @@
363 // group, one group for the entire environment, and another for the363 // group, one group for the entire environment, and another for the
364 // new instance.364 // new instance.
365 assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)})365 assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)})
366 err = env.StopInstances([]instance.Instance{inst})366 err = env.StopInstances(inst.Id())
367 c.Assert(err, gc.IsNil)367 c.Assert(err, gc.IsNil)
368 // The security group for this instance is now removed.368 // The security group for this instance is now removed.
369 assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name())})369 assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name())})
@@ -391,7 +391,7 @@
391 inst, _ := testing.AssertStartInstance(c, env, instanceName)391 inst, _ := testing.AssertStartInstance(c, env, instanceName)
392 allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)}392 allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)}
393 assertSecurityGroups(c, env, allSecurityGroups)393 assertSecurityGroups(c, env, allSecurityGroups)
394 err = env.StopInstances([]instance.Instance{inst})394 err = env.StopInstances(inst.Id())
395 c.Assert(err, gc.IsNil)395 c.Assert(err, gc.IsNil)
396 assertSecurityGroups(c, env, allSecurityGroups)396 assertSecurityGroups(c, env, allSecurityGroups)
397}397}
@@ -478,7 +478,7 @@
478 // goose's test service always returns ACTIVE state.478 // goose's test service always returns ACTIVE state.
479 inst, _ := testing.AssertStartInstance(c, env, "100")479 inst, _ := testing.AssertStartInstance(c, env, "100")
480 c.Assert(inst.Status(), gc.Equals, nova.StatusActive)480 c.Assert(inst.Status(), gc.Equals, nova.StatusActive)
481 err := env.StopInstances([]instance.Instance{inst})481 err := env.StopInstances(inst.Id())
482 c.Assert(err, gc.IsNil)482 c.Assert(err, gc.IsNil)
483}483}
484484
@@ -489,7 +489,7 @@
489 inst1, _ := testing.AssertStartInstance(c, env, "101")489 inst1, _ := testing.AssertStartInstance(c, env, "101")
490 id1 := inst1.Id()490 id1 := inst1.Id()
491 defer func() {491 defer func() {
492 err := env.StopInstances([]instance.Instance{inst0, inst1})492 err := env.StopInstances(inst0.Id(), inst1.Id())
493 c.Assert(err, gc.IsNil)493 c.Assert(err, gc.IsNil)
494 }()494 }()
495495
@@ -534,7 +534,7 @@
534 defer cleanup()534 defer cleanup()
535 stateInst, _ := testing.AssertStartInstance(c, env, "100")535 stateInst, _ := testing.AssertStartInstance(c, env, "100")
536 defer func() {536 defer func() {
537 err := env.StopInstances([]instance.Instance{stateInst})537 err := env.StopInstances(stateInst.Id())
538 c.Assert(err, gc.IsNil)538 c.Assert(err, gc.IsNil)
539 }()539 }()
540 found := make(map[instance.Id]instance.Instance)540 found := make(map[instance.Id]instance.Instance)
@@ -559,7 +559,7 @@
559 defer cleanup()559 defer cleanup()
560 stateInst, _ := testing.AssertStartInstance(c, env, "100")560 stateInst, _ := testing.AssertStartInstance(c, env, "100")
561 defer func() {561 defer func() {
562 err := env.StopInstances([]instance.Instance{stateInst})562 err := env.StopInstances(stateInst.Id())
563 c.Assert(err, gc.IsNil)563 c.Assert(err, gc.IsNil)
564 }()564 }()
565565
566566
=== modified file 'provider/openstack/provider.go'
--- provider/openstack/provider.go 2014-05-06 16:08:51 +0000
+++ provider/openstack/provider.go 2014-05-14 03:01:15 +0000
@@ -6,7 +6,6 @@
6package openstack6package openstack
77
8import (8import (
9 "errors"
10 "fmt"9 "fmt"
11 "io/ioutil"10 "io/ioutil"
12 "net/http"11 "net/http"
@@ -902,28 +901,33 @@
902 return inst, inst.hardwareCharacteristics(), nil, nil901 return inst, inst.hardwareCharacteristics(), nil, nil
903}902}
904903
905func (e *environ) StopInstances(insts []instance.Instance) error {904func (e *environ) StopInstances(ids ...instance.Id) error {
906 ids := make([]instance.Id, len(insts))905 // If in instance firewall mode, gather the security group names.
907 securityGroupNames := make([]string, len(insts))906 var securityGroupNames []string
908 for i, inst := range insts {907 if e.Config().FirewallMode() == config.FwInstance {
909 instanceValue, ok := inst.(*openstackInstance)908 instances, err := e.Instances(ids)
910 if !ok {909 if err == environs.ErrNoInstances {
911 return errors.New("Incompatible instance.Instance supplied")910 return nil
912 }911 }
913 ids[i] = instanceValue.Id()912 securityGroupNames = make([]string, 0, len(ids))
914 openstackName := instanceValue.getServerDetail().Name913 for _, inst := range instances {
915 lastDashPos := strings.LastIndex(openstackName, "-")914 if inst == nil {
916 if lastDashPos == -1 {915 continue
917 return fmt.Errorf("cannot identify instance ID in openstack server name %q", openstackName)916 }
918 }917 openstackName := inst.(*openstackInstance).getServerDetail().Name
919 securityGroupNames[i] = e.machineGroupName(openstackName[lastDashPos+1:])918 lastDashPos := strings.LastIndex(openstackName, "-")
919 if lastDashPos == -1 {
920 return fmt.Errorf("cannot identify machine ID in openstack server name %q", openstackName)
921 }
922 securityGroupName := e.machineGroupName(openstackName[lastDashPos+1:])
923 securityGroupNames = append(securityGroupNames, securityGroupName)
924 }
920 }925 }
921 logger.Debugf("terminating instances %v", ids)926 logger.Debugf("terminating instances %v", ids)
922 err := e.terminateInstances(ids)927 if err := e.terminateInstances(ids); err != nil {
923 if err != nil {
924 return err928 return err
925 }929 }
926 if e.Config().FirewallMode() == config.FwInstance {930 if securityGroupNames != nil {
927 return e.deleteSecurityGroups(securityGroupNames)931 return e.deleteSecurityGroups(securityGroupNames)
928 }932 }
929 return nil933 return nil
930934
=== modified file 'state/apiserver/client/destroy.go'
--- state/apiserver/client/destroy.go 2014-01-17 16:41:32 +0000
+++ state/apiserver/client/destroy.go 2014-05-14 03:01:15 +0000
@@ -104,28 +104,7 @@
104 if err != nil {104 if err != nil {
105 return err105 return err
106 }106 }
107 // TODO(axw) 2013-12-12 #1260171107 return env.StopInstances(ids...)
108 // Modify InstanceBroker.StopInstances to take
109 // a slice of IDs rather than Instances.
110 instances, err := env.Instances(ids)
111 switch err {
112 case nil:
113 default:
114 return err
115 case environs.ErrNoInstances:
116 return nil
117 case environs.ErrPartialInstances:
118 var nonNilInstances []instance.Instance
119 for i, inst := range instances {
120 if inst == nil {
121 logger.Warningf("unknown instance ID: %v", ids[i])
122 continue
123 }
124 nonNilInstances = append(nonNilInstances, inst)
125 }
126 instances = nonNilInstances
127 }
128 return env.StopInstances(instances)
129}108}
130109
131// checkManualMachines checks if any of the machines in the slice were110// checkManualMachines checks if any of the machines in the slice were
132111
=== modified file 'worker/provisioner/kvm-broker.go'
--- worker/provisioner/kvm-broker.go 2014-05-09 13:24:50 +0000
+++ worker/provisioner/kvm-broker.go 2014-05-14 03:01:15 +0000
@@ -105,11 +105,11 @@
105}105}
106106
107// StopInstances shuts down the given instances.107// StopInstances shuts down the given instances.
108func (broker *kvmBroker) StopInstances(instances []instance.Instance) error {108func (broker *kvmBroker) StopInstances(ids ...instance.Id) error {
109 // TODO: potentially parallelise.109 // TODO: potentially parallelise.
110 for _, instance := range instances {110 for _, id := range ids {
111 kvmLogger.Infof("stopping kvm container for instance: %s", instance.Id())111 kvmLogger.Infof("stopping kvm container for instance: %s", id)
112 if err := broker.manager.DestroyContainer(instance); err != nil {112 if err := broker.manager.DestroyContainer(id); err != nil {
113 kvmLogger.Errorf("container did not stop: %v", err)113 kvmLogger.Errorf("container did not stop: %v", err)
114 return err114 return err
115 }115 }
116116
=== modified file 'worker/provisioner/kvm-broker_test.go'
--- worker/provisioner/kvm-broker_test.go 2014-05-09 13:24:50 +0000
+++ worker/provisioner/kvm-broker_test.go 2014-05-14 03:01:15 +0000
@@ -102,13 +102,13 @@
102 kvm1 := s.startInstance(c, "1/kvm/1")102 kvm1 := s.startInstance(c, "1/kvm/1")
103 kvm2 := s.startInstance(c, "1/kvm/2")103 kvm2 := s.startInstance(c, "1/kvm/2")
104104
105 err := s.broker.StopInstances([]instance.Instance{kvm0})105 err := s.broker.StopInstances(kvm0.Id())
106 c.Assert(err, gc.IsNil)106 c.Assert(err, gc.IsNil)
107 s.assertInstances(c, kvm1, kvm2)107 s.assertInstances(c, kvm1, kvm2)
108 c.Assert(s.kvmContainerDir(kvm0), jc.DoesNotExist)108 c.Assert(s.kvmContainerDir(kvm0), jc.DoesNotExist)
109 c.Assert(s.kvmRemovedContainerDir(kvm0), jc.IsDirectory)109 c.Assert(s.kvmRemovedContainerDir(kvm0), jc.IsDirectory)
110110
111 err = s.broker.StopInstances([]instance.Instance{kvm1, kvm2})111 err = s.broker.StopInstances(kvm1.Id(), kvm2.Id())
112 c.Assert(err, gc.IsNil)112 c.Assert(err, gc.IsNil)
113 s.assertInstances(c)113 s.assertInstances(c)
114}114}
@@ -118,7 +118,7 @@
118 kvm1 := s.startInstance(c, "1/kvm/1")118 kvm1 := s.startInstance(c, "1/kvm/1")
119 s.assertInstances(c, kvm0, kvm1)119 s.assertInstances(c, kvm0, kvm1)
120120
121 err := s.broker.StopInstances([]instance.Instance{kvm1})121 err := s.broker.StopInstances(kvm1.Id())
122 c.Assert(err, gc.IsNil)122 c.Assert(err, gc.IsNil)
123 kvm2 := s.startInstance(c, "1/kvm/2")123 kvm2 := s.startInstance(c, "1/kvm/2")
124 s.assertInstances(c, kvm0, kvm2)124 s.assertInstances(c, kvm0, kvm2)
125125
=== modified file 'worker/provisioner/lxc-broker.go'
--- worker/provisioner/lxc-broker.go 2014-05-09 13:24:50 +0000
+++ worker/provisioner/lxc-broker.go 2014-05-14 03:01:15 +0000
@@ -102,11 +102,11 @@
102}102}
103103
104// StopInstances shuts down the given instances.104// StopInstances shuts down the given instances.
105func (broker *lxcBroker) StopInstances(instances []instance.Instance) error {105func (broker *lxcBroker) StopInstances(ids ...instance.Id) error {
106 // TODO: potentially parallelise.106 // TODO: potentially parallelise.
107 for _, instance := range instances {107 for _, id := range ids {
108 lxcLogger.Infof("stopping lxc container for instance: %s", instance.Id())108 lxcLogger.Infof("stopping lxc container for instance: %s", id)
109 if err := broker.manager.DestroyContainer(instance); err != nil {109 if err := broker.manager.DestroyContainer(id); err != nil {
110 lxcLogger.Errorf("container did not stop: %v", err)110 lxcLogger.Errorf("container did not stop: %v", err)
111 return err111 return err
112 }112 }
113113
=== modified file 'worker/provisioner/lxc-broker_test.go'
--- worker/provisioner/lxc-broker_test.go 2014-05-09 13:24:50 +0000
+++ worker/provisioner/lxc-broker_test.go 2014-05-14 03:01:15 +0000
@@ -131,13 +131,13 @@
131 lxc1 := s.startInstance(c, "1/lxc/1")131 lxc1 := s.startInstance(c, "1/lxc/1")
132 lxc2 := s.startInstance(c, "1/lxc/2")132 lxc2 := s.startInstance(c, "1/lxc/2")
133133
134 err := s.broker.StopInstances([]instance.Instance{lxc0})134 err := s.broker.StopInstances(lxc0.Id())
135 c.Assert(err, gc.IsNil)135 c.Assert(err, gc.IsNil)
136 s.assertInstances(c, lxc1, lxc2)136 s.assertInstances(c, lxc1, lxc2)
137 c.Assert(s.lxcContainerDir(lxc0), jc.DoesNotExist)137 c.Assert(s.lxcContainerDir(lxc0), jc.DoesNotExist)
138 c.Assert(s.lxcRemovedContainerDir(lxc0), jc.IsDirectory)138 c.Assert(s.lxcRemovedContainerDir(lxc0), jc.IsDirectory)
139139
140 err = s.broker.StopInstances([]instance.Instance{lxc1, lxc2})140 err = s.broker.StopInstances(lxc1.Id(), lxc2.Id())
141 c.Assert(err, gc.IsNil)141 c.Assert(err, gc.IsNil)
142 s.assertInstances(c)142 s.assertInstances(c)
143}143}
@@ -147,7 +147,7 @@
147 lxc1 := s.startInstance(c, "1/lxc/1")147 lxc1 := s.startInstance(c, "1/lxc/1")
148 s.assertInstances(c, lxc0, lxc1)148 s.assertInstances(c, lxc0, lxc1)
149149
150 err := s.broker.StopInstances([]instance.Instance{lxc1})150 err := s.broker.StopInstances(lxc1.Id())
151 c.Assert(err, gc.IsNil)151 c.Assert(err, gc.IsNil)
152 lxc2 := s.startInstance(c, "1/lxc/2")152 lxc2 := s.startInstance(c, "1/lxc/2")
153 s.assertInstances(c, lxc0, lxc2)153 s.assertInstances(c, lxc0, lxc2)
154154
=== modified file 'worker/provisioner/provisioner_task.go'
--- worker/provisioner/provisioner_task.go 2014-05-08 06:58:42 +0000
+++ worker/provisioner/provisioner_task.go 2014-05-14 03:01:15 +0000
@@ -397,7 +397,11 @@
397 if len(instances) == 0 {397 if len(instances) == 0 {
398 return nil398 return nil
399 }399 }
400 if err := task.broker.StopInstances(instances); err != nil {400 ids := make([]instance.Id, len(instances))
401 for i, inst := range instances {
402 ids[i] = inst.Id()
403 }
404 if err := task.broker.StopInstances(ids...); err != nil {
401 logger.Errorf("broker failed to stop instances: %v", err)405 logger.Errorf("broker failed to stop instances: %v", err)
402 return err406 return err
403 }407 }
@@ -484,7 +488,7 @@
484 }488 }
485 // We need to stop the instance right away here, set error status and go on.489 // We need to stop the instance right away here, set error status and go on.
486 task.setErrorStatus("cannot register instance for machine %v: %v", machine, err)490 task.setErrorStatus("cannot register instance for machine %v: %v", machine, err)
487 if err := task.broker.StopInstances([]instance.Instance{inst}); err != nil {491 if err := task.broker.StopInstances(inst.Id()); err != nil {
488 // We cannot even stop the instance, log the error and quit.492 // We cannot even stop the instance, log the error and quit.
489 logger.Errorf("cannot stop instance %q for machine %v: %v", inst.Id(), machine, err)493 logger.Errorf("cannot stop instance %q for machine %v: %v", inst.Id(), machine, err)
490 return err494 return err
491495
=== modified file 'worker/provisioner/provisioner_test.go'
--- worker/provisioner/provisioner_test.go 2014-04-30 23:18:40 +0000
+++ worker/provisioner/provisioner_test.go 2014-05-14 03:01:15 +0000
@@ -254,8 +254,8 @@
254 case o := <-s.op:254 case o := <-s.op:
255 switch o := o.(type) {255 switch o := o.(type) {
256 case dummy.OpStopInstances:256 case dummy.OpStopInstances:
257 for _, stoppedInstance := range o.Instances {257 for _, id := range o.Ids {
258 instId := string(stoppedInstance.Id())258 instId := string(id)
259 instanceIdsToStop.Remove(instId)259 instanceIdsToStop.Remove(instId)
260 if instanceIdsToKeep.Contains(instId) {260 if instanceIdsToKeep.Contains(instId) {
261 c.Errorf("provisioner unexpectedly stopped instance %s", instId)261 c.Errorf("provisioner unexpectedly stopped instance %s", instId)

Subscribers

People subscribed via source and target branches

to status/vote changes: