ソースを参照

added: info, history, logs, ps, start, stop, restart, rm, rmi

Victor Vieux 12 年 前
コミット
b295239de2
5 ファイル変更713 行追加38 行削除
  1. 315 8
      api.go
  2. 43 0
      api_params.go
  3. 1 1
      commands.go
  4. 351 25
      commands2.go
  5. 3 4
      container.go

+ 315 - 8
api.go

@@ -2,14 +2,18 @@ package docker
 
 import (
 	"encoding/json"
-	_ "fmt"
+	"fmt"
 	"github.com/gorilla/mux"
+	"io/ioutil"
 	"log"
 	"net/http"
+	"os"
+	"runtime"
+	"strings"
 	"time"
 )
 
-func ListenAndServe(addr string, runtime *Runtime) error {
+func ListenAndServe(addr string, rtime *Runtime) error {
 	r := mux.NewRouter()
 	log.Printf("Listening for HTTP on %s\n", addr)
 
@@ -32,7 +36,7 @@ func ListenAndServe(addr string, runtime *Runtime) error {
 
 		var ret SimpleMessage
 		for _, name := range ids {
-			container := runtime.Get(name)
+			container := rtime.Get(name)
 			if container == nil {
 				ret.Message = "No such container: " + name + "\n"
 				break
@@ -63,22 +67,22 @@ func ListenAndServe(addr string, runtime *Runtime) error {
 		var allImages map[string]*Image
 		var err error
 		if in.All {
-			allImages, err = runtime.graph.Map()
+			allImages, err = rtime.graph.Map()
 		} else {
-			allImages, err = runtime.graph.Heads()
+			allImages, err = rtime.graph.Heads()
 		}
 		if err != nil {
 			w.WriteHeader(500)
 			return
 		}
 		var outs []ImagesOut
-		for name, repository := range runtime.repositories.Repositories {
+		for name, repository := range rtime.repositories.Repositories {
 			if in.NameFilter != "" && name != in.NameFilter {
 				continue
 			}
 			for tag, id := range repository {
 				var out ImagesOut
-				image, err := runtime.graph.Get(id)
+				image, err := rtime.graph.Get(id)
 				if err != nil {
 					log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
 					continue
@@ -113,7 +117,310 @@ func ListenAndServe(addr string, runtime *Runtime) error {
 
 		b, err := json.Marshal(outs)
 		if err != nil {
-			w.WriteHeader(500)
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
+		}
+
+	})
+
+	r.Path("/info").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		images, _ := rtime.graph.All()
+		var imgcount int
+		if images == nil {
+			imgcount = 0
+		} else {
+			imgcount = len(images)
+		}
+		var out InfoOut
+		out.Containers = len(rtime.List())
+		out.Version = VERSION
+		out.Images = imgcount
+		if os.Getenv("DEBUG") == "1" {
+			out.Debug = true
+			out.NFd = getTotalUsedFds()
+			out.NGoroutines = runtime.NumGoroutine()
+		}
+		b, err := json.Marshal(out)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
+		}
+	})
+
+	r.Path("/history").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		log.Println(r.RequestURI)
+
+		var in HistoryIn
+		json.NewDecoder(r.Body).Decode(&in)
+
+		image, err := rtime.repositories.LookupImage(in.Name)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		var outs []HistoryOut
+		err = image.WalkHistory(func(img *Image) error {
+			var out HistoryOut
+			out.Id = rtime.repositories.ImageName(img.ShortId())
+			out.Created = HumanDuration(time.Now().Sub(img.Created)) + " ago"
+			out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
+			return nil
+		})
+
+		b, err := json.Marshal(outs)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
+		}
+
+	})
+
+	r.Path("/logs").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+		var in LogsIn
+		json.NewDecoder(r.Body).Decode(&in)
+
+		if container := rtime.Get(in.Name); container != nil {
+			var out LogsOut
+
+			logStdout, err := container.ReadLog("stdout")
+			if err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+			logStderr, err := container.ReadLog("stderr")
+			if err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+
+			stdout, errStdout := ioutil.ReadAll(logStdout)
+			if errStdout != nil {
+				http.Error(w, errStdout.Error(), http.StatusInternalServerError)
+				return
+			} else {
+				out.Stdout = fmt.Sprintf("%s", stdout)
+			}
+			stderr, errStderr := ioutil.ReadAll(logStderr)
+			if errStderr != nil {
+				http.Error(w, errStderr.Error(), http.StatusInternalServerError)
+				return
+			} else {
+				out.Stderr = fmt.Sprintf("%s", stderr)
+			}
+
+			b, err := json.Marshal(out)
+			if err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+			} else {
+				w.Write(b)
+			}
+
+		} else {
+			http.Error(w, "No such container: "+in.Name, http.StatusInternalServerError)
+		}
+	})
+
+	r.Path("/ps").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		var in PsIn
+		json.NewDecoder(r.Body).Decode(&in)
+
+		var outs []PsOut
+
+		for i, container := range rtime.List() {
+			if !container.State.Running && !in.All && in.Last == -1 {
+				continue
+			}
+			if i == in.Last {
+				break
+			}
+			var out PsOut
+			out.Id = container.ShortId()
+			if !in.Quiet {
+				command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
+				if !in.Full {
+					command = Trunc(command, 20)
+				}
+				out.Image = rtime.repositories.ImageName(container.Image)
+				out.Command = command
+				out.Created = HumanDuration(time.Now().Sub(container.Created)) + " ago"
+				out.Status = container.State.String()
+			}
+			outs = append(outs, out)
+		}
+
+		b, err := json.Marshal(outs)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
+		}
+
+	})
+
+	r.Path("/restart").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		var ins, outs []string
+		json.NewDecoder(r.Body).Decode(&ins)
+
+		for _, name := range ins {
+			if container := rtime.Get(name); container != nil {
+				if err := container.Restart(); err != nil {
+					http.Error(w, "Error restaring container "+name+": "+err.Error(), http.StatusInternalServerError)
+					return
+				}
+				outs = append(outs, container.ShortId())
+			} else {
+				http.Error(w, "No such container: "+name, http.StatusInternalServerError)
+				return
+			}
+		}
+		b, err := json.Marshal(outs)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
+		}
+	})
+
+	r.Path("/rm").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		var ins, outs []string
+		json.NewDecoder(r.Body).Decode(&ins)
+
+		for _, name := range ins {
+			if container := rtime.Get(name); container != nil {
+				if err := rtime.Destroy(container); err != nil {
+					http.Error(w, "Error destroying container "+name+": "+err.Error(), http.StatusInternalServerError)
+					return
+				}
+				outs = append(outs, container.ShortId())
+			} else {
+				http.Error(w, "No such container: "+name, http.StatusInternalServerError)
+				return
+			}
+		}
+		b, err := json.Marshal(outs)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
+		}
+
+	})
+
+	r.Path("/rmi").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		var ins, outs []string
+		json.NewDecoder(r.Body).Decode(&ins)
+
+		for _, name := range ins {
+			img, err := rtime.repositories.LookupImage(name)
+			if err != nil {
+				http.Error(w, "No such image: "+name, http.StatusInternalServerError)
+				return
+			} else {
+				if err := rtime.graph.Delete(img.Id); err != nil {
+					http.Error(w, "Error deleting image "+name+": "+err.Error(), http.StatusInternalServerError)
+					return
+				}
+				outs = append(outs, img.ShortId())
+			}
+		}
+		b, err := json.Marshal(outs)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
+		}
+
+	})
+
+	r.Path("/run").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+
+		var config Config
+		json.NewDecoder(r.Body).Decode(&config)
+
+		hj, ok := w.(http.Hijacker)
+		if !ok {
+			http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
+			return
+		}
+		conn, bufrw, err := hj.Hijack()
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+		defer conn.Close()
+
+		//TODO  config.Tty
+
+		// Create new container                                         
+		container, err := rtime.Create(&config)
+		if err != nil {
+			// If container not found, try to pull it                  
+			if rtime.graph.IsNotExist(err) {
+				bufrw.WriteString("Image " + config.Image + " not found, trying to pull it from registry.\r\n")
+				bufrw.Flush()
+				//TODO if err = srv.CmdPull(stdin, stdout, config.Image); err != nil {
+				//return err
+				//}
+				if container, err = rtime.Create(&config); err != nil {
+					http.Error(w, err.Error(), http.StatusInternalServerError)
+					return
+				}
+			} else {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+		}
+		container = container
+	})
+
+	r.Path("/start").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		var ins, outs []string
+		json.NewDecoder(r.Body).Decode(&ins)
+
+		for _, name := range ins {
+			if container := rtime.Get(name); container != nil {
+				if err := container.Start(); err != nil {
+					http.Error(w, "Error starting container "+name+": "+err.Error(), http.StatusInternalServerError)
+					return
+				}
+				outs = append(outs, container.ShortId())
+			} else {
+				http.Error(w, "No such container: "+name, http.StatusInternalServerError)
+				return
+			}
+		}
+		b, err := json.Marshal(outs)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
+		}
+
+	})
+
+	r.Path("/stop").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		var ins, outs []string
+		json.NewDecoder(r.Body).Decode(&ins)
+
+		for _, name := range ins {
+			if container := rtime.Get(name); container != nil {
+				if err := container.Stop(); err != nil {
+					http.Error(w, "Error stopping container "+name+": "+err.Error(), http.StatusInternalServerError)
+					return
+				}
+				outs = append(outs, container.ShortId())
+			} else {
+				http.Error(w, "No such container: "+name, http.StatusInternalServerError)
+				return
+			}
+		}
+		b, err := json.Marshal(outs)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
 		} else {
 			w.Write(b)
 		}

+ 43 - 0
api_params.go

@@ -4,6 +4,16 @@ type SimpleMessage struct {
 	Message string
 }
 
+type HistoryIn struct {
+	Name string
+}
+
+type HistoryOut struct {
+	Id        string
+	Created   string
+	CreatedBy string
+}
+
 type ImagesIn struct {
 	NameFilter string
 	Quiet      bool
@@ -17,6 +27,39 @@ type ImagesOut struct {
 	Created    string `json:",omitempty"`
 }
 
+type InfoOut struct {
+	Containers  int
+	Version     string
+	Images      int
+	Debug       bool
+	NFd         int `json:",omitempty"`
+	NGoroutines int `json:",omitempty"`
+}
+
+type PsIn struct {
+	Quiet bool
+	All   bool
+	Full  bool
+	Last  int
+}
+
+type PsOut struct {
+	Id      string
+	Image   string `json:",omitempty"`
+	Command string `json:",omitempty"`
+	Created string `json:",omitempty"`
+	Status  string `json:",omitempty"`
+}
+
+type LogsIn struct {
+	Name string
+}
+
+type LogsOut struct {
+	Stdout string
+	Stderr string
+}
+
 type VersionOut struct {
 	Version             string
 	GitCommit           string

+ 1 - 1
commands.go

@@ -906,7 +906,7 @@ func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string)
 }
 
 func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
-	config, err := ParseRun(args, stdout)
+	config, err := ParseRun(args)
 	if err != nil {
 		return err
 	}

+ 351 - 25
commands2.go

@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"flag"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"net/http"
 	"os"
@@ -15,7 +16,17 @@ func ParseCommands(args []string) error {
 
 	cmds := map[string]func(args []string) error{
 		"images":  cmdImages,
+		"info":    cmdInfo,
+		"history": cmdHistory,
 		"kill":    cmdKill,
+		"logs":    cmdLogs,
+		"ps":      cmdPs,
+		"restart": cmdRestart,
+		"rm":      cmdRm,
+		"rmi":     cmdRmi,
+		"run":     cmdRun,
+		"start":   cmdStart,
+		"stop":    cmdStop,
 		"version": cmdVersion,
 	}
 
@@ -37,24 +48,24 @@ func cmdHelp(args []string) error {
 		//		{"commit", "Create a new image from a container's changes"},
 		//		{"diff", "Inspect changes on a container's filesystem"},
 		//		{"export", "Stream the contents of a container as a tar archive"},
-		//		{"history", "Show the history of an image"},
+		{"history", "Show the history of an image"},
 		{"images", "List images"},
 		//		{"import", "Create a new filesystem image from the contents of a tarball"},
-		//		{"info", "Display system-wide information"},
+		{"info", "Display system-wide information"},
 		//		{"inspect", "Return low-level information on a container"},
 		{"kill", "Kill a running container"},
 		//		{"login", "Register or Login to the docker registry server"},
-		//		{"logs", "Fetch the logs of a container"},
+		{"logs", "Fetch the logs of a container"},
 		//		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
-		//		{"ps", "List containers"},
+		{"ps", "List containers"},
 		//		{"pull", "Pull an image or a repository from the docker registry server"},
 		//		{"push", "Push an image or a repository to the docker registry server"},
-		//		{"restart", "Restart a running container"},
-		//		{"rm", "Remove a container"},
-		//		{"rmi", "Remove an image"},
-		//		{"run", "Run a command in a new container"},
-		//		{"start", "Start a stopped container"},
-		//		{"stop", "Stop a running container"},
+		{"restart", "Restart a running container"},
+		{"rm", "Remove a container"},
+		{"rmi", "Remove an image"},
+		{"run", "Run a command in a new container"},
+		{"start", "Start a stopped container"},
+		{"stop", "Stop a running container"},
 		//		{"tag", "Tag an image into a repository"},
 		{"version", "Show the docker version information"},
 		//		{"wait", "Block until a container stops, then print its exit code"},
@@ -66,9 +77,10 @@ func cmdHelp(args []string) error {
 }
 
 func cmdImages(args []string) error {
-	cmd := subcmd("images", "[OPTIONS] [NAME]", "List images")
-	quiet := cmd.Bool("q", false, "only show numeric IDs")
-	flAll := cmd.Bool("a", false, "show all images")
+	cmd := Subcmd("images", "[OPTIONS] [NAME]", "List images")
+	var in ImagesIn
+	cmd.BoolVar(&in.Quiet, "q", false, "only show numeric IDs")
+	cmd.BoolVar(&in.All, "a", false, "show all images")
 	if err := cmd.Parse(args); err != nil {
 		return nil
 	}
@@ -76,13 +88,10 @@ func cmdImages(args []string) error {
 		cmd.Usage()
 		return nil
 	}
-	var nameFilter string
 	if cmd.NArg() == 1 {
-		nameFilter = cmd.Arg(0)
+		in.NameFilter = cmd.Arg(0)
 	}
 
-	in := ImagesIn{nameFilter, *quiet, *flAll}
-
 	body, err := apiPost("images", in)
 	if err != nil {
 		return err
@@ -94,27 +103,86 @@ func cmdImages(args []string) error {
 		return err
 	}
 	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
-	if !*quiet {
+	if !in.Quiet {
 		fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED")
 	}
 
 	for _, out := range outs {
-		if !*quiet {
+		if !in.Quiet {
 			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Repository, out.Tag, out.Id, out.Created)
 		} else {
 			fmt.Fprintln(w, out.Id)
 		}
 	}
 
-	if !*quiet {
+	if !in.Quiet {
 		w.Flush()
 	}
 	return nil
 
 }
 
+func cmdInfo(args []string) error {
+	cmd := Subcmd("info", "", "Display system-wide information")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() > 0 {
+		cmd.Usage()
+		return nil
+	}
+
+	body, err := apiGet("info")
+	if err != nil {
+		return err
+	}
+
+	var out InfoOut
+	err = json.Unmarshal(body, &out)
+	if err != nil {
+		return err
+	}
+	fmt.Printf("containers: %d\nversion: %s\nimages: %d\n", out.Containers, out.Version, out.Images)
+	if out.Debug {
+		fmt.Println("debug mode enabled")
+		fmt.Printf("fds: %d\ngoroutines: %d\n", out.NFd, out.NGoroutines)
+	}
+	return nil
+
+}
+
+func cmdHistory(args []string) error {
+	cmd := Subcmd("history", "IMAGE", "Show the history of an image")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() != 1 {
+		cmd.Usage()
+		return nil
+	}
+
+	body, err := apiPost("history", HistoryIn{cmd.Arg(0)})
+	if err != nil {
+		return err
+	}
+
+	var outs []HistoryOut
+	err = json.Unmarshal(body, &outs)
+	if err != nil {
+		return err
+	}
+	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
+	fmt.Fprintln(w, "ID\tCREATED\tCREATED BY")
+
+	for _, out := range outs {
+		fmt.Fprintf(w, "%s\t%s\t%s\n", out.Id, out.Created, out.CreatedBy)
+	}
+	w.Flush()
+	return nil
+}
+
 func cmdKill(args []string) error {
-	cmd := subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
+	cmd := Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container")
 	if err := cmd.Parse(args); err != nil {
 		return nil
 	}
@@ -137,7 +205,247 @@ func cmdKill(args []string) error {
 	return nil
 }
 
-func cmdVersion(_ []string) error {
+func cmdLogs(args []string) error {
+	cmd := Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() != 1 {
+		cmd.Usage()
+		return nil
+	}
+	body, err := apiPost("logs", LogsIn{cmd.Arg(0)})
+	if err != nil {
+		return err
+	}
+
+	var out LogsOut
+	err = json.Unmarshal(body, &out)
+	if err != nil {
+		return err
+	}
+	fmt.Fprintln(os.Stdout, out.Stdout)
+	fmt.Fprintln(os.Stderr, out.Stderr)
+
+	return nil
+}
+
+func cmdPs(args []string) error {
+	cmd := Subcmd("ps", "[OPTIONS]", "List containers")
+	var in PsIn
+	cmd.BoolVar(&in.Quiet, "q", false, "Only display numeric IDs")
+	cmd.BoolVar(&in.All, "a", false, "Show all containers. Only running containers are shown by default.")
+	cmd.BoolVar(&in.Full, "notrunc", false, "Don't truncate output")
+	nLatest := cmd.Bool("l", false, "Show only the latest created container, include non-running ones.")
+	cmd.IntVar(&in.Last, "n", -1, "Show n last created containers, include non-running ones.")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if in.Last == -1 && *nLatest {
+		in.Last = 1
+	}
+
+	body, err := apiPost("ps", in)
+	if err != nil {
+		return err
+	}
+
+	var outs []PsOut
+	err = json.Unmarshal(body, &outs)
+	if err != nil {
+		return err
+	}
+	w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
+	if !in.Quiet {
+		fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS")
+	}
+
+	for _, out := range outs {
+		if !in.Quiet {
+			fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Id, out.Image, out.Command, out.Status, out.Created)
+		} else {
+			fmt.Fprintln(w, out.Id)
+		}
+	}
+
+	if !in.Quiet {
+		w.Flush()
+	}
+	return nil
+}
+
+func cmdRestart(args []string) error {
+	cmd := Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() < 1 {
+		cmd.Usage()
+		return nil
+	}
+
+	body, err := apiPost("restart", cmd.Args())
+	if err != nil {
+		return err
+	}
+
+	var out []string
+	err = json.Unmarshal(body, &out)
+	if err != nil {
+		return err
+	}
+	for _, name := range out {
+		fmt.Println(name)
+	}
+	return nil
+}
+
+func cmdRm(args []string) error {
+	cmd := Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove a container")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() < 1 {
+		cmd.Usage()
+		return nil
+	}
+
+	body, err := apiPost("rm", cmd.Args())
+	if err != nil {
+		return err
+	}
+
+	var out []string
+	err = json.Unmarshal(body, &out)
+	if err != nil {
+		return err
+	}
+	for _, name := range out {
+		fmt.Println(name)
+	}
+	return nil
+}
+
+func cmdRmi(args []string) error {
+	cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() < 1 {
+		cmd.Usage()
+		return nil
+	}
+
+	body, err := apiPost("rmi", cmd.Args())
+	if err != nil {
+		return err
+	}
+
+	var out []string
+	err = json.Unmarshal(body, &out)
+	if err != nil {
+		return err
+	}
+	for _, name := range out {
+		fmt.Println(name)
+	}
+	return nil
+}
+
+func cmdRun(args []string) error {
+	config, err := ParseRun(args)
+	if err != nil {
+		return err
+	}
+	if config.Image == "" {
+		fmt.Println("Error: Image not specified")
+		return fmt.Errorf("Image not specified")
+	}
+	if len(config.Cmd) == 0 {
+		fmt.Println("Error: Command not specified")
+		return fmt.Errorf("Command not specified")
+	}
+
+	body, err := apiPostHijack("run", config)
+	if err != nil {
+		return err
+	}
+	defer body.Close()
+
+	/*str, err2 := ioutil.ReadAll(body)
+	if err2 != nil {
+		return err2
+	}
+	fmt.Println(str)*/
+	return nil
+
+}
+
+func cmdStart(args []string) error {
+	cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() < 1 {
+		cmd.Usage()
+		return nil
+	}
+
+	body, err := apiPost("start", cmd.Args())
+	if err != nil {
+		return err
+	}
+
+	var out []string
+	err = json.Unmarshal(body, &out)
+	if err != nil {
+		return err
+	}
+	for _, name := range out {
+		fmt.Println(name)
+	}
+
+	return nil
+
+}
+
+func cmdStop(args []string) error {
+	cmd := Subcmd("stop", "CONTAINER [CONTAINER...]", "Restart a running container")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() < 1 {
+		cmd.Usage()
+		return nil
+	}
+
+	body, err := apiPost("stop", cmd.Args())
+	if err != nil {
+		return err
+	}
+
+	var out []string
+	err = json.Unmarshal(body, &out)
+	if err != nil {
+		return err
+	}
+	for _, name := range out {
+		fmt.Println(name)
+	}
+	return nil
+
+}
+
+func cmdVersion(args []string) error {
+	cmd := Subcmd("version", "", "Show the docker version information.")
+	if err := cmd.Parse(args); err != nil {
+		return nil
+	}
+	if cmd.NArg() > 0 {
+		cmd.Usage()
+		return nil
+	}
+
 	body, err := apiGet("version")
 	if err != nil {
 		return err
@@ -162,12 +470,14 @@ func apiGet(path string) ([]byte, error) {
 	if err != nil {
 		return nil, err
 	}
-	//TODO check status code
 	defer resp.Body.Close()
 	body, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
 		return nil, err
 	}
+	if resp.StatusCode != 200 {
+		return nil, fmt.Errorf("error: %s", body)
+	}
 	return body, nil
 
 }
@@ -182,17 +492,33 @@ func apiPost(path string, data interface{}) ([]byte, error) {
 	if err != nil {
 		return nil, err
 	}
-	//TODO check status code
 	defer resp.Body.Close()
 	body, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
 		return nil, err
 	}
+	if resp.StatusCode != 200 {
+		return nil, fmt.Errorf("[error] %s", body)
+	}
 	return body, nil
+}
+
+func apiPostHijack(path string, data interface{}) (io.ReadCloser, error) {
+	buf, err := json.Marshal(data)
+	if err != nil {
+		return nil, err
+	}
+	dataBuf := bytes.NewBuffer(buf)
+	resp, err := http.Post("http://0.0.0.0:4243/"+path, "application/json", dataBuf)
+	if err != nil {
+		return nil, err
+	}
+	//TODO check status code
 
+	return resp.Body, nil
 }
 
-func subcmd(name, signature, description string) *flag.FlagSet {
+func Subcmd(name, signature, description string) *flag.FlagSet {
 	flags := flag.NewFlagSet(name, flag.ContinueOnError)
 	flags.Usage = func() {
 		fmt.Printf("\nUsage: docker %s %s\n\n%s\n\n", name, signature, description)

+ 3 - 4
container.go

@@ -3,7 +3,6 @@ package docker
 import (
 	"encoding/json"
 	"fmt"
-	"github.com/dotcloud/docker/rcli"
 	"github.com/kr/pty"
 	"io"
 	"io/ioutil"
@@ -66,8 +65,8 @@ type Config struct {
 	Image        string // Name of the image as it was passed by the operator (eg. could be symbolic)
 }
 
-func ParseRun(args []string, stdout io.Writer) (*Config, error) {
-	cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
+func ParseRun(args []string) (*Config, error) {
+	cmd := Subcmd("run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container")
 	if len(args) > 0 && args[0] != "--help" {
 		cmd.SetOutput(ioutil.Discard)
 	}
@@ -82,7 +81,7 @@ func ParseRun(args []string, stdout io.Writer) (*Config, error) {
 	flMemory := cmd.Int64("m", 0, "Memory limit (in bytes)")
 
 	if *flMemory > 0 && NO_MEMORY_LIMIT {
-		fmt.Fprintf(stdout, "WARNING: This version of docker has been compiled without memory limit support. Discarding -m.")
+		fmt.Println("WARNING: This version of docker has been compiled without memory limit support. Discarding -m.")
 		*flMemory = 0
 	}