Merge lp:~thumper/golxc/clone into lp:golxc

Proposed by Tim Penhey
Status: Merged
Merged at revision: 8
Proposed branch: lp:~thumper/golxc/clone
Merge into: lp:golxc
Diff against target: 432 lines (+149/-48)
3 files modified
golxc.go (+50/-11)
golxc_test.go (+85/-37)
package_test.go (+14/-0)
To merge this branch: bzr merge lp:~thumper/golxc/clone
Reviewer Review Type Date Requested Status
Ian Booth Approve
Review via email: mp+209790@code.launchpad.net

Description of the change

Update signature for Create and Clone

Make it possible for the caller to specify additional args for both
Create and Clone. Instead of trying to provide an abstracted
interface for this, just allowing the caller to provide extra
args.

Some test cleanup.

To post a comment you must log in.
lp:~thumper/golxc/clone updated
10. By Tim Penhey

Add a test to show command line args.

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

Nice.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'golxc.go'
--- golxc.go 2014-03-05 03:36:19 +0000
+++ golxc.go 2014-03-11 00:35:14 +0000
@@ -11,10 +11,15 @@
11 "fmt"11 "fmt"
12 "os"12 "os"
13 "os/exec"13 "os/exec"
14 "path"
14 "strconv"15 "strconv"
15 "strings"16 "strings"
17
18 "github.com/juju/loggo"
16)19)
1720
21var logger = loggo.GetLogger("golxc")
22
18// Error reports the failure of a LXC command.23// Error reports the failure of a LXC command.
19type Error struct {24type Error struct {
20 Name string25 Name string
@@ -65,7 +70,7 @@
65 Name() string70 Name() string
6671
67 // Create creates a new container based on the given template.72 // Create creates a new container based on the given template.
68 Create(configFile, template string, templateArgs ...string) error73 Create(configFile, template string, extraArgs []string, templateArgs []string) error
6974
70 // Start runs the container as a daemon.75 // Start runs the container as a daemon.
71 Start(configFile, consoleFile string) error76 Start(configFile, consoleFile string) error
@@ -74,7 +79,7 @@
74 Stop() error79 Stop() error
7580
76 // Clone creates a copy of the container, giving the copy the specified name.81 // Clone creates a copy of the container, giving the copy the specified name.
77 Clone(name string) (Container, error)82 Clone(name string, extraArgs []string, templateArgs []string) (Container, error)
7883
79 // Freeze freezes all the container's processes.84 // Freeze freezes all the container's processes.
80 Freeze() error85 Freeze() error
@@ -127,18 +132,26 @@
127 return &containerFactory{}132 return &containerFactory{}
128}133}
129134
135const DefaultLXCDir = "/var/lib/lxc"
136
137var ContainerDir = DefaultLXCDir
138
130type container struct {139type container struct {
131 name string140 name string
132 logFile string141 logFile string
133 logLevel LogLevel142 logLevel LogLevel
143 // Newer LXC libraries can have containers in non-default locations. The
144 // containerDir is the directory that is the 'home' of this container.
145 containerDir string
134}146}
135147
136type containerFactory struct{}148type containerFactory struct{}
137149
138func (*containerFactory) New(name string) Container {150func (*containerFactory) New(name string) Container {
139 return &container{151 return &container{
140 name: name,152 name: name,
141 logLevel: LogWarning,153 logLevel: LogWarning,
154 containerDir: ContainerDir,
142 }155 }
143}156}
144157
@@ -179,7 +192,7 @@
179}192}
180193
181// Create creates a new container based on the given template.194// Create creates a new container based on the given template.
182func (c *container) Create(configFile, template string, templateArgs ...string) error {195func (c *container) Create(configFile, template string, extraArgs []string, templateArgs []string) error {
183 if c.IsConstructed() {196 if c.IsConstructed() {
184 return fmt.Errorf("container %q is already created", c.Name())197 return fmt.Errorf("container %q is already created", c.Name())
185 }198 }
@@ -190,8 +203,12 @@
190 if configFile != "" {203 if configFile != "" {
191 args = append(args, "-f", configFile)204 args = append(args, "-f", configFile)
192 }205 }
193 args = append(args, "--")206 if len(extraArgs) != 0 {
207 args = append(args, extraArgs...)
208 }
194 if len(templateArgs) != 0 {209 if len(templateArgs) != 0 {
210 // Must be done in two steps due to current language implementation details.
211 args = append(args, "--")
195 args = append(args, templateArgs...)212 args = append(args, templateArgs...)
196 }213 }
197 _, err := run("lxc-create", args...)214 _, err := run("lxc-create", args...)
@@ -223,7 +240,13 @@
223 if err != nil {240 if err != nil {
224 return err241 return err
225 }242 }
226 return c.Wait(StateRunning, StateStopped)243 if err := c.Wait(StateRunning, StateStopped); err != nil {
244 return err
245 }
246 if !c.IsRunning() {
247 return fmt.Errorf("container failed to start")
248 }
249 return nil
227}250}
228251
229// Stop terminates the running container.252// Stop terminates the running container.
@@ -249,10 +272,13 @@
249}272}
250273
251// Clone creates a copy of the container, it gets the given name.274// Clone creates a copy of the container, it gets the given name.
252func (c *container) Clone(name string) (Container, error) {275func (c *container) Clone(name string, extraArgs []string, templateArgs []string) (Container, error) {
253 if !c.IsConstructed() {276 if !c.IsConstructed() {
254 return nil, fmt.Errorf("container %q is not yet created", c.name)277 return nil, fmt.Errorf("container %q is not yet created", c.name)
255 }278 }
279 if c.IsRunning() {
280 return nil, fmt.Errorf("cannot clone a running container")
281 }
256 cc := &container{282 cc := &container{
257 name: name,283 name: name,
258 }284 }
@@ -263,6 +289,14 @@
263 "-o", c.name,289 "-o", c.name,
264 "-n", name,290 "-n", name,
265 }291 }
292 if len(extraArgs) != 0 {
293 args = append(args, extraArgs...)
294 }
295 if len(templateArgs) != 0 {
296 // Must be done in two steps due to current language implementation details.
297 args = append(args, "--")
298 args = append(args, templateArgs...)
299 }
266 _, err := run("lxc-clone", args...)300 _, err := run("lxc-clone", args...)
267 if err != nil {301 if err != nil {
268 return nil, err302 return nil, err
@@ -388,27 +422,32 @@
388422
389// containerHome returns the name of the container directory.423// containerHome returns the name of the container directory.
390func (c *container) containerHome() string {424func (c *container) containerHome() string {
391 return "/var/lib/lxc/" + c.name425 return path.Join(c.containerDir, c.name)
392}426}
393427
394// rootfs returns the name of the directory containing the428// rootfs returns the name of the directory containing the
395// root filesystem of the container.429// root filesystem of the container.
396func (c *container) rootfs() string {430func (c *container) rootfs() string {
397 return c.containerHome() + "/rootfs/"431 return path.Join(c.containerHome(), "rootfs")
398}432}
399433
400// run executes the passed command and returns the out.434// run executes the passed command and returns the out.
401func run(name string, args ...string) (string, error) {435func run(name string, args ...string) (string, error) {
436 logger := loggo.GetLogger(fmt.Sprintf("golxc.run.%s", name))
437 logger.Tracef("run: %s %v", name, args)
402 cmd := exec.Command(name, args...)438 cmd := exec.Command(name, args...)
403 // LXC tools do not use stdout and stderr in a predictable439 // LXC tools do not use stdout and stderr in a predictable
404 // way; based on experimentation, the most convenient440 // way; based on experimentation, the most convenient
405 // solution is to combine them and leave the client to441 // solution is to combine them and leave the client to
406 // determine sanity as best it can.442 // determine sanity as best it can.
407 out, err := cmd.CombinedOutput()443 out, err := cmd.CombinedOutput()
444 result := string(out)
408 if err != nil {445 if err != nil {
446 logger.Tracef("run failed output: %s", result)
409 return "", runError(name, err, out)447 return "", runError(name, err, out)
410 }448 }
411 return string(out), nil449 logger.Tracef("run successful output: %s", result)
450 return result, nil
412}451}
413452
414// runError creates an error if run fails.453// runError creates an error if run fails.
415454
=== modified file 'golxc_test.go'
--- golxc_test.go 2014-03-05 03:36:19 +0000
+++ golxc_test.go 2014-03-11 00:35:14 +0000
@@ -5,17 +5,18 @@
55
6import (6import (
7 "io/ioutil"7 "io/ioutil"
8 . "launchpad.net/gocheck"
9 "os"8 "os"
10 "os/user"9 "os/user"
11 "path/filepath"10 "path/filepath"
12 "testing"11
12 "github.com/juju/testing"
13 jc "github.com/juju/testing/checkers"
14 "github.com/juju/testing/logging"
15 . "launchpad.net/gocheck"
1316
14 "launchpad.net/golxc"17 "launchpad.net/golxc"
15)18)
1619
17func Test(t *testing.T) { TestingT(t) }
18
19var lxcfile = `# MIRROR to be used by ubuntu template at container creation:20var lxcfile = `# MIRROR to be used by ubuntu template at container creation:
20# Leaving it undefined is fine21# Leaving it undefined is fine
21#MIRROR="http://archive.ubuntu.com/ubuntu"22#MIRROR="http://archive.ubuntu.com/ubuntu"
@@ -142,6 +143,15 @@
142 }143 }
143}144}
144145
146func (s *LXCSuite) createContainer(c *C) golxc.Container {
147 container := s.factory.New("golxc")
148 c.Assert(container.IsConstructed(), Equals, false)
149 err := container.Create("", "ubuntu", nil, nil)
150 c.Assert(err, IsNil)
151 c.Assert(container.IsConstructed(), Equals, true)
152 return container
153}
154
145func (s *LXCSuite) TestCreateDestroy(c *C) {155func (s *LXCSuite) TestCreateDestroy(c *C) {
146 // Test clean creation and destroying of a container.156 // Test clean creation and destroying of a container.
147 lc := s.factory.New("golxc")157 lc := s.factory.New("golxc")
@@ -149,7 +159,7 @@
149 home := golxc.ContainerHome(lc)159 home := golxc.ContainerHome(lc)
150 _, err := os.Stat(home)160 _, err := os.Stat(home)
151 c.Assert(err, ErrorMatches, "stat .*: no such file or directory")161 c.Assert(err, ErrorMatches, "stat .*: no such file or directory")
152 err = lc.Create("", "ubuntu")162 err = lc.Create("", "ubuntu", nil, nil)
153 c.Assert(err, IsNil)163 c.Assert(err, IsNil)
154 c.Assert(lc.IsConstructed(), Equals, true)164 c.Assert(lc.IsConstructed(), Equals, true)
155 defer func() {165 defer func() {
@@ -165,16 +175,13 @@
165175
166func (s *LXCSuite) TestCreateTwice(c *C) {176func (s *LXCSuite) TestCreateTwice(c *C) {
167 // Test that a container cannot be created twice.177 // Test that a container cannot be created twice.
168 lc1 := s.factory.New("golxc")178 lc1 := s.createContainer(c)
169 c.Assert(lc1.IsConstructed(), Equals, false)
170 err := lc1.Create("", "ubuntu")
171 c.Assert(err, IsNil)
172 c.Assert(lc1.IsConstructed(), Equals, true)179 c.Assert(lc1.IsConstructed(), Equals, true)
173 defer func() {180 defer func() {
174 c.Assert(lc1.Destroy(), IsNil)181 c.Assert(lc1.Destroy(), IsNil)
175 }()182 }()
176 lc2 := s.factory.New("golxc")183 lc2 := s.factory.New("golxc")
177 err = lc2.Create("", "ubuntu")184 err := lc2.Create("", "ubuntu", nil, nil)
178 c.Assert(err, ErrorMatches, "container .* is already created")185 c.Assert(err, ErrorMatches, "container .* is already created")
179}186}
180187
@@ -183,7 +190,7 @@
183 // case of an illegal template.190 // case of an illegal template.
184 lc := s.factory.New("golxc")191 lc := s.factory.New("golxc")
185 c.Assert(lc.IsConstructed(), Equals, false)192 c.Assert(lc.IsConstructed(), Equals, false)
186 err := lc.Create("", "name-of-a-not-existing-template-for-golxc")193 err := lc.Create("", "name-of-a-not-existing-template-for-golxc", nil, nil)
187 c.Assert(err, ErrorMatches, `error executing "lxc-create": .*bad template.*`)194 c.Assert(err, ErrorMatches, `error executing "lxc-create": .*bad template.*`)
188 c.Assert(lc.IsConstructed(), Equals, false)195 c.Assert(lc.IsConstructed(), Equals, false)
189}196}
@@ -211,10 +218,7 @@
211 oldLen := len(lcs)218 oldLen := len(lcs)
212 c.Assert(err, IsNil)219 c.Assert(err, IsNil)
213 c.Assert(oldLen >= 0, Equals, true)220 c.Assert(oldLen >= 0, Equals, true)
214 lc := s.factory.New("golxc")221 lc := s.createContainer(c)
215 c.Assert(lc.IsConstructed(), Equals, false)
216 c.Assert(lc.Create("", "ubuntu"), IsNil)
217 c.Assert(lc.IsConstructed(), Equals, true)
218 defer func() {222 defer func() {
219 c.Assert(lc.Destroy(), IsNil)223 c.Assert(lc.Destroy(), IsNil)
220 }()224 }()
@@ -226,16 +230,13 @@
226230
227func (s *LXCSuite) TestClone(c *C) {231func (s *LXCSuite) TestClone(c *C) {
228 // Test the cloning of an existing container.232 // Test the cloning of an existing container.
229 lc1 := s.factory.New("golxc")233 lc1 := s.createContainer(c)
230 c.Assert(lc1.IsConstructed(), Equals, false)
231 c.Assert(lc1.Create("", "ubuntu"), IsNil)
232 c.Assert(lc1.IsConstructed(), Equals, true)
233 defer func() {234 defer func() {
234 c.Assert(lc1.Destroy(), IsNil)235 c.Assert(lc1.Destroy(), IsNil)
235 }()236 }()
236 lcs, _ := s.factory.List()237 lcs, _ := s.factory.List()
237 oldLen := len(lcs)238 oldLen := len(lcs)
238 lc2, err := lc1.Clone("golxcclone")239 lc2, err := lc1.Clone("golxcclone", nil, nil)
239 c.Assert(err, IsNil)240 c.Assert(err, IsNil)
240 c.Assert(lc2.IsConstructed(), Equals, true)241 c.Assert(lc2.IsConstructed(), Equals, true)
241 defer func() {242 defer func() {
@@ -252,15 +253,13 @@
252 // Test the cloning of a non-existing container.253 // Test the cloning of a non-existing container.
253 lc := s.factory.New("golxc")254 lc := s.factory.New("golxc")
254 c.Assert(lc.IsConstructed(), Equals, false)255 c.Assert(lc.IsConstructed(), Equals, false)
255 _, err := lc.Clone("golxcclone")256 _, err := lc.Clone("golxcclone", nil, nil)
256 c.Assert(err, ErrorMatches, "container .* is not yet created")257 c.Assert(err, ErrorMatches, "container .* is not yet created")
257}258}
258259
259func (s *LXCSuite) TestStartStop(c *C) {260func (s *LXCSuite) TestStartStop(c *C) {
260 // Test starting and stopping a container.261 // Test starting and stopping a container.
261 lc := s.factory.New("golxc")262 lc := s.createContainer(c)
262 c.Assert(lc.IsConstructed(), Equals, false)
263 c.Assert(lc.Create("", "ubuntu"), IsNil)
264 defer func() {263 defer func() {
265 c.Assert(lc.Destroy(), IsNil)264 c.Assert(lc.Destroy(), IsNil)
266 }()265 }()
@@ -279,9 +278,7 @@
279278
280func (s *LXCSuite) TestStopNotRunning(c *C) {279func (s *LXCSuite) TestStopNotRunning(c *C) {
281 // Test that a not running container can't be stopped.280 // Test that a not running container can't be stopped.
282 lc := s.factory.New("golxc")281 lc := s.createContainer(c)
283 c.Assert(lc.IsConstructed(), Equals, false)
284 c.Assert(lc.Create("", "ubuntu"), IsNil)
285 defer func() {282 defer func() {
286 c.Assert(lc.Destroy(), IsNil)283 c.Assert(lc.Destroy(), IsNil)
287 }()284 }()
@@ -296,7 +293,7 @@
296 c.Assert(lc.Wait(), ErrorMatches, "no states specified")293 c.Assert(lc.Wait(), ErrorMatches, "no states specified")
297 c.Assert(lc.Wait(golxc.StateStopped), IsNil)294 c.Assert(lc.Wait(golxc.StateStopped), IsNil)
298 c.Assert(lc.Wait(golxc.StateStopped, golxc.StateRunning), IsNil)295 c.Assert(lc.Wait(golxc.StateStopped, golxc.StateRunning), IsNil)
299 c.Assert(lc.Create("", "ubuntu"), IsNil)296 c.Assert(lc.Create("", "ubuntu", nil, nil), IsNil)
300 defer func() {297 defer func() {
301 c.Assert(lc.Destroy(), IsNil)298 c.Assert(lc.Destroy(), IsNil)
302 }()299 }()
@@ -308,9 +305,7 @@
308305
309func (s *LXCSuite) TestFreezeUnfreeze(c *C) {306func (s *LXCSuite) TestFreezeUnfreeze(c *C) {
310 // Test the freezing and unfreezing of a started container.307 // Test the freezing and unfreezing of a started container.
311 lc := s.factory.New("golxc")308 lc := s.createContainer(c)
312 c.Assert(lc.IsConstructed(), Equals, false)
313 c.Assert(lc.Create("", "ubuntu"), IsNil)
314 defer func() {309 defer func() {
315 c.Assert(lc.Destroy(), IsNil)310 c.Assert(lc.Destroy(), IsNil)
316 }()311 }()
@@ -327,9 +322,7 @@
327322
328func (s *LXCSuite) TestFreezeNotStarted(c *C) {323func (s *LXCSuite) TestFreezeNotStarted(c *C) {
329 // Test that a not running container can't be frozen.324 // Test that a not running container can't be frozen.
330 lc := s.factory.New("golxc")325 lc := s.createContainer(c)
331 c.Assert(lc.IsConstructed(), Equals, false)
332 c.Assert(lc.Create("", "ubuntu"), IsNil)
333 defer func() {326 defer func() {
334 c.Assert(lc.Destroy(), IsNil)327 c.Assert(lc.Destroy(), IsNil)
335 }()328 }()
@@ -352,9 +345,7 @@
352345
353func (s *LXCSuite) TestUnfreezeNotFrozen(c *C) {346func (s *LXCSuite) TestUnfreezeNotFrozen(c *C) {
354 // Test that a running container can't be unfrozen.347 // Test that a running container can't be unfrozen.
355 lc := s.factory.New("golxc")348 lc := s.createContainer(c)
356 c.Assert(lc.IsConstructed(), Equals, false)
357 c.Assert(lc.Create("", "ubuntu"), IsNil)
358 defer func() {349 defer func() {
359 c.Assert(lc.Destroy(), IsNil)350 c.Assert(lc.Destroy(), IsNil)
360 }()351 }()
@@ -364,3 +355,60 @@
364 }()355 }()
365 c.Assert(lc.Unfreeze(), ErrorMatches, "container .* is not frozen")356 c.Assert(lc.Unfreeze(), ErrorMatches, "container .* is not frozen")
366}357}
358
359type commandArgs struct {
360 logging.LoggingSuite
361}
362
363var _ = Suite(&commandArgs{})
364
365func (s *commandArgs) TestCreateArgs(c *C) {
366 s.PatchValue(&golxc.ContainerDir, c.MkDir())
367 testing.PatchExecutableAsEchoArgs(c, s, "lxc-create")
368
369 factory := golxc.Factory()
370 container := factory.New("test")
371 err := container.Create(
372 "config-file", "template",
373 []string{"extra-1", "extra-2"},
374 []string{"template-1", "template-2"},
375 )
376 c.Assert(err, IsNil)
377 testing.AssertEchoArgs(
378 c, "lxc-create",
379 "-n", "test",
380 "-t", "template",
381 "-f", "config-file",
382 "extra-1", "extra-2",
383 "--", "template-1", "template-2")
384}
385
386func (s *commandArgs) TestCloneArgs(c *C) {
387 dir := c.MkDir()
388 s.PatchValue(&golxc.ContainerDir, dir)
389 // Patch lxc-info too as clone checks to see if it is running.
390 testing.PatchExecutableAsEchoArgs(c, s, "lxc-info")
391 testing.PatchExecutableAsEchoArgs(c, s, "lxc-clone")
392
393 factory := golxc.Factory()
394 container := factory.New("test")
395 // Make the rootfs for the "test" container so it thinks it is created.
396 rootfs := filepath.Join(dir, "test", "rootfs")
397 err := os.MkdirAll(rootfs, 0755)
398 c.Assert(err, IsNil)
399 c.Assert(rootfs, jc.IsDirectory)
400 c.Assert(container.IsConstructed(), jc.IsTrue)
401 clone, err := container.Clone(
402 "name",
403 []string{"extra-1", "extra-2"},
404 []string{"template-1", "template-2"},
405 )
406 c.Assert(err, IsNil)
407 testing.AssertEchoArgs(
408 c, "lxc-clone",
409 "-o", "test",
410 "-n", "name",
411 "extra-1", "extra-2",
412 "--", "template-1", "template-2")
413 c.Assert(clone.Name(), Equals, "name")
414}
367415
=== added file 'package_test.go'
--- package_test.go 1970-01-01 00:00:00 +0000
+++ package_test.go 2014-03-11 00:35:14 +0000
@@ -0,0 +1,14 @@
1// Copyright 2013 Canonical Ltd.
2// Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details.
3
4package golxc_test
5
6import (
7 "testing"
8
9 gc "launchpad.net/gocheck"
10)
11
12func Test(t *testing.T) {
13 gc.TestingT(t)
14}

Subscribers

People subscribed via source and target branches

to all changes: