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

Proposed by Tim Penhey on 2014-03-06
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 2014-03-06 Approve on 2014-03-11
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 on 2014-03-11
10. By Tim Penhey on 2014-03-11

Add a test to show command line args.

Ian Booth (wallyworld) wrote :

Nice.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'golxc.go'
2--- golxc.go 2014-03-05 03:36:19 +0000
3+++ golxc.go 2014-03-11 00:35:14 +0000
4@@ -11,10 +11,15 @@
5 "fmt"
6 "os"
7 "os/exec"
8+ "path"
9 "strconv"
10 "strings"
11+
12+ "github.com/juju/loggo"
13 )
14
15+var logger = loggo.GetLogger("golxc")
16+
17 // Error reports the failure of a LXC command.
18 type Error struct {
19 Name string
20@@ -65,7 +70,7 @@
21 Name() string
22
23 // Create creates a new container based on the given template.
24- Create(configFile, template string, templateArgs ...string) error
25+ Create(configFile, template string, extraArgs []string, templateArgs []string) error
26
27 // Start runs the container as a daemon.
28 Start(configFile, consoleFile string) error
29@@ -74,7 +79,7 @@
30 Stop() error
31
32 // Clone creates a copy of the container, giving the copy the specified name.
33- Clone(name string) (Container, error)
34+ Clone(name string, extraArgs []string, templateArgs []string) (Container, error)
35
36 // Freeze freezes all the container's processes.
37 Freeze() error
38@@ -127,18 +132,26 @@
39 return &containerFactory{}
40 }
41
42+const DefaultLXCDir = "/var/lib/lxc"
43+
44+var ContainerDir = DefaultLXCDir
45+
46 type container struct {
47 name string
48 logFile string
49 logLevel LogLevel
50+ // Newer LXC libraries can have containers in non-default locations. The
51+ // containerDir is the directory that is the 'home' of this container.
52+ containerDir string
53 }
54
55 type containerFactory struct{}
56
57 func (*containerFactory) New(name string) Container {
58 return &container{
59- name: name,
60- logLevel: LogWarning,
61+ name: name,
62+ logLevel: LogWarning,
63+ containerDir: ContainerDir,
64 }
65 }
66
67@@ -179,7 +192,7 @@
68 }
69
70 // Create creates a new container based on the given template.
71-func (c *container) Create(configFile, template string, templateArgs ...string) error {
72+func (c *container) Create(configFile, template string, extraArgs []string, templateArgs []string) error {
73 if c.IsConstructed() {
74 return fmt.Errorf("container %q is already created", c.Name())
75 }
76@@ -190,8 +203,12 @@
77 if configFile != "" {
78 args = append(args, "-f", configFile)
79 }
80- args = append(args, "--")
81+ if len(extraArgs) != 0 {
82+ args = append(args, extraArgs...)
83+ }
84 if len(templateArgs) != 0 {
85+ // Must be done in two steps due to current language implementation details.
86+ args = append(args, "--")
87 args = append(args, templateArgs...)
88 }
89 _, err := run("lxc-create", args...)
90@@ -223,7 +240,13 @@
91 if err != nil {
92 return err
93 }
94- return c.Wait(StateRunning, StateStopped)
95+ if err := c.Wait(StateRunning, StateStopped); err != nil {
96+ return err
97+ }
98+ if !c.IsRunning() {
99+ return fmt.Errorf("container failed to start")
100+ }
101+ return nil
102 }
103
104 // Stop terminates the running container.
105@@ -249,10 +272,13 @@
106 }
107
108 // Clone creates a copy of the container, it gets the given name.
109-func (c *container) Clone(name string) (Container, error) {
110+func (c *container) Clone(name string, extraArgs []string, templateArgs []string) (Container, error) {
111 if !c.IsConstructed() {
112 return nil, fmt.Errorf("container %q is not yet created", c.name)
113 }
114+ if c.IsRunning() {
115+ return nil, fmt.Errorf("cannot clone a running container")
116+ }
117 cc := &container{
118 name: name,
119 }
120@@ -263,6 +289,14 @@
121 "-o", c.name,
122 "-n", name,
123 }
124+ if len(extraArgs) != 0 {
125+ args = append(args, extraArgs...)
126+ }
127+ if len(templateArgs) != 0 {
128+ // Must be done in two steps due to current language implementation details.
129+ args = append(args, "--")
130+ args = append(args, templateArgs...)
131+ }
132 _, err := run("lxc-clone", args...)
133 if err != nil {
134 return nil, err
135@@ -388,27 +422,32 @@
136
137 // containerHome returns the name of the container directory.
138 func (c *container) containerHome() string {
139- return "/var/lib/lxc/" + c.name
140+ return path.Join(c.containerDir, c.name)
141 }
142
143 // rootfs returns the name of the directory containing the
144 // root filesystem of the container.
145 func (c *container) rootfs() string {
146- return c.containerHome() + "/rootfs/"
147+ return path.Join(c.containerHome(), "rootfs")
148 }
149
150 // run executes the passed command and returns the out.
151 func run(name string, args ...string) (string, error) {
152+ logger := loggo.GetLogger(fmt.Sprintf("golxc.run.%s", name))
153+ logger.Tracef("run: %s %v", name, args)
154 cmd := exec.Command(name, args...)
155 // LXC tools do not use stdout and stderr in a predictable
156 // way; based on experimentation, the most convenient
157 // solution is to combine them and leave the client to
158 // determine sanity as best it can.
159 out, err := cmd.CombinedOutput()
160+ result := string(out)
161 if err != nil {
162+ logger.Tracef("run failed output: %s", result)
163 return "", runError(name, err, out)
164 }
165- return string(out), nil
166+ logger.Tracef("run successful output: %s", result)
167+ return result, nil
168 }
169
170 // runError creates an error if run fails.
171
172=== modified file 'golxc_test.go'
173--- golxc_test.go 2014-03-05 03:36:19 +0000
174+++ golxc_test.go 2014-03-11 00:35:14 +0000
175@@ -5,17 +5,18 @@
176
177 import (
178 "io/ioutil"
179- . "launchpad.net/gocheck"
180 "os"
181 "os/user"
182 "path/filepath"
183- "testing"
184+
185+ "github.com/juju/testing"
186+ jc "github.com/juju/testing/checkers"
187+ "github.com/juju/testing/logging"
188+ . "launchpad.net/gocheck"
189
190 "launchpad.net/golxc"
191 )
192
193-func Test(t *testing.T) { TestingT(t) }
194-
195 var lxcfile = `# MIRROR to be used by ubuntu template at container creation:
196 # Leaving it undefined is fine
197 #MIRROR="http://archive.ubuntu.com/ubuntu"
198@@ -142,6 +143,15 @@
199 }
200 }
201
202+func (s *LXCSuite) createContainer(c *C) golxc.Container {
203+ container := s.factory.New("golxc")
204+ c.Assert(container.IsConstructed(), Equals, false)
205+ err := container.Create("", "ubuntu", nil, nil)
206+ c.Assert(err, IsNil)
207+ c.Assert(container.IsConstructed(), Equals, true)
208+ return container
209+}
210+
211 func (s *LXCSuite) TestCreateDestroy(c *C) {
212 // Test clean creation and destroying of a container.
213 lc := s.factory.New("golxc")
214@@ -149,7 +159,7 @@
215 home := golxc.ContainerHome(lc)
216 _, err := os.Stat(home)
217 c.Assert(err, ErrorMatches, "stat .*: no such file or directory")
218- err = lc.Create("", "ubuntu")
219+ err = lc.Create("", "ubuntu", nil, nil)
220 c.Assert(err, IsNil)
221 c.Assert(lc.IsConstructed(), Equals, true)
222 defer func() {
223@@ -165,16 +175,13 @@
224
225 func (s *LXCSuite) TestCreateTwice(c *C) {
226 // Test that a container cannot be created twice.
227- lc1 := s.factory.New("golxc")
228- c.Assert(lc1.IsConstructed(), Equals, false)
229- err := lc1.Create("", "ubuntu")
230- c.Assert(err, IsNil)
231+ lc1 := s.createContainer(c)
232 c.Assert(lc1.IsConstructed(), Equals, true)
233 defer func() {
234 c.Assert(lc1.Destroy(), IsNil)
235 }()
236 lc2 := s.factory.New("golxc")
237- err = lc2.Create("", "ubuntu")
238+ err := lc2.Create("", "ubuntu", nil, nil)
239 c.Assert(err, ErrorMatches, "container .* is already created")
240 }
241
242@@ -183,7 +190,7 @@
243 // case of an illegal template.
244 lc := s.factory.New("golxc")
245 c.Assert(lc.IsConstructed(), Equals, false)
246- err := lc.Create("", "name-of-a-not-existing-template-for-golxc")
247+ err := lc.Create("", "name-of-a-not-existing-template-for-golxc", nil, nil)
248 c.Assert(err, ErrorMatches, `error executing "lxc-create": .*bad template.*`)
249 c.Assert(lc.IsConstructed(), Equals, false)
250 }
251@@ -211,10 +218,7 @@
252 oldLen := len(lcs)
253 c.Assert(err, IsNil)
254 c.Assert(oldLen >= 0, Equals, true)
255- lc := s.factory.New("golxc")
256- c.Assert(lc.IsConstructed(), Equals, false)
257- c.Assert(lc.Create("", "ubuntu"), IsNil)
258- c.Assert(lc.IsConstructed(), Equals, true)
259+ lc := s.createContainer(c)
260 defer func() {
261 c.Assert(lc.Destroy(), IsNil)
262 }()
263@@ -226,16 +230,13 @@
264
265 func (s *LXCSuite) TestClone(c *C) {
266 // Test the cloning of an existing container.
267- lc1 := s.factory.New("golxc")
268- c.Assert(lc1.IsConstructed(), Equals, false)
269- c.Assert(lc1.Create("", "ubuntu"), IsNil)
270- c.Assert(lc1.IsConstructed(), Equals, true)
271+ lc1 := s.createContainer(c)
272 defer func() {
273 c.Assert(lc1.Destroy(), IsNil)
274 }()
275 lcs, _ := s.factory.List()
276 oldLen := len(lcs)
277- lc2, err := lc1.Clone("golxcclone")
278+ lc2, err := lc1.Clone("golxcclone", nil, nil)
279 c.Assert(err, IsNil)
280 c.Assert(lc2.IsConstructed(), Equals, true)
281 defer func() {
282@@ -252,15 +253,13 @@
283 // Test the cloning of a non-existing container.
284 lc := s.factory.New("golxc")
285 c.Assert(lc.IsConstructed(), Equals, false)
286- _, err := lc.Clone("golxcclone")
287+ _, err := lc.Clone("golxcclone", nil, nil)
288 c.Assert(err, ErrorMatches, "container .* is not yet created")
289 }
290
291 func (s *LXCSuite) TestStartStop(c *C) {
292 // Test starting and stopping a container.
293- lc := s.factory.New("golxc")
294- c.Assert(lc.IsConstructed(), Equals, false)
295- c.Assert(lc.Create("", "ubuntu"), IsNil)
296+ lc := s.createContainer(c)
297 defer func() {
298 c.Assert(lc.Destroy(), IsNil)
299 }()
300@@ -279,9 +278,7 @@
301
302 func (s *LXCSuite) TestStopNotRunning(c *C) {
303 // Test that a not running container can't be stopped.
304- lc := s.factory.New("golxc")
305- c.Assert(lc.IsConstructed(), Equals, false)
306- c.Assert(lc.Create("", "ubuntu"), IsNil)
307+ lc := s.createContainer(c)
308 defer func() {
309 c.Assert(lc.Destroy(), IsNil)
310 }()
311@@ -296,7 +293,7 @@
312 c.Assert(lc.Wait(), ErrorMatches, "no states specified")
313 c.Assert(lc.Wait(golxc.StateStopped), IsNil)
314 c.Assert(lc.Wait(golxc.StateStopped, golxc.StateRunning), IsNil)
315- c.Assert(lc.Create("", "ubuntu"), IsNil)
316+ c.Assert(lc.Create("", "ubuntu", nil, nil), IsNil)
317 defer func() {
318 c.Assert(lc.Destroy(), IsNil)
319 }()
320@@ -308,9 +305,7 @@
321
322 func (s *LXCSuite) TestFreezeUnfreeze(c *C) {
323 // Test the freezing and unfreezing of a started container.
324- lc := s.factory.New("golxc")
325- c.Assert(lc.IsConstructed(), Equals, false)
326- c.Assert(lc.Create("", "ubuntu"), IsNil)
327+ lc := s.createContainer(c)
328 defer func() {
329 c.Assert(lc.Destroy(), IsNil)
330 }()
331@@ -327,9 +322,7 @@
332
333 func (s *LXCSuite) TestFreezeNotStarted(c *C) {
334 // Test that a not running container can't be frozen.
335- lc := s.factory.New("golxc")
336- c.Assert(lc.IsConstructed(), Equals, false)
337- c.Assert(lc.Create("", "ubuntu"), IsNil)
338+ lc := s.createContainer(c)
339 defer func() {
340 c.Assert(lc.Destroy(), IsNil)
341 }()
342@@ -352,9 +345,7 @@
343
344 func (s *LXCSuite) TestUnfreezeNotFrozen(c *C) {
345 // Test that a running container can't be unfrozen.
346- lc := s.factory.New("golxc")
347- c.Assert(lc.IsConstructed(), Equals, false)
348- c.Assert(lc.Create("", "ubuntu"), IsNil)
349+ lc := s.createContainer(c)
350 defer func() {
351 c.Assert(lc.Destroy(), IsNil)
352 }()
353@@ -364,3 +355,60 @@
354 }()
355 c.Assert(lc.Unfreeze(), ErrorMatches, "container .* is not frozen")
356 }
357+
358+type commandArgs struct {
359+ logging.LoggingSuite
360+}
361+
362+var _ = Suite(&commandArgs{})
363+
364+func (s *commandArgs) TestCreateArgs(c *C) {
365+ s.PatchValue(&golxc.ContainerDir, c.MkDir())
366+ testing.PatchExecutableAsEchoArgs(c, s, "lxc-create")
367+
368+ factory := golxc.Factory()
369+ container := factory.New("test")
370+ err := container.Create(
371+ "config-file", "template",
372+ []string{"extra-1", "extra-2"},
373+ []string{"template-1", "template-2"},
374+ )
375+ c.Assert(err, IsNil)
376+ testing.AssertEchoArgs(
377+ c, "lxc-create",
378+ "-n", "test",
379+ "-t", "template",
380+ "-f", "config-file",
381+ "extra-1", "extra-2",
382+ "--", "template-1", "template-2")
383+}
384+
385+func (s *commandArgs) TestCloneArgs(c *C) {
386+ dir := c.MkDir()
387+ s.PatchValue(&golxc.ContainerDir, dir)
388+ // Patch lxc-info too as clone checks to see if it is running.
389+ testing.PatchExecutableAsEchoArgs(c, s, "lxc-info")
390+ testing.PatchExecutableAsEchoArgs(c, s, "lxc-clone")
391+
392+ factory := golxc.Factory()
393+ container := factory.New("test")
394+ // Make the rootfs for the "test" container so it thinks it is created.
395+ rootfs := filepath.Join(dir, "test", "rootfs")
396+ err := os.MkdirAll(rootfs, 0755)
397+ c.Assert(err, IsNil)
398+ c.Assert(rootfs, jc.IsDirectory)
399+ c.Assert(container.IsConstructed(), jc.IsTrue)
400+ clone, err := container.Clone(
401+ "name",
402+ []string{"extra-1", "extra-2"},
403+ []string{"template-1", "template-2"},
404+ )
405+ c.Assert(err, IsNil)
406+ testing.AssertEchoArgs(
407+ c, "lxc-clone",
408+ "-o", "test",
409+ "-n", "name",
410+ "extra-1", "extra-2",
411+ "--", "template-1", "template-2")
412+ c.Assert(clone.Name(), Equals, "name")
413+}
414
415=== added file 'package_test.go'
416--- package_test.go 1970-01-01 00:00:00 +0000
417+++ package_test.go 2014-03-11 00:35:14 +0000
418@@ -0,0 +1,14 @@
419+// Copyright 2013 Canonical Ltd.
420+// Licensed under the LGPLv3, see COPYING and COPYING.LESSER file for details.
421+
422+package golxc_test
423+
424+import (
425+ "testing"
426+
427+ gc "launchpad.net/gocheck"
428+)
429+
430+func Test(t *testing.T) {
431+ gc.TestingT(t)
432+}

Subscribers

People subscribed via source and target branches

to all changes: