Merge lp:~thumper/juju-core/kvm-local-provider into lp:~go-bot/juju-core/trunk
- kvm-local-provider
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Tim Penhey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2115 |
Proposed branch: | lp:~thumper/juju-core/kvm-local-provider |
Merge into: | lp:~go-bot/juju-core/trunk |
Prerequisite: | lp:~thumper/juju-core/container-factory |
Diff against target: |
597 lines (+238/-48) 11 files modified
container/kvm/initialisation.go (+46/-0) container/kvm/libvirt.go (+4/-11) provider/local/config.go (+8/-1) provider/local/environ.go (+6/-2) provider/local/environprovider.go (+16/-1) provider/local/prereqs.go (+13/-13) provider/local/prereqs_test.go (+8/-7) utils/apt.go (+17/-0) utils/apt_test.go (+49/-13) utils/command.go (+19/-0) utils/command_test.go (+52/-0) |
To merge this branch: | bzr merge lp:~thumper/juju-core/kvm-local-provider |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+196465@code.launchpad.net |
Commit message
Allow the local provider to use kvm.
Add a 'container' config value for the local provider.
The allows a local provider to use kvm instead of lxc
if the host machine is kvm capable.
Description of the change
Allow the local provider to use kvm.
Add a 'container' config value for the local provider.
The allows a local provider to use kvm instead of lxc
if the host machine is kvm capable.
TODO: prereqs needs to check for other needed packages.
Tim Penhey (thumper) wrote : | # |
Tim Penhey (thumper) wrote : | # |
Please take a look.
Ian Booth (wallyworld) wrote : | # |
LGTM with primarily the consolidation of the code to check kvm container
dependencies with the code to handle kvm initialisation. I also would
like the command code in apt moved. I'm not sure about the need for a
ContainerConfig interface but anytime we need to rely on UnknownAttrs()
seems a bit hacky.
https:/
File container/
https:/
container/
args...)
\o/
https:/
File provider/
https:/
provider/
unknown params.
My brain found it hard to parse the 2nd line above
https:/
File provider/
https:/
provider/
cfg.UnknownAttr
Part of me really doesn't like this UnknownAttrs() business and would
love to see a proper cfg.ContainerType() API available. Not sure if we
should introduce a ContainerConfig interface and cast to that?
https:/
provider/
"lxc" && localConfig.
Can we use the ContainerType consts here instead of "lxc", "kvm"
https:/
File provider/
https:/
provider/
support with the local provider is only supported
s/but//
https:/
provider/
"uvtool-libvirt", "kvm"}
There's also code in the container.kvm package to deal with installing
the required packages - see initialisation.go
I'd prefer this verifyKvm() function to be moved there so it's all done
in one place.
https:/
File utils/apt.go (right):
https:/
utils/apt.go:87: func RunCommand(command string, args ...string) (output
string, err error) {
I'd like to see this in a file called command.go since it's not really
anything to do with apt per se. And tests in command_test.go
Jorge Castro (jorge) wrote : | # |
Thumper please remember to submit an update to the local provider docs as you finish this up!
Tim Penhey (thumper) wrote : | # |
Please take a look.
https:/
File provider/
https:/
provider/
unknown params.
On 2013/12/02 07:10:58, wallyworld wrote:
> My brain found it hard to parse the 2nd line above
tweaked slightly
https:/
File provider/
https:/
provider/
cfg.UnknownAttr
On 2013/12/02 07:10:58, wallyworld wrote:
> Part of me really doesn't like this UnknownAttrs() business and would
love to
> see a proper cfg.ContainerType() API available. Not sure if we should
introduce
> a ContainerConfig interface and cast to that?
Refactored for better reading and understanding and taking the very
small overhead of creating the local config twice.
https:/
provider/
"lxc" && localConfig.
On 2013/12/02 07:10:58, wallyworld wrote:
> Can we use the ContainerType consts here instead of "lxc", "kvm"
Yes.
https:/
File provider/
https:/
provider/
support with the local provider is only supported
On 2013/12/02 07:10:58, wallyworld wrote:
> s/but//
Done.
https:/
provider/
"uvtool-libvirt", "kvm"}
On 2013/12/02 07:10:58, wallyworld wrote:
> There's also code in the container.kvm package to deal with installing
the
> required packages - see initialisation.go
> I'd prefer this verifyKvm() function to be moved there so it's all
done in one
> place.
Done.
https:/
File utils/apt.go (right):
https:/
utils/apt.go:87: func RunCommand(command string, args ...string) (output
string, err error) {
On 2013/12/02 07:10:58, wallyworld wrote:
> I'd like to see this in a file called command.go since it's not really
anything
> to do with apt per se. And tests in command_test.go
Done.
Preview Diff
1 | === modified file 'container/kvm/initialisation.go' |
2 | --- container/kvm/initialisation.go 2013-11-15 06:36:39 +0000 |
3 | +++ container/kvm/initialisation.go 2013-12-02 21:28:17 +0000 |
4 | @@ -4,6 +4,9 @@ |
5 | package kvm |
6 | |
7 | import ( |
8 | + "fmt" |
9 | + "strings" |
10 | + |
11 | "launchpad.net/juju-core/container" |
12 | "launchpad.net/juju-core/utils" |
13 | ) |
14 | @@ -33,3 +36,46 @@ |
15 | func ensureDependencies() error { |
16 | return utils.AptGetInstall(requiredPackages...) |
17 | } |
18 | + |
19 | +const kvmNeedsUbuntu = `Sorry, KVM support with the local provider is only supported |
20 | +on the Ubuntu OS.` |
21 | + |
22 | +const kvmNotSupported = `KVM is not currently supported with the current settings. |
23 | +You could try running 'kvm-ok' yourself as root to get the full rationale as to |
24 | +why it isn't supported, or potentially some BIOS settings to change to enable |
25 | +KVM support.` |
26 | + |
27 | +const neetToInstallKVMOk = `kvm-ok is not installed. Please install the cpu-checker package. |
28 | + sudo apt-get install cpu-checker` |
29 | + |
30 | +const missingKVMDeps = `Some required packages are missing for KVM to work: |
31 | + |
32 | + sudo apt-get install %s |
33 | +` |
34 | + |
35 | +// VerifyKVMEnabled makes sure that the host OS is Ubuntu, and that the required |
36 | +// packages are installed, and that the host CPU is able to support KVM. |
37 | +func VerifyKVMEnabled() error { |
38 | + if !utils.IsUbuntu() { |
39 | + return fmt.Errorf(kvmNeedsUbuntu) |
40 | + } |
41 | + supported, err := IsKVMSupported() |
42 | + if err != nil { |
43 | + // Missing the kvm-ok package. |
44 | + return fmt.Errorf(neetToInstallKVMOk) |
45 | + } |
46 | + if !supported { |
47 | + return fmt.Errorf(kvmNotSupported) |
48 | + } |
49 | + // Check for other packages needed. |
50 | + toInstall := []string{} |
51 | + for _, pkg := range requiredPackages { |
52 | + if !utils.IsPackageInstalled(pkg) { |
53 | + toInstall = append(toInstall, pkg) |
54 | + } |
55 | + } |
56 | + if len(toInstall) > 0 { |
57 | + return fmt.Errorf(missingKVMDeps, strings.Join(toInstall, " ")) |
58 | + } |
59 | + return nil |
60 | +} |
61 | |
62 | === modified file 'container/kvm/libvirt.go' |
63 | --- container/kvm/libvirt.go 2013-11-22 03:34:42 +0000 |
64 | +++ container/kvm/libvirt.go 2013-12-02 21:28:17 +0000 |
65 | @@ -16,9 +16,10 @@ |
66 | |
67 | import ( |
68 | "fmt" |
69 | - "os/exec" |
70 | "regexp" |
71 | "strings" |
72 | + |
73 | + "launchpad.net/juju-core/utils" |
74 | ) |
75 | |
76 | var ( |
77 | @@ -32,17 +33,9 @@ |
78 | // run the command and return the combined output. |
79 | func run(command string, args ...string) (output string, err error) { |
80 | logger.Tracef("%s %v", command, args) |
81 | - cmd := exec.Command(command, args...) |
82 | - out, err := cmd.CombinedOutput() |
83 | - output = string(out) |
84 | + output, err = utils.RunCommand(command, args...) |
85 | logger.Tracef("output: %v", output) |
86 | - if err != nil { |
87 | - return output, err |
88 | - } |
89 | - if !cmd.ProcessState.Success() { |
90 | - return output, fmt.Errorf("%s returned non-zero exit", command) |
91 | - } |
92 | - return output, nil |
93 | + return output, err |
94 | } |
95 | |
96 | // SyncImages updates the local cached images by reading the simplestreams |
97 | |
98 | === modified file 'provider/local/config.go' |
99 | --- provider/local/config.go 2013-10-03 03:03:09 +0000 |
100 | +++ provider/local/config.go 2013-12-02 21:28:17 +0000 |
101 | @@ -9,6 +9,7 @@ |
102 | "path/filepath" |
103 | |
104 | "launchpad.net/juju-core/environs/config" |
105 | + "launchpad.net/juju-core/instance" |
106 | "launchpad.net/juju-core/schema" |
107 | "launchpad.net/juju-core/utils" |
108 | ) |
109 | @@ -22,6 +23,7 @@ |
110 | "root-dir": schema.String(), |
111 | "bootstrap-ip": schema.String(), |
112 | "network-bridge": schema.String(), |
113 | + "container": schema.String(), |
114 | "storage-port": schema.ForceInt(), |
115 | "shared-storage-port": schema.ForceInt(), |
116 | } |
117 | @@ -32,6 +34,7 @@ |
118 | configDefaults = schema.Defaults{ |
119 | "root-dir": "", |
120 | "network-bridge": "lxcbr0", |
121 | + "container": string(instance.LXC), |
122 | "bootstrap-ip": schema.Omit, |
123 | "storage-port": 8040, |
124 | "shared-storage-port": 8041, |
125 | @@ -64,7 +67,7 @@ |
126 | |
127 | // Since it is technically possible for two different users on one machine to |
128 | // have the same local provider name, we need to have a simple way to |
129 | -// namespace the file locations, but more importantly the lxc containers. |
130 | +// namespace the file locations, but more importantly the containers. |
131 | func (c *environConfig) namespace() string { |
132 | return fmt.Sprintf("%s-%s", c.user, c.Name()) |
133 | } |
134 | @@ -73,6 +76,10 @@ |
135 | return c.attrs["root-dir"].(string) |
136 | } |
137 | |
138 | +func (c *environConfig) container() instance.ContainerType { |
139 | + return instance.ContainerType(c.attrs["container"].(string)) |
140 | +} |
141 | + |
142 | func (c *environConfig) networkBridge() string { |
143 | return c.attrs["network-bridge"].(string) |
144 | } |
145 | |
146 | === modified file 'provider/local/environ.go' |
147 | --- provider/local/environ.go 2013-11-26 12:24:48 +0000 |
148 | +++ provider/local/environ.go 2013-12-02 21:28:17 +0000 |
149 | @@ -17,7 +17,7 @@ |
150 | agenttools "launchpad.net/juju-core/agent/tools" |
151 | "launchpad.net/juju-core/constraints" |
152 | "launchpad.net/juju-core/container" |
153 | - "launchpad.net/juju-core/container/lxc" |
154 | + "launchpad.net/juju-core/container/factory" |
155 | "launchpad.net/juju-core/environs" |
156 | "launchpad.net/juju-core/environs/bootstrap" |
157 | "launchpad.net/juju-core/environs/cloudinit" |
158 | @@ -194,11 +194,15 @@ |
159 | env.config = ecfg |
160 | env.name = ecfg.Name() |
161 | |
162 | - env.containerManager = lxc.NewContainerManager( |
163 | + env.containerManager, err = factory.NewContainerManager( |
164 | + ecfg.container(), |
165 | container.ManagerConfig{ |
166 | Name: env.config.namespace(), |
167 | LogDir: env.config.logDir(), |
168 | }) |
169 | + if err != nil { |
170 | + return err |
171 | + } |
172 | |
173 | // Here is the end of normal config setting. |
174 | if ecfg.bootstrapped() { |
175 | |
176 | === modified file 'provider/local/environprovider.go' |
177 | --- provider/local/environprovider.go 2013-10-14 15:01:07 +0000 |
178 | +++ provider/local/environprovider.go 2013-12-02 21:28:17 +0000 |
179 | @@ -12,6 +12,7 @@ |
180 | |
181 | "launchpad.net/juju-core/environs" |
182 | "launchpad.net/juju-core/environs/config" |
183 | + "launchpad.net/juju-core/instance" |
184 | "launchpad.net/juju-core/provider" |
185 | "launchpad.net/juju-core/utils" |
186 | "launchpad.net/juju-core/version" |
187 | @@ -41,7 +42,12 @@ |
188 | } |
189 | cfg = newCfg |
190 | } |
191 | - if err := VerifyPrerequisites(); err != nil { |
192 | + // Do the initial validation on the config. |
193 | + localConfig, err := providerInstance.newConfig(cfg) |
194 | + if err != nil { |
195 | + return nil, err |
196 | + } |
197 | + if err := VerifyPrerequisites(localConfig.container()); err != nil { |
198 | logger.Errorf("failed verification of local provider prerequisites: %v", err) |
199 | return nil, err |
200 | } |
201 | @@ -109,6 +115,11 @@ |
202 | if err != nil { |
203 | return nil, fmt.Errorf("old config is not a valid local config: %v", old) |
204 | } |
205 | + if localConfig.container() != oldLocalConfig.container() { |
206 | + return nil, fmt.Errorf("cannot change container from %q to %q", |
207 | + oldLocalConfig.container(), |
208 | + localConfig.container()) |
209 | + } |
210 | if localConfig.rootDir() != oldLocalConfig.rootDir() { |
211 | return nil, fmt.Errorf("cannot change root-dir from %q to %q", |
212 | oldLocalConfig.rootDir(), |
213 | @@ -130,6 +141,10 @@ |
214 | localConfig.sharedStoragePort()) |
215 | } |
216 | } |
217 | + // Currently only supported containers are "lxc" and "kvm". |
218 | + if localConfig.container() != instance.LXC && localConfig.container() != instance.KVM { |
219 | + return nil, fmt.Errorf("unsupported container type: %q", localConfig.container()) |
220 | + } |
221 | dir := utils.NormalizePath(localConfig.rootDir()) |
222 | if dir == "." { |
223 | dir = config.JujuHomePath(cfg.Name()) |
224 | |
225 | === modified file 'provider/local/prereqs.go' |
226 | --- provider/local/prereqs.go 2013-08-09 06:42:05 +0000 |
227 | +++ provider/local/prereqs.go 2013-12-02 21:28:17 +0000 |
228 | @@ -9,8 +9,10 @@ |
229 | "os" |
230 | "os/exec" |
231 | "runtime" |
232 | - "strings" |
233 | |
234 | + "launchpad.net/juju-core/container/kvm" |
235 | + "launchpad.net/juju-core/instance" |
236 | + "launchpad.net/juju-core/utils" |
237 | "launchpad.net/juju-core/version" |
238 | ) |
239 | |
240 | @@ -61,14 +63,20 @@ |
241 | // VerifyPrerequisites verifies the prerequisites of |
242 | // the local machine (machine 0) for running the local |
243 | // provider. |
244 | -func VerifyPrerequisites() error { |
245 | +func VerifyPrerequisites(containerType instance.ContainerType) error { |
246 | if goos != "linux" { |
247 | return fmt.Errorf(errUnsupportedOS, goos) |
248 | } |
249 | if err := verifyMongod(); err != nil { |
250 | return err |
251 | } |
252 | - return verifyLxc() |
253 | + switch containerType { |
254 | + case instance.LXC: |
255 | + return verifyLxc() |
256 | + case instance.KVM: |
257 | + return kvm.VerifyKVMEnabled() |
258 | + } |
259 | + return fmt.Errorf("Unknown container type specified in the config.") |
260 | } |
261 | |
262 | func verifyMongod() error { |
263 | @@ -91,16 +99,8 @@ |
264 | return nil |
265 | } |
266 | |
267 | -func isUbuntu() bool { |
268 | - out, err := exec.Command("lsb_release", "-i", "-s").CombinedOutput() |
269 | - if err != nil { |
270 | - return false |
271 | - } |
272 | - return strings.TrimSpace(string(out)) == "Ubuntu" |
273 | -} |
274 | - |
275 | func wrapMongodNotExist(err error) error { |
276 | - if isUbuntu() { |
277 | + if utils.IsUbuntu() { |
278 | series := version.Current.Series |
279 | args := []interface{}{err, installMongodUbuntu} |
280 | format := "%v\n%s\n%s" |
281 | @@ -115,7 +115,7 @@ |
282 | } |
283 | |
284 | func wrapLxcNotFound(err error) error { |
285 | - if isUbuntu() { |
286 | + if utils.IsUbuntu() { |
287 | return fmt.Errorf("%v\n%s", err, installLxcUbuntu) |
288 | } |
289 | return fmt.Errorf("%v\n%s", err, installLxcGeneric) |
290 | |
291 | === modified file 'provider/local/prereqs_test.go' |
292 | --- provider/local/prereqs_test.go 2013-09-20 02:53:59 +0000 |
293 | +++ provider/local/prereqs_test.go 2013-12-02 21:28:17 +0000 |
294 | @@ -10,6 +10,7 @@ |
295 | |
296 | gc "launchpad.net/gocheck" |
297 | |
298 | + "launchpad.net/juju-core/instance" |
299 | "launchpad.net/juju-core/testing/testbase" |
300 | ) |
301 | |
302 | @@ -59,17 +60,17 @@ |
303 | goos = old |
304 | }(goos) |
305 | goos = "windows" |
306 | - err := VerifyPrerequisites() |
307 | + err := VerifyPrerequisites(instance.LXC) |
308 | c.Assert(err, gc.ErrorMatches, "Unsupported operating system: windows(.|\n)*") |
309 | } |
310 | |
311 | func (s *prereqsSuite) TestMongoPrereq(c *gc.C) { |
312 | - err := VerifyPrerequisites() |
313 | + err := VerifyPrerequisites(instance.LXC) |
314 | c.Assert(err, gc.ErrorMatches, "(.|\n)*MongoDB server must be installed(.|\n)*") |
315 | c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install mongodb-server(.|\n)*") |
316 | |
317 | os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu") |
318 | - err = VerifyPrerequisites() |
319 | + err = VerifyPrerequisites(instance.LXC) |
320 | c.Assert(err, gc.ErrorMatches, "(.|\n)*MongoDB server must be installed(.|\n)*") |
321 | c.Assert(err, gc.Not(gc.ErrorMatches), "(.|\n)*apt-get install(.|\n)*") |
322 | |
323 | @@ -77,7 +78,7 @@ |
324 | c.Assert(err, gc.IsNil) |
325 | err = ioutil.WriteFile(filepath.Join(s.tmpdir, "lxc-ls"), nil, 0777) |
326 | c.Assert(err, gc.IsNil) |
327 | - err = VerifyPrerequisites() |
328 | + err = VerifyPrerequisites(instance.LXC) |
329 | c.Assert(err, gc.IsNil) |
330 | } |
331 | |
332 | @@ -85,17 +86,17 @@ |
333 | err := ioutil.WriteFile(mongodPath, nil, 0777) |
334 | c.Assert(err, gc.IsNil) |
335 | |
336 | - err = VerifyPrerequisites() |
337 | + err = VerifyPrerequisites(instance.LXC) |
338 | c.Assert(err, gc.ErrorMatches, "(.|\n)*Linux Containers \\(LXC\\) userspace tools must be\ninstalled(.|\n)*") |
339 | c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install lxc(.|\n)*") |
340 | |
341 | os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu") |
342 | - err = VerifyPrerequisites() |
343 | + err = VerifyPrerequisites(instance.LXC) |
344 | c.Assert(err, gc.ErrorMatches, "(.|\n)*Linux Containers \\(LXC\\) userspace tools must be installed(.|\n)*") |
345 | c.Assert(err, gc.Not(gc.ErrorMatches), "(.|\n)*apt-get install(.|\n)*") |
346 | |
347 | err = ioutil.WriteFile(lxclsPath, nil, 0777) |
348 | c.Assert(err, gc.IsNil) |
349 | - err = VerifyPrerequisites() |
350 | + err = VerifyPrerequisites(instance.LXC) |
351 | c.Assert(err, gc.IsNil) |
352 | } |
353 | |
354 | === modified file 'utils/apt.go' |
355 | --- utils/apt.go 2013-11-15 06:36:39 +0000 |
356 | +++ utils/apt.go 2013-12-02 21:28:17 +0000 |
357 | @@ -9,6 +9,7 @@ |
358 | "os" |
359 | "os/exec" |
360 | "regexp" |
361 | + "strings" |
362 | |
363 | "launchpad.net/loggo" |
364 | ) |
365 | @@ -72,3 +73,19 @@ |
366 | } |
367 | return string(bytes.Join(aptProxyRE.FindAll(out, -1), []byte("\n"))), nil |
368 | } |
369 | + |
370 | +// IsUbuntu executes lxb_release to see if the host OS is Ubuntu. |
371 | +func IsUbuntu() bool { |
372 | + out, err := RunCommand("lsb_release", "-i", "-s") |
373 | + if err != nil { |
374 | + return false |
375 | + } |
376 | + return strings.TrimSpace(out) == "Ubuntu" |
377 | +} |
378 | + |
379 | +// IsPackageInstalled uses dpkg-query to determine if the `packageName` |
380 | +// package is installed. |
381 | +func IsPackageInstalled(packageName string) bool { |
382 | + _, err := RunCommand("dpkg-query", "--status", packageName) |
383 | + return err == nil |
384 | +} |
385 | |
386 | === modified file 'utils/apt_test.go' |
387 | --- utils/apt_test.go 2013-11-15 06:36:39 +0000 |
388 | +++ utils/apt_test.go 2013-12-02 21:28:17 +0000 |
389 | @@ -1,14 +1,16 @@ |
390 | // Copyright 2012, 2013 Canonical Ltd. |
391 | // Licensed under the AGPLv3, see LICENCE file for details. |
392 | |
393 | -package utils |
394 | +package utils_test |
395 | |
396 | import ( |
397 | "fmt" |
398 | |
399 | gc "launchpad.net/gocheck" |
400 | |
401 | + jc "launchpad.net/juju-core/testing/checkers" |
402 | "launchpad.net/juju-core/testing/testbase" |
403 | + "launchpad.net/juju-core/utils" |
404 | ) |
405 | |
406 | type AptSuite struct { |
407 | @@ -18,9 +20,9 @@ |
408 | var _ = gc.Suite(&AptSuite{}) |
409 | |
410 | func (s *AptSuite) TestOnePackage(c *gc.C) { |
411 | - cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte{}, nil) |
412 | + cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) |
413 | defer cleanup() |
414 | - err := AptGetInstall("test-package") |
415 | + err := utils.AptGetInstall("test-package") |
416 | c.Assert(err, gc.IsNil) |
417 | cmd := <-cmdChan |
418 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
419 | @@ -35,9 +37,9 @@ |
420 | const expected = `E: frobnicator failure detected` |
421 | cmdError := fmt.Errorf("error") |
422 | cmdExpectedError := fmt.Errorf("apt-get failed: error") |
423 | - cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(expected), cmdError) |
424 | + cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), cmdError) |
425 | defer cleanup() |
426 | - err := AptGetInstall("foo") |
427 | + err := utils.AptGetInstall("foo") |
428 | c.Assert(err, gc.DeepEquals, cmdExpectedError) |
429 | cmd := <-cmdChan |
430 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
431 | @@ -48,9 +50,9 @@ |
432 | } |
433 | |
434 | func (s *AptSuite) TestConfigProxyEmpty(c *gc.C) { |
435 | - cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte{}, nil) |
436 | + cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) |
437 | defer cleanup() |
438 | - out, err := AptConfigProxy() |
439 | + out, err := utils.AptConfigProxy() |
440 | c.Assert(err, gc.IsNil) |
441 | cmd := <-cmdChan |
442 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
443 | @@ -63,9 +65,9 @@ |
444 | func (s *AptSuite) TestConfigProxyConfigured(c *gc.C) { |
445 | const expected = `Acquire::http::Proxy "10.0.3.1:3142"; |
446 | Acquire::https::Proxy "false";` |
447 | - cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(expected), nil) |
448 | + cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), nil) |
449 | defer cleanup() |
450 | - out, err := AptConfigProxy() |
451 | + out, err := utils.AptConfigProxy() |
452 | c.Assert(err, gc.IsNil) |
453 | cmd := <-cmdChan |
454 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
455 | @@ -83,9 +85,9 @@ |
456 | expected = `Acquire::http::Proxy "10.0.3.1:3142"; |
457 | Acquire::https::Proxy "false";` |
458 | ) |
459 | - cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(output), nil) |
460 | + cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(output), nil) |
461 | defer cleanup() |
462 | - out, err := AptConfigProxy() |
463 | + out, err := utils.AptConfigProxy() |
464 | c.Assert(err, gc.IsNil) |
465 | cmd := <-cmdChan |
466 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
467 | @@ -99,9 +101,9 @@ |
468 | const expected = `E: frobnicator failure detected` |
469 | cmdError := fmt.Errorf("error") |
470 | cmdExpectedError := fmt.Errorf("apt-config failed: error") |
471 | - cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(expected), cmdError) |
472 | + cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), cmdError) |
473 | defer cleanup() |
474 | - out, err := AptConfigProxy() |
475 | + out, err := utils.AptConfigProxy() |
476 | c.Assert(err, gc.DeepEquals, cmdExpectedError) |
477 | cmd := <-cmdChan |
478 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
479 | @@ -110,3 +112,37 @@ |
480 | }) |
481 | c.Assert(out, gc.Equals, "") |
482 | } |
483 | + |
484 | +func (s *AptSuite) patchLsbRelease(c *gc.C, name string) { |
485 | + content := fmt.Sprintf("#!/bin/bash --norc\necho %s", name) |
486 | + patchExecutable(s, c.MkDir(), "lsb_release", content) |
487 | +} |
488 | + |
489 | +func (s *AptSuite) TestIsUbuntu(c *gc.C) { |
490 | + s.patchLsbRelease(c, "Ubuntu") |
491 | + c.Assert(utils.IsUbuntu(), jc.IsTrue) |
492 | +} |
493 | + |
494 | +func (s *AptSuite) TestIsNotUbuntu(c *gc.C) { |
495 | + s.patchLsbRelease(c, "Windows NT") |
496 | + c.Assert(utils.IsUbuntu(), jc.IsFalse) |
497 | +} |
498 | + |
499 | +func (s *AptSuite) patchDpkgQuery(c *gc.C, installed bool) { |
500 | + rc := 0 |
501 | + if !installed { |
502 | + rc = 1 |
503 | + } |
504 | + content := fmt.Sprintf("#!/bin/bash --norc\nexit %v", rc) |
505 | + patchExecutable(s, c.MkDir(), "dpkg-query", content) |
506 | +} |
507 | + |
508 | +func (s *AptSuite) TestIsPackageInstalled(c *gc.C) { |
509 | + s.patchDpkgQuery(c, true) |
510 | + c.Assert(utils.IsPackageInstalled("foo-bar"), jc.IsTrue) |
511 | +} |
512 | + |
513 | +func (s *AptSuite) TestIsPackageNotInstalled(c *gc.C) { |
514 | + s.patchDpkgQuery(c, false) |
515 | + c.Assert(utils.IsPackageInstalled("foo-bar"), jc.IsFalse) |
516 | +} |
517 | |
518 | === added file 'utils/command.go' |
519 | --- utils/command.go 1970-01-01 00:00:00 +0000 |
520 | +++ utils/command.go 2013-12-02 21:28:17 +0000 |
521 | @@ -0,0 +1,19 @@ |
522 | +// Copyright 2013 Canonical Ltd. |
523 | +// Licensed under the AGPLv3, see LICENCE file for details. |
524 | + |
525 | +package utils |
526 | + |
527 | +import ( |
528 | + "os/exec" |
529 | +) |
530 | + |
531 | +// RunCommand executes the command and return the combined output. |
532 | +func RunCommand(command string, args ...string) (output string, err error) { |
533 | + cmd := exec.Command(command, args...) |
534 | + out, err := cmd.CombinedOutput() |
535 | + output = string(out) |
536 | + if err != nil { |
537 | + return output, err |
538 | + } |
539 | + return output, nil |
540 | +} |
541 | |
542 | === added file 'utils/command_test.go' |
543 | --- utils/command_test.go 1970-01-01 00:00:00 +0000 |
544 | +++ utils/command_test.go 2013-12-02 21:28:17 +0000 |
545 | @@ -0,0 +1,52 @@ |
546 | +// Copyright 2012, 2013 Canonical Ltd. |
547 | +// Licensed under the AGPLv3, see LICENCE file for details. |
548 | + |
549 | +package utils_test |
550 | + |
551 | +import ( |
552 | + "io/ioutil" |
553 | + "path/filepath" |
554 | + |
555 | + gc "launchpad.net/gocheck" |
556 | + |
557 | + "launchpad.net/juju-core/testing/testbase" |
558 | + "launchpad.net/juju-core/utils" |
559 | +) |
560 | + |
561 | +type EnvironmentPatcher interface { |
562 | + PatchEnvironment(name, value string) |
563 | +} |
564 | + |
565 | +func patchExecutable(patcher EnvironmentPatcher, dir, execName, script string) { |
566 | + patcher.PatchEnvironment("PATH", dir) |
567 | + filename := filepath.Join(dir, execName) |
568 | + ioutil.WriteFile(filename, []byte(script), 0755) |
569 | +} |
570 | + |
571 | +type commandSuite struct { |
572 | + testbase.LoggingSuite |
573 | +} |
574 | + |
575 | +var _ = gc.Suite(&commandSuite{}) |
576 | + |
577 | +func (s *commandSuite) TestRunCommandCombinesOutput(c *gc.C) { |
578 | + content := `#!/bin/bash --norc |
579 | +echo stdout |
580 | +echo stderr 1>&2 |
581 | +` |
582 | + patchExecutable(s, c.MkDir(), "test-output", content) |
583 | + output, err := utils.RunCommand("test-output") |
584 | + c.Assert(err, gc.IsNil) |
585 | + c.Assert(output, gc.Equals, "stdout\nstderr\n") |
586 | +} |
587 | + |
588 | +func (s *commandSuite) TestRunCommandNonZeroExit(c *gc.C) { |
589 | + content := `#!/bin/bash --norc |
590 | +echo stdout |
591 | +exit 42 |
592 | +` |
593 | + patchExecutable(s, c.MkDir(), "test-output", content) |
594 | + output, err := utils.RunCommand("test-output") |
595 | + c.Assert(err, gc.ErrorMatches, `exit status 42`) |
596 | + c.Assert(output, gc.Equals, "stdout\n") |
597 | +} |
Reviewers: mp+196465_ code.launchpad. net,
Message:
Please take a look.
Description:
Allow the local provider to use kvm.
Add a 'container' config value for the local provider.
The allows a local provider to use kvm instead of lxc
if the host machine is kvm capable.
TODO: prereqs needs to check for other needed packages.
https:/ /code.launchpad .net/~thumper/ juju-core/ kvm-local- provider/ +merge/ 196465
Requires: /code.launchpad .net/~thumper/ juju-core/ container- factory/ +merge/ 196073
https:/
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/31870043/
Affected files (+91, -13 lines): local/config. go local/environ. go local/environpr ovider. go local/prereqs. go local/prereqs_ test.go
A [revision details]
M provider/
M provider/
M provider/
M provider/
M provider/