github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/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  }
    37  
    38  // iterationAction represents possible outcomes happening during the container iteration.
    39  type iterationAction int
    40  
    41  // containerReducer represents a reducer for a container.
    42  // Returns the object to serialize by the api.
    43  type containerReducer func(*container.Container, *listContext) (*types.Container, error)
    44  
    45  const (
    46  	// includeContainer is the action to include a container in the reducer.
    47  	includeContainer iterationAction = iota
    48  	// excludeContainer is the action to exclude a container in the reducer.
    49  	excludeContainer
    50  	// stopIteration is the action to stop iterating over the list of containers.
    51  	stopIteration
    52  )
    53  
    54  // errStopIteration makes the iterator to stop without returning an error.
    55  var errStopIteration = errors.New("container list iteration stopped")
    56  
    57  // List returns an array of all containers registered in the daemon.
    58  func (daemon *Daemon) List() []*container.Container {
    59  	return daemon.containers.List()
    60  }
    61  
    62  // listContext is the daemon generated filtering to iterate over containers.
    63  // This is created based on the user specification from types.ContainerListOptions.
    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  	// ContainerListOptions is the filters set by the user
    91  	*types.ContainerListOptions
    92  }
    93  
    94  // Containers returns the list of containers to show given the user's filtering.
    95  func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) {
    96  	return daemon.reduceContainers(config, daemon.transformContainer)
    97  }
    98  
    99  // reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
   100  func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, 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 on the user's filtering options.
   143  func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listContext, error) {
   144  	psFilters := config.Filter
   145  
   146  	if err := psFilters.Validate(acceptedPsFilterTags); err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	var filtExited []int
   151  
   152  	err := psFilters.WalkValues("exited", func(value string) error {
   153  		code, err := strconv.Atoi(value)
   154  		if err != nil {
   155  			return err
   156  		}
   157  		filtExited = append(filtExited, code)
   158  		return nil
   159  	})
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	err = psFilters.WalkValues("status", func(value string) error {
   165  		if !container.IsValidStateString(value) {
   166  			return fmt.Errorf("Unrecognised filter value for status: %s", value)
   167  		}
   168  
   169  		config.All = true
   170  		return nil
   171  	})
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	var beforeContFilter, sinceContFilter *container.Container
   177  	// FIXME remove this for 1.12 as --since and --before are deprecated
   178  	var beforeContainer, sinceContainer *container.Container
   179  
   180  	err = psFilters.WalkValues("before", func(value string) error {
   181  		beforeContFilter, err = daemon.GetContainer(value)
   182  		return err
   183  	})
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	err = psFilters.WalkValues("since", func(value string) error {
   189  		sinceContFilter, err = daemon.GetContainer(value)
   190  		return err
   191  	})
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	imagesFilter := map[image.ID]bool{}
   197  	var ancestorFilter bool
   198  	if psFilters.Include("ancestor") {
   199  		ancestorFilter = true
   200  		psFilters.WalkValues("ancestor", func(ancestor string) error {
   201  			id, err := daemon.GetImageID(ancestor)
   202  			if err != nil {
   203  				logrus.Warnf("Error while looking up for image %v", ancestor)
   204  				return nil
   205  			}
   206  			if imagesFilter[id] {
   207  				// Already seen this ancestor, skip it
   208  				return nil
   209  			}
   210  			// Then walk down the graph and put the imageIds in imagesFilter
   211  			populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children)
   212  			return nil
   213  		})
   214  	}
   215  
   216  	// FIXME remove this for 1.12 as --since and --before are deprecated
   217  	if config.Before != "" {
   218  		beforeContainer, err = daemon.GetContainer(config.Before)
   219  		if err != nil {
   220  			return nil, err
   221  		}
   222  	}
   223  
   224  	// FIXME remove this for 1.12 as --since and --before are deprecated
   225  	if config.Since != "" {
   226  		sinceContainer, err = daemon.GetContainer(config.Since)
   227  		if err != nil {
   228  			return nil, err
   229  		}
   230  	}
   231  
   232  	return &listContext{
   233  		filters:              psFilters,
   234  		ancestorFilter:       ancestorFilter,
   235  		images:               imagesFilter,
   236  		exitAllowed:          filtExited,
   237  		beforeContainer:      beforeContainer,
   238  		sinceContainer:       sinceContainer,
   239  		beforeFilter:         beforeContFilter,
   240  		sinceFilter:          sinceContFilter,
   241  		ContainerListOptions: config,
   242  		names:                daemon.nameIndex.GetAll(),
   243  	}, nil
   244  }
   245  
   246  // includeContainerInList decides whether a container should be included in the output or not based in the filter.
   247  // It also decides if the iteration should be stopped or not.
   248  func includeContainerInList(container *container.Container, ctx *listContext) iterationAction {
   249  	// Do not include container if it's in the list before the filter container.
   250  	// Set the filter container to nil to include the rest of containers after this one.
   251  	if ctx.beforeFilter != nil {
   252  		if container.ID == ctx.beforeFilter.ID {
   253  			ctx.beforeFilter = nil
   254  		}
   255  		return excludeContainer
   256  	}
   257  
   258  	// Stop iteration when the container arrives to the filter container
   259  	if ctx.sinceFilter != nil {
   260  		if container.ID == ctx.sinceFilter.ID {
   261  			return stopIteration
   262  		}
   263  	}
   264  
   265  	// Do not include container if it's stopped and we're not filters
   266  	// FIXME remove the ctx.beforContainer and ctx.sinceContainer part of the condition for 1.12 as --since and --before are deprecated
   267  	if !container.Running && !ctx.All && ctx.Limit <= 0 && ctx.beforeContainer == nil && ctx.sinceContainer == nil {
   268  		return excludeContainer
   269  	}
   270  
   271  	// Do not include container if the name doesn't match
   272  	if !ctx.filters.Match("name", container.Name) {
   273  		return excludeContainer
   274  	}
   275  
   276  	// Do not include container if the id doesn't match
   277  	if !ctx.filters.Match("id", container.ID) {
   278  		return excludeContainer
   279  	}
   280  
   281  	// Do not include container if any of the labels don't match
   282  	if !ctx.filters.MatchKVList("label", container.Config.Labels) {
   283  		return excludeContainer
   284  	}
   285  
   286  	// Do not include container if isolation doesn't match
   287  	if excludeContainer == excludeByIsolation(container, ctx) {
   288  		return excludeContainer
   289  	}
   290  
   291  	// FIXME remove this for 1.12 as --since and --before are deprecated
   292  	if ctx.beforeContainer != nil {
   293  		if container.ID == ctx.beforeContainer.ID {
   294  			ctx.beforeContainer = nil
   295  		}
   296  		return excludeContainer
   297  	}
   298  
   299  	// FIXME remove this for 1.12 as --since and --before are deprecated
   300  	if ctx.sinceContainer != nil {
   301  		if container.ID == ctx.sinceContainer.ID {
   302  			return stopIteration
   303  		}
   304  	}
   305  
   306  	// Stop iteration when the index is over the limit
   307  	if ctx.Limit > 0 && ctx.idx == ctx.Limit {
   308  		return stopIteration
   309  	}
   310  
   311  	// Do not include container if its exit code is not in the filter
   312  	if len(ctx.exitAllowed) > 0 {
   313  		shouldSkip := true
   314  		for _, code := range ctx.exitAllowed {
   315  			if code == container.ExitCode && !container.Running {
   316  				shouldSkip = false
   317  				break
   318  			}
   319  		}
   320  		if shouldSkip {
   321  			return excludeContainer
   322  		}
   323  	}
   324  
   325  	// Do not include container if its status doesn't match the filter
   326  	if !ctx.filters.Match("status", container.State.StateString()) {
   327  		return excludeContainer
   328  	}
   329  
   330  	if ctx.filters.Include("volume") {
   331  		volumesByName := make(map[string]*volume.MountPoint)
   332  		for _, m := range container.MountPoints {
   333  			if m.Name != "" {
   334  				volumesByName[m.Name] = m
   335  			} else {
   336  				volumesByName[m.Source] = m
   337  			}
   338  		}
   339  
   340  		volumeExist := fmt.Errorf("volume mounted in container")
   341  		err := ctx.filters.WalkValues("volume", func(value string) error {
   342  			if _, exist := container.MountPoints[value]; exist {
   343  				return volumeExist
   344  			}
   345  			if _, exist := volumesByName[value]; exist {
   346  				return volumeExist
   347  			}
   348  			return nil
   349  		})
   350  		if err != volumeExist {
   351  			return excludeContainer
   352  		}
   353  	}
   354  
   355  	if ctx.ancestorFilter {
   356  		if len(ctx.images) == 0 {
   357  			return excludeContainer
   358  		}
   359  		if !ctx.images[container.ImageID] {
   360  			return excludeContainer
   361  		}
   362  	}
   363  
   364  	return includeContainer
   365  }
   366  
   367  // transformContainer generates the container type expected by the docker ps command.
   368  func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) {
   369  	newC := &types.Container{
   370  		ID:      container.ID,
   371  		Names:   ctx.names[container.ID],
   372  		ImageID: container.ImageID.String(),
   373  	}
   374  	if newC.Names == nil {
   375  		// Dead containers will often have no name, so make sure the response isn't null
   376  		newC.Names = []string{}
   377  	}
   378  
   379  	image := container.Config.Image // if possible keep the original ref
   380  	if image != container.ImageID.String() {
   381  		id, err := daemon.GetImageID(image)
   382  		if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE {
   383  			return nil, err
   384  		}
   385  		if err != nil || id != container.ImageID {
   386  			image = container.ImageID.String()
   387  		}
   388  	}
   389  	newC.Image = image
   390  
   391  	if len(container.Args) > 0 {
   392  		args := []string{}
   393  		for _, arg := range container.Args {
   394  			if strings.Contains(arg, " ") {
   395  				args = append(args, fmt.Sprintf("'%s'", arg))
   396  			} else {
   397  				args = append(args, arg)
   398  			}
   399  		}
   400  		argsAsString := strings.Join(args, " ")
   401  
   402  		newC.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
   403  	} else {
   404  		newC.Command = container.Path
   405  	}
   406  	newC.Created = container.Created.Unix()
   407  	newC.State = container.State.StateString()
   408  	newC.Status = container.State.String()
   409  	newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
   410  	// copy networks to avoid races
   411  	networks := make(map[string]*networktypes.EndpointSettings)
   412  	for name, network := range container.NetworkSettings.Networks {
   413  		if network == nil {
   414  			continue
   415  		}
   416  		networks[name] = &networktypes.EndpointSettings{
   417  			EndpointID:          network.EndpointID,
   418  			Gateway:             network.Gateway,
   419  			IPAddress:           network.IPAddress,
   420  			IPPrefixLen:         network.IPPrefixLen,
   421  			IPv6Gateway:         network.IPv6Gateway,
   422  			GlobalIPv6Address:   network.GlobalIPv6Address,
   423  			GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen,
   424  			MacAddress:          network.MacAddress,
   425  		}
   426  		if network.IPAMConfig != nil {
   427  			networks[name].IPAMConfig = &networktypes.EndpointIPAMConfig{
   428  				IPv4Address: network.IPAMConfig.IPv4Address,
   429  				IPv6Address: network.IPAMConfig.IPv6Address,
   430  			}
   431  		}
   432  	}
   433  	newC.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
   434  
   435  	newC.Ports = []types.Port{}
   436  	for port, bindings := range container.NetworkSettings.Ports {
   437  		p, err := nat.ParsePort(port.Port())
   438  		if err != nil {
   439  			return nil, err
   440  		}
   441  		if len(bindings) == 0 {
   442  			newC.Ports = append(newC.Ports, types.Port{
   443  				PrivatePort: p,
   444  				Type:        port.Proto(),
   445  			})
   446  			continue
   447  		}
   448  		for _, binding := range bindings {
   449  			h, err := nat.ParsePort(binding.HostPort)
   450  			if err != nil {
   451  				return nil, err
   452  			}
   453  			newC.Ports = append(newC.Ports, types.Port{
   454  				PrivatePort: p,
   455  				PublicPort:  h,
   456  				Type:        port.Proto(),
   457  				IP:          binding.HostIP,
   458  			})
   459  		}
   460  	}
   461  
   462  	if ctx.Size {
   463  		sizeRw, sizeRootFs := daemon.getSize(container)
   464  		newC.SizeRw = sizeRw
   465  		newC.SizeRootFs = sizeRootFs
   466  	}
   467  	newC.Labels = container.Config.Labels
   468  	newC.Mounts = addMountPoints(container)
   469  
   470  	return newC, nil
   471  }
   472  
   473  // Volumes lists known volumes, using the filter to restrict the range
   474  // of volumes returned.
   475  func (daemon *Daemon) Volumes(filter string) ([]*types.Volume, []string, error) {
   476  	var (
   477  		volumesOut []*types.Volume
   478  	)
   479  	volFilters, err := filters.FromParam(filter)
   480  	if err != nil {
   481  		return nil, nil, err
   482  	}
   483  
   484  	if err := volFilters.Validate(acceptedVolumeFilterTags); err != nil {
   485  		return nil, nil, err
   486  	}
   487  
   488  	volumes, warnings, err := daemon.volumes.List()
   489  	filterVolumes, err := daemon.filterVolumes(volumes, volFilters)
   490  	if err != nil {
   491  		return nil, nil, err
   492  	}
   493  	for _, v := range filterVolumes {
   494  		volumesOut = append(volumesOut, volumeToAPIType(v))
   495  	}
   496  	return volumesOut, warnings, nil
   497  }
   498  
   499  // filterVolumes filters volume list according to user specified filter
   500  // and returns user chosen volumes
   501  func (daemon *Daemon) filterVolumes(vols []volume.Volume, filter filters.Args) ([]volume.Volume, error) {
   502  	// if filter is empty, return original volume list
   503  	if filter.Len() == 0 {
   504  		return vols, nil
   505  	}
   506  
   507  	var retVols []volume.Volume
   508  	for _, vol := range vols {
   509  		if filter.Include("name") {
   510  			if !filter.Match("name", vol.Name()) {
   511  				continue
   512  			}
   513  		}
   514  		if filter.Include("driver") {
   515  			if !filter.Match("driver", vol.DriverName()) {
   516  				continue
   517  			}
   518  		}
   519  		retVols = append(retVols, vol)
   520  	}
   521  	danglingOnly := false
   522  	if filter.Include("dangling") {
   523  		if filter.ExactMatch("dangling", "true") || filter.ExactMatch("dangling", "1") {
   524  			danglingOnly = true
   525  		} else if !filter.ExactMatch("dangling", "false") && !filter.ExactMatch("dangling", "0") {
   526  			return nil, fmt.Errorf("Invalid filter 'dangling=%s'", filter.Get("dangling"))
   527  		}
   528  		retVols = daemon.volumes.FilterByUsed(retVols, !danglingOnly)
   529  	}
   530  	return retVols, nil
   531  }
   532  
   533  func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) {
   534  	if !ancestorMap[imageID] {
   535  		for _, id := range getChildren(imageID) {
   536  			populateImageFilterByParents(ancestorMap, id, getChildren)
   537  		}
   538  		ancestorMap[imageID] = true
   539  	}
   540  }