option to show incremental size diff between images

closes #14
This commit is contained in:
Nate Jones 2015-11-08 11:33:00 -08:00
parent 80970dcee2
commit e936558a78
3 changed files with 77 additions and 16 deletions

View file

@ -91,6 +91,9 @@ $ dockviz images -t
└─cb12405ee8fa Virtual Size: 98.5 MB └─cb12405ee8fa Virtual Size: 98.5 MB
└─316b678ddf48 Virtual Size: 169.4 MB Tags: ubuntu:13.04, ubuntu:raring └─316b678ddf48 Virtual Size: 169.4 MB Tags: ubuntu:13.04, ubuntu:raring
``` ```
Only showing labelled images:
``` ```
$ dockviz images -t -l $ dockviz images -t -l
└─511136ea3c5a Virtual Size: 0.0 B └─511136ea3c5a Virtual Size: 0.0 B
@ -104,6 +107,41 @@ $ dockviz images -t -l
└─316b678ddf48 Virtual Size: 169.4 MB Tags: ubuntu:13.04, ubuntu:raring └─316b678ddf48 Virtual Size: 169.4 MB Tags: ubuntu:13.04, ubuntu:raring
``` ```
Showing incremental size rather than cumulative:
```
$ dockviz images -t -i
└─511136ea3c5a Virtual Size: 0.0 B
├─f10ebce2c0e1 Virtual Size: 103.7 MB
│ └─82cdea7ab5b5 Virtual Size: 255.5 KB
│ └─5dbd9cb5a02f Virtual Size: 1.9 KB
│ └─74fe38d11401 Virtual Size: 105.7 MB Tags: ubuntu:12.04, ubuntu:precise
├─ef519c9ee91a Virtual Size: 100.9 MB
│ └─07302703becc Virtual Size: 251.0 KB
│ └─cf8dc907452c Virtual Size: 1.9 KB
│ └─a7cf8ae4e998 Virtual Size: 70.1 MB Tags: ubuntu:12.10, ubuntu:quantal
│ ├─e18d8001204e Virtual Size: 29.4 KB
│ │ └─d0525208a46c Virtual Size: 71.0 B
│ │ └─59dac4bae93b Virtual Size: 71.2 MB
│ │ └─89541b3b35f2 Virtual Size: 269.3 MB
│ │ └─7dac4e98548e Virtual Size: 0.0 B
│ │ └─341d0cc3fac8 Virtual Size: 0.0 B
│ │ └─2f96171d2098 Virtual Size: 0.0 B
│ │ └─67b8b7262a67 Virtual Size: 1.9 MB
│ │ └─0fe9a2bc50fe Virtual Size: 656.0 B
│ │ └─8c32832f07ba Virtual Size: 383.0 B
│ │ └─cc4e1358bc80 Virtual Size: 0.0 B
│ │ └─5c0d04fba9df Virtual Size: 0.0 B Tags: nate/mongodb:latest
│ └─398d592f2009 Virtual Size: 70.9 MB
│ └─0cd8e7f50270 Virtual Size: 1.4 MB
│ └─594b6f8e6f92 Virtual Size: 0.0 B
│ └─f832a63e87a4 Virtual Size: 0.0 B Tags: redis:latest
└─02dae1c13f51 Virtual Size: 98.3 MB
└─e7206bfc66aa Virtual Size: 190.0 KB
└─cb12405ee8fa Virtual Size: 1.9 KB
└─316b678ddf48 Virtual Size: 70.8 MB Tags: ubuntu:13.04, ubuntu:raring
```
# Running # Running
Dockviz supports connecting to the Docker daemon directly. It defaults to `unix:///var/run/docker.sock`, but respects the following as well: Dockviz supports connecting to the Docker daemon directly. It defaults to `unix:///var/run/docker.sock`, but respects the following as well:

View file

@ -25,6 +25,7 @@ type ImagesCommand struct {
Tree bool `short:"t" long:"tree" description:"Show image information as tree. You can add a start image id or name -t/--tree [id/name]"` Tree bool `short:"t" long:"tree" description:"Show image information as tree. You can add a start image id or name -t/--tree [id/name]"`
Short bool `short:"s" long:"short" description:"Show short summary of images (repo name and list of tags)."` Short bool `short:"s" long:"short" description:"Show short summary of images (repo name and list of tags)."`
NoTruncate bool `short:"n" long:"no-trunc" description:"Don't truncate the image IDs."` NoTruncate bool `short:"n" long:"no-trunc" description:"Don't truncate the image IDs."`
Incremental bool `short:"i" long:"incremental" description:"Display image size as incremental rather than cumulative."`
OnlyLabelled bool `short:"l" long:"only-labelled" description:"Print only labelled images/containers."` OnlyLabelled bool `short:"l" long:"only-labelled" description:"Print only labelled images/containers."`
} }
@ -110,7 +111,7 @@ func (x *ImagesCommand) Execute(args []string) error {
} }
if imagesCommand.Tree { if imagesCommand.Tree {
fmt.Print(jsonToTree(roots, imagesByParent, imagesCommand.NoTruncate)) fmt.Print(jsonToTree(roots, imagesByParent, imagesCommand.NoTruncate, imagesCommand.Incremental))
} }
if imagesCommand.Dot { if imagesCommand.Dot {
fmt.Print(jsonToDot(roots, imagesByParent)) fmt.Print(jsonToDot(roots, imagesByParent))
@ -163,10 +164,10 @@ IMAGES:
return startImage, nil return startImage, nil
} }
func jsonToTree(images []Image, byParent map[string][]Image, noTrunc bool) string { func jsonToTree(images []Image, byParent map[string][]Image, noTrunc bool, incremental bool) string {
var buffer bytes.Buffer var buffer bytes.Buffer
jsonToText(&buffer, images, byParent, noTrunc, "") jsonToText(&buffer, images, byParent, noTrunc, incremental, "")
return buffer.String() return buffer.String()
} }
@ -235,33 +236,33 @@ func filterImages(images *[]Image, byParent *map[string][]Image) (filteredImages
return filteredImages, filteredChildren return filteredImages, filteredChildren
} }
func jsonToText(buffer *bytes.Buffer, images []Image, byParent map[string][]Image, noTrunc bool, prefix string) { func jsonToText(buffer *bytes.Buffer, images []Image, byParent map[string][]Image, noTrunc bool, incremental bool, prefix string) {
var length = len(images) var length = len(images)
if length > 1 { if length > 1 {
for index, image := range images { for index, image := range images {
var nextPrefix string = "" var nextPrefix string = ""
if index+1 == length { if index+1 == length {
PrintTreeNode(buffer, image, noTrunc, prefix+"└─") PrintTreeNode(buffer, image, noTrunc, incremental, prefix+"└─")
nextPrefix = " " nextPrefix = " "
} else { } else {
PrintTreeNode(buffer, image, noTrunc, prefix+"├─") PrintTreeNode(buffer, image, noTrunc, incremental, prefix+"├─")
nextPrefix = "│ " nextPrefix = "│ "
} }
if subimages, exists := byParent[image.Id]; exists { if subimages, exists := byParent[image.Id]; exists {
jsonToText(buffer, subimages, byParent, noTrunc, prefix+nextPrefix) jsonToText(buffer, subimages, byParent, noTrunc, incremental, prefix+nextPrefix)
} }
} }
} else { } else {
for _, image := range images { for _, image := range images {
PrintTreeNode(buffer, image, noTrunc, prefix+"└─") PrintTreeNode(buffer, image, noTrunc, incremental, prefix+"└─")
if subimages, exists := byParent[image.Id]; exists { if subimages, exists := byParent[image.Id]; exists {
jsonToText(buffer, subimages, byParent, noTrunc, prefix+" ") jsonToText(buffer, subimages, byParent, noTrunc, incremental, prefix+" ")
} }
} }
} }
} }
func PrintTreeNode(buffer *bytes.Buffer, image Image, noTrunc bool, prefix string) { func PrintTreeNode(buffer *bytes.Buffer, image Image, noTrunc bool, incremental bool, prefix string) {
var imageID string var imageID string
if noTrunc { if noTrunc {
imageID = image.Id imageID = image.Id
@ -269,7 +270,14 @@ func PrintTreeNode(buffer *bytes.Buffer, image Image, noTrunc bool, prefix strin
imageID = truncate(image.Id) imageID = truncate(image.Id)
} }
buffer.WriteString(fmt.Sprintf("%s%s Virtual Size: %s", prefix, imageID, humanSize(image.VirtualSize))) var size int64
if incremental {
size = image.Size
} else {
size = image.VirtualSize
}
buffer.WriteString(fmt.Sprintf("%s%s Virtual Size: %s", prefix, imageID, humanSize(size)))
if image.RepoTags[0] != "<none>:<none>" { if image.RepoTags[0] != "<none>:<none>" {
buffer.WriteString(fmt.Sprintf(" Tags: %s\n", strings.Join(image.RepoTags, ", "))) buffer.WriteString(fmt.Sprintf(" Tags: %s\n", strings.Join(image.RepoTags, ", ")))
} else { } else {

View file

@ -19,6 +19,7 @@ type TreeTest struct {
json string json string
startImage string startImage string
noTrunc bool noTrunc bool
incr bool
regexps []string regexps []string
} }
@ -78,23 +79,36 @@ func Test_Dot(t *testing.T) {
} }
func Test_Tree(t *testing.T) { func Test_Tree(t *testing.T) {
treeJSON := `[ { "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "foo:latest" ], "ParentId": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Id": "c87be8e5e697c735f5db5626147582d2ae3f2088574c5faaf8d4d1bccab99470", "Created": 1386142123 }, { "VirtualSize": 682553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Id": "626147582d2ae3735f5db5f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Created": 1386142123 }, { "VirtualSize": 712553464, "Size": 0, "RepoTags": [ "base:latest" ], "ParentId": "626147582d2ae3735f5db5f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Id": "574c5faaf8d4d1bccab994626147582d2ae3735f5db5f2c87be8e5e697c08870", "Created": 1386142123 }, { "VirtualSize": 752553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "574c5faaf8d4d1bccab994626147582d2ae3735f5db5f2c87be8e5e697c08870", "Id": "aaf8d4d1bccab994574c5f626147582d2ae3735f5db5f2c87be8e5e697c08870", "Created": 1386142123 }, { "VirtualSize": 662553464, "Size": 0, "RepoTags": [ "<none>:<none>" ], "ParentId": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Id": "735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470", "Created": 1386142123 }, { "VirtualSize": 662553464, "Size": 662553464, "RepoTags": [ "<none>:<none>" ], "ParentId": "", "Id": "4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358", "Created": 1386114144 } ]` treeJSON := `[{"VirtualSize":674553464,"Size":2000000,"RepoTags":["foo:latest"],"ParentId":"735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470","Id":"c87be8e5e697c735f5db5626147582d2ae3f2088574c5faaf8d4d1bccab99470","Created":1386142123},{"VirtualSize":682553464,"Size":20000000,"RepoTags":["<none>:<none>"],"ParentId":"4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358","Id":"626147582d2ae3735f5db5f2c87be8e5e697c088574c5faaf8d4d1bccab99470","Created":1386142123},{"VirtualSize":712553464,"Size":30000000,"RepoTags":["base:latest"],"ParentId":"626147582d2ae3735f5db5f2c87be8e5e697c088574c5faaf8d4d1bccab99470","Id":"574c5faaf8d4d1bccab994626147582d2ae3735f5db5f2c87be8e5e697c08870","Created":1386142123},{"VirtualSize":752553464,"Size":40000000,"RepoTags":["<none>:<none>"],"ParentId":"574c5faaf8d4d1bccab994626147582d2ae3735f5db5f2c87be8e5e697c08870","Id":"aaf8d4d1bccab994574c5f626147582d2ae3735f5db5f2c87be8e5e697c08870","Created":1386142123},{"VirtualSize":672553464,"Size":10000000,"RepoTags":["<none>:<none>"],"ParentId":"4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358","Id":"735f5db5626147582d2ae3f2c87be8e5e697c088574c5faaf8d4d1bccab99470","Created":1386142123},{"VirtualSize":662553464,"Size":662553464,"RepoTags":["<none>:<none>"],"ParentId":"","Id":"4c1208b690c68af3476b437e7bc2bcc460f062bda2094d2d8f21a7e70368d358","Created":1386114144}]`
treeTests := []TreeTest{ treeTests := []TreeTest{
TreeTest{ TreeTest{
json: treeJSON, json: treeJSON,
startImage: "", startImage: "",
noTrunc: false, noTrunc: false,
incr: false,
regexps: []string{ regexps: []string{
`(?m)└─4c1208b690c6`, `(?m)└─4c1208b690c6 Virtual Size: 662.6 MB`,
`(?m) └─735f5db56261`, `(?m) └─735f5db56261 Virtual Size: 672.6 MB`,
`(?m) └─c87be8e5e697`, `(?m) └─c87be8e5e697 Virtual Size: 674.6 MB Tags: foo:latest`,
},
},
TreeTest{
json: treeJSON,
startImage: "",
noTrunc: false,
incr: true,
regexps: []string{
`(?m)└─4c1208b690c6 Virtual Size: 662.6 MB`,
`(?m) └─735f5db56261 Virtual Size: 10.0 MB`,
`(?m) └─c87be8e5e697 Virtual Size: 2.0 MB Tags: foo:latest`,
}, },
}, },
TreeTest{ TreeTest{
json: treeJSON, json: treeJSON,
startImage: "626147582d2a", startImage: "626147582d2a",
noTrunc: false, noTrunc: false,
incr: false,
regexps: []string{ regexps: []string{
`(?m)└─626147582d2a`, `(?m)└─626147582d2a`,
`(?m) └─574c5faaf8d4`, `(?m) └─574c5faaf8d4`,
@ -105,6 +119,7 @@ func Test_Tree(t *testing.T) {
json: treeJSON, json: treeJSON,
startImage: "base:latest", startImage: "base:latest",
noTrunc: true, noTrunc: true,
incr: false,
regexps: []string{ regexps: []string{
`(?m)└─574c5faaf8d4d1bccab994626147582d2ae3735f5db5f2c87be8e5e697c08870`, `(?m)└─574c5faaf8d4d1bccab994626147582d2ae3735f5db5f2c87be8e5e697c08870`,
`(?m) └─aaf8d4d1bccab994574c5f626147582d2ae3735f5db5f2c87be8e5e697c08870`, `(?m) └─aaf8d4d1bccab994574c5f626147582d2ae3735f5db5f2c87be8e5e697c08870`,
@ -123,7 +138,7 @@ func Test_Tree(t *testing.T) {
} else { } else {
roots = collectRoots(im) roots = collectRoots(im)
} }
result := jsonToTree(roots, byParent, treeTest.noTrunc) result := jsonToTree(roots, byParent, treeTest.noTrunc, treeTest.incr)
for _, regexp := range compileRegexps(t, treeTest.regexps) { for _, regexp := range compileRegexps(t, treeTest.regexps) {
if !regexp.MatchString(result) { if !regexp.MatchString(result) {