2018-02-05 21:05:59 +00:00
|
|
|
package daemon // import "github.com/docker/docker/daemon"
|
2014-07-30 02:04:39 +00:00
|
|
|
|
|
|
|
import (
|
2022-08-09 12:42:50 +00:00
|
|
|
"context"
|
2014-07-30 02:04:39 +00:00
|
|
|
"fmt"
|
2016-08-04 02:02:39 +00:00
|
|
|
"sort"
|
2014-06-02 19:08:39 +00:00
|
|
|
"strconv"
|
2014-07-30 02:04:39 +00:00
|
|
|
"strings"
|
|
|
|
|
2023-09-13 15:41:45 +00:00
|
|
|
"github.com/containerd/log"
|
2016-09-06 18:18:12 +00:00
|
|
|
"github.com/docker/docker/api/types"
|
2023-08-25 21:51:48 +00:00
|
|
|
containertypes "github.com/docker/docker/api/types/container"
|
2016-09-06 18:18:12 +00:00
|
|
|
"github.com/docker/docker/api/types/filters"
|
2022-07-25 15:24:03 +00:00
|
|
|
imagetypes "github.com/docker/docker/api/types/image"
|
2015-11-12 19:55:17 +00:00
|
|
|
"github.com/docker/docker/container"
|
2018-01-11 19:53:06 +00:00
|
|
|
"github.com/docker/docker/errdefs"
|
2015-08-20 07:57:15 +00:00
|
|
|
"github.com/docker/docker/image"
|
2015-12-18 17:58:48 +00:00
|
|
|
"github.com/docker/go-connections/nat"
|
2017-07-19 14:20:13 +00:00
|
|
|
"github.com/pkg/errors"
|
2014-07-30 02:04:39 +00:00
|
|
|
)
|
|
|
|
|
2016-03-15 03:54:12 +00:00
|
|
|
var acceptedPsFilterTags = map[string]bool{
|
|
|
|
"ancestor": true,
|
|
|
|
"before": true,
|
|
|
|
"exited": true,
|
|
|
|
"id": true,
|
|
|
|
"isolation": true,
|
|
|
|
"label": true,
|
|
|
|
"name": true,
|
|
|
|
"status": true,
|
2016-07-15 18:21:19 +00:00
|
|
|
"health": true,
|
2016-03-15 03:54:12 +00:00
|
|
|
"since": true,
|
|
|
|
"volume": true,
|
2016-05-27 19:20:31 +00:00
|
|
|
"network": true,
|
2016-09-28 10:45:30 +00:00
|
|
|
"is-task": true,
|
2016-10-15 22:53:06 +00:00
|
|
|
"publish": true,
|
|
|
|
"expose": true,
|
2016-03-15 03:54:12 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
// iterationAction represents possible outcomes happening during the container iteration.
|
|
|
|
type iterationAction int
|
|
|
|
|
|
|
|
const (
|
2023-09-15 05:41:59 +00:00
|
|
|
// includeContainer is the action to include a container.
|
2015-08-26 08:16:53 +00:00
|
|
|
includeContainer iterationAction = iota
|
2023-09-15 05:41:59 +00:00
|
|
|
// excludeContainer is the action to exclude a container.
|
2015-08-26 08:16:53 +00:00
|
|
|
excludeContainer
|
|
|
|
// stopIteration is the action to stop iterating over the list of containers.
|
|
|
|
stopIteration
|
|
|
|
)
|
|
|
|
|
2014-07-31 21:26:25 +00:00
|
|
|
// List returns an array of all containers registered in the daemon.
|
2015-11-12 19:55:17 +00:00
|
|
|
func (daemon *Daemon) List() []*container.Container {
|
2014-07-31 21:26:25 +00:00
|
|
|
return daemon.containers.List()
|
|
|
|
}
|
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
// listContext is the daemon generated filtering to iterate over containers.
|
2023-08-25 21:51:48 +00:00
|
|
|
// This is created based on the user specification from [containertypes.ListOptions].
|
2015-08-26 08:16:53 +00:00
|
|
|
type listContext struct {
|
|
|
|
// idx is the container iteration index for this context
|
|
|
|
idx int
|
|
|
|
// ancestorFilter tells whether it should check ancestors or not
|
|
|
|
ancestorFilter bool
|
|
|
|
// names is a list of container names to filter with
|
|
|
|
names map[string][]string
|
|
|
|
// images is a list of images to filter with
|
2015-11-18 22:20:54 +00:00
|
|
|
images map[image.ID]bool
|
2015-08-26 08:16:53 +00:00
|
|
|
// filters is a collection of arguments to filter with, specified by the user
|
|
|
|
filters filters.Args
|
|
|
|
// exitAllowed is a list of exit codes allowed to filter with
|
|
|
|
exitAllowed []int
|
2016-02-09 08:06:29 +00:00
|
|
|
|
2015-11-05 07:08:00 +00:00
|
|
|
// beforeFilter is a filter to ignore containers that appear before the one given
|
2017-02-22 23:01:46 +00:00
|
|
|
beforeFilter *container.Snapshot
|
2018-09-20 12:00:35 +00:00
|
|
|
// sinceFilter is a filter to stop the filtering when the iterator arrives to the given container
|
2017-02-22 23:01:46 +00:00
|
|
|
sinceFilter *container.Snapshot
|
2016-09-28 10:45:30 +00:00
|
|
|
|
2018-09-20 12:00:35 +00:00
|
|
|
// taskFilter tells if we should filter based on whether a container is part of a task
|
2016-09-28 10:45:30 +00:00
|
|
|
taskFilter bool
|
2018-09-20 12:00:35 +00:00
|
|
|
// isTask tells us if we should filter container that is a task (true) or not (false)
|
2016-09-28 10:45:30 +00:00
|
|
|
isTask bool
|
2016-10-15 22:53:06 +00:00
|
|
|
|
|
|
|
// publish is a list of published ports to filter with
|
|
|
|
publish map[nat.Port]bool
|
|
|
|
// expose is a list of exposed ports to filter with
|
|
|
|
expose map[nat.Port]bool
|
|
|
|
|
2023-08-25 21:51:48 +00:00
|
|
|
// ListOptions is the filters set by the user
|
|
|
|
*containertypes.ListOptions
|
2015-08-26 08:16:53 +00:00
|
|
|
}
|
|
|
|
|
2017-03-27 17:18:53 +00:00
|
|
|
// byCreatedDescending is a temporary type used to sort a list of containers by creation time.
|
|
|
|
type byCreatedDescending []container.Snapshot
|
2016-08-04 02:02:39 +00:00
|
|
|
|
2017-03-27 17:18:53 +00:00
|
|
|
func (r byCreatedDescending) Len() int { return len(r) }
|
|
|
|
func (r byCreatedDescending) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
|
|
|
func (r byCreatedDescending) Less(i, j int) bool {
|
|
|
|
return r[j].CreatedAt.UnixNano() < r[i].CreatedAt.UnixNano()
|
2016-08-04 02:02:39 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
// Containers returns the list of containers to show given the user's filtering.
|
2023-08-25 21:51:48 +00:00
|
|
|
func (daemon *Daemon) Containers(ctx context.Context, config *containertypes.ListOptions) ([]*types.Container, error) {
|
2023-09-15 05:41:59 +00:00
|
|
|
if err := config.Filters.Validate(acceptedPsFilterTags); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
view = daemon.containersReplica.Snapshot()
|
|
|
|
containers = []*types.Container{}
|
|
|
|
)
|
|
|
|
|
|
|
|
filter, err := daemon.foldFilter(ctx, view, config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// fastpath to only look at a subset of containers if specific name
|
|
|
|
// or ID matches were provided by the user--otherwise we potentially
|
|
|
|
// end up querying many more containers than intended
|
|
|
|
containerList, err := daemon.filterByNameIDMatches(view, filter)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range containerList {
|
|
|
|
currentContainer := &containerList[i]
|
|
|
|
switch includeContainerInList(currentContainer, filter) {
|
|
|
|
case excludeContainer:
|
|
|
|
continue
|
|
|
|
case stopIteration:
|
|
|
|
return containers, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// transform internal container struct into api structs
|
|
|
|
newC, err := daemon.refreshImage(ctx, currentContainer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// release lock because size calculation is slow
|
|
|
|
if filter.Size {
|
|
|
|
sizeRw, sizeRootFs, err := daemon.imageService.GetContainerLayerSize(ctx, newC.ID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
newC.SizeRw = sizeRw
|
|
|
|
newC.SizeRootFs = sizeRootFs
|
|
|
|
}
|
|
|
|
if newC != nil {
|
|
|
|
containers = append(containers, newC)
|
|
|
|
filter.idx++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return containers, nil
|
2015-08-26 08:16:53 +00:00
|
|
|
}
|
|
|
|
|
2022-09-21 10:20:13 +00:00
|
|
|
func (daemon *Daemon) filterByNameIDMatches(view *container.View, filter *listContext) ([]container.Snapshot, error) {
|
2016-05-29 03:31:30 +00:00
|
|
|
idSearch := false
|
2022-08-08 10:55:10 +00:00
|
|
|
names := filter.filters.Get("name")
|
|
|
|
ids := filter.filters.Get("id")
|
2016-05-29 03:31:30 +00:00
|
|
|
if len(names)+len(ids) == 0 {
|
|
|
|
// if name or ID filters are not in use, return to
|
|
|
|
// standard behavior of walking the entire container
|
|
|
|
// list from the daemon's in-memory store
|
2017-02-22 23:01:46 +00:00
|
|
|
all, err := view.All()
|
2022-07-08 11:07:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-03-27 17:18:53 +00:00
|
|
|
sort.Sort(byCreatedDescending(all))
|
2022-07-08 11:07:19 +00:00
|
|
|
return all, nil
|
2016-05-29 03:31:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// idSearch will determine if we limit name matching to the IDs
|
|
|
|
// matched from any IDs which were specified as filters
|
|
|
|
if len(ids) > 0 {
|
|
|
|
idSearch = true
|
|
|
|
}
|
|
|
|
|
|
|
|
matches := make(map[string]bool)
|
|
|
|
// find ID matches; errors represent "not found" and can be ignored
|
|
|
|
for _, id := range ids {
|
2022-05-20 10:12:02 +00:00
|
|
|
if fullID, err := daemon.containersReplica.GetByPrefix(id); err == nil {
|
2016-05-29 03:31:30 +00:00
|
|
|
matches[fullID] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// look for name matches; if ID filtering was used, then limit the
|
|
|
|
// search space to the matches map only; errors represent "not found"
|
|
|
|
// and can be ignored
|
|
|
|
if len(names) > 0 {
|
2022-08-08 10:55:10 +00:00
|
|
|
for id, idNames := range filter.names {
|
2016-05-29 03:31:30 +00:00
|
|
|
// if ID filters were used and no matches on that ID were
|
|
|
|
// found, continue to next ID in the list
|
|
|
|
if idSearch && !matches[id] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, eachName := range idNames {
|
2018-08-28 19:40:13 +00:00
|
|
|
// match both on container name with, and without slash-prefix
|
2022-08-08 10:55:10 +00:00
|
|
|
if filter.filters.Match("name", eachName) || filter.filters.Match("name", strings.TrimPrefix(eachName, "/")) {
|
2016-05-29 03:31:30 +00:00
|
|
|
matches[id] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-22 23:01:46 +00:00
|
|
|
cntrs := make([]container.Snapshot, 0, len(matches))
|
2016-05-29 03:31:30 +00:00
|
|
|
for id := range matches {
|
2017-02-22 23:01:46 +00:00
|
|
|
c, err := view.Get(id)
|
2022-07-08 11:12:30 +00:00
|
|
|
if err != nil {
|
|
|
|
if errdefs.IsNotFound(err) {
|
|
|
|
// ignore error
|
|
|
|
continue
|
|
|
|
}
|
2017-06-29 22:25:17 +00:00
|
|
|
return nil, err
|
2016-08-26 17:02:10 +00:00
|
|
|
}
|
2022-07-08 11:12:30 +00:00
|
|
|
cntrs = append(cntrs, *c)
|
2016-05-29 03:31:30 +00:00
|
|
|
}
|
2016-08-04 02:02:39 +00:00
|
|
|
|
|
|
|
// Restore sort-order after filtering
|
|
|
|
// Created gives us nanosec resolution for sorting
|
2017-03-27 17:18:53 +00:00
|
|
|
sort.Sort(byCreatedDescending(cntrs))
|
2016-08-04 02:02:39 +00:00
|
|
|
|
2017-02-22 23:01:46 +00:00
|
|
|
return cntrs, nil
|
2016-05-29 03:31:30 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 17:20:41 +00:00
|
|
|
// foldFilter generates the container filter based on the user's filtering options.
|
2023-08-25 21:51:48 +00:00
|
|
|
func (daemon *Daemon) foldFilter(ctx context.Context, view *container.View, config *containertypes.ListOptions) (*listContext, error) {
|
2016-11-01 14:01:16 +00:00
|
|
|
psFilters := config.Filters
|
2015-08-26 08:16:53 +00:00
|
|
|
|
|
|
|
var filtExited []int
|
2016-03-15 03:54:12 +00:00
|
|
|
|
2016-01-27 22:09:42 +00:00
|
|
|
err := psFilters.WalkValues("exited", func(value string) error {
|
2015-11-26 01:27:11 +00:00
|
|
|
code, err := strconv.Atoi(value)
|
|
|
|
if err != nil {
|
2022-11-17 16:45:50 +00:00
|
|
|
return errdefs.InvalidParameter(errors.Wrapf(err, "invalid filter 'exited=%s'", value))
|
2014-06-02 19:08:39 +00:00
|
|
|
}
|
2015-11-26 01:27:11 +00:00
|
|
|
filtExited = append(filtExited, code)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2014-06-02 19:08:39 +00:00
|
|
|
}
|
|
|
|
|
2015-11-26 01:27:11 +00:00
|
|
|
err = psFilters.WalkValues("status", func(value string) error {
|
2015-11-12 19:55:17 +00:00
|
|
|
if !container.IsValidStateString(value) {
|
2022-11-17 16:45:50 +00:00
|
|
|
return errdefs.InvalidParameter(fmt.Errorf("invalid filter 'status=%s'", value))
|
2015-01-08 23:14:04 +00:00
|
|
|
}
|
2015-11-26 01:27:11 +00:00
|
|
|
|
|
|
|
config.All = true
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-01-08 23:14:04 +00:00
|
|
|
}
|
2015-08-20 07:57:15 +00:00
|
|
|
|
2022-11-17 16:45:50 +00:00
|
|
|
taskFilter := psFilters.Contains("is-task")
|
|
|
|
isTask, err := psFilters.GetBoolOrDefault("is-task", false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-09-28 10:45:30 +00:00
|
|
|
}
|
|
|
|
|
2016-07-15 18:21:19 +00:00
|
|
|
err = psFilters.WalkValues("health", func(value string) error {
|
|
|
|
if !container.IsValidHealthString(value) {
|
2022-11-17 16:45:50 +00:00
|
|
|
return errdefs.InvalidParameter(fmt.Errorf("unrecognized filter value for health: %s", value))
|
2016-07-15 18:21:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-22 23:01:46 +00:00
|
|
|
var beforeContFilter, sinceContFilter *container.Snapshot
|
2016-02-09 08:06:29 +00:00
|
|
|
|
2015-11-26 01:27:11 +00:00
|
|
|
err = psFilters.WalkValues("before", func(value string) error {
|
2018-01-05 01:47:53 +00:00
|
|
|
beforeContFilter, err = idOrNameFilter(view, value)
|
2015-11-26 01:27:11 +00:00
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-11-05 07:08:00 +00:00
|
|
|
}
|
|
|
|
|
2015-11-26 01:27:11 +00:00
|
|
|
err = psFilters.WalkValues("since", func(value string) error {
|
2018-01-05 01:47:53 +00:00
|
|
|
sinceContFilter, err = idOrNameFilter(view, value)
|
2015-11-26 01:27:11 +00:00
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-11-05 07:08:00 +00:00
|
|
|
}
|
|
|
|
|
2015-11-18 22:20:54 +00:00
|
|
|
imagesFilter := map[image.ID]bool{}
|
2015-08-26 08:16:53 +00:00
|
|
|
var ancestorFilter bool
|
2017-09-26 11:39:56 +00:00
|
|
|
if psFilters.Contains("ancestor") {
|
2015-08-20 07:57:15 +00:00
|
|
|
ancestorFilter = true
|
2023-04-13 13:07:17 +00:00
|
|
|
err := psFilters.WalkValues("ancestor", func(ancestor string) error {
|
2022-08-09 12:42:50 +00:00
|
|
|
img, err := daemon.imageService.GetImage(ctx, ancestor, imagetypes.GetImageOpts{})
|
2015-08-20 07:57:15 +00:00
|
|
|
if err != nil {
|
2023-06-23 00:33:17 +00:00
|
|
|
log.G(ctx).Warnf("Error while looking up for image %v", ancestor)
|
2015-11-26 01:27:11 +00:00
|
|
|
return nil
|
2015-08-20 07:57:15 +00:00
|
|
|
}
|
2018-02-14 20:19:37 +00:00
|
|
|
if imagesFilter[img.ID()] {
|
2015-08-20 07:57:15 +00:00
|
|
|
// Already seen this ancestor, skip it
|
2015-11-26 01:27:11 +00:00
|
|
|
return nil
|
2015-08-20 07:57:15 +00:00
|
|
|
}
|
|
|
|
// Then walk down the graph and put the imageIds in imagesFilter
|
2023-04-13 13:07:17 +00:00
|
|
|
return populateImageFilterByParents(ctx, imagesFilter, img.ID(), daemon.imageService.Children)
|
2015-11-26 01:27:11 +00:00
|
|
|
})
|
2023-04-13 13:07:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-08-20 07:57:15 +00:00
|
|
|
}
|
|
|
|
|
2016-10-15 22:53:06 +00:00
|
|
|
publishFilter := map[nat.Port]bool{}
|
2017-02-28 10:11:55 +00:00
|
|
|
err = psFilters.WalkValues("publish", portOp("publish", publishFilter))
|
2016-10-15 22:53:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
exposeFilter := map[nat.Port]bool{}
|
2017-02-28 10:11:55 +00:00
|
|
|
err = psFilters.WalkValues("expose", portOp("expose", exposeFilter))
|
2016-10-15 22:53:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
return &listContext{
|
2023-08-25 21:51:48 +00:00
|
|
|
filters: psFilters,
|
|
|
|
ancestorFilter: ancestorFilter,
|
|
|
|
images: imagesFilter,
|
|
|
|
exitAllowed: filtExited,
|
|
|
|
beforeFilter: beforeContFilter,
|
|
|
|
sinceFilter: sinceContFilter,
|
|
|
|
taskFilter: taskFilter,
|
|
|
|
isTask: isTask,
|
|
|
|
publish: publishFilter,
|
|
|
|
expose: exposeFilter,
|
|
|
|
ListOptions: config,
|
|
|
|
names: view.GetAllNames(),
|
2015-08-26 08:16:53 +00:00
|
|
|
}, nil
|
|
|
|
}
|
2018-01-05 01:47:53 +00:00
|
|
|
|
2022-09-21 10:20:13 +00:00
|
|
|
func idOrNameFilter(view *container.View, value string) (*container.Snapshot, error) {
|
2018-01-05 01:47:53 +00:00
|
|
|
filter, err := view.Get(value)
|
2022-07-08 11:12:30 +00:00
|
|
|
if err != nil && errdefs.IsNotFound(err) {
|
2018-01-05 01:47:53 +00:00
|
|
|
// Try name search instead
|
|
|
|
found := ""
|
|
|
|
for id, idNames := range view.GetAllNames() {
|
|
|
|
for _, eachName := range idNames {
|
|
|
|
if strings.TrimPrefix(value, "/") == strings.TrimPrefix(eachName, "/") {
|
|
|
|
if found != "" && found != id {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
found = id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if found != "" {
|
|
|
|
filter, err = view.Get(found)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return filter, err
|
|
|
|
}
|
|
|
|
|
2017-02-28 10:11:55 +00:00
|
|
|
func portOp(key string, filter map[nat.Port]bool) func(value string) error {
|
|
|
|
return func(value string) error {
|
|
|
|
if strings.Contains(value, ":") {
|
|
|
|
return fmt.Errorf("filter for '%s' should not contain ':': %s", key, value)
|
|
|
|
}
|
2019-11-27 14:43:53 +00:00
|
|
|
// support two formats, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
|
2017-02-28 10:11:55 +00:00
|
|
|
proto, port := nat.SplitProtoPort(value)
|
|
|
|
start, end, err := nat.ParsePortRange(port)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while looking up for %s %s: %s", key, value, err)
|
|
|
|
}
|
|
|
|
for i := start; i <= end; i++ {
|
|
|
|
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while looking up for %s %s: %s", key, value, err)
|
|
|
|
}
|
|
|
|
filter[p] = true
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2014-10-13 06:12:44 +00:00
|
|
|
|
2016-03-17 17:15:32 +00:00
|
|
|
// includeContainerInList decides whether a container should be included in the output or not based in the filter.
|
2015-08-26 08:16:53 +00:00
|
|
|
// It also decides if the iteration should be stopped or not.
|
2022-08-08 10:55:10 +00:00
|
|
|
func includeContainerInList(container *container.Snapshot, filter *listContext) iterationAction {
|
2016-02-19 06:07:16 +00:00
|
|
|
// Do not include container if it's in the list before the filter container.
|
|
|
|
// Set the filter container to nil to include the rest of containers after this one.
|
2022-08-08 10:55:10 +00:00
|
|
|
if filter.beforeFilter != nil {
|
|
|
|
if container.ID == filter.beforeFilter.ID {
|
|
|
|
filter.beforeFilter = nil
|
2016-02-19 06:07:16 +00:00
|
|
|
}
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop iteration when the container arrives to the filter container
|
2022-08-08 10:55:10 +00:00
|
|
|
if filter.sinceFilter != nil {
|
|
|
|
if container.ID == filter.sinceFilter.ID {
|
2016-02-19 06:07:16 +00:00
|
|
|
return stopIteration
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
// Do not include container if it's stopped and we're not filters
|
2022-08-08 10:55:10 +00:00
|
|
|
if !container.Running && !filter.All && filter.Limit <= 0 {
|
2015-08-26 08:16:53 +00:00
|
|
|
return excludeContainer
|
|
|
|
}
|
2014-10-13 06:12:44 +00:00
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
// Do not include container if the name doesn't match
|
2022-08-08 10:55:10 +00:00
|
|
|
if !filter.filters.Match("name", container.Name) && !filter.filters.Match("name", strings.TrimPrefix(container.Name, "/")) {
|
2015-08-26 08:16:53 +00:00
|
|
|
return excludeContainer
|
|
|
|
}
|
2015-01-07 00:04:10 +00:00
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
// Do not include container if the id doesn't match
|
2022-08-08 10:55:10 +00:00
|
|
|
if !filter.filters.Match("id", container.ID) {
|
2015-08-26 08:16:53 +00:00
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
2022-08-08 10:55:10 +00:00
|
|
|
if filter.taskFilter {
|
|
|
|
if filter.isTask != container.Managed {
|
2016-09-28 10:45:30 +00:00
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
// Do not include container if any of the labels don't match
|
2022-08-08 10:55:10 +00:00
|
|
|
if !filter.filters.MatchKVList("label", container.Labels) {
|
2015-08-26 08:16:53 +00:00
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
2016-02-03 20:07:00 +00:00
|
|
|
// Do not include container if isolation doesn't match
|
2022-08-08 10:55:10 +00:00
|
|
|
if excludeContainer == excludeByIsolation(container, filter) {
|
2015-10-19 16:33:43 +00:00
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
2015-11-05 07:08:00 +00:00
|
|
|
// Stop iteration when the index is over the limit
|
2022-08-08 10:55:10 +00:00
|
|
|
if filter.Limit > 0 && filter.idx == filter.Limit {
|
2015-11-05 07:08:00 +00:00
|
|
|
return stopIteration
|
|
|
|
}
|
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
// Do not include container if its exit code is not in the filter
|
2022-08-08 10:55:10 +00:00
|
|
|
if len(filter.exitAllowed) > 0 {
|
2015-08-26 08:16:53 +00:00
|
|
|
shouldSkip := true
|
2022-08-08 10:55:10 +00:00
|
|
|
for _, code := range filter.exitAllowed {
|
2017-02-22 23:01:46 +00:00
|
|
|
if code == container.ExitCode && !container.Running && !container.StartedAt.IsZero() {
|
2015-08-26 08:16:53 +00:00
|
|
|
shouldSkip = false
|
|
|
|
break
|
2014-07-30 02:04:39 +00:00
|
|
|
}
|
|
|
|
}
|
2015-08-26 08:16:53 +00:00
|
|
|
if shouldSkip {
|
|
|
|
return excludeContainer
|
2014-06-02 19:08:39 +00:00
|
|
|
}
|
2015-08-26 08:16:53 +00:00
|
|
|
}
|
2014-10-13 06:12:44 +00:00
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
// Do not include container if its status doesn't match the filter
|
2022-08-08 10:55:10 +00:00
|
|
|
if !filter.filters.Match("status", container.State) {
|
2015-08-26 08:16:53 +00:00
|
|
|
return excludeContainer
|
|
|
|
}
|
2015-08-20 07:57:15 +00:00
|
|
|
|
2016-07-15 18:21:19 +00:00
|
|
|
// Do not include container if its health doesn't match the filter
|
2022-08-08 10:55:10 +00:00
|
|
|
if !filter.filters.ExactMatch("health", container.Health) {
|
2016-07-15 18:21:19 +00:00
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
2022-08-08 10:55:10 +00:00
|
|
|
if filter.filters.Contains("volume") {
|
2017-02-22 23:01:46 +00:00
|
|
|
volumesByName := make(map[string]types.MountPoint)
|
|
|
|
for _, m := range container.Mounts {
|
2016-03-16 21:10:16 +00:00
|
|
|
if m.Name != "" {
|
|
|
|
volumesByName[m.Name] = m
|
|
|
|
} else {
|
|
|
|
volumesByName[m.Source] = m
|
|
|
|
}
|
2016-02-03 22:46:01 +00:00
|
|
|
}
|
2017-02-22 23:01:46 +00:00
|
|
|
volumesByDestination := make(map[string]types.MountPoint)
|
|
|
|
for _, m := range container.Mounts {
|
|
|
|
if m.Destination != "" {
|
|
|
|
volumesByDestination[m.Destination] = m
|
|
|
|
}
|
|
|
|
}
|
2016-02-03 22:46:01 +00:00
|
|
|
|
|
|
|
volumeExist := fmt.Errorf("volume mounted in container")
|
2022-08-08 10:55:10 +00:00
|
|
|
err := filter.filters.WalkValues("volume", func(value string) error {
|
2017-02-22 23:01:46 +00:00
|
|
|
if _, exist := volumesByDestination[value]; exist {
|
2016-02-03 22:46:01 +00:00
|
|
|
return volumeExist
|
|
|
|
}
|
|
|
|
if _, exist := volumesByName[value]; exist {
|
|
|
|
return volumeExist
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != volumeExist {
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-08 10:55:10 +00:00
|
|
|
if filter.ancestorFilter {
|
|
|
|
if len(filter.images) == 0 {
|
2015-08-26 08:16:53 +00:00
|
|
|
return excludeContainer
|
2015-08-20 07:57:15 +00:00
|
|
|
}
|
2022-08-08 10:55:10 +00:00
|
|
|
if !filter.images[image.ID(container.ImageID)] {
|
2015-08-26 08:16:53 +00:00
|
|
|
return excludeContainer
|
2015-04-04 00:39:06 +00:00
|
|
|
}
|
2015-08-26 08:16:53 +00:00
|
|
|
}
|
2015-05-01 01:13:18 +00:00
|
|
|
|
2017-03-27 17:18:53 +00:00
|
|
|
var (
|
|
|
|
networkExist = errors.New("container part of network")
|
|
|
|
noNetworks = errors.New("container is not part of any networks")
|
|
|
|
)
|
2022-08-08 10:55:10 +00:00
|
|
|
if filter.filters.Contains("network") {
|
|
|
|
err := filter.filters.WalkValues("network", func(value string) error {
|
2017-03-27 17:18:53 +00:00
|
|
|
if container.NetworkSettings == nil {
|
|
|
|
return noNetworks
|
|
|
|
}
|
2016-06-06 00:04:33 +00:00
|
|
|
if _, ok := container.NetworkSettings.Networks[value]; ok {
|
2016-05-27 19:20:31 +00:00
|
|
|
return networkExist
|
|
|
|
}
|
2016-06-06 00:04:33 +00:00
|
|
|
for _, nw := range container.NetworkSettings.Networks {
|
2017-02-22 23:01:46 +00:00
|
|
|
if nw == nil {
|
2016-08-23 23:50:15 +00:00
|
|
|
continue
|
|
|
|
}
|
2016-11-22 20:02:46 +00:00
|
|
|
if strings.HasPrefix(nw.NetworkID, value) {
|
2016-06-06 00:04:33 +00:00
|
|
|
return networkExist
|
|
|
|
}
|
|
|
|
}
|
2016-05-27 19:20:31 +00:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != networkExist {
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-08 10:55:10 +00:00
|
|
|
if len(filter.expose) > 0 || len(filter.publish) > 0 {
|
2020-02-01 17:34:54 +00:00
|
|
|
var (
|
2021-05-31 09:54:56 +00:00
|
|
|
shouldSkip = true
|
2020-02-01 17:34:54 +00:00
|
|
|
publishedPort nat.Port
|
|
|
|
exposedPort nat.Port
|
|
|
|
)
|
|
|
|
for _, port := range container.Ports {
|
|
|
|
publishedPort = nat.Port(fmt.Sprintf("%d/%s", port.PublicPort, port.Type))
|
|
|
|
exposedPort = nat.Port(fmt.Sprintf("%d/%s", port.PrivatePort, port.Type))
|
2022-08-08 10:55:10 +00:00
|
|
|
if ok := filter.publish[publishedPort]; ok {
|
2016-10-15 22:53:06 +00:00
|
|
|
shouldSkip = false
|
|
|
|
break
|
2022-08-08 10:55:10 +00:00
|
|
|
} else if ok := filter.expose[exposedPort]; ok {
|
2016-10-15 22:53:06 +00:00
|
|
|
shouldSkip = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if shouldSkip {
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-26 08:16:53 +00:00
|
|
|
return includeContainer
|
|
|
|
}
|
2015-05-01 01:13:18 +00:00
|
|
|
|
2023-07-26 11:20:20 +00:00
|
|
|
// refreshImage checks if the Image ref still points to the correct ID, and
|
|
|
|
// updates the ref to the actual ID when it doesn't.
|
|
|
|
// This happens when the image with a reference that was used to create
|
|
|
|
// container was deleted or updated and now resolves to a different ID.
|
|
|
|
//
|
|
|
|
// For example:
|
|
|
|
// $ docker run -d busybox:latest
|
|
|
|
// $ docker ps -a
|
|
|
|
// CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
|
|
|
// b0318bca5aef busybox "sh" 4 seconds ago Exited (0) 3 seconds ago ecstatic_beaver
|
|
|
|
//
|
|
|
|
// After some time, busybox image got updated on the Docker Hub:
|
|
|
|
// $ docker pull busybox:latest
|
|
|
|
//
|
|
|
|
// So now busybox:latest points to a different digest, but that doesn't impact
|
|
|
|
// the ecstatic_beaver container which was still created under an older
|
|
|
|
// version. In this case, it should still point to the original image ID it was
|
|
|
|
// created from.
|
|
|
|
//
|
|
|
|
// $ docker ps -a
|
|
|
|
// CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
|
|
|
// b0318bca5aef 3fbc63216742 "sh" 3 years ago Exited (0) 3 years ago ecstatic_beaver
|
2023-07-26 12:52:11 +00:00
|
|
|
func (daemon *Daemon) refreshImage(ctx context.Context, s *container.Snapshot) (*types.Container, error) {
|
2017-03-27 17:18:53 +00:00
|
|
|
c := s.Container
|
2023-07-26 11:20:20 +00:00
|
|
|
|
|
|
|
// s.Image is the image reference passed by the user to create an image
|
|
|
|
// can be a:
|
|
|
|
// - name (like nginx, ubuntu:latest, docker.io/library/busybox:latest),
|
|
|
|
// - truncated ID (abcdef),
|
|
|
|
// - full digest (sha256:abcdef...)
|
|
|
|
//
|
|
|
|
// s.ImageID is the ID of the image that s.Image resolved to at the time
|
|
|
|
// of the container creation. It's always a full digest.
|
|
|
|
|
|
|
|
// If these match, there's nothing to refresh.
|
|
|
|
if s.Image == s.ImageID {
|
|
|
|
return &c, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the image reference still resolves to the same digest.
|
|
|
|
img, err := daemon.imageService.GetImage(ctx, s.Image, imagetypes.GetImageOpts{})
|
|
|
|
// If the image is no longer found or can't be resolved for some other
|
|
|
|
// reason. Update the Image to the specific ID of the original image it
|
|
|
|
// resolved to when the container was created.
|
|
|
|
if err != nil {
|
|
|
|
if !errdefs.IsNotFound(err) {
|
2023-07-30 15:18:56 +00:00
|
|
|
log.G(ctx).WithFields(log.Fields{
|
|
|
|
"error": err,
|
|
|
|
"containerID": c.ID,
|
|
|
|
"image": s.Image,
|
|
|
|
"imageID": s.ImageID,
|
2023-07-26 11:20:20 +00:00
|
|
|
}).Warn("failed to resolve container image")
|
2015-11-18 22:20:54 +00:00
|
|
|
}
|
2023-07-26 11:20:20 +00:00
|
|
|
c.Image = s.ImageID
|
|
|
|
return &c, nil
|
2015-08-26 08:16:53 +00:00
|
|
|
}
|
2023-07-26 11:20:20 +00:00
|
|
|
|
|
|
|
// Also update the image to the specific image ID, if the Image now
|
|
|
|
// resolves to a different ID.
|
|
|
|
if img.ImageID() != s.ImageID {
|
|
|
|
c.Image = s.ImageID
|
|
|
|
}
|
|
|
|
|
2017-03-27 17:18:53 +00:00
|
|
|
return &c, nil
|
2014-07-30 02:04:39 +00:00
|
|
|
}
|
2015-06-12 13:25:32 +00:00
|
|
|
|
2023-04-13 13:07:17 +00:00
|
|
|
func populateImageFilterByParents(ctx context.Context, ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(context.Context, image.ID) ([]image.ID, error)) error {
|
2015-08-20 07:57:15 +00:00
|
|
|
if !ancestorMap[imageID] {
|
2023-04-13 13:07:17 +00:00
|
|
|
children, err := getChildren(ctx, imageID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, id := range children {
|
|
|
|
if err := populateImageFilterByParents(ctx, ancestorMap, id, getChildren); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-08-20 07:57:15 +00:00
|
|
|
}
|
|
|
|
ancestorMap[imageID] = true
|
|
|
|
}
|
2023-04-13 13:07:17 +00:00
|
|
|
return nil
|
2015-08-20 07:57:15 +00:00
|
|
|
}
|