Merge lp:~thumper/juju-core/kvm-provisioner into lp:~go-bot/juju-core/trunk

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 2118
Proposed branch: lp:~thumper/juju-core/kvm-provisioner
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 766 lines (+481/-54)
12 files modified
container/kvm/initialisation.go (+0/-1)
container/kvm/kvm.go (+6/-1)
container/kvm/kvm_test.go (+35/-10)
container/lxc/lxc_test.go (+14/-38)
container/testing/common.go (+43/-0)
environs/cloudinit/cloudinit.go (+1/-0)
provider/local/environ.go (+1/-1)
worker/provisioner/container_initialisation.go (+5/-1)
worker/provisioner/container_initialisation_test.go (+1/-1)
worker/provisioner/kvm-broker.go (+118/-0)
worker/provisioner/kvm-broker_test.go (+256/-0)
worker/provisioner/lxc-broker.go (+1/-1)
To merge this branch: bzr merge lp:~thumper/juju-core/kvm-provisioner
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+197629@code.launchpad.net

Commit message

Hook up the kvm broker and provisioner

This branch adds a kvm broker and hooks it up to the
existing container initialization.

The cpu-checker package is added to every machine to
allow kvm-ok to be executed.

A few drive by fixes related to kvm:
 * added a default bridge, used by the broker when
there is no other config.
 * the container wasn't removing the container dir
 * the kvm package isn't actually needed
 * the local provider was setting the wrong container
type (in the case of kvm)

https://codereview.appspot.com/36910043/

Description of the change

Hook up the kvm broker and provisioner

This branch adds a kvm broker and hooks it up to the
existing container initialization.

The cpu-checker package is added to every machine to
allow kvm-ok to be executed.

A few drive by fixes related to kvm:
 * added a default bridge, used by the broker when
there is no other config.
 * the container wasn't removing the container dir
 * the kvm package isn't actually needed
 * the local provider was setting the wrong container
type (in the case of kvm)

https://codereview.appspot.com/36910043/

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Reviewers: mp+197629_code.launchpad.net,

Message:
Please take a look.

Description:
Hook up the kvm broker and provisioner

This branch adds a kvm broker and hooks it up to the
existing container initialization.

The cpu-checker package is added to every machine to
allow kvm-ok to be executed.

A few drive by fixes related to kvm:
  * added a default bridge, used by the broker when
there is no other config.
  * the container wasn't removing the container dir
  * the kvm package isn't actually needed
  * the local provider was setting the wrong container
type (in the case of kvm)

https://code.launchpad.net/~thumper/juju-core/kvm-provisioner/+merge/197629

(do not edit description out of merge proposal)

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

Affected files (+388, -4 lines):
   A [revision details]
   M container/kvm/initialisation.go
   M container/kvm/kvm.go
   M environs/cloudinit/cloudinit.go
   M provider/local/environ.go
   M worker/provisioner/container_initialisation.go
   A worker/provisioner/kvm-broker.go
   A worker/provisioner/kvm-broker_test.go

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

LGTM
Please add a todo to factor out the common lxc and kvm code in the
respective brokers. And a todo to sort out the kvm bridge stuff. I know
there's a comment but a todo is better.

https://codereview.appspot.com/36910043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'container/kvm/initialisation.go'
2--- container/kvm/initialisation.go 2013-12-02 21:16:18 +0000
3+++ container/kvm/initialisation.go 2013-12-04 04:19:23 +0000
4@@ -14,7 +14,6 @@
5 var requiredPackages = []string{
6 "uvtool-libvirt",
7 "uvtool",
8- "kvm",
9 }
10
11 type containerInitialiser struct{}
12
13=== modified file 'container/kvm/kvm.go'
14--- container/kvm/kvm.go 2013-11-25 03:36:54 +0000
15+++ container/kvm/kvm.go 2013-12-04 04:19:23 +0000
16@@ -22,6 +22,7 @@
17 logger = loggo.GetLogger("juju.container.kvm")
18
19 KvmObjectFactory ContainerFactory = &containerFactory{}
20+ DefaultKvmBridge = "virbr0"
21 )
22
23 // IsKVMSupported calls into the kvm-ok executable from the cpu-checkers package.
24@@ -95,7 +96,11 @@
25 func (manager *containerManager) StopContainer(instance instance.Instance) error {
26 name := string(instance.Id())
27 kvmContainer := KvmObjectFactory.New(name)
28- return kvmContainer.Stop()
29+ if err := kvmContainer.Stop(); err != nil {
30+ logger.Errorf("failed to stop kvm container: %v", err)
31+ return err
32+ }
33+ return container.RemoveDirectory(name)
34 }
35
36 func (manager *containerManager) ListContainers() (result []instance.Instance, err error) {
37
38=== modified file 'container/kvm/kvm_test.go'
39--- container/kvm/kvm_test.go 2013-11-25 03:36:54 +0000
40+++ container/kvm/kvm_test.go 2013-12-04 04:19:23 +0000
41@@ -4,11 +4,14 @@
42 package kvm_test
43
44 import (
45+ "path/filepath"
46+
47 gc "launchpad.net/gocheck"
48
49 "launchpad.net/juju-core/container"
50 "launchpad.net/juju-core/container/kvm"
51 kvmtesting "launchpad.net/juju-core/container/kvm/testing"
52+ containertesting "launchpad.net/juju-core/container/testing"
53 "launchpad.net/juju-core/instance"
54 jc "launchpad.net/juju-core/testing/checkers"
55 "launchpad.net/juju-core/version"
56@@ -16,20 +19,26 @@
57
58 type KVMSuite struct {
59 kvmtesting.TestSuite
60+ manager container.Manager
61 }
62
63 var _ = gc.Suite(&KVMSuite{})
64
65+func (s *KVMSuite) SetUpTest(c *gc.C) {
66+ s.TestSuite.SetUpTest(c)
67+ var err error
68+ s.manager, err = kvm.NewContainerManager(container.ManagerConfig{Name: "test"})
69+ c.Assert(err, gc.IsNil)
70+}
71+
72 func (*KVMSuite) TestManagerNameNeeded(c *gc.C) {
73 manager, err := kvm.NewContainerManager(container.ManagerConfig{})
74 c.Assert(err, gc.ErrorMatches, "name is required")
75 c.Assert(manager, gc.IsNil)
76 }
77
78-func (*KVMSuite) TestListInitiallyEmpty(c *gc.C) {
79- manager, err := kvm.NewContainerManager(container.ManagerConfig{Name: "test"})
80- c.Assert(err, gc.IsNil)
81- containers, err := manager.ListContainers()
82+func (s *KVMSuite) TestListInitiallyEmpty(c *gc.C) {
83+ containers, err := s.manager.ListContainers()
84 c.Assert(err, gc.IsNil)
85 c.Assert(containers, gc.HasLen, 0)
86 }
87@@ -42,13 +51,11 @@
88 }
89
90 func (s *KVMSuite) TestListMatchesManagerName(c *gc.C) {
91- manager, err := kvm.NewContainerManager(container.ManagerConfig{Name: "test"})
92- c.Assert(err, gc.IsNil)
93 s.createRunningContainer(c, "test-match1")
94 s.createRunningContainer(c, "test-match2")
95 s.createRunningContainer(c, "testNoMatch")
96 s.createRunningContainer(c, "other")
97- containers, err := manager.ListContainers()
98+ containers, err := s.manager.ListContainers()
99 c.Assert(err, gc.IsNil)
100 c.Assert(containers, gc.HasLen, 2)
101 expectedIds := []instance.Id{"test-match1", "test-match2"}
102@@ -57,12 +64,30 @@
103 }
104
105 func (s *KVMSuite) TestListMatchesRunningContainers(c *gc.C) {
106- manager, err := kvm.NewContainerManager(container.ManagerConfig{Name: "test"})
107- c.Assert(err, gc.IsNil)
108 running := s.createRunningContainer(c, "test-running")
109 s.Factory.New("test-stopped")
110- containers, err := manager.ListContainers()
111+ containers, err := s.manager.ListContainers()
112 c.Assert(err, gc.IsNil)
113 c.Assert(containers, gc.HasLen, 1)
114 c.Assert(string(containers[0].Id()), gc.Equals, running.Name())
115 }
116+
117+func (s *KVMSuite) TestStartContainer(c *gc.C) {
118+ instance := containertesting.StartContainer(c, s.manager, "1/kvm/0")
119+ name := string(instance.Id())
120+ cloudInitFilename := filepath.Join(s.ContainerDir, name, "cloud-init")
121+ containertesting.AssertCloudInit(c, cloudInitFilename)
122+}
123+
124+func (s *KVMSuite) TestStopContainer(c *gc.C) {
125+ instance := containertesting.StartContainer(c, s.manager, "1/lxc/0")
126+
127+ err := s.manager.StopContainer(instance)
128+ c.Assert(err, gc.IsNil)
129+
130+ name := string(instance.Id())
131+ // Check that the container dir is no longer in the container dir
132+ c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist)
133+ // but instead, in the removed container dir
134+ c.Assert(filepath.Join(s.RemovedDir, name), jc.IsDirectory)
135+}
136
137=== modified file 'container/lxc/lxc_test.go'
138--- container/lxc/lxc_test.go 2013-12-01 23:54:35 +0000
139+++ container/lxc/lxc_test.go 2013-12-04 04:19:23 +0000
140@@ -18,14 +18,10 @@
141 "launchpad.net/juju-core/container"
142 "launchpad.net/juju-core/container/lxc"
143 lxctesting "launchpad.net/juju-core/container/lxc/testing"
144- "launchpad.net/juju-core/environs"
145- "launchpad.net/juju-core/instance"
146+ containertesting "launchpad.net/juju-core/container/testing"
147 instancetest "launchpad.net/juju-core/instance/testing"
148- jujutesting "launchpad.net/juju-core/juju/testing"
149 jc "launchpad.net/juju-core/testing/checkers"
150 "launchpad.net/juju-core/testing/testbase"
151- "launchpad.net/juju-core/tools"
152- "launchpad.net/juju-core/version"
153 )
154
155 func Test(t *stdtesting.T) {
156@@ -66,26 +62,9 @@
157 aptConfigScript = fmt.Sprintf("#!/bin/sh\n echo '%s\n%s'", configHttpProxy, configProxyExtra)
158 )
159
160-func StartContainer(c *gc.C, manager container.Manager, machineId string) instance.Instance {
161- stateInfo := jujutesting.FakeStateInfo(machineId)
162- apiInfo := jujutesting.FakeAPIInfo(machineId)
163- machineConfig := environs.NewMachineConfig(machineId, "fake-nonce", stateInfo, apiInfo)
164- machineConfig.SyslogPort = 2345
165- machineConfig.Tools = &tools.Tools{
166- Version: version.MustParseBinary("2.3.4-foo-bar"),
167- URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz",
168- }
169-
170- series := "series"
171- network := container.BridgeNetworkConfig("nic42")
172- inst, err := manager.StartContainer(machineConfig, series, network)
173- c.Assert(err, gc.IsNil)
174- return inst
175-}
176-
177 func (s *LxcSuite) TestStartContainer(c *gc.C) {
178 manager := lxc.NewContainerManager(container.ManagerConfig{})
179- instance := StartContainer(c, manager, "1/lxc/0")
180+ instance := containertesting.StartContainer(c, manager, "1/lxc/0")
181
182 name := string(instance.Id())
183 // Check our container config files.
184@@ -94,10 +73,7 @@
185 c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.link = nic42")
186
187 cloudInitFilename := filepath.Join(s.ContainerDir, name, "cloud-init")
188- c.Assert(cloudInitFilename, jc.IsNonEmptyFile)
189- data, err := ioutil.ReadFile(cloudInitFilename)
190- c.Assert(err, gc.IsNil)
191- c.Assert(string(data), jc.HasPrefix, "#cloud-config\n")
192+ data := containertesting.AssertCloudInit(c, cloudInitFilename)
193
194 x := make(map[interface{}]interface{})
195 err = goyaml.Unmarshal(data, &x)
196@@ -133,7 +109,7 @@
197
198 func (s *LxcSuite) TestContainerState(c *gc.C) {
199 manager := lxc.NewContainerManager(container.ManagerConfig{})
200- instance := StartContainer(c, manager, "1/lxc/0")
201+ instance := containertesting.StartContainer(c, manager, "1/lxc/0")
202
203 // The mock container will be immediately "running".
204 c.Assert(instance.Status(), gc.Equals, string(golxc.StateRunning))
205@@ -147,7 +123,7 @@
206
207 func (s *LxcSuite) TestStopContainer(c *gc.C) {
208 manager := lxc.NewContainerManager(container.ManagerConfig{})
209- instance := StartContainer(c, manager, "1/lxc/0")
210+ instance := containertesting.StartContainer(c, manager, "1/lxc/0")
211
212 err := manager.StopContainer(instance)
213 c.Assert(err, gc.IsNil)
214@@ -161,7 +137,7 @@
215
216 func (s *LxcSuite) TestStopContainerNameClash(c *gc.C) {
217 manager := lxc.NewContainerManager(container.ManagerConfig{})
218- instance := StartContainer(c, manager, "1/lxc/0")
219+ instance := containertesting.StartContainer(c, manager, "1/lxc/0")
220
221 name := string(instance.Id())
222 targetDir := filepath.Join(s.RemovedDir, name)
223@@ -179,7 +155,7 @@
224
225 func (s *LxcSuite) TestNamedManagerPrefix(c *gc.C) {
226 manager := lxc.NewContainerManager(container.ManagerConfig{Name: "eric"})
227- instance := StartContainer(c, manager, "1/lxc/0")
228+ instance := containertesting.StartContainer(c, manager, "1/lxc/0")
229 c.Assert(string(instance.Id()), gc.Equals, "eric-machine-1-lxc-0")
230 }
231
232@@ -187,12 +163,12 @@
233 foo := lxc.NewContainerManager(container.ManagerConfig{Name: "foo"})
234 bar := lxc.NewContainerManager(container.ManagerConfig{Name: "bar"})
235
236- foo1 := StartContainer(c, foo, "1/lxc/0")
237- foo2 := StartContainer(c, foo, "1/lxc/1")
238- foo3 := StartContainer(c, foo, "1/lxc/2")
239+ foo1 := containertesting.StartContainer(c, foo, "1/lxc/0")
240+ foo2 := containertesting.StartContainer(c, foo, "1/lxc/1")
241+ foo3 := containertesting.StartContainer(c, foo, "1/lxc/2")
242
243- bar1 := StartContainer(c, bar, "1/lxc/0")
244- bar2 := StartContainer(c, bar, "1/lxc/1")
245+ bar1 := containertesting.StartContainer(c, bar, "1/lxc/0")
246+ bar2 := containertesting.StartContainer(c, bar, "1/lxc/1")
247
248 result, err := foo.ListContainers()
249 c.Assert(err, gc.IsNil)
250@@ -205,14 +181,14 @@
251
252 func (s *LxcSuite) TestStartContainerAutostarts(c *gc.C) {
253 manager := lxc.NewContainerManager(container.ManagerConfig{})
254- instance := StartContainer(c, manager, "1/lxc/0")
255+ instance := containertesting.StartContainer(c, manager, "1/lxc/0")
256 autostartLink := lxc.RestartSymlink(string(instance.Id()))
257 c.Assert(autostartLink, jc.IsSymlink)
258 }
259
260 func (s *LxcSuite) TestStopContainerRemovesAutostartLink(c *gc.C) {
261 manager := lxc.NewContainerManager(container.ManagerConfig{})
262- instance := StartContainer(c, manager, "1/lxc/0")
263+ instance := containertesting.StartContainer(c, manager, "1/lxc/0")
264 err := manager.StopContainer(instance)
265 c.Assert(err, gc.IsNil)
266 autostartLink := lxc.RestartSymlink(string(instance.Id()))
267
268=== added directory 'container/testing'
269=== added file 'container/testing/common.go'
270--- container/testing/common.go 1970-01-01 00:00:00 +0000
271+++ container/testing/common.go 2013-12-04 04:19:23 +0000
272@@ -0,0 +1,43 @@
273+// Copyright 2013 Canonical Ltd.
274+// Licensed under the AGPLv3, see LICENCE file for details.
275+
276+package testing
277+
278+import (
279+ "io/ioutil"
280+
281+ gc "launchpad.net/gocheck"
282+
283+ "launchpad.net/juju-core/container"
284+ "launchpad.net/juju-core/environs"
285+ "launchpad.net/juju-core/instance"
286+ jujutesting "launchpad.net/juju-core/juju/testing"
287+ jc "launchpad.net/juju-core/testing/checkers"
288+ "launchpad.net/juju-core/tools"
289+ "launchpad.net/juju-core/version"
290+)
291+
292+func StartContainer(c *gc.C, manager container.Manager, machineId string) instance.Instance {
293+ stateInfo := jujutesting.FakeStateInfo(machineId)
294+ apiInfo := jujutesting.FakeAPIInfo(machineId)
295+ machineConfig := environs.NewMachineConfig(machineId, "fake-nonce", stateInfo, apiInfo)
296+ machineConfig.SyslogPort = 2345
297+ machineConfig.Tools = &tools.Tools{
298+ Version: version.MustParseBinary("2.3.4-foo-bar"),
299+ URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz",
300+ }
301+
302+ series := "series"
303+ network := container.BridgeNetworkConfig("nic42")
304+ inst, err := manager.StartContainer(machineConfig, series, network)
305+ c.Assert(err, gc.IsNil)
306+ return inst
307+}
308+
309+func AssertCloudInit(c *gc.C, filename string) []byte {
310+ c.Assert(filename, jc.IsNonEmptyFile)
311+ data, err := ioutil.ReadFile(filename)
312+ c.Assert(err, gc.IsNil)
313+ c.Assert(string(data), jc.HasPrefix, "#cloud-config\n")
314+ return data
315+}
316
317=== modified file 'environs/cloudinit/cloudinit.go'
318--- environs/cloudinit/cloudinit.go 2013-12-03 06:04:43 +0000
319+++ environs/cloudinit/cloudinit.go 2013-12-04 04:19:23 +0000
320@@ -187,6 +187,7 @@
321
322 // juju requires git for managing charm directories.
323 c.AddPackage("git")
324+ c.AddPackage("cpu-checker")
325
326 c.AddScripts(
327 "set -xe", // ensure we run all the scripts or abort.
328
329=== modified file 'provider/local/environ.go'
330--- provider/local/environ.go 2013-12-01 23:54:35 +0000
331+++ provider/local/environ.go 2013-12-04 04:19:23 +0000
332@@ -288,7 +288,7 @@
333 series := possibleTools.OneSeries()
334 logger.Debugf("StartInstance: %q, %s", machineConfig.MachineId, series)
335 machineConfig.Tools = possibleTools[0]
336- machineConfig.MachineContainerType = instance.LXC
337+ machineConfig.MachineContainerType = env.config.container()
338 logger.Debugf("tools: %#v", machineConfig.Tools)
339 network := container.BridgeNetworkConfig(env.config.networkBridge())
340 if err := environs.FinishMachineConfig(machineConfig, env.config.Config, cons); err != nil {
341
342=== modified file 'worker/provisioner/container_initialisation.go'
343--- worker/provisioner/container_initialisation.go 2013-11-25 05:03:44 +0000
344+++ worker/provisioner/container_initialisation.go 2013-12-04 04:19:23 +0000
345@@ -148,7 +148,11 @@
346 broker = NewLxcBroker(cs.provisioner, tools, cs.config)
347 case instance.KVM:
348 initialiser = kvm.NewContainerInitialiser()
349- //TODO - implement kvm broker
350+ broker, err = NewKvmBroker(cs.provisioner, tools, cs.config)
351+ if err != nil {
352+ logger.Errorf("failed to create new kvm broker")
353+ return nil, nil, err
354+ }
355 default:
356 return nil, nil, fmt.Errorf("unknown container type: %v", containerType)
357 }
358
359=== modified file 'worker/provisioner/container_initialisation_test.go'
360--- worker/provisioner/container_initialisation_test.go 2013-11-25 00:35:32 +0000
361+++ worker/provisioner/container_initialisation_test.go 2013-12-04 04:19:23 +0000
362@@ -187,7 +187,7 @@
363 packages []string
364 }{
365 {instance.LXC, []string{"lxc"}},
366- {instance.KVM, []string{"uvtool-libvirt", "uvtool", "kvm"}},
367+ {instance.KVM, []string{"uvtool-libvirt", "uvtool"}},
368 } {
369 s.assertContainerInitialised(c, test.ctype, test.packages)
370 }
371
372=== added file 'worker/provisioner/kvm-broker.go'
373--- worker/provisioner/kvm-broker.go 1970-01-01 00:00:00 +0000
374+++ worker/provisioner/kvm-broker.go 2013-12-04 04:19:23 +0000
375@@ -0,0 +1,118 @@
376+// Copyright 2013 Canonical Ltd.
377+// Licensed under the AGPLv3, see LICENCE file for details.
378+
379+package provisioner
380+
381+import (
382+ "launchpad.net/loggo"
383+
384+ "launchpad.net/juju-core/agent"
385+ "launchpad.net/juju-core/constraints"
386+ "launchpad.net/juju-core/container"
387+ "launchpad.net/juju-core/container/kvm"
388+ "launchpad.net/juju-core/environs"
389+ "launchpad.net/juju-core/environs/cloudinit"
390+ "launchpad.net/juju-core/instance"
391+ "launchpad.net/juju-core/tools"
392+)
393+
394+var kvmLogger = loggo.GetLogger("juju.provisioner.kvm")
395+
396+var _ environs.InstanceBroker = (*kvmBroker)(nil)
397+var _ tools.HasTools = (*kvmBroker)(nil)
398+
399+func NewKvmBroker(
400+ api APICalls,
401+ tools *tools.Tools,
402+ agentConfig agent.Config,
403+) (environs.InstanceBroker, error) {
404+ manager, err := kvm.NewContainerManager(container.ManagerConfig{Name: "juju"})
405+ if err != nil {
406+ return nil, err
407+ }
408+ return &kvmBroker{
409+ manager: manager,
410+ api: api,
411+ tools: tools,
412+ agentConfig: agentConfig,
413+ }, nil
414+}
415+
416+type kvmBroker struct {
417+ manager container.Manager
418+ api APICalls
419+ tools *tools.Tools
420+ agentConfig agent.Config
421+}
422+
423+func (broker *kvmBroker) Tools() tools.List {
424+ return tools.List{broker.tools}
425+}
426+
427+// StartInstance is specified in the Broker interface.
428+func (broker *kvmBroker) StartInstance(
429+ cons constraints.Value,
430+ possibleTools tools.List,
431+ machineConfig *cloudinit.MachineConfig,
432+) (instance.Instance, *instance.HardwareCharacteristics, error) {
433+
434+ // TODO: refactor common code out of the container brokers.
435+ machineId := machineConfig.MachineId
436+ kvmLogger.Infof("starting kvm container for machineId: %s", machineId)
437+
438+ // TODO: Default to using the host network until we can configure. Yes,
439+ // this is using the LxcBridge value, we should put it in the api call for
440+ // container config.
441+ bridgeDevice := broker.agentConfig.Value(agent.LxcBridge)
442+ if bridgeDevice == "" {
443+ bridgeDevice = kvm.DefaultKvmBridge
444+ }
445+ network := container.BridgeNetworkConfig(bridgeDevice)
446+
447+ // TODO: series doesn't necessarily need to be the same as the host.
448+ series := possibleTools.OneSeries()
449+ machineConfig.MachineContainerType = instance.KVM
450+ machineConfig.Tools = possibleTools[0]
451+
452+ config, err := broker.api.ContainerConfig()
453+ if err != nil {
454+ kvmLogger.Errorf("failed to get container config: %v", err)
455+ return nil, nil, err
456+ }
457+ if err := environs.PopulateMachineConfig(
458+ machineConfig,
459+ config.ProviderType,
460+ config.AuthorizedKeys,
461+ config.SSLHostnameVerification,
462+ config.SyslogPort,
463+ ); err != nil {
464+ kvmLogger.Errorf("failed to populate machine config: %v", err)
465+ return nil, nil, err
466+ }
467+
468+ inst, err := broker.manager.StartContainer(machineConfig, series, network)
469+ if err != nil {
470+ kvmLogger.Errorf("failed to start container: %v", err)
471+ return nil, nil, err
472+ }
473+ kvmLogger.Infof("started kvm container for machineId: %s, %s", machineId, inst.Id())
474+ return inst, nil, nil
475+}
476+
477+// StopInstances shuts down the given instances.
478+func (broker *kvmBroker) StopInstances(instances []instance.Instance) error {
479+ // TODO: potentially parallelise.
480+ for _, instance := range instances {
481+ kvmLogger.Infof("stopping kvm container for instance: %s", instance.Id())
482+ if err := broker.manager.StopContainer(instance); err != nil {
483+ kvmLogger.Errorf("container did not stop: %v", err)
484+ return err
485+ }
486+ }
487+ return nil
488+}
489+
490+// AllInstances only returns running containers.
491+func (broker *kvmBroker) AllInstances() (result []instance.Instance, err error) {
492+ return broker.manager.ListContainers()
493+}
494
495=== added file 'worker/provisioner/kvm-broker_test.go'
496--- worker/provisioner/kvm-broker_test.go 1970-01-01 00:00:00 +0000
497+++ worker/provisioner/kvm-broker_test.go 2013-12-04 04:19:23 +0000
498@@ -0,0 +1,256 @@
499+// Copyright 2013 Canonical Ltd.
500+// Licensed under the AGPLv3, see LICENCE file for details.
501+
502+package provisioner_test
503+
504+import (
505+ "fmt"
506+ "path/filepath"
507+ "time"
508+
509+ gc "launchpad.net/gocheck"
510+
511+ "launchpad.net/juju-core/agent"
512+ "launchpad.net/juju-core/constraints"
513+ "launchpad.net/juju-core/container/kvm/mock"
514+ kvmtesting "launchpad.net/juju-core/container/kvm/testing"
515+ "launchpad.net/juju-core/environs"
516+ "launchpad.net/juju-core/environs/config"
517+ "launchpad.net/juju-core/instance"
518+ instancetest "launchpad.net/juju-core/instance/testing"
519+ jujutesting "launchpad.net/juju-core/juju/testing"
520+ "launchpad.net/juju-core/names"
521+ "launchpad.net/juju-core/state"
522+ coretesting "launchpad.net/juju-core/testing"
523+ jc "launchpad.net/juju-core/testing/checkers"
524+ coretools "launchpad.net/juju-core/tools"
525+ "launchpad.net/juju-core/version"
526+ "launchpad.net/juju-core/worker/provisioner"
527+)
528+
529+type kvmSuite struct {
530+ kvmtesting.TestSuite
531+ events chan mock.Event
532+}
533+
534+type kvmBrokerSuite struct {
535+ kvmSuite
536+ broker environs.InstanceBroker
537+ agentConfig agent.Config
538+}
539+
540+var _ = gc.Suite(&kvmBrokerSuite{})
541+
542+func (s *kvmSuite) SetUpTest(c *gc.C) {
543+ s.TestSuite.SetUpTest(c)
544+ s.events = make(chan mock.Event)
545+ go func() {
546+ for event := range s.events {
547+ c.Output(3, fmt.Sprintf("kvm event: <%s, %s>", event.Action, event.InstanceId))
548+ }
549+ }()
550+ s.TestSuite.Factory.AddListener(s.events)
551+}
552+
553+func (s *kvmSuite) TearDownTest(c *gc.C) {
554+ close(s.events)
555+ s.TestSuite.TearDownTest(c)
556+}
557+
558+func (s *kvmBrokerSuite) SetUpTest(c *gc.C) {
559+ s.kvmSuite.SetUpTest(c)
560+ tools := &coretools.Tools{
561+ Version: version.MustParseBinary("2.3.4-foo-bar"),
562+ URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz",
563+ }
564+ var err error
565+ s.agentConfig, err = agent.NewAgentConfig(
566+ agent.AgentConfigParams{
567+ DataDir: "/not/used/here",
568+ Tag: "tag",
569+ Password: "dummy-secret",
570+ Nonce: "nonce",
571+ APIAddresses: []string{"10.0.0.1:1234"},
572+ CACert: []byte(coretesting.CACert),
573+ })
574+ c.Assert(err, gc.IsNil)
575+ s.broker, err = provisioner.NewKvmBroker(&fakeAPI{}, tools, s.agentConfig)
576+ c.Assert(err, gc.IsNil)
577+}
578+
579+func (s *kvmBrokerSuite) startInstance(c *gc.C, machineId string) instance.Instance {
580+ machineNonce := "fake-nonce"
581+ stateInfo := jujutesting.FakeStateInfo(machineId)
582+ apiInfo := jujutesting.FakeAPIInfo(machineId)
583+ machineConfig := environs.NewMachineConfig(machineId, machineNonce, stateInfo, apiInfo)
584+ cons := constraints.Value{}
585+ possibleTools := s.broker.(coretools.HasTools).Tools()
586+ kvm, _, err := s.broker.StartInstance(cons, possibleTools, machineConfig)
587+ c.Assert(err, gc.IsNil)
588+ return kvm
589+}
590+
591+func (s *kvmBrokerSuite) TestStopInstance(c *gc.C) {
592+ kvm0 := s.startInstance(c, "1/kvm/0")
593+ kvm1 := s.startInstance(c, "1/kvm/1")
594+ kvm2 := s.startInstance(c, "1/kvm/2")
595+
596+ err := s.broker.StopInstances([]instance.Instance{kvm0})
597+ c.Assert(err, gc.IsNil)
598+ s.assertInstances(c, kvm1, kvm2)
599+ c.Assert(s.kvmContainerDir(kvm0), jc.DoesNotExist)
600+ c.Assert(s.kvmRemovedContainerDir(kvm0), jc.IsDirectory)
601+
602+ err = s.broker.StopInstances([]instance.Instance{kvm1, kvm2})
603+ c.Assert(err, gc.IsNil)
604+ s.assertInstances(c)
605+}
606+
607+func (s *kvmBrokerSuite) TestAllInstances(c *gc.C) {
608+ kvm0 := s.startInstance(c, "1/kvm/0")
609+ kvm1 := s.startInstance(c, "1/kvm/1")
610+ s.assertInstances(c, kvm0, kvm1)
611+
612+ err := s.broker.StopInstances([]instance.Instance{kvm1})
613+ c.Assert(err, gc.IsNil)
614+ kvm2 := s.startInstance(c, "1/kvm/2")
615+ s.assertInstances(c, kvm0, kvm2)
616+}
617+
618+func (s *kvmBrokerSuite) assertInstances(c *gc.C, inst ...instance.Instance) {
619+ results, err := s.broker.AllInstances()
620+ c.Assert(err, gc.IsNil)
621+ instancetest.MatchInstances(c, results, inst...)
622+}
623+
624+func (s *kvmBrokerSuite) kvmContainerDir(inst instance.Instance) string {
625+ return filepath.Join(s.ContainerDir, string(inst.Id()))
626+}
627+
628+func (s *kvmBrokerSuite) kvmRemovedContainerDir(inst instance.Instance) string {
629+ return filepath.Join(s.RemovedDir, string(inst.Id()))
630+}
631+
632+type kvmProvisionerSuite struct {
633+ CommonProvisionerSuite
634+ kvmSuite
635+ machineId string
636+ events chan mock.Event
637+}
638+
639+var _ = gc.Suite(&kvmProvisionerSuite{})
640+
641+func (s *kvmProvisionerSuite) SetUpSuite(c *gc.C) {
642+ s.CommonProvisionerSuite.SetUpSuite(c)
643+ s.kvmSuite.SetUpSuite(c)
644+}
645+
646+func (s *kvmProvisionerSuite) TearDownSuite(c *gc.C) {
647+ s.kvmSuite.TearDownSuite(c)
648+ s.CommonProvisionerSuite.TearDownSuite(c)
649+}
650+
651+func (s *kvmProvisionerSuite) SetUpTest(c *gc.C) {
652+ s.CommonProvisionerSuite.SetUpTest(c)
653+ s.kvmSuite.SetUpTest(c)
654+
655+ // The kvm provisioner actually needs the machine it is being created on
656+ // to be in state, in order to get the watcher.
657+ m, err := s.State.AddMachine(config.DefaultSeries, state.JobHostUnits, state.JobManageState)
658+ c.Assert(err, gc.IsNil)
659+ err = m.SetAddresses([]instance.Address{
660+ instance.NewAddress("0.1.2.3"),
661+ })
662+ c.Assert(err, gc.IsNil)
663+ s.machineId = m.Id()
664+ s.APILogin(c, m)
665+ err = m.SetAgentVersion(version.Current)
666+ c.Assert(err, gc.IsNil)
667+
668+ s.events = make(chan mock.Event, 25)
669+ s.Factory.AddListener(s.events)
670+}
671+
672+func (s *kvmProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string {
673+ s.State.StartSync()
674+ event := <-s.events
675+ c.Assert(event.Action, gc.Equals, mock.Started)
676+ err := machine.Refresh()
677+ c.Assert(err, gc.IsNil)
678+ s.waitInstanceId(c, machine, instance.Id(event.InstanceId))
679+ return event.InstanceId
680+}
681+
682+func (s *kvmProvisionerSuite) expectStopped(c *gc.C, instId string) {
683+ s.State.StartSync()
684+ event := <-s.events
685+ c.Assert(event.Action, gc.Equals, mock.Stopped)
686+ c.Assert(event.InstanceId, gc.Equals, instId)
687+}
688+
689+func (s *kvmProvisionerSuite) expectNoEvents(c *gc.C) {
690+ select {
691+ case event := <-s.events:
692+ c.Fatalf("unexpected event %#v", event)
693+ case <-time.After(coretesting.ShortWait):
694+ return
695+ }
696+}
697+
698+func (s *kvmProvisionerSuite) TearDownTest(c *gc.C) {
699+ close(s.events)
700+ s.kvmSuite.TearDownTest(c)
701+ s.CommonProvisionerSuite.TearDownTest(c)
702+}
703+
704+func (s *kvmProvisionerSuite) newKvmProvisioner(c *gc.C) provisioner.Provisioner {
705+ machineTag := names.MachineTag(s.machineId)
706+ agentConfig := s.AgentConfigForTag(c, machineTag)
707+ tools, err := s.provisioner.Tools(agentConfig.Tag())
708+ c.Assert(err, gc.IsNil)
709+ broker, err := provisioner.NewKvmBroker(s.provisioner, tools, agentConfig)
710+ c.Assert(err, gc.IsNil)
711+ return provisioner.NewContainerProvisioner(instance.KVM, s.provisioner, agentConfig, broker)
712+}
713+
714+func (s *kvmProvisionerSuite) TestProvisionerStartStop(c *gc.C) {
715+ p := s.newKvmProvisioner(c)
716+ c.Assert(p.Stop(), gc.IsNil)
717+}
718+
719+func (s *kvmProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) {
720+ p := s.newKvmProvisioner(c)
721+ defer stop(c, p)
722+
723+ // Check that an instance is not provisioned when the machine is created.
724+ _, err := s.State.AddMachine(config.DefaultSeries, state.JobHostUnits)
725+ c.Assert(err, gc.IsNil)
726+
727+ s.expectNoEvents(c)
728+}
729+
730+func (s *kvmProvisionerSuite) addContainer(c *gc.C) *state.Machine {
731+ params := state.AddMachineParams{
732+ ParentId: s.machineId,
733+ ContainerType: instance.KVM,
734+ Series: config.DefaultSeries,
735+ Jobs: []state.MachineJob{state.JobHostUnits},
736+ }
737+ container, err := s.State.AddMachineWithConstraints(&params)
738+ c.Assert(err, gc.IsNil)
739+ return container
740+}
741+
742+func (s *kvmProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) {
743+ p := s.newKvmProvisioner(c)
744+ defer stop(c, p)
745+
746+ container := s.addContainer(c)
747+
748+ instId := s.expectStarted(c, container)
749+
750+ // ...and removed, along with the machine, when the machine is Dead.
751+ c.Assert(container.EnsureDead(), gc.IsNil)
752+ s.expectStopped(c, instId)
753+ s.waitRemoved(c, container)
754+}
755
756=== modified file 'worker/provisioner/lxc-broker.go'
757--- worker/provisioner/lxc-broker.go 2013-12-01 23:54:35 +0000
758+++ worker/provisioner/lxc-broker.go 2013-12-04 04:19:23 +0000
759@@ -49,7 +49,7 @@
760 // StartInstance is specified in the Broker interface.
761 func (broker *lxcBroker) StartInstance(cons constraints.Value, possibleTools tools.List,
762 machineConfig *cloudinit.MachineConfig) (instance.Instance, *instance.HardwareCharacteristics, error) {
763-
764+ // TODO: refactor common code out of the container brokers.
765 machineId := machineConfig.MachineId
766 lxcLogger.Infof("starting lxc container for machineId: %s", machineId)
767

Subscribers

People subscribed via source and target branches

to status/vote changes: