github.com/ruphin/docker@v1.10.1/daemon/list.go (about)

     1  package daemon
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/Sirupsen/logrus"
    10  	"github.com/docker/docker/container"
    11  	"github.com/docker/docker/image"
    12  	"github.com/docker/engine-api/types"
    13  	"github.com/docker/engine-api/types/filters"
    14  	networktypes "github.com/docker/engine-api/types/network"
    15  	"github.com/docker/go-connections/nat"
    16  )
    17  
    18  var acceptedVolumeFilterTags = map[string]bool{
    19  	"dangling": true,
    20  }
    21  
    22  // iterationAction represents possible outcomes happening during the container iteration.
    23  type iterationAction int
    24  
    25  // containerReducer represents a reducer for a container.
    26  // Returns the object to serialize by the api.
    27  type containerReducer func(*container.Container, *listContext) (*types.Container, error)
    28  
    29  const (
    30  	// includeContainer is the action to include a container in the reducer.
    31  	includeContainer iterationAction = iota
    32  	// excludeContainer is the action to exclude a container in the reducer.
    33  	excludeContainer
    34  	// stopIteration is the action to stop iterating over the list of containers.
    35  	stopIteration
    36  )
    37  
    38  // errStopIteration makes the iterator to stop without returning an error.
    39  var errStopIteration = errors.New("container list iteration stopped")
    40  
    41  // List returns an array of all containers registered in the daemon.
    42  func (daemon *Daemon) List() []*container.Container {
    43  	return daemon.containers.List()
    44  }
    45  
    46  // ContainersConfig is the filtering specified by the user to iterate over containers.
    47  type ContainersConfig struct {
    48  	// if true show all containers, otherwise only running containers.
    49  	All bool
    50  	// show all containers created after this container id
    51  	Since string
    52  	// show all containers created before this container id
    53  	Before string
    54  	// number of containers to return at most
    55  	Limit int
    56  	// if true include the sizes of the containers
    57  	Size bool
    58  	// return only containers that match filters
    59  	Filters string
    60  }
    61  
    62  // listContext is the daemon generated filtering to iterate over containers.
    63  // This is created based on the user specification.
    64  type listContext struct {
    65  	// idx is the container iteration index for this context
    66  	idx int
    67  	// ancestorFilter tells whether it should check ancestors or not
    68  	ancestorFilter bool
    69  	// names is a list of container names to filter with
    70  	names map[string][]string
    71  	// images is a list of images to filter with
    72  	images map[image.ID]bool
    73  	// filters is a collection of arguments to filter with, specified by the user
    74  	filters filters.Args
    75  	// exitAllowed is a list of exit codes allowed to filter with
    76  	exitAllowed []int
    77  
    78  	// FIXME Remove this for 1.12 as --since and --before are deprecated
    79  	// beforeContainer is a filter to ignore containers that appear before the one given
    80  	beforeContainer *container.Container
    81  	// sinceContainer is a filter to stop the filtering when the iterator arrive to the given container
    82  	sinceContainer *container.Container
    83  
    84  	// beforeFilter is a filter to ignore containers that appear before the one given
    85  	// this is used for --filter=before= and --before=, the latter is deprecated.
    86  	beforeFilter *container.Container
    87  	// sinceFilter is a filter to stop the filtering when the iterator arrive to the given container
    88  	// this is used for --filter=since= and --since=, the latter is deprecated.
    89  	sinceFilter *container.Container
    90  	// ContainersConfig is the filters set by the user
    91  	*ContainersConfig
    92  }
    93  
    94  // Containers returns the list of containers to show given the user's filtering.
    95  func (daemon *Daemon) Containers(config *ContainersConfig) ([]*types.Container, error) {
    96  	return daemon.reduceContainers(config, daemon.transformContainer)
    97  }
    98  
    99  // reduceContainer parses the user filtering and generates the list of containers to return based on a reducer.
   100  func (daemon *Daemon) reduceContainers(config *ContainersConfig, reducer containerReducer) ([]*types.Container, error) {
   101  	containers := []*types.Container{}
   102  
   103  	ctx, err := daemon.foldFilter(config)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	for _, container := range daemon.List() {
   109  		t, err := daemon.reducePsContainer(container, ctx, reducer)
   110  		if err != nil {
   111  			if err != errStopIteration {
   112  				return nil, err
   113  			}
   114  			break
   115  		}
   116  		if t != nil {
   117  			containers = append(containers, t)
   118  			ctx.idx++
   119  		}
   120  	}
   121  	return containers, nil
   122  }
   123  
   124  // reducePsContainer is the basic representation for a container as expected by the ps command.
   125  func (daemon *Daemon) reducePsContainer(container *container.Container, ctx *listContext, reducer containerReducer) (*types.Container, error) {
   126  	container.Lock()
   127  	defer container.Unlock()
   128  
   129  	// filter containers to return
   130  	action := includeContainerInList(container, ctx)
   131  	switch action {
   132  	case excludeContainer:
   133  		return nil, nil
   134  	case stopIteration:
   135  		return nil, errStopIteration
   136  	}
   137  
   138  	// transform internal container struct into api structs
   139  	return reducer(container, ctx)
   140  }
   141  
   142  // foldFilter generates the container filter based in the user's filtering options.
   143  func (daemon *Daemon) foldFilter(config *ContainersConfig) (*listContext, error) {
   144  	psFilters, err := filters.FromParam(config.Filters)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	var filtExited []int
   150  	err = psFilters.WalkValues("exited", func(value string) error {
   151  		code, err := strconv.Atoi(value)
   152  		if err != nil {
   153  			return err
   154  		}
   155  		filtExited = append(filtExited, code)
   156  		return nil
   157  	})
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  
   162  	err = psFilters.WalkValues("status", func(value string) error {
   163  		if !container.IsValidStateString(value) {
   164  			return fmt.Errorf("Unrecognised filter value for status: %s", value)
   165  		}
   166  
   167  		config.All = true
   168  		return nil
   169  	})
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	var beforeContFilter, sinceContFilter *container.Container
   175  	// FIXME remove this for 1.12 as --since and --before are deprecated
   176  	var beforeContainer, sinceContainer *container.Container
   177  
   178  	err = psFilters.WalkValues("before", func(value string) error {
   179  		beforeContFilter, err = daemon.GetContainer(value)
   180  		return err
   181  	})
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  
   186  	err = psFilters.WalkValues("since", func(value string) error {
   187  		sinceContFilter, err = daemon.GetContainer(value)
   188  		return err
   189  	})
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	imagesFilter := map[image.ID]bool{}
   195  	var ancestorFilter bool
   196  	if psFilters.Include("ancestor") {
   197  		ancestorFilter = true
   198  		psFilters.WalkValues("ancestor", func(ancestor string) error {
   199  			id, err := daemon.GetImageID(ancestor)
   200  			if err != nil {
   201  				logrus.Warnf("Error while looking up for image %v", ancestor)
   202  				return nil
   203  			}
   204  			if imagesFilter[id] {
   205  				// Already seen this ancestor, skip it
   206  				return nil
   207  			}
   208  			// Then walk down the graph and put the imageIds in imagesFilter
   209  			populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children)
   210  			return nil
   211  		})
   212  	}
   213  
   214  	// FIXME remove this for 1.12 as --since and --before are deprecated
   215  	if config.Before != "" {
   216  		beforeContainer, err = daemon.GetContainer(config.Before)
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  	}
   221  
   222  	// FIXME remove this for 1.12 as --since and --before are deprecated
   223  	if config.Since != "" {
   224  		sinceContainer, err = daemon.GetContainer(config.Since)
   225  		if err != nil {
   226  			return nil, err
   227  		}
   228  	}
   229  
   230  	return &listContext{
   231  		filters:          psFilters,
   232  		ancestorFilter:   ancestorFilter,
   233  		images:           imagesFilter,
   234  		exitAllowed:      filtExited,
   235  		beforeContainer:  beforeContainer,
   236  		sinceContainer:   sinceContainer,
   237  		beforeFilter:     beforeContFilter,
   238  		sinceFilter:      sinceContFilter,
   239  		ContainersConfig: config,
   240  		names:            daemon.nameIndex.GetAll(),
   241  	}, nil
   242  }
   243  
   244  // includeContainerInList decides whether a containers should be include in the output or not based in the filter.
   245  // It also decides if the iteration should be stopped or not.
   246  func includeContainerInList(container *container.Container, ctx *listContext) iterationAction {
   247  	// Do not include container if it's stopped and we're not filters
   248  	// FIXME remove the ctx.beforContainer part of the condition for 1.12 as --since and --before are deprecated
   249  	if !container.Running && !ctx.All && ctx.Limit <= 0 && ctx.beforeContainer == nil && ctx.sinceContainer == nil {
   250  		return excludeContainer
   251  	}
   252  
   253  	// Do not include container if the name doesn't match
   254  	if !ctx.filters.Match("name", container.Name) {
   255  		return excludeContainer
   256  	}
   257  
   258  	// Do not include container if the id doesn't match
   259  	if !ctx.filters.Match("id", container.ID) {
   260  		return excludeContainer
   261  	}
   262  
   263  	// Do not include container if any of the labels don't match
   264  	if !ctx.filters.MatchKVList("label", container.Config.Labels) {
   265  		return excludeContainer
   266  	}
   267  
   268  	// Do not include container if the isolation mode doesn't match
   269  	if excludeContainer == excludeByIsolation(container, ctx) {
   270  		return excludeContainer
   271  	}
   272  
   273  	// FIXME remove this for 1.12 as --since and --before are deprecated
   274  	if ctx.beforeContainer != nil {
   275  		if container.ID == ctx.beforeContainer.ID {
   276  			ctx.beforeContainer = nil
   277  		}
   278  		return excludeContainer
   279  	}
   280  
   281  	// FIXME remove this for 1.12 as --since and --before are deprecated
   282  	if ctx.sinceContainer != nil {
   283  		if container.ID == ctx.sinceContainer.ID {
   284  			return stopIteration
   285  		}
   286  	}
   287  
   288  	// Do not include container if it's in the list before the filter container.
   289  	// Set the filter container to nil to include the rest of containers after this one.
   290  	if ctx.beforeFilter != nil {
   291  		if container.ID == ctx.beforeFilter.ID {
   292  			ctx.beforeFilter = nil
   293  		}
   294  		return excludeContainer
   295  	}
   296  
   297  	// Stop iteration when the container arrives to the filter container
   298  	if ctx.sinceFilter != nil {
   299  		if container.ID == ctx.sinceFilter.ID {
   300  			return stopIteration
   301  		}
   302  	}
   303  
   304  	// Stop iteration when the index is over the limit
   305  	if ctx.Limit > 0 && ctx.idx == ctx.Limit {
   306  		return stopIteration
   307  	}
   308  
   309  	// Do not include container if its exit code is not in the filter
   310  	if len(ctx.exitAllowed) > 0 {
   311  		shouldSkip := true
   312  		for _, code := range ctx.exitAllowed {
   313  			if code == container.ExitCode && !container.Running {
   314  				shouldSkip = false
   315  				break
   316  			}
   317  		}
   318  		if shouldSkip {
   319  			return excludeContainer
   320  		}
   321  	}
   322  
   323  	// Do not include container if its status doesn't match the filter
   324  	if !ctx.filters.Match("status", container.State.StateString()) {
   325  		return excludeContainer
   326  	}
   327  
   328  	if ctx.ancestorFilter {
   329  		if len(ctx.images) == 0 {
   330  			return excludeContainer
   331  		}
   332  		if !ctx.images[container.ImageID] {
   333  			return excludeContainer
   334  		}
   335  	}
   336  
   337  	return includeContainer
   338  }
   339  
   340  // transformContainer generates the container type expected by the docker ps command.
   341  func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) {
   342  	newC := &types.Container{
   343  		ID:      container.ID,
   344  		Names:   ctx.names[container.ID],
   345  		ImageID: container.ImageID.String(),
   346  	}
   347  	if newC.Names == nil {
   348  		// Dead containers will often have no name, so make sure the response isn't  null
   349  		newC.Names = []string{}
   350  	}
   351  
   352  	image := container.Config.Image // if possible keep the original ref
   353  	if image != container.ImageID.String() {
   354  		id, err := daemon.GetImageID(image)
   355  		if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE {
   356  			return nil, err
   357  		}
   358  		if err != nil || id != container.ImageID {
   359  			image = container.ImageID.String()
   360  		}
   361  	}
   362  	newC.Image = image
   363  
   364  	if len(container.Args) > 0 {
   365  		args := []string{}
   366  		for _, arg := range container.Args {
   367  			if strings.Contains(arg, " ") {
   368  				args = append(args, fmt.Sprintf("'%s'", arg))
   369  			} else {
   370  				args = append(args, arg)
   371  			}
   372  		}
   373  		argsAsString := strings.Join(args, " ")
   374  
   375  		newC.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
   376  	} else {
   377  		newC.Command = container.Path
   378  	}
   379  	newC.Created = container.Created.Unix()
   380  	newC.Status = container.State.String()
   381  	newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
   382  	// copy networks to avoid races
   383  	networks := make(map[string]*networktypes.EndpointSettings)
   384  	for name, network := range container.NetworkSettings.Networks {
   385  		if network == nil {
   386  			continue
   387  		}
   388  		networks[name] = &networktypes.EndpointSettings{
   389  			EndpointID:          network.EndpointID,
   390  			Gateway:             network.Gateway,
   391  			IPAddress:           network.IPAddress,
   392  			IPPrefixLen:         network.IPPrefixLen,
   393  			IPv6Gateway:         network.IPv6Gateway,
   394  			GlobalIPv6Address:   network.GlobalIPv6Address,
   395  			GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen,
   396  			MacAddress:          network.MacAddress,
   397  		}
   398  		if network.IPAMConfig != nil {
   399  			networks[name].IPAMConfig = &networktypes.EndpointIPAMConfig{
   400  				IPv4Address: network.IPAMConfig.IPv4Address,
   401  				IPv6Address: network.IPAMConfig.IPv6Address,
   402  			}
   403  		}
   404  	}
   405  	newC.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
   406  
   407  	newC.Ports = []types.Port{}
   408  	for port, bindings := range container.NetworkSettings.Ports {
   409  		p, err := nat.ParsePort(port.Port())
   410  		if err != nil {
   411  			return nil, err
   412  		}
   413  		if len(bindings) == 0 {
   414  			newC.Ports = append(newC.Ports, types.Port{
   415  				PrivatePort: p,
   416  				Type:        port.Proto(),
   417  			})
   418  			continue
   419  		}
   420  		for _, binding := range bindings {
   421  			h, err := nat.ParsePort(binding.HostPort)
   422  			if err != nil {
   423  				return nil, err
   424  			}
   425  			newC.Ports = append(newC.Ports, types.Port{
   426  				PrivatePort: p,
   427  				PublicPort:  h,
   428  				Type:        port.Proto(),
   429  				IP:          binding.HostIP,
   430  			})
   431  		}
   432  	}
   433  
   434  	if ctx.Size {
   435  		sizeRw, sizeRootFs := daemon.getSize(container)
   436  		newC.SizeRw = sizeRw
   437  		newC.SizeRootFs = sizeRootFs
   438  	}
   439  	newC.Labels = container.Config.Labels
   440  
   441  	return newC, nil
   442  }
   443  
   444  // Volumes lists known volumes, using the filter to restrict the range
   445  // of volumes returned.
   446  func (daemon *Daemon) Volumes(filter string) ([]*types.Volume, []string, error) {
   447  	var (
   448  		volumesOut   []*types.Volume
   449  		danglingOnly = false
   450  	)
   451  	volFilters, err := filters.FromParam(filter)
   452  	if err != nil {
   453  		return nil, nil, err
   454  	}
   455  
   456  	if err := volFilters.Validate(acceptedVolumeFilterTags); err != nil {
   457  		return nil, nil, err
   458  	}
   459  
   460  	if volFilters.Include("dangling") {
   461  		if volFilters.ExactMatch("dangling", "true") || volFilters.ExactMatch("dangling", "1") {
   462  			danglingOnly = true
   463  		} else if !volFilters.ExactMatch("dangling", "false") && !volFilters.ExactMatch("dangling", "0") {
   464  			return nil, nil, fmt.Errorf("Invalid filter 'dangling=%s'", volFilters.Get("dangling"))
   465  		}
   466  	}
   467  
   468  	volumes, warnings, err := daemon.volumes.List()
   469  	if err != nil {
   470  		return nil, nil, err
   471  	}
   472  	if volFilters.Include("dangling") {
   473  		volumes = daemon.volumes.FilterByUsed(volumes, !danglingOnly)
   474  	}
   475  	for _, v := range volumes {
   476  		volumesOut = append(volumesOut, volumeToAPIType(v))
   477  	}
   478  	return volumesOut, warnings, nil
   479  }
   480  
   481  func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) {
   482  	if !ancestorMap[imageID] {
   483  		for _, id := range getChildren(imageID) {
   484  			populateImageFilterByParents(ancestorMap, id, getChildren)
   485  		}
   486  		ancestorMap[imageID] = true
   487  	}
   488  }