Prechádzať zdrojové kódy

Merge pull request #12070 from duglin/RemoveTableCLI

Last step in removing engine.Table from api/client/*
Phil Estes 10 rokov pred
rodič
commit
dec48d6708
6 zmenil súbory, kde vykonal 258 pridanie a 131 odobranie
  1. 53 54
      api/client/images.go
  2. 22 24
      api/client/ps.go
  3. 58 0
      api/common.go
  4. 33 0
      api/types/types.go
  5. 50 23
      daemon/list.go
  6. 42 30
      graph/list.go

+ 53 - 54
api/client/images.go

@@ -1,13 +1,14 @@
 package client
 
 import (
+	"encoding/json"
 	"fmt"
 	"net/url"
 	"strings"
 	"text/tabwriter"
 	"time"
 
-	"github.com/docker/docker/engine"
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
@@ -18,26 +19,26 @@ import (
 )
 
 // FIXME: --viz and --tree are deprecated. Remove them in a future version.
-func (cli *DockerCli) WalkTree(noTrunc bool, images *engine.Table, byParent map[string]*engine.Table, prefix string, printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)) {
-	length := images.Len()
+func (cli *DockerCli) WalkTree(noTrunc bool, images []*types.Image, byParent map[string][]*types.Image, prefix string, printNode func(cli *DockerCli, noTrunc bool, image *types.Image, prefix string)) {
+	length := len(images)
 	if length > 1 {
-		for index, image := range images.Data {
+		for index, image := range images {
 			if index+1 == length {
 				printNode(cli, noTrunc, image, prefix+"└─")
-				if subimages, exists := byParent[image.Get("Id")]; exists {
+				if subimages, exists := byParent[image.ID]; exists {
 					cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
 				}
 			} else {
 				printNode(cli, noTrunc, image, prefix+"\u251C─")
-				if subimages, exists := byParent[image.Get("Id")]; exists {
+				if subimages, exists := byParent[image.ID]; exists {
 					cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode)
 				}
 			}
 		}
 	} else {
-		for _, image := range images.Data {
+		for _, image := range images {
 			printNode(cli, noTrunc, image, prefix+"└─")
-			if subimages, exists := byParent[image.Get("Id")]; exists {
+			if subimages, exists := byParent[image.ID]; exists {
 				cli.WalkTree(noTrunc, subimages, byParent, prefix+"  ", printNode)
 			}
 		}
@@ -45,41 +46,41 @@ func (cli *DockerCli) WalkTree(noTrunc bool, images *engine.Table, byParent map[
 }
 
 // FIXME: --viz and --tree are deprecated. Remove them in a future version.
-func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) {
+func (cli *DockerCli) printVizNode(noTrunc bool, image *types.Image, prefix string) {
 	var (
 		imageID  string
 		parentID string
 	)
 	if noTrunc {
-		imageID = image.Get("Id")
-		parentID = image.Get("ParentId")
+		imageID = image.ID
+		parentID = image.ParentId
 	} else {
-		imageID = stringid.TruncateID(image.Get("Id"))
-		parentID = stringid.TruncateID(image.Get("ParentId"))
+		imageID = stringid.TruncateID(image.ID)
+		parentID = stringid.TruncateID(image.ParentId)
 	}
 	if parentID == "" {
 		fmt.Fprintf(cli.out, " base -> \"%s\" [style=invis]\n", imageID)
 	} else {
 		fmt.Fprintf(cli.out, " \"%s\" -> \"%s\"\n", parentID, imageID)
 	}
-	if image.GetList("RepoTags")[0] != "<none>:<none>" {
+	if image.RepoTags[0] != "<none>:<none>" {
 		fmt.Fprintf(cli.out, " \"%s\" [label=\"%s\\n%s\",shape=box,fillcolor=\"paleturquoise\",style=\"filled,rounded\"];\n",
-			imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n"))
+			imageID, imageID, strings.Join(image.RepoTags, "\\n"))
 	}
 }
 
 // FIXME: --viz and --tree are deprecated. Remove them in a future version.
-func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) {
+func (cli *DockerCli) printTreeNode(noTrunc bool, image *types.Image, prefix string) {
 	var imageID string
 	if noTrunc {
-		imageID = image.Get("Id")
+		imageID = image.ID
 	} else {
-		imageID = stringid.TruncateID(image.Get("Id"))
+		imageID = stringid.TruncateID(image.ID)
 	}
 
-	fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(float64(image.GetInt64("VirtualSize"))))
-	if image.GetList("RepoTags")[0] != "<none>:<none>" {
-		fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", "))
+	fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, units.HumanSize(float64(image.VirtualSize)))
+	if image.RepoTags[0] != "<none>:<none>" {
+		fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.RepoTags, ", "))
 	} else {
 		fmt.Fprint(cli.out, "\n")
 	}
@@ -101,7 +102,6 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 	flFilter := opts.NewListOpts(nil)
 	cmd.Var(&flFilter, []string{"f", "-filter"}, "Filter output based on conditions provided")
 	cmd.Require(flag.Max, 1)
-
 	cmd.ParseFlags(args, true)
 
 	// Consolidate all filter flags, and sanity check them early.
@@ -129,44 +129,44 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 			v.Set("filters", filterJSON)
 		}
 
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
+		rdr, _, err := cli.call("GET", "/images/json?"+v.Encode(), nil, nil)
 		if err != nil {
 			return err
 		}
 
-		outs := engine.NewTable("Created", 0)
-		if _, err := outs.ReadListFrom(body); err != nil {
+		images := []types.Image{}
+		err = json.NewDecoder(rdr).Decode(&images)
+		if err != nil {
 			return err
 		}
 
 		var (
-			printNode  func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)
-			startImage *engine.Env
+			printNode  func(cli *DockerCli, noTrunc bool, image *types.Image, prefix string)
+			startImage *types.Image
 
-			roots    = engine.NewTable("Created", outs.Len())
-			byParent = make(map[string]*engine.Table)
+			roots    = []*types.Image{}
+			byParent = make(map[string][]*types.Image)
 		)
 
-		for _, image := range outs.Data {
-			if image.Get("ParentId") == "" {
-				roots.Add(image)
+		for _, image := range images {
+			if image.ParentId == "" {
+				roots = append(roots, &image)
 			} else {
-				if children, exists := byParent[image.Get("ParentId")]; exists {
-					children.Add(image)
+				if children, exists := byParent[image.ParentId]; exists {
+					children = append(children, &image)
 				} else {
-					byParent[image.Get("ParentId")] = engine.NewTable("Created", 1)
-					byParent[image.Get("ParentId")].Add(image)
+					byParent[image.ParentId] = []*types.Image{&image}
 				}
 			}
 
 			if matchName != "" {
-				if matchName == image.Get("Id") || matchName == stringid.TruncateID(image.Get("Id")) {
-					startImage = image
+				if matchName == image.ID || matchName == stringid.TruncateID(image.ID) {
+					startImage = &image
 				}
 
-				for _, repotag := range image.GetList("RepoTags") {
+				for _, repotag := range image.RepoTags {
 					if repotag == matchName {
-						startImage = image
+						startImage = &image
 					}
 				}
 			}
@@ -180,8 +180,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 		}
 
 		if startImage != nil {
-			root := engine.NewTable("Created", 1)
-			root.Add(startImage)
+			root := []*types.Image{startImage}
 			cli.WalkTree(*noTrunc, root, byParent, "", printNode)
 		} else if matchName == "" {
 			cli.WalkTree(*noTrunc, roots, byParent, "", printNode)
@@ -207,14 +206,14 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 			v.Set("all", "1")
 		}
 
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
-
+		rdr, _, err := cli.call("GET", "/images/json?"+v.Encode(), nil, nil)
 		if err != nil {
 			return err
 		}
 
-		outs := engine.NewTable("Created", 0)
-		if _, err := outs.ReadListFrom(body); err != nil {
+		images := []types.Image{}
+		err = json.NewDecoder(rdr).Decode(&images)
+		if err != nil {
 			return err
 		}
 
@@ -227,14 +226,14 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 			}
 		}
 
-		for _, out := range outs.Data {
-			outID := out.Get("Id")
+		for _, image := range images {
+			ID := image.ID
 			if !*noTrunc {
-				outID = stringid.TruncateID(outID)
+				ID = stringid.TruncateID(ID)
 			}
 
-			repoTags := out.GetList("RepoTags")
-			repoDigests := out.GetList("RepoDigests")
+			repoTags := image.RepoTags
+			repoDigests := image.RepoDigests
 
 			if len(repoTags) == 1 && repoTags[0] == "<none>:<none>" && len(repoDigests) == 1 && repoDigests[0] == "<none>@<none>" {
 				// dangling image - clear out either repoTags or repoDigsts so we only show it once below
@@ -256,12 +255,12 @@ func (cli *DockerCli) CmdImages(args ...string) error {
 
 				if !*quiet {
 					if *showDigests {
-						fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize"))))
+						fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", repo, tag, digest, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize)))
 					} else {
-						fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), units.HumanSize(float64(out.GetInt64("VirtualSize"))))
+						fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, ID, units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(image.Created), 0))), units.HumanSize(float64(image.VirtualSize)))
 					}
 				} else {
-					fmt.Fprintln(w, outID)
+					fmt.Fprintln(w, ID)
 				}
 			}
 		}

+ 22 - 24
api/client/ps.go

@@ -1,6 +1,7 @@
 package client
 
 import (
+	"encoding/json"
 	"fmt"
 	"net/url"
 	"strconv"
@@ -9,7 +10,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api"
-	"github.com/docker/docker/engine"
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers/filters"
@@ -85,13 +86,14 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		v.Set("filters", filterJSON)
 	}
 
-	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, nil))
+	rdr, _, err := cli.call("GET", "/containers/json?"+v.Encode(), nil, nil)
 	if err != nil {
 		return err
 	}
 
-	outs := engine.NewTable("Created", 0)
-	if _, err := outs.ReadListFrom(body); err != nil {
+	containers := []types.Container{}
+	err = json.NewDecoder(rdr).Decode(&containers)
+	if err != nil {
 		return err
 	}
 
@@ -114,54 +116,50 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		return ss
 	}
 
-	for _, out := range outs.Data {
-		outID := out.Get("Id")
+	for _, container := range containers {
+		ID := container.ID
 
 		if !*noTrunc {
-			outID = stringid.TruncateID(outID)
+			ID = stringid.TruncateID(ID)
 		}
 
 		if *quiet {
-			fmt.Fprintln(w, outID)
+			fmt.Fprintln(w, ID)
 
 			continue
 		}
 
 		var (
-			outNames   = stripNamePrefix(out.GetList("Names"))
-			outCommand = strconv.Quote(out.Get("Command"))
-			ports      = engine.NewTable("", 0)
+			names   = stripNamePrefix(container.Names)
+			command = strconv.Quote(container.Command)
 		)
 
 		if !*noTrunc {
-			outCommand = utils.Trunc(outCommand, 20)
+			command = utils.Trunc(command, 20)
 
 			// only display the default name for the container with notrunc is passed
-			for _, name := range outNames {
+			for _, name := range names {
 				if len(strings.Split(name, "/")) == 1 {
-					outNames = []string{name}
-
+					names = []string{name}
 					break
 				}
 			}
 		}
 
-		ports.ReadListFrom([]byte(out.Get("Ports")))
-
-		image := out.Get("Image")
+		image := container.Image
 		if image == "" {
 			image = "<no image>"
 		}
 
-		fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, image, outCommand,
-			units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))),
-			out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","))
+		fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", ID, image, command,
+			units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(container.Created), 0))),
+			container.Status, api.NewDisplayablePorts(container.Ports), strings.Join(names, ","))
 
 		if *size {
-			if out.GetInt("SizeRootFs") > 0 {
-				fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(out.GetInt64("SizeRw"))), units.HumanSize(float64(out.GetInt64("SizeRootFs"))))
+			if container.SizeRootFs > 0 {
+				fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(container.SizeRw)), units.HumanSize(float64(container.SizeRootFs)))
 			} else {
-				fmt.Fprintf(w, "%s\n", units.HumanSize(float64(out.GetInt64("SizeRw"))))
+				fmt.Fprintf(w, "%s\n", units.HumanSize(float64(container.SizeRw)))
 			}
 
 			continue

+ 58 - 0
api/common.go

@@ -5,9 +5,11 @@ import (
 	"mime"
 	"os"
 	"path/filepath"
+	"sort"
 	"strings"
 
 	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/engine"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/version"
@@ -31,6 +33,7 @@ func ValidateHost(val string) (string, error) {
 }
 
 // TODO remove, used on < 1.5 in getContainersJSON
+// TODO this can go away when we get rid of engine.table
 func DisplayablePorts(ports *engine.Table) string {
 	var (
 		result          = []string{}
@@ -80,6 +83,61 @@ func DisplayablePorts(ports *engine.Table) string {
 	return strings.Join(result, ", ")
 }
 
+type ByPrivatePort []types.Port
+
+func (r ByPrivatePort) Len() int           { return len(r) }
+func (r ByPrivatePort) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
+func (r ByPrivatePort) Less(i, j int) bool { return r[i].PrivatePort < r[j].PrivatePort }
+
+// TODO Rename to DisplayablePorts (remove "New") when engine.Table goes away
+func NewDisplayablePorts(ports []types.Port) string {
+	var (
+		result          = []string{}
+		hostMappings    = []string{}
+		firstInGroupMap map[string]int
+		lastInGroupMap  map[string]int
+	)
+	firstInGroupMap = make(map[string]int)
+	lastInGroupMap = make(map[string]int)
+	sort.Sort(ByPrivatePort(ports))
+	for _, port := range ports {
+		var (
+			current      = port.PrivatePort
+			portKey      = port.Type
+			firstInGroup int
+			lastInGroup  int
+		)
+		if port.IP != "" {
+			if port.PublicPort != current {
+				hostMappings = append(hostMappings, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type))
+				continue
+			}
+			portKey = fmt.Sprintf("%s/%s", port.IP, port.Type)
+		}
+		firstInGroup = firstInGroupMap[portKey]
+		lastInGroup = lastInGroupMap[portKey]
+
+		if firstInGroup == 0 {
+			firstInGroupMap[portKey] = current
+			lastInGroupMap[portKey] = current
+			continue
+		}
+
+		if current == (lastInGroup + 1) {
+			lastInGroupMap[portKey] = current
+			continue
+		}
+		result = append(result, FormGroup(portKey, firstInGroup, lastInGroup))
+		firstInGroupMap[portKey] = current
+		lastInGroupMap[portKey] = current
+	}
+	for portKey, firstInGroup := range firstInGroupMap {
+		result = append(result, FormGroup(portKey, firstInGroup, lastInGroupMap[portKey]))
+	}
+	result = append(result, hostMappings...)
+	return strings.Join(result, ", ")
+}
+
 func FormGroup(key string, start, last int) string {
 	var (
 		group     string

+ 33 - 0
api/types/types.go

@@ -56,3 +56,36 @@ type ImageDelete struct {
 	Untagged string `json:",omitempty"`
 	Deleted  string `json:",omitempty"`
 }
+
+// GET "/images/json"
+type Image struct {
+	ID          string `json:"Id"`
+	ParentId    string
+	RepoTags    []string
+	RepoDigests []string
+	Created     int
+	Size        int
+	VirtualSize int
+	Labels      map[string]string
+}
+
+// GET  "/containers/json"
+type Port struct {
+	IP          string
+	PrivatePort int
+	PublicPort  int
+	Type        string
+}
+
+type Container struct {
+	ID         string            `json:"Id"`
+	Names      []string          `json:,omitempty"`
+	Image      string            `json:,omitempty"`
+	Command    string            `json:,omitempty"`
+	Created    int               `json:,omitempty"`
+	Ports      []Port            `json:,omitempty"`
+	SizeRw     int               `json:,omitempty"`
+	SizeRootFs int               `json:,omitempty"`
+	Labels     map[string]string `json:,omitempty"`
+	Status     string            `json:,omitempty"`
+}

+ 50 - 23
daemon/list.go

@@ -1,18 +1,21 @@
 package daemon
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
+	"sort"
 	"strconv"
 	"strings"
 
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/engine"
 	"github.com/docker/docker/graph"
+	"github.com/docker/docker/nat"
 	"github.com/docker/docker/pkg/graphdb"
-	"github.com/docker/docker/utils"
-
-	"github.com/docker/docker/engine"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/parsers/filters"
+	"github.com/docker/docker/utils"
 )
 
 // List returns an array of all containers registered in the daemon.
@@ -20,6 +23,12 @@ func (daemon *Daemon) List() []*Container {
 	return daemon.containers.List()
 }
 
+type ByCreated []types.Container
+
+func (r ByCreated) Len() int           { return len(r) }
+func (r ByCreated) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
+func (r ByCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
+
 func (daemon *Daemon) Containers(job *engine.Job) error {
 	var (
 		foundBefore bool
@@ -32,7 +41,7 @@ func (daemon *Daemon) Containers(job *engine.Job) error {
 		psFilters   filters.Args
 		filtExited  []int
 	)
-	outs := engine.NewTable("Created", 0)
+	containers := []types.Container{}
 
 	psFilters, err := filters.FromParam(job.Getenv("filters"))
 	if err != nil {
@@ -126,15 +135,16 @@ func (daemon *Daemon) Containers(job *engine.Job) error {
 			return nil
 		}
 		displayed++
-		out := &engine.Env{}
-		out.SetJson("Id", container.ID)
-		out.SetList("Names", names[container.ID])
+		newC := types.Container{
+			ID:    container.ID,
+			Names: names[container.ID],
+		}
 		img := container.Config.Image
 		_, tag := parsers.ParseRepositoryTag(container.Config.Image)
 		if tag == "" {
 			img = utils.ImageReference(img, graph.DEFAULTTAG)
 		}
-		out.SetJson("Image", img)
+		newC.Image = img
 		if len(container.Args) > 0 {
 			args := []string{}
 			for _, arg := range container.Args {
@@ -146,24 +156,41 @@ func (daemon *Daemon) Containers(job *engine.Job) error {
 			}
 			argsAsString := strings.Join(args, " ")
 
-			out.Set("Command", fmt.Sprintf("\"%s %s\"", container.Path, argsAsString))
+			newC.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
 		} else {
-			out.Set("Command", fmt.Sprintf("\"%s\"", container.Path))
-		}
-		out.SetInt64("Created", container.Created.Unix())
-		out.Set("Status", container.State.String())
-		str, err := container.NetworkSettings.PortMappingAPI().ToListString()
-		if err != nil {
-			return err
+			newC.Command = fmt.Sprintf("%s", container.Path)
+		}
+		newC.Created = int(container.Created.Unix())
+		newC.Status = container.State.String()
+
+		newC.Ports = []types.Port{}
+		for port, bindings := range container.NetworkSettings.Ports {
+			p, _ := nat.ParsePort(port.Port())
+			if len(bindings) == 0 {
+				newC.Ports = append(newC.Ports, types.Port{
+					PrivatePort: p,
+					Type:        port.Proto(),
+				})
+				continue
+			}
+			for _, binding := range bindings {
+				h, _ := nat.ParsePort(binding.HostPort)
+				newC.Ports = append(newC.Ports, types.Port{
+					PrivatePort: p,
+					PublicPort:  h,
+					Type:        port.Proto(),
+					IP:          binding.HostIp,
+				})
+			}
 		}
-		out.Set("Ports", str)
+
 		if size {
 			sizeRw, sizeRootFs := container.GetSize()
-			out.SetInt64("SizeRw", sizeRw)
-			out.SetInt64("SizeRootFs", sizeRootFs)
+			newC.SizeRw = int(sizeRw)
+			newC.SizeRootFs = int(sizeRootFs)
 		}
-		out.SetJson("Labels", container.Config.Labels)
-		outs.Add(out)
+		newC.Labels = container.Config.Labels
+		containers = append(containers, newC)
 		return nil
 	}
 
@@ -175,8 +202,8 @@ func (daemon *Daemon) Containers(job *engine.Job) error {
 			break
 		}
 	}
-	outs.ReverseSort()
-	if _, err := outs.WriteListTo(job.Stdout); err != nil {
+	sort.Sort(sort.Reverse(ByCreated(containers)))
+	if err = json.NewEncoder(job.Stdout).Encode(containers); err != nil {
 		return err
 	}
 	return nil

+ 42 - 30
graph/list.go

@@ -1,11 +1,14 @@
 package graph
 
 import (
+	"encoding/json"
 	"fmt"
 	"log"
 	"path"
+	"sort"
 	"strings"
 
+	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/engine"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/parsers/filters"
@@ -17,6 +20,12 @@ var acceptedImageFilterTags = map[string]struct{}{
 	"label":    {},
 }
 
+type ByCreated []*types.Image
+
+func (r ByCreated) Len() int           { return len(r) }
+func (r ByCreated) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
+func (r ByCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
+
 func (s *TagStore) CmdImages(job *engine.Job) error {
 	var (
 		allImages  map[string]*image.Image
@@ -53,7 +62,8 @@ func (s *TagStore) CmdImages(job *engine.Job) error {
 	if err != nil {
 		return err
 	}
-	lookup := make(map[string]*engine.Env)
+
+	lookup := make(map[string]*types.Image)
 	s.Lock()
 	for repoName, repository := range s.Repositories {
 		if job.Getenv("filter") != "" {
@@ -69,12 +79,12 @@ func (s *TagStore) CmdImages(job *engine.Job) error {
 				continue
 			}
 
-			if out, exists := lookup[id]; exists {
+			if lImage, exists := lookup[id]; exists {
 				if filtTagged {
 					if utils.DigestReference(ref) {
-						out.SetList("RepoDigests", append(out.GetList("RepoDigests"), imgRef))
+						lImage.RepoDigests = append(lImage.RepoDigests, imgRef)
 					} else { // Tag Ref.
-						out.SetList("RepoTags", append(out.GetList("RepoTags"), imgRef))
+						lImage.RepoTags = append(lImage.RepoTags, imgRef)
 					}
 				}
 			} else {
@@ -84,23 +94,23 @@ func (s *TagStore) CmdImages(job *engine.Job) error {
 					continue
 				}
 				if filtTagged {
-					out := &engine.Env{}
-					out.SetJson("ParentId", image.Parent)
-					out.SetJson("Id", image.ID)
-					out.SetInt64("Created", image.Created.Unix())
-					out.SetInt64("Size", image.Size)
-					out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
-					out.SetJson("Labels", image.ContainerConfig.Labels)
+					newImage := new(types.Image)
+					newImage.ParentId = image.Parent
+					newImage.ID = image.ID
+					newImage.Created = int(image.Created.Unix())
+					newImage.Size = int(image.Size)
+					newImage.VirtualSize = int(image.GetParentsSize(0) + image.Size)
+					newImage.Labels = image.ContainerConfig.Labels
 
 					if utils.DigestReference(ref) {
-						out.SetList("RepoTags", []string{})
-						out.SetList("RepoDigests", []string{imgRef})
+						newImage.RepoTags = []string{}
+						newImage.RepoDigests = []string{imgRef}
 					} else {
-						out.SetList("RepoTags", []string{imgRef})
-						out.SetList("RepoDigests", []string{})
+						newImage.RepoTags = []string{imgRef}
+						newImage.RepoDigests = []string{}
 					}
 
-					lookup[id] = out
+					lookup[id] = newImage
 				}
 			}
 
@@ -108,9 +118,9 @@ func (s *TagStore) CmdImages(job *engine.Job) error {
 	}
 	s.Unlock()
 
-	outs := engine.NewTable("Created", len(lookup))
+	images := []*types.Image{}
 	for _, value := range lookup {
-		outs.Add(value)
+		images = append(images, value)
 	}
 
 	// Display images which aren't part of a repository/tag
@@ -119,21 +129,23 @@ func (s *TagStore) CmdImages(job *engine.Job) error {
 			if !imageFilters.MatchKVList("label", image.ContainerConfig.Labels) {
 				continue
 			}
-			out := &engine.Env{}
-			out.SetJson("ParentId", image.Parent)
-			out.SetList("RepoTags", []string{"<none>:<none>"})
-			out.SetList("RepoDigests", []string{"<none>@<none>"})
-			out.SetJson("Id", image.ID)
-			out.SetInt64("Created", image.Created.Unix())
-			out.SetInt64("Size", image.Size)
-			out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
-			out.SetJson("Labels", image.ContainerConfig.Labels)
-			outs.Add(out)
+			newImage := new(types.Image)
+			newImage.ParentId = image.Parent
+			newImage.RepoTags = []string{"<none>:<none>"}
+			newImage.RepoDigests = []string{"<none>@<none>"}
+			newImage.ID = image.ID
+			newImage.Created = int(image.Created.Unix())
+			newImage.Size = int(image.Size)
+			newImage.VirtualSize = int(image.GetParentsSize(0) + image.Size)
+			newImage.Labels = image.ContainerConfig.Labels
+
+			images = append(images, newImage)
 		}
 	}
 
-	outs.ReverseSort()
-	if _, err := outs.WriteListTo(job.Stdout); err != nil {
+	sort.Sort(sort.Reverse(ByCreated(images)))
+
+	if err = json.NewEncoder(job.Stdout).Encode(images); err != nil {
 		return err
 	}
 	return nil