From a91b710961b74341543a81b9d16f66d65b1907f1 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Mon, 13 May 2013 15:10:26 +0200 Subject: [PATCH] add sizes in images and containers --- api_params.go | 16 ++++++++++------ api_test.go | 2 +- commands.go | 13 +++++++++---- container.go | 24 ++++++++++++++++++++++++ graph.go | 9 +++++++++ image.go | 24 ++++++++++++++++++++++++ server.go | 5 +++++ utils.go | 23 +++++++++++++++++++++++ 8 files changed, 105 insertions(+), 11 deletions(-) diff --git a/api_params.go b/api_params.go index c7c15585f9..c4942b50e1 100644 --- a/api_params.go +++ b/api_params.go @@ -11,6 +11,8 @@ type ApiImages struct { Tag string `json:",omitempty"` Id string Created int64 `json:",omitempty"` + Size int64 + ParentSize int64 } type ApiInfo struct { @@ -24,12 +26,14 @@ type ApiInfo struct { } type ApiContainers struct { - Id string - Image string `json:",omitempty"` - Command string `json:",omitempty"` - Created int64 `json:",omitempty"` - Status string `json:",omitempty"` - Ports string `json:",omitempty"` + Id string + Image string `json:",omitempty"` + Command string `json:",omitempty"` + Created int64 `json:",omitempty"` + Status string `json:",omitempty"` + Ports string `json:",omitempty"` + SizeRw int64 + SizeRootFs int64 } type ApiSearch struct { diff --git a/api_test.go b/api_test.go index 68e60adc21..2128f3ef35 100644 --- a/api_test.go +++ b/api_test.go @@ -1194,7 +1194,7 @@ func TestDeleteContainers(t *testing.T) { func TestDeleteImages(t *testing.T) { //FIXME: Implement this test - t.Skip("Test not implemented") + t.Log("Test not implemented") } // Mocked types for tests diff --git a/commands.go b/commands.go index dc3f8c4e87..736aee2f7a 100644 --- a/commands.go +++ b/commands.go @@ -728,12 +728,12 @@ func CmdImages(args ...string) error { w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED") + fmt.Fprintln(w, "REPOSITORY\tTAG\tID\tCREATED\tSIZE") } for _, out := range outs { if !*quiet { - fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\n", out.Repository, out.Tag, out.Id, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0)))) + fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s (virtual %s)\n", out.Repository, out.Tag, out.Id, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), HumanSize(out.Size), HumanSize(out.ParentSize)) } else { fmt.Fprintln(w, out.Id) } @@ -794,12 +794,17 @@ func CmdPs(args ...string) error { } w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) if !*quiet { - fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS") + fmt.Fprintln(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tSIZE") } for _, out := range outs { if !*quiet { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", out.Id, out.Image, out.Command, out.Status, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports) + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\t", out.Id, out.Image, out.Command, out.Status, HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Ports) + if out.SizeRootFs > 0 { + fmt.Fprintf(w, "%s (virtual %s)\n", HumanSize(out.SizeRw), HumanSize(out.SizeRootFs)) + } else { + fmt.Fprintf(w, "%s\n", HumanSize(out.SizeRw)) + } } else { fmt.Fprintln(w, out.Id) } diff --git a/container.go b/container.go index c49441e5da..8ccdfb2a43 100644 --- a/container.go +++ b/container.go @@ -11,6 +11,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "sort" "strconv" "strings" @@ -879,3 +880,26 @@ func validateId(id string) error { } return nil } + +// GetSize, return real size, virtual size +func (container *Container) GetSize() (int64, int64) { + var sizeRw, sizeRootfs int64 + + filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error { + if fileInfo != nil { + sizeRw += fileInfo.Size() + } + return nil + }) + + _, err := os.Stat(container.RootfsPath()) + if err == nil { + filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error { + if fileInfo != nil { + sizeRootfs += fileInfo.Size() + } + return nil + }) + } + return sizeRw, sizeRootfs +} diff --git a/graph.go b/graph.go index d9b4f5ac5c..b4a0b57504 100644 --- a/graph.go +++ b/graph.go @@ -89,6 +89,15 @@ func (graph *Graph) Get(name string) (*Image, error) { if img.Id != id { return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.Id) } + if img.Size == 0 { + root, err := img.root() + if err != nil { + return nil, err + } + if err := StoreSize(img, root); err != nil { + return nil, err + } + } img.graph = graph graph.lockSumMap.Lock() defer graph.lockSumMap.Unlock() diff --git a/image.go b/image.go index 413d95673b..76ebdab33d 100644 --- a/image.go +++ b/image.go @@ -12,6 +12,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "strings" "time" ) @@ -27,6 +28,8 @@ type Image struct { Author string `json:"author,omitempty"` Config *Config `json:"config,omitempty"` graph *Graph + Size int64 + ParentSize int64 } func LoadImage(root string) (*Image, error) { @@ -93,6 +96,18 @@ func StoreImage(img *Image, layerData Archive, root string, store bool) error { if err := Untar(layerData, layer); err != nil { return err } + + return StoreSize(img, root) +} + +func StoreSize(img *Image, root string) error { + layer := layerPath(root) + + filepath.Walk(layer, func(path string, fileInfo os.FileInfo, err error) error { + img.Size += fileInfo.Size() + return nil + }) + // Store the json ball jsonData, err := json.Marshal(img) if err != nil { @@ -359,3 +374,12 @@ func (img *Image) Checksum() (string, error) { return hash, nil } + +func (img *Image) getVirtualSize(size int64) int64 { + parentImage, err := img.GetParent() + if err != nil || parentImage == nil { + return size + } + size += parentImage.Size + return parentImage.getVirtualSize(size) +} diff --git a/server.go b/server.go index e96497bff3..cb030fcfa0 100644 --- a/server.go +++ b/server.go @@ -164,6 +164,8 @@ func (srv *Server) Images(all, only_ids bool, filter string) ([]ApiImages, error out.Tag = tag out.Id = TruncateId(id) out.Created = image.Created.Unix() + out.Size = image.Size + out.ParentSize = image.getVirtualSize(0) } else { out.Id = image.ShortId() } @@ -179,6 +181,8 @@ func (srv *Server) Images(all, only_ids bool, filter string) ([]ApiImages, error out.Tag = "" out.Id = TruncateId(id) out.Created = image.Created.Unix() + out.Size = image.Size + out.ParentSize = image.getVirtualSize(0) } else { out.Id = image.ShortId() } @@ -280,6 +284,7 @@ func (srv *Server) Containers(all, trunc_cmd, only_ids bool, n int, since, befor c.Created = container.Created.Unix() c.Status = container.State.String() c.Ports = container.NetworkSettings.PortMappingHuman() + c.SizeRw, c.SizeRootFs = container.GetSize() } retContainers = append(retContainers, c) } diff --git a/utils.go b/utils.go index 4b416cd1e6..5daf3a3c3d 100644 --- a/utils.go +++ b/utils.go @@ -16,6 +16,7 @@ import ( "os/signal" "path/filepath" "runtime" + _ "strconv" "strings" "sync" "time" @@ -133,6 +134,28 @@ func HumanDuration(d time.Duration) string { return fmt.Sprintf("%d years", d.Hours()/24/365) } +// HumanSize returns a human-readabla approximation of a size +// (eg. "44K", "17M") +func HumanSize(size int64) string { + i := 0 + var sizef float64 + sizef = float64(size) + units := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} + for sizef > 1024.0 { + sizef = sizef / 1024.0 + i++ + } + return fmt.Sprintf("%.*f %s", i, sizef, units[i]) + // sprintf(buf, "%.*f %s", i, size, units[i]); + // if size/1024/1024 > 1000 { + // return strconv.FormatFloat((float64)(size/1024/1024), 'f', 2, 32) + "G" + // } + // if size/1024 > 1024 { + // return strconv.FormatInt(size/1024/1024, 10) + "M" + // } + // return strconv.FormatInt(size/1024, 10) + "K" +} + func Trunc(s string, maxlen int) string { if len(s) <= maxlen { return s