| 7 | 6 |
deleted file mode 100644 |
| ... | ... |
@@ -1,53 +0,0 @@ |
| 1 |
-package client |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "github.com/dotcloud/docker/future" |
|
| 5 |
- "github.com/dotcloud/docker/rcli" |
|
| 6 |
- "io" |
|
| 7 |
- "log" |
|
| 8 |
- "os" |
|
| 9 |
-) |
|
| 10 |
- |
|
| 11 |
-// Run docker in "simple mode": run a single command and return. |
|
| 12 |
-func SimpleMode(args []string) error {
|
|
| 13 |
- var oldState *State |
|
| 14 |
- var err error |
|
| 15 |
- if IsTerminal(0) && os.Getenv("NORAW") == "" {
|
|
| 16 |
- oldState, err = MakeRaw(0) |
|
| 17 |
- if err != nil {
|
|
| 18 |
- return err |
|
| 19 |
- } |
|
| 20 |
- defer Restore(0, oldState) |
|
| 21 |
- } |
|
| 22 |
- // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose |
|
| 23 |
- // CloseWrite(), which we need to cleanly signal that stdin is closed without |
|
| 24 |
- // closing the connection. |
|
| 25 |
- // See http://code.google.com/p/go/issues/detail?id=3345 |
|
| 26 |
- conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...)
|
|
| 27 |
- if err != nil {
|
|
| 28 |
- return err |
|
| 29 |
- } |
|
| 30 |
- receive_stdout := future.Go(func() error {
|
|
| 31 |
- _, err := io.Copy(os.Stdout, conn) |
|
| 32 |
- return err |
|
| 33 |
- }) |
|
| 34 |
- send_stdin := future.Go(func() error {
|
|
| 35 |
- _, err := io.Copy(conn, os.Stdin) |
|
| 36 |
- if err := conn.CloseWrite(); err != nil {
|
|
| 37 |
- log.Printf("Couldn't send EOF: " + err.Error())
|
|
| 38 |
- } |
|
| 39 |
- return err |
|
| 40 |
- }) |
|
| 41 |
- if err := <-receive_stdout; err != nil {
|
|
| 42 |
- return err |
|
| 43 |
- } |
|
| 44 |
- if oldState != nil {
|
|
| 45 |
- Restore(0, oldState) |
|
| 46 |
- } |
|
| 47 |
- if !IsTerminal(0) {
|
|
| 48 |
- if err := <-send_stdin; err != nil {
|
|
| 49 |
- return err |
|
| 50 |
- } |
|
| 51 |
- } |
|
| 52 |
- return nil |
|
| 53 |
-} |
| 54 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,143 +0,0 @@ |
| 1 |
-package client |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "syscall" |
|
| 5 |
- "unsafe" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-type Termios struct {
|
|
| 9 |
- Iflag uintptr |
|
| 10 |
- Oflag uintptr |
|
| 11 |
- Cflag uintptr |
|
| 12 |
- Lflag uintptr |
|
| 13 |
- Cc [20]byte |
|
| 14 |
- Ispeed uintptr |
|
| 15 |
- Ospeed uintptr |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-const ( |
|
| 19 |
- // Input flags |
|
| 20 |
- inpck = 0x010 |
|
| 21 |
- istrip = 0x020 |
|
| 22 |
- icrnl = 0x100 |
|
| 23 |
- ixon = 0x200 |
|
| 24 |
- |
|
| 25 |
- // Output flags |
|
| 26 |
- opost = 0x1 |
|
| 27 |
- |
|
| 28 |
- // Control flags |
|
| 29 |
- cs8 = 0x300 |
|
| 30 |
- |
|
| 31 |
- // Local flags |
|
| 32 |
- icanon = 0x100 |
|
| 33 |
- iexten = 0x400 |
|
| 34 |
-) |
|
| 35 |
- |
|
| 36 |
-const ( |
|
| 37 |
- HUPCL = 0x4000 |
|
| 38 |
- ICANON = 0x100 |
|
| 39 |
- ICRNL = 0x100 |
|
| 40 |
- IEXTEN = 0x400 |
|
| 41 |
- BRKINT = 0x2 |
|
| 42 |
- CFLUSH = 0xf |
|
| 43 |
- CLOCAL = 0x8000 |
|
| 44 |
- CREAD = 0x800 |
|
| 45 |
- CS5 = 0x0 |
|
| 46 |
- CS6 = 0x100 |
|
| 47 |
- CS7 = 0x200 |
|
| 48 |
- CS8 = 0x300 |
|
| 49 |
- CSIZE = 0x300 |
|
| 50 |
- CSTART = 0x11 |
|
| 51 |
- CSTATUS = 0x14 |
|
| 52 |
- CSTOP = 0x13 |
|
| 53 |
- CSTOPB = 0x400 |
|
| 54 |
- CSUSP = 0x1a |
|
| 55 |
- IGNBRK = 0x1 |
|
| 56 |
- IGNCR = 0x80 |
|
| 57 |
- IGNPAR = 0x4 |
|
| 58 |
- IMAXBEL = 0x2000 |
|
| 59 |
- INLCR = 0x40 |
|
| 60 |
- INPCK = 0x10 |
|
| 61 |
- ISIG = 0x80 |
|
| 62 |
- ISTRIP = 0x20 |
|
| 63 |
- IUTF8 = 0x4000 |
|
| 64 |
- IXANY = 0x800 |
|
| 65 |
- IXOFF = 0x400 |
|
| 66 |
- IXON = 0x200 |
|
| 67 |
- NOFLSH = 0x80000000 |
|
| 68 |
- OCRNL = 0x10 |
|
| 69 |
- OFDEL = 0x20000 |
|
| 70 |
- OFILL = 0x80 |
|
| 71 |
- ONLCR = 0x2 |
|
| 72 |
- ONLRET = 0x40 |
|
| 73 |
- ONOCR = 0x20 |
|
| 74 |
- ONOEOT = 0x8 |
|
| 75 |
- OPOST = 0x1 |
|
| 76 |
- RENB = 0x1000 |
|
| 77 |
- PARMRK = 0x8 |
|
| 78 |
- PARODD = 0x2000 |
|
| 79 |
- |
|
| 80 |
- TOSTOP = 0x400000 |
|
| 81 |
- VDISCARD = 0xf |
|
| 82 |
- VDSUSP = 0xb |
|
| 83 |
- VEOF = 0x0 |
|
| 84 |
- VEOL = 0x1 |
|
| 85 |
- VEOL2 = 0x2 |
|
| 86 |
- VERASE = 0x3 |
|
| 87 |
- VINTR = 0x8 |
|
| 88 |
- VKILL = 0x5 |
|
| 89 |
- VLNEXT = 0xe |
|
| 90 |
- VMIN = 0x10 |
|
| 91 |
- VQUIT = 0x9 |
|
| 92 |
- VREPRINT = 0x6 |
|
| 93 |
- VSTART = 0xc |
|
| 94 |
- VSTATUS = 0x12 |
|
| 95 |
- VSTOP = 0xd |
|
| 96 |
- VSUSP = 0xa |
|
| 97 |
- VT0 = 0x0 |
|
| 98 |
- VT1 = 0x10000 |
|
| 99 |
- VTDLY = 0x10000 |
|
| 100 |
- VTIME = 0x11 |
|
| 101 |
- ECHO = 0x00000008 |
|
| 102 |
- |
|
| 103 |
- PENDIN = 0x20000000 |
|
| 104 |
-) |
|
| 105 |
- |
|
| 106 |
-type State struct {
|
|
| 107 |
- termios Termios |
|
| 108 |
-} |
|
| 109 |
- |
|
| 110 |
-// IsTerminal returns true if the given file descriptor is a terminal. |
|
| 111 |
-func IsTerminal(fd int) bool {
|
|
| 112 |
- var termios Termios |
|
| 113 |
- _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)), 0, 0, 0) |
|
| 114 |
- return err == 0 |
|
| 115 |
-} |
|
| 116 |
- |
|
| 117 |
-// MakeRaw put the terminal connected to the given file descriptor into raw |
|
| 118 |
-// mode and returns the previous state of the terminal so that it can be |
|
| 119 |
-// restored. |
|
| 120 |
-func MakeRaw(fd int) (*State, error) {
|
|
| 121 |
- var oldState State |
|
| 122 |
- if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
|
| 123 |
- return nil, err |
|
| 124 |
- } |
|
| 125 |
- |
|
| 126 |
- newState := oldState.termios |
|
| 127 |
- newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF |
|
| 128 |
- newState.Iflag |= ICRNL |
|
| 129 |
- newState.Oflag |= ONLCR |
|
| 130 |
- newState.Lflag &^= ECHO | ICANON | ISIG |
|
| 131 |
- if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
|
| 132 |
- return nil, err |
|
| 133 |
- } |
|
| 134 |
- |
|
| 135 |
- return &oldState, nil |
|
| 136 |
-} |
|
| 137 |
- |
|
| 138 |
-// Restore restores the terminal connected to the given file descriptor to a |
|
| 139 |
-// previous state. |
|
| 140 |
-func Restore(fd int, state *State) error {
|
|
| 141 |
- _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) |
|
| 142 |
- return err |
|
| 143 |
-} |
| 9 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,977 @@ |
| 0 |
+package commands |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "bytes" |
|
| 5 |
+ "encoding/json" |
|
| 6 |
+ "errors" |
|
| 7 |
+ "fmt" |
|
| 8 |
+ "github.com/dotcloud/docker" |
|
| 9 |
+ "github.com/dotcloud/docker/fs" |
|
| 10 |
+ "github.com/dotcloud/docker/future" |
|
| 11 |
+ "github.com/dotcloud/docker/rcli" |
|
| 12 |
+ "io" |
|
| 13 |
+ "net/http" |
|
| 14 |
+ "net/url" |
|
| 15 |
+ "os" |
|
| 16 |
+ "path" |
|
| 17 |
+ "strconv" |
|
| 18 |
+ "strings" |
|
| 19 |
+ "sync" |
|
| 20 |
+ "text/tabwriter" |
|
| 21 |
+ "time" |
|
| 22 |
+) |
|
| 23 |
+ |
|
| 24 |
+const VERSION = "0.0.1" |
|
| 25 |
+ |
|
| 26 |
+func (srv *Server) Name() string {
|
|
| 27 |
+ return "docker" |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// FIXME: Stop violating DRY by repeating usage here and in Subcmd declarations |
|
| 31 |
+func (srv *Server) Help() string {
|
|
| 32 |
+ help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n" |
|
| 33 |
+ for _, cmd := range [][]interface{}{
|
|
| 34 |
+ {"run", "Run a command in a container"},
|
|
| 35 |
+ {"ps", "Display a list of containers"},
|
|
| 36 |
+ {"import", "Create a new filesystem image from the contents of a tarball"},
|
|
| 37 |
+ {"attach", "Attach to a running container"},
|
|
| 38 |
+ {"cat", "Write the contents of a container's file to standard output"},
|
|
| 39 |
+ {"commit", "Create a new image from a container's changes"},
|
|
| 40 |
+ {"cp", "Create a copy of IMAGE and call it NAME"},
|
|
| 41 |
+ {"debug", "(debug only) (No documentation available)"},
|
|
| 42 |
+ {"diff", "Inspect changes on a container's filesystem"},
|
|
| 43 |
+ {"images", "List images"},
|
|
| 44 |
+ {"info", "Display system-wide information"},
|
|
| 45 |
+ {"inspect", "Return low-level information on a container"},
|
|
| 46 |
+ {"kill", "Kill a running container"},
|
|
| 47 |
+ {"layers", "(debug only) List filesystem layers"},
|
|
| 48 |
+ {"logs", "Fetch the logs of a container"},
|
|
| 49 |
+ {"ls", "List the contents of a container's directory"},
|
|
| 50 |
+ {"mirror", "(debug only) (No documentation available)"},
|
|
| 51 |
+ {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
|
| 52 |
+ {"ps", "List containers"},
|
|
| 53 |
+ {"reset", "Reset changes to a container's filesystem"},
|
|
| 54 |
+ {"restart", "Restart a running container"},
|
|
| 55 |
+ {"rm", "Remove a container"},
|
|
| 56 |
+ {"rmimage", "Remove an image"},
|
|
| 57 |
+ {"run", "Run a command in a new container"},
|
|
| 58 |
+ {"start", "Start a stopped container"},
|
|
| 59 |
+ {"stop", "Stop a running container"},
|
|
| 60 |
+ {"tar", "Stream the contents of a container as a tar archive"},
|
|
| 61 |
+ {"umount", "(debug only) Mount a container's filesystem"},
|
|
| 62 |
+ {"version", "Show the docker version information"},
|
|
| 63 |
+ {"wait", "Block until a container stops, then print its exit code"},
|
|
| 64 |
+ {"web", "A web UI for docker"},
|
|
| 65 |
+ {"write", "Write the contents of standard input to a container's file"},
|
|
| 66 |
+ } {
|
|
| 67 |
+ help += fmt.Sprintf(" %-10.10s%s\n", cmd...)
|
|
| 68 |
+ } |
|
| 69 |
+ return help |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+// 'docker wait': block until a container stops |
|
| 73 |
+func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 74 |
+ cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.") |
|
| 75 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 76 |
+ return nil |
|
| 77 |
+ } |
|
| 78 |
+ if cmd.NArg() < 1 {
|
|
| 79 |
+ cmd.Usage() |
|
| 80 |
+ return nil |
|
| 81 |
+ } |
|
| 82 |
+ for _, name := range cmd.Args() {
|
|
| 83 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 84 |
+ fmt.Fprintln(stdout, container.Wait()) |
|
| 85 |
+ } else {
|
|
| 86 |
+ return errors.New("No such container: " + name)
|
|
| 87 |
+ } |
|
| 88 |
+ } |
|
| 89 |
+ return nil |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+// 'docker version': show version information |
|
| 93 |
+func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 94 |
+ fmt.Fprintf(stdout, "Version:%s\n", VERSION) |
|
| 95 |
+ return nil |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// 'docker info': display system-wide information. |
|
| 99 |
+func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 100 |
+ images, _ := srv.images.Images() |
|
| 101 |
+ var imgcount int |
|
| 102 |
+ if images == nil {
|
|
| 103 |
+ imgcount = 0 |
|
| 104 |
+ } else {
|
|
| 105 |
+ imgcount = len(images) |
|
| 106 |
+ } |
|
| 107 |
+ cmd := rcli.Subcmd(stdout, "info", "", "Display system-wide information.") |
|
| 108 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 109 |
+ return nil |
|
| 110 |
+ } |
|
| 111 |
+ if cmd.NArg() > 0 {
|
|
| 112 |
+ cmd.Usage() |
|
| 113 |
+ return nil |
|
| 114 |
+ } |
|
| 115 |
+ fmt.Fprintf(stdout, "containers: %d\nversion: %s\nimages: %d\n", |
|
| 116 |
+ len(srv.containers.List()), |
|
| 117 |
+ VERSION, |
|
| 118 |
+ imgcount) |
|
| 119 |
+ return nil |
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 123 |
+ cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container") |
|
| 124 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 125 |
+ return nil |
|
| 126 |
+ } |
|
| 127 |
+ if cmd.NArg() < 1 {
|
|
| 128 |
+ cmd.Usage() |
|
| 129 |
+ return nil |
|
| 130 |
+ } |
|
| 131 |
+ for _, name := range cmd.Args() {
|
|
| 132 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 133 |
+ if err := container.Stop(); err != nil {
|
|
| 134 |
+ return err |
|
| 135 |
+ } |
|
| 136 |
+ fmt.Fprintln(stdout, container.Id) |
|
| 137 |
+ } else {
|
|
| 138 |
+ return errors.New("No such container: " + name)
|
|
| 139 |
+ } |
|
| 140 |
+ } |
|
| 141 |
+ return nil |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 145 |
+ cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container") |
|
| 146 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 147 |
+ return nil |
|
| 148 |
+ } |
|
| 149 |
+ if cmd.NArg() < 1 {
|
|
| 150 |
+ cmd.Usage() |
|
| 151 |
+ return nil |
|
| 152 |
+ } |
|
| 153 |
+ for _, name := range cmd.Args() {
|
|
| 154 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 155 |
+ if err := container.Restart(); err != nil {
|
|
| 156 |
+ return err |
|
| 157 |
+ } |
|
| 158 |
+ fmt.Fprintln(stdout, container.Id) |
|
| 159 |
+ } else {
|
|
| 160 |
+ return errors.New("No such container: " + name)
|
|
| 161 |
+ } |
|
| 162 |
+ } |
|
| 163 |
+ return nil |
|
| 164 |
+} |
|
| 165 |
+ |
|
| 166 |
+func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 167 |
+ cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container") |
|
| 168 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 169 |
+ return nil |
|
| 170 |
+ } |
|
| 171 |
+ if cmd.NArg() < 1 {
|
|
| 172 |
+ cmd.Usage() |
|
| 173 |
+ return nil |
|
| 174 |
+ } |
|
| 175 |
+ for _, name := range cmd.Args() {
|
|
| 176 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 177 |
+ if err := container.Start(); err != nil {
|
|
| 178 |
+ return err |
|
| 179 |
+ } |
|
| 180 |
+ fmt.Fprintln(stdout, container.Id) |
|
| 181 |
+ } else {
|
|
| 182 |
+ return errors.New("No such container: " + name)
|
|
| 183 |
+ } |
|
| 184 |
+ } |
|
| 185 |
+ return nil |
|
| 186 |
+} |
|
| 187 |
+ |
|
| 188 |
+func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 189 |
+ cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "umount a container's filesystem (debug only)") |
|
| 190 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 191 |
+ return nil |
|
| 192 |
+ } |
|
| 193 |
+ if cmd.NArg() < 1 {
|
|
| 194 |
+ cmd.Usage() |
|
| 195 |
+ return nil |
|
| 196 |
+ } |
|
| 197 |
+ for _, name := range cmd.Args() {
|
|
| 198 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 199 |
+ if err := container.Mountpoint.Umount(); err != nil {
|
|
| 200 |
+ return err |
|
| 201 |
+ } |
|
| 202 |
+ fmt.Fprintln(stdout, container.Id) |
|
| 203 |
+ } else {
|
|
| 204 |
+ return errors.New("No such container: " + name)
|
|
| 205 |
+ } |
|
| 206 |
+ } |
|
| 207 |
+ return nil |
|
| 208 |
+} |
|
| 209 |
+ |
|
| 210 |
+func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 211 |
+ cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "mount a container's filesystem (debug only)") |
|
| 212 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 213 |
+ return nil |
|
| 214 |
+ } |
|
| 215 |
+ if cmd.NArg() < 1 {
|
|
| 216 |
+ cmd.Usage() |
|
| 217 |
+ return nil |
|
| 218 |
+ } |
|
| 219 |
+ for _, name := range cmd.Args() {
|
|
| 220 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 221 |
+ if err := container.Mountpoint.EnsureMounted(); err != nil {
|
|
| 222 |
+ return err |
|
| 223 |
+ } |
|
| 224 |
+ fmt.Fprintln(stdout, container.Id) |
|
| 225 |
+ } else {
|
|
| 226 |
+ return errors.New("No such container: " + name)
|
|
| 227 |
+ } |
|
| 228 |
+ } |
|
| 229 |
+ return nil |
|
| 230 |
+} |
|
| 231 |
+ |
|
| 232 |
+func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 233 |
+ cmd := rcli.Subcmd(stdout, "cat", "[OPTIONS] CONTAINER PATH", "write the contents of a container's file to standard output") |
|
| 234 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 235 |
+ return nil |
|
| 236 |
+ } |
|
| 237 |
+ if cmd.NArg() < 2 {
|
|
| 238 |
+ cmd.Usage() |
|
| 239 |
+ return nil |
|
| 240 |
+ } |
|
| 241 |
+ name, path := cmd.Arg(0), cmd.Arg(1) |
|
| 242 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 243 |
+ if f, err := container.Mountpoint.OpenFile(path, os.O_RDONLY, 0); err != nil {
|
|
| 244 |
+ return err |
|
| 245 |
+ } else if _, err := io.Copy(stdout, f); err != nil {
|
|
| 246 |
+ return err |
|
| 247 |
+ } |
|
| 248 |
+ return nil |
|
| 249 |
+ } |
|
| 250 |
+ return errors.New("No such container: " + name)
|
|
| 251 |
+} |
|
| 252 |
+ |
|
| 253 |
+func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 254 |
+ cmd := rcli.Subcmd(stdout, "write", "[OPTIONS] CONTAINER PATH", "write the contents of standard input to a container's file") |
|
| 255 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 256 |
+ return nil |
|
| 257 |
+ } |
|
| 258 |
+ if cmd.NArg() < 2 {
|
|
| 259 |
+ cmd.Usage() |
|
| 260 |
+ return nil |
|
| 261 |
+ } |
|
| 262 |
+ name, path := cmd.Arg(0), cmd.Arg(1) |
|
| 263 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 264 |
+ if f, err := container.Mountpoint.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600); err != nil {
|
|
| 265 |
+ return err |
|
| 266 |
+ } else if _, err := io.Copy(f, stdin); err != nil {
|
|
| 267 |
+ return err |
|
| 268 |
+ } |
|
| 269 |
+ return nil |
|
| 270 |
+ } |
|
| 271 |
+ return errors.New("No such container: " + name)
|
|
| 272 |
+} |
|
| 273 |
+ |
|
| 274 |
+func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 275 |
+ cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory") |
|
| 276 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 277 |
+ return nil |
|
| 278 |
+ } |
|
| 279 |
+ if cmd.NArg() < 2 {
|
|
| 280 |
+ cmd.Usage() |
|
| 281 |
+ return nil |
|
| 282 |
+ } |
|
| 283 |
+ name, path := cmd.Arg(0), cmd.Arg(1) |
|
| 284 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 285 |
+ if files, err := container.Mountpoint.ReadDir(path); err != nil {
|
|
| 286 |
+ return err |
|
| 287 |
+ } else {
|
|
| 288 |
+ for _, f := range files {
|
|
| 289 |
+ fmt.Fprintln(stdout, f.Name()) |
|
| 290 |
+ } |
|
| 291 |
+ } |
|
| 292 |
+ return nil |
|
| 293 |
+ } |
|
| 294 |
+ return errors.New("No such container: " + name)
|
|
| 295 |
+} |
|
| 296 |
+ |
|
| 297 |
+func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 298 |
+ cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container") |
|
| 299 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 300 |
+ return nil |
|
| 301 |
+ } |
|
| 302 |
+ if cmd.NArg() < 1 {
|
|
| 303 |
+ cmd.Usage() |
|
| 304 |
+ return nil |
|
| 305 |
+ } |
|
| 306 |
+ name := cmd.Arg(0) |
|
| 307 |
+ var obj interface{}
|
|
| 308 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 309 |
+ obj = container |
|
| 310 |
+ //} else if image, err := srv.images.List(name); image != nil {
|
|
| 311 |
+ // obj = image |
|
| 312 |
+ } else {
|
|
| 313 |
+ return errors.New("No such container or image: " + name)
|
|
| 314 |
+ } |
|
| 315 |
+ data, err := json.Marshal(obj) |
|
| 316 |
+ if err != nil {
|
|
| 317 |
+ return err |
|
| 318 |
+ } |
|
| 319 |
+ indented := new(bytes.Buffer) |
|
| 320 |
+ if err = json.Indent(indented, data, "", " "); err != nil {
|
|
| 321 |
+ return err |
|
| 322 |
+ } |
|
| 323 |
+ if _, err := io.Copy(stdout, indented); err != nil {
|
|
| 324 |
+ return err |
|
| 325 |
+ } |
|
| 326 |
+ return nil |
|
| 327 |
+} |
|
| 328 |
+ |
|
| 329 |
+func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 330 |
+ cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT") |
|
| 331 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 332 |
+ return nil |
|
| 333 |
+ } |
|
| 334 |
+ if cmd.NArg() != 2 {
|
|
| 335 |
+ cmd.Usage() |
|
| 336 |
+ return nil |
|
| 337 |
+ } |
|
| 338 |
+ name := cmd.Arg(0) |
|
| 339 |
+ privatePort := cmd.Arg(1) |
|
| 340 |
+ if container := srv.containers.Get(name); container == nil {
|
|
| 341 |
+ return errors.New("No such container: " + name)
|
|
| 342 |
+ } else {
|
|
| 343 |
+ if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
|
|
| 344 |
+ return fmt.Errorf("No private port '%s' allocated on %s", privatePort, name)
|
|
| 345 |
+ } else {
|
|
| 346 |
+ fmt.Fprintln(stdout, frontend) |
|
| 347 |
+ } |
|
| 348 |
+ } |
|
| 349 |
+ return nil |
|
| 350 |
+} |
|
| 351 |
+ |
|
| 352 |
+// 'docker rmi NAME' removes all images with the name NAME |
|
| 353 |
+// func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 354 |
+// cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image") |
|
| 355 |
+// fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name")
|
|
| 356 |
+// if err := cmd.Parse(args); err != nil {
|
|
| 357 |
+// cmd.Usage() |
|
| 358 |
+// return nil |
|
| 359 |
+// } |
|
| 360 |
+// if cmd.NArg() < 1 {
|
|
| 361 |
+// cmd.Usage() |
|
| 362 |
+// return nil |
|
| 363 |
+// } |
|
| 364 |
+// for _, name := range cmd.Args() {
|
|
| 365 |
+// var err error |
|
| 366 |
+// if *fl_regexp {
|
|
| 367 |
+// err = srv.images.DeleteMatch(name) |
|
| 368 |
+// } else {
|
|
| 369 |
+// image := srv.images.Find(name) |
|
| 370 |
+// if image == nil {
|
|
| 371 |
+// return errors.New("No such image: " + name)
|
|
| 372 |
+// } |
|
| 373 |
+// err = srv.images.Delete(name) |
|
| 374 |
+// } |
|
| 375 |
+// if err != nil {
|
|
| 376 |
+// return err |
|
| 377 |
+// } |
|
| 378 |
+// } |
|
| 379 |
+// return nil |
|
| 380 |
+// } |
|
| 381 |
+ |
|
| 382 |
+func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 383 |
+ cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container") |
|
| 384 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 385 |
+ return nil |
|
| 386 |
+ } |
|
| 387 |
+ for _, name := range cmd.Args() {
|
|
| 388 |
+ container := srv.containers.Get(name) |
|
| 389 |
+ if container == nil {
|
|
| 390 |
+ return errors.New("No such container: " + name)
|
|
| 391 |
+ } |
|
| 392 |
+ if err := srv.containers.Destroy(container); err != nil {
|
|
| 393 |
+ fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error()) |
|
| 394 |
+ } |
|
| 395 |
+ } |
|
| 396 |
+ return nil |
|
| 397 |
+} |
|
| 398 |
+ |
|
| 399 |
+// 'docker kill NAME' kills a running container |
|
| 400 |
+func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 401 |
+ cmd := rcli.Subcmd(stdout, "kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container") |
|
| 402 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 403 |
+ return nil |
|
| 404 |
+ } |
|
| 405 |
+ for _, name := range cmd.Args() {
|
|
| 406 |
+ container := srv.containers.Get(name) |
|
| 407 |
+ if container == nil {
|
|
| 408 |
+ return errors.New("No such container: " + name)
|
|
| 409 |
+ } |
|
| 410 |
+ if err := container.Kill(); err != nil {
|
|
| 411 |
+ fmt.Fprintln(stdout, "Error killing container "+name+": "+err.Error()) |
|
| 412 |
+ } |
|
| 413 |
+ } |
|
| 414 |
+ return nil |
|
| 415 |
+} |
|
| 416 |
+ |
|
| 417 |
+func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 418 |
+ cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] NAME", "Create a new filesystem image from the contents of a tarball") |
|
| 419 |
+ fl_stdin := cmd.Bool("stdin", false, "Read tarball from stdin")
|
|
| 420 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 421 |
+ return nil |
|
| 422 |
+ } |
|
| 423 |
+ var archive io.Reader |
|
| 424 |
+ name := cmd.Arg(0) |
|
| 425 |
+ if name == "" {
|
|
| 426 |
+ return errors.New("Not enough arguments")
|
|
| 427 |
+ } |
|
| 428 |
+ if *fl_stdin {
|
|
| 429 |
+ archive = stdin |
|
| 430 |
+ } else {
|
|
| 431 |
+ u, err := url.Parse(name) |
|
| 432 |
+ if err != nil {
|
|
| 433 |
+ return err |
|
| 434 |
+ } |
|
| 435 |
+ if u.Scheme == "" {
|
|
| 436 |
+ u.Scheme = "http" |
|
| 437 |
+ } |
|
| 438 |
+ // FIXME: hardcode a mirror URL that does not depend on a single provider. |
|
| 439 |
+ if u.Host == "" {
|
|
| 440 |
+ u.Host = "s3.amazonaws.com" |
|
| 441 |
+ u.Path = path.Join("/docker.io/images", u.Path)
|
|
| 442 |
+ } |
|
| 443 |
+ fmt.Fprintf(stdout, "Downloading from %s\n", u.String()) |
|
| 444 |
+ // Download with curl (pretty progress bar) |
|
| 445 |
+ // If curl is not available, fallback to http.Get() |
|
| 446 |
+ archive, err = future.Curl(u.String(), stdout) |
|
| 447 |
+ if err != nil {
|
|
| 448 |
+ if resp, err := http.Get(u.String()); err != nil {
|
|
| 449 |
+ return err |
|
| 450 |
+ } else {
|
|
| 451 |
+ archive = resp.Body |
|
| 452 |
+ } |
|
| 453 |
+ } |
|
| 454 |
+ } |
|
| 455 |
+ fmt.Fprintf(stdout, "Unpacking to %s\n", name) |
|
| 456 |
+ img, err := srv.images.Create(archive, nil, name, "") |
|
| 457 |
+ if err != nil {
|
|
| 458 |
+ return err |
|
| 459 |
+ } |
|
| 460 |
+ fmt.Fprintln(stdout, img.Id) |
|
| 461 |
+ return nil |
|
| 462 |
+} |
|
| 463 |
+ |
|
| 464 |
+func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 465 |
+ cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images") |
|
| 466 |
+ limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
|
|
| 467 |
+ quiet := cmd.Bool("q", false, "only show numeric IDs")
|
|
| 468 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 469 |
+ return nil |
|
| 470 |
+ } |
|
| 471 |
+ if cmd.NArg() > 1 {
|
|
| 472 |
+ cmd.Usage() |
|
| 473 |
+ return nil |
|
| 474 |
+ } |
|
| 475 |
+ var nameFilter string |
|
| 476 |
+ if cmd.NArg() == 1 {
|
|
| 477 |
+ nameFilter = cmd.Arg(0) |
|
| 478 |
+ } |
|
| 479 |
+ w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0) |
|
| 480 |
+ if !*quiet {
|
|
| 481 |
+ fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n") |
|
| 482 |
+ } |
|
| 483 |
+ paths, err := srv.images.Paths() |
|
| 484 |
+ if err != nil {
|
|
| 485 |
+ return err |
|
| 486 |
+ } |
|
| 487 |
+ for _, name := range paths {
|
|
| 488 |
+ if nameFilter != "" && nameFilter != name {
|
|
| 489 |
+ continue |
|
| 490 |
+ } |
|
| 491 |
+ ids, err := srv.images.List(name) |
|
| 492 |
+ if err != nil {
|
|
| 493 |
+ return err |
|
| 494 |
+ } |
|
| 495 |
+ for idx, img := range ids {
|
|
| 496 |
+ if *limit > 0 && idx >= *limit {
|
|
| 497 |
+ break |
|
| 498 |
+ } |
|
| 499 |
+ if !*quiet {
|
|
| 500 |
+ for idx, field := range []string{
|
|
| 501 |
+ /* NAME */ name, |
|
| 502 |
+ /* ID */ img.Id, |
|
| 503 |
+ /* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago", |
|
| 504 |
+ /* PARENT */ img.Parent, |
|
| 505 |
+ } {
|
|
| 506 |
+ if idx == 0 {
|
|
| 507 |
+ w.Write([]byte(field)) |
|
| 508 |
+ } else {
|
|
| 509 |
+ w.Write([]byte("\t" + field))
|
|
| 510 |
+ } |
|
| 511 |
+ } |
|
| 512 |
+ w.Write([]byte{'\n'})
|
|
| 513 |
+ } else {
|
|
| 514 |
+ stdout.Write([]byte(img.Id + "\n")) |
|
| 515 |
+ } |
|
| 516 |
+ } |
|
| 517 |
+ } |
|
| 518 |
+ if !*quiet {
|
|
| 519 |
+ w.Flush() |
|
| 520 |
+ } |
|
| 521 |
+ return nil |
|
| 522 |
+ |
|
| 523 |
+} |
|
| 524 |
+ |
|
| 525 |
+func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 526 |
+ cmd := rcli.Subcmd(stdout, |
|
| 527 |
+ "ps", "[OPTIONS]", "List containers") |
|
| 528 |
+ quiet := cmd.Bool("q", false, "Only display numeric IDs")
|
|
| 529 |
+ fl_all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
|
|
| 530 |
+ fl_full := cmd.Bool("notrunc", false, "Don't truncate output")
|
|
| 531 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 532 |
+ return nil |
|
| 533 |
+ } |
|
| 534 |
+ w := tabwriter.NewWriter(stdout, 12, 1, 3, ' ', 0) |
|
| 535 |
+ if !*quiet {
|
|
| 536 |
+ fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n") |
|
| 537 |
+ } |
|
| 538 |
+ for _, container := range srv.containers.List() {
|
|
| 539 |
+ comment := container.GetUserData("comment")
|
|
| 540 |
+ if !container.State.Running && !*fl_all {
|
|
| 541 |
+ continue |
|
| 542 |
+ } |
|
| 543 |
+ if !*quiet {
|
|
| 544 |
+ command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
|
|
| 545 |
+ if !*fl_full {
|
|
| 546 |
+ command = docker.Trunc(command, 20) |
|
| 547 |
+ } |
|
| 548 |
+ for idx, field := range []string{
|
|
| 549 |
+ /* ID */ container.Id, |
|
| 550 |
+ /* IMAGE */ container.GetUserData("image"),
|
|
| 551 |
+ /* COMMAND */ command, |
|
| 552 |
+ /* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago", |
|
| 553 |
+ /* STATUS */ container.State.String(), |
|
| 554 |
+ /* COMMENT */ comment, |
|
| 555 |
+ } {
|
|
| 556 |
+ if idx == 0 {
|
|
| 557 |
+ w.Write([]byte(field)) |
|
| 558 |
+ } else {
|
|
| 559 |
+ w.Write([]byte("\t" + field))
|
|
| 560 |
+ } |
|
| 561 |
+ } |
|
| 562 |
+ w.Write([]byte{'\n'})
|
|
| 563 |
+ } else {
|
|
| 564 |
+ stdout.Write([]byte(container.Id + "\n")) |
|
| 565 |
+ } |
|
| 566 |
+ } |
|
| 567 |
+ if !*quiet {
|
|
| 568 |
+ w.Flush() |
|
| 569 |
+ } |
|
| 570 |
+ return nil |
|
| 571 |
+} |
|
| 572 |
+ |
|
| 573 |
+func (srv *Server) CmdLayers(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 574 |
+ cmd := rcli.Subcmd(stdout, |
|
| 575 |
+ "layers", "[OPTIONS]", |
|
| 576 |
+ "List filesystem layers (debug only)") |
|
| 577 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 578 |
+ return nil |
|
| 579 |
+ } |
|
| 580 |
+ for _, layer := range srv.images.Layers() {
|
|
| 581 |
+ fmt.Fprintln(stdout, layer) |
|
| 582 |
+ } |
|
| 583 |
+ return nil |
|
| 584 |
+} |
|
| 585 |
+ |
|
| 586 |
+func (srv *Server) CmdCp(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 587 |
+ cmd := rcli.Subcmd(stdout, |
|
| 588 |
+ "cp", "[OPTIONS] IMAGE NAME", |
|
| 589 |
+ "Create a copy of IMAGE and call it NAME") |
|
| 590 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 591 |
+ return nil |
|
| 592 |
+ } |
|
| 593 |
+ if image, err := srv.images.Get(cmd.Arg(0)); err != nil {
|
|
| 594 |
+ return err |
|
| 595 |
+ } else if image == nil {
|
|
| 596 |
+ return errors.New("Image " + cmd.Arg(0) + " does not exist")
|
|
| 597 |
+ } else {
|
|
| 598 |
+ if img, err := image.Copy(cmd.Arg(1)); err != nil {
|
|
| 599 |
+ return err |
|
| 600 |
+ } else {
|
|
| 601 |
+ fmt.Fprintln(stdout, img.Id) |
|
| 602 |
+ } |
|
| 603 |
+ } |
|
| 604 |
+ return nil |
|
| 605 |
+} |
|
| 606 |
+ |
|
| 607 |
+func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 608 |
+ cmd := rcli.Subcmd(stdout, |
|
| 609 |
+ "commit", "[OPTIONS] CONTAINER [DEST]", |
|
| 610 |
+ "Create a new image from a container's changes") |
|
| 611 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 612 |
+ return nil |
|
| 613 |
+ } |
|
| 614 |
+ containerName, imgName := cmd.Arg(0), cmd.Arg(1) |
|
| 615 |
+ if containerName == "" || imgName == "" {
|
|
| 616 |
+ cmd.Usage() |
|
| 617 |
+ return nil |
|
| 618 |
+ } |
|
| 619 |
+ if container := srv.containers.Get(containerName); container != nil {
|
|
| 620 |
+ // FIXME: freeze the container before copying it to avoid data corruption? |
|
| 621 |
+ rwTar, err := fs.Tar(container.Mountpoint.Rw, fs.Uncompressed) |
|
| 622 |
+ if err != nil {
|
|
| 623 |
+ return err |
|
| 624 |
+ } |
|
| 625 |
+ // Create a new image from the container's base layers + a new layer from container changes |
|
| 626 |
+ parentImg, err := srv.images.Get(container.Image) |
|
| 627 |
+ if err != nil {
|
|
| 628 |
+ return err |
|
| 629 |
+ } |
|
| 630 |
+ |
|
| 631 |
+ img, err := srv.images.Create(rwTar, parentImg, imgName, "") |
|
| 632 |
+ if err != nil {
|
|
| 633 |
+ return err |
|
| 634 |
+ } |
|
| 635 |
+ |
|
| 636 |
+ fmt.Fprintln(stdout, img.Id) |
|
| 637 |
+ return nil |
|
| 638 |
+ } |
|
| 639 |
+ return errors.New("No such container: " + containerName)
|
|
| 640 |
+} |
|
| 641 |
+ |
|
| 642 |
+func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 643 |
+ cmd := rcli.Subcmd(stdout, |
|
| 644 |
+ "tar", "CONTAINER", |
|
| 645 |
+ "Stream the contents of a container as a tar archive") |
|
| 646 |
+ fl_sparse := cmd.Bool("s", false, "Generate a sparse tar stream (top layer + reference to bottom layers)")
|
|
| 647 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 648 |
+ return nil |
|
| 649 |
+ } |
|
| 650 |
+ if *fl_sparse {
|
|
| 651 |
+ return errors.New("Sparse mode not yet implemented") // FIXME
|
|
| 652 |
+ } |
|
| 653 |
+ name := cmd.Arg(0) |
|
| 654 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 655 |
+ if err := container.Mountpoint.EnsureMounted(); err != nil {
|
|
| 656 |
+ return err |
|
| 657 |
+ } |
|
| 658 |
+ data, err := fs.Tar(container.Mountpoint.Root, fs.Uncompressed) |
|
| 659 |
+ if err != nil {
|
|
| 660 |
+ return err |
|
| 661 |
+ } |
|
| 662 |
+ // Stream the entire contents of the container (basically a volatile snapshot) |
|
| 663 |
+ if _, err := io.Copy(stdout, data); err != nil {
|
|
| 664 |
+ return err |
|
| 665 |
+ } |
|
| 666 |
+ return nil |
|
| 667 |
+ } |
|
| 668 |
+ return errors.New("No such container: " + name)
|
|
| 669 |
+} |
|
| 670 |
+ |
|
| 671 |
+func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 672 |
+ cmd := rcli.Subcmd(stdout, |
|
| 673 |
+ "diff", "CONTAINER [OPTIONS]", |
|
| 674 |
+ "Inspect changes on a container's filesystem") |
|
| 675 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 676 |
+ return nil |
|
| 677 |
+ } |
|
| 678 |
+ if cmd.NArg() < 1 {
|
|
| 679 |
+ return errors.New("Not enough arguments")
|
|
| 680 |
+ } |
|
| 681 |
+ if container := srv.containers.Get(cmd.Arg(0)); container == nil {
|
|
| 682 |
+ return errors.New("No such container")
|
|
| 683 |
+ } else {
|
|
| 684 |
+ changes, err := srv.images.Changes(container.Mountpoint) |
|
| 685 |
+ if err != nil {
|
|
| 686 |
+ return err |
|
| 687 |
+ } |
|
| 688 |
+ for _, change := range changes {
|
|
| 689 |
+ fmt.Fprintln(stdout, change.String()) |
|
| 690 |
+ } |
|
| 691 |
+ } |
|
| 692 |
+ return nil |
|
| 693 |
+} |
|
| 694 |
+ |
|
| 695 |
+func (srv *Server) CmdReset(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 696 |
+ cmd := rcli.Subcmd(stdout, |
|
| 697 |
+ "reset", "CONTAINER [OPTIONS]", |
|
| 698 |
+ "Reset changes to a container's filesystem") |
|
| 699 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 700 |
+ return nil |
|
| 701 |
+ } |
|
| 702 |
+ if cmd.NArg() < 1 {
|
|
| 703 |
+ return errors.New("Not enough arguments")
|
|
| 704 |
+ } |
|
| 705 |
+ for _, name := range cmd.Args() {
|
|
| 706 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 707 |
+ if err := container.Mountpoint.Reset(); err != nil {
|
|
| 708 |
+ return errors.New("Reset " + container.Id + ": " + err.Error())
|
|
| 709 |
+ } |
|
| 710 |
+ } |
|
| 711 |
+ } |
|
| 712 |
+ return nil |
|
| 713 |
+} |
|
| 714 |
+ |
|
| 715 |
+func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 716 |
+ cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container") |
|
| 717 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 718 |
+ return nil |
|
| 719 |
+ } |
|
| 720 |
+ if cmd.NArg() != 1 {
|
|
| 721 |
+ cmd.Usage() |
|
| 722 |
+ return nil |
|
| 723 |
+ } |
|
| 724 |
+ name := cmd.Arg(0) |
|
| 725 |
+ if container := srv.containers.Get(name); container != nil {
|
|
| 726 |
+ if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
|
|
| 727 |
+ return err |
|
| 728 |
+ } |
|
| 729 |
+ if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
|
|
| 730 |
+ return err |
|
| 731 |
+ } |
|
| 732 |
+ return nil |
|
| 733 |
+ } |
|
| 734 |
+ return errors.New("No such container: " + cmd.Arg(0))
|
|
| 735 |
+} |
|
| 736 |
+ |
|
| 737 |
+func (srv *Server) CreateContainer(img *fs.Image, ports []int, user string, tty bool, openStdin bool, memory int64, comment string, cmd string, args ...string) (*docker.Container, error) {
|
|
| 738 |
+ id := future.RandomId()[:8] |
|
| 739 |
+ container, err := srv.containers.Create(id, cmd, args, img, |
|
| 740 |
+ &docker.Config{
|
|
| 741 |
+ Hostname: id, |
|
| 742 |
+ Ports: ports, |
|
| 743 |
+ User: user, |
|
| 744 |
+ Tty: tty, |
|
| 745 |
+ OpenStdin: openStdin, |
|
| 746 |
+ Memory: memory, |
|
| 747 |
+ }) |
|
| 748 |
+ if err != nil {
|
|
| 749 |
+ return nil, err |
|
| 750 |
+ } |
|
| 751 |
+ if err := container.SetUserData("image", img.Id); err != nil {
|
|
| 752 |
+ srv.containers.Destroy(container) |
|
| 753 |
+ return nil, errors.New("Error setting container userdata: " + err.Error())
|
|
| 754 |
+ } |
|
| 755 |
+ if err := container.SetUserData("comment", comment); err != nil {
|
|
| 756 |
+ srv.containers.Destroy(container) |
|
| 757 |
+ return nil, errors.New("Error setting container userdata: " + err.Error())
|
|
| 758 |
+ } |
|
| 759 |
+ return container, nil |
|
| 760 |
+} |
|
| 761 |
+ |
|
| 762 |
+func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 763 |
+ cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container") |
|
| 764 |
+ fl_i := cmd.Bool("i", false, "Attach to stdin")
|
|
| 765 |
+ fl_o := cmd.Bool("o", true, "Attach to stdout")
|
|
| 766 |
+ fl_e := cmd.Bool("e", true, "Attach to stderr")
|
|
| 767 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 768 |
+ return nil |
|
| 769 |
+ } |
|
| 770 |
+ if cmd.NArg() != 1 {
|
|
| 771 |
+ cmd.Usage() |
|
| 772 |
+ return nil |
|
| 773 |
+ } |
|
| 774 |
+ name := cmd.Arg(0) |
|
| 775 |
+ container := srv.containers.Get(name) |
|
| 776 |
+ if container == nil {
|
|
| 777 |
+ return errors.New("No such container: " + name)
|
|
| 778 |
+ } |
|
| 779 |
+ var wg sync.WaitGroup |
|
| 780 |
+ if *fl_i {
|
|
| 781 |
+ c_stdin, err := container.StdinPipe() |
|
| 782 |
+ if err != nil {
|
|
| 783 |
+ return err |
|
| 784 |
+ } |
|
| 785 |
+ wg.Add(1) |
|
| 786 |
+ go func() { io.Copy(c_stdin, stdin); wg.Add(-1) }()
|
|
| 787 |
+ } |
|
| 788 |
+ if *fl_o {
|
|
| 789 |
+ c_stdout, err := container.StdoutPipe() |
|
| 790 |
+ if err != nil {
|
|
| 791 |
+ return err |
|
| 792 |
+ } |
|
| 793 |
+ wg.Add(1) |
|
| 794 |
+ go func() { io.Copy(stdout, c_stdout); wg.Add(-1) }()
|
|
| 795 |
+ } |
|
| 796 |
+ if *fl_e {
|
|
| 797 |
+ c_stderr, err := container.StderrPipe() |
|
| 798 |
+ if err != nil {
|
|
| 799 |
+ return err |
|
| 800 |
+ } |
|
| 801 |
+ wg.Add(1) |
|
| 802 |
+ go func() { io.Copy(stdout, c_stderr); wg.Add(-1) }()
|
|
| 803 |
+ } |
|
| 804 |
+ wg.Wait() |
|
| 805 |
+ return nil |
|
| 806 |
+} |
|
| 807 |
+ |
|
| 808 |
+// Ports type - Used to parse multiple -p flags |
|
| 809 |
+type ports []int |
|
| 810 |
+ |
|
| 811 |
+func (p *ports) String() string {
|
|
| 812 |
+ return fmt.Sprint(*p) |
|
| 813 |
+} |
|
| 814 |
+ |
|
| 815 |
+func (p *ports) Set(value string) error {
|
|
| 816 |
+ port, err := strconv.Atoi(value) |
|
| 817 |
+ if err != nil {
|
|
| 818 |
+ return fmt.Errorf("Invalid port: %v", value)
|
|
| 819 |
+ } |
|
| 820 |
+ *p = append(*p, port) |
|
| 821 |
+ return nil |
|
| 822 |
+} |
|
| 823 |
+ |
|
| 824 |
+func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 825 |
+ cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container") |
|
| 826 |
+ fl_user := cmd.String("u", "", "Username or UID")
|
|
| 827 |
+ fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
|
|
| 828 |
+ fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
|
|
| 829 |
+ fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
|
|
| 830 |
+ fl_comment := cmd.String("c", "", "Comment")
|
|
| 831 |
+ fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
|
|
| 832 |
+ var fl_ports ports |
|
| 833 |
+ cmd.Var(&fl_ports, "p", "Map a network port to the container") |
|
| 834 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 835 |
+ return nil |
|
| 836 |
+ } |
|
| 837 |
+ name := cmd.Arg(0) |
|
| 838 |
+ var cmdline []string |
|
| 839 |
+ if len(cmd.Args()) >= 2 {
|
|
| 840 |
+ cmdline = cmd.Args()[1:] |
|
| 841 |
+ } |
|
| 842 |
+ // Choose a default image if needed |
|
| 843 |
+ if name == "" {
|
|
| 844 |
+ name = "base" |
|
| 845 |
+ } |
|
| 846 |
+ // Choose a default command if needed |
|
| 847 |
+ if len(cmdline) == 0 {
|
|
| 848 |
+ *fl_stdin = true |
|
| 849 |
+ *fl_tty = true |
|
| 850 |
+ *fl_attach = true |
|
| 851 |
+ cmdline = []string{"/bin/bash", "-i"}
|
|
| 852 |
+ } |
|
| 853 |
+ // Find the image |
|
| 854 |
+ img, err := srv.images.Find(name) |
|
| 855 |
+ if err != nil {
|
|
| 856 |
+ return err |
|
| 857 |
+ } else if img == nil {
|
|
| 858 |
+ return errors.New("No such image: " + name)
|
|
| 859 |
+ } |
|
| 860 |
+ // Create new container |
|
| 861 |
+ container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty, |
|
| 862 |
+ *fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...) |
|
| 863 |
+ if err != nil {
|
|
| 864 |
+ return errors.New("Error creating container: " + err.Error())
|
|
| 865 |
+ } |
|
| 866 |
+ if *fl_stdin {
|
|
| 867 |
+ cmd_stdin, err := container.StdinPipe() |
|
| 868 |
+ if err != nil {
|
|
| 869 |
+ return err |
|
| 870 |
+ } |
|
| 871 |
+ if *fl_attach {
|
|
| 872 |
+ future.Go(func() error {
|
|
| 873 |
+ _, err := io.Copy(cmd_stdin, stdin) |
|
| 874 |
+ cmd_stdin.Close() |
|
| 875 |
+ return err |
|
| 876 |
+ }) |
|
| 877 |
+ } |
|
| 878 |
+ } |
|
| 879 |
+ // Run the container |
|
| 880 |
+ if *fl_attach {
|
|
| 881 |
+ cmd_stderr, err := container.StderrPipe() |
|
| 882 |
+ if err != nil {
|
|
| 883 |
+ return err |
|
| 884 |
+ } |
|
| 885 |
+ cmd_stdout, err := container.StdoutPipe() |
|
| 886 |
+ if err != nil {
|
|
| 887 |
+ return err |
|
| 888 |
+ } |
|
| 889 |
+ if err := container.Start(); err != nil {
|
|
| 890 |
+ return err |
|
| 891 |
+ } |
|
| 892 |
+ sending_stdout := future.Go(func() error {
|
|
| 893 |
+ _, err := io.Copy(stdout, cmd_stdout) |
|
| 894 |
+ return err |
|
| 895 |
+ }) |
|
| 896 |
+ sending_stderr := future.Go(func() error {
|
|
| 897 |
+ _, err := io.Copy(stdout, cmd_stderr) |
|
| 898 |
+ return err |
|
| 899 |
+ }) |
|
| 900 |
+ err_sending_stdout := <-sending_stdout |
|
| 901 |
+ err_sending_stderr := <-sending_stderr |
|
| 902 |
+ if err_sending_stdout != nil {
|
|
| 903 |
+ return err_sending_stdout |
|
| 904 |
+ } |
|
| 905 |
+ if err_sending_stderr != nil {
|
|
| 906 |
+ return err_sending_stderr |
|
| 907 |
+ } |
|
| 908 |
+ container.Wait() |
|
| 909 |
+ } else {
|
|
| 910 |
+ if err := container.Start(); err != nil {
|
|
| 911 |
+ return err |
|
| 912 |
+ } |
|
| 913 |
+ fmt.Fprintln(stdout, container.Id) |
|
| 914 |
+ } |
|
| 915 |
+ return nil |
|
| 916 |
+} |
|
| 917 |
+ |
|
| 918 |
+func New() (*Server, error) {
|
|
| 919 |
+ future.Seed() |
|
| 920 |
+ // if err != nil {
|
|
| 921 |
+ // return nil, err |
|
| 922 |
+ // } |
|
| 923 |
+ containers, err := docker.New() |
|
| 924 |
+ if err != nil {
|
|
| 925 |
+ return nil, err |
|
| 926 |
+ } |
|
| 927 |
+ srv := &Server{
|
|
| 928 |
+ images: containers.Store, |
|
| 929 |
+ containers: containers, |
|
| 930 |
+ } |
|
| 931 |
+ return srv, nil |
|
| 932 |
+} |
|
| 933 |
+ |
|
| 934 |
+func (srv *Server) CmdMirror(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 935 |
+ _, err := io.Copy(stdout, stdin) |
|
| 936 |
+ return err |
|
| 937 |
+} |
|
| 938 |
+ |
|
| 939 |
+func (srv *Server) CmdDebug(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 940 |
+ for {
|
|
| 941 |
+ if line, err := bufio.NewReader(stdin).ReadString('\n'); err == nil {
|
|
| 942 |
+ fmt.Printf("--- %s", line)
|
|
| 943 |
+ } else if err == io.EOF {
|
|
| 944 |
+ if len(line) > 0 {
|
|
| 945 |
+ fmt.Printf("--- %s\n", line)
|
|
| 946 |
+ } |
|
| 947 |
+ break |
|
| 948 |
+ } else {
|
|
| 949 |
+ return err |
|
| 950 |
+ } |
|
| 951 |
+ } |
|
| 952 |
+ return nil |
|
| 953 |
+} |
|
| 954 |
+ |
|
| 955 |
+func (srv *Server) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 956 |
+ cmd := rcli.Subcmd(stdout, "web", "[OPTIONS]", "A web UI for docker") |
|
| 957 |
+ showurl := cmd.Bool("u", false, "Return the URL of the web UI")
|
|
| 958 |
+ if err := cmd.Parse(args); err != nil {
|
|
| 959 |
+ return nil |
|
| 960 |
+ } |
|
| 961 |
+ if *showurl {
|
|
| 962 |
+ fmt.Fprintln(stdout, "http://localhost:4242/web") |
|
| 963 |
+ } else {
|
|
| 964 |
+ if file, err := os.Open("dockerweb.html"); err != nil {
|
|
| 965 |
+ return err |
|
| 966 |
+ } else if _, err := io.Copy(stdout, file); err != nil {
|
|
| 967 |
+ return err |
|
| 968 |
+ } |
|
| 969 |
+ } |
|
| 970 |
+ return nil |
|
| 971 |
+} |
|
| 972 |
+ |
|
| 973 |
+type Server struct {
|
|
| 974 |
+ containers *docker.Docker |
|
| 975 |
+ images *fs.Store |
|
| 976 |
+} |
| ... | ... |
@@ -1,13 +1,93 @@ |
| 1 | 1 |
package main |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "github.com/dotcloud/docker/client" |
|
| 4 |
+ "flag" |
|
| 5 |
+ "github.com/dotcloud/docker" |
|
| 6 |
+ "github.com/dotcloud/docker/commands" |
|
| 7 |
+ "github.com/dotcloud/docker/future" |
|
| 8 |
+ "github.com/dotcloud/docker/rcli" |
|
| 9 |
+ "github.com/dotcloud/docker/term" |
|
| 10 |
+ "io" |
|
| 5 | 11 |
"log" |
| 6 | 12 |
"os" |
| 7 | 13 |
) |
| 8 | 14 |
|
| 9 | 15 |
func main() {
|
| 10 |
- if err := client.SimpleMode(os.Args[1:]); err != nil {
|
|
| 11 |
- log.Fatal(err) |
|
| 16 |
+ if docker.SelfPath() == "/sbin/init" {
|
|
| 17 |
+ // Running in init mode |
|
| 18 |
+ docker.SysInit() |
|
| 19 |
+ return |
|
| 12 | 20 |
} |
| 21 |
+ fl_daemon := flag.Bool("d", false, "Daemon mode")
|
|
| 22 |
+ flag.Parse() |
|
| 23 |
+ if *fl_daemon {
|
|
| 24 |
+ if flag.NArg() != 0 {
|
|
| 25 |
+ flag.Usage() |
|
| 26 |
+ return |
|
| 27 |
+ } |
|
| 28 |
+ if err := daemon(); err != nil {
|
|
| 29 |
+ log.Fatal(err) |
|
| 30 |
+ } |
|
| 31 |
+ } else {
|
|
| 32 |
+ if err := runCommand(flag.Args()); err != nil {
|
|
| 33 |
+ log.Fatal(err) |
|
| 34 |
+ } |
|
| 35 |
+ } |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func daemon() error {
|
|
| 39 |
+ service, err := commands.New() |
|
| 40 |
+ if err != nil {
|
|
| 41 |
+ return err |
|
| 42 |
+ } |
|
| 43 |
+ return rcli.ListenAndServe("tcp", "127.0.0.1:4242", service)
|
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func runCommand(args []string) error {
|
|
| 47 |
+ var oldState *term.State |
|
| 48 |
+ var err error |
|
| 49 |
+ if term.IsTerminal(0) && os.Getenv("NORAW") == "" {
|
|
| 50 |
+ oldState, err = term.MakeRaw(0) |
|
| 51 |
+ if err != nil {
|
|
| 52 |
+ return err |
|
| 53 |
+ } |
|
| 54 |
+ defer term.Restore(0, oldState) |
|
| 55 |
+ } |
|
| 56 |
+ // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose |
|
| 57 |
+ // CloseWrite(), which we need to cleanly signal that stdin is closed without |
|
| 58 |
+ // closing the connection. |
|
| 59 |
+ // See http://code.google.com/p/go/issues/detail?id=3345 |
|
| 60 |
+ if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil {
|
|
| 61 |
+ receive_stdout := future.Go(func() error {
|
|
| 62 |
+ _, err := io.Copy(os.Stdout, conn) |
|
| 63 |
+ return err |
|
| 64 |
+ }) |
|
| 65 |
+ send_stdin := future.Go(func() error {
|
|
| 66 |
+ _, err := io.Copy(conn, os.Stdin) |
|
| 67 |
+ if err := conn.CloseWrite(); err != nil {
|
|
| 68 |
+ log.Printf("Couldn't send EOF: " + err.Error())
|
|
| 69 |
+ } |
|
| 70 |
+ return err |
|
| 71 |
+ }) |
|
| 72 |
+ if err := <-receive_stdout; err != nil {
|
|
| 73 |
+ return err |
|
| 74 |
+ } |
|
| 75 |
+ if !term.IsTerminal(0) {
|
|
| 76 |
+ if err := <-send_stdin; err != nil {
|
|
| 77 |
+ return err |
|
| 78 |
+ } |
|
| 79 |
+ } |
|
| 80 |
+ } else {
|
|
| 81 |
+ service, err := commands.New() |
|
| 82 |
+ if err != nil {
|
|
| 83 |
+ return err |
|
| 84 |
+ } |
|
| 85 |
+ if err := rcli.LocalCall(service, os.Stdin, os.Stdout, args...); err != nil {
|
|
| 86 |
+ return err |
|
| 87 |
+ } |
|
| 88 |
+ } |
|
| 89 |
+ if oldState != nil {
|
|
| 90 |
+ term.Restore(0, oldState) |
|
| 91 |
+ } |
|
| 92 |
+ return nil |
|
| 13 | 93 |
} |
| 14 | 94 |
deleted file mode 100644 |
| ... | ... |
@@ -1,24 +0,0 @@ |
| 1 |
-package main |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "flag" |
|
| 5 |
- "github.com/dotcloud/docker" |
|
| 6 |
- "github.com/dotcloud/docker/server" |
|
| 7 |
- "log" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func main() {
|
|
| 11 |
- if docker.SelfPath() == "/sbin/init" {
|
|
| 12 |
- // Running in init mode |
|
| 13 |
- docker.SysInit() |
|
| 14 |
- return |
|
| 15 |
- } |
|
| 16 |
- flag.Parse() |
|
| 17 |
- d, err := server.New() |
|
| 18 |
- if err != nil {
|
|
| 19 |
- log.Fatal(err) |
|
| 20 |
- } |
|
| 21 |
- if err := d.ListenAndServe(); err != nil {
|
|
| 22 |
- log.Fatal(err) |
|
| 23 |
- } |
|
| 24 |
-} |
| ... | ... |
@@ -25,7 +25,12 @@ type Service interface {
|
| 25 | 25 |
type Cmd func(io.ReadCloser, io.Writer, ...string) error |
| 26 | 26 |
type CmdMethod func(Service, io.ReadCloser, io.Writer, ...string) error |
| 27 | 27 |
|
| 28 |
+// FIXME: For reverse compatibility |
|
| 28 | 29 |
func call(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
| 30 |
+ return LocalCall(service, stdin, stdout, args...) |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+func LocalCall(service Service, stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 29 | 34 |
if len(args) == 0 {
|
| 30 | 35 |
args = []string{"help"}
|
| 31 | 36 |
} |
| 32 | 37 |
deleted file mode 100644 |
| ... | ... |
@@ -1,986 +0,0 @@ |
| 1 |
-package server |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "bytes" |
|
| 6 |
- "encoding/json" |
|
| 7 |
- "errors" |
|
| 8 |
- "fmt" |
|
| 9 |
- "github.com/dotcloud/docker" |
|
| 10 |
- "github.com/dotcloud/docker/fs" |
|
| 11 |
- "github.com/dotcloud/docker/future" |
|
| 12 |
- "github.com/dotcloud/docker/rcli" |
|
| 13 |
- "io" |
|
| 14 |
- "net/http" |
|
| 15 |
- "net/url" |
|
| 16 |
- "os" |
|
| 17 |
- "path" |
|
| 18 |
- "strconv" |
|
| 19 |
- "strings" |
|
| 20 |
- "sync" |
|
| 21 |
- "text/tabwriter" |
|
| 22 |
- "time" |
|
| 23 |
-) |
|
| 24 |
- |
|
| 25 |
-const VERSION = "0.0.1" |
|
| 26 |
- |
|
| 27 |
-func (srv *Server) ListenAndServe() error {
|
|
| 28 |
- go rcli.ListenAndServeHTTP("127.0.0.1:8080", srv)
|
|
| 29 |
- // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose |
|
| 30 |
- // CloseWrite(), which we need to cleanly signal that stdin is closed without |
|
| 31 |
- // closing the connection. |
|
| 32 |
- // See http://code.google.com/p/go/issues/detail?id=3345 |
|
| 33 |
- return rcli.ListenAndServe("tcp", "127.0.0.1:4242", srv)
|
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 |
-func (srv *Server) Name() string {
|
|
| 37 |
- return "docker" |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-// FIXME: Stop violating DRY by repeating usage here and in Subcmd declarations |
|
| 41 |
-func (srv *Server) Help() string {
|
|
| 42 |
- help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n" |
|
| 43 |
- for _, cmd := range [][]interface{}{
|
|
| 44 |
- {"run", "Run a command in a container"},
|
|
| 45 |
- {"ps", "Display a list of containers"},
|
|
| 46 |
- {"import", "Create a new filesystem image from the contents of a tarball"},
|
|
| 47 |
- {"attach", "Attach to a running container"},
|
|
| 48 |
- {"cat", "Write the contents of a container's file to standard output"},
|
|
| 49 |
- {"commit", "Create a new image from a container's changes"},
|
|
| 50 |
- {"cp", "Create a copy of IMAGE and call it NAME"},
|
|
| 51 |
- {"debug", "(debug only) (No documentation available)"},
|
|
| 52 |
- {"diff", "Inspect changes on a container's filesystem"},
|
|
| 53 |
- {"images", "List images"},
|
|
| 54 |
- {"info", "Display system-wide information"},
|
|
| 55 |
- {"inspect", "Return low-level information on a container"},
|
|
| 56 |
- {"kill", "Kill a running container"},
|
|
| 57 |
- {"layers", "(debug only) List filesystem layers"},
|
|
| 58 |
- {"logs", "Fetch the logs of a container"},
|
|
| 59 |
- {"ls", "List the contents of a container's directory"},
|
|
| 60 |
- {"mirror", "(debug only) (No documentation available)"},
|
|
| 61 |
- {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
|
|
| 62 |
- {"ps", "List containers"},
|
|
| 63 |
- {"reset", "Reset changes to a container's filesystem"},
|
|
| 64 |
- {"restart", "Restart a running container"},
|
|
| 65 |
- {"rm", "Remove a container"},
|
|
| 66 |
- {"rmimage", "Remove an image"},
|
|
| 67 |
- {"run", "Run a command in a new container"},
|
|
| 68 |
- {"start", "Start a stopped container"},
|
|
| 69 |
- {"stop", "Stop a running container"},
|
|
| 70 |
- {"tar", "Stream the contents of a container as a tar archive"},
|
|
| 71 |
- {"umount", "(debug only) Mount a container's filesystem"},
|
|
| 72 |
- {"version", "Show the docker version information"},
|
|
| 73 |
- {"wait", "Block until a container stops, then print its exit code"},
|
|
| 74 |
- {"web", "A web UI for docker"},
|
|
| 75 |
- {"write", "Write the contents of standard input to a container's file"},
|
|
| 76 |
- } {
|
|
| 77 |
- help += fmt.Sprintf(" %-10.10s%s\n", cmd...)
|
|
| 78 |
- } |
|
| 79 |
- return help |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-// 'docker wait': block until a container stops |
|
| 83 |
-func (srv *Server) CmdWait(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 84 |
- cmd := rcli.Subcmd(stdout, "wait", "[OPTIONS] NAME", "Block until a container stops, then print its exit code.") |
|
| 85 |
- if err := cmd.Parse(args); err != nil {
|
|
| 86 |
- return nil |
|
| 87 |
- } |
|
| 88 |
- if cmd.NArg() < 1 {
|
|
| 89 |
- cmd.Usage() |
|
| 90 |
- return nil |
|
| 91 |
- } |
|
| 92 |
- for _, name := range cmd.Args() {
|
|
| 93 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 94 |
- fmt.Fprintln(stdout, container.Wait()) |
|
| 95 |
- } else {
|
|
| 96 |
- return errors.New("No such container: " + name)
|
|
| 97 |
- } |
|
| 98 |
- } |
|
| 99 |
- return nil |
|
| 100 |
-} |
|
| 101 |
- |
|
| 102 |
-// 'docker version': show version information |
|
| 103 |
-func (srv *Server) CmdVersion(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 104 |
- fmt.Fprintf(stdout, "Version:%s\n", VERSION) |
|
| 105 |
- return nil |
|
| 106 |
-} |
|
| 107 |
- |
|
| 108 |
-// 'docker info': display system-wide information. |
|
| 109 |
-func (srv *Server) CmdInfo(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 110 |
- images, _ := srv.images.Images() |
|
| 111 |
- var imgcount int |
|
| 112 |
- if images == nil {
|
|
| 113 |
- imgcount = 0 |
|
| 114 |
- } else {
|
|
| 115 |
- imgcount = len(images) |
|
| 116 |
- } |
|
| 117 |
- cmd := rcli.Subcmd(stdout, "info", "", "Display system-wide information.") |
|
| 118 |
- if err := cmd.Parse(args); err != nil {
|
|
| 119 |
- return nil |
|
| 120 |
- } |
|
| 121 |
- if cmd.NArg() > 0 {
|
|
| 122 |
- cmd.Usage() |
|
| 123 |
- return nil |
|
| 124 |
- } |
|
| 125 |
- fmt.Fprintf(stdout, "containers: %d\nversion: %s\nimages: %d\n", |
|
| 126 |
- len(srv.containers.List()), |
|
| 127 |
- VERSION, |
|
| 128 |
- imgcount) |
|
| 129 |
- return nil |
|
| 130 |
-} |
|
| 131 |
- |
|
| 132 |
-func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 133 |
- cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container") |
|
| 134 |
- if err := cmd.Parse(args); err != nil {
|
|
| 135 |
- return nil |
|
| 136 |
- } |
|
| 137 |
- if cmd.NArg() < 1 {
|
|
| 138 |
- cmd.Usage() |
|
| 139 |
- return nil |
|
| 140 |
- } |
|
| 141 |
- for _, name := range cmd.Args() {
|
|
| 142 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 143 |
- if err := container.Stop(); err != nil {
|
|
| 144 |
- return err |
|
| 145 |
- } |
|
| 146 |
- fmt.Fprintln(stdout, container.Id) |
|
| 147 |
- } else {
|
|
| 148 |
- return errors.New("No such container: " + name)
|
|
| 149 |
- } |
|
| 150 |
- } |
|
| 151 |
- return nil |
|
| 152 |
-} |
|
| 153 |
- |
|
| 154 |
-func (srv *Server) CmdRestart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 155 |
- cmd := rcli.Subcmd(stdout, "restart", "[OPTIONS] NAME", "Restart a running container") |
|
| 156 |
- if err := cmd.Parse(args); err != nil {
|
|
| 157 |
- return nil |
|
| 158 |
- } |
|
| 159 |
- if cmd.NArg() < 1 {
|
|
| 160 |
- cmd.Usage() |
|
| 161 |
- return nil |
|
| 162 |
- } |
|
| 163 |
- for _, name := range cmd.Args() {
|
|
| 164 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 165 |
- if err := container.Restart(); err != nil {
|
|
| 166 |
- return err |
|
| 167 |
- } |
|
| 168 |
- fmt.Fprintln(stdout, container.Id) |
|
| 169 |
- } else {
|
|
| 170 |
- return errors.New("No such container: " + name)
|
|
| 171 |
- } |
|
| 172 |
- } |
|
| 173 |
- return nil |
|
| 174 |
-} |
|
| 175 |
- |
|
| 176 |
-func (srv *Server) CmdStart(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 177 |
- cmd := rcli.Subcmd(stdout, "start", "[OPTIONS] NAME", "Start a stopped container") |
|
| 178 |
- if err := cmd.Parse(args); err != nil {
|
|
| 179 |
- return nil |
|
| 180 |
- } |
|
| 181 |
- if cmd.NArg() < 1 {
|
|
| 182 |
- cmd.Usage() |
|
| 183 |
- return nil |
|
| 184 |
- } |
|
| 185 |
- for _, name := range cmd.Args() {
|
|
| 186 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 187 |
- if err := container.Start(); err != nil {
|
|
| 188 |
- return err |
|
| 189 |
- } |
|
| 190 |
- fmt.Fprintln(stdout, container.Id) |
|
| 191 |
- } else {
|
|
| 192 |
- return errors.New("No such container: " + name)
|
|
| 193 |
- } |
|
| 194 |
- } |
|
| 195 |
- return nil |
|
| 196 |
-} |
|
| 197 |
- |
|
| 198 |
-func (srv *Server) CmdUmount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 199 |
- cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "umount a container's filesystem (debug only)") |
|
| 200 |
- if err := cmd.Parse(args); err != nil {
|
|
| 201 |
- return nil |
|
| 202 |
- } |
|
| 203 |
- if cmd.NArg() < 1 {
|
|
| 204 |
- cmd.Usage() |
|
| 205 |
- return nil |
|
| 206 |
- } |
|
| 207 |
- for _, name := range cmd.Args() {
|
|
| 208 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 209 |
- if err := container.Mountpoint.Umount(); err != nil {
|
|
| 210 |
- return err |
|
| 211 |
- } |
|
| 212 |
- fmt.Fprintln(stdout, container.Id) |
|
| 213 |
- } else {
|
|
| 214 |
- return errors.New("No such container: " + name)
|
|
| 215 |
- } |
|
| 216 |
- } |
|
| 217 |
- return nil |
|
| 218 |
-} |
|
| 219 |
- |
|
| 220 |
-func (srv *Server) CmdMount(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 221 |
- cmd := rcli.Subcmd(stdout, "umount", "[OPTIONS] NAME", "mount a container's filesystem (debug only)") |
|
| 222 |
- if err := cmd.Parse(args); err != nil {
|
|
| 223 |
- return nil |
|
| 224 |
- } |
|
| 225 |
- if cmd.NArg() < 1 {
|
|
| 226 |
- cmd.Usage() |
|
| 227 |
- return nil |
|
| 228 |
- } |
|
| 229 |
- for _, name := range cmd.Args() {
|
|
| 230 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 231 |
- if err := container.Mountpoint.EnsureMounted(); err != nil {
|
|
| 232 |
- return err |
|
| 233 |
- } |
|
| 234 |
- fmt.Fprintln(stdout, container.Id) |
|
| 235 |
- } else {
|
|
| 236 |
- return errors.New("No such container: " + name)
|
|
| 237 |
- } |
|
| 238 |
- } |
|
| 239 |
- return nil |
|
| 240 |
-} |
|
| 241 |
- |
|
| 242 |
-func (srv *Server) CmdCat(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 243 |
- cmd := rcli.Subcmd(stdout, "cat", "[OPTIONS] CONTAINER PATH", "write the contents of a container's file to standard output") |
|
| 244 |
- if err := cmd.Parse(args); err != nil {
|
|
| 245 |
- return nil |
|
| 246 |
- } |
|
| 247 |
- if cmd.NArg() < 2 {
|
|
| 248 |
- cmd.Usage() |
|
| 249 |
- return nil |
|
| 250 |
- } |
|
| 251 |
- name, path := cmd.Arg(0), cmd.Arg(1) |
|
| 252 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 253 |
- if f, err := container.Mountpoint.OpenFile(path, os.O_RDONLY, 0); err != nil {
|
|
| 254 |
- return err |
|
| 255 |
- } else if _, err := io.Copy(stdout, f); err != nil {
|
|
| 256 |
- return err |
|
| 257 |
- } |
|
| 258 |
- return nil |
|
| 259 |
- } |
|
| 260 |
- return errors.New("No such container: " + name)
|
|
| 261 |
-} |
|
| 262 |
- |
|
| 263 |
-func (srv *Server) CmdWrite(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 264 |
- cmd := rcli.Subcmd(stdout, "write", "[OPTIONS] CONTAINER PATH", "write the contents of standard input to a container's file") |
|
| 265 |
- if err := cmd.Parse(args); err != nil {
|
|
| 266 |
- return nil |
|
| 267 |
- } |
|
| 268 |
- if cmd.NArg() < 2 {
|
|
| 269 |
- cmd.Usage() |
|
| 270 |
- return nil |
|
| 271 |
- } |
|
| 272 |
- name, path := cmd.Arg(0), cmd.Arg(1) |
|
| 273 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 274 |
- if f, err := container.Mountpoint.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600); err != nil {
|
|
| 275 |
- return err |
|
| 276 |
- } else if _, err := io.Copy(f, stdin); err != nil {
|
|
| 277 |
- return err |
|
| 278 |
- } |
|
| 279 |
- return nil |
|
| 280 |
- } |
|
| 281 |
- return errors.New("No such container: " + name)
|
|
| 282 |
-} |
|
| 283 |
- |
|
| 284 |
-func (srv *Server) CmdLs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 285 |
- cmd := rcli.Subcmd(stdout, "ls", "[OPTIONS] CONTAINER PATH", "List the contents of a container's directory") |
|
| 286 |
- if err := cmd.Parse(args); err != nil {
|
|
| 287 |
- return nil |
|
| 288 |
- } |
|
| 289 |
- if cmd.NArg() < 2 {
|
|
| 290 |
- cmd.Usage() |
|
| 291 |
- return nil |
|
| 292 |
- } |
|
| 293 |
- name, path := cmd.Arg(0), cmd.Arg(1) |
|
| 294 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 295 |
- if files, err := container.Mountpoint.ReadDir(path); err != nil {
|
|
| 296 |
- return err |
|
| 297 |
- } else {
|
|
| 298 |
- for _, f := range files {
|
|
| 299 |
- fmt.Fprintln(stdout, f.Name()) |
|
| 300 |
- } |
|
| 301 |
- } |
|
| 302 |
- return nil |
|
| 303 |
- } |
|
| 304 |
- return errors.New("No such container: " + name)
|
|
| 305 |
-} |
|
| 306 |
- |
|
| 307 |
-func (srv *Server) CmdInspect(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 308 |
- cmd := rcli.Subcmd(stdout, "inspect", "[OPTIONS] CONTAINER", "Return low-level information on a container") |
|
| 309 |
- if err := cmd.Parse(args); err != nil {
|
|
| 310 |
- return nil |
|
| 311 |
- } |
|
| 312 |
- if cmd.NArg() < 1 {
|
|
| 313 |
- cmd.Usage() |
|
| 314 |
- return nil |
|
| 315 |
- } |
|
| 316 |
- name := cmd.Arg(0) |
|
| 317 |
- var obj interface{}
|
|
| 318 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 319 |
- obj = container |
|
| 320 |
- //} else if image, err := srv.images.List(name); image != nil {
|
|
| 321 |
- // obj = image |
|
| 322 |
- } else {
|
|
| 323 |
- return errors.New("No such container or image: " + name)
|
|
| 324 |
- } |
|
| 325 |
- data, err := json.Marshal(obj) |
|
| 326 |
- if err != nil {
|
|
| 327 |
- return err |
|
| 328 |
- } |
|
| 329 |
- indented := new(bytes.Buffer) |
|
| 330 |
- if err = json.Indent(indented, data, "", " "); err != nil {
|
|
| 331 |
- return err |
|
| 332 |
- } |
|
| 333 |
- if _, err := io.Copy(stdout, indented); err != nil {
|
|
| 334 |
- return err |
|
| 335 |
- } |
|
| 336 |
- return nil |
|
| 337 |
-} |
|
| 338 |
- |
|
| 339 |
-func (srv *Server) CmdPort(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 340 |
- cmd := rcli.Subcmd(stdout, "port", "[OPTIONS] CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT") |
|
| 341 |
- if err := cmd.Parse(args); err != nil {
|
|
| 342 |
- return nil |
|
| 343 |
- } |
|
| 344 |
- if cmd.NArg() != 2 {
|
|
| 345 |
- cmd.Usage() |
|
| 346 |
- return nil |
|
| 347 |
- } |
|
| 348 |
- name := cmd.Arg(0) |
|
| 349 |
- privatePort := cmd.Arg(1) |
|
| 350 |
- if container := srv.containers.Get(name); container == nil {
|
|
| 351 |
- return errors.New("No such container: " + name)
|
|
| 352 |
- } else {
|
|
| 353 |
- if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
|
|
| 354 |
- return fmt.Errorf("No private port '%s' allocated on %s", privatePort, name)
|
|
| 355 |
- } else {
|
|
| 356 |
- fmt.Fprintln(stdout, frontend) |
|
| 357 |
- } |
|
| 358 |
- } |
|
| 359 |
- return nil |
|
| 360 |
-} |
|
| 361 |
- |
|
| 362 |
-// 'docker rmi NAME' removes all images with the name NAME |
|
| 363 |
-// func (srv *Server) CmdRmi(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 364 |
-// cmd := rcli.Subcmd(stdout, "rmimage", "[OPTIONS] IMAGE", "Remove an image") |
|
| 365 |
-// fl_regexp := cmd.Bool("r", false, "Use IMAGE as a regular expression instead of an exact name")
|
|
| 366 |
-// if err := cmd.Parse(args); err != nil {
|
|
| 367 |
-// cmd.Usage() |
|
| 368 |
-// return nil |
|
| 369 |
-// } |
|
| 370 |
-// if cmd.NArg() < 1 {
|
|
| 371 |
-// cmd.Usage() |
|
| 372 |
-// return nil |
|
| 373 |
-// } |
|
| 374 |
-// for _, name := range cmd.Args() {
|
|
| 375 |
-// var err error |
|
| 376 |
-// if *fl_regexp {
|
|
| 377 |
-// err = srv.images.DeleteMatch(name) |
|
| 378 |
-// } else {
|
|
| 379 |
-// image := srv.images.Find(name) |
|
| 380 |
-// if image == nil {
|
|
| 381 |
-// return errors.New("No such image: " + name)
|
|
| 382 |
-// } |
|
| 383 |
-// err = srv.images.Delete(name) |
|
| 384 |
-// } |
|
| 385 |
-// if err != nil {
|
|
| 386 |
-// return err |
|
| 387 |
-// } |
|
| 388 |
-// } |
|
| 389 |
-// return nil |
|
| 390 |
-// } |
|
| 391 |
- |
|
| 392 |
-func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 393 |
- cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER", "Remove a container") |
|
| 394 |
- if err := cmd.Parse(args); err != nil {
|
|
| 395 |
- return nil |
|
| 396 |
- } |
|
| 397 |
- for _, name := range cmd.Args() {
|
|
| 398 |
- container := srv.containers.Get(name) |
|
| 399 |
- if container == nil {
|
|
| 400 |
- return errors.New("No such container: " + name)
|
|
| 401 |
- } |
|
| 402 |
- if err := srv.containers.Destroy(container); err != nil {
|
|
| 403 |
- fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error()) |
|
| 404 |
- } |
|
| 405 |
- } |
|
| 406 |
- return nil |
|
| 407 |
-} |
|
| 408 |
- |
|
| 409 |
-// 'docker kill NAME' kills a running container |
|
| 410 |
-func (srv *Server) CmdKill(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 411 |
- cmd := rcli.Subcmd(stdout, "kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container") |
|
| 412 |
- if err := cmd.Parse(args); err != nil {
|
|
| 413 |
- return nil |
|
| 414 |
- } |
|
| 415 |
- for _, name := range cmd.Args() {
|
|
| 416 |
- container := srv.containers.Get(name) |
|
| 417 |
- if container == nil {
|
|
| 418 |
- return errors.New("No such container: " + name)
|
|
| 419 |
- } |
|
| 420 |
- if err := container.Kill(); err != nil {
|
|
| 421 |
- fmt.Fprintln(stdout, "Error killing container "+name+": "+err.Error()) |
|
| 422 |
- } |
|
| 423 |
- } |
|
| 424 |
- return nil |
|
| 425 |
-} |
|
| 426 |
- |
|
| 427 |
-func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 428 |
- cmd := rcli.Subcmd(stdout, "import", "[OPTIONS] NAME", "Create a new filesystem image from the contents of a tarball") |
|
| 429 |
- fl_stdin := cmd.Bool("stdin", false, "Read tarball from stdin")
|
|
| 430 |
- if err := cmd.Parse(args); err != nil {
|
|
| 431 |
- return nil |
|
| 432 |
- } |
|
| 433 |
- var archive io.Reader |
|
| 434 |
- name := cmd.Arg(0) |
|
| 435 |
- if name == "" {
|
|
| 436 |
- return errors.New("Not enough arguments")
|
|
| 437 |
- } |
|
| 438 |
- if *fl_stdin {
|
|
| 439 |
- archive = stdin |
|
| 440 |
- } else {
|
|
| 441 |
- u, err := url.Parse(name) |
|
| 442 |
- if err != nil {
|
|
| 443 |
- return err |
|
| 444 |
- } |
|
| 445 |
- if u.Scheme == "" {
|
|
| 446 |
- u.Scheme = "http" |
|
| 447 |
- } |
|
| 448 |
- // FIXME: hardcode a mirror URL that does not depend on a single provider. |
|
| 449 |
- if u.Host == "" {
|
|
| 450 |
- u.Host = "s3.amazonaws.com" |
|
| 451 |
- u.Path = path.Join("/docker.io/images", u.Path)
|
|
| 452 |
- } |
|
| 453 |
- fmt.Fprintf(stdout, "Downloading from %s\n", u.String()) |
|
| 454 |
- // Download with curl (pretty progress bar) |
|
| 455 |
- // If curl is not available, fallback to http.Get() |
|
| 456 |
- archive, err = future.Curl(u.String(), stdout) |
|
| 457 |
- if err != nil {
|
|
| 458 |
- if resp, err := http.Get(u.String()); err != nil {
|
|
| 459 |
- return err |
|
| 460 |
- } else {
|
|
| 461 |
- archive = resp.Body |
|
| 462 |
- } |
|
| 463 |
- } |
|
| 464 |
- } |
|
| 465 |
- fmt.Fprintf(stdout, "Unpacking to %s\n", name) |
|
| 466 |
- img, err := srv.images.Create(archive, nil, name, "") |
|
| 467 |
- if err != nil {
|
|
| 468 |
- return err |
|
| 469 |
- } |
|
| 470 |
- fmt.Fprintln(stdout, img.Id) |
|
| 471 |
- return nil |
|
| 472 |
-} |
|
| 473 |
- |
|
| 474 |
-func (srv *Server) CmdImages(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 475 |
- cmd := rcli.Subcmd(stdout, "images", "[OPTIONS] [NAME]", "List images") |
|
| 476 |
- limit := cmd.Int("l", 0, "Only show the N most recent versions of each image")
|
|
| 477 |
- quiet := cmd.Bool("q", false, "only show numeric IDs")
|
|
| 478 |
- if err := cmd.Parse(args); err != nil {
|
|
| 479 |
- return nil |
|
| 480 |
- } |
|
| 481 |
- if cmd.NArg() > 1 {
|
|
| 482 |
- cmd.Usage() |
|
| 483 |
- return nil |
|
| 484 |
- } |
|
| 485 |
- var nameFilter string |
|
| 486 |
- if cmd.NArg() == 1 {
|
|
| 487 |
- nameFilter = cmd.Arg(0) |
|
| 488 |
- } |
|
| 489 |
- w := tabwriter.NewWriter(stdout, 20, 1, 3, ' ', 0) |
|
| 490 |
- if !*quiet {
|
|
| 491 |
- fmt.Fprintf(w, "NAME\tID\tCREATED\tPARENT\n") |
|
| 492 |
- } |
|
| 493 |
- paths, err := srv.images.Paths() |
|
| 494 |
- if err != nil {
|
|
| 495 |
- return err |
|
| 496 |
- } |
|
| 497 |
- for _, name := range paths {
|
|
| 498 |
- if nameFilter != "" && nameFilter != name {
|
|
| 499 |
- continue |
|
| 500 |
- } |
|
| 501 |
- ids, err := srv.images.List(name) |
|
| 502 |
- if err != nil {
|
|
| 503 |
- return err |
|
| 504 |
- } |
|
| 505 |
- for idx, img := range ids {
|
|
| 506 |
- if *limit > 0 && idx >= *limit {
|
|
| 507 |
- break |
|
| 508 |
- } |
|
| 509 |
- if !*quiet {
|
|
| 510 |
- for idx, field := range []string{
|
|
| 511 |
- /* NAME */ name, |
|
| 512 |
- /* ID */ img.Id, |
|
| 513 |
- /* CREATED */ future.HumanDuration(time.Now().Sub(time.Unix(img.Created, 0))) + " ago", |
|
| 514 |
- /* PARENT */ img.Parent, |
|
| 515 |
- } {
|
|
| 516 |
- if idx == 0 {
|
|
| 517 |
- w.Write([]byte(field)) |
|
| 518 |
- } else {
|
|
| 519 |
- w.Write([]byte("\t" + field))
|
|
| 520 |
- } |
|
| 521 |
- } |
|
| 522 |
- w.Write([]byte{'\n'})
|
|
| 523 |
- } else {
|
|
| 524 |
- stdout.Write([]byte(img.Id + "\n")) |
|
| 525 |
- } |
|
| 526 |
- } |
|
| 527 |
- } |
|
| 528 |
- if !*quiet {
|
|
| 529 |
- w.Flush() |
|
| 530 |
- } |
|
| 531 |
- return nil |
|
| 532 |
- |
|
| 533 |
-} |
|
| 534 |
- |
|
| 535 |
-func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 536 |
- cmd := rcli.Subcmd(stdout, |
|
| 537 |
- "ps", "[OPTIONS]", "List containers") |
|
| 538 |
- quiet := cmd.Bool("q", false, "Only display numeric IDs")
|
|
| 539 |
- fl_all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
|
|
| 540 |
- fl_full := cmd.Bool("notrunc", false, "Don't truncate output")
|
|
| 541 |
- if err := cmd.Parse(args); err != nil {
|
|
| 542 |
- return nil |
|
| 543 |
- } |
|
| 544 |
- w := tabwriter.NewWriter(stdout, 12, 1, 3, ' ', 0) |
|
| 545 |
- if !*quiet {
|
|
| 546 |
- fmt.Fprintf(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tCOMMENT\n") |
|
| 547 |
- } |
|
| 548 |
- for _, container := range srv.containers.List() {
|
|
| 549 |
- comment := container.GetUserData("comment")
|
|
| 550 |
- if !container.State.Running && !*fl_all {
|
|
| 551 |
- continue |
|
| 552 |
- } |
|
| 553 |
- if !*quiet {
|
|
| 554 |
- command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
|
|
| 555 |
- if !*fl_full {
|
|
| 556 |
- command = docker.Trunc(command, 20) |
|
| 557 |
- } |
|
| 558 |
- for idx, field := range []string{
|
|
| 559 |
- /* ID */ container.Id, |
|
| 560 |
- /* IMAGE */ container.GetUserData("image"),
|
|
| 561 |
- /* COMMAND */ command, |
|
| 562 |
- /* CREATED */ future.HumanDuration(time.Now().Sub(container.Created)) + " ago", |
|
| 563 |
- /* STATUS */ container.State.String(), |
|
| 564 |
- /* COMMENT */ comment, |
|
| 565 |
- } {
|
|
| 566 |
- if idx == 0 {
|
|
| 567 |
- w.Write([]byte(field)) |
|
| 568 |
- } else {
|
|
| 569 |
- w.Write([]byte("\t" + field))
|
|
| 570 |
- } |
|
| 571 |
- } |
|
| 572 |
- w.Write([]byte{'\n'})
|
|
| 573 |
- } else {
|
|
| 574 |
- stdout.Write([]byte(container.Id + "\n")) |
|
| 575 |
- } |
|
| 576 |
- } |
|
| 577 |
- if !*quiet {
|
|
| 578 |
- w.Flush() |
|
| 579 |
- } |
|
| 580 |
- return nil |
|
| 581 |
-} |
|
| 582 |
- |
|
| 583 |
-func (srv *Server) CmdLayers(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 584 |
- cmd := rcli.Subcmd(stdout, |
|
| 585 |
- "layers", "[OPTIONS]", |
|
| 586 |
- "List filesystem layers (debug only)") |
|
| 587 |
- if err := cmd.Parse(args); err != nil {
|
|
| 588 |
- return nil |
|
| 589 |
- } |
|
| 590 |
- for _, layer := range srv.images.Layers() {
|
|
| 591 |
- fmt.Fprintln(stdout, layer) |
|
| 592 |
- } |
|
| 593 |
- return nil |
|
| 594 |
-} |
|
| 595 |
- |
|
| 596 |
-func (srv *Server) CmdCp(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 597 |
- cmd := rcli.Subcmd(stdout, |
|
| 598 |
- "cp", "[OPTIONS] IMAGE NAME", |
|
| 599 |
- "Create a copy of IMAGE and call it NAME") |
|
| 600 |
- if err := cmd.Parse(args); err != nil {
|
|
| 601 |
- return nil |
|
| 602 |
- } |
|
| 603 |
- if image, err := srv.images.Get(cmd.Arg(0)); err != nil {
|
|
| 604 |
- return err |
|
| 605 |
- } else if image == nil {
|
|
| 606 |
- return errors.New("Image " + cmd.Arg(0) + " does not exist")
|
|
| 607 |
- } else {
|
|
| 608 |
- if img, err := image.Copy(cmd.Arg(1)); err != nil {
|
|
| 609 |
- return err |
|
| 610 |
- } else {
|
|
| 611 |
- fmt.Fprintln(stdout, img.Id) |
|
| 612 |
- } |
|
| 613 |
- } |
|
| 614 |
- return nil |
|
| 615 |
-} |
|
| 616 |
- |
|
| 617 |
-func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 618 |
- cmd := rcli.Subcmd(stdout, |
|
| 619 |
- "commit", "[OPTIONS] CONTAINER [DEST]", |
|
| 620 |
- "Create a new image from a container's changes") |
|
| 621 |
- if err := cmd.Parse(args); err != nil {
|
|
| 622 |
- return nil |
|
| 623 |
- } |
|
| 624 |
- containerName, imgName := cmd.Arg(0), cmd.Arg(1) |
|
| 625 |
- if containerName == "" || imgName == "" {
|
|
| 626 |
- cmd.Usage() |
|
| 627 |
- return nil |
|
| 628 |
- } |
|
| 629 |
- if container := srv.containers.Get(containerName); container != nil {
|
|
| 630 |
- // FIXME: freeze the container before copying it to avoid data corruption? |
|
| 631 |
- rwTar, err := fs.Tar(container.Mountpoint.Rw, fs.Uncompressed) |
|
| 632 |
- if err != nil {
|
|
| 633 |
- return err |
|
| 634 |
- } |
|
| 635 |
- // Create a new image from the container's base layers + a new layer from container changes |
|
| 636 |
- parentImg, err := srv.images.Get(container.Image) |
|
| 637 |
- if err != nil {
|
|
| 638 |
- return err |
|
| 639 |
- } |
|
| 640 |
- |
|
| 641 |
- img, err := srv.images.Create(rwTar, parentImg, imgName, "") |
|
| 642 |
- if err != nil {
|
|
| 643 |
- return err |
|
| 644 |
- } |
|
| 645 |
- |
|
| 646 |
- fmt.Fprintln(stdout, img.Id) |
|
| 647 |
- return nil |
|
| 648 |
- } |
|
| 649 |
- return errors.New("No such container: " + containerName)
|
|
| 650 |
-} |
|
| 651 |
- |
|
| 652 |
-func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 653 |
- cmd := rcli.Subcmd(stdout, |
|
| 654 |
- "tar", "CONTAINER", |
|
| 655 |
- "Stream the contents of a container as a tar archive") |
|
| 656 |
- fl_sparse := cmd.Bool("s", false, "Generate a sparse tar stream (top layer + reference to bottom layers)")
|
|
| 657 |
- if err := cmd.Parse(args); err != nil {
|
|
| 658 |
- return nil |
|
| 659 |
- } |
|
| 660 |
- if *fl_sparse {
|
|
| 661 |
- return errors.New("Sparse mode not yet implemented") // FIXME
|
|
| 662 |
- } |
|
| 663 |
- name := cmd.Arg(0) |
|
| 664 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 665 |
- if err := container.Mountpoint.EnsureMounted(); err != nil {
|
|
| 666 |
- return err |
|
| 667 |
- } |
|
| 668 |
- data, err := fs.Tar(container.Mountpoint.Root, fs.Uncompressed) |
|
| 669 |
- if err != nil {
|
|
| 670 |
- return err |
|
| 671 |
- } |
|
| 672 |
- // Stream the entire contents of the container (basically a volatile snapshot) |
|
| 673 |
- if _, err := io.Copy(stdout, data); err != nil {
|
|
| 674 |
- return err |
|
| 675 |
- } |
|
| 676 |
- return nil |
|
| 677 |
- } |
|
| 678 |
- return errors.New("No such container: " + name)
|
|
| 679 |
-} |
|
| 680 |
- |
|
| 681 |
-func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 682 |
- cmd := rcli.Subcmd(stdout, |
|
| 683 |
- "diff", "CONTAINER [OPTIONS]", |
|
| 684 |
- "Inspect changes on a container's filesystem") |
|
| 685 |
- if err := cmd.Parse(args); err != nil {
|
|
| 686 |
- return nil |
|
| 687 |
- } |
|
| 688 |
- if cmd.NArg() < 1 {
|
|
| 689 |
- return errors.New("Not enough arguments")
|
|
| 690 |
- } |
|
| 691 |
- if container := srv.containers.Get(cmd.Arg(0)); container == nil {
|
|
| 692 |
- return errors.New("No such container")
|
|
| 693 |
- } else {
|
|
| 694 |
- changes, err := srv.images.Changes(container.Mountpoint) |
|
| 695 |
- if err != nil {
|
|
| 696 |
- return err |
|
| 697 |
- } |
|
| 698 |
- for _, change := range changes {
|
|
| 699 |
- fmt.Fprintln(stdout, change.String()) |
|
| 700 |
- } |
|
| 701 |
- } |
|
| 702 |
- return nil |
|
| 703 |
-} |
|
| 704 |
- |
|
| 705 |
-func (srv *Server) CmdReset(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 706 |
- cmd := rcli.Subcmd(stdout, |
|
| 707 |
- "reset", "CONTAINER [OPTIONS]", |
|
| 708 |
- "Reset changes to a container's filesystem") |
|
| 709 |
- if err := cmd.Parse(args); err != nil {
|
|
| 710 |
- return nil |
|
| 711 |
- } |
|
| 712 |
- if cmd.NArg() < 1 {
|
|
| 713 |
- return errors.New("Not enough arguments")
|
|
| 714 |
- } |
|
| 715 |
- for _, name := range cmd.Args() {
|
|
| 716 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 717 |
- if err := container.Mountpoint.Reset(); err != nil {
|
|
| 718 |
- return errors.New("Reset " + container.Id + ": " + err.Error())
|
|
| 719 |
- } |
|
| 720 |
- } |
|
| 721 |
- } |
|
| 722 |
- return nil |
|
| 723 |
-} |
|
| 724 |
- |
|
| 725 |
-func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 726 |
- cmd := rcli.Subcmd(stdout, "logs", "[OPTIONS] CONTAINER", "Fetch the logs of a container") |
|
| 727 |
- if err := cmd.Parse(args); err != nil {
|
|
| 728 |
- return nil |
|
| 729 |
- } |
|
| 730 |
- if cmd.NArg() != 1 {
|
|
| 731 |
- cmd.Usage() |
|
| 732 |
- return nil |
|
| 733 |
- } |
|
| 734 |
- name := cmd.Arg(0) |
|
| 735 |
- if container := srv.containers.Get(name); container != nil {
|
|
| 736 |
- if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
|
|
| 737 |
- return err |
|
| 738 |
- } |
|
| 739 |
- if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
|
|
| 740 |
- return err |
|
| 741 |
- } |
|
| 742 |
- return nil |
|
| 743 |
- } |
|
| 744 |
- return errors.New("No such container: " + cmd.Arg(0))
|
|
| 745 |
-} |
|
| 746 |
- |
|
| 747 |
-func (srv *Server) CreateContainer(img *fs.Image, ports []int, user string, tty bool, openStdin bool, memory int64, comment string, cmd string, args ...string) (*docker.Container, error) {
|
|
| 748 |
- id := future.RandomId()[:8] |
|
| 749 |
- container, err := srv.containers.Create(id, cmd, args, img, |
|
| 750 |
- &docker.Config{
|
|
| 751 |
- Hostname: id, |
|
| 752 |
- Ports: ports, |
|
| 753 |
- User: user, |
|
| 754 |
- Tty: tty, |
|
| 755 |
- OpenStdin: openStdin, |
|
| 756 |
- Memory: memory, |
|
| 757 |
- }) |
|
| 758 |
- if err != nil {
|
|
| 759 |
- return nil, err |
|
| 760 |
- } |
|
| 761 |
- if err := container.SetUserData("image", img.Id); err != nil {
|
|
| 762 |
- srv.containers.Destroy(container) |
|
| 763 |
- return nil, errors.New("Error setting container userdata: " + err.Error())
|
|
| 764 |
- } |
|
| 765 |
- if err := container.SetUserData("comment", comment); err != nil {
|
|
| 766 |
- srv.containers.Destroy(container) |
|
| 767 |
- return nil, errors.New("Error setting container userdata: " + err.Error())
|
|
| 768 |
- } |
|
| 769 |
- return container, nil |
|
| 770 |
-} |
|
| 771 |
- |
|
| 772 |
-func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 773 |
- cmd := rcli.Subcmd(stdout, "attach", "[OPTIONS]", "Attach to a running container") |
|
| 774 |
- fl_i := cmd.Bool("i", false, "Attach to stdin")
|
|
| 775 |
- fl_o := cmd.Bool("o", true, "Attach to stdout")
|
|
| 776 |
- fl_e := cmd.Bool("e", true, "Attach to stderr")
|
|
| 777 |
- if err := cmd.Parse(args); err != nil {
|
|
| 778 |
- return nil |
|
| 779 |
- } |
|
| 780 |
- if cmd.NArg() != 1 {
|
|
| 781 |
- cmd.Usage() |
|
| 782 |
- return nil |
|
| 783 |
- } |
|
| 784 |
- name := cmd.Arg(0) |
|
| 785 |
- container := srv.containers.Get(name) |
|
| 786 |
- if container == nil {
|
|
| 787 |
- return errors.New("No such container: " + name)
|
|
| 788 |
- } |
|
| 789 |
- var wg sync.WaitGroup |
|
| 790 |
- if *fl_i {
|
|
| 791 |
- c_stdin, err := container.StdinPipe() |
|
| 792 |
- if err != nil {
|
|
| 793 |
- return err |
|
| 794 |
- } |
|
| 795 |
- wg.Add(1) |
|
| 796 |
- go func() { io.Copy(c_stdin, stdin); wg.Add(-1) }()
|
|
| 797 |
- } |
|
| 798 |
- if *fl_o {
|
|
| 799 |
- c_stdout, err := container.StdoutPipe() |
|
| 800 |
- if err != nil {
|
|
| 801 |
- return err |
|
| 802 |
- } |
|
| 803 |
- wg.Add(1) |
|
| 804 |
- go func() { io.Copy(stdout, c_stdout); wg.Add(-1) }()
|
|
| 805 |
- } |
|
| 806 |
- if *fl_e {
|
|
| 807 |
- c_stderr, err := container.StderrPipe() |
|
| 808 |
- if err != nil {
|
|
| 809 |
- return err |
|
| 810 |
- } |
|
| 811 |
- wg.Add(1) |
|
| 812 |
- go func() { io.Copy(stdout, c_stderr); wg.Add(-1) }()
|
|
| 813 |
- } |
|
| 814 |
- wg.Wait() |
|
| 815 |
- return nil |
|
| 816 |
-} |
|
| 817 |
- |
|
| 818 |
-// Ports type - Used to parse multiple -p flags |
|
| 819 |
-type ports []int |
|
| 820 |
- |
|
| 821 |
-func (p *ports) String() string {
|
|
| 822 |
- return fmt.Sprint(*p) |
|
| 823 |
-} |
|
| 824 |
- |
|
| 825 |
-func (p *ports) Set(value string) error {
|
|
| 826 |
- port, err := strconv.Atoi(value) |
|
| 827 |
- if err != nil {
|
|
| 828 |
- return fmt.Errorf("Invalid port: %v", value)
|
|
| 829 |
- } |
|
| 830 |
- *p = append(*p, port) |
|
| 831 |
- return nil |
|
| 832 |
-} |
|
| 833 |
- |
|
| 834 |
-func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 835 |
- cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container") |
|
| 836 |
- fl_user := cmd.String("u", "", "Username or UID")
|
|
| 837 |
- fl_attach := cmd.Bool("a", false, "Attach stdin and stdout")
|
|
| 838 |
- fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached")
|
|
| 839 |
- fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty")
|
|
| 840 |
- fl_comment := cmd.String("c", "", "Comment")
|
|
| 841 |
- fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)")
|
|
| 842 |
- var fl_ports ports |
|
| 843 |
- cmd.Var(&fl_ports, "p", "Map a network port to the container") |
|
| 844 |
- if err := cmd.Parse(args); err != nil {
|
|
| 845 |
- return nil |
|
| 846 |
- } |
|
| 847 |
- name := cmd.Arg(0) |
|
| 848 |
- var cmdline []string |
|
| 849 |
- if len(cmd.Args()) >= 2 {
|
|
| 850 |
- cmdline = cmd.Args()[1:] |
|
| 851 |
- } |
|
| 852 |
- // Choose a default image if needed |
|
| 853 |
- if name == "" {
|
|
| 854 |
- name = "base" |
|
| 855 |
- } |
|
| 856 |
- // Choose a default command if needed |
|
| 857 |
- if len(cmdline) == 0 {
|
|
| 858 |
- *fl_stdin = true |
|
| 859 |
- *fl_tty = true |
|
| 860 |
- *fl_attach = true |
|
| 861 |
- cmdline = []string{"/bin/bash", "-i"}
|
|
| 862 |
- } |
|
| 863 |
- // Find the image |
|
| 864 |
- img, err := srv.images.Find(name) |
|
| 865 |
- if err != nil {
|
|
| 866 |
- return err |
|
| 867 |
- } else if img == nil {
|
|
| 868 |
- return errors.New("No such image: " + name)
|
|
| 869 |
- } |
|
| 870 |
- // Create new container |
|
| 871 |
- container, err := srv.CreateContainer(img, fl_ports, *fl_user, *fl_tty, |
|
| 872 |
- *fl_stdin, *fl_memory, *fl_comment, cmdline[0], cmdline[1:]...) |
|
| 873 |
- if err != nil {
|
|
| 874 |
- return errors.New("Error creating container: " + err.Error())
|
|
| 875 |
- } |
|
| 876 |
- if *fl_stdin {
|
|
| 877 |
- cmd_stdin, err := container.StdinPipe() |
|
| 878 |
- if err != nil {
|
|
| 879 |
- return err |
|
| 880 |
- } |
|
| 881 |
- if *fl_attach {
|
|
| 882 |
- future.Go(func() error {
|
|
| 883 |
- _, err := io.Copy(cmd_stdin, stdin) |
|
| 884 |
- cmd_stdin.Close() |
|
| 885 |
- return err |
|
| 886 |
- }) |
|
| 887 |
- } |
|
| 888 |
- } |
|
| 889 |
- // Run the container |
|
| 890 |
- if *fl_attach {
|
|
| 891 |
- cmd_stderr, err := container.StderrPipe() |
|
| 892 |
- if err != nil {
|
|
| 893 |
- return err |
|
| 894 |
- } |
|
| 895 |
- cmd_stdout, err := container.StdoutPipe() |
|
| 896 |
- if err != nil {
|
|
| 897 |
- return err |
|
| 898 |
- } |
|
| 899 |
- if err := container.Start(); err != nil {
|
|
| 900 |
- return err |
|
| 901 |
- } |
|
| 902 |
- sending_stdout := future.Go(func() error {
|
|
| 903 |
- _, err := io.Copy(stdout, cmd_stdout) |
|
| 904 |
- return err |
|
| 905 |
- }) |
|
| 906 |
- sending_stderr := future.Go(func() error {
|
|
| 907 |
- _, err := io.Copy(stdout, cmd_stderr) |
|
| 908 |
- return err |
|
| 909 |
- }) |
|
| 910 |
- err_sending_stdout := <-sending_stdout |
|
| 911 |
- err_sending_stderr := <-sending_stderr |
|
| 912 |
- if err_sending_stdout != nil {
|
|
| 913 |
- return err_sending_stdout |
|
| 914 |
- } |
|
| 915 |
- if err_sending_stderr != nil {
|
|
| 916 |
- return err_sending_stderr |
|
| 917 |
- } |
|
| 918 |
- container.Wait() |
|
| 919 |
- } else {
|
|
| 920 |
- if err := container.Start(); err != nil {
|
|
| 921 |
- return err |
|
| 922 |
- } |
|
| 923 |
- fmt.Fprintln(stdout, container.Id) |
|
| 924 |
- } |
|
| 925 |
- return nil |
|
| 926 |
-} |
|
| 927 |
- |
|
| 928 |
-func New() (*Server, error) {
|
|
| 929 |
- future.Seed() |
|
| 930 |
- // if err != nil {
|
|
| 931 |
- // return nil, err |
|
| 932 |
- // } |
|
| 933 |
- containers, err := docker.New() |
|
| 934 |
- if err != nil {
|
|
| 935 |
- return nil, err |
|
| 936 |
- } |
|
| 937 |
- srv := &Server{
|
|
| 938 |
- images: containers.Store, |
|
| 939 |
- containers: containers, |
|
| 940 |
- } |
|
| 941 |
- return srv, nil |
|
| 942 |
-} |
|
| 943 |
- |
|
| 944 |
-func (srv *Server) CmdMirror(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 945 |
- _, err := io.Copy(stdout, stdin) |
|
| 946 |
- return err |
|
| 947 |
-} |
|
| 948 |
- |
|
| 949 |
-func (srv *Server) CmdDebug(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 950 |
- for {
|
|
| 951 |
- if line, err := bufio.NewReader(stdin).ReadString('\n'); err == nil {
|
|
| 952 |
- fmt.Printf("--- %s", line)
|
|
| 953 |
- } else if err == io.EOF {
|
|
| 954 |
- if len(line) > 0 {
|
|
| 955 |
- fmt.Printf("--- %s\n", line)
|
|
| 956 |
- } |
|
| 957 |
- break |
|
| 958 |
- } else {
|
|
| 959 |
- return err |
|
| 960 |
- } |
|
| 961 |
- } |
|
| 962 |
- return nil |
|
| 963 |
-} |
|
| 964 |
- |
|
| 965 |
-func (srv *Server) CmdWeb(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
|
| 966 |
- cmd := rcli.Subcmd(stdout, "web", "[OPTIONS]", "A web UI for docker") |
|
| 967 |
- showurl := cmd.Bool("u", false, "Return the URL of the web UI")
|
|
| 968 |
- if err := cmd.Parse(args); err != nil {
|
|
| 969 |
- return nil |
|
| 970 |
- } |
|
| 971 |
- if *showurl {
|
|
| 972 |
- fmt.Fprintln(stdout, "http://localhost:4242/web") |
|
| 973 |
- } else {
|
|
| 974 |
- if file, err := os.Open("dockerweb.html"); err != nil {
|
|
| 975 |
- return err |
|
| 976 |
- } else if _, err := io.Copy(stdout, file); err != nil {
|
|
| 977 |
- return err |
|
| 978 |
- } |
|
| 979 |
- } |
|
| 980 |
- return nil |
|
| 981 |
-} |
|
| 982 |
- |
|
| 983 |
-type Server struct {
|
|
| 984 |
- containers *docker.Docker |
|
| 985 |
- images *fs.Store |
|
| 986 |
-} |
| 987 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,143 @@ |
| 0 |
+package term |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "syscall" |
|
| 4 |
+ "unsafe" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+type Termios struct {
|
|
| 8 |
+ Iflag uintptr |
|
| 9 |
+ Oflag uintptr |
|
| 10 |
+ Cflag uintptr |
|
| 11 |
+ Lflag uintptr |
|
| 12 |
+ Cc [20]byte |
|
| 13 |
+ Ispeed uintptr |
|
| 14 |
+ Ospeed uintptr |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+const ( |
|
| 18 |
+ // Input flags |
|
| 19 |
+ inpck = 0x010 |
|
| 20 |
+ istrip = 0x020 |
|
| 21 |
+ icrnl = 0x100 |
|
| 22 |
+ ixon = 0x200 |
|
| 23 |
+ |
|
| 24 |
+ // Output flags |
|
| 25 |
+ opost = 0x1 |
|
| 26 |
+ |
|
| 27 |
+ // Control flags |
|
| 28 |
+ cs8 = 0x300 |
|
| 29 |
+ |
|
| 30 |
+ // Local flags |
|
| 31 |
+ icanon = 0x100 |
|
| 32 |
+ iexten = 0x400 |
|
| 33 |
+) |
|
| 34 |
+ |
|
| 35 |
+const ( |
|
| 36 |
+ HUPCL = 0x4000 |
|
| 37 |
+ ICANON = 0x100 |
|
| 38 |
+ ICRNL = 0x100 |
|
| 39 |
+ IEXTEN = 0x400 |
|
| 40 |
+ BRKINT = 0x2 |
|
| 41 |
+ CFLUSH = 0xf |
|
| 42 |
+ CLOCAL = 0x8000 |
|
| 43 |
+ CREAD = 0x800 |
|
| 44 |
+ CS5 = 0x0 |
|
| 45 |
+ CS6 = 0x100 |
|
| 46 |
+ CS7 = 0x200 |
|
| 47 |
+ CS8 = 0x300 |
|
| 48 |
+ CSIZE = 0x300 |
|
| 49 |
+ CSTART = 0x11 |
|
| 50 |
+ CSTATUS = 0x14 |
|
| 51 |
+ CSTOP = 0x13 |
|
| 52 |
+ CSTOPB = 0x400 |
|
| 53 |
+ CSUSP = 0x1a |
|
| 54 |
+ IGNBRK = 0x1 |
|
| 55 |
+ IGNCR = 0x80 |
|
| 56 |
+ IGNPAR = 0x4 |
|
| 57 |
+ IMAXBEL = 0x2000 |
|
| 58 |
+ INLCR = 0x40 |
|
| 59 |
+ INPCK = 0x10 |
|
| 60 |
+ ISIG = 0x80 |
|
| 61 |
+ ISTRIP = 0x20 |
|
| 62 |
+ IUTF8 = 0x4000 |
|
| 63 |
+ IXANY = 0x800 |
|
| 64 |
+ IXOFF = 0x400 |
|
| 65 |
+ IXON = 0x200 |
|
| 66 |
+ NOFLSH = 0x80000000 |
|
| 67 |
+ OCRNL = 0x10 |
|
| 68 |
+ OFDEL = 0x20000 |
|
| 69 |
+ OFILL = 0x80 |
|
| 70 |
+ ONLCR = 0x2 |
|
| 71 |
+ ONLRET = 0x40 |
|
| 72 |
+ ONOCR = 0x20 |
|
| 73 |
+ ONOEOT = 0x8 |
|
| 74 |
+ OPOST = 0x1 |
|
| 75 |
+ RENB = 0x1000 |
|
| 76 |
+ PARMRK = 0x8 |
|
| 77 |
+ PARODD = 0x2000 |
|
| 78 |
+ |
|
| 79 |
+ TOSTOP = 0x400000 |
|
| 80 |
+ VDISCARD = 0xf |
|
| 81 |
+ VDSUSP = 0xb |
|
| 82 |
+ VEOF = 0x0 |
|
| 83 |
+ VEOL = 0x1 |
|
| 84 |
+ VEOL2 = 0x2 |
|
| 85 |
+ VERASE = 0x3 |
|
| 86 |
+ VINTR = 0x8 |
|
| 87 |
+ VKILL = 0x5 |
|
| 88 |
+ VLNEXT = 0xe |
|
| 89 |
+ VMIN = 0x10 |
|
| 90 |
+ VQUIT = 0x9 |
|
| 91 |
+ VREPRINT = 0x6 |
|
| 92 |
+ VSTART = 0xc |
|
| 93 |
+ VSTATUS = 0x12 |
|
| 94 |
+ VSTOP = 0xd |
|
| 95 |
+ VSUSP = 0xa |
|
| 96 |
+ VT0 = 0x0 |
|
| 97 |
+ VT1 = 0x10000 |
|
| 98 |
+ VTDLY = 0x10000 |
|
| 99 |
+ VTIME = 0x11 |
|
| 100 |
+ ECHO = 0x00000008 |
|
| 101 |
+ |
|
| 102 |
+ PENDIN = 0x20000000 |
|
| 103 |
+) |
|
| 104 |
+ |
|
| 105 |
+type State struct {
|
|
| 106 |
+ termios Termios |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+// IsTerminal returns true if the given file descriptor is a terminal. |
|
| 110 |
+func IsTerminal(fd int) bool {
|
|
| 111 |
+ var termios Termios |
|
| 112 |
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)), 0, 0, 0) |
|
| 113 |
+ return err == 0 |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+// MakeRaw put the terminal connected to the given file descriptor into raw |
|
| 117 |
+// mode and returns the previous state of the terminal so that it can be |
|
| 118 |
+// restored. |
|
| 119 |
+func MakeRaw(fd int) (*State, error) {
|
|
| 120 |
+ var oldState State |
|
| 121 |
+ if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 {
|
|
| 122 |
+ return nil, err |
|
| 123 |
+ } |
|
| 124 |
+ |
|
| 125 |
+ newState := oldState.termios |
|
| 126 |
+ newState.Iflag &^= ISTRIP | INLCR | IGNCR | IXON | IXOFF |
|
| 127 |
+ newState.Iflag |= ICRNL |
|
| 128 |
+ newState.Oflag |= ONLCR |
|
| 129 |
+ newState.Lflag &^= ECHO | ICANON | ISIG |
|
| 130 |
+ if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
|
| 131 |
+ return nil, err |
|
| 132 |
+ } |
|
| 133 |
+ |
|
| 134 |
+ return &oldState, nil |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+// Restore restores the terminal connected to the given file descriptor to a |
|
| 138 |
+// previous state. |
|
| 139 |
+func Restore(fd int, state *State) error {
|
|
| 140 |
+ _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) |
|
| 141 |
+ return err |
|
| 142 |
+} |