Merge pull request #3575 from shykes/engine-get-images
move `docker images` to a job
This commit is contained in:
commit
9792df2080
16 changed files with 397 additions and 324 deletions
59
api.go
59
api.go
|
@ -10,6 +10,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/dotcloud/docker/archive"
|
||||
"github.com/dotcloud/docker/auth"
|
||||
"github.com/dotcloud/docker/engine"
|
||||
"github.com/dotcloud/docker/pkg/systemd"
|
||||
"github.com/dotcloud/docker/utils"
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -28,7 +29,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
APIVERSION = 1.8
|
||||
APIVERSION = 1.9
|
||||
DEFAULTHTTPHOST = "127.0.0.1"
|
||||
DEFAULTHTTPPORT = 4243
|
||||
DEFAULTUNIXSOCKET = "/var/run/docker.sock"
|
||||
|
@ -182,26 +183,54 @@ func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.
|
|||
return err
|
||||
}
|
||||
|
||||
all, err := getBoolParam(r.Form.Get("all"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filter := r.Form.Get("filter")
|
||||
var (
|
||||
buffer *bytes.Buffer
|
||||
job = srv.Eng.Job("images")
|
||||
)
|
||||
|
||||
outs, err := srv.Images(all, filter)
|
||||
if err != nil {
|
||||
job.Setenv("filter", r.Form.Get("filter"))
|
||||
job.Setenv("all", r.Form.Get("all"))
|
||||
|
||||
if version >= 1.9 {
|
||||
job.Stdout.Add(w)
|
||||
} else {
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
job.Stdout.Add(buffer)
|
||||
}
|
||||
|
||||
if err := job.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if version < 1.7 {
|
||||
outs2 := []APIImagesOld{}
|
||||
for _, ctnr := range outs {
|
||||
outs2 = append(outs2, ctnr.ToLegacy()...)
|
||||
if version < 1.9 { // Send as a valide JSON array
|
||||
outs := engine.NewTable("Created", 0)
|
||||
if _, err := outs.ReadFrom(buffer); err != nil {
|
||||
return err
|
||||
}
|
||||
if version < 1.8 { // Convert to legacy format
|
||||
outsLegacy := engine.NewTable("Created", 0)
|
||||
for _, out := range outs.Data {
|
||||
for _, repoTag := range out.GetList("RepoTags") {
|
||||
parts := strings.Split(repoTag, ":")
|
||||
outLegacy := &engine.Env{}
|
||||
outLegacy.Set("Repository", parts[0])
|
||||
outLegacy.Set("Tag", parts[1])
|
||||
outLegacy.Set("ID", out.Get("ID"))
|
||||
outLegacy.SetInt64("Created", out.GetInt64("Created"))
|
||||
outLegacy.SetInt64("Size", out.GetInt64("Size"))
|
||||
outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize"))
|
||||
outsLegacy.Add(outLegacy)
|
||||
}
|
||||
}
|
||||
if _, err := outsLegacy.WriteListTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if _, err := outs.WriteListTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeJSON(w, http.StatusOK, outs2)
|
||||
}
|
||||
return writeJSON(w, http.StatusOK, outs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package docker
|
||||
|
||||
import "strings"
|
||||
|
||||
type (
|
||||
APIHistory struct {
|
||||
ID string `json:"Id"`
|
||||
|
@ -11,24 +9,6 @@ type (
|
|||
Size int64
|
||||
}
|
||||
|
||||
APIImages struct {
|
||||
ID string `json:"Id"`
|
||||
RepoTags []string `json:",omitempty"`
|
||||
Created int64
|
||||
Size int64
|
||||
VirtualSize int64
|
||||
ParentId string `json:",omitempty"`
|
||||
}
|
||||
|
||||
APIImagesOld struct {
|
||||
Repository string `json:",omitempty"`
|
||||
Tag string `json:",omitempty"`
|
||||
ID string `json:"Id"`
|
||||
Created int64
|
||||
Size int64
|
||||
VirtualSize int64
|
||||
}
|
||||
|
||||
APITop struct {
|
||||
Titles []string
|
||||
Processes [][]string
|
||||
|
@ -101,22 +81,6 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
func (api APIImages) ToLegacy() []APIImagesOld {
|
||||
outs := []APIImagesOld{}
|
||||
for _, repotag := range api.RepoTags {
|
||||
components := strings.SplitN(repotag, ":", 2)
|
||||
outs = append(outs, APIImagesOld{
|
||||
ID: api.ID,
|
||||
Repository: components[0],
|
||||
Tag: components[1],
|
||||
Created: api.Created,
|
||||
Size: api.Size,
|
||||
VirtualSize: api.VirtualSize,
|
||||
})
|
||||
}
|
||||
return outs
|
||||
}
|
||||
|
||||
func (api APIContainers) ToLegacy() *APIContainersOld {
|
||||
return &APIContainersOld{
|
||||
ID: api.ID,
|
||||
|
|
105
commands.go
105
commands.go
|
@ -1137,36 +1137,38 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var outs []APIImages
|
||||
if err := json.Unmarshal(body, &outs); err != nil {
|
||||
outs := engine.NewTable("Created", 0)
|
||||
|
||||
if _, err := outs.ReadFrom(bytes.NewReader(body)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
printNode func(cli *DockerCli, noTrunc bool, image APIImages, prefix string)
|
||||
startImage APIImages
|
||||
printNode func(cli *DockerCli, noTrunc bool, image *engine.Env, prefix string)
|
||||
startImage *engine.Env
|
||||
|
||||
roots []APIImages
|
||||
byParent = make(map[string][]APIImages)
|
||||
roots = engine.NewTable("Created", outs.Len())
|
||||
byParent = make(map[string]*engine.Table)
|
||||
)
|
||||
|
||||
for _, image := range outs {
|
||||
if image.ParentId == "" {
|
||||
roots = append(roots, image)
|
||||
for _, image := range outs.Data {
|
||||
if image.Get("ParentId") == "" {
|
||||
roots.Add(image)
|
||||
} else {
|
||||
if children, exists := byParent[image.ParentId]; exists {
|
||||
byParent[image.ParentId] = append(children, image)
|
||||
if children, exists := byParent[image.Get("ParentId")]; exists {
|
||||
children.Add(image)
|
||||
} else {
|
||||
byParent[image.ParentId] = []APIImages{image}
|
||||
byParent[image.Get("ParentId")] = engine.NewTable("Created", 1)
|
||||
byParent[image.Get("ParentId")].Add(image)
|
||||
}
|
||||
}
|
||||
|
||||
if filter != "" {
|
||||
if filter == image.ID || filter == utils.TruncateID(image.ID) {
|
||||
if filter == image.Get("ID") || filter == utils.TruncateID(image.Get("ID")) {
|
||||
startImage = image
|
||||
}
|
||||
|
||||
for _, repotag := range image.RepoTags {
|
||||
for _, repotag := range image.GetList("RepoTags") {
|
||||
if repotag == filter {
|
||||
startImage = image
|
||||
}
|
||||
|
@ -1181,10 +1183,12 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
|||
printNode = (*DockerCli).printTreeNode
|
||||
}
|
||||
|
||||
if startImage.ID != "" {
|
||||
cli.WalkTree(*noTrunc, &[]APIImages{startImage}, byParent, "", printNode)
|
||||
if startImage != nil {
|
||||
root := engine.NewTable("Created", 1)
|
||||
root.Add(startImage)
|
||||
cli.WalkTree(*noTrunc, root, byParent, "", printNode)
|
||||
} else if filter == "" {
|
||||
cli.WalkTree(*noTrunc, &roots, byParent, "", printNode)
|
||||
cli.WalkTree(*noTrunc, roots, byParent, "", printNode)
|
||||
}
|
||||
if *flViz {
|
||||
fmt.Fprintf(cli.out, " base [style=invisible]\n}\n")
|
||||
|
@ -1203,9 +1207,8 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var outs []APIImages
|
||||
err = json.Unmarshal(body, &outs)
|
||||
if err != nil {
|
||||
outs := engine.NewTable("Created", 0)
|
||||
if _, err := outs.ReadFrom(bytes.NewReader(body)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1214,19 +1217,19 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
|||
fmt.Fprintln(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE")
|
||||
}
|
||||
|
||||
for _, out := range outs {
|
||||
for _, repotag := range out.RepoTags {
|
||||
for _, out := range outs.Data {
|
||||
for _, repotag := range out.GetList("RepoTags") {
|
||||
|
||||
repo, tag := utils.ParseRepositoryTag(repotag)
|
||||
|
||||
outID := out.Get("ID")
|
||||
if !*noTrunc {
|
||||
out.ID = utils.TruncateID(out.ID)
|
||||
outID = utils.TruncateID(outID)
|
||||
}
|
||||
|
||||
if !*quiet {
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, out.ID, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.Created, 0))), utils.HumanSize(out.VirtualSize))
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\n", repo, tag, outID, utils.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))), utils.HumanSize(out.GetInt64("VirtualSize")))
|
||||
} else {
|
||||
fmt.Fprintln(w, out.ID)
|
||||
fmt.Fprintln(w, outID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1238,66 +1241,66 @@ func (cli *DockerCli) CmdImages(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) WalkTree(noTrunc bool, images *[]APIImages, byParent map[string][]APIImages, prefix string, printNode func(cli *DockerCli, noTrunc bool, image APIImages, prefix string)) {
|
||||
length := len(*images)
|
||||
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()
|
||||
if length > 1 {
|
||||
for index, image := range *images {
|
||||
for index, image := range images.Data {
|
||||
if index+1 == length {
|
||||
printNode(cli, noTrunc, image, prefix+"└─")
|
||||
if subimages, exists := byParent[image.ID]; exists {
|
||||
cli.WalkTree(noTrunc, &subimages, byParent, prefix+" ", printNode)
|
||||
if subimages, exists := byParent[image.Get("ID")]; exists {
|
||||
cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode)
|
||||
}
|
||||
} else {
|
||||
printNode(cli, noTrunc, image, prefix+"├─")
|
||||
if subimages, exists := byParent[image.ID]; exists {
|
||||
cli.WalkTree(noTrunc, &subimages, byParent, prefix+"│ ", printNode)
|
||||
printNode(cli, noTrunc, image, prefix+"\u251C─")
|
||||
if subimages, exists := byParent[image.Get("ID")]; exists {
|
||||
cli.WalkTree(noTrunc, subimages, byParent, prefix+"\u2502 ", printNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, image := range *images {
|
||||
for _, image := range images.Data {
|
||||
printNode(cli, noTrunc, image, prefix+"└─")
|
||||
if subimages, exists := byParent[image.ID]; exists {
|
||||
cli.WalkTree(noTrunc, &subimages, byParent, prefix+" ", printNode)
|
||||
if subimages, exists := byParent[image.Get("ID")]; exists {
|
||||
cli.WalkTree(noTrunc, subimages, byParent, prefix+" ", printNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) printVizNode(noTrunc bool, image APIImages, prefix string) {
|
||||
func (cli *DockerCli) printVizNode(noTrunc bool, image *engine.Env, prefix string) {
|
||||
var (
|
||||
imageID string
|
||||
parentID string
|
||||
)
|
||||
if noTrunc {
|
||||
imageID = image.ID
|
||||
parentID = image.ParentId
|
||||
imageID = image.Get("ID")
|
||||
parentID = image.Get("ParentId")
|
||||
} else {
|
||||
imageID = utils.TruncateID(image.ID)
|
||||
parentID = utils.TruncateID(image.ParentId)
|
||||
imageID = utils.TruncateID(image.Get("ID"))
|
||||
parentID = utils.TruncateID(image.Get("ParentId"))
|
||||
}
|
||||
if 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.RepoTags[0] != "<none>:<none>" {
|
||||
if image.GetList("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.RepoTags, "\\n"))
|
||||
imageID, imageID, strings.Join(image.GetList("RepoTags"), "\\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *DockerCli) printTreeNode(noTrunc bool, image APIImages, prefix string) {
|
||||
func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix string) {
|
||||
var imageID string
|
||||
if noTrunc {
|
||||
imageID = image.ID
|
||||
imageID = image.Get("ID")
|
||||
} else {
|
||||
imageID = utils.TruncateID(image.ID)
|
||||
imageID = utils.TruncateID(image.Get("ID"))
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, utils.HumanSize(image.VirtualSize))
|
||||
if image.RepoTags[0] != "<none>:<none>" {
|
||||
fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.RepoTags, ", "))
|
||||
fmt.Fprintf(cli.out, "%s%s Virtual Size: %s", prefix, imageID, utils.HumanSize(image.GetInt64("VirtualSize")))
|
||||
if image.GetList("RepoTags")[0] != "<none>:<none>" {
|
||||
fmt.Fprintf(cli.out, " Tags: %s\n", strings.Join(image.GetList("RepoTags"), ", "))
|
||||
} else {
|
||||
fmt.Fprint(cli.out, "\n")
|
||||
}
|
||||
|
|
105
engine/env.go
105
engine/env.go
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
@ -232,3 +233,107 @@ func (env *Env) Map() map[string]string {
|
|||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type Table struct {
|
||||
Data []*Env
|
||||
sortKey string
|
||||
Chan chan *Env
|
||||
}
|
||||
|
||||
func NewTable(sortKey string, sizeHint int) *Table {
|
||||
return &Table{
|
||||
make([]*Env, 0, sizeHint),
|
||||
sortKey,
|
||||
make(chan *Env),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Table) SetKey(sortKey string) {
|
||||
t.sortKey = sortKey
|
||||
}
|
||||
|
||||
func (t *Table) Add(env *Env) {
|
||||
t.Data = append(t.Data, env)
|
||||
}
|
||||
|
||||
func (t *Table) Len() int {
|
||||
return len(t.Data)
|
||||
}
|
||||
|
||||
func (t *Table) Less(a, b int) bool {
|
||||
return t.lessBy(a, b, t.sortKey)
|
||||
}
|
||||
|
||||
func (t *Table) lessBy(a, b int, by string) bool {
|
||||
keyA := t.Data[a].Get(by)
|
||||
keyB := t.Data[b].Get(by)
|
||||
intA, errA := strconv.ParseInt(keyA, 10, 64)
|
||||
intB, errB := strconv.ParseInt(keyB, 10, 64)
|
||||
if errA == nil && errB == nil {
|
||||
return intA < intB
|
||||
}
|
||||
return keyA < keyB
|
||||
}
|
||||
|
||||
func (t *Table) Swap(a, b int) {
|
||||
tmp := t.Data[a]
|
||||
t.Data[a] = t.Data[b]
|
||||
t.Data[b] = tmp
|
||||
}
|
||||
|
||||
func (t *Table) Sort() {
|
||||
sort.Sort(t)
|
||||
}
|
||||
|
||||
func (t *Table) ReverseSort() {
|
||||
sort.Sort(sort.Reverse(t))
|
||||
}
|
||||
|
||||
func (t *Table) WriteListTo(dst io.Writer) (n int64, err error) {
|
||||
if _, err := dst.Write([]byte{'['}); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
n = 1
|
||||
for i, env := range t.Data {
|
||||
bytes, err := env.WriteTo(dst)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
n += bytes
|
||||
if i != len(t.Data)-1 {
|
||||
if _, err := dst.Write([]byte{','}); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
n += 1
|
||||
}
|
||||
}
|
||||
if _, err := dst.Write([]byte{']'}); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return n + 1, nil
|
||||
}
|
||||
|
||||
func (t *Table) WriteTo(dst io.Writer) (n int64, err error) {
|
||||
for _, env := range t.Data {
|
||||
bytes, err := env.WriteTo(dst)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
n += bytes
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (t *Table) ReadFrom(src io.Reader) (n int64, err error) {
|
||||
decoder := NewDecoder(src)
|
||||
for {
|
||||
env, err := decoder.Decode()
|
||||
if err == io.EOF {
|
||||
return 0, nil
|
||||
} else if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
t.Add(env)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
|
|
@ -190,3 +190,19 @@ func (o *Output) AddEnv() (dst *Env, err error) {
|
|||
}()
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func (o *Output) AddTable() (dst *Table, err error) {
|
||||
src, err := o.AddPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dst = NewTable("", 0)
|
||||
o.tasks.Add(1)
|
||||
go func() {
|
||||
defer o.tasks.Done()
|
||||
if _, err := dst.ReadFrom(src); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
return dst, nil
|
||||
}
|
||||
|
|
28
engine/table_test.go
Normal file
28
engine/table_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTableWriteTo(t *testing.T) {
|
||||
table := NewTable("", 0)
|
||||
e := &Env{}
|
||||
e.Set("foo", "bar")
|
||||
table.Add(e)
|
||||
var buf bytes.Buffer
|
||||
if _, err := table.WriteTo(&buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
output := make(map[string]string)
|
||||
if err := json.Unmarshal(buf.Bytes(), &output); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(output) != 1 {
|
||||
t.Fatalf("Incorrect output: %v", output)
|
||||
}
|
||||
if val, exists := output["foo"]; !exists || val != "bar" {
|
||||
t.Fatalf("Inccorect output: %v", output)
|
||||
}
|
||||
}
|
|
@ -60,11 +60,14 @@ func TestGetInfo(t *testing.T) {
|
|||
defer mkRuntimeFromEngine(eng, t).Nuke()
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
|
||||
initialImages, err := srv.Images(false, "")
|
||||
job := eng.Job("images")
|
||||
initialImages, err := job.Stdout.AddTable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req, err := http.NewRequest("GET", "/info", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -85,8 +88,8 @@ func TestGetInfo(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
out.Close()
|
||||
if images := i.GetInt("Images"); images != len(initialImages) {
|
||||
t.Errorf("Expected images: %d, %d found", len(initialImages), images)
|
||||
if images := i.GetInt("Images"); images != initialImages.Len() {
|
||||
t.Errorf("Expected images: %d, %d found", initialImages.Len(), images)
|
||||
}
|
||||
expected := "application/json"
|
||||
if result := r.HeaderMap.Get("Content-Type"); result != expected {
|
||||
|
@ -145,12 +148,14 @@ func TestGetImagesJSON(t *testing.T) {
|
|||
defer mkRuntimeFromEngine(eng, t).Nuke()
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
|
||||
// all=0
|
||||
|
||||
initialImages, err := srv.Images(false, "")
|
||||
job := eng.Job("images")
|
||||
initialImages, err := job.Stdout.AddTable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "/images/json?all=0", nil)
|
||||
if err != nil {
|
||||
|
@ -164,18 +169,18 @@ func TestGetImagesJSON(t *testing.T) {
|
|||
}
|
||||
assertHttpNotError(r, t)
|
||||
|
||||
images := []docker.APIImages{}
|
||||
if err := json.Unmarshal(r.Body.Bytes(), &images); err != nil {
|
||||
images := engine.NewTable("Created", 0)
|
||||
if _, err := images.ReadFrom(r.Body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(images) != len(initialImages) {
|
||||
t.Errorf("Expected %d image, %d found", len(initialImages), len(images))
|
||||
if images.Len() != initialImages.Len() {
|
||||
t.Errorf("Expected %d image, %d found", initialImages.Len(), images.Len())
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, img := range images {
|
||||
if strings.Contains(img.RepoTags[0], unitTestImageName) {
|
||||
for _, img := range images.Data {
|
||||
if strings.Contains(img.GetList("RepoTags")[0], unitTestImageName) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
@ -188,10 +193,7 @@ func TestGetImagesJSON(t *testing.T) {
|
|||
|
||||
// all=1
|
||||
|
||||
initialImages, err = srv.Images(true, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
initialImages = getAllImages(eng, t)
|
||||
|
||||
req2, err := http.NewRequest("GET", "/images/json?all=true", nil)
|
||||
if err != nil {
|
||||
|
@ -202,18 +204,18 @@ func TestGetImagesJSON(t *testing.T) {
|
|||
}
|
||||
assertHttpNotError(r2, t)
|
||||
|
||||
images2 := []docker.APIImages{}
|
||||
if err := json.Unmarshal(r2.Body.Bytes(), &images2); err != nil {
|
||||
images2 := engine.NewTable("ID", 0)
|
||||
if _, err := images2.ReadFrom(r2.Body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(images2) != len(initialImages) {
|
||||
t.Errorf("Expected %d image, %d found", len(initialImages), len(images2))
|
||||
if images2.Len() != initialImages.Len() {
|
||||
t.Errorf("Expected %d image, %d found", initialImages.Len(), images2.Len())
|
||||
}
|
||||
|
||||
found = false
|
||||
for _, img := range images2 {
|
||||
if img.ID == unitTestImageID {
|
||||
for _, img := range images2.Data {
|
||||
if img.Get("ID") == unitTestImageID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
@ -235,29 +237,13 @@ func TestGetImagesJSON(t *testing.T) {
|
|||
}
|
||||
assertHttpNotError(r3, t)
|
||||
|
||||
images3 := []docker.APIImages{}
|
||||
if err := json.Unmarshal(r3.Body.Bytes(), &images3); err != nil {
|
||||
images3 := engine.NewTable("ID", 0)
|
||||
if _, err := images3.ReadFrom(r3.Body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(images3) != 0 {
|
||||
t.Errorf("Expected 0 image, %d found", len(images3))
|
||||
}
|
||||
|
||||
r4 := httptest.NewRecorder()
|
||||
|
||||
// all=foobar
|
||||
req4, err := http.NewRequest("GET", "/images/json?all=foobar", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := docker.ServeRequest(srv, docker.APIVERSION, r4, req4); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Don't assert against HTTP error since we expect an error
|
||||
if r4.Code != http.StatusBadRequest {
|
||||
t.Fatalf("%d Bad Request expected, received %d\n", http.StatusBadRequest, r4.Code)
|
||||
if images3.Len() != 0 {
|
||||
t.Errorf("Expected 0 image, %d found", images3.Len())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1126,21 +1112,16 @@ func TestDeleteImages(t *testing.T) {
|
|||
defer mkRuntimeFromEngine(eng, t).Nuke()
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
|
||||
initialImages, err := srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
initialImages := getImages(eng, t, true, "")
|
||||
|
||||
if err := eng.Job("tag", unitTestImageName, "test", "test").Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images, err := srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+1 {
|
||||
t.Errorf("Expected %d images, %d found", len(initialImages)+1, len(images))
|
||||
images := getImages(eng, t, true, "")
|
||||
|
||||
if len(images.Data[0].GetList("RepoTags")) != len(initialImages.Data[0].GetList("RepoTags"))+1 {
|
||||
t.Errorf("Expected %d images, %d found", len(initialImages.Data[0].GetList("RepoTags"))+1, len(images.Data[0].GetList("RepoTags")))
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("DELETE", "/images/"+unitTestImageID, nil)
|
||||
|
@ -1177,13 +1158,10 @@ func TestDeleteImages(t *testing.T) {
|
|||
if len(outs) != 1 {
|
||||
t.Fatalf("Expected %d event (untagged), got %d", 1, len(outs))
|
||||
}
|
||||
images, err = srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images = getImages(eng, t, false, "")
|
||||
|
||||
if len(images[0].RepoTags) != len(initialImages[0].RepoTags) {
|
||||
t.Errorf("Expected %d image, %d found", len(initialImages), len(images))
|
||||
if images.Len() != initialImages.Len() {
|
||||
t.Errorf("Expected %d image, %d found", initialImages.Len(), images.Len())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,14 +51,17 @@ func cleanup(eng *engine.Engine, t *testing.T) error {
|
|||
container.Kill()
|
||||
runtime.Destroy(container)
|
||||
}
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
images, err := srv.Images(true, "")
|
||||
job := eng.Job("images")
|
||||
images, err := job.Stdout.AddTable()
|
||||
if err != nil {
|
||||
return err
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, image := range images {
|
||||
if image.ID != unitTestImageID {
|
||||
srv.ImageDelete(image.ID, false)
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, image := range images.Data {
|
||||
if image.Get("ID") != unitTestImageID {
|
||||
mkServerFromEngine(eng, t).ImageDelete(image.Get("ID"), false)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -158,7 +161,7 @@ func spawnGlobalDaemon() {
|
|||
Host: testDaemonAddr,
|
||||
}
|
||||
job := eng.Job("serveapi", listenURL.String())
|
||||
job.SetenvBool("Logging", os.Getenv("DEBUG") != "")
|
||||
job.SetenvBool("Logging", true)
|
||||
if err := job.Run(); err != nil {
|
||||
log.Fatalf("Unable to spawn the test daemon: %s", err)
|
||||
}
|
||||
|
|
|
@ -14,10 +14,7 @@ func TestImageTagImageDelete(t *testing.T) {
|
|||
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
|
||||
initialImages, err := srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
initialImages := getAllImages(eng, t)
|
||||
if err := eng.Job("tag", unitTestImageName, "utest", "tag1").Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -30,52 +27,43 @@ func TestImageTagImageDelete(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images, err := srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images := getAllImages(eng, t)
|
||||
|
||||
if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+3 {
|
||||
t.Errorf("Expected %d images, %d found", len(initialImages)+3, len(images))
|
||||
nExpected := len(initialImages.Data[0].GetList("RepoTags")) + 3
|
||||
nActual := len(images.Data[0].GetList("RepoTags"))
|
||||
if nExpected != nActual {
|
||||
t.Errorf("Expected %d images, %d found", nExpected, nActual)
|
||||
}
|
||||
|
||||
if _, err := srv.ImageDelete("utest/docker:tag2", true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images, err = srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images = getAllImages(eng, t)
|
||||
|
||||
if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+2 {
|
||||
t.Errorf("Expected %d images, %d found", len(initialImages)+2, len(images))
|
||||
nExpected = len(initialImages.Data[0].GetList("RepoTags")) + 2
|
||||
nActual = len(images.Data[0].GetList("RepoTags"))
|
||||
if nExpected != nActual {
|
||||
t.Errorf("Expected %d images, %d found", nExpected, nActual)
|
||||
}
|
||||
|
||||
if _, err := srv.ImageDelete("utest:5000/docker:tag3", true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images, err = srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images = getAllImages(eng, t)
|
||||
|
||||
if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+1 {
|
||||
t.Errorf("Expected %d images, %d found", len(initialImages)+1, len(images))
|
||||
}
|
||||
nExpected = len(initialImages.Data[0].GetList("RepoTags")) + 1
|
||||
nActual = len(images.Data[0].GetList("RepoTags"))
|
||||
|
||||
if _, err := srv.ImageDelete("utest:tag1", true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images, err = srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images = getAllImages(eng, t)
|
||||
|
||||
if len(images) != len(initialImages) {
|
||||
t.Errorf("Expected %d image, %d found", len(initialImages), len(images))
|
||||
if images.Len() != initialImages.Len() {
|
||||
t.Errorf("Expected %d image, %d found", initialImages.Len(), images.Len())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,10 +238,7 @@ func TestRmi(t *testing.T) {
|
|||
srv := mkServerFromEngine(eng, t)
|
||||
defer mkRuntimeFromEngine(eng, t).Nuke()
|
||||
|
||||
initialImages, err := srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
initialImages := getAllImages(eng, t)
|
||||
|
||||
config, hostConfig, _, err := docker.ParseRun([]string{unitTestImageID, "echo", "test"}, nil)
|
||||
if err != nil {
|
||||
|
@ -308,13 +293,10 @@ func TestRmi(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images, err := srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images := getAllImages(eng, t)
|
||||
|
||||
if len(images)-len(initialImages) != 2 {
|
||||
t.Fatalf("Expected 2 new images, found %d.", len(images)-len(initialImages))
|
||||
if images.Len()-initialImages.Len() != 2 {
|
||||
t.Fatalf("Expected 2 new images, found %d.", images.Len()-initialImages.Len())
|
||||
}
|
||||
|
||||
_, err = srv.ImageDelete(imageID, true)
|
||||
|
@ -322,20 +304,17 @@ func TestRmi(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images, err = srv.Images(false, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
images = getAllImages(eng, t)
|
||||
|
||||
if images.Len()-initialImages.Len() != 1 {
|
||||
t.Fatalf("Expected 1 new image, found %d.", images.Len()-initialImages.Len())
|
||||
}
|
||||
|
||||
if len(images)-len(initialImages) != 1 {
|
||||
t.Fatalf("Expected 1 new image, found %d.", len(images)-len(initialImages))
|
||||
}
|
||||
|
||||
for _, image := range images {
|
||||
if strings.Contains(unitTestImageID, image.ID) {
|
||||
for _, image := range images.Data {
|
||||
if strings.Contains(unitTestImageID, image.Get("ID")) {
|
||||
continue
|
||||
}
|
||||
if image.RepoTags[0] == "<none>:<none>" {
|
||||
if image.GetList("RepoTags")[0] == "<none>:<none>" {
|
||||
t.Fatalf("Expected tagged image, got untagged one.")
|
||||
}
|
||||
}
|
||||
|
@ -345,8 +324,6 @@ func TestImagesFilter(t *testing.T) {
|
|||
eng := NewTestEngine(t)
|
||||
defer nuke(mkRuntimeFromEngine(eng, t))
|
||||
|
||||
srv := mkServerFromEngine(eng, t)
|
||||
|
||||
if err := eng.Job("tag", unitTestImageName, "utest", "tag1").Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -359,39 +336,27 @@ func TestImagesFilter(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images, err := srv.Images(false, "utest*/*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images := getImages(eng, t, false, "utest*/*")
|
||||
|
||||
if len(images[0].RepoTags) != 2 {
|
||||
if len(images.Data[0].GetList("RepoTags")) != 2 {
|
||||
t.Fatal("incorrect number of matches returned")
|
||||
}
|
||||
|
||||
images, err = srv.Images(false, "utest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images = getImages(eng, t, false, "utest")
|
||||
|
||||
if len(images[0].RepoTags) != 1 {
|
||||
if len(images.Data[0].GetList("RepoTags")) != 1 {
|
||||
t.Fatal("incorrect number of matches returned")
|
||||
}
|
||||
|
||||
images, err = srv.Images(false, "utest*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images = getImages(eng, t, false, "utest*")
|
||||
|
||||
if len(images[0].RepoTags) != 1 {
|
||||
if len(images.Data[0].GetList("RepoTags")) != 1 {
|
||||
t.Fatal("incorrect number of matches returned")
|
||||
}
|
||||
|
||||
images, err = srv.Images(false, "*5000*/*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images = getImages(eng, t, false, "*5000*/*")
|
||||
|
||||
if len(images[0].RepoTags) != 1 {
|
||||
if len(images.Data[0].GetList("RepoTags")) != 1 {
|
||||
t.Fatal("incorrect number of matches returned")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,10 @@ func TestServerListOrderedImagesByCreationDate(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images, err := srv.Images(true, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images := getImages(eng, t, true, "")
|
||||
|
||||
if images[0].Created < images[1].Created {
|
||||
t.Error("Expected []APIImges to be ordered by most recent creation date.")
|
||||
if images.Data[0].GetInt("Created") < images.Data[1].GetInt("Created") {
|
||||
t.Error("Expected images to be ordered by most recent creation date.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,13 +41,10 @@ func TestServerListOrderedImagesByCreationDateAndTag(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
images, err := srv.Images(true, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
images := getImages(eng, t, true, "")
|
||||
|
||||
if images[0].RepoTags[0] != "repo:zed" && images[0].RepoTags[0] != "repo:bar" {
|
||||
t.Errorf("Expected []APIImges to be ordered by most recent creation date. %s", images)
|
||||
if repoTags := images.Data[0].GetList("RepoTags"); repoTags[0] != "repo:zed" && repoTags[0] != "repo:bar" {
|
||||
t.Errorf("Expected Images to be ordered by most recent creation date.")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -186,8 +186,6 @@ func NewTestEngine(t utils.Fataler) *engine.Engine {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eng.Stdout = ioutil.Discard
|
||||
eng.Stderr = ioutil.Discard
|
||||
// Load default plugins
|
||||
// (This is manually copied and modified from main() until we have a more generic plugin system)
|
||||
job := eng.Job("initapi")
|
||||
|
@ -329,3 +327,22 @@ func fakeTar() (io.Reader, error) {
|
|||
tw.Close()
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func getAllImages(eng *engine.Engine, t *testing.T) *engine.Table {
|
||||
return getImages(eng, t, true, "")
|
||||
}
|
||||
|
||||
func getImages(eng *engine.Engine, t *testing.T, all bool, filter string) *engine.Table {
|
||||
job := eng.Job("images")
|
||||
job.SetenvBool("all", all)
|
||||
job.Setenv("filter", filter)
|
||||
images, err := job.Stdout.AddTable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := job.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return images
|
||||
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func pingRegistryEndpoint(endpoint string) (bool, error) {
|
|||
// versions of the registry
|
||||
if standalone == "" {
|
||||
return true, nil
|
||||
// Accepted values are "true" (case-insensitive) and "1".
|
||||
// Accepted values are "true" (case-insensitive) and "1".
|
||||
} else if strings.EqualFold(standalone, "true") || standalone == "1" {
|
||||
return true, nil
|
||||
}
|
||||
|
|
68
server.go
68
server.go
|
@ -127,6 +127,10 @@ func jobInitApi(job *engine.Job) engine.Status {
|
|||
job.Error(err)
|
||||
return engine.StatusErr
|
||||
}
|
||||
if err := job.Eng.Register("images", srv.Images); err != nil {
|
||||
job.Error(err)
|
||||
return engine.StatusErr
|
||||
}
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
||||
|
@ -568,23 +572,24 @@ func (srv *Server) ImagesViz(out io.Writer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
|
||||
func (srv *Server) Images(job *engine.Job) engine.Status {
|
||||
var (
|
||||
allImages map[string]*Image
|
||||
err error
|
||||
)
|
||||
if all {
|
||||
if job.GetenvBool("all") {
|
||||
allImages, err = srv.runtime.graph.Map()
|
||||
} else {
|
||||
allImages, err = srv.runtime.graph.Heads()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
job.Errorf("%s", err)
|
||||
return engine.StatusErr
|
||||
}
|
||||
lookup := make(map[string]APIImages)
|
||||
lookup := make(map[string]*engine.Env)
|
||||
for name, repository := range srv.runtime.repositories.Repositories {
|
||||
if filter != "" {
|
||||
if match, _ := path.Match(filter, name); !match {
|
||||
if job.Getenv("filter") != "" {
|
||||
if match, _ := path.Match(job.Getenv("filter"), name); !match {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -596,48 +601,47 @@ func (srv *Server) Images(all bool, filter string) ([]APIImages, error) {
|
|||
}
|
||||
|
||||
if out, exists := lookup[id]; exists {
|
||||
out.RepoTags = append(out.RepoTags, fmt.Sprintf("%s:%s", name, tag))
|
||||
|
||||
lookup[id] = out
|
||||
out.SetList("RepoTags", append(out.GetList("RepoTags"), fmt.Sprintf("%s:%s", name, tag)))
|
||||
} else {
|
||||
var out APIImages
|
||||
|
||||
out := &engine.Env{}
|
||||
delete(allImages, id)
|
||||
|
||||
out.ParentId = image.Parent
|
||||
out.RepoTags = []string{fmt.Sprintf("%s:%s", name, tag)}
|
||||
out.ID = image.ID
|
||||
out.Created = image.Created.Unix()
|
||||
out.Size = image.Size
|
||||
out.VirtualSize = image.getParentsSize(0) + image.Size
|
||||
|
||||
out.Set("ParentId", image.Parent)
|
||||
out.SetList("RepoTags", []string{fmt.Sprintf("%s:%s", name, tag)})
|
||||
out.Set("ID", image.ID)
|
||||
out.SetInt64("Created", image.Created.Unix())
|
||||
out.SetInt64("Size", image.Size)
|
||||
out.SetInt64("VirtualSize", image.getParentsSize(0)+image.Size)
|
||||
lookup[id] = out
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
outs := make([]APIImages, 0, len(lookup))
|
||||
outs := engine.NewTable("Created", len(lookup))
|
||||
for _, value := range lookup {
|
||||
outs = append(outs, value)
|
||||
outs.Add(value)
|
||||
}
|
||||
|
||||
// Display images which aren't part of a repository/tag
|
||||
if filter == "" {
|
||||
if job.Getenv("filter") == "" {
|
||||
for _, image := range allImages {
|
||||
var out APIImages
|
||||
out.ID = image.ID
|
||||
out.ParentId = image.Parent
|
||||
out.RepoTags = []string{"<none>:<none>"}
|
||||
out.Created = image.Created.Unix()
|
||||
out.Size = image.Size
|
||||
out.VirtualSize = image.getParentsSize(0) + image.Size
|
||||
outs = append(outs, out)
|
||||
out := &engine.Env{}
|
||||
out.Set("ParentId", image.Parent)
|
||||
out.SetList("RepoTags", []string{"<none>:<none>"})
|
||||
out.Set("ID", image.ID)
|
||||
out.SetInt64("Created", image.Created.Unix())
|
||||
out.SetInt64("Size", image.Size)
|
||||
out.SetInt64("VirtualSize", image.getParentsSize(0)+image.Size)
|
||||
outs.Add(out)
|
||||
}
|
||||
}
|
||||
|
||||
sortImagesByCreationAndTag(outs)
|
||||
return outs, nil
|
||||
outs.ReverseSort()
|
||||
if _, err := outs.WriteTo(job.Stdout); err != nil {
|
||||
job.Errorf("%s", err)
|
||||
return engine.StatusErr
|
||||
}
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
||||
func (srv *Server) DockerInfo(job *engine.Job) engine.Status {
|
||||
|
|
33
sorter.go
33
sorter.go
|
@ -2,39 +2,6 @@ package docker
|
|||
|
||||
import "sort"
|
||||
|
||||
type imageSorter struct {
|
||||
images []APIImages
|
||||
by func(i1, i2 *APIImages) bool // Closure used in the Less method.
|
||||
}
|
||||
|
||||
// Len is part of sort.Interface.
|
||||
func (s *imageSorter) Len() int {
|
||||
return len(s.images)
|
||||
}
|
||||
|
||||
// Swap is part of sort.Interface.
|
||||
func (s *imageSorter) Swap(i, j int) {
|
||||
s.images[i], s.images[j] = s.images[j], s.images[i]
|
||||
}
|
||||
|
||||
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
|
||||
func (s *imageSorter) Less(i, j int) bool {
|
||||
return s.by(&s.images[i], &s.images[j])
|
||||
}
|
||||
|
||||
// Sort []ApiImages by most recent creation date and tag name.
|
||||
func sortImagesByCreationAndTag(images []APIImages) {
|
||||
creationAndTag := func(i1, i2 *APIImages) bool {
|
||||
return i1.Created > i2.Created
|
||||
}
|
||||
|
||||
sorter := &imageSorter{
|
||||
images: images,
|
||||
by: creationAndTag}
|
||||
|
||||
sort.Sort(sorter)
|
||||
}
|
||||
|
||||
type portSorter struct {
|
||||
ports []Port
|
||||
by func(i, j Port) bool
|
||||
|
|
|
@ -92,7 +92,7 @@ func Purge(maxAge int) int {
|
|||
datat = make(map[*http.Request]int64)
|
||||
} else {
|
||||
min := time.Now().Unix() - int64(maxAge)
|
||||
for r, _ := range data {
|
||||
for r := range data {
|
||||
if datat[r] < min {
|
||||
clear(r)
|
||||
count++
|
||||
|
|
|
@ -96,8 +96,8 @@ func TestRouteMatchers(t *testing.T) {
|
|||
method = "GET"
|
||||
headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
|
||||
resultVars = map[bool]map[string]string{
|
||||
true: map[string]string{"var1": "www", "var2": "product", "var3": "42"},
|
||||
false: map[string]string{},
|
||||
true: {"var1": "www", "var2": "product", "var3": "42"},
|
||||
false: {},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,8 +110,8 @@ func TestRouteMatchers(t *testing.T) {
|
|||
method = "POST"
|
||||
headers = map[string]string{"Content-Type": "application/json"}
|
||||
resultVars = map[bool]map[string]string{
|
||||
true: map[string]string{"var4": "google", "var5": "product", "var6": "42"},
|
||||
false: map[string]string{},
|
||||
true: {"var4": "google", "var5": "product", "var6": "42"},
|
||||
false: {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue