added kill and images(wip)

This commit is contained in:
Victor Vieux 2013-04-18 18:56:22 +02:00
parent c0d5d5969b
commit 79e9105806
4 changed files with 274 additions and 57 deletions

100
api.go
View File

@ -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 = "<none>"
out.Tag = "<none>"
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)
}

View File

@ -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
}

View File

@ -976,7 +976,6 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout rcli.DockerConn, args ...s
return nil
}
type Server struct {
runtime *Runtime
}

View File

@ -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
}
}
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
}