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