github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/daemon/list.go (about)

     1  package daemon
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sort"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/api/types/filters"
    13  	networktypes "github.com/docker/docker/api/types/network"
    14  	"github.com/docker/docker/container"
    15  	"github.com/docker/docker/image"
    16  	"github.com/docker/docker/volume"
    17  	"github.com/docker/go-connections/nat"
    18  )
    19  
    20  var acceptedVolumeFilterTags = map[string]bool{
    21  	"dangling": true,
    22  	"name":     true,
    23  	"driver":   true,
    24  	"label":    true,
    25  }
    26  
    27  var acceptedPsFilterTags = map[string]bool{
    28  	"ancestor":  true,
    29  	"before":    true,
    30  	"exited":    true,
    31  	"id":        true,
    32  	"isolation": true,
    33  	"label":     true,
    34  	"name":      true,
    35  	"status":    true,
    36  	"health":    true,
    37  	"since":     true,
    38  	"volume":    true,
    39  	"network":   true,
    40  	"is-task":   true,
    41  	"publish":   true,
    42  	"expose":    true,
    43  }
    44  
    45  // iterationAction represents possible outcomes happening during the container iteration.
    46  type iterationAction int
    47  
    48  // containerReducer represents a reducer for a container.
    49  // Returns the object to serialize by the api.
    50  type containerReducer func(*container.Container, *listContext) (*types.Container, error)
    51  
    52  const (
    53  	// includeContainer is the action to include a container in the reducer.
    54  	includeContainer iterationAction = iota
    55  	// excludeContainer is the action to exclude a container in the reducer.
    56  	excludeContainer
    57  	// stopIteration is the action to stop iterating over the list of containers.
    58  	stopIteration
    59  )
    60  
    61  // errStopIteration makes the iterator to stop without returning an error.
    62  var errStopIteration = errors.New("container list iteration stopped")
    63  
    64  // List returns an array of all containers registered in the daemon.
    65  func (daemon *Daemon) List() []*container.Container {
    66  	return daemon.containers.List()
    67  }
    68  
    69  // listContext is the daemon generated filtering to iterate over containers.
    70  // This is created based on the user specification from types.ContainerListOptions.
    71  type listContext struct {
    72  	// idx is the container iteration index for this context
    73  	idx int
    74  	// ancestorFilter tells whether it should check ancestors or not
    75  	ancestorFilter bool
    76  	// names is a list of container names to filter with
    77  	names map[string][]string
    78  	// images is a list of images to filter with
    79  	images map[image.ID]bool
    80  	// filters is a collection of arguments to filter with, specified by the user
    81  	filters filters.Args
    82  	// exitAllowed is a list of exit codes allowed to filter with
    83  	exitAllowed []int
    84  
    85  	// beforeFilter is a filter to ignore containers that appear before the one given
    86  	beforeFilter *container.Container
    87  	// sinceFilter is a filter to stop the filtering when the iterator arrive to the given container
    88  	sinceFilter *container.Container
    89  
    90  	// taskFilter tells if we should filter based on wether a container is part of a task
    91  	taskFilter bool
    92  	// isTask tells us if the we should filter container that are a task (true) or not (false)
    93  	isTask bool
    94  
    95  	// publish is a list of published ports to filter with
    96  	publish map[nat.Port]bool
    97  	// expose is a list of exposed ports to filter with
    98  	expose map[nat.Port]bool
    99  
   100  	// ContainerListOptions is the filters set by the user
   101  	*types.ContainerListOptions
   102  }
   103  
   104  // byContainerCreated is a temporary type used to sort a list of containers by creation time.
   105  type byContainerCreated []*container.Container
   106  
   107  func (r byContainerCreated) Len() int      { return len(r) }
   108  func (r byContainerCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
   109  func (r byContainerCreated) Less(i, j int) bool {
   110  	return r[i].Created.UnixNano() < r[j].Created.UnixNano()
   111  }
   112  
   113  // Containers returns the list of containers to show given the user's filtering.
   114  func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) {
   115  	return daemon.reduceContainers(config, daemon.transformContainer)
   116  }
   117  
   118  func (daemon *Daemon) filterByNameIDMatches(ctx *listContext) []*container.Container {
   119  	idSearch := false
   120  	names := ctx.filters.Get("name")
   121  	ids := ctx.filters.Get("id")
   122  	if len(names)+len(ids) == 0 {
   123  		// if name or ID filters are not in use, return to
   124  		// standard behavior of walking the entire container
   125  		// list from the daemon's in-memory store
   126  		return daemon.List()
   127  	}
   128  
   129  	// idSearch will determine if we limit name matching to the IDs
   130  	// matched from any IDs which were specified as filters
   131  	if len(ids) > 0 {
   132  		idSearch = true
   133  	}
   134  
   135  	matches := make(map[string]bool)
   136  	// find ID matches; errors represent "not found" and can be ignored
   137  	for _, id := range ids {
   138  		if fullID, err := daemon.idIndex.Get(id); err == nil {
   139  			matches[fullID] = true
   140  		}
   141  	}
   142  
   143  	// look for name matches; if ID filtering was used, then limit the
   144  	// search space to the matches map only; errors represent "not found"
   145  	// and can be ignored
   146  	if len(names) > 0 {
   147  		for id, idNames := range ctx.names {
   148  			// if ID filters were used and no matches on that ID were
   149  			// found, continue to next ID in the list
   150  			if idSearch && !matches[id] {
   151  				continue
   152  			}
   153  			for _, eachName := range idNames {
   154  				if ctx.filters.Match("name", eachName) {
   155  					matches[id] = true
   156  				}
   157  			}
   158  		}
   159  	}
   160  
   161  	cntrs := make([]*container.Container, 0, len(matches))
   162  	for id := range matches {
   163  		if c := daemon.containers.Get(id); c != nil {
   164  			cntrs = append(cntrs, c)
   165  		}
   166  	}
   167  
   168  	// Restore sort-order after filtering
   169  	// Created gives us nanosec resolution for sorting
   170  	sort.Sort(sort.Reverse(byContainerCreated(cntrs)))
   171  
   172  	return cntrs
   173  }
   174  
   175  // reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
   176  func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) {
   177  	var (
   178  		containers = []*types.Container{}
   179  	)
   180  
   181  	ctx, err := daemon.foldFilter(config)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  
   186  	// fastpath to only look at a subset of containers if specific name
   187  	// or ID matches were provided by the user--otherwise we potentially
   188  	// end up locking and querying many more containers than intended
   189  	containerList := daemon.filterByNameIDMatches(ctx)
   190  
   191  	for _, container := range containerList {
   192  		t, err := daemon.reducePsContainer(container, ctx, reducer)
   193  		if err != nil {
   194  			if err != errStopIteration {
   195  				return nil, err
   196  			}
   197  			break
   198  		}
   199  		if t != nil {
   200  			containers = append(containers, t)
   201  			ctx.idx++
   202  		}
   203  	}
   204  
   205  	return containers, nil
   206  }
   207  
   208  // reducePsContainer is the basic representation for a container as expected by the ps command.
   209  func (daemon *Daemon) reducePsContainer(container *container.Container, ctx *listContext, reducer containerReducer) (*types.Container, error) {
   210  	container.Lock()
   211  
   212  	// filter containers to return
   213  	action := includeContainerInList(container, ctx)
   214  	switch action {
   215  	case excludeContainer:
   216  		container.Unlock()
   217  		return nil, nil
   218  	case stopIteration:
   219  		container.Unlock()
   220  		return nil, errStopIteration
   221  	}
   222  
   223  	// transform internal container struct into api structs
   224  	newC, err := reducer(container, ctx)
   225  	container.Unlock()
   226  	if err != nil {
   227  		return nil, err
   228  	}
   229  
   230  	// release lock because size calculation is slow
   231  	if ctx.Size {
   232  		sizeRw, sizeRootFs := daemon.getSize(newC.ID)
   233  		newC.SizeRw = sizeRw
   234  		newC.SizeRootFs = sizeRootFs
   235  	}
   236  	return newC, nil
   237  }
   238  
   239  // foldFilter generates the container filter based on the user's filtering options.
   240  func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listContext, error) {
   241  	psFilters := config.Filters
   242  
   243  	if err := psFilters.Validate(acceptedPsFilterTags); err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	var filtExited []int
   248  
   249  	err := psFilters.WalkValues("exited", func(value string) error {
   250  		code, err := strconv.Atoi(value)
   251  		if err != nil {
   252  			return err
   253  		}
   254  		filtExited = append(filtExited, code)
   255  		return nil
   256  	})
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	err = psFilters.WalkValues("status", func(value string) error {
   262  		if !container.IsValidStateString(value) {
   263  			return fmt.Errorf("Unrecognised filter value for status: %s", value)
   264  		}
   265  
   266  		config.All = true
   267  		return nil
   268  	})
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	var taskFilter, isTask bool
   274  	if psFilters.Include("is-task") {
   275  		if psFilters.ExactMatch("is-task", "true") {
   276  			taskFilter = true
   277  			isTask = true
   278  		} else if psFilters.ExactMatch("is-task", "false") {
   279  			taskFilter = true
   280  			isTask = false
   281  		} else {
   282  			return nil, fmt.Errorf("Invalid filter 'is-task=%s'", psFilters.Get("is-task"))
   283  		}
   284  	}
   285  
   286  	err = psFilters.WalkValues("health", func(value string) error {
   287  		if !container.IsValidHealthString(value) {
   288  			return fmt.Errorf("Unrecognised filter value for health: %s", value)
   289  		}
   290  
   291  		return nil
   292  	})
   293  	if err != nil {
   294  		return nil, err
   295  	}
   296  
   297  	var beforeContFilter, sinceContFilter *container.Container
   298  
   299  	err = psFilters.WalkValues("before", func(value string) error {
   300  		beforeContFilter, err = daemon.GetContainer(value)
   301  		return err
   302  	})
   303  	if err != nil {
   304  		return nil, err
   305  	}
   306  
   307  	err = psFilters.WalkValues("since", func(value string) error {
   308  		sinceContFilter, err = daemon.GetContainer(value)
   309  		return err
   310  	})
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  
   315  	imagesFilter := map[image.ID]bool{}
   316  	var ancestorFilter bool
   317  	if psFilters.Include("ancestor") {
   318  		ancestorFilter = true
   319  		psFilters.WalkValues("ancestor", func(ancestor string) error {
   320  			id, err := daemon.GetImageID(ancestor)
   321  			if err != nil {
   322  				logrus.Warnf("Error while looking up for image %v", ancestor)
   323  				return nil
   324  			}
   325  			if imagesFilter[id] {
   326  				// Already seen this ancestor, skip it
   327  				return nil
   328  			}
   329  			// Then walk down the graph and put the imageIds in imagesFilter
   330  			populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children)
   331  			return nil
   332  		})
   333  	}
   334  
   335  	publishFilter := map[nat.Port]bool{}
   336  	err = psFilters.WalkValues("publish", portOp("publish", publishFilter))
   337  	if err != nil {
   338  		return nil, err
   339  	}
   340  
   341  	exposeFilter := map[nat.Port]bool{}
   342  	err = psFilters.WalkValues("expose", portOp("expose", exposeFilter))
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  
   347  	return &listContext{
   348  		filters:              psFilters,
   349  		ancestorFilter:       ancestorFilter,
   350  		images:               imagesFilter,
   351  		exitAllowed:          filtExited,
   352  		beforeFilter:         beforeContFilter,
   353  		sinceFilter:          sinceContFilter,
   354  		taskFilter:           taskFilter,
   355  		isTask:               isTask,
   356  		publish:              publishFilter,
   357  		expose:               exposeFilter,
   358  		ContainerListOptions: config,
   359  		names:                daemon.nameIndex.GetAll(),
   360  	}, nil
   361  }
   362  func portOp(key string, filter map[nat.Port]bool) func(value string) error {
   363  	return func(value string) error {
   364  		if strings.Contains(value, ":") {
   365  			return fmt.Errorf("filter for '%s' should not contain ':': %s", key, value)
   366  		}
   367  		//support two formats, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
   368  		proto, port := nat.SplitProtoPort(value)
   369  		start, end, err := nat.ParsePortRange(port)
   370  		if err != nil {
   371  			return fmt.Errorf("error while looking up for %s %s: %s", key, value, err)
   372  		}
   373  		for i := start; i <= end; i++ {
   374  			p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
   375  			if err != nil {
   376  				return fmt.Errorf("error while looking up for %s %s: %s", key, value, err)
   377  			}
   378  			filter[p] = true
   379  		}
   380  		return nil
   381  	}
   382  }
   383  
   384  // includeContainerInList decides whether a container should be included in the output or not based in the filter.
   385  // It also decides if the iteration should be stopped or not.
   386  func includeContainerInList(container *container.Container, ctx *listContext) iterationAction {
   387  	// Do not include container if it's in the list before the filter container.
   388  	// Set the filter container to nil to include the rest of containers after this one.
   389  	if ctx.beforeFilter != nil {
   390  		if container.ID == ctx.beforeFilter.ID {
   391  			ctx.beforeFilter = nil
   392  		}
   393  		return excludeContainer
   394  	}
   395  
   396  	// Stop iteration when the container arrives to the filter container
   397  	if ctx.sinceFilter != nil {
   398  		if container.ID == ctx.sinceFilter.ID {
   399  			return stopIteration
   400  		}
   401  	}
   402  
   403  	// Do not include container if it's stopped and we're not filters
   404  	if !container.Running && !ctx.All && ctx.Limit <= 0 {
   405  		return excludeContainer
   406  	}
   407  
   408  	// Do not include container if the name doesn't match
   409  	if !ctx.filters.Match("name", container.Name) {
   410  		return excludeContainer
   411  	}
   412  
   413  	// Do not include container if the id doesn't match
   414  	if !ctx.filters.Match("id", container.ID) {
   415  		return excludeContainer
   416  	}
   417  
   418  	if ctx.taskFilter {
   419  		if ctx.isTask != container.Managed {
   420  			return excludeContainer
   421  		}
   422  	}
   423  
   424  	// Do not include container if any of the labels don't match
   425  	if !ctx.filters.MatchKVList("label", container.Config.Labels) {
   426  		return excludeContainer
   427  	}
   428  
   429  	// Do not include container if isolation doesn't match
   430  	if excludeContainer == excludeByIsolation(container, ctx) {
   431  		return excludeContainer
   432  	}
   433  
   434  	// Stop iteration when the index is over the limit
   435  	if ctx.Limit > 0 && ctx.idx == ctx.Limit {
   436  		return stopIteration
   437  	}
   438  
   439  	// Do not include container if its exit code is not in the filter
   440  	if len(ctx.exitAllowed) > 0 {
   441  		shouldSkip := true
   442  		for _, code := range ctx.exitAllowed {
   443  			if code == container.ExitCode() && !container.Running && !container.StartedAt.IsZero() {
   444  				shouldSkip = false
   445  				break
   446  			}
   447  		}
   448  		if shouldSkip {
   449  			return excludeContainer
   450  		}
   451  	}
   452  
   453  	// Do not include container if its status doesn't match the filter
   454  	if !ctx.filters.Match("status", container.State.StateString()) {
   455  		return excludeContainer
   456  	}
   457  
   458  	// Do not include container if its health doesn't match the filter
   459  	if !ctx.filters.ExactMatch("health", container.State.HealthString()) {
   460  		return excludeContainer
   461  	}
   462  
   463  	if ctx.filters.Include("volume") {
   464  		volumesByName := make(map[string]*volume.MountPoint)
   465  		for _, m := range container.MountPoints {
   466  			if m.Name != "" {
   467  				volumesByName[m.Name] = m
   468  			} else {
   469  				volumesByName[m.Source] = m
   470  			}
   471  		}
   472  
   473  		volumeExist := fmt.Errorf("volume mounted in container")
   474  		err := ctx.filters.WalkValues("volume", func(value string) error {
   475  			if _, exist := container.MountPoints[value]; exist {
   476  				return volumeExist
   477  			}
   478  			if _, exist := volumesByName[value]; exist {
   479  				return volumeExist
   480  			}
   481  			return nil
   482  		})
   483  		if err != volumeExist {
   484  			return excludeContainer
   485  		}
   486  	}
   487  
   488  	if ctx.ancestorFilter {
   489  		if len(ctx.images) == 0 {
   490  			return excludeContainer
   491  		}
   492  		if !ctx.images[container.ImageID] {
   493  			return excludeContainer
   494  		}
   495  	}
   496  
   497  	networkExist := fmt.Errorf("container part of network")
   498  	if ctx.filters.Include("network") {
   499  		err := ctx.filters.WalkValues("network", func(value string) error {
   500  			if _, ok := container.NetworkSettings.Networks[value]; ok {
   501  				return networkExist
   502  			}
   503  			for _, nw := range container.NetworkSettings.Networks {
   504  				if nw.EndpointSettings == nil {
   505  					continue
   506  				}
   507  				if strings.HasPrefix(nw.NetworkID, value) {
   508  					return networkExist
   509  				}
   510  			}
   511  			return nil
   512  		})
   513  		if err != networkExist {
   514  			return excludeContainer
   515  		}
   516  	}
   517  
   518  	if len(ctx.publish) > 0 {
   519  		shouldSkip := true
   520  		for port := range ctx.publish {
   521  			if _, ok := container.HostConfig.PortBindings[port]; ok {
   522  				shouldSkip = false
   523  				break
   524  			}
   525  		}
   526  		if shouldSkip {
   527  			return excludeContainer
   528  		}
   529  	}
   530  
   531  	if len(ctx.expose) > 0 {
   532  		shouldSkip := true
   533  		for port := range ctx.expose {
   534  			if _, ok := container.Config.ExposedPorts[port]; ok {
   535  				shouldSkip = false
   536  				break
   537  			}
   538  		}
   539  		if shouldSkip {
   540  			return excludeContainer
   541  		}
   542  	}
   543  
   544  	return includeContainer
   545  }
   546  
   547  // transformContainer generates the container type expected by the docker ps command.
   548  func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) {
   549  	newC := &types.Container{
   550  		ID:      container.ID,
   551  		Names:   ctx.names[container.ID],
   552  		ImageID: container.ImageID.String(),
   553  	}
   554  	if newC.Names == nil {
   555  		// Dead containers will often have no name, so make sure the response isn't null
   556  		newC.Names = []string{}
   557  	}
   558  
   559  	image := container.Config.Image // if possible keep the original ref
   560  	if image != container.ImageID.String() {
   561  		id, err := daemon.GetImageID(image)
   562  		if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE {
   563  			return nil, err
   564  		}
   565  		if err != nil || id != container.ImageID {
   566  			image = container.ImageID.String()
   567  		}
   568  	}
   569  	newC.Image = image
   570  
   571  	if len(container.Args) > 0 {
   572  		args := []string{}
   573  		for _, arg := range container.Args {
   574  			if strings.Contains(arg, " ") {
   575  				args = append(args, fmt.Sprintf("'%s'", arg))
   576  			} else {
   577  				args = append(args, arg)
   578  			}
   579  		}
   580  		argsAsString := strings.Join(args, " ")
   581  
   582  		newC.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
   583  	} else {
   584  		newC.Command = container.Path
   585  	}
   586  	newC.Created = container.Created.Unix()
   587  	newC.State = container.State.StateString()
   588  	newC.Status = container.State.String()
   589  	newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
   590  	// copy networks to avoid races
   591  	networks := make(map[string]*networktypes.EndpointSettings)
   592  	for name, network := range container.NetworkSettings.Networks {
   593  		if network == nil || network.EndpointSettings == nil {
   594  			continue
   595  		}
   596  		networks[name] = &networktypes.EndpointSettings{
   597  			EndpointID:          network.EndpointID,
   598  			Gateway:             network.Gateway,
   599  			IPAddress:           network.IPAddress,
   600  			IPPrefixLen:         network.IPPrefixLen,
   601  			IPv6Gateway:         network.IPv6Gateway,
   602  			GlobalIPv6Address:   network.GlobalIPv6Address,
   603  			GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen,
   604  			MacAddress:          network.MacAddress,
   605  			NetworkID:           network.NetworkID,
   606  		}
   607  		if network.IPAMConfig != nil {
   608  			networks[name].IPAMConfig = &networktypes.EndpointIPAMConfig{
   609  				IPv4Address: network.IPAMConfig.IPv4Address,
   610  				IPv6Address: network.IPAMConfig.IPv6Address,
   611  			}
   612  		}
   613  	}
   614  	newC.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
   615  
   616  	newC.Ports = []types.Port{}
   617  	for port, bindings := range container.NetworkSettings.Ports {
   618  		p, err := nat.ParsePort(port.Port())
   619  		if err != nil {
   620  			return nil, err
   621  		}
   622  		if len(bindings) == 0 {
   623  			newC.Ports = append(newC.Ports, types.Port{
   624  				PrivatePort: uint16(p),
   625  				Type:        port.Proto(),
   626  			})
   627  			continue
   628  		}
   629  		for _, binding := range bindings {
   630  			h, err := nat.ParsePort(binding.HostPort)
   631  			if err != nil {
   632  				return nil, err
   633  			}
   634  			newC.Ports = append(newC.Ports, types.Port{
   635  				PrivatePort: uint16(p),
   636  				PublicPort:  uint16(h),
   637  				Type:        port.Proto(),
   638  				IP:          binding.HostIP,
   639  			})
   640  		}
   641  	}
   642  
   643  	newC.Labels = container.Config.Labels
   644  	newC.Mounts = addMountPoints(container)
   645  
   646  	return newC, nil
   647  }
   648  
   649  // Volumes lists known volumes, using the filter to restrict the range
   650  // of volumes returned.
   651  func (daemon *Daemon) Volumes(filter string) ([]*types.Volume, []string, error) {
   652  	var (
   653  		volumesOut []*types.Volume
   654  	)
   655  	volFilters, err := filters.FromParam(filter)
   656  	if err != nil {
   657  		return nil, nil, err
   658  	}
   659  
   660  	if err := volFilters.Validate(acceptedVolumeFilterTags); err != nil {
   661  		return nil, nil, err
   662  	}
   663  
   664  	volumes, warnings, err := daemon.volumes.List()
   665  	if err != nil {
   666  		return nil, nil, err
   667  	}
   668  
   669  	filterVolumes, err := daemon.filterVolumes(volumes, volFilters)
   670  	if err != nil {
   671  		return nil, nil, err
   672  	}
   673  	for _, v := range filterVolumes {
   674  		apiV := volumeToAPIType(v)
   675  		if vv, ok := v.(interface {
   676  			CachedPath() string
   677  		}); ok {
   678  			apiV.Mountpoint = vv.CachedPath()
   679  		} else {
   680  			apiV.Mountpoint = v.Path()
   681  		}
   682  		volumesOut = append(volumesOut, apiV)
   683  	}
   684  	return volumesOut, warnings, nil
   685  }
   686  
   687  // filterVolumes filters volume list according to user specified filter
   688  // and returns user chosen volumes
   689  func (daemon *Daemon) filterVolumes(vols []volume.Volume, filter filters.Args) ([]volume.Volume, error) {
   690  	// if filter is empty, return original volume list
   691  	if filter.Len() == 0 {
   692  		return vols, nil
   693  	}
   694  
   695  	var retVols []volume.Volume
   696  	for _, vol := range vols {
   697  		if filter.Include("name") {
   698  			if !filter.Match("name", vol.Name()) {
   699  				continue
   700  			}
   701  		}
   702  		if filter.Include("driver") {
   703  			if !filter.ExactMatch("driver", vol.DriverName()) {
   704  				continue
   705  			}
   706  		}
   707  		if filter.Include("label") {
   708  			v, ok := vol.(volume.DetailedVolume)
   709  			if !ok {
   710  				continue
   711  			}
   712  			if !filter.MatchKVList("label", v.Labels()) {
   713  				continue
   714  			}
   715  		}
   716  		retVols = append(retVols, vol)
   717  	}
   718  	danglingOnly := false
   719  	if filter.Include("dangling") {
   720  		if filter.ExactMatch("dangling", "true") || filter.ExactMatch("dangling", "1") {
   721  			danglingOnly = true
   722  		} else if !filter.ExactMatch("dangling", "false") && !filter.ExactMatch("dangling", "0") {
   723  			return nil, fmt.Errorf("Invalid filter 'dangling=%s'", filter.Get("dangling"))
   724  		}
   725  		retVols = daemon.volumes.FilterByUsed(retVols, !danglingOnly)
   726  	}
   727  	return retVols, nil
   728  }
   729  
   730  func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) {
   731  	if !ancestorMap[imageID] {
   732  		for _, id := range getChildren(imageID) {
   733  			populateImageFilterByParents(ancestorMap, id, getChildren)
   734  		}
   735  		ancestorMap[imageID] = true
   736  	}
   737  }