Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 // | 1 // |
2 // golxc - Go package to interact with Linux Containers (LXC). | 2 // golxc - Go package to interact with Linux Containers (LXC). |
3 // | 3 // |
4 // https://launchpad.net/golxc/ | 4 // https://launchpad.net/golxc/ |
5 // | 5 // |
6 // Copyright (c) 2012 Canonical Ltd. | 6 // Copyright (c) 2012 Canonical Ltd. |
7 // | 7 // |
8 | 8 |
9 package golxc | 9 package golxc |
10 | 10 |
11 import ( | 11 import ( |
12 "bytes" | |
13 "fmt" | 12 "fmt" |
14 "os" | 13 "os" |
15 "os/exec" | 14 "os/exec" |
16 "strconv" | 15 "strconv" |
17 "strings" | 16 "strings" |
18 ) | 17 ) |
19 | 18 |
20 // Error reports the failure of a LXC command. | 19 // Error reports the failure of a LXC command. |
21 type Error struct { | 20 type Error struct { |
22 Name string | 21 Name string |
23 Err error | 22 Err error |
24 » StdErr string | 23 » Output []string |
25 } | 24 } |
26 | 25 |
27 func (e Error) Error() string { | 26 func (e Error) Error() string { |
28 » var info string | 27 » if e.Output == nil { |
29 » if strings.HasPrefix(e.StdErr, e.Name) { | 28 » » return fmt.Sprintf("error executing %q: %v", e.Name, e.Err) |
rog
2012/11/23 10:58:19
perhaps slightly more robust to do: HasPrefix(e.St
TheMue
2012/11/23 13:46:48
Done.
| |
30 » » info = e.StdErr[len(e.Name)+2:] | 29 » } |
31 » } else { | 30 » if len(e.Output) == 1 { |
32 » » info = e.StdErr | 31 » » return fmt.Sprintf("error executing %q: %v", e.Name, e.Output[0] ) |
33 » } | 32 » } |
34 » if info == "" { | 33 » return fmt.Sprintf("error executing %q: %s", e.Name, strings.Join(e.Outp ut, "; ")) |
35 » » info = e.Err.Error() | |
36 » } | |
37 » return fmt.Sprintf("error executing %q: %s", e.Name, info) | |
38 } | 34 } |
39 | 35 |
40 // State represents a container state. | 36 // State represents a container state. |
41 type State string | 37 type State string |
42 | 38 |
43 const ( | 39 const ( |
44 StateUnknown State = "UNKNOWN" | 40 StateUnknown State = "UNKNOWN" |
45 StateStopped State = "STOPPED" | 41 StateStopped State = "STOPPED" |
46 StateStarting State = "STARTING" | 42 StateStarting State = "STARTING" |
47 StateRunning State = "RUNNING" | 43 StateRunning State = "RUNNING" |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
112 } | 108 } |
113 _, err := run("lxc-create", args...) | 109 _, err := run("lxc-create", args...) |
114 if err != nil { | 110 if err != nil { |
115 return err | 111 return err |
116 } | 112 } |
117 return nil | 113 return nil |
118 } | 114 } |
119 | 115 |
120 // Start runs the container as a daemon. | 116 // Start runs the container as a daemon. |
121 func (c *Container) Start(configFile, consoleFile string) error { | 117 func (c *Container) Start(configFile, consoleFile string) error { |
118 if !c.IsConstructed() { | |
119 return fmt.Errorf("container %q is not yet created", c.name) | |
120 } | |
122 args := []string{ | 121 args := []string{ |
123 "--daemon", | 122 "--daemon", |
124 "-n", c.name, | 123 "-n", c.name, |
125 } | 124 } |
126 if configFile != "" { | 125 if configFile != "" { |
127 args = append(args, "-f", configFile) | 126 args = append(args, "-f", configFile) |
128 } | 127 } |
129 if consoleFile != "" { | 128 if consoleFile != "" { |
130 args = append(args, "-c", consoleFile) | 129 args = append(args, "-c", consoleFile) |
131 } | 130 } |
132 if c.LogFile != "" { | 131 if c.LogFile != "" { |
133 args = append(args, "-o", c.LogFile, "-l", string(c.LogLevel)) | 132 args = append(args, "-o", c.LogFile, "-l", string(c.LogLevel)) |
134 } | 133 } |
135 _, err := run("lxc-start", args...) | 134 _, err := run("lxc-start", args...) |
136 if err != nil { | 135 if err != nil { |
137 return err | 136 return err |
138 } | 137 } |
139 return c.Wait(StateRunning) | 138 return c.Wait(StateRunning) |
140 } | 139 } |
141 | 140 |
142 // Stop terminates the running container. | 141 // Stop terminates the running container. |
143 func (c *Container) Stop() error { | 142 func (c *Container) Stop() error { |
143 if !c.IsConstructed() { | |
144 return fmt.Errorf("container %q is not yet created", c.name) | |
145 } | |
144 args := []string{ | 146 args := []string{ |
145 "-n", c.name, | 147 "-n", c.name, |
146 } | 148 } |
147 if c.LogFile != "" { | 149 if c.LogFile != "" { |
148 args = append(args, "-o", c.LogFile, "-l", string(c.LogLevel)) | 150 args = append(args, "-o", c.LogFile, "-l", string(c.LogLevel)) |
149 } | 151 } |
150 _, err := run("lxc-stop", args...) | 152 _, err := run("lxc-stop", args...) |
151 if err != nil { | 153 if err != nil { |
152 return err | 154 return err |
153 } | 155 } |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
297 return "/var/lib/lxc/" + c.name | 299 return "/var/lib/lxc/" + c.name |
298 } | 300 } |
299 | 301 |
300 // rootfs returns the name of the directory containing the | 302 // rootfs returns the name of the directory containing the |
301 // root filesystem of the container. | 303 // root filesystem of the container. |
302 func (c *Container) rootfs() string { | 304 func (c *Container) rootfs() string { |
303 return c.containerHome() + "/rootfs/" | 305 return c.containerHome() + "/rootfs/" |
304 } | 306 } |
305 | 307 |
306 // run executes the passed command and returns the output. | 308 // run executes the passed command and returns the output. |
307 func run(c string, args ...string) (string, error) { | 309 func run(name string, args ...string) (string, error) { |
308 » var outbuf bytes.Buffer | 310 » cmd := exec.Command(name, args...) |
309 » var errbuf bytes.Buffer | 311 » output, err := cmd.CombinedOutput() |
310 » cmd := exec.Command(c, args...) | 312 » if err != nil { |
311 » cmd.Stdout = &outbuf | 313 » » return "", runError(name, err, output) |
312 » cmd.Stderr = &errbuf | 314 » } |
313 » err := cmd.Run() | 315 » return string(output), nil |
314 » if err != nil { | 316 } |
315 » » return "", &Error{c, err, errbuf.String()} | 317 |
316 » } | 318 // runError creates an error if run fails. |
317 » return outbuf.String(), nil | 319 func runError(name string, err error, output []byte) error { |
320 » e := &Error{name, err, nil} | |
321 » for _, l := range strings.Split(string(output), "\n") { | |
322 » » if strings.HasPrefix(l, name+": ") { | |
323 » » » l = l[len(name)+2:] | |
324 » » } | |
325 » » if l != "" { | |
326 » » » e.Output = append(e.Output, l) | |
327 » » } | |
328 » } | |
329 » return e | |
318 } | 330 } |
319 | 331 |
320 // keyValues retrieves key/value pairs out of a command output. | 332 // keyValues retrieves key/value pairs out of a command output. |
321 func keyValues(raw string, sep string) map[string]string { | 333 func keyValues(raw string, sep string) map[string]string { |
322 kv := map[string]string{} | 334 kv := map[string]string{} |
323 lines := strings.Split(raw, "\n") | 335 lines := strings.Split(raw, "\n") |
324 for _, line := range lines { | 336 for _, line := range lines { |
325 parts := strings.SplitN(line, sep, 2) | 337 parts := strings.SplitN(line, sep, 2) |
326 if len(parts) == 2 { | 338 if len(parts) == 2 { |
327 kv[parts[0]] = strings.TrimSpace(parts[1]) | 339 kv[parts[0]] = strings.TrimSpace(parts[1]) |
(...skipping 11 matching lines...) Expand all Loading... | |
339 name := strings.TrimSpace(line) | 351 name := strings.TrimSpace(line) |
340 if name != "" { | 352 if name != "" { |
341 collector[name] = struct{}{} | 353 collector[name] = struct{}{} |
342 } | 354 } |
343 } | 355 } |
344 for name := range collector { | 356 for name := range collector { |
345 set = append(set, name) | 357 set = append(set, name) |
346 } | 358 } |
347 return set | 359 return set |
348 } | 360 } |
LEFT | RIGHT |