|
@@ -1,33 +1,31 @@
|
|
|
package main
|
|
|
|
|
|
import (
|
|
|
+ "github.com/dotcloud/docker"
|
|
|
"github.com/dotcloud/docker/rcli"
|
|
|
- "github.com/dotcloud/docker/fake"
|
|
|
"github.com/dotcloud/docker/future"
|
|
|
"bufio"
|
|
|
"errors"
|
|
|
"log"
|
|
|
"io"
|
|
|
- "io/ioutil"
|
|
|
- "os/exec"
|
|
|
"flag"
|
|
|
"fmt"
|
|
|
- "github.com/kr/pty"
|
|
|
"strings"
|
|
|
- "bytes"
|
|
|
"text/tabwriter"
|
|
|
"sort"
|
|
|
"os"
|
|
|
"time"
|
|
|
"net/http"
|
|
|
+ "encoding/json"
|
|
|
+ "bytes"
|
|
|
)
|
|
|
|
|
|
|
|
|
-func (docker *Docker) Name() string {
|
|
|
+func (srv *Server) Name() string {
|
|
|
return "docker"
|
|
|
}
|
|
|
|
|
|
-func (docker *Docker) Help() string {
|
|
|
+func (srv *Server) Help() string {
|
|
|
help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
|
|
|
for _, cmd := range [][]interface{}{
|
|
|
{"run", "Run a command in a container"},
|
|
@@ -42,6 +40,7 @@ func (docker *Docker) Help() string {
|
|
|
{"commit", "Save the state of a container"},
|
|
|
{"attach", "Attach to the standard inputs and outputs of a running container"},
|
|
|
{"info", "Display system-wide information"},
|
|
|
+ {"tar", "Stream the contents of a container as a tar archive"},
|
|
|
{"web", "Generate a web UI"},
|
|
|
} {
|
|
|
help += fmt.Sprintf(" %-10.10s%s\n", cmd...)
|
|
@@ -50,7 +49,176 @@ func (docker *Docker) Help() string {
|
|
|
}
|
|
|
|
|
|
|
|
|
-func (docker *Docker) CmdList(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+ cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if cmd.NArg() < 1 {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ for _, name := range cmd.Args() {
|
|
|
+ if container := srv.docker.Get(name); container != nil {
|
|
|
+ if err := container.Stop(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ fmt.Fprintln(stdout, container.Id)
|
|
|
+ } else {
|
|
|
+ return errors.New("No such container: " + name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+ cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "umount a container's filesystem (debug only)")
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if cmd.NArg() < 1 {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ for _, name := range cmd.Args() {
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
+ if err := container.Filesystem.Umount(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ fmt.Fprintln(stdout, container.Id)
|
|
|
+ } else {
|
|
|
+ return errors.New("No such container: " + name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+ cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "mount a container's filesystem (debug only)")
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if cmd.NArg() < 1 {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ for _, name := range cmd.Args() {
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
+ if err := container.Filesystem.Mount(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ fmt.Fprintln(stdout, container.Id)
|
|
|
+ } else {
|
|
|
+ return errors.New("No such container: " + name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+ cmd := rcli.Subcmd(stdout, "cat", "[OPTIONS] CONTAINER PATH", "write the contents of a container's file to standard output")
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if cmd.NArg() < 2 {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ name, path := cmd.Arg(0), cmd.Arg(1)
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
+ if f, err := container.Filesystem.OpenFile(path, os.O_RDONLY, 0); err != nil {
|
|
|
+ return err
|
|
|
+ } else if _, err := io.Copy(stdout, f); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return errors.New("No such container: " + name)
|
|
|
+}
|
|
|
+
|
|
|
+func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+ cmd := rcli.Subcmd(stdout, "write", "[OPTIONS] CONTAINER PATH", "write the contents of standard input to a container's file")
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if cmd.NArg() < 2 {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ name, path := cmd.Arg(0), cmd.Arg(1)
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
+ if f, err := container.Filesystem.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600); err != nil {
|
|
|
+ return err
|
|
|
+ } else if _, err := io.Copy(f, stdin); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return errors.New("No such container: " + name)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+ cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory")
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if cmd.NArg() < 2 {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ name, path := cmd.Arg(0), cmd.Arg(1)
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
+ if files, err := container.Filesystem.ReadDir(path); err != nil {
|
|
|
+ return err
|
|
|
+ } else {
|
|
|
+ for _, f := range files {
|
|
|
+ fmt.Fprintln(stdout, f.Name())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return errors.New("No such container: " + name)
|
|
|
+}
|
|
|
+
|
|
|
+func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+ cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container")
|
|
|
+ if err := cmd.Parse(args); err != nil {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if cmd.NArg() < 1 {
|
|
|
+ cmd.Usage()
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ name := cmd.Arg(0)
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
+ data, err := json.Marshal(container)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ indented := new(bytes.Buffer)
|
|
|
+ if err = json.Indent(indented, data, "", " "); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if _, err := io.Copy(stdout, indented); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return errors.New("No such container: " + name)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (srv *Server) CmdList(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
flags := rcli.Subcmd(stdout, "list", "[OPTIONS] [NAME]", "List containers")
|
|
|
limit := flags.Int("l", 0, "Only show the N most recent versions of each name")
|
|
|
quiet := flags.Bool("q", false, "only show numeric IDs")
|
|
@@ -64,32 +232,33 @@ func (docker *Docker) CmdList(stdin io.ReadCloser, stdout io.Writer, args ...str
|
|
|
nameFilter = flags.Arg(0)
|
|
|
}
|
|
|
var names []string
|
|
|
- for name := range docker.containersByName {
|
|
|
+ for name := range srv.containersByName {
|
|
|
names = append(names, name)
|
|
|
}
|
|
|
sort.Strings(names)
|
|
|
w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0)
|
|
|
if (!*quiet) {
|
|
|
- fmt.Fprintf(w, "NAME\tID\tCREATED\tSOURCE\tSIZE\tCHANGES\tRUNNING\tCOMMAND\n")
|
|
|
+ fmt.Fprintf(w, "NAME\tID\tCREATED\tSOURCE\tRUNNING\tMOUNTED\tCOMMAND\tPID\tEXIT\n")
|
|
|
}
|
|
|
for _, name := range names {
|
|
|
if nameFilter != "" && nameFilter != name {
|
|
|
continue
|
|
|
}
|
|
|
- for idx, container := range *docker.containersByName[name] {
|
|
|
+ for idx, container := range *srv.containersByName[name] {
|
|
|
if *limit > 0 && idx >= *limit {
|
|
|
break
|
|
|
}
|
|
|
if !*quiet {
|
|
|
for idx, field := range []string{
|
|
|
- /* NAME */ container.Name,
|
|
|
+ /* NAME */ container.GetUserData("name"),
|
|
|
/* ID */ container.Id,
|
|
|
/* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago",
|
|
|
- /* SOURCE */ container.Source,
|
|
|
- /* SIZE */ fmt.Sprintf("%.1fM", float32(container.Size) / 1024 / 1024),
|
|
|
- /* CHANGES */ fmt.Sprintf("%.1fM", float32(container.BytesChanged) / 1024 / 1024),
|
|
|
- /* RUNNING */ fmt.Sprintf("%v", container.Running),
|
|
|
- /* COMMAND */ container.CmdString(),
|
|
|
+ /* SOURCE */ container.GetUserData("source"),
|
|
|
+ /* RUNNING */ fmt.Sprintf("%v", container.State.Running),
|
|
|
+ /* MOUNTED */ fmt.Sprintf("%v", container.Filesystem.IsMounted()),
|
|
|
+ /* COMMAND */ fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " ")),
|
|
|
+ /* PID */ fmt.Sprintf("%v", container.State.Pid),
|
|
|
+ /* EXIT CODE */ fmt.Sprintf("%v", container.State.ExitCode),
|
|
|
} {
|
|
|
if idx == 0 {
|
|
|
w.Write([]byte(field))
|
|
@@ -109,33 +278,33 @@ func (docker *Docker) CmdList(stdin io.ReadCloser, stdout io.Writer, args ...str
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (docker *Docker) findContainer(name string) (*Container, bool) {
|
|
|
+func (srv *Server) findContainer(name string) (*docker.Container, bool) {
|
|
|
// 1: look for container by ID
|
|
|
- if container, exists := docker.containers[name]; exists {
|
|
|
+ if container := srv.docker.Get(name); container != nil {
|
|
|
return container, true
|
|
|
}
|
|
|
// 2: look for a container by name (and pick the most recent)
|
|
|
- if containers, exists := docker.containersByName[name]; exists {
|
|
|
+ if containers, exists := srv.containersByName[name]; exists {
|
|
|
return (*containers)[0], true
|
|
|
}
|
|
|
return nil, false
|
|
|
}
|
|
|
|
|
|
|
|
|
-func (docker *Docker) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
flags := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container")
|
|
|
if err := flags.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
|
for _, name := range flags.Args() {
|
|
|
- if _, err := docker.rm(name); err != nil {
|
|
|
+ if _, err := srv.rm(name); err != nil {
|
|
|
fmt.Fprintln(stdout, "Error: " + err.Error())
|
|
|
}
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (docker *Docker) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
if len(args) < 1 {
|
|
|
return errors.New("Not enough arguments")
|
|
|
}
|
|
@@ -143,31 +312,42 @@ func (docker *Docker) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...str
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- layer, err := docker.layers.AddLayer(resp.Body, stdout)
|
|
|
+ layer, err := srv.layers.AddLayer(resp.Body, stdout)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ container, err := srv.addContainer(layer.Id(), []string{layer.Path}, args[0], "download")
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- docker.addContainer(args[0], "download", 0)
|
|
|
- fmt.Fprintln(stdout, layer.Id())
|
|
|
+ fmt.Fprintln(stdout, container.Id)
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (docker *Docker) CmdPut(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdPut(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
if len(args) < 1 {
|
|
|
return errors.New("Not enough arguments")
|
|
|
}
|
|
|
fmt.Printf("Adding layer\n")
|
|
|
- layer, err := docker.layers.AddLayer(stdin, stdout)
|
|
|
+ layer, err := srv.layers.AddLayer(stdin, stdout)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- docker.addContainer(args[0], "upload", 0)
|
|
|
- fmt.Fprintln(stdout, layer.Id())
|
|
|
+ id := layer.Id()
|
|
|
+ if !srv.docker.Exists(id) {
|
|
|
+ log.Println("Creating new container: " + id)
|
|
|
+ log.Printf("%v\n", srv.docker.List())
|
|
|
+ _, err := srv.addContainer(id, []string{layer.Path}, args[0], "upload")
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ fmt.Fprintln(stdout, id)
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
|
|
|
-func (docker *Docker) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
flags := rcli.Subcmd(stdout,
|
|
|
"fork", "[OPTIONS] CONTAINER [DEST]",
|
|
|
"Duplicate a container")
|
|
@@ -183,15 +363,18 @@ func (docker *Docker) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...s
|
|
|
if dstName == "" {
|
|
|
dstName = srcName
|
|
|
}
|
|
|
- if src, exists := docker.findContainer(srcName); exists {
|
|
|
- dst := docker.addContainer(dstName, "snapshot:" + src.Id, src.Size)
|
|
|
- fmt.Fprintln(stdout, dst.Id)
|
|
|
+ /*
|
|
|
+ if src, exists := srv.findContainer(srcName); exists {
|
|
|
+ baseLayer := src.Filesystem.Layers[0]
|
|
|
+ //dst := srv.addContainer(dstName, "snapshot:" + src.Id, src.Size)
|
|
|
+ //fmt.Fprintln(stdout, dst.Id)
|
|
|
return nil
|
|
|
}
|
|
|
+ */
|
|
|
return errors.New("No such container: " + srcName)
|
|
|
}
|
|
|
|
|
|
-func (docker *Docker) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
flags := rcli.Subcmd(stdout,
|
|
|
"tar", "CONTAINER",
|
|
|
"Stream the contents of a container as a tar archive")
|
|
@@ -199,70 +382,67 @@ func (docker *Docker) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
|
|
return nil
|
|
|
}
|
|
|
name := flags.Arg(0)
|
|
|
- if _, exists := docker.findContainer(name); exists {
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
+ data, err := container.Filesystem.Tar()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
// Stream the entire contents of the container (basically a volatile snapshot)
|
|
|
- return fake.WriteFakeTar(stdout)
|
|
|
+ if _, err := io.Copy(stdout, data); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return nil
|
|
|
}
|
|
|
return errors.New("No such container: " + name)
|
|
|
}
|
|
|
|
|
|
-func (docker *Docker) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
flags := rcli.Subcmd(stdout,
|
|
|
"diff", "CONTAINER [OPTIONS]",
|
|
|
"Inspect changes on a container's filesystem")
|
|
|
- fl_diff := flags.Bool("d", true, "Show changes in diff format")
|
|
|
- fl_bytes := flags.Bool("b", false, "Show how many bytes have been changed")
|
|
|
- fl_list := flags.Bool("l", false, "Show a list of changed files")
|
|
|
if err := flags.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
|
if flags.NArg() < 1 {
|
|
|
return errors.New("Not enough arguments")
|
|
|
}
|
|
|
- if container, exists := docker.findContainer(flags.Arg(0)); !exists {
|
|
|
+ if container, exists := srv.findContainer(flags.Arg(0)); !exists {
|
|
|
return errors.New("No such container")
|
|
|
- } else if *fl_bytes {
|
|
|
- fmt.Fprintf(stdout, "%d\n", container.BytesChanged)
|
|
|
- } else if *fl_list {
|
|
|
- // FAKE
|
|
|
- fmt.Fprintf(stdout, strings.Join([]string{
|
|
|
- "/etc/postgres/pg.conf",
|
|
|
- "/etc/passwd",
|
|
|
- "/var/lib/postgres",
|
|
|
- "/usr/bin/postgres",
|
|
|
- "/usr/bin/psql",
|
|
|
- "/var/log/postgres",
|
|
|
- "/var/log/postgres/postgres.log",
|
|
|
- "/var/log/postgres/postgres.log.0",
|
|
|
- "/var/log/postgres/postgres.log.1.gz"}, "\n"))
|
|
|
- } else if *fl_diff {
|
|
|
- // Achievement unlocked: embed a diff of your code as a string in your code
|
|
|
- fmt.Fprintf(stdout, `
|
|
|
-diff --git a/dockerd/dockerd.go b/dockerd/dockerd.go
|
|
|
-index 2dae694..e43caca 100644
|
|
|
---- a/dockerd/dockerd.go
|
|
|
-+++ b/dockerd/dockerd.go
|
|
|
-@@ -158,6 +158,7 @@ func (docker *Docker) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...str
|
|
|
- flags := rcli.Subcmd(stdout,
|
|
|
- "diff", "CONTAINER [OPTIONS]",
|
|
|
- "Inspect changes on a container's filesystem")
|
|
|
-+ fl_diff := flags.Bool("d", true, "Show changes in diff format")
|
|
|
- fl_bytes := flags.Bool("b", false, "Show how many bytes have been changes")
|
|
|
- fl_list := flags.Bool("l", false, "Show a list of changed files")
|
|
|
- fl_download := flags.Bool("d", false, "Download the changes as gzipped tar stream")
|
|
|
-`)
|
|
|
- return nil
|
|
|
} else {
|
|
|
- flags.Usage()
|
|
|
- return nil
|
|
|
+ changes, err := container.Filesystem.Changes()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ for _, change := range changes {
|
|
|
+ fmt.Fprintln(stdout, change.String())
|
|
|
+ }
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+func (srv *Server) CmdReset(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+ flags := rcli.Subcmd(stdout,
|
|
|
+ "reset", "CONTAINER [OPTIONS]",
|
|
|
+ "Reset changes to a container's filesystem")
|
|
|
+ if err := flags.Parse(args); err != nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ if flags.NArg() < 1 {
|
|
|
+ return errors.New("Not enough arguments")
|
|
|
+ }
|
|
|
+ for _, name := range flags.Args() {
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
+ if err := container.Filesystem.Reset(); err != nil {
|
|
|
+ return errors.New("Reset " + container.Id + ": " + err.Error())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
|
|
|
// ByDate wraps an array of layers so they can be sorted by date (most recent first)
|
|
|
|
|
|
-type ByDate []*Container
|
|
|
+type ByDate []*docker.Container
|
|
|
|
|
|
func (c *ByDate) Len() int {
|
|
|
return len(*c)
|
|
@@ -280,7 +460,7 @@ func (c *ByDate) Swap(i, j int) {
|
|
|
containers[j] = tmp
|
|
|
}
|
|
|
|
|
|
-func (c *ByDate) Add(container *Container) {
|
|
|
+func (c *ByDate) Add(container *docker.Container) {
|
|
|
*c = append(*c, container)
|
|
|
sort.Sort(c)
|
|
|
}
|
|
@@ -294,46 +474,38 @@ func (c *ByDate) Del(id string) {
|
|
|
}
|
|
|
|
|
|
|
|
|
-func (docker *Docker) addContainer(name string, source string, size uint) *Container {
|
|
|
- if size == 0 {
|
|
|
- size = fake.RandomContainerSize()
|
|
|
+func (srv *Server) addContainer(id string, layers []string, name string, source string) (*docker.Container, error) {
|
|
|
+ c, err := srv.docker.Create(id, "", nil, layers, &docker.Config{Hostname: id, Ram: 512 * 1024 * 1024})
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- c := &Container{
|
|
|
- Id: future.RandomId(),
|
|
|
- Name: name,
|
|
|
- Created: time.Now(),
|
|
|
- Source: source,
|
|
|
- Size: size,
|
|
|
- stdinLog: new(bytes.Buffer),
|
|
|
- stdoutLog: new(bytes.Buffer),
|
|
|
+ if err := c.SetUserData("name", name); err != nil {
|
|
|
+ srv.docker.Destroy(c)
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- docker.containers[c.Id] = c
|
|
|
- if _, exists := docker.containersByName[c.Name]; !exists {
|
|
|
- docker.containersByName[c.Name] = new(ByDate)
|
|
|
+ if _, exists := srv.containersByName[name]; !exists {
|
|
|
+ srv.containersByName[name] = new(ByDate)
|
|
|
}
|
|
|
- docker.containersByName[c.Name].Add(c)
|
|
|
- return c
|
|
|
-
|
|
|
+ srv.containersByName[name].Add(c)
|
|
|
+ return c, nil
|
|
|
}
|
|
|
|
|
|
|
|
|
-func (docker *Docker) rm(id string) (*Container, error) {
|
|
|
- if container, exists := docker.containers[id]; exists {
|
|
|
- if container.Running {
|
|
|
- return nil, errors.New("Container is running: " + id)
|
|
|
- } else {
|
|
|
- // Remove from name lookup
|
|
|
- docker.containersByName[container.Name].Del(container.Id)
|
|
|
- // Remove from id lookup
|
|
|
- delete(docker.containers, container.Id)
|
|
|
- return container, nil
|
|
|
- }
|
|
|
+func (srv *Server) rm(id string) (*docker.Container, error) {
|
|
|
+ container := srv.docker.Get(id)
|
|
|
+ if container == nil {
|
|
|
+ return nil, errors.New("No such continer: " + id)
|
|
|
+ }
|
|
|
+ // Remove from name lookup
|
|
|
+ srv.containersByName[container.GetUserData("name")].Del(container.Id)
|
|
|
+ if err := srv.docker.Destroy(container); err != nil {
|
|
|
+ return container, err
|
|
|
}
|
|
|
- return nil, errors.New(fmt.Sprintf("No such container: %s", id))
|
|
|
+ return container, nil
|
|
|
}
|
|
|
|
|
|
|
|
|
-func (docker *Docker) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
flags := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container")
|
|
|
if err := flags.Parse(args); err != nil {
|
|
|
return nil
|
|
@@ -343,19 +515,23 @@ func (docker *Docker) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...str
|
|
|
return nil
|
|
|
}
|
|
|
name := flags.Arg(0)
|
|
|
- if container, exists := docker.findContainer(name); exists {
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
+ if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
return nil
|
|
|
}
|
|
|
return errors.New("No such container: " + flags.Arg(0))
|
|
|
}
|
|
|
|
|
|
-func (docker *Docker) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+
|
|
|
+func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
flags := rcli.Subcmd(stdout, "run", "[OPTIONS] CONTAINER COMMAND [ARG...]", "Run a command in a container")
|
|
|
fl_attach := flags.Bool("a", false, "Attach stdin and stdout")
|
|
|
- fl_tty := flags.Bool("t", false, "Allocate a pseudo-tty")
|
|
|
+ //fl_tty := flags.Bool("t", false, "Allocate a pseudo-tty")
|
|
|
if err := flags.Parse(args); err != nil {
|
|
|
return nil
|
|
|
}
|
|
@@ -364,62 +540,60 @@ func (docker *Docker) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
|
|
return nil
|
|
|
}
|
|
|
name, cmd := flags.Arg(0), flags.Args()[1:]
|
|
|
- if container, exists := docker.findContainer(name); exists {
|
|
|
- if container.Running {
|
|
|
- return errors.New("Already running: " + name)
|
|
|
- }
|
|
|
+ if container, exists := srv.findContainer(name); exists {
|
|
|
+ log.Printf("Running container %#v\n", container)
|
|
|
+ container.Path = cmd[0]
|
|
|
+ container.Args = cmd[1:]
|
|
|
if *fl_attach {
|
|
|
- return container.Run(cmd[0], cmd[1:], stdin, stdout, *fl_tty)
|
|
|
+ cmd_stdout, err := container.StdoutPipe()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ cmd_stderr, err := container.StderrPipe()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ if err := container.Start(); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ sending_stdout := future.Go(func() error { _, err := io.Copy(stdout, cmd_stdout); return err })
|
|
|
+ sending_stderr := future.Go(func() error { _, err := io.Copy(stdout, cmd_stderr); return err })
|
|
|
+ err_sending_stdout := <-sending_stdout
|
|
|
+ err_sending_stderr := <-sending_stderr
|
|
|
+ if err_sending_stdout != nil {
|
|
|
+ return err_sending_stdout
|
|
|
+ }
|
|
|
+ return err_sending_stderr
|
|
|
} else {
|
|
|
- go container.Run(cmd[0], cmd[1:], ioutil.NopCloser(new(bytes.Buffer)), ioutil.Discard, *fl_tty)
|
|
|
- fmt.Fprintln(stdout, container.Id)
|
|
|
- return nil
|
|
|
+ if output, err := container.Output(); err != nil {
|
|
|
+ return err
|
|
|
+ } else {
|
|
|
+ fmt.Printf("-->|%s|\n", output)
|
|
|
+ }
|
|
|
}
|
|
|
+ return nil
|
|
|
}
|
|
|
return errors.New("No such container: " + name)
|
|
|
}
|
|
|
|
|
|
-func startCommand(cmd *exec.Cmd, interactive bool) (io.WriteCloser, io.ReadCloser, error) {
|
|
|
- if interactive {
|
|
|
- term, err := pty.Start(cmd)
|
|
|
- if err != nil {
|
|
|
- return nil, nil, err
|
|
|
- }
|
|
|
- return term, term, nil
|
|
|
- }
|
|
|
- stdin, err := cmd.StdinPipe()
|
|
|
- if err != nil {
|
|
|
- return nil, nil, err
|
|
|
- }
|
|
|
- stdout, err := cmd.StdoutPipe()
|
|
|
- if err != nil {
|
|
|
- return nil, nil, err
|
|
|
- }
|
|
|
- if err := cmd.Start(); err != nil {
|
|
|
- return nil, nil, err
|
|
|
- }
|
|
|
- return stdin, stdout, nil
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
func main() {
|
|
|
future.Seed()
|
|
|
flag.Parse()
|
|
|
- docker, err := New()
|
|
|
+ d, err := New()
|
|
|
if err != nil {
|
|
|
log.Fatal(err)
|
|
|
}
|
|
|
go func() {
|
|
|
- if err := rcli.ListenAndServeHTTP(":8080", docker); err != nil {
|
|
|
+ if err := rcli.ListenAndServeHTTP(":8080", d); err != nil {
|
|
|
log.Fatal(err)
|
|
|
}
|
|
|
}()
|
|
|
- if err := rcli.ListenAndServeTCP(":4242", docker); err != nil {
|
|
|
+ if err := rcli.ListenAndServeTCP(":4242", d); err != nil {
|
|
|
log.Fatal(err)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func New() (*Docker, error) {
|
|
|
+func New() (*Server, error) {
|
|
|
store, err := future.NewStore("/var/lib/docker/layers")
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
@@ -427,20 +601,32 @@ func New() (*Docker, error) {
|
|
|
if err := store.Init(); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
- return &Docker{
|
|
|
+ d, err := docker.New()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ srv := &Server{
|
|
|
containersByName: make(map[string]*ByDate),
|
|
|
- containers: make(map[string]*Container),
|
|
|
layers: store,
|
|
|
- }, nil
|
|
|
+ docker: d,
|
|
|
+ }
|
|
|
+ for _, container := range srv.docker.List() {
|
|
|
+ name := container.GetUserData("name")
|
|
|
+ if _, exists := srv.containersByName[name]; !exists {
|
|
|
+ srv.containersByName[name] = new(ByDate)
|
|
|
+ }
|
|
|
+ srv.containersByName[name].Add(container)
|
|
|
+ }
|
|
|
+ log.Printf("Done building index\n")
|
|
|
+ return srv, nil
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-func (docker *Docker) CmdMirror(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdMirror(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
_, err := io.Copy(stdout, stdin)
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
-func (docker *Docker) CmdDebug(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdDebug(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
for {
|
|
|
if line, err := bufio.NewReader(stdin).ReadString('\n'); err == nil {
|
|
|
fmt.Printf("--- %s", line)
|
|
@@ -456,7 +642,7 @@ func (docker *Docker) CmdDebug(stdin io.ReadCloser, stdout io.Writer, args ...st
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (docker *Docker) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
+func (srv *Server) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
|
flags := rcli.Subcmd(stdout, "web", "[OPTIONS]", "A web UI for docker")
|
|
|
showurl := flags.Bool("u", false, "Return the URL of the web UI")
|
|
|
if err := flags.Parse(args); err != nil {
|
|
@@ -475,85 +661,9 @@ func (docker *Docker) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
|
|
}
|
|
|
|
|
|
|
|
|
-type Docker struct {
|
|
|
- containers map[string]*Container
|
|
|
+type Server struct {
|
|
|
containersByName map[string]*ByDate
|
|
|
layers *future.Store
|
|
|
-}
|
|
|
-
|
|
|
-type Container struct {
|
|
|
- Id string
|
|
|
- Name string
|
|
|
- Created time.Time
|
|
|
- Source string
|
|
|
- Size uint
|
|
|
- FilesChanged uint
|
|
|
- BytesChanged uint
|
|
|
- Running bool
|
|
|
- Cmd string
|
|
|
- Args []string
|
|
|
- stdoutLog *bytes.Buffer
|
|
|
- stdinLog *bytes.Buffer
|
|
|
-}
|
|
|
-
|
|
|
-func (c *Container) Run(command string, args []string, stdin io.ReadCloser, stdout io.Writer, tty bool) error {
|
|
|
- // Not thread-safe
|
|
|
- if c.Running {
|
|
|
- return errors.New("Already running")
|
|
|
- }
|
|
|
- c.Cmd = command
|
|
|
- c.Args = args
|
|
|
- // Reset logs
|
|
|
- c.stdoutLog.Reset()
|
|
|
- c.stdinLog.Reset()
|
|
|
- cmd := exec.Command(c.Cmd, c.Args...)
|
|
|
- cmd_stdin, cmd_stdout, err := startCommand(cmd, tty)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- c.Running = true
|
|
|
- // ADD FAKE RANDOM CHANGES
|
|
|
- c.FilesChanged = fake.RandomFilesChanged()
|
|
|
- c.BytesChanged = fake.RandomBytesChanged()
|
|
|
- copy_out := future.Go(func() error {
|
|
|
- _, err := io.Copy(io.MultiWriter(stdout, c.stdoutLog), cmd_stdout)
|
|
|
- return err
|
|
|
- })
|
|
|
- future.Go(func() error {
|
|
|
- _, err := io.Copy(io.MultiWriter(cmd_stdin, c.stdinLog), stdin)
|
|
|
- cmd_stdin.Close()
|
|
|
- stdin.Close()
|
|
|
- return err
|
|
|
- })
|
|
|
- wait := future.Go(func() error {
|
|
|
- err := cmd.Wait()
|
|
|
- c.Running = false
|
|
|
- return err
|
|
|
- })
|
|
|
- if err := <-copy_out; err != nil {
|
|
|
- if c.Running {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
- if err := <-wait; err != nil {
|
|
|
- if status, ok := err.(*exec.ExitError); ok {
|
|
|
- fmt.Fprintln(stdout, status)
|
|
|
- return nil
|
|
|
- }
|
|
|
- return err
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-func (c *Container) StdoutLog() io.Reader {
|
|
|
- return strings.NewReader(c.stdoutLog.String())
|
|
|
-}
|
|
|
-
|
|
|
-func (c *Container) StdinLog() io.Reader {
|
|
|
- return strings.NewReader(c.stdinLog.String())
|
|
|
-}
|
|
|
-
|
|
|
-func (c *Container) CmdString() string {
|
|
|
- return strings.Join(append([]string{c.Cmd}, c.Args...), " ")
|
|
|
+ docker *docker.Docker
|
|
|
}
|
|
|
|