Signed-off-by: Roman Strashkin <roman.strashkin@gmail.com>
| ... | ... |
@@ -884,10 +884,7 @@ func (daemon *Daemon) ContainerGraph() *graphdb.Database {
|
| 884 | 884 |
|
| 885 | 885 |
func (daemon *Daemon) ImageGetCached(imgID string, config *runconfig.Config) (*graph.Image, error) {
|
| 886 | 886 |
// Retrieve all images |
| 887 |
- images, err := daemon.Graph().Map() |
|
| 888 |
- if err != nil {
|
|
| 889 |
- return nil, err |
|
| 890 |
- } |
|
| 887 |
+ images := daemon.Graph().Map() |
|
| 891 | 888 |
|
| 892 | 889 |
// Store the tree in a map of map (map[parentId][childId]) |
| 893 | 890 |
imageMap := make(map[string]map[string]struct{})
|
| ... | ... |
@@ -55,10 +55,7 @@ func (daemon *Daemon) imgDeleteHelper(name string, list *[]types.ImageDelete, fi |
| 55 | 55 |
tag = "" |
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 |
- byParents, err := daemon.Graph().ByParent() |
|
| 59 |
- if err != nil {
|
|
| 60 |
- return err |
|
| 61 |
- } |
|
| 58 |
+ byParents := daemon.Graph().ByParent() |
|
| 62 | 59 |
|
| 63 | 60 |
repos := daemon.Repositories().ByID()[img.ID] |
| 64 | 61 |
|
| ... | ... |
@@ -327,42 +327,33 @@ func (graph *Graph) Delete(name string) error {
|
| 327 | 327 |
} |
| 328 | 328 |
|
| 329 | 329 |
// Map returns a list of all images in the graph, addressable by ID. |
| 330 |
-func (graph *Graph) Map() (map[string]*Image, error) {
|
|
| 330 |
+func (graph *Graph) Map() map[string]*Image {
|
|
| 331 | 331 |
images := make(map[string]*Image) |
| 332 |
- err := graph.walkAll(func(image *Image) {
|
|
| 332 |
+ graph.walkAll(func(image *Image) {
|
|
| 333 | 333 |
images[image.ID] = image |
| 334 | 334 |
}) |
| 335 |
- if err != nil {
|
|
| 336 |
- return nil, err |
|
| 337 |
- } |
|
| 338 |
- return images, nil |
|
| 335 |
+ return images |
|
| 339 | 336 |
} |
| 340 | 337 |
|
| 341 | 338 |
// walkAll iterates over each image in the graph, and passes it to a handler. |
| 342 | 339 |
// The walking order is undetermined. |
| 343 |
-func (graph *Graph) walkAll(handler func(*Image)) error {
|
|
| 344 |
- files, err := ioutil.ReadDir(graph.root) |
|
| 345 |
- if err != nil {
|
|
| 346 |
- return err |
|
| 347 |
- } |
|
| 348 |
- for _, st := range files {
|
|
| 349 |
- if img, err := graph.Get(st.Name()); err != nil {
|
|
| 350 |
- // Skip image |
|
| 351 |
- continue |
|
| 340 |
+func (graph *Graph) walkAll(handler func(*Image)) {
|
|
| 341 |
+ graph.idIndex.Iterate(func(id string) {
|
|
| 342 |
+ if img, err := graph.Get(id); err != nil {
|
|
| 343 |
+ return |
|
| 352 | 344 |
} else if handler != nil {
|
| 353 | 345 |
handler(img) |
| 354 | 346 |
} |
| 355 |
- } |
|
| 356 |
- return nil |
|
| 347 |
+ }) |
|
| 357 | 348 |
} |
| 358 | 349 |
|
| 359 | 350 |
// ByParent returns a lookup table of images by their parent. |
| 360 | 351 |
// If an image of id ID has 3 children images, then the value for key ID |
| 361 | 352 |
// will be a list of 3 images. |
| 362 | 353 |
// If an image has no children, it will not have an entry in the table. |
| 363 |
-func (graph *Graph) ByParent() (map[string][]*Image, error) {
|
|
| 354 |
+func (graph *Graph) ByParent() map[string][]*Image {
|
|
| 364 | 355 |
byParent := make(map[string][]*Image) |
| 365 |
- err := graph.walkAll(func(img *Image) {
|
|
| 356 |
+ graph.walkAll(func(img *Image) {
|
|
| 366 | 357 |
parent, err := graph.Get(img.Parent) |
| 367 | 358 |
if err != nil {
|
| 368 | 359 |
return |
| ... | ... |
@@ -373,25 +364,22 @@ func (graph *Graph) ByParent() (map[string][]*Image, error) {
|
| 373 | 373 |
byParent[parent.ID] = []*Image{img}
|
| 374 | 374 |
} |
| 375 | 375 |
}) |
| 376 |
- return byParent, err |
|
| 376 |
+ return byParent |
|
| 377 | 377 |
} |
| 378 | 378 |
|
| 379 | 379 |
// Heads returns all heads in the graph, keyed by id. |
| 380 | 380 |
// A head is an image which is not the parent of another image in the graph. |
| 381 |
-func (graph *Graph) Heads() (map[string]*Image, error) {
|
|
| 381 |
+func (graph *Graph) Heads() map[string]*Image {
|
|
| 382 | 382 |
heads := make(map[string]*Image) |
| 383 |
- byParent, err := graph.ByParent() |
|
| 384 |
- if err != nil {
|
|
| 385 |
- return nil, err |
|
| 386 |
- } |
|
| 387 |
- err = graph.walkAll(func(image *Image) {
|
|
| 383 |
+ byParent := graph.ByParent() |
|
| 384 |
+ graph.walkAll(func(image *Image) {
|
|
| 388 | 385 |
// If it's not in the byParent lookup table, then |
| 389 | 386 |
// it's not a parent -> so it's a head! |
| 390 | 387 |
if _, exists := byParent[image.ID]; !exists {
|
| 391 | 388 |
heads[image.ID] = image |
| 392 | 389 |
} |
| 393 | 390 |
}) |
| 394 |
- return heads, err |
|
| 391 |
+ return heads |
|
| 395 | 392 |
} |
| 396 | 393 |
|
| 397 | 394 |
func (graph *Graph) imageRoot(id string) string {
|
| ... | ... |
@@ -56,9 +56,8 @@ func TestInit(t *testing.T) {
|
| 56 | 56 |
t.Fatal(err) |
| 57 | 57 |
} |
| 58 | 58 |
// Map() should be empty |
| 59 |
- if l, err := graph.Map(); err != nil {
|
|
| 60 |
- t.Fatal(err) |
|
| 61 |
- } else if len(l) != 0 {
|
|
| 59 |
+ l := graph.Map() |
|
| 60 |
+ if len(l) != 0 {
|
|
| 62 | 61 |
t.Fatalf("len(Map()) should return %d, not %d", 0, len(l))
|
| 63 | 62 |
} |
| 64 | 63 |
} |
| ... | ... |
@@ -110,10 +109,8 @@ func TestGraphCreate(t *testing.T) {
|
| 110 | 110 |
if img.DockerVersion != dockerversion.VERSION {
|
| 111 | 111 |
t.Fatalf("Wrong docker_version: should be '%s', not '%s'", dockerversion.VERSION, img.DockerVersion)
|
| 112 | 112 |
} |
| 113 |
- images, err := graph.Map() |
|
| 114 |
- if err != nil {
|
|
| 115 |
- t.Fatal(err) |
|
| 116 |
- } else if l := len(images); l != 1 {
|
|
| 113 |
+ images := graph.Map() |
|
| 114 |
+ if l := len(images); l != 1 {
|
|
| 117 | 115 |
t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
|
| 118 | 116 |
} |
| 119 | 117 |
if images[img.ID] == nil {
|
| ... | ... |
@@ -137,9 +134,8 @@ func TestRegister(t *testing.T) {
|
| 137 | 137 |
if err != nil {
|
| 138 | 138 |
t.Fatal(err) |
| 139 | 139 |
} |
| 140 |
- if images, err := graph.Map(); err != nil {
|
|
| 141 |
- t.Fatal(err) |
|
| 142 |
- } else if l := len(images); l != 1 {
|
|
| 140 |
+ images := graph.Map() |
|
| 141 |
+ if l := len(images); l != 1 {
|
|
| 143 | 142 |
t.Fatalf("Wrong number of images. Should be %d, not %d", 1, l)
|
| 144 | 143 |
} |
| 145 | 144 |
if resultImg, err := graph.Get(image.ID); err != nil {
|
| ... | ... |
@@ -254,10 +250,7 @@ func TestByParent(t *testing.T) {
|
| 254 | 254 |
_ = graph.Register(childImage1, archive2) |
| 255 | 255 |
_ = graph.Register(childImage2, archive3) |
| 256 | 256 |
|
| 257 |
- byParent, err := graph.ByParent() |
|
| 258 |
- if err != nil {
|
|
| 259 |
- t.Fatal(err) |
|
| 260 |
- } |
|
| 257 |
+ byParent := graph.ByParent() |
|
| 261 | 258 |
numChildren := len(byParent[parentImage.ID]) |
| 262 | 259 |
if numChildren != 2 {
|
| 263 | 260 |
t.Fatalf("Expected 2 children, found %d", numChildren)
|
| ... | ... |
@@ -277,9 +270,8 @@ func createTestImage(graph *Graph, t *testing.T) *Image {
|
| 277 | 277 |
} |
| 278 | 278 |
|
| 279 | 279 |
func assertNImages(graph *Graph, t *testing.T, n int) {
|
| 280 |
- if images, err := graph.Map(); err != nil {
|
|
| 281 |
- t.Fatal(err) |
|
| 282 |
- } else if actualN := len(images); actualN != n {
|
|
| 280 |
+ images := graph.Map() |
|
| 281 |
+ if actualN := len(images); actualN != n {
|
|
| 283 | 282 |
t.Fatalf("Expected %d images, found %d", n, actualN)
|
| 284 | 283 |
} |
| 285 | 284 |
} |
| ... | ... |
@@ -58,12 +58,9 @@ func (s *TagStore) Images(config *ImagesConfig) ([]*types.Image, error) {
|
| 58 | 58 |
_, filtLabel = imageFilters["label"] |
| 59 | 59 |
|
| 60 | 60 |
if config.All && filtTagged {
|
| 61 |
- allImages, err = s.graph.Map() |
|
| 61 |
+ allImages = s.graph.Map() |
|
| 62 | 62 |
} else {
|
| 63 |
- allImages, err = s.graph.Heads() |
|
| 64 |
- } |
|
| 65 |
- if err != nil {
|
|
| 66 |
- return nil, err |
|
| 63 |
+ allImages = s.graph.Heads() |
|
| 67 | 64 |
} |
| 68 | 65 |
|
| 69 | 66 |
lookup := make(map[string]*types.Image) |
| ... | ... |
@@ -31,10 +31,7 @@ func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error {
|
| 31 | 31 |
if err := os.Mkdir(repoDir, os.ModeDir); err != nil {
|
| 32 | 32 |
return err |
| 33 | 33 |
} |
| 34 |
- images, err := s.graph.Map() |
|
| 35 |
- if err != nil {
|
|
| 36 |
- return err |
|
| 37 |
- } |
|
| 34 |
+ images := s.graph.Map() |
|
| 38 | 35 |
excludes := make([]string, len(images)) |
| 39 | 36 |
i := 0 |
| 40 | 37 |
for k := range images {
|
| ... | ... |
@@ -108,3 +108,13 @@ func (idx *TruncIndex) Get(s string) (string, error) {
|
| 108 | 108 |
} |
| 109 | 109 |
return "", fmt.Errorf("no such id: %s", s)
|
| 110 | 110 |
} |
| 111 |
+ |
|
| 112 |
+// Iterates over all stored IDs, and passes each of them to the given handler |
|
| 113 |
+func (idx *TruncIndex) Iterate(handler func(id string)) {
|
|
| 114 |
+ idx.RLock() |
|
| 115 |
+ defer idx.RUnlock() |
|
| 116 |
+ idx.trie.Visit(func(prefix patricia.Prefix, item patricia.Item) error {
|
|
| 117 |
+ handler(string(prefix)) |
|
| 118 |
+ return nil |
|
| 119 |
+ }) |
|
| 120 |
+} |
| ... | ... |
@@ -96,6 +96,29 @@ func TestTruncIndex(t *testing.T) {
|
| 96 | 96 |
assertIndexGet(t, index, id[:7], id, false) |
| 97 | 97 |
assertIndexGet(t, index, id[:15], id, false) |
| 98 | 98 |
assertIndexGet(t, index, id, id, false) |
| 99 |
+ |
|
| 100 |
+ assertIndexIterate(t) |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+func assertIndexIterate(t *testing.T) {
|
|
| 104 |
+ ids := []string{
|
|
| 105 |
+ "19b36c2c326ccc11e726eee6ee78a0baf166ef96", |
|
| 106 |
+ "28b36c2c326ccc11e726eee6ee78a0baf166ef96", |
|
| 107 |
+ "37b36c2c326ccc11e726eee6ee78a0baf166ef96", |
|
| 108 |
+ "46b36c2c326ccc11e726eee6ee78a0baf166ef96", |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ index := NewTruncIndex(ids) |
|
| 112 |
+ |
|
| 113 |
+ index.Iterate(func(targetId string) {
|
|
| 114 |
+ for _, id := range ids {
|
|
| 115 |
+ if targetId == id {
|
|
| 116 |
+ return |
|
| 117 |
+ } |
|
| 118 |
+ } |
|
| 119 |
+ |
|
| 120 |
+ t.Fatalf("An unknown ID '%s'", targetId)
|
|
| 121 |
+ }) |
|
| 99 | 122 |
} |
| 100 | 123 |
|
| 101 | 124 |
func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult string, expectError bool) {
|