github.com/rish1988/moby@v25.0.2+incompatible/daemon/images/image_list.go (about)

     1  package images // import "github.com/docker/docker/daemon/images"
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"sort"
     8  	"time"
     9  
    10  	"github.com/distribution/reference"
    11  	"github.com/docker/docker/api/types/backend"
    12  	imagetypes "github.com/docker/docker/api/types/image"
    13  	timetypes "github.com/docker/docker/api/types/time"
    14  	"github.com/docker/docker/container"
    15  	"github.com/docker/docker/image"
    16  	"github.com/docker/docker/layer"
    17  )
    18  
    19  var acceptedImageFilterTags = map[string]bool{
    20  	"dangling":  true,
    21  	"label":     true,
    22  	"before":    true,
    23  	"since":     true,
    24  	"reference": true,
    25  	"until":     true,
    26  }
    27  
    28  // byCreated is a temporary type used to sort a list of images by creation
    29  // time.
    30  type byCreated []*imagetypes.Summary
    31  
    32  func (r byCreated) Len() int           { return len(r) }
    33  func (r byCreated) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }
    34  func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
    35  
    36  // Images returns a filtered list of images.
    37  func (i *ImageService) Images(ctx context.Context, opts imagetypes.ListOptions) ([]*imagetypes.Summary, error) {
    38  	if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	danglingOnly, err := opts.Filters.GetBoolOrDefault("dangling", false)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	var beforeFilter, sinceFilter time.Time
    48  	err = opts.Filters.WalkValues("before", func(value string) error {
    49  		img, err := i.GetImage(ctx, value, backend.GetImageOpts{})
    50  		if err != nil {
    51  			return err
    52  		}
    53  		// Resolve multiple values to the oldest image,
    54  		// equivalent to ANDing all the values together.
    55  		if img.Created != nil && (beforeFilter.IsZero() || beforeFilter.After(*img.Created)) {
    56  			beforeFilter = *img.Created
    57  		}
    58  		return nil
    59  	})
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	err = opts.Filters.WalkValues("until", func(value string) error {
    65  		ts, err := timetypes.GetTimestamp(value, time.Now())
    66  		if err != nil {
    67  			return err
    68  		}
    69  		seconds, nanoseconds, err := timetypes.ParseTimestamps(ts, 0)
    70  		if err != nil {
    71  			return err
    72  		}
    73  		timestamp := time.Unix(seconds, nanoseconds)
    74  		if beforeFilter.IsZero() || beforeFilter.After(timestamp) {
    75  			beforeFilter = timestamp
    76  		}
    77  		return nil
    78  	})
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	err = opts.Filters.WalkValues("since", func(value string) error {
    84  		img, err := i.GetImage(ctx, value, backend.GetImageOpts{})
    85  		if err != nil {
    86  			return err
    87  		}
    88  		// Resolve multiple values to the newest image,
    89  		// equivalent to ANDing all the values together.
    90  		if img.Created != nil && sinceFilter.Before(*img.Created) {
    91  			sinceFilter = *img.Created
    92  		}
    93  		return nil
    94  	})
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  
    99  	var selectedImages map[image.ID]*image.Image
   100  	if danglingOnly {
   101  		selectedImages = i.imageStore.Heads()
   102  	} else {
   103  		selectedImages = i.imageStore.Map()
   104  	}
   105  
   106  	var (
   107  		summaries     = make([]*imagetypes.Summary, 0, len(selectedImages))
   108  		summaryMap    map[*image.Image]*imagetypes.Summary
   109  		allContainers []*container.Container
   110  	)
   111  	for id, img := range selectedImages {
   112  		select {
   113  		case <-ctx.Done():
   114  			return nil, ctx.Err()
   115  		default:
   116  		}
   117  
   118  		if !beforeFilter.IsZero() && (img.Created == nil || !img.Created.Before(beforeFilter)) {
   119  			continue
   120  		}
   121  		if !sinceFilter.IsZero() && (img.Created == nil || !img.Created.After(sinceFilter)) {
   122  			continue
   123  		}
   124  
   125  		if opts.Filters.Contains("label") {
   126  			// Very old image that do not have image.Config (or even labels)
   127  			if img.Config == nil {
   128  				continue
   129  			}
   130  			// We are now sure image.Config is not nil
   131  			if !opts.Filters.MatchKVList("label", img.Config.Labels) {
   132  				continue
   133  			}
   134  		}
   135  
   136  		// Skip any images with an unsupported operating system to avoid a potential
   137  		// panic when indexing through the layerstore. Don't error as we want to list
   138  		// the other images. This should never happen, but here as a safety precaution.
   139  		if err := image.CheckOS(img.OperatingSystem()); err != nil {
   140  			continue
   141  		}
   142  
   143  		var size int64
   144  		if layerID := img.RootFS.ChainID(); layerID != "" {
   145  			l, err := i.layerStore.Get(layerID)
   146  			if err != nil {
   147  				// The layer may have been deleted between the call to `Map()` or
   148  				// `Heads()` and the call to `Get()`, so we just ignore this error
   149  				if errors.Is(err, layer.ErrLayerDoesNotExist) {
   150  					continue
   151  				}
   152  				return nil, err
   153  			}
   154  
   155  			size = l.Size()
   156  			layer.ReleaseAndLog(i.layerStore, l)
   157  		}
   158  
   159  		summary := newImageSummary(img, size)
   160  
   161  		for _, ref := range i.referenceStore.References(id.Digest()) {
   162  			if opts.Filters.Contains("reference") {
   163  				var found bool
   164  				var matchErr error
   165  				for _, pattern := range opts.Filters.Get("reference") {
   166  					found, matchErr = reference.FamiliarMatch(pattern, ref)
   167  					if matchErr != nil {
   168  						return nil, matchErr
   169  					}
   170  					if found {
   171  						break
   172  					}
   173  				}
   174  				if !found {
   175  					continue
   176  				}
   177  			}
   178  			if _, ok := ref.(reference.Canonical); ok {
   179  				summary.RepoDigests = append(summary.RepoDigests, reference.FamiliarString(ref))
   180  			}
   181  			if _, ok := ref.(reference.NamedTagged); ok {
   182  				summary.RepoTags = append(summary.RepoTags, reference.FamiliarString(ref))
   183  			}
   184  		}
   185  		if summary.RepoDigests == nil && summary.RepoTags == nil {
   186  			if opts.All || len(i.imageStore.Children(id)) == 0 {
   187  				if opts.Filters.Contains("dangling") && !danglingOnly {
   188  					// dangling=false case, so dangling image is not needed
   189  					continue
   190  				}
   191  				if opts.Filters.Contains("reference") { // skip images with no references if filtering by reference
   192  					continue
   193  				}
   194  			} else {
   195  				continue
   196  			}
   197  		} else if danglingOnly && len(summary.RepoTags) > 0 {
   198  			continue
   199  		}
   200  
   201  		if opts.ContainerCount {
   202  			// Lazily init allContainers.
   203  			if allContainers == nil {
   204  				allContainers = i.containers.List()
   205  			}
   206  
   207  			// Get container count
   208  			var containers int64
   209  			for _, c := range allContainers {
   210  				if c.ImageID == id {
   211  					containers++
   212  				}
   213  			}
   214  			// NOTE: By default, Containers is -1, or "not set"
   215  			summary.Containers = containers
   216  		}
   217  
   218  		if opts.ContainerCount || opts.SharedSize {
   219  			// Lazily init summaryMap.
   220  			if summaryMap == nil {
   221  				summaryMap = make(map[*image.Image]*imagetypes.Summary, len(selectedImages))
   222  			}
   223  			summaryMap[img] = summary
   224  		}
   225  		summaries = append(summaries, summary)
   226  	}
   227  
   228  	if opts.SharedSize {
   229  		allLayers := i.layerStore.Map()
   230  		layerRefs := make(map[layer.ChainID]int, len(allLayers))
   231  
   232  		allImages := selectedImages
   233  		if danglingOnly {
   234  			// If danglingOnly is true, then selectedImages include only dangling images,
   235  			// but we need to consider all existing images to correctly perform reference counting.
   236  			// If danglingOnly is false, selectedImages (and, hence, allImages) is already equal to i.imageStore.Map()
   237  			// and we can avoid performing an otherwise redundant method call.
   238  			allImages = i.imageStore.Map()
   239  		}
   240  		// Count layer references across all known images
   241  		for _, img := range allImages {
   242  			rootFS := *img.RootFS
   243  			rootFS.DiffIDs = nil
   244  			for _, id := range img.RootFS.DiffIDs {
   245  				rootFS.Append(id)
   246  				layerRefs[rootFS.ChainID()]++
   247  			}
   248  		}
   249  
   250  		// Get Shared sizes
   251  		for img, summary := range summaryMap {
   252  			rootFS := *img.RootFS
   253  			rootFS.DiffIDs = nil
   254  
   255  			// Indicate that we collected shared size information (default is -1, or "not set")
   256  			summary.SharedSize = 0
   257  			for _, id := range img.RootFS.DiffIDs {
   258  				rootFS.Append(id)
   259  				chid := rootFS.ChainID()
   260  
   261  				if layerRefs[chid] > 1 {
   262  					if _, ok := allLayers[chid]; !ok {
   263  						return nil, fmt.Errorf("layer %v was not found (corruption?)", chid)
   264  					}
   265  					summary.SharedSize += allLayers[chid].DiffSize()
   266  				}
   267  			}
   268  		}
   269  	}
   270  
   271  	sort.Sort(sort.Reverse(byCreated(summaries)))
   272  
   273  	return summaries, nil
   274  }
   275  
   276  func newImageSummary(image *image.Image, size int64) *imagetypes.Summary {
   277  	var created int64
   278  	if image.Created != nil {
   279  		created = image.Created.Unix()
   280  	}
   281  	summary := &imagetypes.Summary{
   282  		ParentID: image.Parent.String(),
   283  		ID:       image.ID().String(),
   284  		Created:  created,
   285  		Size:     size,
   286  		// -1 indicates that the value has not been set (avoids ambiguity
   287  		// between 0 (default) and "not set". We cannot use a pointer (nil)
   288  		// for this, as the JSON representation uses "omitempty", which would
   289  		// consider both "0" and "nil" to be "empty".
   290  		SharedSize: -1,
   291  		Containers: -1,
   292  	}
   293  	if image.Config != nil {
   294  		summary.Labels = image.Config.Labels
   295  	}
   296  	return summary
   297  }