mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Simplified the core container API, ported it to the new graph. Some features are missing eg. image 'paths' and tags
This commit is contained in:
parent
84e8c4aa1d
commit
7c57a4cfc0
7 changed files with 363 additions and 430 deletions
208
commands.go
208
commands.go
|
@ -1,20 +1,16 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/auth"
|
"github.com/dotcloud/docker/auth"
|
||||||
"github.com/dotcloud/docker/fs"
|
|
||||||
"github.com/dotcloud/docker/future"
|
"github.com/dotcloud/docker/future"
|
||||||
"github.com/dotcloud/docker/rcli"
|
"github.com/dotcloud/docker/rcli"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -137,7 +133,7 @@ func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...str
|
||||||
|
|
||||||
// 'docker info': display system-wide information.
|
// 'docker info': display system-wide information.
|
||||||
func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
images, _ := srv.images.Images()
|
images, _ := srv.containers.graph.All()
|
||||||
var imgcount int
|
var imgcount int
|
||||||
if images == nil {
|
if images == nil {
|
||||||
imgcount = 0
|
imgcount = 0
|
||||||
|
@ -236,7 +232,7 @@ func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...strin
|
||||||
}
|
}
|
||||||
for _, name := range cmd.Args() {
|
for _, name := range cmd.Args() {
|
||||||
if container := srv.containers.Get(name); container != nil {
|
if container := srv.containers.Get(name); container != nil {
|
||||||
if err := container.Mountpoint.EnsureMounted(); err != nil {
|
if err := container.EnsureMounted(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintln(stdout, container.Id)
|
fmt.Fprintln(stdout, container.Id)
|
||||||
|
@ -260,7 +256,7 @@ func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...str
|
||||||
var obj interface{}
|
var obj interface{}
|
||||||
if container := srv.containers.Get(name); container != nil {
|
if container := srv.containers.Get(name); container != nil {
|
||||||
obj = container
|
obj = container
|
||||||
} else if image, err := srv.images.Find(name); err != nil {
|
} else if image, err := srv.containers.graph.Get(name); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if image != nil {
|
} else if image != nil {
|
||||||
obj = image
|
obj = image
|
||||||
|
@ -310,27 +306,12 @@ func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
// 'docker rmi NAME' removes all images with the name NAME
|
// 'docker rmi NAME' removes all images with the name NAME
|
||||||
func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) (err error) {
|
func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) (err error) {
|
||||||
cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
|
cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image")
|
||||||
fl_all := cmd.Bool("a", false, "Use IMAGE as a path and remove ALL images in this path")
|
|
||||||
fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name")
|
|
||||||
if cmd.Parse(args) != nil || cmd.NArg() < 1 {
|
if cmd.Parse(args) != nil || cmd.NArg() < 1 {
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, name := range cmd.Args() {
|
for _, name := range cmd.Args() {
|
||||||
if *fl_regexp {
|
if err := srv.containers.graph.Delete(name); err != nil {
|
||||||
err = srv.images.RemoveRegexp(name)
|
|
||||||
} else if *fl_all {
|
|
||||||
err = srv.images.RemoveInPath(name)
|
|
||||||
} else {
|
|
||||||
if image, err1 := srv.images.Find(name); err1 != nil {
|
|
||||||
err = err1
|
|
||||||
} else if err1 == nil && image == nil {
|
|
||||||
err = fmt.Errorf("No such image: %s", name)
|
|
||||||
} else {
|
|
||||||
err = srv.images.Remove(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,7 +390,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||||
archive = future.ProgressReader(resp.Body, int(resp.ContentLength), stdout)
|
archive = future.ProgressReader(resp.Body, int(resp.ContentLength), stdout)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(stdout, "Unpacking to %s\n", name)
|
fmt.Fprintf(stdout, "Unpacking to %s\n", name)
|
||||||
img, err := srv.images.Create(archive, nil, name, "")
|
img, err := srv.containers.graph.Create(archive, "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -419,7 +400,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||||
|
|
||||||
func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
|
cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images")
|
||||||
limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
|
//limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
|
||||||
quiet := cmd.Bool("q", false, "only show numeric IDs")
|
quiet := cmd.Bool("q", false, "only show numeric IDs")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -428,51 +409,65 @@ func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
var nameFilter string
|
var nameFilter string
|
||||||
if cmd.NArg() == 1 {
|
if cmd.NArg() == 1 {
|
||||||
nameFilter = cmd.Arg(0)
|
nameFilter = cmd.Arg(0)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
|
w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
|
||||||
if !*quiet {
|
if !*quiet {
|
||||||
fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n")
|
fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n")
|
||||||
}
|
}
|
||||||
paths, err := srv.images.Paths()
|
if *quiet {
|
||||||
|
images, err := srv.containers.graph.All()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, name := range paths {
|
for _, image := range images {
|
||||||
if nameFilter != "" && nameFilter != name {
|
fmt.Fprintln(stdout, image.Id)
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
ids, err := srv.images.List(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for idx, img := range ids {
|
|
||||||
if *limit > 0 && idx >= *limit {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if !*quiet {
|
|
||||||
for idx, field := range []string{
|
|
||||||
/* NAME */ name,
|
|
||||||
/* ID */ img.Id,
|
|
||||||
/* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago",
|
|
||||||
/* PARENT */ img.Parent,
|
|
||||||
} {
|
|
||||||
if idx == 0 {
|
|
||||||
w.Write([]byte(field))
|
|
||||||
} else {
|
} else {
|
||||||
w.Write([]byte("\t" + field))
|
// FIXME:
|
||||||
}
|
// paths, err := srv.images.Paths()
|
||||||
}
|
// if err != nil {
|
||||||
w.Write([]byte{'\n'})
|
// return err
|
||||||
} else {
|
// }
|
||||||
stdout.Write([]byte(img.Id + "\n"))
|
// for _, name := range paths {
|
||||||
}
|
// if nameFilter != "" && nameFilter != name {
|
||||||
}
|
// continue
|
||||||
}
|
// }
|
||||||
if !*quiet {
|
// ids, err := srv.images.List(name)
|
||||||
w.Flush()
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// for idx, img := range ids {
|
||||||
|
// if *limit > 0 && idx >= *limit {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// if !*quiet {
|
||||||
|
// for idx, field := range []string{
|
||||||
|
// /* NAME */ name,
|
||||||
|
// /* ID */ img.Id,
|
||||||
|
// /* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago",
|
||||||
|
// /* PARENT */ img.Parent,
|
||||||
|
// } {
|
||||||
|
// if idx == 0 {
|
||||||
|
// w.Write([]byte(field))
|
||||||
|
// } else {
|
||||||
|
// w.Write([]byte("\t" + field))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// w.Write([]byte{'\n'})
|
||||||
|
// } else {
|
||||||
|
// stdout.Write([]byte(img.Id + "\n"))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if !*quiet {
|
||||||
|
// w.Flush()
|
||||||
|
// }
|
||||||
|
//
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
@ -492,7 +487,6 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n")
|
fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n")
|
||||||
}
|
}
|
||||||
for _, container := range srv.containers.List() {
|
for _, container := range srv.containers.List() {
|
||||||
comment := container.GetUserData("comment")
|
|
||||||
if !container.State.Running && !*fl_all {
|
if !container.State.Running && !*fl_all {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -503,11 +497,11 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
}
|
}
|
||||||
for idx, field := range []string{
|
for idx, field := range []string{
|
||||||
/* ID */ container.Id,
|
/* ID */ container.Id,
|
||||||
/* IMAGE */ container.GetUserData("image"),
|
/* IMAGE */ container.Image,
|
||||||
/* COMMAND */ command,
|
/* COMMAND */ command,
|
||||||
/* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago",
|
/* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago",
|
||||||
/* STATUS */ container.State.String(),
|
/* STATUS */ container.State.String(),
|
||||||
/* COMMENT */ comment,
|
/* COMMENT */ "",
|
||||||
} {
|
} {
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
w.Write([]byte(field))
|
w.Write([]byte(field))
|
||||||
|
@ -540,17 +534,13 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||||
}
|
}
|
||||||
if container := srv.containers.Get(containerName); container != nil {
|
if container := srv.containers.Get(containerName); container != nil {
|
||||||
// FIXME: freeze the container before copying it to avoid data corruption?
|
// FIXME: freeze the container before copying it to avoid data corruption?
|
||||||
rwTar, err := fs.Tar(container.Mountpoint.Rw, fs.Uncompressed)
|
// FIXME: this shouldn't be in commands.
|
||||||
|
rwTar, err := container.ExportRw()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Create a new image from the container's base layers + a new layer from container changes
|
// Create a new image from the container's base layers + a new layer from container changes
|
||||||
parentImg, err := srv.images.Get(container.Image)
|
img, err := srv.containers.graph.Create(rwTar, container.Image, "")
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
img, err := srv.images.Create(rwTar, parentImg, imgName, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -574,10 +564,10 @@ func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
}
|
}
|
||||||
name := cmd.Arg(0)
|
name := cmd.Arg(0)
|
||||||
if container := srv.containers.Get(name); container != nil {
|
if container := srv.containers.Get(name); container != nil {
|
||||||
if err := container.Mountpoint.EnsureMounted(); err != nil {
|
if err := container.EnsureMounted(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data, err := fs.Tar(container.Mountpoint.Root, fs.Uncompressed)
|
data, err := container.Export()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -603,7 +593,7 @@ func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
if container := srv.containers.Get(cmd.Arg(0)); container == nil {
|
if container := srv.containers.Get(cmd.Arg(0)); container == nil {
|
||||||
return errors.New("No such container")
|
return errors.New("No such container")
|
||||||
} else {
|
} else {
|
||||||
changes, err := srv.images.Changes(container.Mountpoint)
|
changes, err := container.Changes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -625,10 +615,20 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
}
|
}
|
||||||
name := cmd.Arg(0)
|
name := cmd.Arg(0)
|
||||||
if container := srv.containers.Get(name); container != nil {
|
if container := srv.containers.Get(name); container != nil {
|
||||||
if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
|
log_stdout, err := container.ReadLog("stdout")
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
|
log_stderr, err := container.ReadLog("stderr")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// FIXME: Interpolate stdout and stderr instead of concatenating them
|
||||||
|
// FIXME: Differentiate stdout and stderr in the remote protocol
|
||||||
|
if _, err := io.Copy(stdout, log_stdout); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(stdout, log_stderr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -636,31 +636,6 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
return errors.New("No such container: " + cmd.Arg(0))
|
return errors.New("No such container: " + cmd.Arg(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
|
||||||
id := future.RandomId()[:8]
|
|
||||||
container, err := srv.containers.Create(id, cmd, args, img,
|
|
||||||
&Config{
|
|
||||||
Hostname: id,
|
|
||||||
Ports: ports,
|
|
||||||
User: user,
|
|
||||||
Tty: tty,
|
|
||||||
OpenStdin: openStdin,
|
|
||||||
Memory: memory,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := container.SetUserData("image", img.Id); err != nil {
|
|
||||||
srv.containers.Destroy(container)
|
|
||||||
return nil, errors.New("Error setting container userdata: " + err.Error())
|
|
||||||
}
|
|
||||||
if err := container.SetUserData("comment", comment); err != nil {
|
|
||||||
srv.containers.Destroy(container)
|
|
||||||
return nil, errors.New("Error setting container userdata: " + err.Error())
|
|
||||||
}
|
|
||||||
return container, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container")
|
cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container")
|
||||||
fl_i := cmd.Bool("i", false, "Attach to stdin")
|
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)
|
||||||
fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
|
fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
|
||||||
fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
|
fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
|
||||||
fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
|
fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
|
||||||
fl_comment := cmd.String("c", "", "Comment")
|
|
||||||
fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
|
fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
|
||||||
var fl_ports ports
|
var fl_ports ports
|
||||||
|
|
||||||
|
@ -738,8 +712,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
name := cmd.Arg(0)
|
name := cmd.Arg(0)
|
||||||
var img_name string
|
|
||||||
//var img_version string // Only here for reference
|
|
||||||
var cmdline []string
|
var cmdline []string
|
||||||
|
|
||||||
if len(cmd.Args()) >= 2 {
|
if len(cmd.Args()) >= 2 {
|
||||||
|
@ -758,33 +730,15 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
cmdline = []string{"/bin/bash", "-i"}
|
cmdline = []string{"/bin/bash", "-i"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the image
|
|
||||||
img, err := srv.images.Find(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if img == nil {
|
|
||||||
// Separate the name:version tag
|
|
||||||
if strings.Contains(name, ":") {
|
|
||||||
parts := strings.SplitN(name, ":", 2)
|
|
||||||
img_name = parts[0]
|
|
||||||
//img_version = parts[1] // Only here for reference
|
|
||||||
} else {
|
|
||||||
img_name = name
|
|
||||||
}
|
|
||||||
|
|
||||||
stdin_noclose := ioutil.NopCloser(stdin)
|
|
||||||
if err := srv.CmdImport(stdin_noclose, stdout, img_name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
img, err = srv.images.Find(name)
|
|
||||||
if err != nil || img == nil {
|
|
||||||
return errors.New("Could not find image after downloading: " + name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new container
|
// Create new container
|
||||||
container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty,
|
container, err := srv.containers.Create(cmdline[0], cmdline[1:], name,
|
||||||
*fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...)
|
&Config{
|
||||||
|
Ports: fl_ports,
|
||||||
|
User: *fl_user,
|
||||||
|
Tty: *fl_tty,
|
||||||
|
OpenStdin: *fl_stdin,
|
||||||
|
Memory: *fl_memory,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Error creating container: " + err.Error())
|
return errors.New("Error creating container: " + err.Error())
|
||||||
}
|
}
|
||||||
|
@ -850,7 +804,6 @@ func NewServer() (*Server, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
srv := &Server{
|
srv := &Server{
|
||||||
images: containers.Store,
|
|
||||||
containers: containers,
|
containers: containers,
|
||||||
}
|
}
|
||||||
return srv, nil
|
return srv, nil
|
||||||
|
@ -858,5 +811,4 @@ func NewServer() (*Server, error) {
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
containers *Docker
|
containers *Docker
|
||||||
images *fs.Store
|
|
||||||
}
|
}
|
||||||
|
|
278
container.go
278
container.go
|
@ -3,7 +3,9 @@ package docker
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/dotcloud/docker/fs"
|
"fmt"
|
||||||
|
"github.com/dotcloud/docker/future"
|
||||||
|
"github.com/dotcloud/docker/graph"
|
||||||
"github.com/kr/pty"
|
"github.com/kr/pty"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -16,15 +18,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sysInitPath string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
sysInitPath = SelfPath()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
|
root string
|
||||||
|
|
||||||
Id string
|
Id string
|
||||||
Root string
|
|
||||||
|
|
||||||
Created time.Time
|
Created time.Time
|
||||||
|
|
||||||
|
@ -32,8 +29,7 @@ type Container struct {
|
||||||
Args []string
|
Args []string
|
||||||
|
|
||||||
Config *Config
|
Config *Config
|
||||||
Mountpoint *fs.Mountpoint
|
State State
|
||||||
State *State
|
|
||||||
Image string
|
Image string
|
||||||
|
|
||||||
network *NetworkInterface
|
network *NetworkInterface
|
||||||
|
@ -41,7 +37,6 @@ type Container struct {
|
||||||
NetworkSettings *NetworkSettings
|
NetworkSettings *NetworkSettings
|
||||||
|
|
||||||
SysInitPath string
|
SysInitPath string
|
||||||
lxcConfigPath string
|
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
stdout *writeBroadcaster
|
stdout *writeBroadcaster
|
||||||
stderr *writeBroadcaster
|
stderr *writeBroadcaster
|
||||||
|
@ -50,6 +45,7 @@ type Container struct {
|
||||||
|
|
||||||
stdoutLog *os.File
|
stdoutLog *os.File
|
||||||
stderrLog *os.File
|
stderrLog *os.File
|
||||||
|
runtime *Docker // FIXME: rename Docker to Runtime for clarity
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -69,104 +65,9 @@ type NetworkSettings struct {
|
||||||
PortMapping map[string]string
|
PortMapping map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func createContainer(id string, root string, command string, args []string, image *fs.Image, config *Config, netManager *NetworkManager) (*Container, error) {
|
func GenerateId() string {
|
||||||
mountpoint, err := image.Mountpoint(path.Join(root, "rootfs"), path.Join(root, "rw"))
|
future.Seed()
|
||||||
if err != nil {
|
return future.RandomId()
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
container := &Container{
|
|
||||||
Id: id,
|
|
||||||
Root: root,
|
|
||||||
Created: time.Now(),
|
|
||||||
Path: command,
|
|
||||||
Args: args,
|
|
||||||
Config: config,
|
|
||||||
Image: image.Id,
|
|
||||||
Mountpoint: mountpoint,
|
|
||||||
State: newState(),
|
|
||||||
networkManager: netManager,
|
|
||||||
NetworkSettings: &NetworkSettings{},
|
|
||||||
SysInitPath: sysInitPath,
|
|
||||||
lxcConfigPath: path.Join(root, "config.lxc"),
|
|
||||||
stdout: newWriteBroadcaster(),
|
|
||||||
stderr: newWriteBroadcaster(),
|
|
||||||
}
|
|
||||||
if err := os.Mkdir(root, 0700); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Setup logging of stdout and stderr to disk
|
|
||||||
if stdoutLog, err := os.OpenFile(path.Join(container.Root, id+"-stdout.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
container.stdoutLog = stdoutLog
|
|
||||||
}
|
|
||||||
if stderrLog, err := os.OpenFile(path.Join(container.Root, id+"-stderr.log"), os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
container.stderrLog = stderrLog
|
|
||||||
}
|
|
||||||
if container.Config.OpenStdin {
|
|
||||||
container.stdin, container.stdinPipe = io.Pipe()
|
|
||||||
} else {
|
|
||||||
container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
|
||||||
}
|
|
||||||
container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
|
|
||||||
container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
|
|
||||||
|
|
||||||
if err := container.save(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return container, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadContainer(store *fs.Store, containerPath string, netManager *NetworkManager) (*Container, error) {
|
|
||||||
data, err := ioutil.ReadFile(path.Join(containerPath, "config.json"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mountpoint, err := store.FetchMountpoint(
|
|
||||||
path.Join(containerPath, "rootfs"),
|
|
||||||
path.Join(containerPath, "rw"),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if mountpoint == nil {
|
|
||||||
return nil, errors.New("Couldn't load container: unregistered mountpoint.")
|
|
||||||
}
|
|
||||||
container := &Container{
|
|
||||||
stdout: newWriteBroadcaster(),
|
|
||||||
stderr: newWriteBroadcaster(),
|
|
||||||
lxcConfigPath: path.Join(containerPath, "config.lxc"),
|
|
||||||
networkManager: netManager,
|
|
||||||
NetworkSettings: &NetworkSettings{},
|
|
||||||
Mountpoint: mountpoint,
|
|
||||||
}
|
|
||||||
// Load container settings
|
|
||||||
if err := json.Unmarshal(data, container); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup logging of stdout and stderr to disk
|
|
||||||
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 {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
container.stdoutLog = stdoutLog
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
return nil, err
|
|
||||||
} else {
|
|
||||||
container.stderrLog = stderrLog
|
|
||||||
}
|
|
||||||
container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
|
|
||||||
container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
|
|
||||||
|
|
||||||
if container.Config.OpenStdin {
|
|
||||||
container.stdin, container.stdinPipe = io.Pipe()
|
|
||||||
} else {
|
|
||||||
container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
|
||||||
}
|
|
||||||
container.State = newState()
|
|
||||||
return container, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) Cmd() *exec.Cmd {
|
func (container *Container) Cmd() *exec.Cmd {
|
||||||
|
@ -177,64 +78,32 @@ func (container *Container) When() time.Time {
|
||||||
return container.Created
|
return container.Created
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) loadUserData() (map[string]string, error) {
|
func (container *Container) FromDisk() error {
|
||||||
jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
|
data, err := ioutil.ReadFile(container.jsonPath())
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return make(map[string]string), nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data := make(map[string]string)
|
|
||||||
if err := json.Unmarshal(jsonData, &data); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (container *Container) saveUserData(data map[string]string) error {
|
|
||||||
jsonData, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ioutil.WriteFile(path.Join(container.Root, "userdata.json"), jsonData, 0700)
|
// Load container settings
|
||||||
}
|
if err := json.Unmarshal(data, container); err != nil {
|
||||||
|
|
||||||
func (container *Container) SetUserData(key, value string) error {
|
|
||||||
data, err := container.loadUserData()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data[key] = value
|
return nil
|
||||||
return container.saveUserData(data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) GetUserData(key string) string {
|
func (container *Container) ToDisk() (err error) {
|
||||||
data, err := container.loadUserData()
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if value, exists := data[key]; exists {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (container *Container) save() (err error) {
|
|
||||||
data, err := json.Marshal(container)
|
data, err := json.Marshal(container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return ioutil.WriteFile(path.Join(container.Root, "config.json"), data, 0666)
|
return ioutil.WriteFile(container.jsonPath(), data, 0666)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) generateLXCConfig() error {
|
func (container *Container) generateLXCConfig() error {
|
||||||
fo, err := os.Create(container.lxcConfigPath)
|
fo, err := os.Create(container.lxcConfigPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer fo.Close()
|
defer fo.Close()
|
||||||
|
|
||||||
if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
|
if err := LxcTemplateCompiled.Execute(fo, container); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -309,7 +178,7 @@ func (container *Container) start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) Start() error {
|
func (container *Container) Start() error {
|
||||||
if err := container.Mountpoint.EnsureMounted(); err != nil {
|
if err := container.EnsureMounted(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := container.allocateNetwork(); err != nil {
|
if err := container.allocateNetwork(); err != nil {
|
||||||
|
@ -320,7 +189,7 @@ func (container *Container) Start() error {
|
||||||
}
|
}
|
||||||
params := []string{
|
params := []string{
|
||||||
"-n", container.Id,
|
"-n", container.Id,
|
||||||
"-f", container.lxcConfigPath,
|
"-f", container.lxcConfigPath(),
|
||||||
"--",
|
"--",
|
||||||
"/sbin/init",
|
"/sbin/init",
|
||||||
}
|
}
|
||||||
|
@ -348,8 +217,10 @@ func (container *Container) Start() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// FIXME: save state on disk *first*, then converge
|
||||||
|
// this way disk state is used as a journal, eg. we can restore after crash etc.
|
||||||
container.State.setRunning(container.cmd.Process.Pid)
|
container.State.setRunning(container.cmd.Process.Pid)
|
||||||
container.save()
|
container.ToDisk()
|
||||||
go container.monitor()
|
go container.monitor()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -389,28 +260,12 @@ func (container *Container) StdoutPipe() (io.ReadCloser, error) {
|
||||||
return newBufReader(reader), nil
|
return newBufReader(reader), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) StdoutLog() io.Reader {
|
|
||||||
r, err := os.Open(container.stdoutLog.Name())
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
||||||
reader, writer := io.Pipe()
|
reader, writer := io.Pipe()
|
||||||
container.stderr.AddWriter(writer)
|
container.stderr.AddWriter(writer)
|
||||||
return newBufReader(reader), nil
|
return newBufReader(reader), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) StderrLog() io.Reader {
|
|
||||||
r, err := os.Open(container.stderrLog.Name())
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (container *Container) allocateNetwork() error {
|
func (container *Container) allocateNetwork() error {
|
||||||
iface, err := container.networkManager.Allocate()
|
iface, err := container.networkManager.Allocate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -450,7 +305,7 @@ func (container *Container) monitor() {
|
||||||
}
|
}
|
||||||
container.stdout.Close()
|
container.stdout.Close()
|
||||||
container.stderr.Close()
|
container.stderr.Close()
|
||||||
if err := container.Mountpoint.Umount(); err != nil {
|
if err := container.Unmount(); err != nil {
|
||||||
log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
|
log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,7 +316,7 @@ func (container *Container) monitor() {
|
||||||
|
|
||||||
// Report status back
|
// Report status back
|
||||||
container.State.setStopped(exitCode)
|
container.State.setStopped(exitCode)
|
||||||
container.save()
|
container.ToDisk()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) kill() error {
|
func (container *Container) kill() error {
|
||||||
|
@ -523,6 +378,17 @@ func (container *Container) Wait() int {
|
||||||
return container.State.ExitCode
|
return container.State.ExitCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *Container) ExportRw() (graph.Archive, error) {
|
||||||
|
return graph.Tar(container.rwPath(), graph.Uncompressed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) Export() (graph.Archive, error) {
|
||||||
|
if err := container.EnsureMounted(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return graph.Tar(container.RootfsPath(), graph.Uncompressed)
|
||||||
|
}
|
||||||
|
|
||||||
func (container *Container) WaitTimeout(timeout time.Duration) error {
|
func (container *Container) WaitTimeout(timeout time.Duration) error {
|
||||||
done := make(chan bool)
|
done := make(chan bool)
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -538,3 +404,75 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *Container) EnsureMounted() error {
|
||||||
|
if mounted, err := container.Mounted(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if mounted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return container.Mount()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) Mount() error {
|
||||||
|
image, err := container.GetImage()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return image.Mount(container.RootfsPath(), container.rwPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) Changes() ([]graph.Change, error) {
|
||||||
|
image, err := container.GetImage()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return image.Changes(container.rwPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) GetImage() (*graph.Image, error) {
|
||||||
|
if container.runtime == nil {
|
||||||
|
return nil, fmt.Errorf("Can't get image of unregistered container")
|
||||||
|
}
|
||||||
|
return container.runtime.graph.Get(container.Image)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) Mounted() (bool, error) {
|
||||||
|
return graph.Mounted(container.RootfsPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) Unmount() error {
|
||||||
|
return graph.Unmount(container.RootfsPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) logPath(name string) string {
|
||||||
|
return path.Join(container.root, fmt.Sprintf("%s-%s.log", container.Id, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) ReadLog(name string) (io.Reader, error) {
|
||||||
|
return os.Open(container.logPath(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) jsonPath() string {
|
||||||
|
return path.Join(container.root, "config.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) lxcConfigPath() string {
|
||||||
|
return path.Join(container.root, "config.lxc")
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method must be exported to be used from the lxc template
|
||||||
|
func (container *Container) RootfsPath() string {
|
||||||
|
return path.Join(container.root, "rootfs")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (container *Container) rwPath() string {
|
||||||
|
return path.Join(container.root, "rw")
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateId(id string) error {
|
||||||
|
if id == "" {
|
||||||
|
return fmt.Errorf("Invalid empty id")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package docker
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/fs"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
@ -21,10 +20,9 @@ func TestCommitRun(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container1, err := docker.Create(
|
container1, err := docker.Create(
|
||||||
"precommit_test",
|
|
||||||
"/bin/sh",
|
"/bin/sh",
|
||||||
[]string{"-c", "echo hello > /world"},
|
[]string{"-c", "echo hello > /world"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
Memory: 33554432,
|
Memory: 33554432,
|
||||||
},
|
},
|
||||||
|
@ -44,18 +42,11 @@ func TestCommitRun(t *testing.T) {
|
||||||
t.Errorf("Container shouldn't be running")
|
t.Errorf("Container shouldn't be running")
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: freeze the container before copying it to avoid data corruption?
|
rwTar, err := container1.ExportRw()
|
||||||
rwTar, err := fs.Tar(container1.Mountpoint.Rw, fs.Uncompressed)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
// Create a new image from the container's base layers + a new layer from container changes
|
img, err := docker.graph.Create(rwTar, container1.Image, "unit test commited image")
|
||||||
parentImg, err := docker.Store.Get(container1.Image)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
img, err := docker.Store.Create(rwTar, parentImg, "test_commitrun", "unit test commited image")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -63,10 +54,9 @@ func TestCommitRun(t *testing.T) {
|
||||||
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
|
// FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
|
||||||
|
|
||||||
container2, err := docker.Create(
|
container2, err := docker.Create(
|
||||||
"postcommit_test",
|
|
||||||
"cat",
|
"cat",
|
||||||
[]string{"/world"},
|
[]string{"/world"},
|
||||||
img,
|
img.Id,
|
||||||
&Config{
|
&Config{
|
||||||
Memory: 33554432,
|
Memory: 33554432,
|
||||||
},
|
},
|
||||||
|
@ -98,10 +88,9 @@ func TestRun(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"run_test",
|
|
||||||
"ls",
|
"ls",
|
||||||
[]string{"-al"},
|
[]string{"-al"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
Memory: 33554432,
|
Memory: 33554432,
|
||||||
},
|
},
|
||||||
|
@ -129,10 +118,9 @@ func TestOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"output_test",
|
|
||||||
"echo",
|
"echo",
|
||||||
[]string{"-n", "foobar"},
|
[]string{"-n", "foobar"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -155,10 +143,9 @@ func TestKill(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"stop_test",
|
|
||||||
"cat",
|
"cat",
|
||||||
[]string{"/dev/zero"},
|
[]string{"/dev/zero"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -199,10 +186,9 @@ func TestExitCode(t *testing.T) {
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
|
|
||||||
trueContainer, err := docker.Create(
|
trueContainer, err := docker.Create(
|
||||||
"exit_test_1",
|
|
||||||
"/bin/true",
|
"/bin/true",
|
||||||
[]string{""},
|
[]string{""},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -214,10 +200,9 @@ func TestExitCode(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
falseContainer, err := docker.Create(
|
falseContainer, err := docker.Create(
|
||||||
"exit_test_2",
|
|
||||||
"/bin/false",
|
"/bin/false",
|
||||||
[]string{""},
|
[]string{""},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -244,10 +229,9 @@ func TestRestart(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"restart_test",
|
|
||||||
"echo",
|
"echo",
|
||||||
[]string{"-n", "foobar"},
|
[]string{"-n", "foobar"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -279,10 +263,9 @@ func TestRestartStdin(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"restart_stdin_test",
|
|
||||||
"cat",
|
"cat",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
OpenStdin: true,
|
OpenStdin: true,
|
||||||
},
|
},
|
||||||
|
@ -331,10 +314,9 @@ func TestUser(t *testing.T) {
|
||||||
|
|
||||||
// Default user must be root
|
// Default user must be root
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"user_default",
|
|
||||||
"id",
|
"id",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -351,10 +333,9 @@ func TestUser(t *testing.T) {
|
||||||
|
|
||||||
// Set a username
|
// Set a username
|
||||||
container, err = docker.Create(
|
container, err = docker.Create(
|
||||||
"user_root",
|
|
||||||
"id",
|
"id",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
User: "root",
|
User: "root",
|
||||||
},
|
},
|
||||||
|
@ -373,10 +354,9 @@ func TestUser(t *testing.T) {
|
||||||
|
|
||||||
// Set a UID
|
// Set a UID
|
||||||
container, err = docker.Create(
|
container, err = docker.Create(
|
||||||
"user_uid0",
|
|
||||||
"id",
|
"id",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
User: "0",
|
User: "0",
|
||||||
},
|
},
|
||||||
|
@ -395,10 +375,9 @@ func TestUser(t *testing.T) {
|
||||||
|
|
||||||
// Set a different user by uid
|
// Set a different user by uid
|
||||||
container, err = docker.Create(
|
container, err = docker.Create(
|
||||||
"user_uid1",
|
|
||||||
"id",
|
"id",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
User: "1",
|
User: "1",
|
||||||
},
|
},
|
||||||
|
@ -419,10 +398,9 @@ func TestUser(t *testing.T) {
|
||||||
|
|
||||||
// Set a different user by username
|
// Set a different user by username
|
||||||
container, err = docker.Create(
|
container, err = docker.Create(
|
||||||
"user_daemon",
|
|
||||||
"id",
|
"id",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
User: "daemon",
|
User: "daemon",
|
||||||
},
|
},
|
||||||
|
@ -448,10 +426,9 @@ func TestMultipleContainers(t *testing.T) {
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
|
|
||||||
container1, err := docker.Create(
|
container1, err := docker.Create(
|
||||||
"container1",
|
|
||||||
"cat",
|
"cat",
|
||||||
[]string{"/dev/zero"},
|
[]string{"/dev/zero"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -460,10 +437,9 @@ func TestMultipleContainers(t *testing.T) {
|
||||||
defer docker.Destroy(container1)
|
defer docker.Destroy(container1)
|
||||||
|
|
||||||
container2, err := docker.Create(
|
container2, err := docker.Create(
|
||||||
"container2",
|
|
||||||
"cat",
|
"cat",
|
||||||
[]string{"/dev/zero"},
|
[]string{"/dev/zero"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -504,10 +480,9 @@ func TestStdin(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"stdin_test",
|
|
||||||
"cat",
|
"cat",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
OpenStdin: true,
|
OpenStdin: true,
|
||||||
},
|
},
|
||||||
|
@ -540,10 +515,9 @@ func TestTty(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"tty_test",
|
|
||||||
"cat",
|
"cat",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
OpenStdin: true,
|
OpenStdin: true,
|
||||||
},
|
},
|
||||||
|
@ -576,10 +550,9 @@ func TestEnv(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"env_test",
|
|
||||||
"/usr/bin/env",
|
"/usr/bin/env",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -651,10 +624,9 @@ func TestLXCConfig(t *testing.T) {
|
||||||
memMax := 536870912
|
memMax := 536870912
|
||||||
mem := memMin + rand.Intn(memMax-memMin)
|
mem := memMin + rand.Intn(memMax-memMin)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"config_test",
|
|
||||||
"/bin/true",
|
"/bin/true",
|
||||||
[]string{},
|
[]string{},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{
|
&Config{
|
||||||
Hostname: "foobar",
|
Hostname: "foobar",
|
||||||
Memory: int64(mem),
|
Memory: int64(mem),
|
||||||
|
@ -665,10 +637,10 @@ func TestLXCConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer docker.Destroy(container)
|
defer docker.Destroy(container)
|
||||||
container.generateLXCConfig()
|
container.generateLXCConfig()
|
||||||
grepFile(t, container.lxcConfigPath, "lxc.utsname = foobar")
|
grepFile(t, container.lxcConfigPath(), "lxc.utsname = foobar")
|
||||||
grepFile(t, container.lxcConfigPath,
|
grepFile(t, container.lxcConfigPath(),
|
||||||
fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
|
fmt.Sprintf("lxc.cgroup.memory.limit_in_bytes = %d", mem))
|
||||||
grepFile(t, container.lxcConfigPath,
|
grepFile(t, container.lxcConfigPath(),
|
||||||
fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
|
fmt.Sprintf("lxc.cgroup.memory.memsw.limit_in_bytes = %d", mem*2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,10 +652,9 @@ func BenchmarkRunSequencial(b *testing.B) {
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
fmt.Sprintf("bench_%v", i),
|
|
||||||
"echo",
|
"echo",
|
||||||
[]string{"-n", "foo"},
|
[]string{"-n", "foo"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -717,10 +688,9 @@ func BenchmarkRunParallel(b *testing.B) {
|
||||||
tasks = append(tasks, complete)
|
tasks = append(tasks, complete)
|
||||||
go func(i int, complete chan error) {
|
go func(i int, complete chan error) {
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
fmt.Sprintf("bench_%v", i),
|
|
||||||
"echo",
|
"echo",
|
||||||
[]string{"-n", "foo"},
|
[]string{"-n", "foo"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
132
docker.go
132
docker.go
|
@ -3,12 +3,15 @@ package docker
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/fs"
|
"github.com/dotcloud/docker/graph"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Docker struct {
|
type Docker struct {
|
||||||
|
@ -16,7 +19,13 @@ type Docker struct {
|
||||||
repository string
|
repository string
|
||||||
containers *list.List
|
containers *list.List
|
||||||
networkManager *NetworkManager
|
networkManager *NetworkManager
|
||||||
Store *fs.Store
|
graph *graph.Graph
|
||||||
|
}
|
||||||
|
|
||||||
|
var sysInitPath string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
sysInitPath = SelfPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (docker *Docker) List() []*Container {
|
func (docker *Docker) List() []*Container {
|
||||||
|
@ -49,20 +58,98 @@ func (docker *Docker) Exists(id string) bool {
|
||||||
return docker.Get(id) != nil
|
return docker.Get(id) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (docker *Docker) Create(id string, command string, args []string, image *fs.Image, config *Config) (*Container, error) {
|
func (docker *Docker) containerRoot(id string) string {
|
||||||
if docker.Exists(id) {
|
return path.Join(docker.repository, id)
|
||||||
return nil, fmt.Errorf("Container %v already exists", id)
|
|
||||||
}
|
}
|
||||||
root := path.Join(docker.repository, id)
|
|
||||||
|
|
||||||
container, err := createContainer(id, root, command, args, image, config, docker.networkManager)
|
func (docker *Docker) Create(command string, args []string, image string, config *Config) (*Container, error) {
|
||||||
if err != nil {
|
container := &Container{
|
||||||
|
// FIXME: we should generate the ID here instead of receiving it as an argument
|
||||||
|
Id: GenerateId(),
|
||||||
|
Created: time.Now(),
|
||||||
|
Path: command,
|
||||||
|
Args: args,
|
||||||
|
Config: config,
|
||||||
|
Image: image,
|
||||||
|
NetworkSettings: &NetworkSettings{},
|
||||||
|
// FIXME: do we need to store this in the container?
|
||||||
|
SysInitPath: sysInitPath,
|
||||||
|
}
|
||||||
|
container.root = docker.containerRoot(container.Id)
|
||||||
|
// Step 1: create the container directory.
|
||||||
|
// This doubles as a barrier to avoid race conditions.
|
||||||
|
if err := os.Mkdir(container.root, 0700); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Step 2: save the container json
|
||||||
|
if err := container.ToDisk(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Step 3: register the container
|
||||||
|
if err := docker.Register(container); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
docker.containers.PushBack(container)
|
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (docker *Docker) Load(id string) (*Container, error) {
|
||||||
|
container := &Container{root: docker.containerRoot(id)}
|
||||||
|
if err := container.FromDisk(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if container.Id != id {
|
||||||
|
return container, fmt.Errorf("Container %s is stored at %s", container.Id, id)
|
||||||
|
}
|
||||||
|
if err := docker.Register(container); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register makes a container object usable by the runtime as <container.Id>
|
||||||
|
func (docker *Docker) Register(container *Container) error {
|
||||||
|
if container.runtime != nil || docker.Exists(container.Id) {
|
||||||
|
return fmt.Errorf("Container is already loaded")
|
||||||
|
}
|
||||||
|
if err := validateId(container.Id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
container.runtime = docker
|
||||||
|
container.networkManager = docker.networkManager // FIXME: infer from docker.runtime
|
||||||
|
// Setup state lock (formerly in newState()
|
||||||
|
lock := new(sync.Mutex)
|
||||||
|
container.State.stateChangeLock = lock
|
||||||
|
container.State.stateChangeCond = sync.NewCond(lock)
|
||||||
|
// Attach to stdout and stderr
|
||||||
|
container.stderr = newWriteBroadcaster()
|
||||||
|
container.stdout = newWriteBroadcaster()
|
||||||
|
// Attach to stdin
|
||||||
|
if container.Config.OpenStdin {
|
||||||
|
container.stdin, container.stdinPipe = io.Pipe()
|
||||||
|
} else {
|
||||||
|
container.stdinPipe = NopWriteCloser(ioutil.Discard) // Silently drop stdin
|
||||||
|
}
|
||||||
|
// Setup logging of stdout and stderr to disk
|
||||||
|
if err := docker.LogToDisk(container.stdout, container.logPath("stdout")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := docker.LogToDisk(container.stderr, container.logPath("stderr")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// done
|
||||||
|
docker.containers.PushBack(container)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (docker *Docker) LogToDisk(src *writeBroadcaster, dst string) error {
|
||||||
|
log, err := os.OpenFile(dst, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
src.AddWriter(NopWriteCloser(log))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (docker *Docker) Destroy(container *Container) error {
|
func (docker *Docker) Destroy(container *Container) error {
|
||||||
element := docker.getContainerElement(container.Id)
|
element := docker.getContainerElement(container.Id)
|
||||||
if element == nil {
|
if element == nil {
|
||||||
|
@ -72,18 +159,18 @@ func (docker *Docker) Destroy(container *Container) error {
|
||||||
if err := container.Stop(); err != nil {
|
if err := container.Stop(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if container.Mountpoint.Mounted() {
|
if mounted, err := container.Mounted(); err != nil {
|
||||||
if err := container.Mountpoint.Umount(); err != nil {
|
return err
|
||||||
return fmt.Errorf("Unable to umount container %v: %v", container.Id, err)
|
} else if mounted {
|
||||||
|
if err := container.Unmount(); err != nil {
|
||||||
|
return fmt.Errorf("Unable to unmount container %v: %v", container.Id, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := container.Mountpoint.Deregister(); err != nil {
|
// Deregister the container before removing its directory, to avoid race conditions
|
||||||
return fmt.Errorf("Unable to deregiser -- ? mountpoint %v: %v", container.Mountpoint.Root, err)
|
docker.containers.Remove(element)
|
||||||
}
|
if err := os.RemoveAll(container.root); err != nil {
|
||||||
if err := os.RemoveAll(container.Root); err != nil {
|
|
||||||
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.Id, err)
|
return fmt.Errorf("Unable to remove filesystem for %v: %v", container.Id, err)
|
||||||
}
|
}
|
||||||
docker.containers.Remove(element)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,12 +180,13 @@ func (docker *Docker) restore() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, v := range dir {
|
for _, v := range dir {
|
||||||
container, err := loadContainer(docker.Store, path.Join(docker.repository, v.Name()), docker.networkManager)
|
id := v.Name()
|
||||||
|
container, err := docker.Load(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to load container %v: %v", v.Name(), err)
|
log.Printf("Failed to load container %v: %v", id, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
docker.containers.PushBack(container)
|
log.Printf("Loaded container %v", container.Id)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -114,7 +202,7 @@ func NewFromDirectory(root string) (*Docker, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
store, err := fs.New(path.Join(root, "images"))
|
graph, err := graph.New(path.Join(root, "graph"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -127,8 +215,8 @@ func NewFromDirectory(root string) (*Docker, error) {
|
||||||
root: root,
|
root: root,
|
||||||
repository: docker_repo,
|
repository: docker_repo,
|
||||||
containers: list.New(),
|
containers: list.New(),
|
||||||
Store: store,
|
|
||||||
networkManager: netManager,
|
networkManager: netManager,
|
||||||
|
graph: graph,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := docker.restore(); err != nil {
|
if err := docker.restore(); err != nil {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/fs"
|
"github.com/dotcloud/docker/graph"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -63,7 +63,6 @@ func init() {
|
||||||
}
|
}
|
||||||
// Create the "Server"
|
// Create the "Server"
|
||||||
srv := &Server{
|
srv := &Server{
|
||||||
images: docker.Store,
|
|
||||||
containers: docker,
|
containers: docker,
|
||||||
}
|
}
|
||||||
// Retrieve the Image
|
// Retrieve the Image
|
||||||
|
@ -93,8 +92,8 @@ func newTestDocker() (*Docker, error) {
|
||||||
return docker, nil
|
return docker, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTestImage(docker *Docker) *fs.Image {
|
func GetTestImage(docker *Docker) *graph.Image {
|
||||||
imgs, err := docker.Store.Images()
|
imgs, err := docker.graph.All()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else if len(imgs) < 1 {
|
} else if len(imgs) < 1 {
|
||||||
|
@ -115,10 +114,9 @@ func TestCreate(t *testing.T) {
|
||||||
t.Errorf("Expected 0 containers, %v found", len(docker.List()))
|
t.Errorf("Expected 0 containers, %v found", len(docker.List()))
|
||||||
}
|
}
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"test_create",
|
|
||||||
"ls",
|
"ls",
|
||||||
[]string{"-al"},
|
[]string{"-al"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -137,22 +135,22 @@ func TestCreate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the container List() returns is the right one
|
// Make sure the container List() returns is the right one
|
||||||
if docker.List()[0].Id != "test_create" {
|
if docker.List()[0].Id != container.Id {
|
||||||
t.Errorf("Unexpected container %v returned by List", docker.List()[0])
|
t.Errorf("Unexpected container %v returned by List", docker.List()[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we can get the container with Get()
|
// Make sure we can get the container with Get()
|
||||||
if docker.Get("test_create") == nil {
|
if docker.Get(container.Id) == nil {
|
||||||
t.Errorf("Unable to get newly created container")
|
t.Errorf("Unable to get newly created container")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure it is the right container
|
// Make sure it is the right container
|
||||||
if docker.Get("test_create") != container {
|
if docker.Get(container.Id) != container {
|
||||||
t.Errorf("Get() returned the wrong container")
|
t.Errorf("Get() returned the wrong container")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure Exists returns it as existing
|
// Make sure Exists returns it as existing
|
||||||
if !docker.Exists("test_create") {
|
if !docker.Exists(container.Id) {
|
||||||
t.Errorf("Exists() returned false for a newly created container")
|
t.Errorf("Exists() returned false for a newly created container")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,10 +162,9 @@ func TestDestroy(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container, err := docker.Create(
|
container, err := docker.Create(
|
||||||
"test_destroy",
|
|
||||||
"ls",
|
"ls",
|
||||||
[]string{"-al"},
|
[]string{"-al"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -189,12 +186,12 @@ func TestDestroy(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure docker.Get() refuses to return the unexisting container
|
// Make sure docker.Get() refuses to return the unexisting container
|
||||||
if docker.Get("test_destroy") != nil {
|
if docker.Get(container.Id) != nil {
|
||||||
t.Errorf("Unable to get newly created container")
|
t.Errorf("Unable to get newly created container")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the container root directory does not exist anymore
|
// Make sure the container root directory does not exist anymore
|
||||||
_, err = os.Stat(container.Root)
|
_, err = os.Stat(container.root)
|
||||||
if err == nil || !os.IsNotExist(err) {
|
if err == nil || !os.IsNotExist(err) {
|
||||||
t.Errorf("Container root directory still exists after destroy")
|
t.Errorf("Container root directory still exists after destroy")
|
||||||
}
|
}
|
||||||
|
@ -213,10 +210,9 @@ func TestGet(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer nuke(docker)
|
defer nuke(docker)
|
||||||
container1, err := docker.Create(
|
container1, err := docker.Create(
|
||||||
"test1",
|
|
||||||
"ls",
|
"ls",
|
||||||
[]string{"-al"},
|
[]string{"-al"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -225,10 +221,9 @@ func TestGet(t *testing.T) {
|
||||||
defer docker.Destroy(container1)
|
defer docker.Destroy(container1)
|
||||||
|
|
||||||
container2, err := docker.Create(
|
container2, err := docker.Create(
|
||||||
"test2",
|
|
||||||
"ls",
|
"ls",
|
||||||
[]string{"-al"},
|
[]string{"-al"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -237,10 +232,9 @@ func TestGet(t *testing.T) {
|
||||||
defer docker.Destroy(container2)
|
defer docker.Destroy(container2)
|
||||||
|
|
||||||
container3, err := docker.Create(
|
container3, err := docker.Create(
|
||||||
"test3",
|
|
||||||
"ls",
|
"ls",
|
||||||
[]string{"-al"},
|
[]string{"-al"},
|
||||||
GetTestImage(docker),
|
GetTestImage(docker).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -248,16 +242,16 @@ func TestGet(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer docker.Destroy(container3)
|
defer docker.Destroy(container3)
|
||||||
|
|
||||||
if docker.Get("test1") != container1 {
|
if docker.Get(container1.Id) != container1 {
|
||||||
t.Errorf("Get(test1) returned %v while expecting %v", docker.Get("test1"), container1)
|
t.Errorf("Get(test1) returned %v while expecting %v", docker.Get(container1.Id), container1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if docker.Get("test2") != container2 {
|
if docker.Get(container2.Id) != container2 {
|
||||||
t.Errorf("Get(test2) returned %v while expecting %v", docker.Get("test2"), container2)
|
t.Errorf("Get(test2) returned %v while expecting %v", docker.Get(container2.Id), container2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if docker.Get("test3") != container3 {
|
if docker.Get(container3.Id) != container3 {
|
||||||
t.Errorf("Get(test3) returned %v while expecting %v", docker.Get("test3"), container3)
|
t.Errorf("Get(test3) returned %v while expecting %v", docker.Get(container3.Id), container3)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -282,10 +276,9 @@ func TestRestore(t *testing.T) {
|
||||||
|
|
||||||
// Create a container with one instance of docker
|
// Create a container with one instance of docker
|
||||||
container1, err := docker1.Create(
|
container1, err := docker1.Create(
|
||||||
"restore_test",
|
|
||||||
"ls",
|
"ls",
|
||||||
[]string{"-al"},
|
[]string{"-al"},
|
||||||
GetTestImage(docker1),
|
GetTestImage(docker1).Id,
|
||||||
&Config{},
|
&Config{},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -309,7 +302,7 @@ func TestRestore(t *testing.T) {
|
||||||
if len(docker2.List()) != 1 {
|
if len(docker2.List()) != 1 {
|
||||||
t.Errorf("Expected 1 container, %v found", len(docker2.List()))
|
t.Errorf("Expected 1 container, %v found", len(docker2.List()))
|
||||||
}
|
}
|
||||||
container2 := docker2.Get("restore_test")
|
container2 := docker2.Get(container1.Id)
|
||||||
if container2 == nil {
|
if container2 == nil {
|
||||||
t.Fatal("Unable to Get container")
|
t.Fatal("Unable to Get container")
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ lxc.network.mtu = 1500
|
||||||
lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}}
|
lxc.network.ipv4 = {{.NetworkSettings.IpAddress}}/{{.NetworkSettings.IpPrefixLen}}
|
||||||
|
|
||||||
# root filesystem
|
# root filesystem
|
||||||
{{$ROOTFS := .Mountpoint.Root}}
|
{{$ROOTFS := .RootfsPath}}
|
||||||
lxc.rootfs = {{$ROOTFS}}
|
lxc.rootfs = {{$ROOTFS}}
|
||||||
|
|
||||||
# use a dedicated pts for the container (and limit the number of pseudo terminal
|
# use a dedicated pts for the container (and limit the number of pseudo terminal
|
||||||
|
|
8
state.go
8
state.go
|
@ -17,14 +17,6 @@ type State struct {
|
||||||
stateChangeCond *sync.Cond
|
stateChangeCond *sync.Cond
|
||||||
}
|
}
|
||||||
|
|
||||||
func newState() *State {
|
|
||||||
lock := new(sync.Mutex)
|
|
||||||
return &State{
|
|
||||||
stateChangeLock: lock,
|
|
||||||
stateChangeCond: sync.NewCond(lock),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a human-readable description of the state
|
// String returns a human-readable description of the state
|
||||||
func (s *State) String() string {
|
func (s *State) String() string {
|
||||||
if s.Running {
|
if s.Running {
|
||||||
|
|
Loading…
Reference in a new issue