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 | 4 | package kvm | 4 | package kvm |
6 | 5 | 5 | ||
7 | 6 | import ( | 6 | import ( |
8 | 7 | "fmt" | ||
9 | 8 | "strings" | ||
10 | 9 | |||
11 | 7 | "launchpad.net/juju-core/container" | 10 | "launchpad.net/juju-core/container" |
12 | 8 | "launchpad.net/juju-core/utils" | 11 | "launchpad.net/juju-core/utils" |
13 | 9 | ) | 12 | ) |
14 | @@ -33,3 +36,46 @@ | |||
15 | 33 | func ensureDependencies() error { | 36 | func ensureDependencies() error { |
16 | 34 | return utils.AptGetInstall(requiredPackages...) | 37 | return utils.AptGetInstall(requiredPackages...) |
17 | 35 | } | 38 | } |
18 | 39 | |||
19 | 40 | const kvmNeedsUbuntu = `Sorry, KVM support with the local provider is only supported | ||
20 | 41 | on the Ubuntu OS.` | ||
21 | 42 | |||
22 | 43 | const kvmNotSupported = `KVM is not currently supported with the current settings. | ||
23 | 44 | You could try running 'kvm-ok' yourself as root to get the full rationale as to | ||
24 | 45 | why it isn't supported, or potentially some BIOS settings to change to enable | ||
25 | 46 | KVM support.` | ||
26 | 47 | |||
27 | 48 | const neetToInstallKVMOk = `kvm-ok is not installed. Please install the cpu-checker package. | ||
28 | 49 | sudo apt-get install cpu-checker` | ||
29 | 50 | |||
30 | 51 | const missingKVMDeps = `Some required packages are missing for KVM to work: | ||
31 | 52 | |||
32 | 53 | sudo apt-get install %s | ||
33 | 54 | ` | ||
34 | 55 | |||
35 | 56 | // VerifyKVMEnabled makes sure that the host OS is Ubuntu, and that the required | ||
36 | 57 | // packages are installed, and that the host CPU is able to support KVM. | ||
37 | 58 | func VerifyKVMEnabled() error { | ||
38 | 59 | if !utils.IsUbuntu() { | ||
39 | 60 | return fmt.Errorf(kvmNeedsUbuntu) | ||
40 | 61 | } | ||
41 | 62 | supported, err := IsKVMSupported() | ||
42 | 63 | if err != nil { | ||
43 | 64 | // Missing the kvm-ok package. | ||
44 | 65 | return fmt.Errorf(neetToInstallKVMOk) | ||
45 | 66 | } | ||
46 | 67 | if !supported { | ||
47 | 68 | return fmt.Errorf(kvmNotSupported) | ||
48 | 69 | } | ||
49 | 70 | // Check for other packages needed. | ||
50 | 71 | toInstall := []string{} | ||
51 | 72 | for _, pkg := range requiredPackages { | ||
52 | 73 | if !utils.IsPackageInstalled(pkg) { | ||
53 | 74 | toInstall = append(toInstall, pkg) | ||
54 | 75 | } | ||
55 | 76 | } | ||
56 | 77 | if len(toInstall) > 0 { | ||
57 | 78 | return fmt.Errorf(missingKVMDeps, strings.Join(toInstall, " ")) | ||
58 | 79 | } | ||
59 | 80 | return nil | ||
60 | 81 | } | ||
61 | 36 | 82 | ||
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 | 16 | 16 | ||
67 | 17 | import ( | 17 | import ( |
68 | 18 | "fmt" | 18 | "fmt" |
69 | 19 | "os/exec" | ||
70 | 20 | "regexp" | 19 | "regexp" |
71 | 21 | "strings" | 20 | "strings" |
72 | 21 | |||
73 | 22 | "launchpad.net/juju-core/utils" | ||
74 | 22 | ) | 23 | ) |
75 | 23 | 24 | ||
76 | 24 | var ( | 25 | var ( |
77 | @@ -32,17 +33,9 @@ | |||
78 | 32 | // run the command and return the combined output. | 33 | // run the command and return the combined output. |
79 | 33 | func run(command string, args ...string) (output string, err error) { | 34 | func run(command string, args ...string) (output string, err error) { |
80 | 34 | logger.Tracef("%s %v", command, args) | 35 | logger.Tracef("%s %v", command, args) |
84 | 35 | cmd := exec.Command(command, args...) | 36 | output, err = utils.RunCommand(command, args...) |
82 | 36 | out, err := cmd.CombinedOutput() | ||
83 | 37 | output = string(out) | ||
85 | 38 | logger.Tracef("output: %v", output) | 37 | logger.Tracef("output: %v", output) |
93 | 39 | if err != nil { | 38 | return output, err |
87 | 40 | return output, err | ||
88 | 41 | } | ||
89 | 42 | if !cmd.ProcessState.Success() { | ||
90 | 43 | return output, fmt.Errorf("%s returned non-zero exit", command) | ||
91 | 44 | } | ||
92 | 45 | return output, nil | ||
94 | 46 | } | 39 | } |
95 | 47 | 40 | ||
96 | 48 | // SyncImages updates the local cached images by reading the simplestreams | 41 | // SyncImages updates the local cached images by reading the simplestreams |
97 | 49 | 42 | ||
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 | 9 | "path/filepath" | 9 | "path/filepath" |
103 | 10 | 10 | ||
104 | 11 | "launchpad.net/juju-core/environs/config" | 11 | "launchpad.net/juju-core/environs/config" |
105 | 12 | "launchpad.net/juju-core/instance" | ||
106 | 12 | "launchpad.net/juju-core/schema" | 13 | "launchpad.net/juju-core/schema" |
107 | 13 | "launchpad.net/juju-core/utils" | 14 | "launchpad.net/juju-core/utils" |
108 | 14 | ) | 15 | ) |
109 | @@ -22,6 +23,7 @@ | |||
110 | 22 | "root-dir": schema.String(), | 23 | "root-dir": schema.String(), |
111 | 23 | "bootstrap-ip": schema.String(), | 24 | "bootstrap-ip": schema.String(), |
112 | 24 | "network-bridge": schema.String(), | 25 | "network-bridge": schema.String(), |
113 | 26 | "container": schema.String(), | ||
114 | 25 | "storage-port": schema.ForceInt(), | 27 | "storage-port": schema.ForceInt(), |
115 | 26 | "shared-storage-port": schema.ForceInt(), | 28 | "shared-storage-port": schema.ForceInt(), |
116 | 27 | } | 29 | } |
117 | @@ -32,6 +34,7 @@ | |||
118 | 32 | configDefaults = schema.Defaults{ | 34 | configDefaults = schema.Defaults{ |
119 | 33 | "root-dir": "", | 35 | "root-dir": "", |
120 | 34 | "network-bridge": "lxcbr0", | 36 | "network-bridge": "lxcbr0", |
121 | 37 | "container": string(instance.LXC), | ||
122 | 35 | "bootstrap-ip": schema.Omit, | 38 | "bootstrap-ip": schema.Omit, |
123 | 36 | "storage-port": 8040, | 39 | "storage-port": 8040, |
124 | 37 | "shared-storage-port": 8041, | 40 | "shared-storage-port": 8041, |
125 | @@ -64,7 +67,7 @@ | |||
126 | 64 | 67 | ||
127 | 65 | // Since it is technically possible for two different users on one machine to | 68 | // Since it is technically possible for two different users on one machine to |
128 | 66 | // have the same local provider name, we need to have a simple way to | 69 | // have the same local provider name, we need to have a simple way to |
130 | 67 | // namespace the file locations, but more importantly the lxc containers. | 70 | // namespace the file locations, but more importantly the containers. |
131 | 68 | func (c *environConfig) namespace() string { | 71 | func (c *environConfig) namespace() string { |
132 | 69 | return fmt.Sprintf("%s-%s", c.user, c.Name()) | 72 | return fmt.Sprintf("%s-%s", c.user, c.Name()) |
133 | 70 | } | 73 | } |
134 | @@ -73,6 +76,10 @@ | |||
135 | 73 | return c.attrs["root-dir"].(string) | 76 | return c.attrs["root-dir"].(string) |
136 | 74 | } | 77 | } |
137 | 75 | 78 | ||
138 | 79 | func (c *environConfig) container() instance.ContainerType { | ||
139 | 80 | return instance.ContainerType(c.attrs["container"].(string)) | ||
140 | 81 | } | ||
141 | 82 | |||
142 | 76 | func (c *environConfig) networkBridge() string { | 83 | func (c *environConfig) networkBridge() string { |
143 | 77 | return c.attrs["network-bridge"].(string) | 84 | return c.attrs["network-bridge"].(string) |
144 | 78 | } | 85 | } |
145 | 79 | 86 | ||
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 | 17 | agenttools "launchpad.net/juju-core/agent/tools" | 17 | agenttools "launchpad.net/juju-core/agent/tools" |
151 | 18 | "launchpad.net/juju-core/constraints" | 18 | "launchpad.net/juju-core/constraints" |
152 | 19 | "launchpad.net/juju-core/container" | 19 | "launchpad.net/juju-core/container" |
154 | 20 | "launchpad.net/juju-core/container/lxc" | 20 | "launchpad.net/juju-core/container/factory" |
155 | 21 | "launchpad.net/juju-core/environs" | 21 | "launchpad.net/juju-core/environs" |
156 | 22 | "launchpad.net/juju-core/environs/bootstrap" | 22 | "launchpad.net/juju-core/environs/bootstrap" |
157 | 23 | "launchpad.net/juju-core/environs/cloudinit" | 23 | "launchpad.net/juju-core/environs/cloudinit" |
158 | @@ -194,11 +194,15 @@ | |||
159 | 194 | env.config = ecfg | 194 | env.config = ecfg |
160 | 195 | env.name = ecfg.Name() | 195 | env.name = ecfg.Name() |
161 | 196 | 196 | ||
163 | 197 | env.containerManager = lxc.NewContainerManager( | 197 | env.containerManager, err = factory.NewContainerManager( |
164 | 198 | ecfg.container(), | ||
165 | 198 | container.ManagerConfig{ | 199 | container.ManagerConfig{ |
166 | 199 | Name: env.config.namespace(), | 200 | Name: env.config.namespace(), |
167 | 200 | LogDir: env.config.logDir(), | 201 | LogDir: env.config.logDir(), |
168 | 201 | }) | 202 | }) |
169 | 203 | if err != nil { | ||
170 | 204 | return err | ||
171 | 205 | } | ||
172 | 202 | 206 | ||
173 | 203 | // Here is the end of normal config setting. | 207 | // Here is the end of normal config setting. |
174 | 204 | if ecfg.bootstrapped() { | 208 | if ecfg.bootstrapped() { |
175 | 205 | 209 | ||
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 | 12 | 12 | ||
181 | 13 | "launchpad.net/juju-core/environs" | 13 | "launchpad.net/juju-core/environs" |
182 | 14 | "launchpad.net/juju-core/environs/config" | 14 | "launchpad.net/juju-core/environs/config" |
183 | 15 | "launchpad.net/juju-core/instance" | ||
184 | 15 | "launchpad.net/juju-core/provider" | 16 | "launchpad.net/juju-core/provider" |
185 | 16 | "launchpad.net/juju-core/utils" | 17 | "launchpad.net/juju-core/utils" |
186 | 17 | "launchpad.net/juju-core/version" | 18 | "launchpad.net/juju-core/version" |
187 | @@ -41,7 +42,12 @@ | |||
188 | 41 | } | 42 | } |
189 | 42 | cfg = newCfg | 43 | cfg = newCfg |
190 | 43 | } | 44 | } |
192 | 44 | if err := VerifyPrerequisites(); err != nil { | 45 | // Do the initial validation on the config. |
193 | 46 | localConfig, err := providerInstance.newConfig(cfg) | ||
194 | 47 | if err != nil { | ||
195 | 48 | return nil, err | ||
196 | 49 | } | ||
197 | 50 | if err := VerifyPrerequisites(localConfig.container()); err != nil { | ||
198 | 45 | logger.Errorf("failed verification of local provider prerequisites: %v", err) | 51 | logger.Errorf("failed verification of local provider prerequisites: %v", err) |
199 | 46 | return nil, err | 52 | return nil, err |
200 | 47 | } | 53 | } |
201 | @@ -109,6 +115,11 @@ | |||
202 | 109 | if err != nil { | 115 | if err != nil { |
203 | 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) |
204 | 111 | } | 117 | } |
205 | 118 | if localConfig.container() != oldLocalConfig.container() { | ||
206 | 119 | return nil, fmt.Errorf("cannot change container from %q to %q", | ||
207 | 120 | oldLocalConfig.container(), | ||
208 | 121 | localConfig.container()) | ||
209 | 122 | } | ||
210 | 112 | if localConfig.rootDir() != oldLocalConfig.rootDir() { | 123 | if localConfig.rootDir() != oldLocalConfig.rootDir() { |
211 | 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", |
212 | 114 | oldLocalConfig.rootDir(), | 125 | oldLocalConfig.rootDir(), |
213 | @@ -130,6 +141,10 @@ | |||
214 | 130 | localConfig.sharedStoragePort()) | 141 | localConfig.sharedStoragePort()) |
215 | 131 | } | 142 | } |
216 | 132 | } | 143 | } |
217 | 144 | // Currently only supported containers are "lxc" and "kvm". | ||
218 | 145 | if localConfig.container() != instance.LXC && localConfig.container() != instance.KVM { | ||
219 | 146 | return nil, fmt.Errorf("unsupported container type: %q", localConfig.container()) | ||
220 | 147 | } | ||
221 | 133 | dir := utils.NormalizePath(localConfig.rootDir()) | 148 | dir := utils.NormalizePath(localConfig.rootDir()) |
222 | 134 | if dir == "." { | 149 | if dir == "." { |
223 | 135 | dir = config.JujuHomePath(cfg.Name()) | 150 | dir = config.JujuHomePath(cfg.Name()) |
224 | 136 | 151 | ||
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 | 9 | "os" | 9 | "os" |
230 | 10 | "os/exec" | 10 | "os/exec" |
231 | 11 | "runtime" | 11 | "runtime" |
232 | 12 | "strings" | ||
233 | 13 | 12 | ||
234 | 13 | "launchpad.net/juju-core/container/kvm" | ||
235 | 14 | "launchpad.net/juju-core/instance" | ||
236 | 15 | "launchpad.net/juju-core/utils" | ||
237 | 14 | "launchpad.net/juju-core/version" | 16 | "launchpad.net/juju-core/version" |
238 | 15 | ) | 17 | ) |
239 | 16 | 18 | ||
240 | @@ -61,14 +63,20 @@ | |||
241 | 61 | // VerifyPrerequisites verifies the prerequisites of | 63 | // VerifyPrerequisites verifies the prerequisites of |
242 | 62 | // the local machine (machine 0) for running the local | 64 | // the local machine (machine 0) for running the local |
243 | 63 | // provider. | 65 | // provider. |
245 | 64 | func VerifyPrerequisites() error { | 66 | func VerifyPrerequisites(containerType instance.ContainerType) error { |
246 | 65 | if goos != "linux" { | 67 | if goos != "linux" { |
247 | 66 | return fmt.Errorf(errUnsupportedOS, goos) | 68 | return fmt.Errorf(errUnsupportedOS, goos) |
248 | 67 | } | 69 | } |
249 | 68 | if err := verifyMongod(); err != nil { | 70 | if err := verifyMongod(); err != nil { |
250 | 69 | return err | 71 | return err |
251 | 70 | } | 72 | } |
253 | 71 | return verifyLxc() | 73 | switch containerType { |
254 | 74 | case instance.LXC: | ||
255 | 75 | return verifyLxc() | ||
256 | 76 | case instance.KVM: | ||
257 | 77 | return kvm.VerifyKVMEnabled() | ||
258 | 78 | } | ||
259 | 79 | return fmt.Errorf("Unknown container type specified in the config.") | ||
260 | 72 | } | 80 | } |
261 | 73 | 81 | ||
262 | 74 | func verifyMongod() error { | 82 | func verifyMongod() error { |
263 | @@ -91,16 +99,8 @@ | |||
264 | 91 | return nil | 99 | return nil |
265 | 92 | } | 100 | } |
266 | 93 | 101 | ||
267 | 94 | func isUbuntu() bool { | ||
268 | 95 | out, err := exec.Command("lsb_release", "-i", "-s").CombinedOutput() | ||
269 | 96 | if err != nil { | ||
270 | 97 | return false | ||
271 | 98 | } | ||
272 | 99 | return strings.TrimSpace(string(out)) == "Ubuntu" | ||
273 | 100 | } | ||
274 | 101 | |||
275 | 102 | func wrapMongodNotExist(err error) error { | 102 | func wrapMongodNotExist(err error) error { |
277 | 103 | if isUbuntu() { | 103 | if utils.IsUbuntu() { |
278 | 104 | series := version.Current.Series | 104 | series := version.Current.Series |
279 | 105 | args := []interface{}{err, installMongodUbuntu} | 105 | args := []interface{}{err, installMongodUbuntu} |
280 | 106 | format := "%v\n%s\n%s" | 106 | format := "%v\n%s\n%s" |
281 | @@ -115,7 +115,7 @@ | |||
282 | 115 | } | 115 | } |
283 | 116 | 116 | ||
284 | 117 | func wrapLxcNotFound(err error) error { | 117 | func wrapLxcNotFound(err error) error { |
286 | 118 | if isUbuntu() { | 118 | if utils.IsUbuntu() { |
287 | 119 | return fmt.Errorf("%v\n%s", err, installLxcUbuntu) | 119 | return fmt.Errorf("%v\n%s", err, installLxcUbuntu) |
288 | 120 | } | 120 | } |
289 | 121 | return fmt.Errorf("%v\n%s", err, installLxcGeneric) | 121 | return fmt.Errorf("%v\n%s", err, installLxcGeneric) |
290 | 122 | 122 | ||
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 | 10 | 10 | ||
296 | 11 | gc "launchpad.net/gocheck" | 11 | gc "launchpad.net/gocheck" |
297 | 12 | 12 | ||
298 | 13 | "launchpad.net/juju-core/instance" | ||
299 | 13 | "launchpad.net/juju-core/testing/testbase" | 14 | "launchpad.net/juju-core/testing/testbase" |
300 | 14 | ) | 15 | ) |
301 | 15 | 16 | ||
302 | @@ -59,17 +60,17 @@ | |||
303 | 59 | goos = old | 60 | goos = old |
304 | 60 | }(goos) | 61 | }(goos) |
305 | 61 | goos = "windows" | 62 | goos = "windows" |
307 | 62 | err := VerifyPrerequisites() | 63 | err := VerifyPrerequisites(instance.LXC) |
308 | 63 | c.Assert(err, gc.ErrorMatches, "Unsupported operating system: windows(.|\n)*") | 64 | c.Assert(err, gc.ErrorMatches, "Unsupported operating system: windows(.|\n)*") |
309 | 64 | } | 65 | } |
310 | 65 | 66 | ||
311 | 66 | func (s *prereqsSuite) TestMongoPrereq(c *gc.C) { | 67 | func (s *prereqsSuite) TestMongoPrereq(c *gc.C) { |
313 | 67 | err := VerifyPrerequisites() | 68 | err := VerifyPrerequisites(instance.LXC) |
314 | 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)*") |
315 | 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)*") |
316 | 70 | 71 | ||
317 | 71 | os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu") | 72 | os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu") |
319 | 72 | err = VerifyPrerequisites() | 73 | err = VerifyPrerequisites(instance.LXC) |
320 | 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)*") |
321 | 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)*") |
322 | 75 | 76 | ||
323 | @@ -77,7 +78,7 @@ | |||
324 | 77 | c.Assert(err, gc.IsNil) | 78 | c.Assert(err, gc.IsNil) |
325 | 78 | err = ioutil.WriteFile(filepath.Join(s.tmpdir, "lxc-ls"), nil, 0777) | 79 | err = ioutil.WriteFile(filepath.Join(s.tmpdir, "lxc-ls"), nil, 0777) |
326 | 79 | c.Assert(err, gc.IsNil) | 80 | c.Assert(err, gc.IsNil) |
328 | 80 | err = VerifyPrerequisites() | 81 | err = VerifyPrerequisites(instance.LXC) |
329 | 81 | c.Assert(err, gc.IsNil) | 82 | c.Assert(err, gc.IsNil) |
330 | 82 | } | 83 | } |
331 | 83 | 84 | ||
332 | @@ -85,17 +86,17 @@ | |||
333 | 85 | err := ioutil.WriteFile(mongodPath, nil, 0777) | 86 | err := ioutil.WriteFile(mongodPath, nil, 0777) |
334 | 86 | c.Assert(err, gc.IsNil) | 87 | c.Assert(err, gc.IsNil) |
335 | 87 | 88 | ||
337 | 88 | err = VerifyPrerequisites() | 89 | err = VerifyPrerequisites(instance.LXC) |
338 | 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)*") |
339 | 90 | c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install lxc(.|\n)*") | 91 | c.Assert(err, gc.ErrorMatches, "(.|\n)*apt-get install lxc(.|\n)*") |
340 | 91 | 92 | ||
341 | 92 | os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu") | 93 | os.Setenv("JUJUTEST_LSB_RELEASE_ID", "NotUbuntu") |
343 | 93 | err = VerifyPrerequisites() | 94 | err = VerifyPrerequisites(instance.LXC) |
344 | 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)*") |
345 | 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)*") |
346 | 96 | 97 | ||
347 | 97 | err = ioutil.WriteFile(lxclsPath, nil, 0777) | 98 | err = ioutil.WriteFile(lxclsPath, nil, 0777) |
348 | 98 | c.Assert(err, gc.IsNil) | 99 | c.Assert(err, gc.IsNil) |
350 | 99 | err = VerifyPrerequisites() | 100 | err = VerifyPrerequisites(instance.LXC) |
351 | 100 | c.Assert(err, gc.IsNil) | 101 | c.Assert(err, gc.IsNil) |
352 | 101 | } | 102 | } |
353 | 102 | 103 | ||
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 | 9 | "os" | 9 | "os" |
359 | 10 | "os/exec" | 10 | "os/exec" |
360 | 11 | "regexp" | 11 | "regexp" |
361 | 12 | "strings" | ||
362 | 12 | 13 | ||
363 | 13 | "launchpad.net/loggo" | 14 | "launchpad.net/loggo" |
364 | 14 | ) | 15 | ) |
365 | @@ -72,3 +73,19 @@ | |||
366 | 72 | } | 73 | } |
367 | 73 | return string(bytes.Join(aptProxyRE.FindAll(out, -1), []byte("\n"))), nil | 74 | return string(bytes.Join(aptProxyRE.FindAll(out, -1), []byte("\n"))), nil |
368 | 74 | } | 75 | } |
369 | 76 | |||
370 | 77 | // IsUbuntu executes lxb_release to see if the host OS is Ubuntu. | ||
371 | 78 | func IsUbuntu() bool { | ||
372 | 79 | out, err := RunCommand("lsb_release", "-i", "-s") | ||
373 | 80 | if err != nil { | ||
374 | 81 | return false | ||
375 | 82 | } | ||
376 | 83 | return strings.TrimSpace(out) == "Ubuntu" | ||
377 | 84 | } | ||
378 | 85 | |||
379 | 86 | // IsPackageInstalled uses dpkg-query to determine if the `packageName` | ||
380 | 87 | // package is installed. | ||
381 | 88 | func IsPackageInstalled(packageName string) bool { | ||
382 | 89 | _, err := RunCommand("dpkg-query", "--status", packageName) | ||
383 | 90 | return err == nil | ||
384 | 91 | } | ||
385 | 75 | 92 | ||
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 | 1 | // Copyright 2012, 2013 Canonical Ltd. | 1 | // Copyright 2012, 2013 Canonical Ltd. |
391 | 2 | // Licensed under the AGPLv3, see LICENCE file for details. | 2 | // Licensed under the AGPLv3, see LICENCE file for details. |
392 | 3 | 3 | ||
394 | 4 | package utils | 4 | package utils_test |
395 | 5 | 5 | ||
396 | 6 | import ( | 6 | import ( |
397 | 7 | "fmt" | 7 | "fmt" |
398 | 8 | 8 | ||
399 | 9 | gc "launchpad.net/gocheck" | 9 | gc "launchpad.net/gocheck" |
400 | 10 | 10 | ||
401 | 11 | jc "launchpad.net/juju-core/testing/checkers" | ||
402 | 11 | "launchpad.net/juju-core/testing/testbase" | 12 | "launchpad.net/juju-core/testing/testbase" |
403 | 13 | "launchpad.net/juju-core/utils" | ||
404 | 12 | ) | 14 | ) |
405 | 13 | 15 | ||
406 | 14 | type AptSuite struct { | 16 | type AptSuite struct { |
407 | @@ -18,9 +20,9 @@ | |||
408 | 18 | var _ = gc.Suite(&AptSuite{}) | 20 | var _ = gc.Suite(&AptSuite{}) |
409 | 19 | 21 | ||
410 | 20 | func (s *AptSuite) TestOnePackage(c *gc.C) { | 22 | func (s *AptSuite) TestOnePackage(c *gc.C) { |
412 | 21 | cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte{}, nil) | 23 | cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) |
413 | 22 | defer cleanup() | 24 | defer cleanup() |
415 | 23 | err := AptGetInstall("test-package") | 25 | err := utils.AptGetInstall("test-package") |
416 | 24 | c.Assert(err, gc.IsNil) | 26 | c.Assert(err, gc.IsNil) |
417 | 25 | cmd := <-cmdChan | 27 | cmd := <-cmdChan |
418 | 26 | c.Assert(cmd.Args, gc.DeepEquals, []string{ | 28 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
419 | @@ -35,9 +37,9 @@ | |||
420 | 35 | const expected = `E: frobnicator failure detected` | 37 | const expected = `E: frobnicator failure detected` |
421 | 36 | cmdError := fmt.Errorf("error") | 38 | cmdError := fmt.Errorf("error") |
422 | 37 | cmdExpectedError := fmt.Errorf("apt-get failed: error") | 39 | cmdExpectedError := fmt.Errorf("apt-get failed: error") |
424 | 38 | cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(expected), cmdError) | 40 | cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), cmdError) |
425 | 39 | defer cleanup() | 41 | defer cleanup() |
427 | 40 | err := AptGetInstall("foo") | 42 | err := utils.AptGetInstall("foo") |
428 | 41 | c.Assert(err, gc.DeepEquals, cmdExpectedError) | 43 | c.Assert(err, gc.DeepEquals, cmdExpectedError) |
429 | 42 | cmd := <-cmdChan | 44 | cmd := <-cmdChan |
430 | 43 | c.Assert(cmd.Args, gc.DeepEquals, []string{ | 45 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
431 | @@ -48,9 +50,9 @@ | |||
432 | 48 | } | 50 | } |
433 | 49 | 51 | ||
434 | 50 | func (s *AptSuite) TestConfigProxyEmpty(c *gc.C) { | 52 | func (s *AptSuite) TestConfigProxyEmpty(c *gc.C) { |
436 | 51 | cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte{}, nil) | 53 | cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte{}, nil) |
437 | 52 | defer cleanup() | 54 | defer cleanup() |
439 | 53 | out, err := AptConfigProxy() | 55 | out, err := utils.AptConfigProxy() |
440 | 54 | c.Assert(err, gc.IsNil) | 56 | c.Assert(err, gc.IsNil) |
441 | 55 | cmd := <-cmdChan | 57 | cmd := <-cmdChan |
442 | 56 | c.Assert(cmd.Args, gc.DeepEquals, []string{ | 58 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
443 | @@ -63,9 +65,9 @@ | |||
444 | 63 | func (s *AptSuite) TestConfigProxyConfigured(c *gc.C) { | 65 | func (s *AptSuite) TestConfigProxyConfigured(c *gc.C) { |
445 | 64 | const expected = `Acquire::http::Proxy "10.0.3.1:3142"; | 66 | const expected = `Acquire::http::Proxy "10.0.3.1:3142"; |
446 | 65 | Acquire::https::Proxy "false";` | 67 | Acquire::https::Proxy "false";` |
448 | 66 | cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(expected), nil) | 68 | cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), nil) |
449 | 67 | defer cleanup() | 69 | defer cleanup() |
451 | 68 | out, err := AptConfigProxy() | 70 | out, err := utils.AptConfigProxy() |
452 | 69 | c.Assert(err, gc.IsNil) | 71 | c.Assert(err, gc.IsNil) |
453 | 70 | cmd := <-cmdChan | 72 | cmd := <-cmdChan |
454 | 71 | c.Assert(cmd.Args, gc.DeepEquals, []string{ | 73 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
455 | @@ -83,9 +85,9 @@ | |||
456 | 83 | expected = `Acquire::http::Proxy "10.0.3.1:3142"; | 85 | expected = `Acquire::http::Proxy "10.0.3.1:3142"; |
457 | 84 | Acquire::https::Proxy "false";` | 86 | Acquire::https::Proxy "false";` |
458 | 85 | ) | 87 | ) |
460 | 86 | cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(output), nil) | 88 | cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(output), nil) |
461 | 87 | defer cleanup() | 89 | defer cleanup() |
463 | 88 | out, err := AptConfigProxy() | 90 | out, err := utils.AptConfigProxy() |
464 | 89 | c.Assert(err, gc.IsNil) | 91 | c.Assert(err, gc.IsNil) |
465 | 90 | cmd := <-cmdChan | 92 | cmd := <-cmdChan |
466 | 91 | c.Assert(cmd.Args, gc.DeepEquals, []string{ | 93 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
467 | @@ -99,9 +101,9 @@ | |||
468 | 99 | const expected = `E: frobnicator failure detected` | 101 | const expected = `E: frobnicator failure detected` |
469 | 100 | cmdError := fmt.Errorf("error") | 102 | cmdError := fmt.Errorf("error") |
470 | 101 | cmdExpectedError := fmt.Errorf("apt-config failed: error") | 103 | cmdExpectedError := fmt.Errorf("apt-config failed: error") |
472 | 102 | cmdChan, cleanup := testbase.HookCommandOutput(&AptCommandOutput, []byte(expected), cmdError) | 104 | cmdChan, cleanup := testbase.HookCommandOutput(&utils.AptCommandOutput, []byte(expected), cmdError) |
473 | 103 | defer cleanup() | 105 | defer cleanup() |
475 | 104 | out, err := AptConfigProxy() | 106 | out, err := utils.AptConfigProxy() |
476 | 105 | c.Assert(err, gc.DeepEquals, cmdExpectedError) | 107 | c.Assert(err, gc.DeepEquals, cmdExpectedError) |
477 | 106 | cmd := <-cmdChan | 108 | cmd := <-cmdChan |
478 | 107 | c.Assert(cmd.Args, gc.DeepEquals, []string{ | 109 | c.Assert(cmd.Args, gc.DeepEquals, []string{ |
479 | @@ -110,3 +112,37 @@ | |||
480 | 110 | }) | 112 | }) |
481 | 111 | c.Assert(out, gc.Equals, "") | 113 | c.Assert(out, gc.Equals, "") |
482 | 112 | } | 114 | } |
483 | 115 | |||
484 | 116 | func (s *AptSuite) patchLsbRelease(c *gc.C, name string) { | ||
485 | 117 | content := fmt.Sprintf("#!/bin/bash --norc\necho %s", name) | ||
486 | 118 | patchExecutable(s, c.MkDir(), "lsb_release", content) | ||
487 | 119 | } | ||
488 | 120 | |||
489 | 121 | func (s *AptSuite) TestIsUbuntu(c *gc.C) { | ||
490 | 122 | s.patchLsbRelease(c, "Ubuntu") | ||
491 | 123 | c.Assert(utils.IsUbuntu(), jc.IsTrue) | ||
492 | 124 | } | ||
493 | 125 | |||
494 | 126 | func (s *AptSuite) TestIsNotUbuntu(c *gc.C) { | ||
495 | 127 | s.patchLsbRelease(c, "Windows NT") | ||
496 | 128 | c.Assert(utils.IsUbuntu(), jc.IsFalse) | ||
497 | 129 | } | ||
498 | 130 | |||
499 | 131 | func (s *AptSuite) patchDpkgQuery(c *gc.C, installed bool) { | ||
500 | 132 | rc := 0 | ||
501 | 133 | if !installed { | ||
502 | 134 | rc = 1 | ||
503 | 135 | } | ||
504 | 136 | content := fmt.Sprintf("#!/bin/bash --norc\nexit %v", rc) | ||
505 | 137 | patchExecutable(s, c.MkDir(), "dpkg-query", content) | ||
506 | 138 | } | ||
507 | 139 | |||
508 | 140 | func (s *AptSuite) TestIsPackageInstalled(c *gc.C) { | ||
509 | 141 | s.patchDpkgQuery(c, true) | ||
510 | 142 | c.Assert(utils.IsPackageInstalled("foo-bar"), jc.IsTrue) | ||
511 | 143 | } | ||
512 | 144 | |||
513 | 145 | func (s *AptSuite) TestIsPackageNotInstalled(c *gc.C) { | ||
514 | 146 | s.patchDpkgQuery(c, false) | ||
515 | 147 | c.Assert(utils.IsPackageInstalled("foo-bar"), jc.IsFalse) | ||
516 | 148 | } | ||
517 | 113 | 149 | ||
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 | 1 | // Copyright 2013 Canonical Ltd. | ||
523 | 2 | // Licensed under the AGPLv3, see LICENCE file for details. | ||
524 | 3 | |||
525 | 4 | package utils | ||
526 | 5 | |||
527 | 6 | import ( | ||
528 | 7 | "os/exec" | ||
529 | 8 | ) | ||
530 | 9 | |||
531 | 10 | // RunCommand executes the command and return the combined output. | ||
532 | 11 | func RunCommand(command string, args ...string) (output string, err error) { | ||
533 | 12 | cmd := exec.Command(command, args...) | ||
534 | 13 | out, err := cmd.CombinedOutput() | ||
535 | 14 | output = string(out) | ||
536 | 15 | if err != nil { | ||
537 | 16 | return output, err | ||
538 | 17 | } | ||
539 | 18 | return output, nil | ||
540 | 19 | } | ||
541 | 0 | 20 | ||
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 | 1 | // Copyright 2012, 2013 Canonical Ltd. | ||
547 | 2 | // Licensed under the AGPLv3, see LICENCE file for details. | ||
548 | 3 | |||
549 | 4 | package utils_test | ||
550 | 5 | |||
551 | 6 | import ( | ||
552 | 7 | "io/ioutil" | ||
553 | 8 | "path/filepath" | ||
554 | 9 | |||
555 | 10 | gc "launchpad.net/gocheck" | ||
556 | 11 | |||
557 | 12 | "launchpad.net/juju-core/testing/testbase" | ||
558 | 13 | "launchpad.net/juju-core/utils" | ||
559 | 14 | ) | ||
560 | 15 | |||
561 | 16 | type EnvironmentPatcher interface { | ||
562 | 17 | PatchEnvironment(name, value string) | ||
563 | 18 | } | ||
564 | 19 | |||
565 | 20 | func patchExecutable(patcher EnvironmentPatcher, dir, execName, script string) { | ||
566 | 21 | patcher.PatchEnvironment("PATH", dir) | ||
567 | 22 | filename := filepath.Join(dir, execName) | ||
568 | 23 | ioutil.WriteFile(filename, []byte(script), 0755) | ||
569 | 24 | } | ||
570 | 25 | |||
571 | 26 | type commandSuite struct { | ||
572 | 27 | testbase.LoggingSuite | ||
573 | 28 | } | ||
574 | 29 | |||
575 | 30 | var _ = gc.Suite(&commandSuite{}) | ||
576 | 31 | |||
577 | 32 | func (s *commandSuite) TestRunCommandCombinesOutput(c *gc.C) { | ||
578 | 33 | content := `#!/bin/bash --norc | ||
579 | 34 | echo stdout | ||
580 | 35 | echo stderr 1>&2 | ||
581 | 36 | ` | ||
582 | 37 | patchExecutable(s, c.MkDir(), "test-output", content) | ||
583 | 38 | output, err := utils.RunCommand("test-output") | ||
584 | 39 | c.Assert(err, gc.IsNil) | ||
585 | 40 | c.Assert(output, gc.Equals, "stdout\nstderr\n") | ||
586 | 41 | } | ||
587 | 42 | |||
588 | 43 | func (s *commandSuite) TestRunCommandNonZeroExit(c *gc.C) { | ||
589 | 44 | content := `#!/bin/bash --norc | ||
590 | 45 | echo stdout | ||
591 | 46 | exit 42 | ||
592 | 47 | ` | ||
593 | 48 | patchExecutable(s, c.MkDir(), "test-output", content) | ||
594 | 49 | output, err := utils.RunCommand("test-output") | ||
595 | 50 | c.Assert(err, gc.ErrorMatches, `exit status 42`) | ||
596 | 51 | c.Assert(output, gc.Equals, "stdout\n") | ||
597 | 52 | } |
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/