package images // import "github.com/docker/docker/daemon/images" import ( "context" "fmt" "sort" "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" imagetypes "github.com/docker/docker/api/types/image" "github.com/docker/docker/container" "github.com/docker/docker/image" "github.com/docker/docker/layer" "github.com/docker/docker/pkg/system" ) var acceptedImageFilterTags = map[string]bool{ "dangling": true, "label": true, "before": true, "since": true, "reference": true, } // byCreated is a temporary type used to sort a list of images by creation // time. type byCreated []*types.ImageSummary 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 } // Images returns a filtered list of images. func (i *ImageService) Images(_ context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error) { if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil { return nil, err } var danglingOnly bool if opts.Filters.Contains("dangling") { if opts.Filters.ExactMatch("dangling", "true") { danglingOnly = true } else if !opts.Filters.ExactMatch("dangling", "false") { return nil, invalidFilter{"dangling", opts.Filters.Get("dangling")} } } var ( beforeFilter, sinceFilter *image.Image err error ) err = opts.Filters.WalkValues("before", func(value string) error { beforeFilter, err = i.GetImage(value, imagetypes.GetImageOpts{}) return err }) if err != nil { return nil, err } err = opts.Filters.WalkValues("since", func(value string) error { sinceFilter, err = i.GetImage(value, imagetypes.GetImageOpts{}) return err }) if err != nil { return nil, err } var selectedImages map[image.ID]*image.Image if danglingOnly { selectedImages = i.imageStore.Heads() } else { selectedImages = i.imageStore.Map() } var ( summaries = make([]*types.ImageSummary, 0, len(selectedImages)) summaryMap map[*image.Image]*types.ImageSummary allContainers []*container.Container ) for id, img := range selectedImages { 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 } } if opts.Filters.Contains("label") { // Very old image that do not have image.Config (or even labels) if img.Config == nil { continue } // We are now sure image.Config is not nil if !opts.Filters.MatchKVList("label", img.Config.Labels) { continue } } // Skip any images with an unsupported operating system to avoid a potential // panic when indexing through the layerstore. Don't error as we want to list // the other images. This should never happen, but here as a safety precaution. if !system.IsOSSupported(img.OperatingSystem()) { continue } var size int64 if layerID := img.RootFS.ChainID(); layerID != "" { l, err := i.layerStore.Get(layerID) if err != nil { // The layer may have been deleted between the call to `Map()` or // `Heads()` and the call to `Get()`, so we just ignore this error if err == layer.ErrLayerDoesNotExist { continue } return nil, err } size = l.Size() layer.ReleaseAndLog(i.layerStore, l) } summary := newImageSummary(img, size) for _, ref := range i.referenceStore.References(id.Digest()) { if opts.Filters.Contains("reference") { var found bool var matchErr error for _, pattern := range opts.Filters.Get("reference") { found, matchErr = reference.FamiliarMatch(pattern, ref) if matchErr != nil { return nil, matchErr } if found { break } } if !found { continue } } if _, ok := ref.(reference.Canonical); ok { summary.RepoDigests = append(summary.RepoDigests, reference.FamiliarString(ref)) } if _, ok := ref.(reference.NamedTagged); ok { summary.RepoTags = append(summary.RepoTags, reference.FamiliarString(ref)) } } if summary.RepoDigests == nil && summary.RepoTags == nil { if opts.All || len(i.imageStore.Children(id)) == 0 { if opts.Filters.Contains("dangling") && !danglingOnly { // dangling=false case, so dangling image is not needed continue } if opts.Filters.Contains("reference") { // skip images with no references if filtering by reference continue } summary.RepoDigests = []string{"@"} summary.RepoTags = []string{":"} } else { continue } } else if danglingOnly && len(summary.RepoTags) > 0 { continue } if opts.ContainerCount { // Lazily init allContainers. if allContainers == nil { allContainers = i.containers.List() } // Get container count var containers int64 for _, c := range allContainers { if c.ImageID == id { containers++ } } // NOTE: By default, Containers is -1, or "not set" summary.Containers = containers } if opts.ContainerCount || opts.SharedSize { // Lazily init summaryMap. if summaryMap == nil { summaryMap = make(map[*image.Image]*types.ImageSummary, len(selectedImages)) } summaryMap[img] = summary } summaries = append(summaries, summary) } if opts.SharedSize { allLayers := i.layerStore.Map() layerRefs := make(map[layer.ChainID]int, len(allLayers)) allImages := selectedImages if danglingOnly { // If danglingOnly is true, then selectedImages include only dangling images, // but we need to consider all existing images to correctly perform reference counting. // If danglingOnly is false, selectedImages (and, hence, allImages) is already equal to i.imageStore.Map() // and we can avoid performing an otherwise redundant method call. allImages = i.imageStore.Map() } // Count layer references across all known images for _, img := range allImages { rootFS := *img.RootFS rootFS.DiffIDs = nil for _, id := range img.RootFS.DiffIDs { rootFS.Append(id) layerRefs[rootFS.ChainID()]++ } } // Get Shared sizes for img, summary := range summaryMap { rootFS := *img.RootFS rootFS.DiffIDs = nil // Indicate that we collected shared size information (default is -1, or "not set") summary.SharedSize = 0 for _, id := range img.RootFS.DiffIDs { rootFS.Append(id) chid := rootFS.ChainID() if layerRefs[chid] > 1 { if _, ok := allLayers[chid]; !ok { return nil, fmt.Errorf("layer %v was not found (corruption?)", chid) } summary.SharedSize += allLayers[chid].DiffSize() } } } } sort.Sort(sort.Reverse(byCreated(summaries))) return summaries, nil } func newImageSummary(image *image.Image, size int64) *types.ImageSummary { summary := &types.ImageSummary{ ParentID: image.Parent.String(), ID: image.ID().String(), Created: image.Created.Unix(), Size: size, VirtualSize: size, // -1 indicates that the value has not been set (avoids ambiguity // between 0 (default) and "not set". We cannot use a pointer (nil) // for this, as the JSON representation uses "omitempty", which would // consider both "0" and "nil" to be "empty". SharedSize: -1, Containers: -1, } if image.Config != nil { summary.Labels = image.Config.Labels } return summary }