Merge lp:~thumper/juju-core/kvm-provisioner into lp:~go-bot/juju-core/trunk
- kvm-provisioner
- Merge into trunk
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 |
Related bugs: |
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)
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)
Tim Penhey (thumper) wrote : | # |
Tim Penhey (thumper) wrote : | # |
Please take a look.
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.
Preview Diff
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(¶ms) |
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 |
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): kvm/initialisat ion.go kvm/kvm. go cloudinit/ cloudinit. go local/environ. go provisioner/ container_ initialisation. go provisioner/ kvm-broker. go provisioner/ kvm-broker_ test.go
A [revision details]
M container/
M container/
M environs/
M provider/
M worker/
A worker/
A worker/