From d2cba75d5f1e8ceea7f301ef146c571806252fa0 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Tue, 12 Mar 2013 15:05:41 -0700 Subject: [PATCH 1/8] Removed interactive mode ('docker -i'). Cool UI experiment but seems more trouble than it's worth --- client/client.go | 73 ------------------------------------------------ docker/docker.go | 20 ++----------- 2 files changed, 2 insertions(+), 91 deletions(-) diff --git a/client/client.go b/client/client.go index 073fe02b0..815e20a04 100644 --- a/client/client.go +++ b/client/client.go @@ -4,12 +4,8 @@ import ( "../future" "../rcli" "io" - "io/ioutil" "log" "os" - "os/exec" - "path" - "path/filepath" ) // Run docker in "simple mode": run a single command and return. @@ -55,72 +51,3 @@ func SimpleMode(args []string) error { } return nil } - -// Run docker in "interactive mode": run a bash-compatible shell capable of running docker commands. -func InteractiveMode(scripts ...string) error { - // Determine path of current docker binary - dockerPath, err := exec.LookPath(os.Args[0]) - if err != nil { - return err - } - dockerPath, err = filepath.Abs(dockerPath) - if err != nil { - return err - } - - // Create a temp directory - tmp, err := ioutil.TempDir("", "docker-shell") - if err != nil { - return err - } - defer os.RemoveAll(tmp) - - // For each command, create an alias in temp directory - // FIXME: generate this list dynamically with introspection of some sort - // It might make sense to merge docker and dockerd to keep that introspection - // within a single binary. - for _, cmd := range []string{ - "help", - "run", - "ps", - "pull", - "put", - "rm", - "kill", - "wait", - "stop", - "start", - "restart", - "logs", - "diff", - "commit", - "attach", - "info", - "tar", - "web", - "images", - "docker", - } { - if err := os.Symlink(dockerPath, path.Join(tmp, cmd)); err != nil { - return err - } - } - - // Run $SHELL with PATH set to temp directory - rcfile, err := ioutil.TempFile("", "docker-shell-rc") - if err != nil { - return err - } - defer os.Remove(rcfile.Name()) - io.WriteString(rcfile, "enable -n help\n") - os.Setenv("PATH", tmp+":"+os.Getenv("PATH")) - os.Setenv("PS1", "\\h docker> ") - shell := exec.Command("/bin/bash", append([]string{"--rcfile", rcfile.Name()}, scripts...)...) - shell.Stdin = os.Stdin - shell.Stdout = os.Stdout - shell.Stderr = os.Stderr - if err := shell.Run(); err != nil { - return err - } - return nil -} diff --git a/docker/docker.go b/docker/docker.go index df8a09443..b43d5b105 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -2,28 +2,12 @@ package main import ( "../client" - "flag" "log" "os" - "path" ) func main() { - if cmd := path.Base(os.Args[0]); cmd == "docker" { - fl_shell := flag.Bool("i", false, "Interactive mode") - flag.Parse() - if *fl_shell { - if err := client.InteractiveMode(flag.Args()...); err != nil { - log.Fatal(err) - } - } else { - if err := client.SimpleMode(os.Args[1:]); err != nil { - log.Fatal(err) - } - } - } else { - if err := client.SimpleMode(append([]string{cmd}, os.Args[1:]...)); err != nil { - log.Fatal(err) - } + if err := client.SimpleMode(os.Args[1:]); err != nil { + log.Fatal(err) } } From 745edc49cdcf0dc02fe714b0772f146fc8a66d23 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 13 Mar 2013 00:29:40 -0700 Subject: [PATCH 2/8] Merge dockerd into docker. 'docker -d' runs in daemon mode. For all other commands, docker auto-detects whether to run standalone or to remote-control the daemon --- .gitignore | 1 - client/client.go | 53 --------------- server/server.go => commands/commands.go | 11 +-- docker/docker.go | 86 +++++++++++++++++++++++- dockerd/dockerd.go | 24 ------- rcli/types.go | 5 ++ {client => term}/term.go | 2 +- {client => term}/termios_darwin.go | 2 +- {client => term}/termios_linux.go | 2 +- 9 files changed, 92 insertions(+), 94 deletions(-) delete mode 100644 client/client.go rename server/server.go => commands/commands.go (98%) delete mode 100644 dockerd/dockerd.go rename {client => term}/term.go (99%) rename {client => term}/termios_darwin.go (85%) rename {client => term}/termios_linux.go (85%) diff --git a/.gitignore b/.gitignore index d55740421..686ac8342 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .vagrant docker/docker -dockerd/dockerd .*.swp a.out *.orig diff --git a/client/client.go b/client/client.go deleted file mode 100644 index 30d741f8b..000000000 --- a/client/client.go +++ /dev/null @@ -1,53 +0,0 @@ -package client - -import ( - "github.com/dotcloud/docker/future" - "github.com/dotcloud/docker/rcli" - "io" - "log" - "os" -) - -// Run docker in "simple mode": run a single command and return. -func SimpleMode(args []string) error { - var oldState *State - var err error - if IsTerminal(0) && os.Getenv("NORAW") == "" { - oldState, err = MakeRaw(0) - if err != nil { - return err - } - defer Restore(0, oldState) - } - // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose - // CloseWrite(), which we need to cleanly signal that stdin is closed without - // closing the connection. - // See http://code.google.com/p/go/issues/detail?id=3345 - conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...) - if err != nil { - return err - } - receive_stdout := future.Go(func() error { - _, err := io.Copy(os.Stdout, conn) - return err - }) - send_stdin := future.Go(func() error { - _, err := io.Copy(conn, os.Stdin) - if err := conn.CloseWrite(); err != nil { - log.Printf("Couldn't send EOF: " + err.Error()) - } - return err - }) - if err := <-receive_stdout; err != nil { - return err - } - if oldState != nil { - Restore(0, oldState) - } - if !IsTerminal(0) { - if err := <-send_stdin; err != nil { - return err - } - } - return nil -} diff --git a/server/server.go b/commands/commands.go similarity index 98% rename from server/server.go rename to commands/commands.go index 18dc57819..14649cad1 100644 --- a/server/server.go +++ b/commands/commands.go @@ -1,4 +1,4 @@ -package server +package commands import ( "bufio" @@ -24,15 +24,6 @@ import ( const VERSION = "0.0.1" -func (srv *Server) ListenAndServe() error { - go rcli.ListenAndServeHTTP("127.0.0.1:8080", srv) - // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose - // CloseWrite(), which we need to cleanly signal that stdin is closed without - // closing the connection. - // See http://code.google.com/p/go/issues/detail?id=3345 - return rcli.ListenAndServe("tcp", "127.0.0.1:4242", srv) -} - func (srv *Server) Name() string { return "docker" } diff --git a/docker/docker.go b/docker/docker.go index 29a550c7b..ad121a8f6 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -1,13 +1,93 @@ package main import ( - "github.com/dotcloud/docker/client" + "flag" + "github.com/dotcloud/docker" + "github.com/dotcloud/docker/commands" + "github.com/dotcloud/docker/future" + "github.com/dotcloud/docker/rcli" + "github.com/dotcloud/docker/term" + "io" "log" "os" ) func main() { - if err := client.SimpleMode(os.Args[1:]); err != nil { - log.Fatal(err) + if docker.SelfPath() == "/sbin/init" { + // Running in init mode + docker.SysInit() + return + } + fl_daemon := flag.Bool("d", false, "Daemon mode") + flag.Parse() + if *fl_daemon { + if flag.NArg() != 0 { + flag.Usage() + return + } + if err := daemon(); err != nil { + log.Fatal(err) + } + } else { + if err := runCommand(flag.Args()); err != nil { + log.Fatal(err) + } } } + +func daemon() error { + service, err := commands.New() + if err != nil { + return err + } + return rcli.ListenAndServe("tcp", "127.0.0.1:4242", service) +} + +func runCommand(args []string) error { + var oldState *term.State + var err error + if term.IsTerminal(0) && os.Getenv("NORAW") == "" { + oldState, err = term.MakeRaw(0) + if err != nil { + return err + } + defer term.Restore(0, oldState) + } + // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose + // CloseWrite(), which we need to cleanly signal that stdin is closed without + // closing the connection. + // See http://code.google.com/p/go/issues/detail?id=3345 + if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil { + receive_stdout := future.Go(func() error { + _, err := io.Copy(os.Stdout, conn) + return err + }) + send_stdin := future.Go(func() error { + _, err := io.Copy(conn, os.Stdin) + if err := conn.CloseWrite(); err != nil { + log.Printf("Couldn't send EOF: " + err.Error()) + } + return err + }) + if err := <-receive_stdout; err != nil { + return err + } + if !term.IsTerminal(0) { + if err := <-send_stdin; err != nil { + return err + } + } + } else { + service, err := commands.New() + if err != nil { + return err + } + if err := rcli.LocalCall(service, os.Stdin, os.Stdout, args...); err != nil { + return err + } + } + if oldState != nil { + term.Restore(0, oldState) + } + return nil +} diff --git a/dockerd/dockerd.go b/dockerd/dockerd.go deleted file mode 100644 index b2337d342..000000000 --- a/dockerd/dockerd.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "flag" - "github.com/dotcloud/docker" - "github.com/dotcloud/docker/server" - "log" -) - -func main() { - if docker.SelfPath() == "/sbin/init" { - // Running in init mode - docker.SysInit() - return - } - flag.Parse() - d, err := server.New() - if err != nil { - log.Fatal(err) - } - if err := d.ListenAndServe(); err != nil { - log.Fatal(err) - } -} diff --git a/rcli/types.go b/rcli/types.go index 52079291b..2600fe240 100644 --- a/rcli/types.go +++ b/rcli/types.go @@ -25,7 +25,12 @@ type Service interface { type Cmd func(io.ReadCloser, io.Writer, ...string) error type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error +// FIXME: For reverse compatibility func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error { + return LocalCall(service, stdin, stdout, args...) +} + +func LocalCall(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error { if len(args) == 0 { args = []string{"help"} } diff --git a/client/term.go b/term/term.go similarity index 99% rename from client/term.go rename to term/term.go index a988d0d79..fdbe3b998 100644 --- a/client/term.go +++ b/term/term.go @@ -1,4 +1,4 @@ -package client +package term import ( "syscall" diff --git a/client/termios_darwin.go b/term/termios_darwin.go similarity index 85% rename from client/termios_darwin.go rename to term/termios_darwin.go index 185687920..26d17b3bb 100644 --- a/client/termios_darwin.go +++ b/term/termios_darwin.go @@ -1,4 +1,4 @@ -package client +package term import "syscall" diff --git a/client/termios_linux.go b/term/termios_linux.go similarity index 85% rename from client/termios_linux.go rename to term/termios_linux.go index 36957c44a..1ea26fa5c 100644 --- a/client/termios_linux.go +++ b/term/termios_linux.go @@ -1,4 +1,4 @@ -package client +package term import "syscall" From c3622a963d326df8da8049302e0f2ea4875fc496 Mon Sep 17 00:00:00 2001 From: shin- Date: Wed, 13 Mar 2013 11:14:37 -0700 Subject: [PATCH 3/8] Re-enabled docker rmi --- fs/store.go | 18 +++++++++++++++ server/server.go | 58 +++++++++++++++++++++++++----------------------- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/fs/store.go b/fs/store.go index 36449a0fb..7b8447105 100644 --- a/fs/store.go +++ b/fs/store.go @@ -92,6 +92,24 @@ func (store *Store) Paths() ([]string, error) { return paths, nil } +func (store *Store) RemoveInPath(pth string) error { + images, err := store.List(pth) + if err != nil { + return err + } + for _, img := range images { + if err = store.Remove(img); err != nil { + return err + } + } + return nil +} + +func (store *Store) Remove(img *Image) error { + _, err := store.orm.Delete(img) + return err +} + func (store *Store) List(pth string) ([]*Image, error) { pth = path.Clean(pth) images, err := store.orm.Select(Image{}, "select images.* from images, paths where Path=? and paths.Image=images.Id", pth) diff --git a/server/server.go b/server/server.go index 18dc57819..07b462d09 100644 --- a/server/server.go +++ b/server/server.go @@ -360,34 +360,36 @@ func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string } // 'docker rmi NAME' removes all images with the name NAME -// func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) error { -// cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image") -// fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name") -// if err := cmd.Parse(args); err != nil { -// cmd.Usage() -// return nil -// } -// if cmd.NArg() < 1 { -// cmd.Usage() -// return nil -// } -// for _, name := range cmd.Args() { -// var err error -// if *fl_regexp { -// err = srv.images.DeleteMatch(name) -// } else { -// image := srv.images.Find(name) -// if image == nil { -// return errors.New("No such image: " + name) -// } -// err = srv.images.Delete(name) -// } -// if err != nil { -// return err -// } -// } -// return nil -// } +func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) error { + 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") + if err := cmd.Parse(args); err != nil { + cmd.Usage() + return nil + } + if cmd.NArg() < 1 { + cmd.Usage() + return nil + } + for _, name := range cmd.Args() { + var err error + if *fl_all { + err = srv.images.RemoveInPath(name) + } else { + image, err := srv.images.Get(name) + if err != nil { + return err + } else if image == nil { + return errors.New("No such image: " + name) + } + err = srv.images.Remove(image) + } + if err != nil { + return err + } + } + return nil +} func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error { cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container") From d54ff35f022e5e11c5ca124237a1888c4a6161b9 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 13 Mar 2013 11:27:06 -0700 Subject: [PATCH 4/8] Moved download binaries to get.docker.io --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a4a78b1e..6f02edf82 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ Installation apt-get update apt-get install lxc wget bsdtar curl -4. Download the latest docker binaries: `wget http://docker.io.s3.amazonaws.com/builds/$(uname -s)/$(uname -m)/docker-master.tgz` ([Or get the Linux/x86_64 binaries here](http://docker.io.s3.amazonaws.com/builds/Linux/x86_64/docker-master.tgz) ) +4. Download the latest docker binaries: `wget http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz` ([Or get the Linux/x86_64 binaries here](http://get.docker.io/builds/Linux/x86_64/docker-master.tgz) ) 5. Extract the contents of the tar file `tar -xf docker-master.tar.gz` 6. Launch the docker daemon in the background `./dockerd &` 7. Download a base image `./docker pull base` From 895e145e615b407aa485189a8021fd8640700163 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 13 Mar 2013 11:28:52 -0700 Subject: [PATCH 5/8] README: removed superfluous install step --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6f02edf82..abfd268ad 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,6 @@ Installation 2. Assume root identity on your newly installed environment (`sudo -s`) 3. Type the following commands: - apt-get update apt-get install lxc wget bsdtar curl 4. Download the latest docker binaries: `wget http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz` ([Or get the Linux/x86_64 binaries here](http://get.docker.io/builds/Linux/x86_64/docker-master.tgz) ) From baf6988d872be24130fc2923bef227f5365cbf1b Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 13 Mar 2013 11:46:09 -0700 Subject: [PATCH 6/8] Updated README --- README.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 8a4a78b1e..49a418bae 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,31 @@ Under the hood, Docker is built on the following components: * [lxc](http://lxc.sourceforge.net/), a set of convenience scripts to simplify the creation of linux containers. -Setup instructions +Install instructions ================== -Requirements ------------- +Installing on Ubuntu 12.04 and 12.10 +------------------------------------ + +1. Install dependencies: + + sudo apt-get install lxc wget bsdtar curl + +2. Install the latest docker binary: + + wget http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz + tar -xf docker-master.tgz + +3. Run your first container! + + cd docker-master + sudo ./docker import base + sudo ./docker run -a -i -t base /bin/bash + +Consider adding docker to your `PATH` for simplicity. + +Installing on other Linux distributions +--------------------------------------- Right now, the officially supported distributions are: @@ -64,26 +84,6 @@ Right now, the officially supported distributions are: Docker probably works on other distributions featuring a recent kernel, the AUFS patch, and up-to-date lxc. However this has not been tested. -Installation ---------------- - -1. Set up your host of choice on a physical / virtual machine -2. Assume root identity on your newly installed environment (`sudo -s`) -3. Type the following commands: - - apt-get update - apt-get install lxc wget bsdtar curl - -4. Download the latest docker binaries: `wget http://docker.io.s3.amazonaws.com/builds/$(uname -s)/$(uname -m)/docker-master.tgz` ([Or get the Linux/x86_64 binaries here](http://docker.io.s3.amazonaws.com/builds/Linux/x86_64/docker-master.tgz) ) -5. Extract the contents of the tar file `tar -xf docker-master.tar.gz` -6. Launch the docker daemon in the background `./dockerd &` -7. Download a base image `./docker pull base` -8. Run your first container! `./docker run -i -a -t base /bin/bash` -9. Start exploring `./docker --help` - -Consider adding docker and dockerd to your `PATH` for simplicity. - - What is a Standard Container? ----------------------------- From d614e91b62339d1960cf171c2436404bb1fa8806 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 13 Mar 2013 11:58:15 -0700 Subject: [PATCH 7/8] Added usage exmaples to the README --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 49a418bae..103410e79 100644 --- a/README.md +++ b/README.md @@ -84,8 +84,68 @@ Right now, the officially supported distributions are: Docker probably works on other distributions featuring a recent kernel, the AUFS patch, and up-to-date lxc. However this has not been tested. +Usage examples +============== + +Running an interactive shell +---------------------------- + +```bash + # Download a base image + docker import base + + # Run an interactive shell in the base image, + # allocate a tty, attach stdin and stdout + docker run -a -i -t base /bin/bash +``` + + +Starting a long-running worker process +-------------------------------------- + +```bash + # Run docker in daemon mode + (docker -d || echo "Docker daemon already running") & + + # Start a very useful long-running process + JOB=$(docker run /bin/sh -c "while true; do echo Hello world!; sleep 1; done") + + # Collect the output of the job so far + docker logs $JOB + + # Kill the job + docker kill $JOB +``` + + +Listing all running containers +------------------------------ + +```bash + docker ps +``` + + +Expose a service on a TCP port +------------------------------ + +```bash + # Expose port 4444 of this container, and tell netcat to listen on it + JOB=$(docker run -p 4444 base /bin/nc -l -p 4444) + + # Which public port is NATed to my container? + PORT=$(docker port $JOB 4444) + + # Connect to the public port via the host's public address + echo hello world | nc $(hostname) $PORT + + # Verify that the network connection worked + echo "Daemon received: $(docker logs $JOB)" +``` + + What is a Standard Container? ------------------------------ +============================= Docker defines a unit of software delivery called a Standard Container. The goal of a Standard Container is to encapsulate a software component and all its dependencies in a format that is self-describing and portable, so that any compliant runtime can run it without extra dependency, regardless of the underlying machine and the contents of the container. From 8d5f683dc47795f6797eb14b498149c612398bd2 Mon Sep 17 00:00:00 2001 From: Solomon Hykes Date: Wed, 13 Mar 2013 12:00:13 -0700 Subject: [PATCH 8/8] Fixed formatting in README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 103410e79..07449fa3f 100644 --- a/README.md +++ b/README.md @@ -58,18 +58,24 @@ Installing on Ubuntu 12.04 and 12.10 1. Install dependencies: +```bash sudo apt-get install lxc wget bsdtar curl +``` 2. Install the latest docker binary: +```bash wget http://get.docker.io/builds/$(uname -s)/$(uname -m)/docker-master.tgz tar -xf docker-master.tgz +``` 3. Run your first container! +```bash cd docker-master sudo ./docker import base sudo ./docker run -a -i -t base /bin/bash +``` Consider adding docker to your `PATH` for simplicity.