daemon/images.go
4352da78
 package daemon
 
 import (
 	"fmt"
 	"path"
 	"sort"
 
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
2655954c
 	"github.com/docker/docker/reference"
907407d0
 	"github.com/docker/engine-api/types"
 	"github.com/docker/engine-api/types/filters"
4352da78
 )
 
93d1dd80
 var acceptedImageFilterTags = map[string]bool{
 	"dangling": true,
 	"label":    true,
750e16f5
 	"before":   true,
 	"since":    true,
4352da78
 }
 
 // byCreated is a temporary type used to sort a list of images by creation
 // time.
 type byCreated []*types.Image
 
 func (r byCreated) Len() int           { return len(r) }
 func (r byCreated) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
 func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
 
 // Map returns a map of all images in the ImageStore
 func (daemon *Daemon) Map() map[image.ID]*image.Image {
 	return daemon.imageStore.Map()
 }
 
 // Images returns a filtered list of images. filterArgs is a JSON-encoded set
e98cae49
 // of filter arguments which will be interpreted by api/types/filters.
4352da78
 // filter is a shell glob string applied to repository names. The argument
 // named all controls whether all images in the graph are filtered, or just
 // the heads.
 func (daemon *Daemon) Images(filterArgs, filter string, all bool) ([]*types.Image, error) {
 	var (
 		allImages    map[image.ID]*image.Image
 		err          error
 		danglingOnly = false
 	)
 
 	imageFilters, err := filters.FromParam(filterArgs)
 	if err != nil {
 		return nil, err
 	}
93d1dd80
 	if err := imageFilters.Validate(acceptedImageFilterTags); err != nil {
 		return nil, err
4352da78
 	}
 
93d1dd80
 	if imageFilters.Include("dangling") {
 		if imageFilters.ExactMatch("dangling", "true") {
 			danglingOnly = true
 		} else if !imageFilters.ExactMatch("dangling", "false") {
 			return nil, fmt.Errorf("Invalid filter 'dangling=%s'", imageFilters.Get("dangling"))
4352da78
 		}
 	}
 	if danglingOnly {
 		allImages = daemon.imageStore.Heads()
 	} else {
 		allImages = daemon.imageStore.Map()
 	}
 
750e16f5
 	var beforeFilter, sinceFilter *image.Image
 	err = imageFilters.WalkValues("before", func(value string) error {
 		beforeFilter, err = daemon.GetImage(value)
 		return err
 	})
 	if err != nil {
 		return nil, err
 	}
 
 	err = imageFilters.WalkValues("since", func(value string) error {
 		sinceFilter, err = daemon.GetImage(value)
 		return err
 	})
 	if err != nil {
 		return nil, err
 	}
 
4352da78
 	images := []*types.Image{}
 
 	var filterTagged bool
 	if filter != "" {
2655954c
 		filterRef, err := reference.ParseNamed(filter)
4352da78
 		if err == nil { // parse error means wildcard repo
2655954c
 			if _, ok := filterRef.(reference.NamedTagged); ok {
4352da78
 				filterTagged = true
 			}
 		}
 	}
 
 	for id, img := range allImages {
750e16f5
 		if beforeFilter != nil {
 			if img.Created.Equal(beforeFilter.Created) || img.Created.After(beforeFilter.Created) {
 				continue
 			}
 		}
 
 		if sinceFilter != nil {
 			if img.Created.Equal(sinceFilter.Created) || img.Created.Before(sinceFilter.Created) {
 				continue
 			}
 		}
 
93d1dd80
 		if imageFilters.Include("label") {
 			// Very old image that do not have image.Config (or even labels)
4352da78
 			if img.Config == nil {
 				continue
 			}
 			// We are now sure image.Config is not nil
 			if !imageFilters.MatchKVList("label", img.Config.Labels) {
 				continue
 			}
 		}
 
 		layerID := img.RootFS.ChainID()
 		var size int64
 		if layerID != "" {
 			l, err := daemon.layerStore.Get(layerID)
 			if err != nil {
 				return nil, err
 			}
 
 			size, err = l.Size()
 			layer.ReleaseAndLog(daemon.layerStore, l)
 			if err != nil {
 				return nil, err
 			}
 		}
 
 		newImage := newImage(img, size)
 
2655954c
 		for _, ref := range daemon.referenceStore.References(id) {
4352da78
 			if filter != "" { // filter by tag/repo name
 				if filterTagged { // filter by tag, require full ref match
 					if ref.String() != filter {
 						continue
 					}
 				} else if matched, err := path.Match(filter, ref.Name()); !matched || err != nil { // name only match, FIXME: docs say exact
 					continue
 				}
 			}
2655954c
 			if _, ok := ref.(reference.Canonical); ok {
4352da78
 				newImage.RepoDigests = append(newImage.RepoDigests, ref.String())
 			}
2655954c
 			if _, ok := ref.(reference.NamedTagged); ok {
4352da78
 				newImage.RepoTags = append(newImage.RepoTags, ref.String())
 			}
 		}
 		if newImage.RepoDigests == nil && newImage.RepoTags == nil {
 			if all || len(daemon.imageStore.Children(id)) == 0 {
5ee69eb4
 
 				if imageFilters.Include("dangling") && !danglingOnly {
 					//dangling=false case, so dangling image is not needed
 					continue
 				}
4352da78
 				if filter != "" { // skip images with no references if filtering by tag
 					continue
 				}
 				newImage.RepoDigests = []string{"<none>@<none>"}
 				newImage.RepoTags = []string{"<none>:<none>"}
 			} else {
 				continue
 			}
 		} else if danglingOnly {
 			continue
 		}
 
 		images = append(images, newImage)
 	}
 
 	sort.Sort(sort.Reverse(byCreated(images)))
 
 	return images, nil
 }
 
 func newImage(image *image.Image, size int64) *types.Image {
 	newImage := new(types.Image)
 	newImage.ParentID = image.Parent.String()
 	newImage.ID = image.ID().String()
 	newImage.Created = image.Created.Unix()
 	newImage.Size = size
 	newImage.VirtualSize = size
 	if image.Config != nil {
 		newImage.Labels = image.Config.Labels
 	}
 	return newImage
 }