From 79e910580604339a74c1577061d8b207ae8c2ee1 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 18 Apr 2013 18:56:22 +0200 Subject: [PATCH] added kill and images(wip) --- api.go | 100 +++++++++++++++++++++++- api_params.go | 25 +++++- commands.go | 1 - commands2.go | 205 ++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 274 insertions(+), 57 deletions(-) diff --git a/api.go b/api.go index 353a93288a..4e2750b332 100644 --- a/api.go +++ b/api.go @@ -2,9 +2,11 @@ package docker import ( "encoding/json" - "log" + _ "fmt" "github.com/gorilla/mux" + "log" "net/http" + "time" ) func ListenAndServe(addr string, runtime *Runtime) error { @@ -21,10 +23,102 @@ func ListenAndServe(addr string, runtime *Runtime) error { } }) + r.Path("/kill").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var ids []string + if err := json.NewDecoder(r.Body).Decode(&ids); err != nil { + w.WriteHeader(500) + return + } + + var ret SimpleMessage + for _, name := range ids { + container := runtime.Get(name) + if container == nil { + ret.Message = "No such container: " + name + "\n" + break + } + if err := container.Kill(); err != nil { + ret.Message = ret.Message + "Error killing container " + name + ": " + err.Error() + "\n" + } + } + if ret.Message == "" { + w.WriteHeader(200) + } else { + w.WriteHeader(500) + } + + b, err := json.Marshal(ret) + if err != nil { + w.WriteHeader(500) + } else { + w.Write(b) + } + + }) + r.Path("/images").Methods("GET", "POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - //TODO use runtime + var in ImagesIn + json.NewDecoder(r.Body).Decode(&in) + + var allImages map[string]*Image + var err error + if in.All { + allImages, err = runtime.graph.Map() + } else { + allImages, err = runtime.graph.Heads() + } + if err != nil { + w.WriteHeader(500) + return + } + var outs []ImagesOut + for name, repository := range runtime.repositories.Repositories { + if in.NameFilter != "" && name != in.NameFilter { + continue + } + for tag, id := range repository { + var out ImagesOut + image, err := 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 !in.Quiet { + out.Repository = name + out.Tag = tag + out.Id = TruncateId(id) + out.Created = HumanDuration(time.Now().Sub(image.Created)) + " ago" + } else { + out.Id = image.ShortId() + } + outs = append(outs, out) + } + } + // Display images which aren't part of a + if in.NameFilter == "" { + for id, image := range allImages { + var out ImagesOut + if !in.Quiet { + out.Repository = "" + out.Tag = "" + out.Id = TruncateId(id) + out.Created = HumanDuration(time.Now().Sub(image.Created)) + " ago" + } else { + out.Id = image.ShortId() + } + outs = append(outs, out) + } + } + + b, err := json.Marshal(outs) + if err != nil { + w.WriteHeader(500) + } else { + w.Write(b) + } + }) return http.ListenAndServe(addr, r) } - diff --git a/api_params.go b/api_params.go index 277074964d..f882b353db 100644 --- a/api_params.go +++ b/api_params.go @@ -1,7 +1,24 @@ package docker -type VersionOut struct { - Version string - GitCommit string - MemoryLimitDisabled bool +type SimpleMessage struct { + Message string +} + +type ImagesIn struct { + NameFilter string + Quiet bool + All bool +} + +type ImagesOut struct { + Repository string `json:",omitempty"` + Tag string `json:",omitempty"` + Id string + Created string `json:",omitempty"` +} + +type VersionOut struct { + Version string + GitCommit string + MemoryLimitDisabled bool } diff --git a/commands.go b/commands.go index b30f9690e4..59bd87d573 100644 --- a/commands.go +++ b/commands.go @@ -976,7 +976,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s return nil } - type Server struct { runtime *Runtime } diff --git a/commands2.go b/commands2.go index 014a4bbe62..a2bdedeec6 100644 --- a/commands2.go +++ b/commands2.go @@ -1,25 +1,31 @@ package docker import ( + "bytes" + "encoding/json" + "flag" "fmt" "io/ioutil" "net/http" - "encoding/json" + "os" + "text/tabwriter" ) func ParseCommands(args []string) error { - - cmds := map[string]func(args []string) error { - "version":cmdVersion, + + cmds := map[string]func(args []string) error{ + "images": cmdImages, + "kill": cmdKill, + "version": cmdVersion, } if len(args) > 0 { - cmd, exists := cmds[args[0]] + cmd, exists := cmds[args[0]] if !exists { //TODO display commend not found return cmdHelp(args) } - return cmd(args) + return cmd(args[1:]) } return cmdHelp(args) } @@ -27,31 +33,31 @@ func ParseCommands(args []string) error { func cmdHelp(args []string) error { help := "Usage: docker COMMAND [arg...]\n\nA self-sufficient runtime for linux containers.\n\nCommands:\n" for _, cmd := range [][]string{ -// {"attach", "Attach to a running container"}, -// {"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"}, -// {"images", "List images"}, -// {"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"}, -// {"kill", "Kill a running container"}, -// {"login", "Register or Login to the docker registry server"}, -// {"logs", "Fetch the logs of a container"}, -// {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"}, -// {"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"}, -// {"tag", "Tag an image into a repository"}, + // {"attach", "Attach to a running container"}, + // {"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"}, + {"images", "List images"}, + // {"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"}, + {"kill", "Kill a running container"}, + // {"login", "Register or Login to the docker registry server"}, + // {"logs", "Fetch the logs of a container"}, + // {"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"}, + // {"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"}, + // {"tag", "Tag an image into a repository"}, {"version", "Show the docker version information"}, -// {"wait", "Block until a container stops, then print its exit code"}, + // {"wait", "Block until a container stops, then print its exit code"}, } { help += fmt.Sprintf(" %-10.10s%s\n", cmd[0], cmd[1]) } @@ -59,8 +65,80 @@ func cmdHelp(args []string) error { return nil } -func cmdVersion(args []string) error { - body, err := apiCall("version") +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") + if err := cmd.Parse(args); err != nil { + return nil + } + if cmd.NArg() > 1 { + cmd.Usage() + return nil + } + var nameFilter string + if cmd.NArg() == 1 { + nameFilter = cmd.Arg(0) + } + + in := ImagesIn{nameFilter, *quiet, *flAll} + + body, err := apiPost("images", in) + if err != nil { + return err + } + + var outs []ImagesOut + err = json.Unmarshal(body, &outs) + if err != nil { + return err + } + w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) + if !*quiet { + fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED") + } + + 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) + } else { + fmt.Fprintln(w, out.Id) + } + } + + if !*quiet { + w.Flush() + } + return nil + +} + +func cmdKill(args []string) error { + cmd := subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container") + if err := cmd.Parse(args); err != nil { + return nil + } + if cmd.NArg() < 1 { + cmd.Usage() + return nil + } + + body, err := apiPost("kill", args) + if err != nil { + return err + } + + var out SimpleMessage + err = json.Unmarshal(body, &out) + if err != nil { + return err + } + fmt.Print(out.Message) + return nil +} + +func cmdVersion(_ []string) error { + body, err := apiGet("version") if err != nil { return err } @@ -68,28 +146,57 @@ func cmdVersion(args []string) error { var out VersionOut err = json.Unmarshal(body, &out) if err != nil { - return err - } + return err + } fmt.Println("Version:", out.Version) - fmt.Println("Git Commit:", out.GitCommit) - if out.MemoryLimitDisabled { - fmt.Println("Memory limit disabled") - } + fmt.Println("Git Commit:", out.GitCommit) + if out.MemoryLimitDisabled { + fmt.Println("Memory limit disabled") + } return nil } -func apiCall(path string) ([]byte, error) { - resp, err := http.Get("http://0.0.0.0:4243/" + path) - if err != nil { - return nil,err - } +func apiGet(path string) ([]byte, error) { + resp, err := http.Get("http://0.0.0.0:4243/" + path) + 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 - } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } return body, nil -} \ No newline at end of file +} + +func apiPost(path string, data interface{}) ([]byte, 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 + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return body, nil + +} + +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) + flags.PrintDefaults() + } + return flags +}