diff --git a/api.go b/api.go index 3bcb423d5c..7e0c8fc249 100644 --- a/api.go +++ b/api.go @@ -10,11 +10,11 @@ import ( "log" "net" "net/http" + "net/url" "os" "runtime" "strconv" "strings" - "time" ) func ListenAndServe(addr string, rtime *Runtime) error { @@ -105,7 +105,7 @@ func ListenAndServe(addr string, rtime *Runtime) error { w.WriteHeader(500) return } - var outs []ApiImages + var outs []ApiImages = []ApiImages{} //produce [] when empty instead of 'null' for name, repository := range rtime.repositories.Repositories { if NameFilter != "" && name != NameFilter { continue @@ -122,7 +122,7 @@ func ListenAndServe(addr string, rtime *Runtime) error { out.Repository = name out.Tag = tag out.Id = TruncateId(id) - out.Created = HumanDuration(time.Now().Sub(image.Created)) + " ago" + out.Created = image.Created.Unix() } else { out.Id = image.ShortId() } @@ -137,7 +137,7 @@ func ListenAndServe(addr string, rtime *Runtime) error { out.Repository = "" out.Tag = "" out.Id = TruncateId(id) - out.Created = HumanDuration(time.Now().Sub(image.Created)) + " ago" + out.Created = image.Created.Unix() } else { out.Id = image.ShortId() } @@ -189,11 +189,12 @@ func ListenAndServe(addr string, rtime *Runtime) error { http.Error(w, err.Error(), http.StatusInternalServerError) return } - var outs []ApiHistory + + 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 = HumanDuration(time.Now().Sub(img.Created)) + " ago" + out.Created = img.Created.Unix() out.CreatedBy = strings.Join(img.ContainerConfig.Cmd, " ") return nil }) @@ -318,8 +319,7 @@ func ListenAndServe(addr string, rtime *Runtime) error { if err != nil { n = -1 } - var outs []ApiContainers - + 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 @@ -336,7 +336,7 @@ func ListenAndServe(addr string, rtime *Runtime) error { } out.Image = rtime.repositories.ImageName(container.Image) out.Command = command - out.Created = HumanDuration(time.Now().Sub(container.Created)) + " ago" + out.Created = container.Created.Unix() out.Status = container.State.String() } outs = append(outs, out) @@ -427,6 +427,77 @@ func ListenAndServe(addr string, rtime *Runtime) error { } }) + /* /!\ W.I.P /!\ */ + r.Path("/images").Methods("POST").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) + } + src := r.Form.Get("src") + repo := r.Form.Get("repo") + tag := r.Form.Get("tag") + + var archive io.Reader + var resp *http.Response + + 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) + 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 { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + archive = ProgressReader(resp.Body, int(resp.ContentLength), file) + } + img, err := rtime.graph.Create(archive, nil, "Imported from "+src) + 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) + return + } + } + + fmt.Fprintln(file, img.ShortId()) + }) + r.Path("/containers").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println(r.Method, r.RequestURI) var config Config diff --git a/api_params.go b/api_params.go index ed19fd869c..837b5a6426 100644 --- a/api_params.go +++ b/api_params.go @@ -2,7 +2,7 @@ package docker type ApiHistory struct { Id string - Created string + Created int64 CreatedBy string } @@ -10,7 +10,7 @@ type ApiImages struct { Repository string `json:",omitempty"` Tag string `json:",omitempty"` Id string - Created string `json:",omitempty"` + Created int64 `json:",omitempty"` } type ApiInfo struct { @@ -26,7 +26,7 @@ type ApiContainers struct { Id string Image string `json:",omitempty"` Command string `json:",omitempty"` - Created string `json:",omitempty"` + Created int64 `json:",omitempty"` Status string `json:",omitempty"` } diff --git a/commands.go b/commands.go index f8a5d2a68d..963689598a 100644 --- a/commands.go +++ b/commands.go @@ -15,6 +15,7 @@ import ( "os" "strconv" "text/tabwriter" + "time" ) const VERSION = "0.1.4" @@ -34,6 +35,7 @@ func ParseCommands(args []string) error { "images": CmdImages, "info": CmdInfo, "inspect": CmdInspect, + //"import": CmdImport, "history": CmdHistory, "kill": CmdKill, "logs": CmdLogs, @@ -71,7 +73,7 @@ func cmdHelp(args []string) error { {"export", "Stream the contents of a container as a tar archive"}, {"history", "Show the history of an image"}, {"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"}, {"inspect", "Return low-level information on a container/image"}, {"kill", "Kill a running container"}, @@ -436,7 +438,7 @@ func CmdHistory(args []string) error { 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) + fmt.Fprintf(w, "%s\t%s ago\t%s\n", out.Id, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.CreatedBy) } w.Flush() return nil @@ -485,12 +487,9 @@ func CmdKill(args []string) error { return nil } -/* -func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error { - stdout.Flush() - cmd := rcli.Subcmd(stdout, "import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball") - var archive io.Reader - var resp *http.Response +/* /!\ W.I.P /!\ */ +func CmdImport(args []string) error { + cmd := Subcmd("import", "URL|- [REPOSITORY [TAG]]", "Create a new filesystem image from the contents of a tarball") if err := cmd.Parse(args); err != nil { return nil @@ -499,43 +498,18 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args . cmd.Usage() return nil } - src := cmd.Arg(0) - if src == "-" { - archive = stdin - } else { - u, err := url.Parse(src) - if err != nil { - return err - } - if u.Scheme == "" { - u.Scheme = "http" - u.Host = src - u.Path = "" - } - fmt.Fprintln(stdout, "Downloading from", u) - // Download with curl (pretty progress bar) - // If curl is not available, fallback to http.Get() - resp, err = Download(u.String(), stdout) - if err != nil { - return err - } - archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout) - } - img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src) + src, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2) + v := url.Values{} + v.Set("repo", repository) + v.Set("tag", tag) + v.Set("src", src) + + err := callStream("POST", "/images?"+v.Encode(), nil, false) if err != nil { return err } - // Optionally register the image at REPO/TAG - if repository := cmd.Arg(1); repository != "" { - tag := cmd.Arg(2) // Repository will handle an empty tag properly - if err := srv.runtime.repositories.Set(repository, tag, img.Id, true); err != nil { - return err - } - } - fmt.Fprintln(stdout, img.ShortId()) return nil } -*/ /* func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error { @@ -656,7 +630,7 @@ func CmdImages(args []string) error { for _, out := range outs { if !*quiet { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", out.Repository, out.Tag, out.Id, out.Created) + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\n", out.Repository, out.Tag, out.Id, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) } else { fmt.Fprintln(w, out.Id) } @@ -713,7 +687,7 @@ func CmdPs(args []string) error { for _, out := range outs { if !*quiet { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", out.Id, out.Image, out.Command, out.Status, out.Created) + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\n", out.Id, out.Image, out.Command, out.Status, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) } else { fmt.Fprintln(w, out.Id) }