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) }