Przeglądaj źródła

split api and server. run return exit code. import, pull and commit uses the smae endpoint. non zero status code on failure

Victor Vieux 12 lat temu
rodzic
commit
04cd20fa62
7 zmienionych plików z 718 dodań i 571 usunięć
  1. 1 1
      Makefile
  2. 172 474
      api.go
  3. 84 40
      commands.go
  4. 6 3
      commands_test.go
  5. 5 51
      docker/docker.go
  6. 2 2
      runtime_test.go
  7. 448 0
      server.go

+ 1 - 1
Makefile

@@ -38,7 +38,7 @@ $(DOCKER_BIN): $(DOCKER_DIR)
 
 
 $(DOCKER_DIR):
 $(DOCKER_DIR):
 	@mkdir -p $(dir $@)
 	@mkdir -p $(dir $@)
-	@if [ -h $@ ]; then rm -f $@; ln -sf $(CURDIR)/ $@; fi
+	@if [ -h $@ ]; then rm -f $@; fi; ln -sf $(CURDIR)/ $@
 	@(cd $(DOCKER_MAIN); go get $(GO_OPTIONS))
 	@(cd $(DOCKER_MAIN); go get $(GO_OPTIONS))
 
 
 whichrelease:
 whichrelease:

+ 172 - 474
api.go

@@ -5,24 +5,46 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
-	"io"
 	"log"
 	"log"
 	"net"
 	"net"
 	"net/http"
 	"net/http"
-	"net/url"
 	"os"
 	"os"
-	"runtime"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 )
 )
 
 
-func ListenAndServe(addr string, rtime *Runtime) error {
+func hijackServer(w http.ResponseWriter) (*os.File, net.Conn, error) {
+	rwc, _, err := w.(http.Hijacker).Hijack()
+	if err != nil {
+		return nil, nil, err
+	}
+
+	file, err := rwc.(*net.TCPConn).File()
+	if err != nil {
+		return nil, rwc, err
+	}
+
+	// Flush the options to make sure the client sets the raw mode
+	rwc.Write([]byte{})
+
+	return file, rwc, nil
+}
+
+func httpError(w http.ResponseWriter, err error) {
+	if strings.HasPrefix(err.Error(), "No such") {
+		http.Error(w, err.Error(), http.StatusNotFound)
+	} else {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+	}
+}
+
+func ListenAndServe(addr string, srv *Server) error {
 	r := mux.NewRouter()
 	r := mux.NewRouter()
 	log.Printf("Listening for HTTP on %s\n", addr)
 	log.Printf("Listening for HTTP on %s\n", addr)
 
 
 	r.Path("/version").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/version").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
-		m := ApiVersion{VERSION, GIT_COMMIT, rtime.capabilities.MemoryLimit, rtime.capabilities.SwapLimit}
+		m := srv.DockerVersion()
 		b, err := json.Marshal(m)
 		b, err := json.Marshal(m)
 		if err != nil {
 		if err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -35,16 +57,11 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-		if container := rtime.Get(name); container != nil {
-			if err := container.Kill(); err != nil {
-				http.Error(w, "Error restarting container "+name+": "+err.Error(), http.StatusInternalServerError)
-				return
-			}
+		if err := srv.ContainerKill(name); err != nil {
+			httpError(w, err)
 		} else {
 		} else {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
-			return
+			w.WriteHeader(http.StatusOK)
 		}
 		}
-		w.WriteHeader(http.StatusOK)
 	})
 	})
 
 
 	r.Path("/containers/{name:.*}/export").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/containers/{name:.*}/export").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -52,36 +69,21 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
 
 
-		if container := rtime.Get(name); container != nil {
-
-			data, err := container.Export()
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-				return
-			}
-			conn, _, err := w.(http.Hijacker).Hijack()
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-				return
-			}
-			defer conn.Close()
-			file, err := conn.(*net.TCPConn).File()
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-				return
-			}
+		file, rwc, err := hijackServer(w)
+		if file != nil {
 			defer file.Close()
 			defer file.Close()
-
-			fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
-			// Stream the entire contents of the container (basically a volatile snapshot)
-			if _, err := io.Copy(file, data); err != nil {
-				fmt.Fprintln(file, "Error: "+err.Error())
-				return
-			}
-		} else {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
 		}
 		}
-
+		if rwc != nil {
+			defer rwc.Close()
+		}
+		if err != nil {
+			httpError(w, err)
+			return
+		}
+		fmt.Fprintf(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
+		if err := srv.ContainerExport(name, file); err != nil {
+			fmt.Fprintln(file, "Error: "+err.Error())
+		}
 	})
 	})
 
 
 	r.Path("/images").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/images").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -89,61 +91,14 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		if err := r.ParseForm(); err != nil {
 		if err := r.ParseForm(); err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 		}
 		}
-		All := r.Form.Get("all")
-		NameFilter := r.Form.Get("filter")
-		Quiet := r.Form.Get("quiet")
+		all := r.Form.Get("all")
+		filter := r.Form.Get("filter")
+		quiet := r.Form.Get("quiet")
 
 
-		var allImages map[string]*Image
-		var err error
-		if All == "1" {
-			allImages, err = rtime.graph.Map()
-		} else {
-			allImages, err = rtime.graph.Heads()
-		}
+		outs, err := srv.Images(all, filter, quiet)
 		if err != nil {
 		if err != nil {
-			w.WriteHeader(500)
-			return
-		}
-		var outs []ApiImages = []ApiImages{} //produce [] when empty instead of 'null'
-		for name, repository := range rtime.repositories.Repositories {
-			if NameFilter != "" && name != NameFilter {
-				continue
-			}
-			for tag, id := range repository {
-				var out ApiImages
-				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
-				}
-				delete(allImages, id)
-				if Quiet != "1" {
-					out.Repository = name
-					out.Tag = tag
-					out.Id = TruncateId(id)
-					out.Created = image.Created.Unix()
-				} else {
-					out.Id = image.ShortId()
-				}
-				outs = append(outs, out)
-			}
-		}
-		// Display images which aren't part of a
-		if NameFilter == "" {
-			for id, image := range allImages {
-				var out ApiImages
-				if Quiet != "1" {
-					out.Repository = "<none>"
-					out.Tag = "<none>"
-					out.Id = TruncateId(id)
-					out.Created = image.Created.Unix()
-				} else {
-					out.Id = image.ShortId()
-				}
-				outs = append(outs, out)
-			}
+			httpError(w, err)
 		}
 		}
-
 		b, err := json.Marshal(outs)
 		b, err := json.Marshal(outs)
 		if err != nil {
 		if err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -154,22 +109,7 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 
 
 	r.Path("/info").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/info").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
-		images, _ := rtime.graph.All()
-		var imgcount int
-		if images == nil {
-			imgcount = 0
-		} else {
-			imgcount = len(images)
-		}
-		var out ApiInfo
-		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()
-		}
+		out := srv.DockerInfo()
 		b, err := json.Marshal(out)
 		b, err := json.Marshal(out)
 		if err != nil {
 		if err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -182,22 +122,10 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-
-		image, err := rtime.repositories.LookupImage(name)
+		outs, err := srv.ImageHistory(name)
 		if err != nil {
 		if err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
+			httpError(w, err)
 		}
 		}
-
-		var outs []ApiHistory = []ApiHistory{} //produce [] when empty instead of 'null'
-		err = image.WalkHistory(func(img *Image) error {
-			var out ApiHistory
-			out.Id = rtime.repositories.ImageName(img.ShortId())
-			out.Created = img.Created.Unix()
-			out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
-			return nil
-		})
-
 		b, err := json.Marshal(outs)
 		b, err := json.Marshal(outs)
 		if err != nil {
 		if err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -210,25 +138,15 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-
-		if container := rtime.Get(name); container != nil {
-			changes, err := container.Changes()
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-				return
-			}
-			var changesStr []string
-			for _, name := range changes {
-				changesStr = append(changesStr, name.String())
-			}
-			b, err := json.Marshal(changesStr)
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-			} else {
-				w.Write(b)
-			}
+		changesStr, err := srv.ContainerChanges(name)
+		if err != nil {
+			httpError(w, err)
+		}
+		b, err := json.Marshal(changesStr)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
 		} else {
 		} else {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
+			w.Write(b)
 		}
 		}
 	})
 	})
 
 
@@ -237,99 +155,36 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		if err := r.ParseForm(); err != nil {
 		if err := r.ParseForm(); err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 		}
 		}
-		privatePort := r.Form.Get("port")
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-
-		if container := rtime.Get(name); container == nil {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
-			return
-		} else {
-			if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; !exists {
-				http.Error(w, "No private port '"+privatePort+"' allocated on "+name, http.StatusInternalServerError)
-				return
-			} else {
-				b, err := json.Marshal(ApiPort{frontend})
-				if err != nil {
-					http.Error(w, err.Error(), http.StatusInternalServerError)
-				} else {
-					w.Write(b)
-				}
-			}
-		}
-	})
-
-	r.Path("/containers").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		log.Println(r.Method, r.RequestURI)
-		if err := r.ParseForm(); err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-		}
-		All := r.Form.Get("all")
-		NoTrunc := r.Form.Get("notrunc")
-		Quiet := r.Form.Get("quiet")
-		Last := r.Form.Get("n")
-		n, err := strconv.Atoi(Last)
+		out, err := srv.ContainerPort(name, r.Form.Get("port"))
 		if err != nil {
 		if err != nil {
-			n = -1
-		}
-		var outs []ApiContainers = []ApiContainers{} //produce [] when empty instead of 'null'
-		for i, container := range rtime.List() {
-			if !container.State.Running && All != "1" && n == -1 {
-				continue
-			}
-			if i == n {
-				break
-			}
-			var out ApiContainers
-			out.Id = container.ShortId()
-			if Quiet != "1" {
-				command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
-				if NoTrunc != "1" {
-					command = Trunc(command, 20)
-				}
-				out.Image = rtime.repositories.ImageName(container.Image)
-				out.Command = command
-				out.Created = container.Created.Unix()
-				out.Status = container.State.String()
-				out.Ports = container.NetworkSettings.PortMappingHuman()
-			}
-			outs = append(outs, out)
+			httpError(w, err)
 		}
 		}
-
-		b, err := json.Marshal(outs)
+		b, err := json.Marshal(ApiPort{out})
 		if err != nil {
 		if err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 		} else {
 		} else {
 			w.Write(b)
 			w.Write(b)
 		}
 		}
+
 	})
 	})
 
 
-	r.Path("/containers/{name:.*}/commit").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+	r.Path("/containers").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
-		var config Config
-		if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
-		}
-
 		if err := r.ParseForm(); err != nil {
 		if err := r.ParseForm(); err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
 		}
 		}
-		vars := mux.Vars(r)
-		name := vars["name"]
-		repo := r.Form.Get("repo")
-		tag := r.Form.Get("tag")
-		author := r.Form.Get("author")
-		comment := r.Form.Get("comment")
-
-		img, err := rtime.Commit(name, repo, tag, comment, author, &config)
+		all := r.Form.Get("all")
+		notrunc := r.Form.Get("notrunc")
+		quiet := r.Form.Get("quiet")
+		n, err := strconv.Atoi(r.Form.Get("n"))
 		if err != nil {
 		if err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
+			n = -1
 		}
 		}
 
 
-		b, err := json.Marshal(ApiId{img.ShortId()})
+		outs := srv.Containers(all, notrunc, quiet, n)
+		b, err := json.Marshal(outs)
 		if err != nil {
 		if err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 		} else {
 		} else {
@@ -342,121 +197,79 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		if err := r.ParseForm(); err != nil {
 		if err := r.ParseForm(); err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 		}
 		}
-		vars := mux.Vars(r)
-		name := vars["name"]
 		repo := r.Form.Get("repo")
 		repo := r.Form.Get("repo")
 		tag := r.Form.Get("tag")
 		tag := r.Form.Get("tag")
+		vars := mux.Vars(r)
+		name := vars["name"]
 		var force bool
 		var force bool
 		if r.Form.Get("force") == "1" {
 		if r.Form.Get("force") == "1" {
 			force = true
 			force = true
 		}
 		}
 
 
-		if err := rtime.repositories.Set(repo, tag, name, force); err != nil {
+		if err := srv.ContainerTag(name, repo, tag, force); err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			return
 			return
 		}
 		}
 		w.WriteHeader(http.StatusCreated)
 		w.WriteHeader(http.StatusCreated)
 	})
 	})
 
 
-	r.Path("/images/{name:.*}/pull").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		log.Println(r.Method, r.RequestURI)
-		vars := mux.Vars(r)
-		name := vars["name"]
-
-		conn, _, err := w.(http.Hijacker).Hijack()
-		if err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
-		}
-		defer conn.Close()
-		file, err := conn.(*net.TCPConn).File()
-		if err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
-		}
-		defer file.Close()
-
-		fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
-		if rtime.graph.LookupRemoteImage(name, rtime.authConfig) {
-			if err := rtime.graph.PullImage(file, name, rtime.authConfig); err != nil {
-				fmt.Fprintln(file, "Error: "+err.Error())
-			}
-			return
-		}
-		if err := rtime.graph.PullRepository(file, name, "", rtime.repositories, rtime.authConfig); err != nil {
-			fmt.Fprintln(file, "Error: "+err.Error())
-		}
-	})
-
-	/* /!\ W.I.P /!\ */
 	r.Path("/images").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/images").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
 		if err := r.ParseForm(); err != nil {
 		if err := r.ParseForm(); err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 		}
 		}
-		src := r.Form.Get("src")
+
+		src := r.Form.Get("fromSrc")
+		image := r.Form.Get("fromImage")
+		container := r.Form.Get("fromContainer")
 		repo := r.Form.Get("repo")
 		repo := r.Form.Get("repo")
 		tag := r.Form.Get("tag")
 		tag := r.Form.Get("tag")
 
 
-		var archive io.Reader
-		var resp *http.Response
+		if container != "" { //commit
+			var config Config
+			if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+			author := r.Form.Get("author")
+			comment := r.Form.Get("comment")
 
 
-		conn, _, err := w.(http.Hijacker).Hijack()
-		if err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
-		}
-		defer conn.Close()
-		file, err := conn.(*net.TCPConn).File()
-		if err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
-		}
-		defer file.Close()
-
-		fmt.Fprintln(file, "HTTP/1.1 201 Created\r\nContent-Type: application/json\r\n\r\n")
-		if src == "-" {
-			r, w := io.Pipe()
-			go func() {
-				defer w.Close()
-				defer Debugf("Closing buffered stdin pipe")
-				io.Copy(w, file)
-			}()
-			archive = r
-		} else {
-			u, err := url.Parse(src)
+			id, err := srv.ContainerCommit(container, repo, tag, author, comment, &config)
 			if err != nil {
 			if err != nil {
-				fmt.Fprintln(file, "Error: "+err.Error())
+				httpError(w, err)
 			}
 			}
-			if u.Scheme == "" {
-				u.Scheme = "http"
-				u.Host = src
-				u.Path = ""
-			}
-			fmt.Fprintln(file, "Downloading from", u)
-			// Download with curl (pretty progress bar)
-			// If curl is not available, fallback to http.Get()
-			resp, err = Download(u.String(), file)
+			b, err := json.Marshal(ApiId{id})
 			if err != nil {
 			if err != nil {
 				http.Error(w, err.Error(), http.StatusInternalServerError)
 				http.Error(w, err.Error(), http.StatusInternalServerError)
-				return
+			} else {
+				w.Write(b)
 			}
 			}
-			archive = ProgressReader(resp.Body, int(resp.ContentLength), file, "Importing %v/%v (%v)")
-		}
-		img, err := rtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
-		if err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
-		}
-		// Optionally register the image at REPO/TAG
-		if repo != "" {
-			if err := rtime.repositories.Set(repo, tag, img.Id, true); err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else if image != "" || src != "" {
+			file, rwc, err := hijackServer(w)
+			if file != nil {
+				defer file.Close()
+			}
+			if rwc != nil {
+				defer rwc.Close()
+			}
+			if err != nil {
+				httpError(w, err)
 				return
 				return
 			}
 			}
-		}
+			fmt.Fprintf(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
 
 
-		fmt.Fprintln(file, img.ShortId())
+			if image != "" { //pull
+				if err := srv.ImagePull(image, file); err != nil {
+					fmt.Fprintln(file, "Error: "+err.Error())
+				}
+			} else { //import
+				if err := srv.ImageImport(src, repo, tag, file); err != nil {
+					fmt.Fprintln(file, "Error: "+err.Error())
+				}
+			}
+		} else {
+			w.WriteHeader(http.StatusNotFound)
+		}
 	})
 	})
 
 
 	r.Path("/containers").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/containers").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -466,37 +279,19 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			return
 			return
 		}
 		}
-		var memoryW, swapW bool
-
-		if config.Memory > 0 && !rtime.capabilities.MemoryLimit {
-			memoryW = true
-			log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
-			config.Memory = 0
-		}
-
-		if config.Memory > 0 && !rtime.capabilities.SwapLimit {
-			swapW = true
-			log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.")
-			config.MemorySwap = -1
-		}
-		container, err := rtime.Create(&config)
+		id, memoryW, swapW, err := srv.ContainerCreate(config)
 		if err != nil {
 		if err != nil {
-			if rtime.graph.IsNotExist(err) {
-				http.Error(w, "No such image: "+config.Image, http.StatusNotFound)
-			} else {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-			}
+			httpError(w, err)
 			return
 			return
 		}
 		}
 		var out ApiRun
 		var out ApiRun
-		out.Id = container.ShortId()
+		out.Id = id
 		if memoryW {
 		if memoryW {
 			out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.")
 			out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.")
 		}
 		}
 		if swapW {
 		if swapW {
 			out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.")
 			out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.")
 		}
 		}
-
 		b, err := json.Marshal(out)
 		b, err := json.Marshal(out)
 		if err != nil {
 		if err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -516,66 +311,44 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		}
 		}
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-		if container := rtime.Get(name); container != nil {
-			if err := container.Restart(t); err != nil {
-				http.Error(w, "Error restarting container "+name+": "+err.Error(), http.StatusInternalServerError)
-				return
-			}
+		if err := srv.ContainerRestart(name, t); err != nil {
+			httpError(w, err)
 		} else {
 		} else {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
-			return
+			w.WriteHeader(http.StatusOK)
 		}
 		}
-		w.WriteHeader(http.StatusOK)
 	})
 	})
 
 
 	r.Path("/containers/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/containers/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-		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
-			}
+		if err := srv.ContainerDestroy(name); err != nil {
+			httpError(w, err)
 		} else {
 		} else {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
-			return
+			w.WriteHeader(http.StatusOK)
 		}
 		}
-		w.WriteHeader(http.StatusOK)
 	})
 	})
 
 
 	r.Path("/images/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/images/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-
-		img, err := rtime.repositories.LookupImage(name)
-		if err != nil {
-			http.Error(w, "No such image: "+name, http.StatusNotFound)
-			return
+		if err := srv.ImageDelete(name); err != nil {
+			httpError(w, err)
 		} else {
 		} else {
-			if err := rtime.graph.Delete(img.Id); err != nil {
-				http.Error(w, "Error deleting image "+name+": "+err.Error(), http.StatusInternalServerError)
-				return
-			}
+			w.WriteHeader(http.StatusOK)
 		}
 		}
-		w.WriteHeader(http.StatusOK)
 	})
 	})
 
 
 	r.Path("/containers/{name:.*}/start").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/containers/{name:.*}/start").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-		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
-			}
+		if err := srv.ContainerStart(name); err != nil {
+			httpError(w, err)
 		} else {
 		} else {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
-			return
+			w.WriteHeader(http.StatusOK)
 		}
 		}
-		w.WriteHeader(http.StatusOK)
 	})
 	})
 
 
 	r.Path("/containers/{name:.*}/stop").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/containers/{name:.*}/stop").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -589,33 +362,27 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		}
 		}
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-		if container := rtime.Get(name); container != nil {
-			if err := container.Stop(t); err != nil {
-				http.Error(w, "Error stopping container "+name+": "+err.Error(), http.StatusInternalServerError)
-				return
-			}
+
+		if err := srv.ContainerStop(name, t); err != nil {
+			httpError(w, err)
 		} else {
 		} else {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
-			return
+			w.WriteHeader(http.StatusOK)
 		}
 		}
-		w.WriteHeader(http.StatusOK)
 	})
 	})
 
 
 	r.Path("/containers/{name:.*}/wait").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/containers/{name:.*}/wait").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		log.Println(r.Method, r.RequestURI)
 		log.Println(r.Method, r.RequestURI)
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
-		if container := rtime.Get(name); container != nil {
-			b, err := json.Marshal(ApiWait{container.Wait()})
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-			} else {
-				w.Write(b)
-			}
-			return
+		status, err := srv.ContainerWait(name)
+		if err != nil {
+			httpError(w, err)
+		}
+		b, err := json.Marshal(ApiWait{status})
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
 		} else {
 		} else {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
-			return
+			w.Write(b)
 		}
 		}
 	})
 	})
 
 
@@ -632,90 +399,21 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
 
 
-		if container := rtime.Get(name); container != nil {
-			conn, _, err := w.(http.Hijacker).Hijack()
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-				return
-			}
-			defer conn.Close()
-			file, err := conn.(*net.TCPConn).File()
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-				return
-			}
+		file, rwc, err := hijackServer(w)
+		if file != nil {
 			defer file.Close()
 			defer file.Close()
+		}
+		if rwc != nil {
+			defer rwc.Close()
+		}
+		if err != nil {
+			httpError(w, err)
+			return
+		}
 
 
-			// Flush the options to make sure the client sets the raw mode
-			conn.Write([]byte{})
-
-			fmt.Fprintln(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
-			//logs
-			if logs == "1" {
-				if stdout == "1" {
-					cLog, err := container.ReadLog("stdout")
-					if err != nil {
-						Debugf(err.Error())
-					} else if _, err := io.Copy(file, cLog); err != nil {
-						Debugf(err.Error())
-					}
-				}
-				if stderr == "1" {
-					cLog, err := container.ReadLog("stderr")
-					if err != nil {
-						Debugf(err.Error())
-					} else if _, err := io.Copy(file, cLog); err != nil {
-						Debugf(err.Error())
-					}
-				}
-			}
-
-			//stream
-			if stream == "1" {
-
-				if container.State.Ghost {
-					fmt.Fprintf(file, "error: Impossible to attach to a ghost container")
-					return
-				}
-
-				if container.Config.Tty {
-					oldState, err := SetRawTerminal()
-					if err != nil {
-						if os.Getenv("DEBUG") != "" {
-							log.Printf("Can't set the terminal in raw mode: %s", err)
-						}
-					} else {
-						defer RestoreTerminal(oldState)
-					}
-
-				}
-				var (
-					cStdin           io.ReadCloser
-					cStdout, cStderr io.Writer
-					cStdinCloser     io.Closer
-				)
-
-				if stdin == "1" {
-					r, w := io.Pipe()
-					go func() {
-						defer w.Close()
-						defer Debugf("Closing buffered stdin pipe")
-						io.Copy(w, file)
-					}()
-					cStdin = r
-					cStdinCloser = file
-				}
-				if stdout == "1" {
-					cStdout = file
-				}
-				if stderr == "1" {
-					cStderr = file
-				}
-
-				<-container.Attach(cStdin, cStdinCloser, cStdout, cStderr)
-			}
-		} else {
-			http.Error(w, "No such container: "+name, http.StatusNotFound)
+		fmt.Fprintf(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
+		if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, file); err != nil {
+			fmt.Fprintln(file, "Error: "+err.Error())
 		}
 		}
 	})
 	})
 
 
@@ -724,16 +422,16 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
 
 
-		if container := rtime.Get(name); container != nil {
-			b, err := json.Marshal(container)
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-			} else {
-				w.Write(b)
-			}
-			return
+		container, err := srv.ContainerInspect(name)
+		if err != nil {
+			httpError(w, err)
+		}
+		b, err := json.Marshal(container)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
 		}
 		}
-		http.Error(w, "No such container: "+name, http.StatusNotFound)
 	})
 	})
 
 
 	r.Path("/images/{name:.*}").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 	r.Path("/images/{name:.*}").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -741,16 +439,16 @@ func ListenAndServe(addr string, rtime *Runtime) error {
 		vars := mux.Vars(r)
 		vars := mux.Vars(r)
 		name := vars["name"]
 		name := vars["name"]
 
 
-		if image, err := rtime.repositories.LookupImage(name); err == nil && image != nil {
-			b, err := json.Marshal(image)
-			if err != nil {
-				http.Error(w, err.Error(), http.StatusInternalServerError)
-			} else {
-				w.Write(b)
-			}
-			return
+		image, err := srv.ImageInspect(name)
+		if err != nil {
+			httpError(w, err)
+		}
+		b, err := json.Marshal(image)
+		if err != nil {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		} else {
+			w.Write(b)
 		}
 		}
-		http.Error(w, "No such image: "+name, http.StatusNotFound)
 	})
 	})
 
 
 	return http.ListenAndServe(addr, r)
 	return http.ListenAndServe(addr, r)

+ 84 - 40
commands.go

@@ -24,9 +24,30 @@ var (
 	GIT_COMMIT string
 	GIT_COMMIT string
 )
 )
 
 
-func ParseCommands(args []string) error {
+func checkRemoteVersion() error {
+	body, _, err := call("GET", "/version", nil)
+	if err != nil {
+		return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
+	}
+
+	var out ApiVersion
+	err = json.Unmarshal(body, &out)
+	if err != nil {
+		return err
+	}
+	if out.Version != VERSION {
+		fmt.Fprintf(os.Stderr, "Warning: client and server don't have the same version (client: %s, server: %)", VERSION, out.Version)
+	}
+	return nil
+}
+
+func ParseCommands(args ...string) error {
+
+	if err := checkRemoteVersion(); err != nil {
+		return err
+	}
 
 
-	cmds := map[string]func(args []string) error{
+	cmds := map[string]func(args ...string) error{
 		"attach":  CmdAttach,
 		"attach":  CmdAttach,
 		"commit":  CmdCommit,
 		"commit":  CmdCommit,
 		"diff":    CmdDiff,
 		"diff":    CmdDiff,
@@ -34,7 +55,7 @@ func ParseCommands(args []string) error {
 		"images":  CmdImages,
 		"images":  CmdImages,
 		"info":    CmdInfo,
 		"info":    CmdInfo,
 		"inspect": CmdInspect,
 		"inspect": CmdInspect,
-		//"import":  CmdImport,
+		"import":  CmdImport,
 		"history": CmdHistory,
 		"history": CmdHistory,
 		"kill":    CmdKill,
 		"kill":    CmdKill,
 		"logs":    CmdLogs,
 		"logs":    CmdLogs,
@@ -56,14 +77,14 @@ func ParseCommands(args []string) error {
 		cmd, exists := cmds[args[0]]
 		cmd, exists := cmds[args[0]]
 		if !exists {
 		if !exists {
 			fmt.Println("Error: Command not found:", args[0])
 			fmt.Println("Error: Command not found:", args[0])
-			return cmdHelp(args)
+			return cmdHelp(args...)
 		}
 		}
-		return cmd(args[1:])
+		return cmd(args[1:]...)
 	}
 	}
-	return cmdHelp(args)
+	return cmdHelp(args...)
 }
 }
 
 
-func cmdHelp(args []string) error {
+func cmdHelp(args ...string) error {
 	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
 	help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n"
 	for _, cmd := range [][]string{
 	for _, cmd := range [][]string{
 		{"attach", "Attach to a running container"},
 		{"attach", "Attach to a running container"},
@@ -72,7 +93,7 @@ func cmdHelp(args []string) error {
 		{"export", "Stream the contents of a container as a tar archive"},
 		{"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"},
 		{"images", "List images"},
-		//{"import", "Create a new filesystem image from the contents of a tarball"},
+		{"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/image"},
 		{"inspect", "Return low-level information on a container/image"},
 		{"kill", "Kill a running container"},
 		{"kill", "Kill a running container"},
@@ -199,7 +220,7 @@ func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ..
 */
 */
 
 
 // 'docker wait': block until a container stops
 // 'docker wait': block until a container stops
-func CmdWait(args []string) error {
+func CmdWait(args ...string) error {
 	cmd := Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.")
 	cmd := Subcmd("wait", "CONTAINER [CONTAINER...]", "Block until a container stops, then print its exit code.")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -225,7 +246,7 @@ func CmdWait(args []string) error {
 }
 }
 
 
 // 'docker version': show version information
 // 'docker version': show version information
-func CmdVersion(args []string) error {
+func CmdVersion(args ...string) error {
 	cmd := Subcmd("version", "", "Show the docker version information.")
 	cmd := Subcmd("version", "", "Show the docker version information.")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -258,7 +279,7 @@ func CmdVersion(args []string) error {
 }
 }
 
 
 // 'docker info': display system-wide information.
 // 'docker info': display system-wide information.
-func CmdInfo(args []string) error {
+func CmdInfo(args ...string) error {
 	cmd := Subcmd("info", "", "Display system-wide information")
 	cmd := Subcmd("info", "", "Display system-wide information")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -286,7 +307,7 @@ func CmdInfo(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdStop(args []string) error {
+func CmdStop(args ...string) error {
 	cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container")
 	cmd := Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container")
 	nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container")
 	nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
@@ -311,7 +332,7 @@ func CmdStop(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdRestart(args []string) error {
+func CmdRestart(args ...string) error {
 	cmd := Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container")
 	cmd := Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container")
 	nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container")
 	nSeconds := cmd.Int("t", 10, "wait t seconds before killing the container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
@@ -336,7 +357,7 @@ func CmdRestart(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdStart(args []string) error {
+func CmdStart(args ...string) error {
 	cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
 	cmd := Subcmd("start", "CONTAINER [CONTAINER...]", "Restart a stopped container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -357,7 +378,7 @@ func CmdStart(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdInspect(args []string) error {
+func CmdInspect(args ...string) error {
 	cmd := Subcmd("inspect", "CONTAINER|IMAGE", "Return low-level information on a container/image")
 	cmd := Subcmd("inspect", "CONTAINER|IMAGE", "Return low-level information on a container/image")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -377,7 +398,7 @@ func CmdInspect(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdPort(args []string) error {
+func CmdPort(args ...string) error {
 	cmd := Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
 	cmd := Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -403,7 +424,7 @@ func CmdPort(args []string) error {
 }
 }
 
 
 // 'docker rmi IMAGE' removes all images with the name IMAGE
 // 'docker rmi IMAGE' removes all images with the name IMAGE
-func CmdRmi(args []string) error {
+func CmdRmi(args ...string) error {
 	cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image")
 	cmd := Subcmd("rmi", "IMAGE [IMAGE...]", "Remove an image")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -424,7 +445,7 @@ func CmdRmi(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdHistory(args []string) error {
+func CmdHistory(args ...string) error {
 	cmd := Subcmd("history", "IMAGE", "Show the history of an image")
 	cmd := Subcmd("history", "IMAGE", "Show the history of an image")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -454,7 +475,7 @@ func CmdHistory(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdRm(args []string) error {
+func CmdRm(args ...string) error {
 	cmd := Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove a container")
 	cmd := Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove a container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -476,7 +497,7 @@ func CmdRm(args []string) error {
 }
 }
 
 
 // 'docker kill NAME' kills a running container
 // 'docker kill NAME' kills a running container
-func CmdKill(args []string) error {
+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 {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -498,7 +519,7 @@ func CmdKill(args []string) error {
 }
 }
 
 
 /* /!\ W.I.P /!\ */
 /* /!\ W.I.P /!\ */
-func CmdImport(args []string) error {
+func CmdImport(args ...string) error {
 	cmd := Subcmd("import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball")
 	cmd := Subcmd("import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball")
 
 
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
@@ -512,7 +533,7 @@ func CmdImport(args []string) error {
 	v := url.Values{}
 	v := url.Values{}
 	v.Set("repo", repository)
 	v.Set("repo", repository)
 	v.Set("tag", tag)
 	v.Set("tag", tag)
-	v.Set("src", src)
+	v.Set("fromSrc", src)
 
 
 	err := hijack("POST", "/images?"+v.Encode(), false)
 	err := hijack("POST", "/images?"+v.Encode(), false)
 	if err != nil {
 	if err != nil {
@@ -582,7 +603,7 @@ func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...
 }
 }
 */
 */
 
 
-func CmdPull(args []string) error {
+func CmdPull(args ...string) error {
 	cmd := Subcmd("pull", "NAME", "Pull an image or a repository from the registry")
 	cmd := Subcmd("pull", "NAME", "Pull an image or a repository from the registry")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -592,15 +613,17 @@ func CmdPull(args []string) error {
 		cmd.Usage()
 		cmd.Usage()
 		return nil
 		return nil
 	}
 	}
+	v := url.Values{}
+	v.Set("fromImage", cmd.Arg(0))
 
 
-	if err := hijack("POST", "/images/"+cmd.Arg(0)+"/pull", false); err != nil {
+	if err := hijack("POST", "/images?"+v.Encode(), false); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	return nil
 	return nil
 }
 }
 
 
-func CmdImages(args []string) error {
+func CmdImages(args ...string) error {
 	cmd := Subcmd("images", "[OPTIONS] [NAME]", "List images")
 	cmd := Subcmd("images", "[OPTIONS] [NAME]", "List images")
 	quiet := cmd.Bool("q", false, "only show numeric IDs")
 	quiet := cmd.Bool("q", false, "only show numeric IDs")
 	all := cmd.Bool("a", false, "show all images")
 	all := cmd.Bool("a", false, "show all images")
@@ -652,7 +675,7 @@ func CmdImages(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdPs(args []string) error {
+func CmdPs(args ...string) error {
 	cmd := Subcmd("ps", "[OPTIONS]", "List containers")
 	cmd := Subcmd("ps", "[OPTIONS]", "List containers")
 	quiet := cmd.Bool("q", false, "Only display numeric IDs")
 	quiet := cmd.Bool("q", false, "Only display numeric IDs")
 	all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
 	all := cmd.Bool("a", false, "Show all containers. Only running containers are shown by default.")
@@ -709,7 +732,7 @@ func CmdPs(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdCommit(args []string) error {
+func CmdCommit(args ...string) error {
 	cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
 	cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
 	flComment := cmd.String("m", "", "Commit message")
 	flComment := cmd.String("m", "", "Commit message")
 	flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
 	flAuthor := cmd.String("author", "", "Author (eg. \"John Hannibal Smith <hannibal@a-team.com>\"")
@@ -724,6 +747,7 @@ func CmdCommit(args []string) error {
 	}
 	}
 
 
 	v := url.Values{}
 	v := url.Values{}
+	v.Set("fromContainer", name)
 	v.Set("repo", repository)
 	v.Set("repo", repository)
 	v.Set("tag", tag)
 	v.Set("tag", tag)
 	v.Set("comment", *flComment)
 	v.Set("comment", *flComment)
@@ -736,7 +760,7 @@ func CmdCommit(args []string) error {
 		}
 		}
 	}
 	}
 
 
-	body, _, err := call("POST", "/containers/"+name+"/commit?"+v.Encode(), config)
+	body, _, err := call("POST", "/images?"+v.Encode(), config)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -751,7 +775,7 @@ func CmdCommit(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdExport(args []string) error {
+func CmdExport(args ...string) error {
 	cmd := Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive")
 	cmd := Subcmd("export", "CONTAINER", "Export the contents of a filesystem as a tar archive")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -768,7 +792,7 @@ func CmdExport(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdDiff(args []string) error {
+func CmdDiff(args ...string) error {
 	cmd := Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem")
 	cmd := Subcmd("diff", "CONTAINER", "Inspect changes on a container's filesystem")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -794,7 +818,7 @@ func CmdDiff(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdLogs(args []string) error {
+func CmdLogs(args ...string) error {
 	cmd := Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
 	cmd := Subcmd("logs", "CONTAINER", "Fetch the logs of a container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -815,7 +839,7 @@ func CmdLogs(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdAttach(args []string) error {
+func CmdAttach(args ...string) error {
 	cmd := Subcmd("attach", "CONTAINER", "Attach to a running container")
 	cmd := Subcmd("attach", "CONTAINER", "Attach to a running container")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
 		return nil
 		return nil
@@ -906,7 +930,7 @@ func (opts AttachOpts) Get(val string) bool {
 	return false
 	return false
 }
 }
 
 
-func CmdTag(args []string) error {
+func CmdTag(args ...string) error {
 	cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
 	cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
 	force := cmd.Bool("f", false, "Force")
 	force := cmd.Bool("f", false, "Force")
 	if err := cmd.Parse(args); err != nil {
 	if err := cmd.Parse(args); err != nil {
@@ -933,7 +957,7 @@ func CmdTag(args []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func CmdRun(args []string) error {
+func CmdRun(args ...string) error {
 	config, cmd, err := ParseRun(args)
 	config, cmd, err := ParseRun(args)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -952,11 +976,18 @@ func CmdRun(args []string) error {
 
 
 	//if image not found try to pull it
 	//if image not found try to pull it
 	if statusCode == 404 {
 	if statusCode == 404 {
-		err = hijack("POST", "/images/"+config.Image+"/pull", false)
+		v := url.Values{}
+		v.Set("fromImage", config.Image)
+		err = hijack("POST", "/images?"+v.Encode(), false)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 		body, _, err = call("POST", "/containers", *config)
 		body, _, err = call("POST", "/containers", *config)
+		if err != nil {
+			return err
+		}
+		return nil
+
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -1001,6 +1032,18 @@ func CmdRun(args []string) error {
 		if err := hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty); err != nil {
 		if err := hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty); err != nil {
 			return err
 			return err
 		}
 		}
+		body, _, err := call("POST", "/containers/"+out.Id+"/wait", nil)
+		if err != nil {
+			fmt.Printf("%s", err)
+		} else {
+			var out ApiWait
+			err = json.Unmarshal(body, &out)
+			if err != nil {
+				return err
+			}
+			os.Exit(out.StatusCode)
+		}
+
 	} else {
 	} else {
 		fmt.Println(out.Id)
 		fmt.Println(out.Id)
 	}
 	}
@@ -1054,9 +1097,14 @@ func hijack(method, path string, setRawTerminal bool) error {
 	clientconn.Do(req)
 	clientconn.Do(req)
 	defer clientconn.Close()
 	defer clientconn.Close()
 
 
-	rwc, _ := clientconn.Hijack()
+	rwc, br := clientconn.Hijack()
 	defer rwc.Close()
 	defer rwc.Close()
 
 
+	receiveStdout := Go(func() error {
+		_, err := io.Copy(os.Stdout, br)
+		return err
+	})
+
 	if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" {
 	if setRawTerminal && term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" {
 		if oldState, err := SetRawTerminal(); err != nil {
 		if oldState, err := SetRawTerminal(); err != nil {
 			return err
 			return err
@@ -1065,10 +1113,6 @@ func hijack(method, path string, setRawTerminal bool) error {
 		}
 		}
 	}
 	}
 
 
-	receiveStdout := Go(func() error {
-		_, err := io.Copy(os.Stdout, rwc)
-		return err
-	})
 	sendStdin := Go(func() error {
 	sendStdin := Go(func() error {
 		_, err := io.Copy(rwc, os.Stdin)
 		_, err := io.Copy(rwc, os.Stdin)
 		rwc.Close()
 		rwc.Close()

+ 6 - 3
commands_test.go

@@ -1,16 +1,17 @@
 package docker
 package docker
 
 
 import (
 import (
-	"bufio"
+	/*"bufio"
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker/rcli"
 	"github.com/dotcloud/docker/rcli"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
-	"strings"
+	"strings"*/
 	"testing"
 	"testing"
 	"time"
 	"time"
 )
 )
 
 
+/*TODO
 func closeWrap(args ...io.Closer) error {
 func closeWrap(args ...io.Closer) error {
 	e := false
 	e := false
 	ret := fmt.Errorf("Error closing elements")
 	ret := fmt.Errorf("Error closing elements")
@@ -25,7 +26,7 @@ func closeWrap(args ...io.Closer) error {
 	}
 	}
 	return nil
 	return nil
 }
 }
-
+*/
 func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
 func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
 	c := make(chan bool)
 	c := make(chan bool)
 
 
@@ -43,6 +44,7 @@ func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
 	}
 	}
 }
 }
 
 
+/*TODO
 func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error {
 func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error {
 	for i := 0; i < count; i++ {
 	for i := 0; i < count; i++ {
 		if _, err := w.Write([]byte(input)); err != nil {
 		if _, err := w.Write([]byte(input)); err != nil {
@@ -396,3 +398,4 @@ func TestAttachDisconnect(t *testing.T) {
 	cStdin.Close()
 	cStdin.Close()
 	container.Wait()
 	container.Wait()
 }
 }
+*/

+ 5 - 51
docker/docker.go

@@ -4,14 +4,10 @@ import (
 	"flag"
 	"flag"
 	"fmt"
 	"fmt"
 	"github.com/dotcloud/docker"
 	"github.com/dotcloud/docker"
-	"github.com/dotcloud/docker/rcli"
-	"github.com/dotcloud/docker/term"
-	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"log"
 	"log"
 	"os"
 	"os"
 	"os/signal"
 	"os/signal"
-	"runtime"
 	"strconv"
 	"strconv"
 	"syscall"
 	"syscall"
 )
 )
@@ -49,10 +45,12 @@ func main() {
 		}
 		}
 		if err := daemon(*pidfile, *flAutoRestart); err != nil {
 		if err := daemon(*pidfile, *flAutoRestart); err != nil {
 			log.Fatal(err)
 			log.Fatal(err)
+			os.Exit(-1)
 		}
 		}
 	} else {
 	} else {
-		if err := docker.ParseCommands(flag.Args()); err != nil {
+		if err := docker.ParseCommands(flag.Args()...); err != nil {
 			log.Fatal(err)
 			log.Fatal(err)
+			os.Exit(-1)
 		}
 		}
 	}
 	}
 }
 }
@@ -99,54 +97,10 @@ func daemon(pidfile string, autoRestart bool) error {
 		os.Exit(0)
 		os.Exit(0)
 	}()
 	}()
 
 
-	if runtime.GOARCH != "amd64" {
-		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
-	}
-	runtime, err := docker.NewRuntime(autoRestart)
+	server, err := docker.NewServer(autoRestart)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	return docker.ListenAndServe("0.0.0.0:4243", runtime)
-}
-
-func runCommand(args []string) error {
-	// FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose
-	// CloseWrite(), which we need to cleanly signal that stdin is closed without
-	// closing the connection.
-	// See http://code.google.com/p/go/issues/detail?id=3345
-	if conn, err := rcli.Call("tcp", "127.0.0.1:4242", args...); err == nil {
-		options := conn.GetOptions()
-		if options.RawTerminal &&
-			term.IsTerminal(int(os.Stdin.Fd())) &&
-			os.Getenv("NORAW") == "" {
-			if oldState, err := rcli.SetRawTerminal(); err != nil {
-				return err
-			} else {
-				defer rcli.RestoreTerminal(oldState)
-			}
-		}
-		receiveStdout := docker.Go(func() error {
-			_, err := io.Copy(os.Stdout, conn)
-			return err
-		})
-		sendStdin := docker.Go(func() error {
-			_, err := io.Copy(conn, os.Stdin)
-			if err := conn.CloseWrite(); err != nil {
-				log.Printf("Couldn't send EOF: " + err.Error())
-			}
-			return err
-		})
-		if err := <-receiveStdout; err != nil {
-			return err
-		}
-		if !term.IsTerminal(int(os.Stdin.Fd())) {
-			if err := <-sendStdin; err != nil {
-				return err
-			}
-		}
-	} else {
-		return fmt.Errorf("Can't connect to docker daemon. Is 'docker -d' running on this host?")
-	}
-	return nil
+	return docker.ListenAndServe("0.0.0.0:4243", server)
 }
 }

+ 2 - 2
runtime_test.go

@@ -2,7 +2,6 @@ package docker
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/rcli"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"net"
 	"net"
@@ -67,12 +66,13 @@ func init() {
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
+
 	// Create the "Server"
 	// Create the "Server"
 	srv := &Server{
 	srv := &Server{
 		runtime: runtime,
 		runtime: runtime,
 	}
 	}
 	// Retrieve the Image
 	// Retrieve the Image
-	if err := srv.CmdPull(os.Stdin, rcli.NewDockerLocalConn(os.Stdout), unitTestImageName); err != nil {
+	if err := srv.ImagePull(unitTestImageName, os.Stdout); err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
 }
 }

+ 448 - 0
server.go

@@ -0,0 +1,448 @@
+package docker
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"net/http"
+	"net/url"
+	"os"
+	"runtime"
+	"strings"
+)
+
+func (srv *Server) DockerVersion() ApiVersion {
+	return ApiVersion{VERSION, GIT_COMMIT, srv.runtime.capabilities.MemoryLimit, srv.runtime.capabilities.SwapLimit}
+}
+
+func (srv *Server) ContainerKill(name string) error {
+	if container := srv.runtime.Get(name); container != nil {
+		if err := container.Kill(); err != nil {
+			return fmt.Errorf("Error restarting container %s: %s", name, err.Error())
+		}
+	} else {
+		return fmt.Errorf("No such container: %s", name)
+	}
+	return nil
+}
+
+func (srv *Server) ContainerExport(name string, file *os.File) error {
+	if container := srv.runtime.Get(name); container != nil {
+
+		data, err := container.Export()
+		if err != nil {
+			return err
+		}
+
+		// Stream the entire contents of the container (basically a volatile snapshot)
+		if _, err := io.Copy(file, data); err != nil {
+			return err
+		}
+		return nil
+	}
+	return fmt.Errorf("No such container: %s", name)
+}
+
+func (srv *Server) Images(all, filter, quiet string) ([]ApiImages, error) {
+	var allImages map[string]*Image
+	var err error
+	if all == "1" {
+		allImages, err = srv.runtime.graph.Map()
+	} else {
+		allImages, err = srv.runtime.graph.Heads()
+	}
+	if err != nil {
+		return nil, err
+	}
+	var outs []ApiImages = []ApiImages{} //produce [] when empty instead of 'null'
+	for name, repository := range srv.runtime.repositories.Repositories {
+		if filter != "" && name != filter {
+			continue
+		}
+		for tag, id := range repository {
+			var out ApiImages
+			image, err := srv.runtime.graph.Get(id)
+			if err != nil {
+				log.Printf("Warning: couldn't load %s from %s/%s: %s", id, name, tag, err)
+				continue
+			}
+			delete(allImages, id)
+			if quiet != "1" {
+				out.Repository = name
+				out.Tag = tag
+				out.Id = TruncateId(id)
+				out.Created = image.Created.Unix()
+			} else {
+				out.Id = image.ShortId()
+			}
+			outs = append(outs, out)
+		}
+	}
+	// Display images which aren't part of a
+	if filter == "" {
+		for id, image := range allImages {
+			var out ApiImages
+			if quiet != "1" {
+				out.Repository = "<none>"
+				out.Tag = "<none>"
+				out.Id = TruncateId(id)
+				out.Created = image.Created.Unix()
+			} else {
+				out.Id = image.ShortId()
+			}
+			outs = append(outs, out)
+		}
+	}
+	return outs, nil
+}
+
+func (srv *Server) DockerInfo() ApiInfo {
+	images, _ := srv.runtime.graph.All()
+	var imgcount int
+	if images == nil {
+		imgcount = 0
+	} else {
+		imgcount = len(images)
+	}
+	var out ApiInfo
+	out.Containers = len(srv.runtime.List())
+	out.Version = VERSION
+	out.Images = imgcount
+	if os.Getenv("DEBUG") == "1" {
+		out.Debug = true
+		out.NFd = getTotalUsedFds()
+		out.NGoroutines = runtime.NumGoroutine()
+	}
+	return out
+}
+
+func (srv *Server) ImageHistory(name string) ([]ApiHistory, error) {
+	image, err := srv.runtime.repositories.LookupImage(name)
+	if err != nil {
+		return nil, err
+	}
+
+	var outs []ApiHistory = []ApiHistory{} //produce [] when empty instead of 'null'
+	err = image.WalkHistory(func(img *Image) error {
+		var out ApiHistory
+		out.Id = srv.runtime.repositories.ImageName(img.ShortId())
+		out.Created = img.Created.Unix()
+		out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ")
+		outs = append(outs, out)
+		return nil
+	})
+	return outs, nil
+
+}
+
+func (srv *Server) ContainerChanges(name string) ([]string, error) {
+	if container := srv.runtime.Get(name); container != nil {
+		changes, err := container.Changes()
+		if err != nil {
+			return nil, err
+		}
+		var changesStr []string
+		for _, name := range changes {
+			changesStr = append(changesStr, name.String())
+		}
+		return changesStr, nil
+	}
+	return nil, fmt.Errorf("No such container: %s", name)
+}
+
+func (srv *Server) ContainerPort(name, privatePort string) (string, error) {
+	if container := srv.runtime.Get(name); container != nil {
+		if frontend, exists := container.NetworkSettings.PortMapping[privatePort]; exists {
+			return frontend, nil
+		}
+		return "", fmt.Errorf("No private port '%s' allocated on %s", privatePort, name)
+	}
+	return "", fmt.Errorf("No such container: %s", name)
+}
+
+func (srv *Server) Containers(all, notrunc, quiet string, n int) []ApiContainers {
+	var outs []ApiContainers = []ApiContainers{} //produce [] when empty instead of 'null'
+	for i, container := range srv.runtime.List() {
+		if !container.State.Running && all != "1" && n == -1 {
+			continue
+		}
+		if i == n {
+			break
+		}
+		var out ApiContainers
+		out.Id = container.ShortId()
+		if quiet != "1" {
+			command := fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
+			if notrunc != "1" {
+				command = Trunc(command, 20)
+			}
+			out.Image = srv.runtime.repositories.ImageName(container.Image)
+			out.Command = command
+			out.Created = container.Created.Unix()
+			out.Status = container.State.String()
+			out.Ports = container.NetworkSettings.PortMappingHuman()
+		}
+		outs = append(outs, out)
+	}
+	return outs
+}
+
+func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, config *Config) (string, error) {
+	img, err := srv.runtime.Commit(name, repo, tag, comment, author, config)
+	if err != nil {
+		return "", err
+	}
+	return img.ShortId(), err
+}
+
+func (srv *Server) ContainerTag(name, repo, tag string, force bool) error {
+	if err := srv.runtime.repositories.Set(repo, tag, name, force); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (srv *Server) ImagePull(name string, file *os.File) error {
+	if srv.runtime.graph.LookupRemoteImage(name, srv.runtime.authConfig) {
+		if err := srv.runtime.graph.PullImage(file, name, srv.runtime.authConfig); err != nil {
+			return err
+		}
+	}
+	if err := srv.runtime.graph.PullRepository(file, name, "", srv.runtime.repositories, srv.runtime.authConfig); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (srv *Server) ImageImport(src, repo, tag string, file *os.File) error {
+	var archive io.Reader
+	var resp *http.Response
+
+	if src == "-" {
+		r, w := io.Pipe()
+		go func() {
+			defer w.Close()
+			defer Debugf("Closing buffered stdin pipe")
+			io.Copy(w, file)
+		}()
+		archive = r
+	} else {
+		u, err := url.Parse(src)
+		if err != nil {
+			fmt.Fprintln(file, "Error: "+err.Error())
+		}
+		if u.Scheme == "" {
+			u.Scheme = "http"
+			u.Host = src
+			u.Path = ""
+		}
+		fmt.Fprintln(file, "Downloading from", u)
+		// Download with curl (pretty progress bar)                                                                            
+		// If curl is not available, fallback to http.Get()                                                                    
+		resp, err = Download(u.String(), file)
+		if err != nil {
+			return err
+		}
+		archive = ProgressReader(resp.Body, int(resp.ContentLength), file, "Importing %v/%v (%v)")
+	}
+	img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "", nil)
+	if err != nil {
+		return err
+	}
+	// Optionally register the image at REPO/TAG                                                                                   
+	if repo != "" {
+		if err := srv.runtime.repositories.Set(repo, tag, img.Id, true); err != nil {
+			return err
+		}
+	}
+	fmt.Fprintln(file, img.ShortId())
+	return nil
+}
+
+func (srv *Server) ContainerCreate(config Config) (string, bool, bool, error) {
+	var memoryW, swapW bool
+
+	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
+		memoryW = true
+		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
+		config.Memory = 0
+	}
+
+	if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
+		swapW = true
+		log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.")
+		config.MemorySwap = -1
+	}
+	container, err := srv.runtime.Create(&config)
+	if err != nil {
+		if srv.runtime.graph.IsNotExist(err) {
+			return "", false, false, fmt.Errorf("No such image: %s", config.Image)
+		}
+		return "", false, false, err
+	}
+	return container.ShortId(), memoryW, swapW, nil
+}
+
+func (srv *Server) ContainerRestart(name string, t int) error {
+	if container := srv.runtime.Get(name); container != nil {
+		if err := container.Restart(t); err != nil {
+			return fmt.Errorf("Error restarting container %s: %s", name, err.Error())
+		}
+	} else {
+		return fmt.Errorf("No such container: %s", name)
+	}
+	return nil
+}
+
+func (srv *Server) ContainerDestroy(name string) error {
+	if container := srv.runtime.Get(name); container != nil {
+		if err := srv.runtime.Destroy(container); err != nil {
+			return fmt.Errorf("Error destroying container %s: %s", name, err.Error())
+		}
+	} else {
+		return fmt.Errorf("No such container: %s", name)
+	}
+	return nil
+}
+
+func (srv *Server) ImageDelete(name string) error {
+	img, err := srv.runtime.repositories.LookupImage(name)
+	if err != nil {
+		return fmt.Errorf("No such image: %s", name)
+	} else {
+		if err := srv.runtime.graph.Delete(img.Id); err != nil {
+			return fmt.Errorf("Error deleteing image %s: %s", name, err.Error())
+		}
+	}
+	return nil
+}
+
+func (srv *Server) ContainerStart(name string) error {
+	if container := srv.runtime.Get(name); container != nil {
+		if err := container.Start(); err != nil {
+			return fmt.Errorf("Error starting container %s: %s", name, err.Error())
+		}
+	} else {
+		return fmt.Errorf("No such container: %s", name)
+	}
+	return nil
+}
+
+func (srv *Server) ContainerStop(name string, t int) error {
+	if container := srv.runtime.Get(name); container != nil {
+		if err := container.Stop(t); err != nil {
+			return fmt.Errorf("Error stopping container %s: %s", name, err.Error())
+		}
+	} else {
+		return fmt.Errorf("No such container: %s", name)
+	}
+	return nil
+}
+
+func (srv *Server) ContainerWait(name string) (int, error) {
+	if container := srv.runtime.Get(name); container != nil {
+		return container.Wait(), nil
+	}
+	return 0, fmt.Errorf("No such container: %s", name)
+}
+
+func (srv *Server) ContainerAttach(name, logs, stream, stdin, stdout, stderr string, file *os.File) error {
+	if container := srv.runtime.Get(name); container != nil {
+		//logs
+		if logs == "1" {
+			if stdout == "1" {
+				cLog, err := container.ReadLog("stdout")
+				if err != nil {
+					Debugf(err.Error())
+				} else if _, err := io.Copy(file, cLog); err != nil {
+					Debugf(err.Error())
+				}
+			}
+			if stderr == "1" {
+				cLog, err := container.ReadLog("stderr")
+				if err != nil {
+					Debugf(err.Error())
+				} else if _, err := io.Copy(file, cLog); err != nil {
+					Debugf(err.Error())
+				}
+			}
+		}
+
+		//stream
+		if stream == "1" {
+			if container.State.Ghost {
+				return fmt.Errorf("Impossible to attach to a ghost container")
+			}
+
+			if container.Config.Tty {
+				oldState, err := SetRawTerminal()
+				if err != nil {
+					if os.Getenv("DEBUG") != "" {
+						log.Printf("Can't set the terminal in raw mode: %s", err)
+					}
+				} else {
+					defer RestoreTerminal(oldState)
+				}
+			}
+			var (
+				cStdin           io.ReadCloser
+				cStdout, cStderr io.Writer
+				cStdinCloser     io.Closer
+			)
+
+			if stdin == "1" {
+				r, w := io.Pipe()
+				go func() {
+					defer w.Close()
+					defer Debugf("Closing buffered stdin pipe")
+					io.Copy(w, file)
+				}()
+				cStdin = r
+				cStdinCloser = file
+			}
+			if stdout == "1" {
+				cStdout = file
+			}
+			if stderr == "1" {
+				cStderr = file
+			}
+
+			<-container.Attach(cStdin, cStdinCloser, cStdout, cStderr)
+		}
+	} else {
+		return fmt.Errorf("No such container: %s", name)
+	}
+	return nil
+}
+
+func (srv *Server) ContainerInspect(name string) (*Container, error) {
+	if container := srv.runtime.Get(name); container != nil {
+		return container, nil
+	}
+	return nil, fmt.Errorf("No such container: %s", name)
+}
+
+func (srv *Server) ImageInspect(name string) (*Image, error) {
+	if image, err := srv.runtime.repositories.LookupImage(name); err == nil && image != nil {
+		return image, nil
+	}
+	return nil, fmt.Errorf("No such image: %s", name)
+}
+
+func NewServer(autoRestart bool) (*Server, error) {
+	if runtime.GOARCH != "amd64" {
+		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
+	}
+	runtime, err := NewRuntime(autoRestart)
+	if err != nil {
+		return nil, err
+	}
+	srv := &Server{
+		runtime: runtime,
+	}
+	return srv, nil
+}
+
+type Server struct {
+	runtime *Runtime
+}