1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #3728 from vieux/container_job

Move containers to a job
This commit is contained in:
Victor Vieux 2014-01-24 16:10:46 -08:00
commit e2003fea3a
8 changed files with 240 additions and 144 deletions

45
api.go
View file

@ -291,32 +291,37 @@ func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *h
if err := parseForm(r); err != nil { if err := parseForm(r); err != nil {
return err return err
} }
all, err := getBoolParam(r.Form.Get("all")) var (
if err != nil { err error
outs *engine.Table
job = srv.Eng.Job("containers")
)
job.Setenv("all", r.Form.Get("all"))
job.Setenv("size", r.Form.Get("size"))
job.Setenv("since", r.Form.Get("since"))
job.Setenv("before", r.Form.Get("before"))
job.Setenv("limit", r.Form.Get("limit"))
if version > 1.5 {
job.Stdout.Add(w)
} else if outs, err = job.Stdout.AddTable(); err != nil {
return err return err
} }
size, err := getBoolParam(r.Form.Get("size")) if err = job.Run(); err != nil {
if err != nil {
return err return err
} }
since := r.Form.Get("since") if version < 1.5 { // Convert to legacy format
before := r.Form.Get("before") for _, out := range outs.Data {
n, err := strconv.Atoi(r.Form.Get("limit")) ports := engine.NewTable("", 0)
if err != nil { ports.ReadListFrom([]byte(out.Get("Ports")))
n = -1 out.Set("Ports", displayablePorts(ports))
} }
if _, err = outs.WriteListTo(w); err != nil {
outs := srv.Containers(all, size, n, since, before) return err
if version < 1.5 {
outs2 := []APIContainersOld{}
for _, ctnr := range outs {
outs2 = append(outs2, *ctnr.ToLegacy())
} }
return writeJSON(w, http.StatusOK, outs2)
} }
return writeJSON(w, http.StatusOK, outs) return nil
} }
func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

View file

@ -11,29 +11,6 @@ type (
Untagged string `json:",omitempty"` Untagged string `json:",omitempty"`
} }
APIContainers struct {
ID string `json:"Id"`
Image string
Command string
Created int64
Status string
Ports []APIPort
SizeRw int64
SizeRootFs int64
Names []string
}
APIContainersOld struct {
ID string `json:"Id"`
Image string
Command string
Created int64
Status string
Ports string
SizeRw int64
SizeRootFs int64
}
APIID struct { APIID struct {
ID string `json:"Id"` ID string `json:"Id"`
} }
@ -68,16 +45,3 @@ type (
HostPath string HostPath string
} }
) )
func (api APIContainers) ToLegacy() *APIContainersOld {
return &APIContainersOld{
ID: api.ID,
Image: api.Image,
Command: api.Command,
Created: api.Created,
Status: api.Status,
Ports: displayablePorts(api.Ports),
SizeRw: api.SizeRw,
SizeRootFs: api.SizeRootFs,
}
}

View file

@ -1310,13 +1310,13 @@ func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix stri
} }
} }
func displayablePorts(ports []APIPort) string { func displayablePorts(ports *engine.Table) string {
result := []string{} result := []string{}
for _, port := range ports { for _, port := range ports.Data {
if port.IP == "" { if port.Get("IP") == "" {
result = append(result, fmt.Sprintf("%d/%s", port.PublicPort, port.Type)) result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type")))
} else { } else {
result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type)) result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
} }
} }
sort.Strings(result) sort.Strings(result)
@ -1362,9 +1362,8 @@ func (cli *DockerCli) CmdPs(args ...string) error {
return err return err
} }
var outs []APIContainers outs := engine.NewTable("Created", 0)
err = json.Unmarshal(body, &outs) if _, err := outs.ReadListFrom(body); err != nil {
if err != nil {
return err return err
} }
w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
@ -1377,32 +1376,42 @@ func (cli *DockerCli) CmdPs(args ...string) error {
} }
} }
for _, out := range outs { for _, out := range outs.Data {
var (
outID = out.Get("ID")
outNames = out.GetList("Names")
)
if !*noTrunc { if !*noTrunc {
out.ID = utils.TruncateID(out.ID) outID = utils.TruncateID(outID)
} }
// Remove the leading / from the names // Remove the leading / from the names
for i := 0; i < len(out.Names); i++ { for i := 0; i < len(outNames); i++ {
out.Names[i] = out.Names[i][1:] outNames[i] = outNames[i][1:]
} }
if !*quiet { if !*quiet {
var (
outCommand = out.Get("Command")
ports = engine.NewTable("", 0)
)
if !*noTrunc { if !*noTrunc {
out.Command = utils.Trunc(out.Command, 20) outCommand = utils.Trunc(outCommand, 20)
} }
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports), strings.Join(out.Names, ",")) ports.ReadListFrom([]byte(out.Get("Ports")))
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, out.Get("Image"), outCommand, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), out.Get("Status"), displayablePorts(ports), strings.Join(outNames, ","))
if *size { if *size {
if out.SizeRootFs > 0 { if out.GetInt("SizeRootFs") > 0 {
fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs)) fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.GetInt64("SizeRw")), utils.HumanSize(out.GetInt64("SizeRootFs")))
} else { } else {
fmt.Fprintf(w, "%s\n", utils.HumanSize(out.SizeRw)) fmt.Fprintf(w, "%s\n", utils.HumanSize(out.GetInt64("SizeRw")))
} }
} else { } else {
fmt.Fprint(w, "\n") fmt.Fprint(w, "\n")
} }
} else { } else {
fmt.Fprintln(w, out.ID) fmt.Fprintln(w, outID)
} }
} }

View file

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/dotcloud/docker/archive" "github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/execdriver"
"github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/graphdriver"
"github.com/dotcloud/docker/networkdriver/ipallocator" "github.com/dotcloud/docker/networkdriver/ipallocator"
@ -175,29 +176,28 @@ type NetworkSettings struct {
Ports map[Port][]PortBinding Ports map[Port][]PortBinding
} }
func (settings *NetworkSettings) PortMappingAPI() []APIPort { func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
var mapping []APIPort var outs = engine.NewTable("", 0)
for port, bindings := range settings.Ports { for port, bindings := range settings.Ports {
p, _ := parsePort(port.Port()) p, _ := parsePort(port.Port())
if len(bindings) == 0 { if len(bindings) == 0 {
mapping = append(mapping, APIPort{ out := &engine.Env{}
PublicPort: int64(p), out.SetInt("PublicPort", p)
Type: port.Proto(), out.Set("Type", port.Proto())
}) outs.Add(out)
continue continue
} }
for _, binding := range bindings { for _, binding := range bindings {
p, _ := parsePort(port.Port()) out := &engine.Env{}
h, _ := parsePort(binding.HostPort) h, _ := parsePort(binding.HostPort)
mapping = append(mapping, APIPort{ out.SetInt("PrivatePort", p)
PrivatePort: int64(p), out.SetInt("PublicPort", h)
PublicPort: int64(h), out.Set("Type", port.Proto())
Type: port.Proto(), out.Set("IP", binding.HostIp)
IP: binding.HostIp, outs.Add(out)
})
} }
} }
return mapping return outs
} }
// Inject the io.Reader at the given path. Note: do not close the reader // Inject the io.Reader at the given path. Note: do not close the reader

View file

@ -313,6 +313,14 @@ func (t *Table) WriteListTo(dst io.Writer) (n int64, err error) {
return n + 1, nil return n + 1, nil
} }
func (t *Table) ToListString() (string, error) {
buffer := bytes.NewBuffer(nil)
if _, err := t.WriteListTo(buffer); err != nil {
return "", err
}
return buffer.String(), nil
}
func (t *Table) WriteTo(dst io.Writer) (n int64, err error) { func (t *Table) WriteTo(dst io.Writer) (n int64, err error) {
for _, env := range t.Data { for _, env := range t.Data {
bytes, err := env.WriteTo(dst) bytes, err := env.WriteTo(dst)

View file

@ -302,7 +302,16 @@ func TestGetContainersJSON(t *testing.T) {
defer mkRuntimeFromEngine(eng, t).Nuke() defer mkRuntimeFromEngine(eng, t).Nuke()
srv := mkServerFromEngine(eng, t) srv := mkServerFromEngine(eng, t)
beginLen := len(srv.Containers(true, false, -1, "", "")) job := eng.Job("containers")
job.SetenvBool("all", true)
outs, err := job.Stdout.AddTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
}
beginLen := len(outs.Data)
containerID := createTestContainer(eng, &docker.Config{ containerID := createTestContainer(eng, &docker.Config{
Image: unitTestImageID, Image: unitTestImageID,
@ -323,15 +332,15 @@ func TestGetContainersJSON(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
assertHttpNotError(r, t) assertHttpNotError(r, t)
containers := []docker.APIContainers{} containers := engine.NewTable("", 0)
if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil { if _, err := containers.ReadListFrom(r.Body.Bytes()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(containers) != beginLen+1 { if len(containers.Data) != beginLen+1 {
t.Fatalf("Expected %d container, %d found (started with: %d)", beginLen+1, len(containers), beginLen) t.Fatalf("Expected %d container, %d found (started with: %d)", beginLen+1, len(containers.Data), beginLen)
} }
if containers[0].ID != containerID { if id := containers.Data[0].Get("ID"); id != containerID {
t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", containerID, containers[0].ID) t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", containerID, id)
} }
} }

View file

@ -69,7 +69,6 @@ func TestImageTagImageDelete(t *testing.T) {
func TestCreateRm(t *testing.T) { func TestCreateRm(t *testing.T) {
eng := NewTestEngine(t) eng := NewTestEngine(t)
srv := mkServerFromEngine(eng, t)
defer mkRuntimeFromEngine(eng, t).Nuke() defer mkRuntimeFromEngine(eng, t).Nuke()
config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil) config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
@ -79,25 +78,44 @@ func TestCreateRm(t *testing.T) {
id := createTestContainer(eng, config, t) id := createTestContainer(eng, config, t)
if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 { job := eng.Job("containers")
t.Errorf("Expected 1 container, %v found", len(c)) job.SetenvBool("all", true)
outs, err := job.Stdout.AddListTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
} }
job := eng.Job("container_delete", id) if len(outs.Data) != 1 {
t.Errorf("Expected 1 container, %v found", len(outs.Data))
}
job = eng.Job("container_delete", id)
job.SetenvBool("removeVolume", true) job.SetenvBool("removeVolume", true)
if err := job.Run(); err != nil { if err := job.Run(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if c := srv.Containers(true, false, -1, "", ""); len(c) != 0 { job = eng.Job("containers")
t.Errorf("Expected 0 container, %v found", len(c)) job.SetenvBool("all", true)
outs, err = job.Stdout.AddListTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
}
if len(outs.Data) != 0 {
t.Errorf("Expected 0 container, %v found", len(outs.Data))
} }
} }
func TestCreateRmVolumes(t *testing.T) { func TestCreateRmVolumes(t *testing.T) {
eng := NewTestEngine(t) eng := NewTestEngine(t)
srv := mkServerFromEngine(eng, t)
defer mkRuntimeFromEngine(eng, t).Nuke() defer mkRuntimeFromEngine(eng, t).Nuke()
config, hostConfig, _, err := docker.ParseRun([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil) config, hostConfig, _, err := docker.ParseRun([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil)
@ -107,11 +125,21 @@ func TestCreateRmVolumes(t *testing.T) {
id := createTestContainer(eng, config, t) id := createTestContainer(eng, config, t)
if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 { job := eng.Job("containers")
t.Errorf("Expected 1 container, %v found", len(c)) job.SetenvBool("all", true)
outs, err := job.Stdout.AddListTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
} }
job := eng.Job("start", id) if len(outs.Data) != 1 {
t.Errorf("Expected 1 container, %v found", len(outs.Data))
}
job = eng.Job("start", id)
if err := job.ImportEnv(hostConfig); err != nil { if err := job.ImportEnv(hostConfig); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -131,8 +159,18 @@ func TestCreateRmVolumes(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if c := srv.Containers(true, false, -1, "", ""); len(c) != 0 { job = eng.Job("containers")
t.Errorf("Expected 0 container, %v found", len(c)) job.SetenvBool("all", true)
outs, err = job.Stdout.AddListTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
}
if len(outs.Data) != 0 {
t.Errorf("Expected 0 container, %v found", len(outs.Data))
} }
} }
@ -169,11 +207,21 @@ func TestRestartKillWait(t *testing.T) {
id := createTestContainer(eng, config, t) id := createTestContainer(eng, config, t)
if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 { job := eng.Job("containers")
t.Errorf("Expected 1 container, %v found", len(c)) job.SetenvBool("all", true)
outs, err := job.Stdout.AddListTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
} }
job := eng.Job("start", id) if len(outs.Data) != 1 {
t.Errorf("Expected 1 container, %v found", len(outs.Data))
}
job = eng.Job("start", id)
if err := job.ImportEnv(hostConfig); err != nil { if err := job.ImportEnv(hostConfig); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -200,13 +248,23 @@ func TestRestartKillWait(t *testing.T) {
} }
srv = mkServerFromEngine(eng, t) srv = mkServerFromEngine(eng, t)
c := srv.Containers(true, false, -1, "", "")
if len(c) != 1 { job = srv.Eng.Job("containers")
t.Errorf("Expected 1 container, %v found", len(c)) job.SetenvBool("all", true)
outs, err = job.Stdout.AddListTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
}
if len(outs.Data) != 1 {
t.Errorf("Expected 1 container, %v found", len(outs.Data))
} }
setTimeout(t, "Waiting on stopped container timedout", 5*time.Second, func() { setTimeout(t, "Waiting on stopped container timedout", 5*time.Second, func() {
job = srv.Eng.Job("wait", c[0].ID) job = srv.Eng.Job("wait", outs.Data[0].Get("ID"))
var statusStr string var statusStr string
job.Stdout.AddString(&statusStr) job.Stdout.AddString(&statusStr)
if err := job.Run(); err != nil { if err := job.Run(); err != nil {
@ -227,11 +285,21 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
id := createTestContainer(eng, config, t) id := createTestContainer(eng, config, t)
if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 { job := srv.Eng.Job("containers")
t.Errorf("Expected 1 container, %v found", len(c)) job.SetenvBool("all", true)
outs, err := job.Stdout.AddListTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
} }
job := eng.Job("start", id) if len(outs.Data) != 1 {
t.Errorf("Expected 1 container, %v found", len(outs.Data))
}
job = eng.Job("start", id)
if err := job.ImportEnv(hostConfig); err != nil { if err := job.ImportEnv(hostConfig); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -270,8 +338,18 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if c := srv.Containers(true, false, -1, "", ""); len(c) != 0 { job = srv.Eng.Job("containers")
t.Errorf("Expected 0 container, %v found", len(c)) job.SetenvBool("all", true)
outs, err = job.Stdout.AddListTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
}
if len(outs.Data) != 0 {
t.Errorf("Expected 0 container, %v found", len(outs.Data))
} }
} }
@ -465,10 +543,18 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
t.Fatal("No id returned") t.Fatal("No id returned")
} }
containers := srv.Containers(true, false, -1, "", "") job := srv.Eng.Job("containers")
job.SetenvBool("all", true)
outs, err := job.Stdout.AddListTable()
if err != nil {
t.Fatal(err)
}
if err := job.Run(); err != nil {
t.Fatal(err)
}
if len(containers) != 1 { if len(outs.Data) != 1 {
t.Fatalf("Expected 1 container got %d", len(containers)) t.Fatalf("Expected 1 container got %d", len(outs.Data))
} }
// Try to remove the tag // Try to remove the tag

View file

@ -103,6 +103,7 @@ func jobInitApi(job *engine.Job) engine.Status {
"inspect": srv.JobInspect, "inspect": srv.JobInspect,
"events": srv.Events, "events": srv.Events,
"push": srv.ImagePush, "push": srv.ImagePush,
"containers": srv.Containers,
} { } {
if err := job.Eng.Register(name, handler); err != nil { if err := job.Eng.Register(name, handler); err != nil {
job.Error(err) job.Error(err)
@ -1055,10 +1056,17 @@ func (srv *Server) ContainerChanges(job *engine.Job) engine.Status {
return engine.StatusOK return engine.StatusOK
} }
func (srv *Server) Containers(all, size bool, n int, since, before string) []APIContainers { func (srv *Server) Containers(job *engine.Job) engine.Status {
var foundBefore bool var (
var displayed int foundBefore bool
out := []APIContainers{} displayed int
all = job.GetenvBool("all")
since = job.Getenv("since")
before = job.Getenv("before")
n = job.GetenvInt("limit")
size = job.GetenvBool("size")
)
outs := engine.NewTable("Created", 0)
names := map[string][]string{} names := map[string][]string{}
srv.runtime.containerGraph.Walk("/", func(p string, e *graphdb.Entity) error { srv.runtime.containerGraph.Walk("/", func(p string, e *graphdb.Entity) error {
@ -1083,27 +1091,34 @@ func (srv *Server) Containers(all, size bool, n int, since, before string) []API
break break
} }
displayed++ displayed++
c := createAPIContainer(names[container.ID], container, size, srv.runtime) out := &engine.Env{}
out = append(out, c) out.Set("ID", container.ID)
out.SetList("Names", names[container.ID])
out.Set("Image", srv.runtime.repositories.ImageName(container.Image))
out.Set("Command", fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " ")))
out.SetInt64("Created", container.Created.Unix())
out.Set("Status", container.State.String())
str, err := container.NetworkSettings.PortMappingAPI().ToListString()
if err != nil {
job.Error(err)
return engine.StatusErr
}
out.Set("Ports", str)
if size {
sizeRw, sizeRootFs := container.GetSize()
out.SetInt64("SizeRw", sizeRw)
out.SetInt64("SizeRootFs", sizeRootFs)
}
outs.Add(out)
} }
return out outs.ReverseSort()
if _, err := outs.WriteListTo(job.Stdout); err != nil {
job.Error(err)
return engine.StatusErr
}
return engine.StatusOK
} }
func createAPIContainer(names []string, container *Container, size bool, runtime *Runtime) APIContainers {
c := APIContainers{
ID: container.ID,
}
c.Names = names
c.Image = runtime.repositories.ImageName(container.Image)
c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
c.Created = container.Created.Unix()
c.Status = container.State.String()
c.Ports = container.NetworkSettings.PortMappingAPI()
if size {
c.SizeRw, c.SizeRootFs = container.GetSize()
}
return c
}
func (srv *Server) ContainerCommit(job *engine.Job) engine.Status { func (srv *Server) ContainerCommit(job *engine.Job) engine.Status {
if len(job.Args) != 1 { if len(job.Args) != 1 {
job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name) job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name)