| package graph |
| |
| import ( |
| "fmt" |
| "strings" |
| |
| "github.com/docker/docker/api/types" |
| "github.com/docker/docker/image" |
| "github.com/docker/docker/utils" |
| ) |
| |
| // WalkHistory calls the handler function for each image in the |
| // provided images lineage starting from immediate parent. |
| func (graph *Graph) WalkHistory(img *image.Image, handler func(image.Image) error) (err error) { |
| currentImg := img |
| for currentImg != nil { |
| if handler != nil { |
| if err := handler(*currentImg); err != nil { |
| return err |
| } |
| } |
| currentImg, err = graph.GetParent(currentImg) |
| if err != nil { |
| return fmt.Errorf("Error while getting parent image: %v", err) |
| } |
| } |
| return nil |
| } |
| |
| // depth returns the number of parents for the current image |
| func (graph *Graph) depth(img *image.Image) (int, error) { |
| var ( |
| count = 0 |
| parent = img |
| err error |
| ) |
| |
| for parent != nil { |
| count++ |
| if parent, err = graph.GetParent(parent); err != nil { |
| return -1, err |
| } |
| } |
| return count, nil |
| } |
| |
| // Set the max depth to the aufs default that most |
| // kernels are compiled with |
| // For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk |
| const MaxImageDepth = 127 |
| |
| // CheckDepth returns an error if the depth of an image, as returned |
| // by ImageDepth, is too large to support creating a container from it |
| // on this daemon. |
| func (graph *Graph) CheckDepth(img *image.Image) error { |
| // We add 2 layers to the depth because the container's rw and |
| // init layer add to the restriction |
| depth, err := graph.depth(img) |
| if err != nil { |
| return err |
| } |
| if depth+2 >= MaxImageDepth { |
| return fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth) |
| } |
| return nil |
| } |
| |
| // History returns a slice of ImageHistory structures for the specified image |
| // name by walking the image lineage. |
| func (s *TagStore) History(name string) ([]*types.ImageHistory, error) { |
| foundImage, err := s.LookupImage(name) |
| if err != nil { |
| return nil, err |
| } |
| |
| lookupMap := make(map[string][]string) |
| for name, repository := range s.Repositories { |
| for tag, id := range repository { |
| // If the ID already has a reverse lookup, do not update it unless for "latest" |
| if _, exists := lookupMap[id]; !exists { |
| lookupMap[id] = []string{} |
| } |
| lookupMap[id] = append(lookupMap[id], utils.ImageReference(name, tag)) |
| } |
| } |
| |
| history := []*types.ImageHistory{} |
| |
| err = s.graph.WalkHistory(foundImage, func(img image.Image) error { |
| history = append(history, &types.ImageHistory{ |
| ID: img.ID, |
| Created: img.Created.Unix(), |
| CreatedBy: strings.Join(img.ContainerConfig.Cmd.Slice(), " "), |
| Tags: lookupMap[img.ID], |
| Size: img.Size, |
| Comment: img.Comment, |
| }) |
| return nil |
| }) |
| |
| return history, err |
| } |
| |
| // GetParent returns the parent image for the specified image. |
| func (graph *Graph) GetParent(img *image.Image) (*image.Image, error) { |
| if img.Parent == "" { |
| return nil, nil |
| } |
| return graph.Get(img.Parent) |
| } |
| |
| // GetParentsSize returns the combined size of all parent images. If there is |
| // no parent image or it's unavailable, it returns 0. |
| func (graph *Graph) GetParentsSize(img *image.Image) int64 { |
| parentImage, err := graph.GetParent(img) |
| if err != nil || parentImage == nil { |
| return 0 |
| } |
| return parentImage.Size + graph.GetParentsSize(parentImage) |
| } |