... | ... |
@@ -1,20 +1,16 @@ |
1 | 1 |
package docker |
2 | 2 |
|
3 | 3 |
import ( |
4 |
- "bufio" |
|
5 | 4 |
"bytes" |
6 | 5 |
"encoding/json" |
7 | 6 |
"errors" |
8 | 7 |
"fmt" |
9 | 8 |
"github.com/dotcloud/docker/auth" |
10 |
- "github.com/dotcloud/docker/fs" |
|
11 | 9 |
"github.com/dotcloud/docker/future" |
12 | 10 |
"github.com/dotcloud/docker/rcli" |
13 | 11 |
"io" |
14 |
- "io/ioutil" |
|
15 | 12 |
"net/http" |
16 | 13 |
"net/url" |
17 |
- "os" |
|
18 | 14 |
"path" |
19 | 15 |
"strconv" |
20 | 16 |
"strings" |
... | ... |
@@ -137,7 +133,7 @@ func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...str |
137 | 137 |
|
138 | 138 |
// 'docker info': display system-wide information. |
139 | 139 |
func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error { |
140 |
- images, _ := srv.images.Images() |
|
140 |
+ images, _ := srv.containers.graph.All() |
|
141 | 141 |
var imgcount int |
142 | 142 |
if images == nil { |
143 | 143 |
imgcount = 0 |
... | ... |
@@ -236,7 +232,7 @@ func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...strin |
236 | 236 |
} |
237 | 237 |
for _, name := range cmd.Args() { |
238 | 238 |
if container := srv.containers.Get(name); container != nil { |
239 |
- if err := container.Mountpoint.EnsureMounted(); err != nil { |
|
239 |
+ if err := container.EnsureMounted(); err != nil { |
|
240 | 240 |
return err |
241 | 241 |
} |
242 | 242 |
fmt.Fprintln(stdout, container.Id) |
... | ... |
@@ -260,7 +256,7 @@ func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...str |
260 | 260 |
var obj interface{} |
261 | 261 |
if container := srv.containers.Get(name); container != nil { |
262 | 262 |
obj = container |
263 |
- } else if image, err := srv.images.Find(name); err != nil { |
|
263 |
+ } else if image, err := srv.containers.graph.Get(name); err != nil { |
|
264 | 264 |
return err |
265 | 265 |
} else if image != nil { |
266 | 266 |
obj = image |
... | ... |
@@ -310,27 +306,12 @@ func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string |
310 | 310 |
// 'docker rmi NAME' removes all images with the name NAME |
311 | 311 |
func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) (err error) { |
312 | 312 |
cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image") |
313 |
- fl_all := cmd.Bool("a", false, "Use IMAGE as a path and remove ALL images in this path") |
|
314 |
- fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name") |
|
315 | 313 |
if cmd.Parse(args) != nil || cmd.NArg() < 1 { |
316 | 314 |
cmd.Usage() |
317 | 315 |
return nil |
318 | 316 |
} |
319 | 317 |
for _, name := range cmd.Args() { |
320 |
- if *fl_regexp { |
|
321 |
- err = srv.images.RemoveRegexp(name) |
|
322 |
- } else if *fl_all { |
|
323 |
- err = srv.images.RemoveInPath(name) |
|
324 |
- } else { |
|
325 |
- if image, err1 := srv.images.Find(name); err1 != nil { |
|
326 |
- err = err1 |
|
327 |
- } else if err1 == nil && image == nil { |
|
328 |
- err = fmt.Errorf("No such image: %s", name) |
|
329 |
- } else { |
|
330 |
- err = srv.images.Remove(image) |
|
331 |
- } |
|
332 |
- } |
|
333 |
- if err != nil { |
|
318 |
+ if err := srv.containers.graph.Delete(name); err != nil { |
|
334 | 319 |
return err |
335 | 320 |
} |
336 | 321 |
} |
... | ... |
@@ -409,7 +390,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri |
409 | 409 |
archive = future.ProgressReader(resp.Body, int(resp.ContentLength), stdout) |
410 | 410 |
} |
411 | 411 |
fmt.Fprintf(stdout, "Unpacking to %s\n", name) |
412 |
- img, err := srv.images.Create(archive, nil, name, "") |
|
412 |
+ img, err := srv.containers.graph.Create(archive, "", "") |
|
413 | 413 |
if err != nil { |
414 | 414 |
return err |
415 | 415 |
} |
... | ... |
@@ -419,7 +400,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri |
419 | 419 |
|
420 | 420 |
func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error { |
421 | 421 |
cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images") |
422 |
- limit := cmd.Int("l", 0, "Only show the N most recent versions of each image") |
|
422 |
+ //limit := cmd.Int("l", 0, "Only show the N most recent versions of each image") |
|
423 | 423 |
quiet := cmd.Bool("q", false, "only show numeric IDs") |
424 | 424 |
if err := cmd.Parse(args); err != nil { |
425 | 425 |
return nil |
... | ... |
@@ -428,51 +409,65 @@ func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...stri |
428 | 428 |
cmd.Usage() |
429 | 429 |
return nil |
430 | 430 |
} |
431 |
- var nameFilter string |
|
432 |
- if cmd.NArg() == 1 { |
|
433 |
- nameFilter = cmd.Arg(0) |
|
434 |
- } |
|
431 |
+ /* |
|
432 |
+ var nameFilter string |
|
433 |
+ if cmd.NArg() == 1 { |
|
434 |
+ nameFilter = cmd.Arg(0) |
|
435 |
+ } |
|
436 |
+ */ |
|
435 | 437 |
w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0) |
436 | 438 |
if !*quiet { |
437 | 439 |
fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n") |
438 | 440 |
} |
439 |
- paths, err := srv.images.Paths() |
|
440 |
- if err != nil { |
|
441 |
- return err |
|
442 |
- } |
|
443 |
- for _, name := range paths { |
|
444 |
- if nameFilter != "" && nameFilter != name { |
|
445 |
- continue |
|
446 |
- } |
|
447 |
- ids, err := srv.images.List(name) |
|
441 |
+ if *quiet { |
|
442 |
+ images, err := srv.containers.graph.All() |
|
448 | 443 |
if err != nil { |
449 | 444 |
return err |
450 | 445 |
} |
451 |
- for idx, img := range ids { |
|
452 |
- if *limit > 0 && idx >= *limit { |
|
453 |
- break |
|
454 |
- } |
|
455 |
- if !*quiet { |
|
456 |
- for idx, field := range []string{ |
|
457 |
- /* NAME */ name, |
|
458 |
- /* ID */ img.Id, |
|
459 |
- /* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago", |
|
460 |
- /* PARENT */ img.Parent, |
|
461 |
- } { |
|
462 |
- if idx == 0 { |
|
463 |
- w.Write([]byte(field)) |
|
464 |
- } else { |
|
465 |
- w.Write([]byte("\t" + field)) |
|
466 |
- } |
|
467 |
- } |
|
468 |
- w.Write([]byte{'\n'}) |
|
469 |
- } else { |
|
470 |
- stdout.Write([]byte(img.Id + "\n")) |
|
471 |
- } |
|
446 |
+ for _, image := range images { |
|
447 |
+ fmt.Fprintln(stdout, image.Id) |
|
472 | 448 |
} |
473 |
- } |
|
474 |
- if !*quiet { |
|
475 |
- w.Flush() |
|
449 |
+ } else { |
|
450 |
+ // FIXME: |
|
451 |
+ // paths, err := srv.images.Paths() |
|
452 |
+ // if err != nil { |
|
453 |
+ // return err |
|
454 |
+ // } |
|
455 |
+ // for _, name := range paths { |
|
456 |
+ // if nameFilter != "" && nameFilter != name { |
|
457 |
+ // continue |
|
458 |
+ // } |
|
459 |
+ // ids, err := srv.images.List(name) |
|
460 |
+ // if err != nil { |
|
461 |
+ // return err |
|
462 |
+ // } |
|
463 |
+ // for idx, img := range ids { |
|
464 |
+ // if *limit > 0 && idx >= *limit { |
|
465 |
+ // break |
|
466 |
+ // } |
|
467 |
+ // if !*quiet { |
|
468 |
+ // for idx, field := range []string{ |
|
469 |
+ // /* NAME */ name, |
|
470 |
+ // /* ID */ img.Id, |
|
471 |
+ // /* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago", |
|
472 |
+ // /* PARENT */ img.Parent, |
|
473 |
+ // } { |
|
474 |
+ // if idx == 0 { |
|
475 |
+ // w.Write([]byte(field)) |
|
476 |
+ // } else { |
|
477 |
+ // w.Write([]byte("\t" + field)) |
|
478 |
+ // } |
|
479 |
+ // } |
|
480 |
+ // w.Write([]byte{'\n'}) |
|
481 |
+ // } else { |
|
482 |
+ // stdout.Write([]byte(img.Id + "\n")) |
|
483 |
+ // } |
|
484 |
+ // } |
|
485 |
+ // } |
|
486 |
+ // if !*quiet { |
|
487 |
+ // w.Flush() |
|
488 |
+ // } |
|
489 |
+ // |
|
476 | 490 |
} |
477 | 491 |
return nil |
478 | 492 |
|
... | ... |
@@ -492,7 +487,6 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) |
492 | 492 |
fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n") |
493 | 493 |
} |
494 | 494 |
for _, container := range srv.containers.List() { |
495 |
- comment := container.GetUserData("comment") |
|
496 | 495 |
if !container.State.Running && !*fl_all { |
497 | 496 |
continue |
498 | 497 |
} |
... | ... |
@@ -503,11 +497,11 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) |
503 | 503 |
} |
504 | 504 |
for idx, field := range []string{ |
505 | 505 |
/* ID */ container.Id, |
506 |
- /* IMAGE */ container.GetUserData("image"), |
|
506 |
+ /* IMAGE */ container.Image, |
|
507 | 507 |
/* COMMAND */ command, |
508 | 508 |
/* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago", |
509 | 509 |
/* STATUS */ container.State.String(), |
510 |
- /* COMMENT */ comment, |
|
510 |
+ /* COMMENT */ "", |
|
511 | 511 |
} { |
512 | 512 |
if idx == 0 { |
513 | 513 |
w.Write([]byte(field)) |
... | ... |
@@ -540,17 +534,13 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri |
540 | 540 |
} |
541 | 541 |
if container := srv.containers.Get(containerName); container != nil { |
542 | 542 |
// FIXME: freeze the container before copying it to avoid data corruption? |
543 |
- rwTar, err := fs.Tar(container.Mountpoint.Rw, fs.Uncompressed) |
|
543 |
+ // FIXME: this shouldn't be in commands. |
|
544 |
+ rwTar, err := container.ExportRw() |
|
544 | 545 |
if err != nil { |
545 | 546 |
return err |
546 | 547 |
} |
547 | 548 |
// Create a new image from the container's base layers + a new layer from container changes |
548 |
- parentImg, err := srv.images.Get(container.Image) |
|
549 |
- if err != nil { |
|
550 |
- return err |
|
551 |
- } |
|
552 |
- |
|
553 |
- img, err := srv.images.Create(rwTar, parentImg, imgName, "") |
|
549 |
+ img, err := srv.containers.graph.Create(rwTar, container.Image, "") |
|
554 | 550 |
if err != nil { |
555 | 551 |
return err |
556 | 552 |
} |
... | ... |
@@ -574,10 +564,10 @@ func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) |
574 | 574 |
} |
575 | 575 |
name := cmd.Arg(0) |
576 | 576 |
if container := srv.containers.Get(name); container != nil { |
577 |
- if err := container.Mountpoint.EnsureMounted(); err != nil { |
|
577 |
+ if err := container.EnsureMounted(); err != nil { |
|
578 | 578 |
return err |
579 | 579 |
} |
580 |
- data, err := fs.Tar(container.Mountpoint.Root, fs.Uncompressed) |
|
580 |
+ data, err := container.Export() |
|
581 | 581 |
if err != nil { |
582 | 582 |
return err |
583 | 583 |
} |
... | ... |
@@ -603,7 +593,7 @@ func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string |
603 | 603 |
if container := srv.containers.Get(cmd.Arg(0)); container == nil { |
604 | 604 |
return errors.New("No such container") |
605 | 605 |
} else { |
606 |
- changes, err := srv.images.Changes(container.Mountpoint) |
|
606 |
+ changes, err := container.Changes() |
|
607 | 607 |
if err != nil { |
608 | 608 |
return err |
609 | 609 |
} |
... | ... |
@@ -625,10 +615,20 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string |
625 | 625 |
} |
626 | 626 |
name := cmd.Arg(0) |
627 | 627 |
if container := srv.containers.Get(name); container != nil { |
628 |
- if _, err := io.Copy(stdout, container.StdoutLog()); err != nil { |
|
628 |
+ log_stdout, err := container.ReadLog("stdout") |
|
629 |
+ if err != nil { |
|
629 | 630 |
return err |
630 | 631 |
} |
631 |
- if _, err := io.Copy(stdout, container.StderrLog()); err != nil { |
|
632 |
+ log_stderr, err := container.ReadLog("stderr") |
|
633 |
+ if err != nil { |
|
634 |
+ return err |
|
635 |
+ } |
|
636 |
+ // FIXME: Interpolate stdout and stderr instead of concatenating them |
|
637 |
+ // FIXME: Differentiate stdout and stderr in the remote protocol |
|
638 |
+ if _, err := io.Copy(stdout, log_stdout); err != nil { |
|
639 |
+ return err |
|
640 |
+ } |
|
641 |
+ if _, err := io.Copy(stdout, log_stderr); err != nil { |
|
632 | 642 |
return err |
633 | 643 |
} |
634 | 644 |
return nil |
... | ... |
@@ -636,31 +636,6 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string |
636 | 636 |
return errors.New("No such container: " + cmd.Arg(0)) |
637 | 637 |
} |
638 | 638 |
|
639 |
-func (srv *Server) CreateContainer(img *fs.Image, ports []int, user string, tty bool, openStdin bool, memory int64, comment string, cmd string, args ...string) (*Container, error) { |
|
640 |
- id := future.RandomId()[:8] |
|
641 |
- container, err := srv.containers.Create(id, cmd, args, img, |
|
642 |
- &Config{ |
|
643 |
- Hostname: id, |
|
644 |
- Ports: ports, |
|
645 |
- User: user, |
|
646 |
- Tty: tty, |
|
647 |
- OpenStdin: openStdin, |
|
648 |
- Memory: memory, |
|
649 |
- }) |
|
650 |
- if err != nil { |
|
651 |
- return nil, err |
|
652 |
- } |
|
653 |
- if err := container.SetUserData("image", img.Id); err != nil { |
|
654 |
- srv.containers.Destroy(container) |
|
655 |
- return nil, errors.New("Error setting container userdata: " + err.Error()) |
|
656 |
- } |
|
657 |
- if err := container.SetUserData("comment", comment); err != nil { |
|
658 |
- srv.containers.Destroy(container) |
|
659 |
- return nil, errors.New("Error setting container userdata: " + err.Error()) |
|
660 |
- } |
|
661 |
- return container, nil |
|
662 |
-} |
|
663 |
- |
|
664 | 639 |
func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error { |
665 | 640 |
cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container") |
666 | 641 |
fl_i := cmd.Bool("i", false, "Attach to stdin") |
... | ... |
@@ -729,7 +704,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) |
729 | 729 |
fl_attach := cmd.Bool("a", false, "Attach stdin and stdout") |
730 | 730 |
fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached") |
731 | 731 |
fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty") |
732 |
- fl_comment := cmd.String("c", "", "Comment") |
|
733 | 732 |
fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)") |
734 | 733 |
var fl_ports ports |
735 | 734 |
|
... | ... |
@@ -738,8 +712,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) |
738 | 738 |
return nil |
739 | 739 |
} |
740 | 740 |
name := cmd.Arg(0) |
741 |
- var img_name string |
|
742 |
- //var img_version string // Only here for reference |
|
743 | 741 |
var cmdline []string |
744 | 742 |
|
745 | 743 |
if len(cmd.Args()) >= 2 { |
... | ... |
@@ -758,33 +730,15 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) |
758 | 758 |
cmdline = []string{"/bin/bash", "-i"} |
759 | 759 |
} |
760 | 760 |
|
761 |
- // Find the image |
|
762 |
- img, err := srv.images.Find(name) |
|
763 |
- if err != nil { |
|
764 |
- return err |
|
765 |
- } else if img == nil { |
|
766 |
- // Separate the name:version tag |
|
767 |
- if strings.Contains(name, ":") { |
|
768 |
- parts := strings.SplitN(name, ":", 2) |
|
769 |
- img_name = parts[0] |
|
770 |
- //img_version = parts[1] // Only here for reference |
|
771 |
- } else { |
|
772 |
- img_name = name |
|
773 |
- } |
|
774 |
- |
|
775 |
- stdin_noclose := ioutil.NopCloser(stdin) |
|
776 |
- if err := srv.CmdImport(stdin_noclose, stdout, img_name); err != nil { |
|
777 |
- return err |
|
778 |
- } |
|
779 |
- img, err = srv.images.Find(name) |
|
780 |
- if err != nil || img == nil { |
|
781 |
- return errors.New("Could not find image after downloading: " + name) |
|
782 |
- } |
|
783 |
- } |
|
784 |
- |
|
785 | 761 |
// Create new container |
786 |
- container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty, |
|
787 |
- *fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...) |
|
762 |
+ container, err := srv.containers.Create(cmdline[0], cmdline[1:], name, |
|
763 |
+ &Config{ |
|
764 |
+ Ports: fl_ports, |
|
765 |
+ User: *fl_user, |
|
766 |
+ Tty: *fl_tty, |
|
767 |
+ OpenStdin: *fl_stdin, |
|
768 |
+ Memory: *fl_memory, |
|
769 |
+ }) |
|
788 | 770 |
if err != nil { |
789 | 771 |
return errors.New("Error creating container: " + err.Error()) |
790 | 772 |
} |
... | ... |
@@ -850,7 +804,6 @@ func NewServer() (*Server, error) { |
850 | 850 |
return nil, err |
851 | 851 |
} |
852 | 852 |
srv := &Server{ |
853 |
- images: containers.Store, |
|
854 | 853 |
containers: containers, |
855 | 854 |
} |
856 | 855 |
return srv, nil |
... | ... |
@@ -858,5 +811,4 @@ func NewServer() (*Server, error) { |
858 | 858 |
|
859 | 859 |
type Server struct { |
860 | 860 |
containers *Docker |
861 |
- images *fs.Store |
|
862 | 861 |
} |
... | ... |
@@ -3,7 +3,9 @@ package docker |
3 | 3 |
import ( |
4 | 4 |
"encoding/json" |
5 | 5 |
"errors" |
6 |
- "github.com/dotcloud/docker/fs" |
|
6 |
+ "fmt" |
|
7 |
+ "github.com/dotcloud/docker/future" |
|
8 |
+ "github.com/dotcloud/docker/graph" |
|
7 | 9 |
"github.com/kr/pty" |
8 | 10 |
"io" |
9 | 11 |
"io/ioutil" |
... | ... |
@@ -16,40 +18,34 @@ import ( |
16 | 16 |
"time" |
17 | 17 |
) |
18 | 18 |
|
19 |
-var sysInitPath string |
|
20 |
- |
|
21 |
-func init() { |
|
22 |
- sysInitPath = SelfPath() |
|
23 |
-} |
|
24 |
- |
|
25 | 19 |
type Container struct { |
26 |
- Id string |
|
27 |
- Root string |
|
20 |
+ root string |
|
21 |
+ |
|
22 |
+ Id string |
|
28 | 23 |
|
29 | 24 |
Created time.Time |
30 | 25 |
|
31 | 26 |
Path string |
32 | 27 |
Args []string |
33 | 28 |
|
34 |
- Config *Config |
|
35 |
- Mountpoint *fs.Mountpoint |
|
36 |
- State *State |
|
37 |
- Image string |
|
29 |
+ Config *Config |
|
30 |
+ State State |
|
31 |
+ Image string |
|
38 | 32 |
|
39 | 33 |
network *NetworkInterface |
40 | 34 |
networkManager *NetworkManager |
41 | 35 |
NetworkSettings *NetworkSettings |
42 | 36 |
|
43 |
- SysInitPath string |
|
44 |
- lxcConfigPath string |
|
45 |
- cmd *exec.Cmd |
|
46 |
- stdout *writeBroadcaster |
|
47 |
- stderr *writeBroadcaster |
|
48 |
- stdin io.ReadCloser |
|
49 |
- stdinPipe io.WriteCloser |
|
37 |
+ SysInitPath string |
|
38 |
+ cmd *exec.Cmd |
|
39 |
+ stdout *writeBroadcaster |
|
40 |
+ stderr *writeBroadcaster |
|
41 |
+ stdin io.ReadCloser |
|
42 |
+ stdinPipe io.WriteCloser |
|
50 | 43 |
|
51 | 44 |
stdoutLog *os.File |
52 | 45 |
stderrLog *os.File |
46 |
+ runtime *Docker // FIXME: rename Docker to Runtime for clarity |
|
53 | 47 |
} |
54 | 48 |
|
55 | 49 |
type Config struct { |
... | ... |
@@ -69,104 +65,9 @@ type NetworkSettings struct { |
69 | 69 |
PortMapping map[string]string |
70 | 70 |
} |
71 | 71 |
|
72 |
-func createContainer(id string, root string, command string, args []string, image *fs.Image, config *Config, netManager *NetworkManager) (*Container, error) { |
|
73 |
- mountpoint, err := image.Mountpoint(path.Join(root, "rootfs"), path.Join(root, "rw")) |
|
74 |
- if err != nil { |
|
75 |
- return nil, err |
|
76 |
- } |
|
77 |
- container := &Container{ |
|
78 |
- Id: id, |
|
79 |
- Root: root, |
|
80 |
- Created: time.Now(), |
|
81 |
- Path: command, |
|
82 |
- Args: args, |
|
83 |
- Config: config, |
|
84 |
- Image: image.Id, |
|
85 |
- Mountpoint: mountpoint, |
|
86 |
- State: newState(), |
|
87 |
- networkManager: netManager, |
|
88 |
- NetworkSettings: &NetworkSettings{}, |
|
89 |
- SysInitPath: sysInitPath, |
|
90 |
- lxcConfigPath: path.Join(root, "config.lxc"), |
|
91 |
- stdout: newWriteBroadcaster(), |
|
92 |
- stderr: newWriteBroadcaster(), |
|
93 |
- } |
|
94 |
- if err := os.Mkdir(root, 0700); err != nil { |
|
95 |
- return nil, err |
|
96 |
- } |
|
97 |
- // Setup logging of stdout and stderr to disk |
|
98 |
- if stdoutLog, err := os.OpenFile(path.Join(container.Root, id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil { |
|
99 |
- return nil, err |
|
100 |
- } else { |
|
101 |
- container.stdoutLog = stdoutLog |
|
102 |
- } |
|
103 |
- if stderrLog, err := os.OpenFile(path.Join(container.Root, id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil { |
|
104 |
- return nil, err |
|
105 |
- } else { |
|
106 |
- container.stderrLog = stderrLog |
|
107 |
- } |
|
108 |
- if container.Config.OpenStdin { |
|
109 |
- container.stdin, container.stdinPipe = io.Pipe() |
|
110 |
- } else { |
|
111 |
- container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin |
|
112 |
- } |
|
113 |
- container.stdout.AddWriter(NopWriteCloser(container.stdoutLog)) |
|
114 |
- container.stderr.AddWriter(NopWriteCloser(container.stderrLog)) |
|
115 |
- |
|
116 |
- if err := container.save(); err != nil { |
|
117 |
- return nil, err |
|
118 |
- } |
|
119 |
- return container, nil |
|
120 |
-} |
|
121 |
- |
|
122 |
-func loadContainer(store *fs.Store, containerPath string, netManager *NetworkManager) (*Container, error) { |
|
123 |
- data, err := ioutil.ReadFile(path.Join(containerPath, "config.json")) |
|
124 |
- if err != nil { |
|
125 |
- return nil, err |
|
126 |
- } |
|
127 |
- mountpoint, err := store.FetchMountpoint( |
|
128 |
- path.Join(containerPath, "rootfs"), |
|
129 |
- path.Join(containerPath, "rw"), |
|
130 |
- ) |
|
131 |
- if err != nil { |
|
132 |
- return nil, err |
|
133 |
- } else if mountpoint == nil { |
|
134 |
- return nil, errors.New("Couldn't load container: unregistered mountpoint.") |
|
135 |
- } |
|
136 |
- container := &Container{ |
|
137 |
- stdout: newWriteBroadcaster(), |
|
138 |
- stderr: newWriteBroadcaster(), |
|
139 |
- lxcConfigPath: path.Join(containerPath, "config.lxc"), |
|
140 |
- networkManager: netManager, |
|
141 |
- NetworkSettings: &NetworkSettings{}, |
|
142 |
- Mountpoint: mountpoint, |
|
143 |
- } |
|
144 |
- // Load container settings |
|
145 |
- if err := json.Unmarshal(data, container); err != nil { |
|
146 |
- return nil, err |
|
147 |
- } |
|
148 |
- |
|
149 |
- // Setup logging of stdout and stderr to disk |
|
150 |
- if stdoutLog, err := os.OpenFile(path.Join(container.Root, container.Id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil { |
|
151 |
- return nil, err |
|
152 |
- } else { |
|
153 |
- container.stdoutLog = stdoutLog |
|
154 |
- } |
|
155 |
- if stderrLog, err := os.OpenFile(path.Join(container.Root, container.Id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil { |
|
156 |
- return nil, err |
|
157 |
- } else { |
|
158 |
- container.stderrLog = stderrLog |
|
159 |
- } |
|
160 |
- container.stdout.AddWriter(NopWriteCloser(container.stdoutLog)) |
|
161 |
- container.stderr.AddWriter(NopWriteCloser(container.stderrLog)) |
|
162 |
- |
|
163 |
- if container.Config.OpenStdin { |
|
164 |
- container.stdin, container.stdinPipe = io.Pipe() |
|
165 |
- } else { |
|
166 |
- container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin |
|
167 |
- } |
|
168 |
- container.State = newState() |
|
169 |
- return container, nil |
|
72 |
+func GenerateId() string { |
|
73 |
+ future.Seed() |
|
74 |
+ return future.RandomId() |
|
170 | 75 |
} |
171 | 76 |
|
172 | 77 |
func (container *Container) Cmd() *exec.Cmd { |
... | ... |
@@ -177,64 +78,32 @@ func (container *Container) When() time.Time { |
177 | 177 |
return container.Created |
178 | 178 |
} |
179 | 179 |
|
180 |
-func (container *Container) loadUserData() (map[string]string, error) { |
|
181 |
- jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json")) |
|
182 |
- if err != nil { |
|
183 |
- if os.IsNotExist(err) { |
|
184 |
- return make(map[string]string), nil |
|
185 |
- } |
|
186 |
- return nil, err |
|
187 |
- } |
|
188 |
- data := make(map[string]string) |
|
189 |
- if err := json.Unmarshal(jsonData, &data); err != nil { |
|
190 |
- return nil, err |
|
191 |
- } |
|
192 |
- return data, nil |
|
193 |
-} |
|
194 |
- |
|
195 |
-func (container *Container) saveUserData(data map[string]string) error { |
|
196 |
- jsonData, err := json.Marshal(data) |
|
180 |
+func (container *Container) FromDisk() error { |
|
181 |
+ data, err := ioutil.ReadFile(container.jsonPath()) |
|
197 | 182 |
if err != nil { |
198 | 183 |
return err |
199 | 184 |
} |
200 |
- return ioutil.WriteFile(path.Join(container.Root, "userdata.json"), jsonData, 0700) |
|
201 |
-} |
|
202 |
- |
|
203 |
-func (container *Container) SetUserData(key, value string) error { |
|
204 |
- data, err := container.loadUserData() |
|
205 |
- if err != nil { |
|
185 |
+ // Load container settings |
|
186 |
+ if err := json.Unmarshal(data, container); err != nil { |
|
206 | 187 |
return err |
207 | 188 |
} |
208 |
- data[key] = value |
|
209 |
- return container.saveUserData(data) |
|
210 |
-} |
|
211 |
- |
|
212 |
-func (container *Container) GetUserData(key string) string { |
|
213 |
- data, err := container.loadUserData() |
|
214 |
- if err != nil { |
|
215 |
- return "" |
|
216 |
- } |
|
217 |
- if value, exists := data[key]; exists { |
|
218 |
- return value |
|
219 |
- } |
|
220 |
- return "" |
|
189 |
+ return nil |
|
221 | 190 |
} |
222 | 191 |
|
223 |
-func (container *Container) save() (err error) { |
|
192 |
+func (container *Container) ToDisk() (err error) { |
|
224 | 193 |
data, err := json.Marshal(container) |
225 | 194 |
if err != nil { |
226 | 195 |
return |
227 | 196 |
} |
228 |
- return ioutil.WriteFile(path.Join(container.Root, "config.json"), data, 0666) |
|
197 |
+ return ioutil.WriteFile(container.jsonPath(), data, 0666) |
|
229 | 198 |
} |
230 | 199 |
|
231 | 200 |
func (container *Container) generateLXCConfig() error { |
232 |
- fo, err := os.Create(container.lxcConfigPath) |
|
201 |
+ fo, err := os.Create(container.lxcConfigPath()) |
|
233 | 202 |
if err != nil { |
234 | 203 |
return err |
235 | 204 |
} |
236 | 205 |
defer fo.Close() |
237 |
- |
|
238 | 206 |
if err := LxcTemplateCompiled.Execute(fo, container); err != nil { |
239 | 207 |
return err |
240 | 208 |
} |
... | ... |
@@ -309,7 +178,7 @@ func (container *Container) start() error { |
309 | 309 |
} |
310 | 310 |
|
311 | 311 |
func (container *Container) Start() error { |
312 |
- if err := container.Mountpoint.EnsureMounted(); err != nil { |
|
312 |
+ if err := container.EnsureMounted(); err != nil { |
|
313 | 313 |
return err |
314 | 314 |
} |
315 | 315 |
if err := container.allocateNetwork(); err != nil { |
... | ... |
@@ -320,7 +189,7 @@ func (container *Container) Start() error { |
320 | 320 |
} |
321 | 321 |
params := []string{ |
322 | 322 |
"-n", container.Id, |
323 |
- "-f", container.lxcConfigPath, |
|
323 |
+ "-f", container.lxcConfigPath(), |
|
324 | 324 |
"--", |
325 | 325 |
"/sbin/init", |
326 | 326 |
} |
... | ... |
@@ -348,8 +217,10 @@ func (container *Container) Start() error { |
348 | 348 |
if err != nil { |
349 | 349 |
return err |
350 | 350 |
} |
351 |
+ // FIXME: save state on disk *first*, then converge |
|
352 |
+ // this way disk state is used as a journal, eg. we can restore after crash etc. |
|
351 | 353 |
container.State.setRunning(container.cmd.Process.Pid) |
352 |
- container.save() |
|
354 |
+ container.ToDisk() |
|
353 | 355 |
go container.monitor() |
354 | 356 |
return nil |
355 | 357 |
} |
... | ... |
@@ -389,28 +260,12 @@ func (container *Container) StdoutPipe() (io.ReadCloser, error) { |
389 | 389 |
return newBufReader(reader), nil |
390 | 390 |
} |
391 | 391 |
|
392 |
-func (container *Container) StdoutLog() io.Reader { |
|
393 |
- r, err := os.Open(container.stdoutLog.Name()) |
|
394 |
- if err != nil { |
|
395 |
- return nil |
|
396 |
- } |
|
397 |
- return r |
|
398 |
-} |
|
399 |
- |
|
400 | 392 |
func (container *Container) StderrPipe() (io.ReadCloser, error) { |
401 | 393 |
reader, writer := io.Pipe() |
402 | 394 |
container.stderr.AddWriter(writer) |
403 | 395 |
return newBufReader(reader), nil |
404 | 396 |
} |
405 | 397 |
|
406 |
-func (container *Container) StderrLog() io.Reader { |
|
407 |
- r, err := os.Open(container.stderrLog.Name()) |
|
408 |
- if err != nil { |
|
409 |
- return nil |
|
410 |
- } |
|
411 |
- return r |
|
412 |
-} |
|
413 |
- |
|
414 | 398 |
func (container *Container) allocateNetwork() error { |
415 | 399 |
iface, err := container.networkManager.Allocate() |
416 | 400 |
if err != nil { |
... | ... |
@@ -450,7 +305,7 @@ func (container *Container) monitor() { |
450 | 450 |
} |
451 | 451 |
container.stdout.Close() |
452 | 452 |
container.stderr.Close() |
453 |
- if err := container.Mountpoint.Umount(); err != nil { |
|
453 |
+ if err := container.Unmount(); err != nil { |
|
454 | 454 |
log.Printf("%v: Failed to umount filesystem: %v", container.Id, err) |
455 | 455 |
} |
456 | 456 |
|
... | ... |
@@ -461,7 +316,7 @@ func (container *Container) monitor() { |
461 | 461 |
|
462 | 462 |
// Report status back |
463 | 463 |
container.State.setStopped(exitCode) |
464 |
- container.save() |
|
464 |
+ container.ToDisk() |
|
465 | 465 |
} |
466 | 466 |
|
467 | 467 |
func (container *Container) kill() error { |
... | ... |
@@ -523,6 +378,17 @@ func (container *Container) Wait() int { |
523 | 523 |
return container.State.ExitCode |
524 | 524 |
} |
525 | 525 |
|
526 |
+func (container *Container) ExportRw() (graph.Archive, error) { |
|
527 |
+ return graph.Tar(container.rwPath(), graph.Uncompressed) |
|
528 |
+} |
|
529 |
+ |
|
530 |
+func (container *Container) Export() (graph.Archive, error) { |
|
531 |
+ if err := container.EnsureMounted(); err != nil { |
|
532 |
+ return nil, err |
|
533 |
+ } |
|
534 |
+ return graph.Tar(container.RootfsPath(), graph.Uncompressed) |
|
535 |
+} |
|
536 |
+ |
|
526 | 537 |
func (container *Container) WaitTimeout(timeout time.Duration) error { |
527 | 538 |
done := make(chan bool) |
528 | 539 |
go func() { |
... | ... |
@@ -538,3 +404,75 @@ func (container *Container) WaitTimeout(timeout time.Duration) error { |
538 | 538 |
} |
539 | 539 |
return nil |
540 | 540 |
} |
541 |
+ |
|
542 |
+func (container *Container) EnsureMounted() error { |
|
543 |
+ if mounted, err := container.Mounted(); err != nil { |
|
544 |
+ return err |
|
545 |
+ } else if mounted { |
|
546 |
+ return nil |
|
547 |
+ } |
|
548 |
+ return container.Mount() |
|
549 |
+} |
|
550 |
+ |
|
551 |
+func (container *Container) Mount() error { |
|
552 |
+ image, err := container.GetImage() |
|
553 |
+ if err != nil { |
|
554 |
+ return err |
|
555 |
+ } |
|
556 |
+ return image.Mount(container.RootfsPath(), container.rwPath()) |
|
557 |
+} |
|
558 |
+ |
|
559 |
+func (container *Container) Changes() ([]graph.Change, error) { |
|
560 |
+ image, err := container.GetImage() |
|
561 |
+ if err != nil { |
|
562 |
+ return nil, err |
|
563 |
+ } |
|
564 |
+ return image.Changes(container.rwPath()) |
|
565 |
+} |
|
566 |
+ |
|
567 |
+func (container *Container) GetImage() (*graph.Image, error) { |
|
568 |
+ if container.runtime == nil { |
|
569 |
+ return nil, fmt.Errorf("Can't get image of unregistered container") |
|
570 |
+ } |
|
571 |
+ return container.runtime.graph.Get(container.Image) |
|
572 |
+} |
|
573 |
+ |
|
574 |
+func (container *Container) Mounted() (bool, error) { |
|
575 |
+ return graph.Mounted(container.RootfsPath()) |
|
576 |
+} |
|
577 |
+ |
|
578 |
+func (container *Container) Unmount() error { |
|
579 |
+ return graph.Unmount(container.RootfsPath()) |
|
580 |
+} |
|
581 |
+ |
|
582 |
+func (container *Container) logPath(name string) string { |
|
583 |
+ return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name)) |
|
584 |
+} |
|
585 |
+ |
|
586 |
+func (container *Container) ReadLog(name string) (io.Reader, error) { |
|
587 |
+ return os.Open(container.logPath(name)) |
|
588 |
+} |
|
589 |
+ |
|
590 |
+func (container *Container) jsonPath() string { |
|
591 |
+ return path.Join(container.root, "config.json") |
|
592 |
+} |
|
593 |
+ |
|
594 |
+func (container *Container) lxcConfigPath() string { |
|
595 |
+ return path.Join(container.root, "config.lxc") |
|
596 |
+} |
|
597 |
+ |
|
598 |
+// This method must be exported to be used from the lxc template |
|
599 |
+func (container *Container) RootfsPath() string { |
|
600 |
+ return path.Join(container.root, "rootfs") |
|
601 |
+} |
|
602 |
+ |
|
603 |
+func (container *Container) rwPath() string { |
|
604 |
+ return path.Join(container.root, "rw") |
|
605 |
+} |
|
606 |
+ |
|
607 |
+func validateId(id string) error { |
|
608 |
+ if id == "" { |
|
609 |
+ return fmt.Errorf("Invalid empty id") |
|
610 |
+ } |
|
611 |
+ return nil |
|
612 |
+} |
... | ... |
@@ -3,7 +3,6 @@ package docker |
3 | 3 |
import ( |
4 | 4 |
"bufio" |
5 | 5 |
"fmt" |
6 |
- "github.com/dotcloud/docker/fs" |
|
7 | 6 |
"io" |
8 | 7 |
"io/ioutil" |
9 | 8 |
"math/rand" |
... | ... |
@@ -21,10 +20,9 @@ func TestCommitRun(t *testing.T) { |
21 | 21 |
} |
22 | 22 |
defer nuke(docker) |
23 | 23 |
container1, err := docker.Create( |
24 |
- "precommit_test", |
|
25 | 24 |
"/bin/sh", |
26 | 25 |
[]string{"-c", "echo hello > /world"}, |
27 |
- GetTestImage(docker), |
|
26 |
+ GetTestImage(docker).Id, |
|
28 | 27 |
&Config{ |
29 | 28 |
Memory: 33554432, |
30 | 29 |
}, |
... | ... |
@@ -44,18 +42,11 @@ func TestCommitRun(t *testing.T) { |
44 | 44 |
t.Errorf("Container shouldn't be running") |
45 | 45 |
} |
46 | 46 |
|
47 |
- // FIXME: freeze the container before copying it to avoid data corruption? |
|
48 |
- rwTar, err := fs.Tar(container1.Mountpoint.Rw, fs.Uncompressed) |
|
47 |
+ rwTar, err := container1.ExportRw() |
|
49 | 48 |
if err != nil { |
50 | 49 |
t.Error(err) |
51 | 50 |
} |
52 |
- // Create a new image from the container's base layers + a new layer from container changes |
|
53 |
- parentImg, err := docker.Store.Get(container1.Image) |
|
54 |
- if err != nil { |
|
55 |
- t.Error(err) |
|
56 |
- } |
|
57 |
- |
|
58 |
- img, err := docker.Store.Create(rwTar, parentImg, "test_commitrun", "unit test commited image") |
|
51 |
+ img, err := docker.graph.Create(rwTar, container1.Image, "unit test commited image") |
|
59 | 52 |
if err != nil { |
60 | 53 |
t.Error(err) |
61 | 54 |
} |
... | ... |
@@ -63,10 +54,9 @@ func TestCommitRun(t *testing.T) { |
63 | 63 |
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world |
64 | 64 |
|
65 | 65 |
container2, err := docker.Create( |
66 |
- "postcommit_test", |
|
67 | 66 |
"cat", |
68 | 67 |
[]string{"/world"}, |
69 |
- img, |
|
68 |
+ img.Id, |
|
70 | 69 |
&Config{ |
71 | 70 |
Memory: 33554432, |
72 | 71 |
}, |
... | ... |
@@ -98,10 +88,9 @@ func TestRun(t *testing.T) { |
98 | 98 |
} |
99 | 99 |
defer nuke(docker) |
100 | 100 |
container, err := docker.Create( |
101 |
- "run_test", |
|
102 | 101 |
"ls", |
103 | 102 |
[]string{"-al"}, |
104 |
- GetTestImage(docker), |
|
103 |
+ GetTestImage(docker).Id, |
|
105 | 104 |
&Config{ |
106 | 105 |
Memory: 33554432, |
107 | 106 |
}, |
... | ... |
@@ -129,10 +118,9 @@ func TestOutput(t *testing.T) { |
129 | 129 |
} |
130 | 130 |
defer nuke(docker) |
131 | 131 |
container, err := docker.Create( |
132 |
- "output_test", |
|
133 | 132 |
"echo", |
134 | 133 |
[]string{"-n", "foobar"}, |
135 |
- GetTestImage(docker), |
|
134 |
+ GetTestImage(docker).Id, |
|
136 | 135 |
&Config{}, |
137 | 136 |
) |
138 | 137 |
if err != nil { |
... | ... |
@@ -155,10 +143,9 @@ func TestKill(t *testing.T) { |
155 | 155 |
} |
156 | 156 |
defer nuke(docker) |
157 | 157 |
container, err := docker.Create( |
158 |
- "stop_test", |
|
159 | 158 |
"cat", |
160 | 159 |
[]string{"/dev/zero"}, |
161 |
- GetTestImage(docker), |
|
160 |
+ GetTestImage(docker).Id, |
|
162 | 161 |
&Config{}, |
163 | 162 |
) |
164 | 163 |
if err != nil { |
... | ... |
@@ -199,10 +186,9 @@ func TestExitCode(t *testing.T) { |
199 | 199 |
defer nuke(docker) |
200 | 200 |
|
201 | 201 |
trueContainer, err := docker.Create( |
202 |
- "exit_test_1", |
|
203 | 202 |
"/bin/true", |
204 | 203 |
[]string{""}, |
205 |
- GetTestImage(docker), |
|
204 |
+ GetTestImage(docker).Id, |
|
206 | 205 |
&Config{}, |
207 | 206 |
) |
208 | 207 |
if err != nil { |
... | ... |
@@ -214,10 +200,9 @@ func TestExitCode(t *testing.T) { |
214 | 214 |
} |
215 | 215 |
|
216 | 216 |
falseContainer, err := docker.Create( |
217 |
- "exit_test_2", |
|
218 | 217 |
"/bin/false", |
219 | 218 |
[]string{""}, |
220 |
- GetTestImage(docker), |
|
219 |
+ GetTestImage(docker).Id, |
|
221 | 220 |
&Config{}, |
222 | 221 |
) |
223 | 222 |
if err != nil { |
... | ... |
@@ -244,10 +229,9 @@ func TestRestart(t *testing.T) { |
244 | 244 |
} |
245 | 245 |
defer nuke(docker) |
246 | 246 |
container, err := docker.Create( |
247 |
- "restart_test", |
|
248 | 247 |
"echo", |
249 | 248 |
[]string{"-n", "foobar"}, |
250 |
- GetTestImage(docker), |
|
249 |
+ GetTestImage(docker).Id, |
|
251 | 250 |
&Config{}, |
252 | 251 |
) |
253 | 252 |
if err != nil { |
... | ... |
@@ -279,10 +263,9 @@ func TestRestartStdin(t *testing.T) { |
279 | 279 |
} |
280 | 280 |
defer nuke(docker) |
281 | 281 |
container, err := docker.Create( |
282 |
- "restart_stdin_test", |
|
283 | 282 |
"cat", |
284 | 283 |
[]string{}, |
285 |
- GetTestImage(docker), |
|
284 |
+ GetTestImage(docker).Id, |
|
286 | 285 |
&Config{ |
287 | 286 |
OpenStdin: true, |
288 | 287 |
}, |
... | ... |
@@ -331,10 +314,9 @@ func TestUser(t *testing.T) { |
331 | 331 |
|
332 | 332 |
// Default user must be root |
333 | 333 |
container, err := docker.Create( |
334 |
- "user_default", |
|
335 | 334 |
"id", |
336 | 335 |
[]string{}, |
337 |
- GetTestImage(docker), |
|
336 |
+ GetTestImage(docker).Id, |
|
338 | 337 |
&Config{}, |
339 | 338 |
) |
340 | 339 |
if err != nil { |
... | ... |
@@ -351,10 +333,9 @@ func TestUser(t *testing.T) { |
351 | 351 |
|
352 | 352 |
// Set a username |
353 | 353 |
container, err = docker.Create( |
354 |
- "user_root", |
|
355 | 354 |
"id", |
356 | 355 |
[]string{}, |
357 |
- GetTestImage(docker), |
|
356 |
+ GetTestImage(docker).Id, |
|
358 | 357 |
&Config{ |
359 | 358 |
User: "root", |
360 | 359 |
}, |
... | ... |
@@ -373,10 +354,9 @@ func TestUser(t *testing.T) { |
373 | 373 |
|
374 | 374 |
// Set a UID |
375 | 375 |
container, err = docker.Create( |
376 |
- "user_uid0", |
|
377 | 376 |
"id", |
378 | 377 |
[]string{}, |
379 |
- GetTestImage(docker), |
|
378 |
+ GetTestImage(docker).Id, |
|
380 | 379 |
&Config{ |
381 | 380 |
User: "0", |
382 | 381 |
}, |
... | ... |
@@ -395,10 +375,9 @@ func TestUser(t *testing.T) { |
395 | 395 |
|
396 | 396 |
// Set a different user by uid |
397 | 397 |
container, err = docker.Create( |
398 |
- "user_uid1", |
|
399 | 398 |
"id", |
400 | 399 |
[]string{}, |
401 |
- GetTestImage(docker), |
|
400 |
+ GetTestImage(docker).Id, |
|
402 | 401 |
&Config{ |
403 | 402 |
User: "1", |
404 | 403 |
}, |
... | ... |
@@ -419,10 +398,9 @@ func TestUser(t *testing.T) { |
419 | 419 |
|
420 | 420 |
// Set a different user by username |
421 | 421 |
container, err = docker.Create( |
422 |
- "user_daemon", |
|
423 | 422 |
"id", |
424 | 423 |
[]string{}, |
425 |
- GetTestImage(docker), |
|
424 |
+ GetTestImage(docker).Id, |
|
426 | 425 |
&Config{ |
427 | 426 |
User: "daemon", |
428 | 427 |
}, |
... | ... |
@@ -448,10 +426,9 @@ func TestMultipleContainers(t *testing.T) { |
448 | 448 |
defer nuke(docker) |
449 | 449 |
|
450 | 450 |
container1, err := docker.Create( |
451 |
- "container1", |
|
452 | 451 |
"cat", |
453 | 452 |
[]string{"/dev/zero"}, |
454 |
- GetTestImage(docker), |
|
453 |
+ GetTestImage(docker).Id, |
|
455 | 454 |
&Config{}, |
456 | 455 |
) |
457 | 456 |
if err != nil { |
... | ... |
@@ -460,10 +437,9 @@ func TestMultipleContainers(t *testing.T) { |
460 | 460 |
defer docker.Destroy(container1) |
461 | 461 |
|
462 | 462 |
container2, err := docker.Create( |
463 |
- "container2", |
|
464 | 463 |
"cat", |
465 | 464 |
[]string{"/dev/zero"}, |
466 |
- GetTestImage(docker), |
|
465 |
+ GetTestImage(docker).Id, |
|
467 | 466 |
&Config{}, |
468 | 467 |
) |
469 | 468 |
if err != nil { |
... | ... |
@@ -504,10 +480,9 @@ func TestStdin(t *testing.T) { |
504 | 504 |
} |
505 | 505 |
defer nuke(docker) |
506 | 506 |
container, err := docker.Create( |
507 |
- "stdin_test", |
|
508 | 507 |
"cat", |
509 | 508 |
[]string{}, |
510 |
- GetTestImage(docker), |
|
509 |
+ GetTestImage(docker).Id, |
|
511 | 510 |
&Config{ |
512 | 511 |
OpenStdin: true, |
513 | 512 |
}, |
... | ... |
@@ -540,10 +515,9 @@ func TestTty(t *testing.T) { |
540 | 540 |
} |
541 | 541 |
defer nuke(docker) |
542 | 542 |
container, err := docker.Create( |
543 |
- "tty_test", |
|
544 | 543 |
"cat", |
545 | 544 |
[]string{}, |
546 |
- GetTestImage(docker), |
|
545 |
+ GetTestImage(docker).Id, |
|
547 | 546 |
&Config{ |
548 | 547 |
OpenStdin: true, |
549 | 548 |
}, |
... | ... |
@@ -576,10 +550,9 @@ func TestEnv(t *testing.T) { |
576 | 576 |
} |
577 | 577 |
defer nuke(docker) |
578 | 578 |
container, err := docker.Create( |
579 |
- "env_test", |
|
580 | 579 |
"/usr/bin/env", |
581 | 580 |
[]string{}, |
582 |
- GetTestImage(docker), |
|
581 |
+ GetTestImage(docker).Id, |
|
583 | 582 |
&Config{}, |
584 | 583 |
) |
585 | 584 |
if err != nil { |
... | ... |
@@ -651,10 +624,9 @@ func TestLXCConfig(t *testing.T) { |
651 | 651 |
memMax := 536870912 |
652 | 652 |
mem := memMin + rand.Intn(memMax-memMin) |
653 | 653 |
container, err := docker.Create( |
654 |
- "config_test", |
|
655 | 654 |
"/bin/true", |
656 | 655 |
[]string{}, |
657 |
- GetTestImage(docker), |
|
656 |
+ GetTestImage(docker).Id, |
|
658 | 657 |
&Config{ |
659 | 658 |
Hostname: "foobar", |
660 | 659 |
Memory: int64(mem), |
... | ... |
@@ -665,10 +637,10 @@ func TestLXCConfig(t *testing.T) { |
665 | 665 |
} |
666 | 666 |
defer docker.Destroy(container) |
667 | 667 |
container.generateLXCConfig() |
668 |
- grepFile(t, container.lxcConfigPath, "lxc.utsname = foobar") |
|
669 |
- grepFile(t, container.lxcConfigPath, |
|
668 |
+ grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar") |
|
669 |
+ grepFile(t, container.lxcConfigPath(), |
|
670 | 670 |
fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem)) |
671 |
- grepFile(t, container.lxcConfigPath, |
|
671 |
+ grepFile(t, container.lxcConfigPath(), |
|
672 | 672 |
fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2)) |
673 | 673 |
} |
674 | 674 |
|
... | ... |
@@ -680,10 +652,9 @@ func BenchmarkRunSequencial(b *testing.B) { |
680 | 680 |
defer nuke(docker) |
681 | 681 |
for i := 0; i < b.N; i++ { |
682 | 682 |
container, err := docker.Create( |
683 |
- fmt.Sprintf("bench_%v", i), |
|
684 | 683 |
"echo", |
685 | 684 |
[]string{"-n", "foo"}, |
686 |
- GetTestImage(docker), |
|
685 |
+ GetTestImage(docker).Id, |
|
687 | 686 |
&Config{}, |
688 | 687 |
) |
689 | 688 |
if err != nil { |
... | ... |
@@ -717,10 +688,9 @@ func BenchmarkRunParallel(b *testing.B) { |
717 | 717 |
tasks = append(tasks, complete) |
718 | 718 |
go func(i int, complete chan error) { |
719 | 719 |
container, err := docker.Create( |
720 |
- fmt.Sprintf("bench_%v", i), |
|
721 | 720 |
"echo", |
722 | 721 |
[]string{"-n", "foo"}, |
723 |
- GetTestImage(docker), |
|
722 |
+ GetTestImage(docker).Id, |
|
724 | 723 |
&Config{}, |
725 | 724 |
) |
726 | 725 |
if err != nil { |
... | ... |
@@ -3,12 +3,15 @@ package docker |
3 | 3 |
import ( |
4 | 4 |
"container/list" |
5 | 5 |
"fmt" |
6 |
- "github.com/dotcloud/docker/fs" |
|
6 |
+ "github.com/dotcloud/docker/graph" |
|
7 |
+ "io" |
|
7 | 8 |
"io/ioutil" |
8 | 9 |
"log" |
9 | 10 |
"os" |
10 | 11 |
"path" |
11 | 12 |
"sort" |
13 |
+ "sync" |
|
14 |
+ "time" |
|
12 | 15 |
) |
13 | 16 |
|
14 | 17 |
type Docker struct { |
... | ... |
@@ -16,7 +19,13 @@ type Docker struct { |
16 | 16 |
repository string |
17 | 17 |
containers *list.List |
18 | 18 |
networkManager *NetworkManager |
19 |
- Store *fs.Store |
|
19 |
+ graph *graph.Graph |
|
20 |
+} |
|
21 |
+ |
|
22 |
+var sysInitPath string |
|
23 |
+ |
|
24 |
+func init() { |
|
25 |
+ sysInitPath = SelfPath() |
|
20 | 26 |
} |
21 | 27 |
|
22 | 28 |
func (docker *Docker) List() []*Container { |
... | ... |
@@ -49,20 +58,98 @@ func (docker *Docker) Exists(id string) bool { |
49 | 49 |
return docker.Get(id) != nil |
50 | 50 |
} |
51 | 51 |
|
52 |
-func (docker *Docker) Create(id string, command string, args []string, image *fs.Image, config *Config) (*Container, error) { |
|
53 |
- if docker.Exists(id) { |
|
54 |
- return nil, fmt.Errorf("Container %v already exists", id) |
|
52 |
+func (docker *Docker) containerRoot(id string) string { |
|
53 |
+ return path.Join(docker.repository, id) |
|
54 |
+} |
|
55 |
+ |
|
56 |
+func (docker *Docker) Create(command string, args []string, image string, config *Config) (*Container, error) { |
|
57 |
+ container := &Container{ |
|
58 |
+ // FIXME: we should generate the ID here instead of receiving it as an argument |
|
59 |
+ Id: GenerateId(), |
|
60 |
+ Created: time.Now(), |
|
61 |
+ Path: command, |
|
62 |
+ Args: args, |
|
63 |
+ Config: config, |
|
64 |
+ Image: image, |
|
65 |
+ NetworkSettings: &NetworkSettings{}, |
|
66 |
+ // FIXME: do we need to store this in the container? |
|
67 |
+ SysInitPath: sysInitPath, |
|
68 |
+ } |
|
69 |
+ container.root = docker.containerRoot(container.Id) |
|
70 |
+ // Step 1: create the container directory. |
|
71 |
+ // This doubles as a barrier to avoid race conditions. |
|
72 |
+ if err := os.Mkdir(container.root, 0700); err != nil { |
|
73 |
+ return nil, err |
|
74 |
+ } |
|
75 |
+ // Step 2: save the container json |
|
76 |
+ if err := container.ToDisk(); err != nil { |
|
77 |
+ return nil, err |
|
78 |
+ } |
|
79 |
+ // Step 3: register the container |
|
80 |
+ if err := docker.Register(container); err != nil { |
|
81 |
+ return nil, err |
|
55 | 82 |
} |
56 |
- root := path.Join(docker.repository, id) |
|
83 |
+ return container, nil |
|
84 |
+} |
|
57 | 85 |
|
58 |
- container, err := createContainer(id, root, command, args, image, config, docker.networkManager) |
|
59 |
- if err != nil { |
|
86 |
+func (docker *Docker) Load(id string) (*Container, error) { |
|
87 |
+ container := &Container{root: docker.containerRoot(id)} |
|
88 |
+ if err := container.FromDisk(); err != nil { |
|
89 |
+ return nil, err |
|
90 |
+ } |
|
91 |
+ if container.Id != id { |
|
92 |
+ return container, fmt.Errorf("Container %s is stored at %s", container.Id, id) |
|
93 |
+ } |
|
94 |
+ if err := docker.Register(container); err != nil { |
|
60 | 95 |
return nil, err |
61 | 96 |
} |
62 |
- docker.containers.PushBack(container) |
|
63 | 97 |
return container, nil |
64 | 98 |
} |
65 | 99 |
|
100 |
+// Register makes a container object usable by the runtime as <container.Id> |
|
101 |
+func (docker *Docker) Register(container *Container) error { |
|
102 |
+ if container.runtime != nil || docker.Exists(container.Id) { |
|
103 |
+ return fmt.Errorf("Container is already loaded") |
|
104 |
+ } |
|
105 |
+ if err := validateId(container.Id); err != nil { |
|
106 |
+ return err |
|
107 |
+ } |
|
108 |
+ container.runtime = docker |
|
109 |
+ container.networkManager = docker.networkManager // FIXME: infer from docker.runtime |
|
110 |
+ // Setup state lock (formerly in newState() |
|
111 |
+ lock := new(sync.Mutex) |
|
112 |
+ container.State.stateChangeLock = lock |
|
113 |
+ container.State.stateChangeCond = sync.NewCond(lock) |
|
114 |
+ // Attach to stdout and stderr |
|
115 |
+ container.stderr = newWriteBroadcaster() |
|
116 |
+ container.stdout = newWriteBroadcaster() |
|
117 |
+ // Attach to stdin |
|
118 |
+ if container.Config.OpenStdin { |
|
119 |
+ container.stdin, container.stdinPipe = io.Pipe() |
|
120 |
+ } else { |
|
121 |
+ container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin |
|
122 |
+ } |
|
123 |
+ // Setup logging of stdout and stderr to disk |
|
124 |
+ if err := docker.LogToDisk(container.stdout, container.logPath("stdout")); err != nil { |
|
125 |
+ return err |
|
126 |
+ } |
|
127 |
+ if err := docker.LogToDisk(container.stderr, container.logPath("stderr")); err != nil { |
|
128 |
+ return err |
|
129 |
+ } |
|
130 |
+ // done |
|
131 |
+ docker.containers.PushBack(container) |
|
132 |
+ return nil |
|
133 |
+} |
|
134 |
+ |
|
135 |
+func (docker *Docker) LogToDisk(src *writeBroadcaster, dst string) error { |
|
136 |
+ log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) |
|
137 |
+ if err != nil { |
|
138 |
+ return err |
|
139 |
+ } |
|
140 |
+ src.AddWriter(NopWriteCloser(log)) |
|
141 |
+ return nil |
|
142 |
+} |
|
143 |
+ |
|
66 | 144 |
func (docker *Docker) Destroy(container *Container) error { |
67 | 145 |
element := docker.getContainerElement(container.Id) |
68 | 146 |
if element == nil { |
... | ... |
@@ -72,18 +159,18 @@ func (docker *Docker) Destroy(container *Container) error { |
72 | 72 |
if err := container.Stop(); err != nil { |
73 | 73 |
return err |
74 | 74 |
} |
75 |
- if container.Mountpoint.Mounted() { |
|
76 |
- if err := container.Mountpoint.Umount(); err != nil { |
|
77 |
- return fmt.Errorf("Unable to umount container %v: %v", container.Id, err) |
|
75 |
+ if mounted, err := container.Mounted(); err != nil { |
|
76 |
+ return err |
|
77 |
+ } else if mounted { |
|
78 |
+ if err := container.Unmount(); err != nil { |
|
79 |
+ return fmt.Errorf("Unable to unmount container %v: %v", container.Id, err) |
|
78 | 80 |
} |
79 | 81 |
} |
80 |
- if err := container.Mountpoint.Deregister(); err != nil { |
|
81 |
- return fmt.Errorf("Unable to deregiser -- ? mountpoint %v: %v", container.Mountpoint.Root, err) |
|
82 |
- } |
|
83 |
- if err := os.RemoveAll(container.Root); err != nil { |
|
82 |
+ // Deregister the container before removing its directory, to avoid race conditions |
|
83 |
+ docker.containers.Remove(element) |
|
84 |
+ if err := os.RemoveAll(container.root); err != nil { |
|
84 | 85 |
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.Id, err) |
85 | 86 |
} |
86 |
- docker.containers.Remove(element) |
|
87 | 87 |
return nil |
88 | 88 |
} |
89 | 89 |
|
... | ... |
@@ -93,12 +180,13 @@ func (docker *Docker) restore() error { |
93 | 93 |
return err |
94 | 94 |
} |
95 | 95 |
for _, v := range dir { |
96 |
- container, err := loadContainer(docker.Store, path.Join(docker.repository, v.Name()), docker.networkManager) |
|
96 |
+ id := v.Name() |
|
97 |
+ container, err := docker.Load(id) |
|
97 | 98 |
if err != nil { |
98 |
- log.Printf("Failed to load container %v: %v", v.Name(), err) |
|
99 |
+ log.Printf("Failed to load container %v: %v", id, err) |
|
99 | 100 |
continue |
100 | 101 |
} |
101 |
- docker.containers.PushBack(container) |
|
102 |
+ log.Printf("Loaded container %v", container.Id) |
|
102 | 103 |
} |
103 | 104 |
return nil |
104 | 105 |
} |
... | ... |
@@ -114,7 +202,7 @@ func NewFromDirectory(root string) (*Docker, error) { |
114 | 114 |
return nil, err |
115 | 115 |
} |
116 | 116 |
|
117 |
- store, err := fs.New(path.Join(root, "images")) |
|
117 |
+ graph, err := graph.New(path.Join(root, "graph")) |
|
118 | 118 |
if err != nil { |
119 | 119 |
return nil, err |
120 | 120 |
} |
... | ... |
@@ -127,8 +215,8 @@ func NewFromDirectory(root string) (*Docker, error) { |
127 | 127 |
root: root, |
128 | 128 |
repository: docker_repo, |
129 | 129 |
containers: list.New(), |
130 |
- Store: store, |
|
131 | 130 |
networkManager: netManager, |
131 |
+ graph: graph, |
|
132 | 132 |
} |
133 | 133 |
|
134 | 134 |
if err := docker.restore(); err != nil { |
... | ... |
@@ -1,7 +1,7 @@ |
1 | 1 |
package docker |
2 | 2 |
|
3 | 3 |
import ( |
4 |
- "github.com/dotcloud/docker/fs" |
|
4 |
+ "github.com/dotcloud/docker/graph" |
|
5 | 5 |
"io" |
6 | 6 |
"io/ioutil" |
7 | 7 |
"os" |
... | ... |
@@ -63,7 +63,6 @@ func init() { |
63 | 63 |
} |
64 | 64 |
// Create the "Server" |
65 | 65 |
srv := &Server{ |
66 |
- images: docker.Store, |
|
67 | 66 |
containers: docker, |
68 | 67 |
} |
69 | 68 |
// Retrieve the Image |
... | ... |
@@ -93,8 +92,8 @@ func newTestDocker() (*Docker, error) { |
93 | 93 |
return docker, nil |
94 | 94 |
} |
95 | 95 |
|
96 |
-func GetTestImage(docker *Docker) *fs.Image { |
|
97 |
- imgs, err := docker.Store.Images() |
|
96 |
+func GetTestImage(docker *Docker) *graph.Image { |
|
97 |
+ imgs, err := docker.graph.All() |
|
98 | 98 |
if err != nil { |
99 | 99 |
panic(err) |
100 | 100 |
} else if len(imgs) < 1 { |
... | ... |
@@ -115,10 +114,9 @@ func TestCreate(t *testing.T) { |
115 | 115 |
t.Errorf("Expected 0 containers, %v found", len(docker.List())) |
116 | 116 |
} |
117 | 117 |
container, err := docker.Create( |
118 |
- "test_create", |
|
119 | 118 |
"ls", |
120 | 119 |
[]string{"-al"}, |
121 |
- GetTestImage(docker), |
|
120 |
+ GetTestImage(docker).Id, |
|
122 | 121 |
&Config{}, |
123 | 122 |
) |
124 | 123 |
if err != nil { |
... | ... |
@@ -137,22 +135,22 @@ func TestCreate(t *testing.T) { |
137 | 137 |
} |
138 | 138 |
|
139 | 139 |
// Make sure the container List() returns is the right one |
140 |
- if docker.List()[0].Id != "test_create" { |
|
140 |
+ if docker.List()[0].Id != container.Id { |
|
141 | 141 |
t.Errorf("Unexpected container %v returned by List", docker.List()[0]) |
142 | 142 |
} |
143 | 143 |
|
144 | 144 |
// Make sure we can get the container with Get() |
145 |
- if docker.Get("test_create") == nil { |
|
145 |
+ if docker.Get(container.Id) == nil { |
|
146 | 146 |
t.Errorf("Unable to get newly created container") |
147 | 147 |
} |
148 | 148 |
|
149 | 149 |
// Make sure it is the right container |
150 |
- if docker.Get("test_create") != container { |
|
150 |
+ if docker.Get(container.Id) != container { |
|
151 | 151 |
t.Errorf("Get() returned the wrong container") |
152 | 152 |
} |
153 | 153 |
|
154 | 154 |
// Make sure Exists returns it as existing |
155 |
- if !docker.Exists("test_create") { |
|
155 |
+ if !docker.Exists(container.Id) { |
|
156 | 156 |
t.Errorf("Exists() returned false for a newly created container") |
157 | 157 |
} |
158 | 158 |
} |
... | ... |
@@ -164,10 +162,9 @@ func TestDestroy(t *testing.T) { |
164 | 164 |
} |
165 | 165 |
defer nuke(docker) |
166 | 166 |
container, err := docker.Create( |
167 |
- "test_destroy", |
|
168 | 167 |
"ls", |
169 | 168 |
[]string{"-al"}, |
170 |
- GetTestImage(docker), |
|
169 |
+ GetTestImage(docker).Id, |
|
171 | 170 |
&Config{}, |
172 | 171 |
) |
173 | 172 |
if err != nil { |
... | ... |
@@ -189,12 +186,12 @@ func TestDestroy(t *testing.T) { |
189 | 189 |
} |
190 | 190 |
|
191 | 191 |
// Make sure docker.Get() refuses to return the unexisting container |
192 |
- if docker.Get("test_destroy") != nil { |
|
192 |
+ if docker.Get(container.Id) != nil { |
|
193 | 193 |
t.Errorf("Unable to get newly created container") |
194 | 194 |
} |
195 | 195 |
|
196 | 196 |
// Make sure the container root directory does not exist anymore |
197 |
- _, err = os.Stat(container.Root) |
|
197 |
+ _, err = os.Stat(container.root) |
|
198 | 198 |
if err == nil || !os.IsNotExist(err) { |
199 | 199 |
t.Errorf("Container root directory still exists after destroy") |
200 | 200 |
} |
... | ... |
@@ -213,10 +210,9 @@ func TestGet(t *testing.T) { |
213 | 213 |
} |
214 | 214 |
defer nuke(docker) |
215 | 215 |
container1, err := docker.Create( |
216 |
- "test1", |
|
217 | 216 |
"ls", |
218 | 217 |
[]string{"-al"}, |
219 |
- GetTestImage(docker), |
|
218 |
+ GetTestImage(docker).Id, |
|
220 | 219 |
&Config{}, |
221 | 220 |
) |
222 | 221 |
if err != nil { |
... | ... |
@@ -225,10 +221,9 @@ func TestGet(t *testing.T) { |
225 | 225 |
defer docker.Destroy(container1) |
226 | 226 |
|
227 | 227 |
container2, err := docker.Create( |
228 |
- "test2", |
|
229 | 228 |
"ls", |
230 | 229 |
[]string{"-al"}, |
231 |
- GetTestImage(docker), |
|
230 |
+ GetTestImage(docker).Id, |
|
232 | 231 |
&Config{}, |
233 | 232 |
) |
234 | 233 |
if err != nil { |
... | ... |
@@ -237,10 +232,9 @@ func TestGet(t *testing.T) { |
237 | 237 |
defer docker.Destroy(container2) |
238 | 238 |
|
239 | 239 |
container3, err := docker.Create( |
240 |
- "test3", |
|
241 | 240 |
"ls", |
242 | 241 |
[]string{"-al"}, |
243 |
- GetTestImage(docker), |
|
242 |
+ GetTestImage(docker).Id, |
|
244 | 243 |
&Config{}, |
245 | 244 |
) |
246 | 245 |
if err != nil { |
... | ... |
@@ -248,16 +242,16 @@ func TestGet(t *testing.T) { |
248 | 248 |
} |
249 | 249 |
defer docker.Destroy(container3) |
250 | 250 |
|
251 |
- if docker.Get("test1") != container1 { |
|
252 |
- t.Errorf("Get(test1) returned %v while expecting %v", docker.Get("test1"), container1) |
|
251 |
+ if docker.Get(container1.Id) != container1 { |
|
252 |
+ t.Errorf("Get(test1) returned %v while expecting %v", docker.Get(container1.Id), container1) |
|
253 | 253 |
} |
254 | 254 |
|
255 |
- if docker.Get("test2") != container2 { |
|
256 |
- t.Errorf("Get(test2) returned %v while expecting %v", docker.Get("test2"), container2) |
|
255 |
+ if docker.Get(container2.Id) != container2 { |
|
256 |
+ t.Errorf("Get(test2) returned %v while expecting %v", docker.Get(container2.Id), container2) |
|
257 | 257 |
} |
258 | 258 |
|
259 |
- if docker.Get("test3") != container3 { |
|
260 |
- t.Errorf("Get(test3) returned %v while expecting %v", docker.Get("test3"), container3) |
|
259 |
+ if docker.Get(container3.Id) != container3 { |
|
260 |
+ t.Errorf("Get(test3) returned %v while expecting %v", docker.Get(container3.Id), container3) |
|
261 | 261 |
} |
262 | 262 |
|
263 | 263 |
} |
... | ... |
@@ -282,10 +276,9 @@ func TestRestore(t *testing.T) { |
282 | 282 |
|
283 | 283 |
// Create a container with one instance of docker |
284 | 284 |
container1, err := docker1.Create( |
285 |
- "restore_test", |
|
286 | 285 |
"ls", |
287 | 286 |
[]string{"-al"}, |
288 |
- GetTestImage(docker1), |
|
287 |
+ GetTestImage(docker1).Id, |
|
289 | 288 |
&Config{}, |
290 | 289 |
) |
291 | 290 |
if err != nil { |
... | ... |
@@ -309,7 +302,7 @@ func TestRestore(t *testing.T) { |
309 | 309 |
if len(docker2.List()) != 1 { |
310 | 310 |
t.Errorf("Expected 1 container, %v found", len(docker2.List())) |
311 | 311 |
} |
312 |
- container2 := docker2.Get("restore_test") |
|
312 |
+ container2 := docker2.Get(container1.Id) |
|
313 | 313 |
if container2 == nil { |
314 | 314 |
t.Fatal("Unable to Get container") |
315 | 315 |
} |
... | ... |
@@ -22,7 +22,7 @@ lxc.network.mtu = 1500 |
22 | 22 |
lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}} |
23 | 23 |
|
24 | 24 |
# root filesystem |
25 |
-{{$ROOTFS := .Mountpoint.Root}} |
|
25 |
+{{$ROOTFS := .RootfsPath}} |
|
26 | 26 |
lxc.rootfs = {{$ROOTFS}} |
27 | 27 |
|
28 | 28 |
# use a dedicated pts for the container (and limit the number of pseudo terminal |
... | ... |
@@ -17,14 +17,6 @@ type State struct { |
17 | 17 |
stateChangeCond *sync.Cond |
18 | 18 |
} |
19 | 19 |
|
20 |
-func newState() *State { |
|
21 |
- lock := new(sync.Mutex) |
|
22 |
- return &State{ |
|
23 |
- stateChangeLock: lock, |
|
24 |
- stateChangeCond: sync.NewCond(lock), |
|
25 |
- } |
|
26 |
-} |
|
27 |
- |
|
28 | 20 |
// String returns a human-readable description of the state |
29 | 21 |
func (s *State) String() string { |
30 | 22 |
if s.Running { |