123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- package images // import "github.com/docker/docker/daemon/images"
- import (
- "context"
- "errors"
- "fmt"
- "sort"
- "time"
- "github.com/distribution/reference"
- "github.com/docker/docker/api/types"
- imagetypes "github.com/docker/docker/api/types/image"
- timetypes "github.com/docker/docker/api/types/time"
- "github.com/docker/docker/container"
- "github.com/docker/docker/image"
- "github.com/docker/docker/layer"
- )
- var acceptedImageFilterTags = map[string]bool{
- "dangling": true,
- "label": true,
- "before": true,
- "since": true,
- "reference": true,
- "until": true,
- }
- // byCreated is a temporary type used to sort a list of images by creation
- // time.
- type byCreated []*imagetypes.Summary
- 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(ctx context.Context, opts types.ImageListOptions) ([]*imagetypes.Summary, error) {
- if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil {
- return nil, err
- }
- danglingOnly, err := opts.Filters.GetBoolOrDefault("dangling", false)
- if err != nil {
- return nil, err
- }
- var beforeFilter, sinceFilter time.Time
- err = opts.Filters.WalkValues("before", func(value string) error {
- img, err := i.GetImage(ctx, value, imagetypes.GetImageOpts{})
- if err != nil {
- return err
- }
- // Resolve multiple values to the oldest image,
- // equivalent to ANDing all the values together.
- if img.Created != nil && (beforeFilter.IsZero() || beforeFilter.After(*img.Created)) {
- beforeFilter = *img.Created
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- err = opts.Filters.WalkValues("until", func(value string) error {
- ts, err := timetypes.GetTimestamp(value, time.Now())
- if err != nil {
- return err
- }
- seconds, nanoseconds, err := timetypes.ParseTimestamps(ts, 0)
- if err != nil {
- return err
- }
- timestamp := time.Unix(seconds, nanoseconds)
- if beforeFilter.IsZero() || beforeFilter.After(timestamp) {
- beforeFilter = timestamp
- }
- return nil
- })
- if err != nil {
- return nil, err
- }
- err = opts.Filters.WalkValues("since", func(value string) error {
- img, err := i.GetImage(ctx, value, imagetypes.GetImageOpts{})
- if err != nil {
- return err
- }
- // Resolve multiple values to the newest image,
- // equivalent to ANDing all the values together.
- if img.Created != nil && sinceFilter.Before(*img.Created) {
- sinceFilter = *img.Created
- }
- return nil
- })
- 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([]*imagetypes.Summary, 0, len(selectedImages))
- summaryMap map[*image.Image]*imagetypes.Summary
- allContainers []*container.Container
- )
- for id, img := range selectedImages {
- select {
- case <-ctx.Done():
- return nil, ctx.Err()
- default:
- }
- if !beforeFilter.IsZero() && (img.Created == nil || !img.Created.Before(beforeFilter)) {
- continue
- }
- if !sinceFilter.IsZero() && (img.Created == nil || !img.Created.After(sinceFilter)) {
- 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 err := image.CheckOS(img.OperatingSystem()); err != nil {
- 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 errors.Is(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
- }
- } 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]*imagetypes.Summary, 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) *imagetypes.Summary {
- var created int64
- if image.Created != nil {
- created = image.Created.Unix()
- }
- summary := &imagetypes.Summary{
- ParentID: image.Parent.String(),
- ID: image.ID().String(),
- Created: created,
- Size: 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
- }
|