diff --git a/daemon/containerd/image_list.go b/daemon/containerd/image_list.go index bb9ea7df8a..59baa51b61 100644 --- a/daemon/containerd/image_list.go +++ b/daemon/containerd/image_list.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "strings" + "time" "github.com/containerd/containerd" "github.com/containerd/containerd/content" @@ -14,6 +15,7 @@ import ( "github.com/docker/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + timetypes "github.com/docker/docker/api/types/time" "github.com/moby/buildkit/util/attestation" "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/identity" @@ -29,6 +31,7 @@ var acceptedImageFilterTags = map[string]bool{ "before": true, "since": true, "reference": true, + "until": true, } // Images returns a filtered list of images. @@ -304,6 +307,27 @@ func (i *ImageService) setupFilters(ctx context.Context, imageFilters filters.Ar return nil, nil, err } + err = imageFilters.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 + } + until := time.Unix(seconds, nanoseconds) + + fltrs = append(fltrs, func(image images.Image) bool { + created := image.CreatedAt + return created.Before(until) + }) + return err + }) + if err != nil { + return nil, nil, err + } + labelFn, err := setupLabelFilter(i.client.ContentStore(), imageFilters) if err != nil { return nil, nil, err diff --git a/daemon/containerd/image_prune.go b/daemon/containerd/image_prune.go index 142512cbd2..e333e4c6bf 100644 --- a/daemon/containerd/image_prune.go +++ b/daemon/containerd/image_prune.go @@ -19,7 +19,7 @@ var imagesAcceptedFilters = map[string]bool{ "dangling": true, "label": true, "label!": true, - "until": false, + "until": true, } // errPruneRunning is returned when a prune request is received while