Browse code

Move containers to a job

Docker-DCO-1.1-Signed-off-by: Victor Vieux <victor.vieux@docker.com> (github: vieux)

Victor Vieux authored on 2014/01/17 07:00:18
Showing 8 changed files
... ...
@@ -291,32 +291,37 @@ func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *h
291 291
 	if err := parseForm(r); err != nil {
292 292
 		return err
293 293
 	}
294
-	all, err := getBoolParam(r.Form.Get("all"))
295
-	if err != nil {
294
+	var (
295
+		err  error
296
+		outs *engine.Table
297
+		job  = srv.Eng.Job("containers")
298
+	)
299
+
300
+	job.Setenv("all", r.Form.Get("all"))
301
+	job.Setenv("size", r.Form.Get("size"))
302
+	job.Setenv("since", r.Form.Get("since"))
303
+	job.Setenv("before", r.Form.Get("before"))
304
+	job.Setenv("limit", r.Form.Get("limit"))
305
+
306
+	if version > 1.5 {
307
+		job.Stdout.Add(w)
308
+	} else if outs, err = job.Stdout.AddTable(); err != nil {
296 309
 		return err
297 310
 	}
298
-	size, err := getBoolParam(r.Form.Get("size"))
299
-	if err != nil {
311
+	if err = job.Run(); err != nil {
300 312
 		return err
301 313
 	}
302
-	since := r.Form.Get("since")
303
-	before := r.Form.Get("before")
304
-	n, err := strconv.Atoi(r.Form.Get("limit"))
305
-	if err != nil {
306
-		n = -1
307
-	}
308
-
309
-	outs := srv.Containers(all, size, n, since, before)
310
-
311
-	if version < 1.5 {
312
-		outs2 := []APIContainersOld{}
313
-		for _, ctnr := range outs {
314
-			outs2 = append(outs2, *ctnr.ToLegacy())
314
+	if version < 1.5 { // Convert to legacy format
315
+		for _, out := range outs.Data {
316
+			ports := engine.NewTable("", 0)
317
+			ports.ReadListFrom([]byte(out.Get("Ports")))
318
+			out.Set("Ports", displayablePorts(ports))
319
+		}
320
+		if _, err = outs.WriteListTo(w); err != nil {
321
+			return err
315 322
 		}
316
-
317
-		return writeJSON(w, http.StatusOK, outs2)
318 323
 	}
319
-	return writeJSON(w, http.StatusOK, outs)
324
+	return nil
320 325
 }
321 326
 
322 327
 func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -11,29 +11,6 @@ type (
11 11
 		Untagged string `json:",omitempty"`
12 12
 	}
13 13
 
14
-	APIContainers struct {
15
-		ID         string `json:"Id"`
16
-		Image      string
17
-		Command    string
18
-		Created    int64
19
-		Status     string
20
-		Ports      []APIPort
21
-		SizeRw     int64
22
-		SizeRootFs int64
23
-		Names      []string
24
-	}
25
-
26
-	APIContainersOld struct {
27
-		ID         string `json:"Id"`
28
-		Image      string
29
-		Command    string
30
-		Created    int64
31
-		Status     string
32
-		Ports      string
33
-		SizeRw     int64
34
-		SizeRootFs int64
35
-	}
36
-
37 14
 	APIID struct {
38 15
 		ID string `json:"Id"`
39 16
 	}
... ...
@@ -68,16 +45,3 @@ type (
68 68
 		HostPath string
69 69
 	}
70 70
 )
71
-
72
-func (api APIContainers) ToLegacy() *APIContainersOld {
73
-	return &APIContainersOld{
74
-		ID:         api.ID,
75
-		Image:      api.Image,
76
-		Command:    api.Command,
77
-		Created:    api.Created,
78
-		Status:     api.Status,
79
-		Ports:      displayablePorts(api.Ports),
80
-		SizeRw:     api.SizeRw,
81
-		SizeRootFs: api.SizeRootFs,
82
-	}
83
-}
... ...
@@ -1310,13 +1310,13 @@ func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix stri
1310 1310
 	}
1311 1311
 }
1312 1312
 
1313
-func displayablePorts(ports []APIPort) string {
1313
+func displayablePorts(ports *engine.Table) string {
1314 1314
 	result := []string{}
1315
-	for _, port := range ports {
1316
-		if port.IP == "" {
1317
-			result = append(result, fmt.Sprintf("%d/%s", port.PublicPort, port.Type))
1315
+	for _, port := range ports.Data {
1316
+		if port.Get("IP") == "" {
1317
+			result = append(result, fmt.Sprintf("%d/%s", port.GetInt("PublicPort"), port.Get("Type")))
1318 1318
 		} else {
1319
-			result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type))
1319
+			result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.Get("IP"), port.GetInt("PublicPort"), port.GetInt("PrivatePort"), port.Get("Type")))
1320 1320
 		}
1321 1321
 	}
1322 1322
 	sort.Strings(result)
... ...
@@ -1362,9 +1362,8 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1362 1362
 		return err
1363 1363
 	}
1364 1364
 
1365
-	var outs []APIContainers
1366
-	err = json.Unmarshal(body, &outs)
1367
-	if err != nil {
1365
+	outs := engine.NewTable("Created", 0)
1366
+	if _, err := outs.ReadListFrom(body); err != nil {
1368 1367
 		return err
1369 1368
 	}
1370 1369
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
... ...
@@ -1377,32 +1376,42 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1377 1377
 		}
1378 1378
 	}
1379 1379
 
1380
-	for _, out := range outs {
1380
+	for _, out := range outs.Data {
1381
+		var (
1382
+			outID    = out.Get("ID")
1383
+			outNames = out.GetList("Names")
1384
+		)
1385
+
1381 1386
 		if !*noTrunc {
1382
-			out.ID = utils.TruncateID(out.ID)
1387
+			outID = utils.TruncateID(outID)
1383 1388
 		}
1384 1389
 
1385 1390
 		// Remove the leading / from the names
1386
-		for i := 0; i < len(out.Names); i++ {
1387
-			out.Names[i] = out.Names[i][1:]
1391
+		for i := 0; i < len(outNames); i++ {
1392
+			outNames[i] = outNames[i][1:]
1388 1393
 		}
1389 1394
 
1390 1395
 		if !*quiet {
1396
+			var (
1397
+				outCommand = out.Get("Command")
1398
+				ports      = engine.NewTable("", 0)
1399
+			)
1391 1400
 			if !*noTrunc {
1392
-				out.Command = utils.Trunc(out.Command, 20)
1401
+				outCommand = utils.Trunc(outCommand, 20)
1393 1402
 			}
1394
-			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, ","))
1403
+			ports.ReadListFrom([]byte(out.Get("Ports")))
1404
+			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, ","))
1395 1405
 			if *size {
1396
-				if out.SizeRootFs > 0 {
1397
-					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.SizeRw), utils.HumanSize(out.SizeRootFs))
1406
+				if out.GetInt("SizeRootFs") > 0 {
1407
+					fmt.Fprintf(w, "%s (virtual %s)\n", utils.HumanSize(out.GetInt64("SizeRw")), utils.HumanSize(out.GetInt64("SizeRootFs")))
1398 1408
 				} else {
1399
-					fmt.Fprintf(w, "%s\n", utils.HumanSize(out.SizeRw))
1409
+					fmt.Fprintf(w, "%s\n", utils.HumanSize(out.GetInt64("SizeRw")))
1400 1410
 				}
1401 1411
 			} else {
1402 1412
 				fmt.Fprint(w, "\n")
1403 1413
 			}
1404 1414
 		} else {
1405
-			fmt.Fprintln(w, out.ID)
1415
+			fmt.Fprintln(w, outID)
1406 1416
 		}
1407 1417
 	}
1408 1418
 
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"errors"
6 6
 	"fmt"
7 7
 	"github.com/dotcloud/docker/archive"
8
+	"github.com/dotcloud/docker/engine"
8 9
 	"github.com/dotcloud/docker/execdriver"
9 10
 	"github.com/dotcloud/docker/graphdriver"
10 11
 	"github.com/dotcloud/docker/networkdriver/ipallocator"
... ...
@@ -175,29 +176,28 @@ type NetworkSettings struct {
175 175
 	Ports       map[Port][]PortBinding
176 176
 }
177 177
 
178
-func (settings *NetworkSettings) PortMappingAPI() []APIPort {
179
-	var mapping []APIPort
178
+func (settings *NetworkSettings) PortMappingAPI() *engine.Table {
179
+	var outs = engine.NewTable("", 0)
180 180
 	for port, bindings := range settings.Ports {
181 181
 		p, _ := parsePort(port.Port())
182 182
 		if len(bindings) == 0 {
183
-			mapping = append(mapping, APIPort{
184
-				PublicPort: int64(p),
185
-				Type:       port.Proto(),
186
-			})
183
+			out := &engine.Env{}
184
+			out.SetInt("PublicPort", p)
185
+			out.Set("Type", port.Proto())
186
+			outs.Add(out)
187 187
 			continue
188 188
 		}
189 189
 		for _, binding := range bindings {
190
-			p, _ := parsePort(port.Port())
190
+			out := &engine.Env{}
191 191
 			h, _ := parsePort(binding.HostPort)
192
-			mapping = append(mapping, APIPort{
193
-				PrivatePort: int64(p),
194
-				PublicPort:  int64(h),
195
-				Type:        port.Proto(),
196
-				IP:          binding.HostIp,
197
-			})
192
+			out.SetInt("PrivatePort", p)
193
+			out.SetInt("PublicPort", h)
194
+			out.Set("Type", port.Proto())
195
+			out.Set("IP", binding.HostIp)
196
+			outs.Add(out)
198 197
 		}
199 198
 	}
200
-	return mapping
199
+	return outs
201 200
 }
202 201
 
203 202
 // Inject the io.Reader at the given path. Note: do not close the reader
... ...
@@ -313,6 +313,14 @@ func (t *Table) WriteListTo(dst io.Writer) (n int64, err error) {
313 313
 	return n + 1, nil
314 314
 }
315 315
 
316
+func (t *Table) ToListString() (string, error) {
317
+	buffer := bytes.NewBuffer(nil)
318
+	if _, err := t.WriteListTo(buffer); err != nil {
319
+		return "", err
320
+	}
321
+	return buffer.String(), nil
322
+}
323
+
316 324
 func (t *Table) WriteTo(dst io.Writer) (n int64, err error) {
317 325
 	for _, env := range t.Data {
318 326
 		bytes, err := env.WriteTo(dst)
... ...
@@ -302,7 +302,16 @@ func TestGetContainersJSON(t *testing.T) {
302 302
 	defer mkRuntimeFromEngine(eng, t).Nuke()
303 303
 	srv := mkServerFromEngine(eng, t)
304 304
 
305
-	beginLen := len(srv.Containers(true, false, -1, "", ""))
305
+	job := eng.Job("containers")
306
+	job.SetenvBool("all", true)
307
+	outs, err := job.Stdout.AddTable()
308
+	if err != nil {
309
+		t.Fatal(err)
310
+	}
311
+	if err := job.Run(); err != nil {
312
+		t.Fatal(err)
313
+	}
314
+	beginLen := len(outs.Data)
306 315
 
307 316
 	containerID := createTestContainer(eng, &docker.Config{
308 317
 		Image: unitTestImageID,
... ...
@@ -323,15 +332,15 @@ func TestGetContainersJSON(t *testing.T) {
323 323
 		t.Fatal(err)
324 324
 	}
325 325
 	assertHttpNotError(r, t)
326
-	containers := []docker.APIContainers{}
327
-	if err := json.Unmarshal(r.Body.Bytes(), &containers); err != nil {
326
+	containers := engine.NewTable("", 0)
327
+	if _, err := containers.ReadListFrom(r.Body.Bytes()); err != nil {
328 328
 		t.Fatal(err)
329 329
 	}
330
-	if len(containers) != beginLen+1 {
331
-		t.Fatalf("Expected %d container, %d found (started with: %d)", beginLen+1, len(containers), beginLen)
330
+	if len(containers.Data) != beginLen+1 {
331
+		t.Fatalf("Expected %d container, %d found (started with: %d)", beginLen+1, len(containers.Data), beginLen)
332 332
 	}
333
-	if containers[0].ID != containerID {
334
-		t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", containerID, containers[0].ID)
333
+	if id := containers.Data[0].Get("ID"); id != containerID {
334
+		t.Fatalf("Container ID mismatch. Expected: %s, received: %s\n", containerID, id)
335 335
 	}
336 336
 }
337 337
 
... ...
@@ -69,7 +69,6 @@ func TestImageTagImageDelete(t *testing.T) {
69 69
 
70 70
 func TestCreateRm(t *testing.T) {
71 71
 	eng := NewTestEngine(t)
72
-	srv := mkServerFromEngine(eng, t)
73 72
 	defer mkRuntimeFromEngine(eng, t).Nuke()
74 73
 
75 74
 	config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
... ...
@@ -79,25 +78,44 @@ func TestCreateRm(t *testing.T) {
79 79
 
80 80
 	id := createTestContainer(eng, config, t)
81 81
 
82
-	if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 {
83
-		t.Errorf("Expected 1 container, %v found", len(c))
82
+	job := eng.Job("containers")
83
+	job.SetenvBool("all", true)
84
+	outs, err := job.Stdout.AddListTable()
85
+	if err != nil {
86
+		t.Fatal(err)
87
+	}
88
+	if err := job.Run(); err != nil {
89
+		t.Fatal(err)
90
+	}
91
+
92
+	if len(outs.Data) != 1 {
93
+		t.Errorf("Expected 1 container, %v found", len(outs.Data))
84 94
 	}
85 95
 
86
-	job := eng.Job("container_delete", id)
96
+	job = eng.Job("container_delete", id)
87 97
 	job.SetenvBool("removeVolume", true)
88 98
 	if err := job.Run(); err != nil {
89 99
 		t.Fatal(err)
90 100
 	}
91 101
 
92
-	if c := srv.Containers(true, false, -1, "", ""); len(c) != 0 {
93
-		t.Errorf("Expected 0 container, %v found", len(c))
102
+	job = eng.Job("containers")
103
+	job.SetenvBool("all", true)
104
+	outs, err = job.Stdout.AddListTable()
105
+	if err != nil {
106
+		t.Fatal(err)
107
+	}
108
+	if err := job.Run(); err != nil {
109
+		t.Fatal(err)
110
+	}
111
+
112
+	if len(outs.Data) != 0 {
113
+		t.Errorf("Expected 0 container, %v found", len(outs.Data))
94 114
 	}
95 115
 
96 116
 }
97 117
 
98 118
 func TestCreateRmVolumes(t *testing.T) {
99 119
 	eng := NewTestEngine(t)
100
-	srv := mkServerFromEngine(eng, t)
101 120
 	defer mkRuntimeFromEngine(eng, t).Nuke()
102 121
 
103 122
 	config, hostConfig, _, err := docker.ParseRun([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil)
... ...
@@ -107,11 +125,21 @@ func TestCreateRmVolumes(t *testing.T) {
107 107
 
108 108
 	id := createTestContainer(eng, config, t)
109 109
 
110
-	if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 {
111
-		t.Errorf("Expected 1 container, %v found", len(c))
110
+	job := eng.Job("containers")
111
+	job.SetenvBool("all", true)
112
+	outs, err := job.Stdout.AddListTable()
113
+	if err != nil {
114
+		t.Fatal(err)
115
+	}
116
+	if err := job.Run(); err != nil {
117
+		t.Fatal(err)
112 118
 	}
113 119
 
114
-	job := eng.Job("start", id)
120
+	if len(outs.Data) != 1 {
121
+		t.Errorf("Expected 1 container, %v found", len(outs.Data))
122
+	}
123
+
124
+	job = eng.Job("start", id)
115 125
 	if err := job.ImportEnv(hostConfig); err != nil {
116 126
 		t.Fatal(err)
117 127
 	}
... ...
@@ -131,8 +159,18 @@ func TestCreateRmVolumes(t *testing.T) {
131 131
 		t.Fatal(err)
132 132
 	}
133 133
 
134
-	if c := srv.Containers(true, false, -1, "", ""); len(c) != 0 {
135
-		t.Errorf("Expected 0 container, %v found", len(c))
134
+	job = eng.Job("containers")
135
+	job.SetenvBool("all", true)
136
+	outs, err = job.Stdout.AddListTable()
137
+	if err != nil {
138
+		t.Fatal(err)
139
+	}
140
+	if err := job.Run(); err != nil {
141
+		t.Fatal(err)
142
+	}
143
+
144
+	if len(outs.Data) != 0 {
145
+		t.Errorf("Expected 0 container, %v found", len(outs.Data))
136 146
 	}
137 147
 }
138 148
 
... ...
@@ -169,11 +207,21 @@ func TestRestartKillWait(t *testing.T) {
169 169
 
170 170
 	id := createTestContainer(eng, config, t)
171 171
 
172
-	if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 {
173
-		t.Errorf("Expected 1 container, %v found", len(c))
172
+	job := eng.Job("containers")
173
+	job.SetenvBool("all", true)
174
+	outs, err := job.Stdout.AddListTable()
175
+	if err != nil {
176
+		t.Fatal(err)
177
+	}
178
+	if err := job.Run(); err != nil {
179
+		t.Fatal(err)
174 180
 	}
175 181
 
176
-	job := eng.Job("start", id)
182
+	if len(outs.Data) != 1 {
183
+		t.Errorf("Expected 1 container, %v found", len(outs.Data))
184
+	}
185
+
186
+	job = eng.Job("start", id)
177 187
 	if err := job.ImportEnv(hostConfig); err != nil {
178 188
 		t.Fatal(err)
179 189
 	}
... ...
@@ -200,13 +248,23 @@ func TestRestartKillWait(t *testing.T) {
200 200
 	}
201 201
 
202 202
 	srv = mkServerFromEngine(eng, t)
203
-	c := srv.Containers(true, false, -1, "", "")
204
-	if len(c) != 1 {
205
-		t.Errorf("Expected 1 container, %v found", len(c))
203
+
204
+	job = srv.Eng.Job("containers")
205
+	job.SetenvBool("all", true)
206
+	outs, err = job.Stdout.AddListTable()
207
+	if err != nil {
208
+		t.Fatal(err)
209
+	}
210
+	if err := job.Run(); err != nil {
211
+		t.Fatal(err)
212
+	}
213
+
214
+	if len(outs.Data) != 1 {
215
+		t.Errorf("Expected 1 container, %v found", len(outs.Data))
206 216
 	}
207 217
 
208 218
 	setTimeout(t, "Waiting on stopped container timedout", 5*time.Second, func() {
209
-		job = srv.Eng.Job("wait", c[0].ID)
219
+		job = srv.Eng.Job("wait", outs.Data[0].Get("ID"))
210 220
 		var statusStr string
211 221
 		job.Stdout.AddString(&statusStr)
212 222
 		if err := job.Run(); err != nil {
... ...
@@ -227,11 +285,21 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
227 227
 
228 228
 	id := createTestContainer(eng, config, t)
229 229
 
230
-	if c := srv.Containers(true, false, -1, "", ""); len(c) != 1 {
231
-		t.Errorf("Expected 1 container, %v found", len(c))
230
+	job := srv.Eng.Job("containers")
231
+	job.SetenvBool("all", true)
232
+	outs, err := job.Stdout.AddListTable()
233
+	if err != nil {
234
+		t.Fatal(err)
235
+	}
236
+	if err := job.Run(); err != nil {
237
+		t.Fatal(err)
238
+	}
239
+
240
+	if len(outs.Data) != 1 {
241
+		t.Errorf("Expected 1 container, %v found", len(outs.Data))
232 242
 	}
233 243
 
234
-	job := eng.Job("start", id)
244
+	job = eng.Job("start", id)
235 245
 	if err := job.ImportEnv(hostConfig); err != nil {
236 246
 		t.Fatal(err)
237 247
 	}
... ...
@@ -270,8 +338,18 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
270 270
 		t.Fatal(err)
271 271
 	}
272 272
 
273
-	if c := srv.Containers(true, false, -1, "", ""); len(c) != 0 {
274
-		t.Errorf("Expected 0 container, %v found", len(c))
273
+	job = srv.Eng.Job("containers")
274
+	job.SetenvBool("all", true)
275
+	outs, err = job.Stdout.AddListTable()
276
+	if err != nil {
277
+		t.Fatal(err)
278
+	}
279
+	if err := job.Run(); err != nil {
280
+		t.Fatal(err)
281
+	}
282
+
283
+	if len(outs.Data) != 0 {
284
+		t.Errorf("Expected 0 container, %v found", len(outs.Data))
275 285
 	}
276 286
 }
277 287
 
... ...
@@ -465,10 +543,18 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
465 465
 		t.Fatal("No id returned")
466 466
 	}
467 467
 
468
-	containers := srv.Containers(true, false, -1, "", "")
468
+	job := srv.Eng.Job("containers")
469
+	job.SetenvBool("all", true)
470
+	outs, err := job.Stdout.AddListTable()
471
+	if err != nil {
472
+		t.Fatal(err)
473
+	}
474
+	if err := job.Run(); err != nil {
475
+		t.Fatal(err)
476
+	}
469 477
 
470
-	if len(containers) != 1 {
471
-		t.Fatalf("Expected 1 container got %d", len(containers))
478
+	if len(outs.Data) != 1 {
479
+		t.Fatalf("Expected 1 container got %d", len(outs.Data))
472 480
 	}
473 481
 
474 482
 	// Try to remove the tag
... ...
@@ -103,6 +103,7 @@ func jobInitApi(job *engine.Job) engine.Status {
103 103
 		"inspect":          srv.JobInspect,
104 104
 		"events":           srv.Events,
105 105
 		"push":             srv.ImagePush,
106
+		"containers":       srv.Containers,
106 107
 	} {
107 108
 		if err := job.Eng.Register(name, handler); err != nil {
108 109
 			job.Error(err)
... ...
@@ -1055,10 +1056,17 @@ func (srv *Server) ContainerChanges(job *engine.Job) engine.Status {
1055 1055
 	return engine.StatusOK
1056 1056
 }
1057 1057
 
1058
-func (srv *Server) Containers(all, size bool, n int, since, before string) []APIContainers {
1059
-	var foundBefore bool
1060
-	var displayed int
1061
-	out := []APIContainers{}
1058
+func (srv *Server) Containers(job *engine.Job) engine.Status {
1059
+	var (
1060
+		foundBefore bool
1061
+		displayed   int
1062
+		all         = job.GetenvBool("all")
1063
+		since       = job.Getenv("since")
1064
+		before      = job.Getenv("before")
1065
+		n           = job.GetenvInt("limit")
1066
+		size        = job.GetenvBool("size")
1067
+	)
1068
+	outs := engine.NewTable("Created", 0)
1062 1069
 
1063 1070
 	names := map[string][]string{}
1064 1071
 	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
1083 1083
 			break
1084 1084
 		}
1085 1085
 		displayed++
1086
-		c := createAPIContainer(names[container.ID], container, size, srv.runtime)
1087
-		out = append(out, c)
1088
-	}
1089
-	return out
1090
-}
1091
-
1092
-func createAPIContainer(names []string, container *Container, size bool, runtime *Runtime) APIContainers {
1093
-	c := APIContainers{
1094
-		ID: container.ID,
1086
+		out := &engine.Env{}
1087
+		out.Set("ID", container.ID)
1088
+		out.SetList("Names", names[container.ID])
1089
+		out.Set("Image", srv.runtime.repositories.ImageName(container.Image))
1090
+		out.Set("Command", fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " ")))
1091
+		out.SetInt64("Created", container.Created.Unix())
1092
+		out.Set("Status", container.State.String())
1093
+		str, err := container.NetworkSettings.PortMappingAPI().ToListString()
1094
+		if err != nil {
1095
+			job.Error(err)
1096
+			return engine.StatusErr
1097
+		}
1098
+		out.Set("Ports", str)
1099
+		if size {
1100
+			sizeRw, sizeRootFs := container.GetSize()
1101
+			out.SetInt64("SizeRw", sizeRw)
1102
+			out.SetInt64("SizeRootFs", sizeRootFs)
1103
+		}
1104
+		outs.Add(out)
1095 1105
 	}
1096
-	c.Names = names
1097
-	c.Image = runtime.repositories.ImageName(container.Image)
1098
-	c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
1099
-	c.Created = container.Created.Unix()
1100
-	c.Status = container.State.String()
1101
-	c.Ports = container.NetworkSettings.PortMappingAPI()
1102
-	if size {
1103
-		c.SizeRw, c.SizeRootFs = container.GetSize()
1106
+	outs.ReverseSort()
1107
+	if _, err := outs.WriteListTo(job.Stdout); err != nil {
1108
+		job.Error(err)
1109
+		return engine.StatusErr
1104 1110
 	}
1105
-	return c
1111
+	return engine.StatusOK
1106 1112
 }
1113
+
1107 1114
 func (srv *Server) ContainerCommit(job *engine.Job) engine.Status {
1108 1115
 	if len(job.Args) != 1 {
1109 1116
 		job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name)