From 5ea2986ce5cfce1b86fdc92610dbc6d670691168 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 16 Jan 2014 14:00:18 -0800 Subject: [PATCH] Move containers to a job Docker-DCO-1.1-Signed-off-by: Victor Vieux (github: vieux) --- api.go | 45 ++++++------ api_params.go | 36 ---------- commands.go | 45 +++++++----- container.go | 28 ++++---- engine/env.go | 8 +++ integration/api_test.go | 23 ++++-- integration/server_test.go | 140 ++++++++++++++++++++++++++++++------- server.go | 59 ++++++++++------ 8 files changed, 240 insertions(+), 144 deletions(-) diff --git a/api.go b/api.go index d2655be0ac..24b18da512 100644 --- a/api.go +++ b/api.go @@ -291,32 +291,37 @@ func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *h if err := parseForm(r); err != nil { return err } - all, err := getBoolParam(r.Form.Get("all")) - if err != nil { + var ( + 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 } - size, err := getBoolParam(r.Form.Get("size")) - if err != nil { + if err = job.Run(); err != nil { return err } - since := r.Form.Get("since") - before := r.Form.Get("before") - n, err := strconv.Atoi(r.Form.Get("limit")) - if err != nil { - n = -1 - } - - outs := srv.Containers(all, size, n, since, before) - - if version < 1.5 { - outs2 := []APIContainersOld{} - for _, ctnr := range outs { - outs2 = append(outs2, *ctnr.ToLegacy()) + if version < 1.5 { // Convert to legacy format + for _, out := range outs.Data { + ports := engine.NewTable("", 0) + ports.ReadListFrom([]byte(out.Get("Ports"))) + out.Set("Ports", displayablePorts(ports)) + } + if _, err = outs.WriteListTo(w); err != nil { + return err } - - 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 { diff --git a/api_params.go b/api_params.go index 5de1255524..9688063c6f 100644 --- a/api_params.go +++ b/api_params.go @@ -11,29 +11,6 @@ type ( 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 { ID string `json:"Id"` } @@ -68,16 +45,3 @@ type ( 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, - } -} diff --git a/commands.go b/commands.go index 98be8b6edf..572de84da0 100644 --- a/commands.go +++ b/commands.go @@ -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{} - for _, port := range ports { - if port.IP == "" { - result = append(result, fmt.Sprintf("%d/%s", port.PublicPort, port.Type)) + for _, port := range ports.Data { + if port.Get("IP") == "" { + result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type"))) } 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) @@ -1362,9 +1362,8 @@ func (cli *DockerCli) CmdPs(args ...string) error { return err } - var outs []APIContainers - err = json.Unmarshal(body, &outs) - if err != nil { + outs := engine.NewTable("Created", 0) + if _, err := outs.ReadListFrom(body); err != nil { return err } 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 { - out.ID = utils.TruncateID(out.ID) + outID = utils.TruncateID(outID) } // Remove the leading / from the names - for i := 0; i < len(out.Names); i++ { - out.Names[i] = out.Names[i][1:] + for i := 0; i < len(outNames); i++ { + outNames[i] = outNames[i][1:] } if !*quiet { + var ( + outCommand = out.Get("Command") + ports = engine.NewTable("", 0) + ) 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 out.SizeRootFs > 0 { - fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs)) + if out.GetInt("SizeRootFs") > 0 { + fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.GetInt64("SizeRw")), utils.HumanSize(out.GetInt64("SizeRootFs"))) } else { - fmt.Fprintf(w, "%s\n", utils.HumanSize(out.SizeRw)) + fmt.Fprintf(w, "%s\n", utils.HumanSize(out.GetInt64("SizeRw"))) } } else { fmt.Fprint(w, "\n") } } else { - fmt.Fprintln(w, out.ID) + fmt.Fprintln(w, outID) } } diff --git a/container.go b/container.go index a0f14ed810..aafda78bc7 100644 --- a/container.go +++ b/container.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/dotcloud/docker/archive" + "github.com/dotcloud/docker/engine" "github.com/dotcloud/docker/execdriver" "github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/networkdriver/ipallocator" @@ -175,29 +176,28 @@ type NetworkSettings struct { Ports map[Port][]PortBinding } -func (settings *NetworkSettings) PortMappingAPI() []APIPort { - var mapping []APIPort +func (settings *NetworkSettings) PortMappingAPI() *engine.Table { + var outs = engine.NewTable("", 0) for port, bindings := range settings.Ports { p, _ := parsePort(port.Port()) if len(bindings) == 0 { - mapping = append(mapping, APIPort{ - PublicPort: int64(p), - Type: port.Proto(), - }) + out := &engine.Env{} + out.SetInt("PublicPort", p) + out.Set("Type", port.Proto()) + outs.Add(out) continue } for _, binding := range bindings { - p, _ := parsePort(port.Port()) + out := &engine.Env{} h, _ := parsePort(binding.HostPort) - mapping = append(mapping, APIPort{ - PrivatePort: int64(p), - PublicPort: int64(h), - Type: port.Proto(), - IP: binding.HostIp, - }) + out.SetInt("PrivatePort", p) + out.SetInt("PublicPort", h) + out.Set("Type", port.Proto()) + out.Set("IP", binding.HostIp) + outs.Add(out) } } - return mapping + return outs } // Inject the io.Reader at the given path. Note: do not close the reader diff --git a/engine/env.go b/engine/env.go index 2df303c9e1..f93555a40b 100644 --- a/engine/env.go +++ b/engine/env.go @@ -313,6 +313,14 @@ func (t *Table) WriteListTo(dst io.Writer) (n int64, err error) { 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) { for _, env := range t.Data { bytes, err := env.WriteTo(dst) diff --git a/integration/api_test.go b/integration/api_test.go index ec9e4acb7c..95cae47e15 100644 --- a/integration/api_test.go +++ b/integration/api_test.go @@ -302,7 +302,16 @@ func TestGetContainersJSON(t *testing.T) { defer mkRuntimeFromEngine(eng, t).Nuke() 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{ Image: unitTestImageID, @@ -323,15 +332,15 @@ func TestGetContainersJSON(t *testing.T) { t.Fatal(err) } assertHttpNotError(r, t) - containers := []docker.APIContainers{} - if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil { + containers := engine.NewTable("", 0) + if _, err := containers.ReadListFrom(r.Body.Bytes()); err != nil { t.Fatal(err) } - if len(containers) != beginLen+1 { - t.Fatalf("Expected %d container, %d found (started with: %d)", beginLen+1, len(containers), beginLen) + if len(containers.Data) != beginLen+1 { + t.Fatalf("Expected %d container, %d found (started with: %d)", beginLen+1, len(containers.Data), beginLen) } - if containers[0].ID != containerID { - t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", containerID, containers[0].ID) + if id := containers.Data[0].Get("ID"); id != containerID { + t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", containerID, id) } } diff --git a/integration/server_test.go b/integration/server_test.go index 4efe478f9b..681e2e3718 100644 --- a/integration/server_test.go +++ b/integration/server_test.go @@ -69,7 +69,6 @@ func TestImageTagImageDelete(t *testing.T) { func TestCreateRm(t *testing.T) { eng := NewTestEngine(t) - srv := mkServerFromEngine(eng, t) defer mkRuntimeFromEngine(eng, t).Nuke() config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil) @@ -79,25 +78,44 @@ func TestCreateRm(t *testing.T) { id := createTestContainer(eng, config, t) - if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 { - t.Errorf("Expected 1 container, %v found", len(c)) + job := 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) } - 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) if err := job.Run(); err != nil { t.Fatal(err) } - if c := srv.Containers(true, false, -1, "", ""); len(c) != 0 { - t.Errorf("Expected 0 container, %v found", len(c)) + job = 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(outs.Data) != 0 { + t.Errorf("Expected 0 container, %v found", len(outs.Data)) } } func TestCreateRmVolumes(t *testing.T) { eng := NewTestEngine(t) - srv := mkServerFromEngine(eng, t) defer mkRuntimeFromEngine(eng, t).Nuke() 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) - if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 { - t.Errorf("Expected 1 container, %v found", len(c)) + job := 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) } - 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 { t.Fatal(err) } @@ -131,8 +159,18 @@ func TestCreateRmVolumes(t *testing.T) { t.Fatal(err) } - if c := srv.Containers(true, false, -1, "", ""); len(c) != 0 { - t.Errorf("Expected 0 container, %v found", len(c)) + job = 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(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) - if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 { - t.Errorf("Expected 1 container, %v found", len(c)) + job := 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) } - 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 { t.Fatal(err) } @@ -200,13 +248,23 @@ func TestRestartKillWait(t *testing.T) { } srv = mkServerFromEngine(eng, t) - c := srv.Containers(true, false, -1, "", "") - if len(c) != 1 { - t.Errorf("Expected 1 container, %v found", len(c)) + + 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(outs.Data) != 1 { + t.Errorf("Expected 1 container, %v found", len(outs.Data)) } 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 job.Stdout.AddString(&statusStr) if err := job.Run(); err != nil { @@ -227,11 +285,21 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) { id := createTestContainer(eng, config, t) - if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 { - t.Errorf("Expected 1 container, %v found", len(c)) + 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) } - 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 { t.Fatal(err) } @@ -270,8 +338,18 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) { t.Fatal(err) } - if c := srv.Containers(true, false, -1, "", ""); len(c) != 0 { - t.Errorf("Expected 0 container, %v found", len(c)) + 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(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") } - 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 { - t.Fatalf("Expected 1 container got %d", len(containers)) + if len(outs.Data) != 1 { + t.Fatalf("Expected 1 container got %d", len(outs.Data)) } // Try to remove the tag diff --git a/server.go b/server.go index 15b207f6ec..cacfa715fc 100644 --- a/server.go +++ b/server.go @@ -103,6 +103,7 @@ func jobInitApi(job *engine.Job) engine.Status { "inspect": srv.JobInspect, "events": srv.Events, "push": srv.ImagePush, + "containers": srv.Containers, } { if err := job.Eng.Register(name, handler); err != nil { job.Error(err) @@ -1055,10 +1056,17 @@ func (srv *Server) ContainerChanges(job *engine.Job) engine.Status { return engine.StatusOK } -func (srv *Server) Containers(all, size bool, n int, since, before string) []APIContainers { - var foundBefore bool - var displayed int - out := []APIContainers{} +func (srv *Server) Containers(job *engine.Job) engine.Status { + var ( + foundBefore bool + 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{} 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 } displayed++ - c := createAPIContainer(names[container.ID], container, size, srv.runtime) - out = append(out, c) + out := &engine.Env{} + 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 { if len(job.Args) != 1 { job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name)