Merge lp:~thumper/juju-core/kvm-local-provider 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: 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
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.

https://codereview.appspot.com/31870043/

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.

https://codereview.appspot.com/31870043/

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

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:
https://code.launchpad.net/~thumper/juju-core/container-factory/+merge/196073

(do not edit description out of merge proposal)

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

Affected files (+91, -13 lines):
   A [revision details]
   M provider/local/config.go
   M provider/local/environ.go
   M provider/local/environprovider.go
   M provider/local/prereqs.go
   M provider/local/prereqs_test.go

Revision history for this message
Tim Penhey (thumper) wrote :
Revision history for this message
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://codereview.appspot.com/31870043/diff/20001/container/kvm/libvirt.go
File container/kvm/libvirt.go (right):

https://codereview.appspot.com/31870043/diff/20001/container/kvm/libvirt.go#newcode36
container/kvm/libvirt.go:36: output, err = utils.RunCommand(command,
args...)
\o/

https://codereview.appspot.com/31870043/diff/20001/provider/local/config.go
File provider/local/config.go (right):

https://codereview.appspot.com/31870043/diff/20001/provider/local/config.go#newcode23
provider/local/config.go:23: // to explicitly get from the config
unknown params.
My brain found it hard to parse the 2nd line above

https://codereview.appspot.com/31870043/diff/20001/provider/local/environprovider.go
File provider/local/environprovider.go (right):

https://codereview.appspot.com/31870043/diff/20001/provider/local/environprovider.go#newcode49
provider/local/environprovider.go:49: containerType, ok :=
cfg.UnknownAttrs()[containerConfigKey].(string)
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://codereview.appspot.com/31870043/diff/20001/provider/local/environprovider.go#newcode148
provider/local/environprovider.go:148: if localConfig.container() !=
"lxc" && localConfig.container() != "kvm" {
Can we use the ContainerType consts here instead of "lxc", "kvm"

https://codereview.appspot.com/31870043/diff/20001/provider/local/prereqs.go
File provider/local/prereqs.go (right):

https://codereview.appspot.com/31870043/diff/20001/provider/local/prereqs.go#newcode50
provider/local/prereqs.go:50: const kvmNeedsUbuntu = `Sorry, but KVM
support with the local provider is only supported
s/but//

https://codereview.appspot.com/31870043/diff/20001/provider/local/prereqs.go#newcode154
provider/local/prereqs.go:154: packagesNeeded := []string{"libvirt-bin",
"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://codereview.appspot.com/31870043/diff/20001/utils/apt.go
File utils/apt.go (right):

https://codereview.appspot.com/31870043/diff/20001/utils/apt.go#newcode87
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

https://codereview.appspot.com/31870043/

Revision history for this message
Jorge Castro (jorge) wrote :

Thumper please remember to submit an update to the local provider docs as you finish this up!

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

Please take a look.

https://codereview.appspot.com/31870043/diff/20001/provider/local/config.go
File provider/local/config.go (right):

https://codereview.appspot.com/31870043/diff/20001/provider/local/config.go#newcode23
provider/local/config.go:23: // to explicitly get from the config
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://codereview.appspot.com/31870043/diff/20001/provider/local/environprovider.go
File provider/local/environprovider.go (right):

https://codereview.appspot.com/31870043/diff/20001/provider/local/environprovider.go#newcode49
provider/local/environprovider.go:49: containerType, ok :=
cfg.UnknownAttrs()[containerConfigKey].(string)
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://codereview.appspot.com/31870043/diff/20001/provider/local/environprovider.go#newcode148
provider/local/environprovider.go:148: if localConfig.container() !=
"lxc" && localConfig.container() != "kvm" {
On 2013/12/02 07:10:58, wallyworld wrote:
> Can we use the ContainerType consts here instead of "lxc", "kvm"

Yes.

https://codereview.appspot.com/31870043/diff/20001/provider/local/prereqs.go
File provider/local/prereqs.go (right):

https://codereview.appspot.com/31870043/diff/20001/provider/local/prereqs.go#newcode50
provider/local/prereqs.go:50: const kvmNeedsUbuntu = `Sorry, but KVM
support with the local provider is only supported
On 2013/12/02 07:10:58, wallyworld wrote:
> s/but//

Done.

https://codereview.appspot.com/31870043/diff/20001/provider/local/prereqs.go#newcode154
provider/local/prereqs.go:154: packagesNeeded := []string{"libvirt-bin",
"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://codereview.appspot.com/31870043/diff/20001/utils/apt.go
File utils/apt.go (right):

https://codereview.appspot.com/31870043/diff/20001/utils/apt.go#newcode87
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.

https://codereview.appspot.com/31870043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'container/kvm/initialisation.go'
--- container/kvm/initialisation.go 2013-11-15 06:36:39 +0000
+++ container/kvm/initialisation.go 2013-12-02 21:28:17 +0000
@@ -4,6 +4,9 @@
4package kvm4package kvm
55
6import (6import (
7 "fmt"
8 "strings"
9
7 "launchpad.net/juju-core/container"10 "launchpad.net/juju-core/container"
8 "launchpad.net/juju-core/utils"11 "launchpad.net/juju-core/utils"
9)12)
@@ -33,3 +36,46 @@
33func ensureDependencies() error {36func ensureDependencies() error {
34 return utils.AptGetInstall(requiredPackages...)37 return utils.AptGetInstall(requiredPackages...)
35}38}
39
40const kvmNeedsUbuntu = `Sorry, KVM support with the local provider is only supported
41on the Ubuntu OS.`
42
43const kvmNotSupported = `KVM is not currently supported with the current settings.
44You could try running 'kvm-ok' yourself as root to get the full rationale as to
45why it isn't supported, or potentially some BIOS settings to change to enable
46KVM support.`
47
48const neetToInstallKVMOk = `kvm-ok is not installed. Please install the cpu-checker package.
49 sudo apt-get install cpu-checker`
50
51const missingKVMDeps = `Some required packages are missing for KVM to work:
52
53 sudo apt-get install %s
54`
55
56// VerifyKVMEnabled makes sure that the host OS is Ubuntu, and that the required
57// packages are installed, and that the host CPU is able to support KVM.
58func VerifyKVMEnabled() error {
59 if !utils.IsUbuntu() {
60 return fmt.Errorf(kvmNeedsUbuntu)
61 }
62 supported, err := IsKVMSupported()
63 if err != nil {
64 // Missing the kvm-ok package.
65 return fmt.Errorf(neetToInstallKVMOk)
66 }
67 if !supported {
68 return fmt.Errorf(kvmNotSupported)
69 }
70 // Check for other packages needed.
71 toInstall := []string{}
72 for _, pkg := range requiredPackages {
73 if !utils.IsPackageInstalled(pkg) {
74 toInstall = append(toInstall, pkg)
75 }
76 }
77 if len(toInstall) > 0 {
78 return fmt.Errorf(missingKVMDeps, strings.Join(toInstall, " "))
79 }
80 return nil
81}
3682
=== modified file 'container/kvm/libvirt.go'
--- container/kvm/libvirt.go 2013-11-22 03:34:42 +0000
+++ container/kvm/libvirt.go 2013-12-02 21:28:17 +0000
@@ -16,9 +16,10 @@
1616
17import (17import (
18 "fmt"18 "fmt"
19 "os/exec"
20 "regexp"19 "regexp"
21 "strings"20 "strings"
21
22 "launchpad.net/juju-core/utils"
22)23)
2324
24var (25var (
@@ -32,17 +33,9 @@
32// run the command and return the combined output.33// run the command and return the combined output.
33func run(command string, args ...string) (output string, err error) {34func run(command string, args ...string) (output string, err error) {
34 logger.Tracef("%s %v", command, args)35 logger.Tracef("%s %v", command, args)
35 cmd := exec.Command(command, args...)36 output, err = utils.RunCommand(command, args...)
36 out, err := cmd.CombinedOutput()
37 output = string(out)
38 logger.Tracef("output: %v", output)37 logger.Tracef("output: %v", output)
39 if err != nil {38 return output, err
40 return output, err
41 }
42 if !cmd.ProcessState.Success() {
43 return output, fmt.Errorf("%s returned non-zero exit", command)
44 }
45 return output, nil
46}39}
4740
48// SyncImages updates the local cached images by reading the simplestreams41// SyncImages updates the local cached images by reading the simplestreams
4942
=== modified file 'provider/local/config.go'
--- provider/local/config.go 2013-10-03 03:03:09 +0000
+++ provider/local/config.go 2013-12-02 21:28:17 +0000
@@ -9,6 +9,7 @@
9 "path/filepath"9 "path/filepath"
1010
11 "launchpad.net/juju-core/environs/config"11 "launchpad.net/juju-core/environs/config"
12 "launchpad.net/juju-core/instance"
12 "launchpad.net/juju-core/schema"13 "launchpad.net/juju-core/schema"
13 "launchpad.net/juju-core/utils"14 "launchpad.net/juju-core/utils"
14)15)
@@ -22,6 +23,7 @@
22 "root-dir": schema.String(),23 "root-dir": schema.String(),
23 "bootstrap-ip": schema.String(),24 "bootstrap-ip": schema.String(),
24 "network-bridge": schema.String(),25 "network-bridge": schema.String(),
26 "container": schema.String(),
25 "storage-port": schema.ForceInt(),27 "storage-port": schema.ForceInt(),
26 "shared-storage-port": schema.ForceInt(),28 "shared-storage-port": schema.ForceInt(),
27 }29 }
@@ -32,6 +34,7 @@
32 configDefaults = schema.Defaults{34 configDefaults = schema.Defaults{
33 "root-dir": "",35 "root-dir": "",
34 "network-bridge": "lxcbr0",36 "network-bridge": "lxcbr0",
37 "container": string(instance.LXC),
35 "bootstrap-ip": schema.Omit,38 "bootstrap-ip": schema.Omit,
36 "storage-port": 8040,39 "storage-port": 8040,
37 "shared-storage-port": 8041,40 "shared-storage-port": 8041,
@@ -64,7 +67,7 @@
6467
65// Since it is technically possible for two different users on one machine to68// Since it is technically possible for two different users on one machine to
66// have the same local provider name, we need to have a simple way to69// have the same local provider name, we need to have a simple way to
67// namespace the file locations, but more importantly the lxc containers.70// namespace the file locations, but more importantly the containers.
68func (c *environConfig) namespace() string {71func (c *environConfig) namespace() string {
69 return fmt.Sprintf("%s-%s", c.user, c.Name())72 return fmt.Sprintf("%s-%s", c.user, c.Name())
70}73}
@@ -73,6 +76,10 @@
73 return c.attrs["root-dir"].(string)76 return c.attrs["root-dir"].(string)
74}77}
7578
79func (c *environConfig) container() instance.ContainerType {
80 return instance.ContainerType(c.attrs["container"].(string))
81}
82
76func (c *environConfig) networkBridge() string {83func (c *environConfig) networkBridge() string {
77 return c.attrs["network-bridge"].(string)84 return c.attrs["network-bridge"].(string)
78}85}
7986
=== modified file 'provider/local/environ.go'
--- provider/local/environ.go 2013-11-26 12:24:48 +0000
+++ provider/local/environ.go 2013-12-02 21:28:17 +0000
@@ -17,7 +17,7 @@
17 agenttools "launchpad.net/juju-core/agent/tools"17 agenttools "launchpad.net/juju-core/agent/tools"
18 "launchpad.net/juju-core/constraints"18 "launchpad.net/juju-core/constraints"
19 "launchpad.net/juju-core/container"19 "launchpad.net/juju-core/container"
20 "launchpad.net/juju-core/container/lxc"20 "launchpad.net/juju-core/container/factory"
21 "launchpad.net/juju-core/environs"21 "launchpad.net/juju-core/environs"
22 "launchpad.net/juju-core/environs/bootstrap"22 "launchpad.net/juju-core/environs/bootstrap"
23 "launchpad.net/juju-core/environs/cloudinit"23 "launchpad.net/juju-core/environs/cloudinit"
@@ -194,11 +194,15 @@
194 env.config = ecfg194 env.config = ecfg
195 env.name = ecfg.Name()195 env.name = ecfg.Name()
196196
197 env.containerManager = lxc.NewContainerManager(197 env.containerManager, err = factory.NewContainerManager(
198 ecfg.container(),
198 container.ManagerConfig{199 container.ManagerConfig{
199 Name: env.config.namespace(),200 Name: env.config.namespace(),
200 LogDir: env.config.logDir(),201 LogDir: env.config.logDir(),
201 })202 })
203 if err != nil {
204 return err
205 }
202206
203 // Here is the end of normal config setting.207 // Here is the end of normal config setting.
204 if ecfg.bootstrapped() {208 if ecfg.bootstrapped() {
205209
=== modified file 'provider/local/environprovider.go'
--- provider/local/environprovider.go 2013-10-14 15:01:07 +0000
+++ provider/local/environprovider.go 2013-12-02 21:28:17 +0000
@@ -12,6 +12,7 @@
1212
13 "launchpad.net/juju-core/environs"13 "launchpad.net/juju-core/environs"
14 "launchpad.net/juju-core/environs/config"14 "launchpad.net/juju-core/environs/config"
15 "launchpad.net/juju-core/instance"
15 "launchpad.net/juju-core/provider"16 "launchpad.net/juju-core/provider"
16 "launchpad.net/juju-core/utils"17 "launchpad.net/juju-core/utils"
17 "launchpad.net/juju-core/version"18 "launchpad.net/juju-core/version"
@@ -41,7 +42,12 @@
41 }42 }
42 cfg = newCfg43 cfg = newCfg
43 }44 }
44 if err := VerifyPrerequisites(); err != nil {45 // Do the initial validation on the config.
46 localConfig, err := providerInstance.newConfig(cfg)
47 if err != nil {
48 return nil, err
49 }
50 if err := VerifyPrerequisites(localConfig.container()); err != nil {
45 logger.Errorf("failed verification of local provider prerequisites: %v", err)51 logger.Errorf("failed verification of local provider prerequisites: %v", err)
46 return nil, err52 return nil, err
47 }53 }
@@ -109,6 +115,11 @@
109 if err != nil {115 if err != nil {
110 return nil, fmt.Errorf("old config is not a valid local config: %v", old)116 return nil, fmt.Errorf("old config is not a valid local config: %v", old)
111 }117 }
118 if localConfig.container() != oldLocalConfig.container() {
119 return nil, fmt.Errorf("cannot change container from %q to %q",
120 oldLocalConfig.container(),
121 localConfig.container())
122 }
112 if localConfig.rootDir() != oldLocalConfig.rootDir() {123 if localConfig.rootDir() != oldLocalConfig.rootDir() {
113 return nil, fmt.Errorf("cannot change root-dir from %q to %q",124 return nil, fmt.Errorf("cannot change root-dir from %q to %q",
114 oldLocalConfig.rootDir(),125 oldLocalConfig.rootDir(),
@@ -130,6 +141,10 @@
130 localConfig.sharedStoragePort())141 localConfig.sharedStoragePort())
131 }142 }
132 }143 }
144 // Currently only supported containers are "lxc" and "kvm".
145 if localConfig.container() != instance.LXC && localConfig.container() != instance.KVM {
146 return nil, fmt.Errorf("unsupported container type: %q", localConfig.container())
147 }
133 dir := utils.NormalizePath(localConfig.rootDir())148 dir := utils.NormalizePath(localConfig.rootDir())
134 if dir == "." {149 if dir == "." {
135 dir = config.JujuHomePath(cfg.Name())150 dir = config.JujuHomePath(cfg.Name())
136151
=== modified file 'provider/local/prereqs.go'
--- provider/local/prereqs.go 2013-08-09 06:42:05 +0000
+++ provider/local/prereqs.go 2013-12-02 21:28:17 +0000
@@ -9,8 +9,10 @@
9 "os"9 "os"
10 "os/exec"10 "os/exec"
11 "runtime"11 "runtime"
12 "strings"
1312
13 "launchpad.net/juju-core/container/kvm"
14 "launchpad.net/juju-core/instance"
15 "launchpad.net/juju-core/utils"
14 "launchpad.net/juju-core/version"16 "launchpad.net/juju-core/version"
15)17)
1618
@@ -61,14 +63,20 @@
61// VerifyPrerequisites verifies the prerequisites of63// VerifyPrerequisites verifies the prerequisites of
62// the local machine (machine 0) for running the local64// the local machine (machine 0) for running the local
63// provider.65// provider.
64func VerifyPrerequisites() error {66func VerifyPrerequisites(containerType instance.ContainerType) error {
65 if goos != "linux" {67 if goos != "linux" {
66 return fmt.Errorf(errUnsupportedOS, goos)68 return fmt.Errorf(errUnsupportedOS, goos)
67 }69 }
68 if err := verifyMongod(); err != nil {70 if err := verifyMongod(); err != nil {
69 return err71 return err
70 }72 }
71 return verifyLxc()73 switch containerType {
74 case instance.LXC:
75 return verifyLxc()
76 case instance.KVM:
77 return kvm.VerifyKVMEnabled()
78 }
79 return fmt.Errorf("Unknown container type specified in the config.")
72}80}
7381
74func verifyMongod() error {82func verifyMongod() error {
@@ -91,16 +99,8 @@
91 return nil99 return nil
92}100}
93101
94func isUbuntu() bool {
95 out, err := exec.Command("lsb_release", "-i", "-s").CombinedOutput()
96 if err != nil {
97 return false
98 }
99 return strings.TrimSpace(string(out)) == "Ubuntu"
100}
101
102func wrapMongodNotExist(err error) error {102func wrapMongodNotExist(err error) error {
103 if isUbuntu() {103 if utils.IsUbuntu() {
104 series := version.Current.Series104 series := version.Current.Series
105 args := []interface{}{err, installMongodUbuntu}105 args := []interface{}{err, installMongodUbuntu}
106 format := "%v\n%s\n%s"106 format := "%v\n%s\n%s"
@@ -115,7 +115,7 @@
115}115}
116116
117func wrapLxcNotFound(err error) error {117func wrapLxcNotFound(err error) error {
118 if isUbuntu() {118 if utils.IsUbuntu() {
119 return fmt.Errorf("%v\n%s", err, installLxcUbuntu)119 return fmt.Errorf("%v\n%s", err, installLxcUbuntu)
120 }120 }
121 return fmt.Errorf("%v\n%s", err, installLxcGeneric)121 return fmt.Errorf("%v\n%s", err, installLxcGeneric)
122122
=== modified file 'provider/local/prereqs_test.go'
--- provider/local/prereqs_test.go 2013-09-20 02:53:59 +0000
+++ provider/local/prereqs_test.go 2013-12-02 21:28:17 +0000
@@ -10,6 +10,7 @@
1010
11 gc "launchpad.net/gocheck"11 gc "launchpad.net/gocheck"
1212
13 "launchpad.net/juju-core/instance"
13 "launchpad.net/juju-core/testing/testbase"14 "launchpad.net/juju-core/testing/testbase"
14)15)
1516
@@ -59,17 +60,17 @@
59 goos = old60 goos = old
60 }(goos)61 }(goos)
61 goos = "windows"62 goos = "windows"
62 err := VerifyPrerequisites()63 err := VerifyPrerequisites(instance.LXC)
63 c.Assert(err, gc.ErrorMatches, "Unsupported operating system: windows(.|\n)*")64 c.Assert(err, gc.ErrorMatches, "Unsupported operating system: windows(.|\n)*")
64}65}
6566
66func (s *prereqsSuite) TestMongoPrereq(c *gc.C) {67func (s *prereqsSuite) TestMongoPrereq(c *gc.C) {
67 err := VerifyPrerequisites()68 err := VerifyPrerequisites(instance.LXC)
68 c.Assert(err, gc.ErrorMatches, "(.|\n)*MongoDB server must be installed(.|\n)*")69 c.Assert(err, gc.ErrorMatches, "(.|\n)*MongoDB server must be installed(.|\n)*")
69 c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install mongodb-server(.|\n)*")70 c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install mongodb-server(.|\n)*")
7071
71 os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu")72 os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu")
72 err = VerifyPrerequisites()73 err = VerifyPrerequisites(instance.LXC)
73 c.Assert(err, gc.ErrorMatches, "(.|\n)*MongoDB server must be installed(.|\n)*")74 c.Assert(err, gc.ErrorMatches, "(.|\n)*MongoDB server must be installed(.|\n)*")
74 c.Assert(err, gc.Not(gc.ErrorMatches), "(.|\n)*apt-get install(.|\n)*")75 c.Assert(err, gc.Not(gc.ErrorMatches), "(.|\n)*apt-get install(.|\n)*")
7576
@@ -77,7 +78,7 @@
77 c.Assert(err, gc.IsNil)78 c.Assert(err, gc.IsNil)
78 err = ioutil.WriteFile(filepath.Join(s.tmpdir, "lxc-ls"), nil, 0777)79 err = ioutil.WriteFile(filepath.Join(s.tmpdir, "lxc-ls"), nil, 0777)
79 c.Assert(err, gc.IsNil)80 c.Assert(err, gc.IsNil)
80 err = VerifyPrerequisites()81 err = VerifyPrerequisites(instance.LXC)
81 c.Assert(err, gc.IsNil)82 c.Assert(err, gc.IsNil)
82}83}
8384
@@ -85,17 +86,17 @@
85 err := ioutil.WriteFile(mongodPath, nil, 0777)86 err := ioutil.WriteFile(mongodPath, nil, 0777)
86 c.Assert(err, gc.IsNil)87 c.Assert(err, gc.IsNil)
8788
88 err = VerifyPrerequisites()89 err = VerifyPrerequisites(instance.LXC)
89 c.Assert(err, gc.ErrorMatches, "(.|\n)*Linux Containers \\(LXC\\) userspace tools must be\ninstalled(.|\n)*")90 c.Assert(err, gc.ErrorMatches, "(.|\n)*Linux Containers \\(LXC\\) userspace tools must be\ninstalled(.|\n)*")
90 c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install lxc(.|\n)*")91 c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install lxc(.|\n)*")
9192
92 os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu")93 os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu")
93 err = VerifyPrerequisites()94 err = VerifyPrerequisites(instance.LXC)
94 c.Assert(err, gc.ErrorMatches, "(.|\n)*Linux Containers \\(LXC\\) userspace tools must be installed(.|\n)*")95 c.Assert(err, gc.ErrorMatches, "(.|\n)*Linux Containers \\(LXC\\) userspace tools must be installed(.|\n)*")
95 c.Assert(err, gc.Not(gc.ErrorMatches), "(.|\n)*apt-get install(.|\n)*")96 c.Assert(err, gc.Not(gc.ErrorMatches), "(.|\n)*apt-get install(.|\n)*")
9697
97 err = ioutil.WriteFile(lxclsPath, nil, 0777)98 err = ioutil.WriteFile(lxclsPath, nil, 0777)
98 c.Assert(err, gc.IsNil)99 c.Assert(err, gc.IsNil)
99 err = VerifyPrerequisites()100 err = VerifyPrerequisites(instance.LXC)
100 c.Assert(err, gc.IsNil)101 c.Assert(err, gc.IsNil)
101}102}
102103
=== modified file 'utils/apt.go'
--- utils/apt.go 2013-11-15 06:36:39 +0000
+++ utils/apt.go 2013-12-02 21:28:17 +0000
@@ -9,6 +9,7 @@
9 "os"9 "os"
10 "os/exec"10 "os/exec"
11 "regexp"11 "regexp"
12 "strings"
1213
13 "launchpad.net/loggo"14 "launchpad.net/loggo"
14)15)
@@ -72,3 +73,19 @@
72 }73 }
73 return string(bytes.Join(aptProxyRE.FindAll(out, -1), []byte("\n"))), nil74 return string(bytes.Join(aptProxyRE.FindAll(out, -1), []byte("\n"))), nil
74}75}
76
77// IsUbuntu executes lxb_release to see if the host OS is Ubuntu.
78func IsUbuntu() bool {
79 out, err := RunCommand("lsb_release", "-i", "-s")
80 if err != nil {
81 return false
82 }
83 return strings.TrimSpace(out) == "Ubuntu"
84}
85
86// IsPackageInstalled uses dpkg-query to determine if the `packageName`
87// package is installed.
88func IsPackageInstalled(packageName string) bool {
89 _, err := RunCommand("dpkg-query", "--status", packageName)
90 return err == nil
91}
7592
=== modified file 'utils/apt_test.go'
--- utils/apt_test.go 2013-11-15 06:36:39 +0000
+++ utils/apt_test.go 2013-12-02 21:28:17 +0000
@@ -1,14 +1,16 @@
1// Copyright 2012, 2013 Canonical Ltd.1// Copyright 2012, 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.2// Licensed under the AGPLv3, see LICENCE file for details.
33
4package utils4package utils_test
55
6import (6import (
7 "fmt"7 "fmt"
88
9 gc "launchpad.net/gocheck"9 gc "launchpad.net/gocheck"
1010
11 jc "launchpad.net/juju-core/testing/checkers"
11 "launchpad.net/juju-core/testing/testbase"12 "launchpad.net/juju-core/testing/testbase"
13 "launchpad.net/juju-core/utils"
12)14)
1315
14type AptSuite struct {16type AptSuite struct {
@@ -18,9 +20,9 @@
18var _ = gc.Suite(&AptSuite{})20var _ = gc.Suite(&AptSuite{})
1921
20func (s *AptSuite) TestOnePackage(c *gc.C) {22func (s *AptSuite) TestOnePackage(c *gc.C) {
21 cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte{}, nil)23 cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil)
22 defer cleanup()24 defer cleanup()
23 err := AptGetInstall("test-package")25 err := utils.AptGetInstall("test-package")
24 c.Assert(err, gc.IsNil)26 c.Assert(err, gc.IsNil)
25 cmd := <-cmdChan27 cmd := <-cmdChan
26 c.Assert(cmd.Args, gc.DeepEquals, []string{28 c.Assert(cmd.Args, gc.DeepEquals, []string{
@@ -35,9 +37,9 @@
35 const expected = `E: frobnicator failure detected`37 const expected = `E: frobnicator failure detected`
36 cmdError := fmt.Errorf("error")38 cmdError := fmt.Errorf("error")
37 cmdExpectedError := fmt.Errorf("apt-get failed: error")39 cmdExpectedError := fmt.Errorf("apt-get failed: error")
38 cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(expected), cmdError)40 cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), cmdError)
39 defer cleanup()41 defer cleanup()
40 err := AptGetInstall("foo")42 err := utils.AptGetInstall("foo")
41 c.Assert(err, gc.DeepEquals, cmdExpectedError)43 c.Assert(err, gc.DeepEquals, cmdExpectedError)
42 cmd := <-cmdChan44 cmd := <-cmdChan
43 c.Assert(cmd.Args, gc.DeepEquals, []string{45 c.Assert(cmd.Args, gc.DeepEquals, []string{
@@ -48,9 +50,9 @@
48}50}
4951
50func (s *AptSuite) TestConfigProxyEmpty(c *gc.C) {52func (s *AptSuite) TestConfigProxyEmpty(c *gc.C) {
51 cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte{}, nil)53 cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil)
52 defer cleanup()54 defer cleanup()
53 out, err := AptConfigProxy()55 out, err := utils.AptConfigProxy()
54 c.Assert(err, gc.IsNil)56 c.Assert(err, gc.IsNil)
55 cmd := <-cmdChan57 cmd := <-cmdChan
56 c.Assert(cmd.Args, gc.DeepEquals, []string{58 c.Assert(cmd.Args, gc.DeepEquals, []string{
@@ -63,9 +65,9 @@
63func (s *AptSuite) TestConfigProxyConfigured(c *gc.C) {65func (s *AptSuite) TestConfigProxyConfigured(c *gc.C) {
64 const expected = `Acquire::http::Proxy "10.0.3.1:3142";66 const expected = `Acquire::http::Proxy "10.0.3.1:3142";
65Acquire::https::Proxy "false";`67Acquire::https::Proxy "false";`
66 cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(expected), nil)68 cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), nil)
67 defer cleanup()69 defer cleanup()
68 out, err := AptConfigProxy()70 out, err := utils.AptConfigProxy()
69 c.Assert(err, gc.IsNil)71 c.Assert(err, gc.IsNil)
70 cmd := <-cmdChan72 cmd := <-cmdChan
71 c.Assert(cmd.Args, gc.DeepEquals, []string{73 c.Assert(cmd.Args, gc.DeepEquals, []string{
@@ -83,9 +85,9 @@
83 expected = `Acquire::http::Proxy "10.0.3.1:3142";85 expected = `Acquire::http::Proxy "10.0.3.1:3142";
84Acquire::https::Proxy "false";`86Acquire::https::Proxy "false";`
85 )87 )
86 cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(output), nil)88 cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(output), nil)
87 defer cleanup()89 defer cleanup()
88 out, err := AptConfigProxy()90 out, err := utils.AptConfigProxy()
89 c.Assert(err, gc.IsNil)91 c.Assert(err, gc.IsNil)
90 cmd := <-cmdChan92 cmd := <-cmdChan
91 c.Assert(cmd.Args, gc.DeepEquals, []string{93 c.Assert(cmd.Args, gc.DeepEquals, []string{
@@ -99,9 +101,9 @@
99 const expected = `E: frobnicator failure detected`101 const expected = `E: frobnicator failure detected`
100 cmdError := fmt.Errorf("error")102 cmdError := fmt.Errorf("error")
101 cmdExpectedError := fmt.Errorf("apt-config failed: error")103 cmdExpectedError := fmt.Errorf("apt-config failed: error")
102 cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(expected), cmdError)104 cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), cmdError)
103 defer cleanup()105 defer cleanup()
104 out, err := AptConfigProxy()106 out, err := utils.AptConfigProxy()
105 c.Assert(err, gc.DeepEquals, cmdExpectedError)107 c.Assert(err, gc.DeepEquals, cmdExpectedError)
106 cmd := <-cmdChan108 cmd := <-cmdChan
107 c.Assert(cmd.Args, gc.DeepEquals, []string{109 c.Assert(cmd.Args, gc.DeepEquals, []string{
@@ -110,3 +112,37 @@
110 })112 })
111 c.Assert(out, gc.Equals, "")113 c.Assert(out, gc.Equals, "")
112}114}
115
116func (s *AptSuite) patchLsbRelease(c *gc.C, name string) {
117 content := fmt.Sprintf("#!/bin/bash --norc\necho %s", name)
118 patchExecutable(s, c.MkDir(), "lsb_release", content)
119}
120
121func (s *AptSuite) TestIsUbuntu(c *gc.C) {
122 s.patchLsbRelease(c, "Ubuntu")
123 c.Assert(utils.IsUbuntu(), jc.IsTrue)
124}
125
126func (s *AptSuite) TestIsNotUbuntu(c *gc.C) {
127 s.patchLsbRelease(c, "Windows NT")
128 c.Assert(utils.IsUbuntu(), jc.IsFalse)
129}
130
131func (s *AptSuite) patchDpkgQuery(c *gc.C, installed bool) {
132 rc := 0
133 if !installed {
134 rc = 1
135 }
136 content := fmt.Sprintf("#!/bin/bash --norc\nexit %v", rc)
137 patchExecutable(s, c.MkDir(), "dpkg-query", content)
138}
139
140func (s *AptSuite) TestIsPackageInstalled(c *gc.C) {
141 s.patchDpkgQuery(c, true)
142 c.Assert(utils.IsPackageInstalled("foo-bar"), jc.IsTrue)
143}
144
145func (s *AptSuite) TestIsPackageNotInstalled(c *gc.C) {
146 s.patchDpkgQuery(c, false)
147 c.Assert(utils.IsPackageInstalled("foo-bar"), jc.IsFalse)
148}
113149
=== added file 'utils/command.go'
--- utils/command.go 1970-01-01 00:00:00 +0000
+++ utils/command.go 2013-12-02 21:28:17 +0000
@@ -0,0 +1,19 @@
1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package utils
5
6import (
7 "os/exec"
8)
9
10// RunCommand executes the command and return the combined output.
11func RunCommand(command string, args ...string) (output string, err error) {
12 cmd := exec.Command(command, args...)
13 out, err := cmd.CombinedOutput()
14 output = string(out)
15 if err != nil {
16 return output, err
17 }
18 return output, nil
19}
020
=== added file 'utils/command_test.go'
--- utils/command_test.go 1970-01-01 00:00:00 +0000
+++ utils/command_test.go 2013-12-02 21:28:17 +0000
@@ -0,0 +1,52 @@
1// Copyright 2012, 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package utils_test
5
6import (
7 "io/ioutil"
8 "path/filepath"
9
10 gc "launchpad.net/gocheck"
11
12 "launchpad.net/juju-core/testing/testbase"
13 "launchpad.net/juju-core/utils"
14)
15
16type EnvironmentPatcher interface {
17 PatchEnvironment(name, value string)
18}
19
20func patchExecutable(patcher EnvironmentPatcher, dir, execName, script string) {
21 patcher.PatchEnvironment("PATH", dir)
22 filename := filepath.Join(dir, execName)
23 ioutil.WriteFile(filename, []byte(script), 0755)
24}
25
26type commandSuite struct {
27 testbase.LoggingSuite
28}
29
30var _ = gc.Suite(&commandSuite{})
31
32func (s *commandSuite) TestRunCommandCombinesOutput(c *gc.C) {
33 content := `#!/bin/bash --norc
34echo stdout
35echo stderr 1>&2
36`
37 patchExecutable(s, c.MkDir(), "test-output", content)
38 output, err := utils.RunCommand("test-output")
39 c.Assert(err, gc.IsNil)
40 c.Assert(output, gc.Equals, "stdout\nstderr\n")
41}
42
43func (s *commandSuite) TestRunCommandNonZeroExit(c *gc.C) {
44 content := `#!/bin/bash --norc
45echo stdout
46exit 42
47`
48 patchExecutable(s, c.MkDir(), "test-output", content)
49 output, err := utils.RunCommand("test-output")
50 c.Assert(err, gc.ErrorMatches, `exit status 42`)
51 c.Assert(output, gc.Equals, "stdout\n")
52}

Subscribers

People subscribed via source and target branches

to status/vote changes: