2014-07-31 17:26:25 -04:00
|
|
|
package daemon
|
2014-07-29 22:04:39 -04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2014-06-02 15:08:39 -04:00
|
|
|
"strconv"
|
2014-07-29 22:04:39 -04:00
|
|
|
"strings"
|
|
|
|
|
2015-08-20 03:57:15 -04:00
|
|
|
"github.com/Sirupsen/logrus"
|
2015-11-12 14:55:17 -05:00
|
|
|
"github.com/docker/docker/container"
|
2015-08-20 03:57:15 -04:00
|
|
|
"github.com/docker/docker/image"
|
2016-02-03 17:46:01 -05:00
|
|
|
"github.com/docker/docker/volume"
|
2016-01-04 19:05:26 -05:00
|
|
|
"github.com/docker/engine-api/types"
|
|
|
|
"github.com/docker/engine-api/types/filters"
|
2016-01-08 16:41:28 -05:00
|
|
|
networktypes "github.com/docker/engine-api/types/network"
|
2015-12-18 12:58:48 -05:00
|
|
|
"github.com/docker/go-connections/nat"
|
2014-07-29 22:04:39 -04:00
|
|
|
)
|
|
|
|
|
2016-01-04 03:37:01 -05:00
|
|
|
var acceptedVolumeFilterTags = map[string]bool{
|
|
|
|
"dangling": true,
|
2016-03-21 03:39:48 -04:00
|
|
|
"name": true,
|
|
|
|
"driver": true,
|
2016-01-04 03:37:01 -05:00
|
|
|
}
|
|
|
|
|
2016-03-14 23:54:12 -04:00
|
|
|
var acceptedPsFilterTags = map[string]bool{
|
|
|
|
"ancestor": true,
|
|
|
|
"before": true,
|
|
|
|
"exited": true,
|
|
|
|
"id": true,
|
|
|
|
"isolation": true,
|
|
|
|
"label": true,
|
|
|
|
"name": true,
|
|
|
|
"status": true,
|
|
|
|
"since": true,
|
|
|
|
"volume": true,
|
|
|
|
}
|
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
// iterationAction represents possible outcomes happening during the container iteration.
|
|
|
|
type iterationAction int
|
|
|
|
|
|
|
|
// containerReducer represents a reducer for a container.
|
|
|
|
// Returns the object to serialize by the api.
|
2015-11-12 14:55:17 -05:00
|
|
|
type containerReducer func(*container.Container, *listContext) (*types.Container, error)
|
2015-08-26 04:16:53 -04:00
|
|
|
|
|
|
|
const (
|
|
|
|
// includeContainer is the action to include a container in the reducer.
|
|
|
|
includeContainer iterationAction = iota
|
|
|
|
// excludeContainer is the action to exclude a container in the reducer.
|
|
|
|
excludeContainer
|
|
|
|
// stopIteration is the action to stop iterating over the list of containers.
|
|
|
|
stopIteration
|
|
|
|
)
|
|
|
|
|
|
|
|
// errStopIteration makes the iterator to stop without returning an error.
|
|
|
|
var errStopIteration = errors.New("container list iteration stopped")
|
|
|
|
|
2014-07-31 17:26:25 -04:00
|
|
|
// List returns an array of all containers registered in the daemon.
|
2015-11-12 14:55:17 -05:00
|
|
|
func (daemon *Daemon) List() []*container.Container {
|
2014-07-31 17:26:25 -04:00
|
|
|
return daemon.containers.List()
|
|
|
|
}
|
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
// listContext is the daemon generated filtering to iterate over containers.
|
2016-01-27 17:09:42 -05:00
|
|
|
// This is created based on the user specification from types.ContainerListOptions.
|
2015-08-26 04:16:53 -04: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 17:20:54 -05:00
|
|
|
images map[image.ID]bool
|
2015-08-26 04:16:53 -04: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 03:06:29 -05:00
|
|
|
|
|
|
|
// FIXME Remove this for 1.12 as --since and --before are deprecated
|
|
|
|
// beforeContainer is a filter to ignore containers that appear before the one given
|
|
|
|
beforeContainer *container.Container
|
|
|
|
// sinceContainer is a filter to stop the filtering when the iterator arrive to the given container
|
|
|
|
sinceContainer *container.Container
|
|
|
|
|
2015-11-05 02:08:00 -05:00
|
|
|
// beforeFilter is a filter to ignore containers that appear before the one given
|
|
|
|
// this is used for --filter=before= and --before=, the latter is deprecated.
|
2015-11-12 14:55:17 -05:00
|
|
|
beforeFilter *container.Container
|
2015-11-05 02:08:00 -05:00
|
|
|
// sinceFilter is a filter to stop the filtering when the iterator arrive to the given container
|
|
|
|
// this is used for --filter=since= and --since=, the latter is deprecated.
|
2015-11-12 14:55:17 -05:00
|
|
|
sinceFilter *container.Container
|
2016-01-27 17:09:42 -05:00
|
|
|
// ContainerListOptions is the filters set by the user
|
|
|
|
*types.ContainerListOptions
|
2015-08-26 04:16:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Containers returns the list of containers to show given the user's filtering.
|
2016-01-27 17:09:42 -05:00
|
|
|
func (daemon *Daemon) Containers(config *types.ContainerListOptions) ([]*types.Container, error) {
|
2015-09-29 13:51:40 -04:00
|
|
|
return daemon.reduceContainers(config, daemon.transformContainer)
|
2015-08-26 04:16:53 -04:00
|
|
|
}
|
|
|
|
|
2015-12-30 12:20:41 -05:00
|
|
|
// reduceContainers parses the user's filtering options and generates the list of containers to return based on a reducer.
|
2016-01-27 17:09:42 -05:00
|
|
|
func (daemon *Daemon) reduceContainers(config *types.ContainerListOptions, reducer containerReducer) ([]*types.Container, error) {
|
2015-09-18 12:39:14 -04:00
|
|
|
containers := []*types.Container{}
|
2015-08-26 04:16:53 -04:00
|
|
|
|
2015-09-29 13:51:40 -04:00
|
|
|
ctx, err := daemon.foldFilter(config)
|
2015-08-26 04:16:53 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-09-29 13:51:40 -04:00
|
|
|
for _, container := range daemon.List() {
|
|
|
|
t, err := daemon.reducePsContainer(container, ctx, reducer)
|
2015-08-26 04:16:53 -04:00
|
|
|
if err != nil {
|
|
|
|
if err != errStopIteration {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if t != nil {
|
|
|
|
containers = append(containers, t)
|
2015-09-29 13:51:40 -04:00
|
|
|
ctx.idx++
|
2015-08-26 04:16:53 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return containers, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// reducePsContainer is the basic representation for a container as expected by the ps command.
|
2015-11-12 14:55:17 -05:00
|
|
|
func (daemon *Daemon) reducePsContainer(container *container.Container, ctx *listContext, reducer containerReducer) (*types.Container, error) {
|
2015-08-26 04:16:53 -04:00
|
|
|
container.Lock()
|
|
|
|
defer container.Unlock()
|
2014-07-29 22:04:39 -04:00
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
// filter containers to return
|
2015-09-29 13:51:40 -04:00
|
|
|
action := includeContainerInList(container, ctx)
|
2015-08-26 04:16:53 -04:00
|
|
|
switch action {
|
|
|
|
case excludeContainer:
|
|
|
|
return nil, nil
|
|
|
|
case stopIteration:
|
|
|
|
return nil, errStopIteration
|
|
|
|
}
|
|
|
|
|
|
|
|
// transform internal container struct into api structs
|
2015-09-29 13:51:40 -04:00
|
|
|
return reducer(container, ctx)
|
2015-08-26 04:16:53 -04:00
|
|
|
}
|
|
|
|
|
2015-12-30 12:20:41 -05:00
|
|
|
// foldFilter generates the container filter based on the user's filtering options.
|
2016-01-27 17:09:42 -05:00
|
|
|
func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listContext, error) {
|
|
|
|
psFilters := config.Filter
|
2015-08-26 04:16:53 -04:00
|
|
|
|
2016-03-14 23:54:12 -04:00
|
|
|
if err := psFilters.Validate(acceptedPsFilterTags); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
var filtExited []int
|
2016-03-14 23:54:12 -04:00
|
|
|
|
2016-01-27 17:09:42 -05:00
|
|
|
err := psFilters.WalkValues("exited", func(value string) error {
|
2015-11-25 20:27:11 -05:00
|
|
|
code, err := strconv.Atoi(value)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2014-06-02 15:08:39 -04:00
|
|
|
}
|
2015-11-25 20:27:11 -05:00
|
|
|
filtExited = append(filtExited, code)
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2014-06-02 15:08:39 -04:00
|
|
|
}
|
|
|
|
|
2015-11-25 20:27:11 -05:00
|
|
|
err = psFilters.WalkValues("status", func(value string) error {
|
2015-11-12 14:55:17 -05:00
|
|
|
if !container.IsValidStateString(value) {
|
2015-11-25 20:27:11 -05:00
|
|
|
return fmt.Errorf("Unrecognised filter value for status: %s", value)
|
2015-01-08 18:14:04 -05:00
|
|
|
}
|
2015-11-25 20:27:11 -05:00
|
|
|
|
|
|
|
config.All = true
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-01-08 18:14:04 -05:00
|
|
|
}
|
2015-08-20 03:57:15 -04:00
|
|
|
|
2015-11-12 14:55:17 -05:00
|
|
|
var beforeContFilter, sinceContFilter *container.Container
|
2016-02-09 03:06:29 -05:00
|
|
|
// FIXME remove this for 1.12 as --since and --before are deprecated
|
|
|
|
var beforeContainer, sinceContainer *container.Container
|
|
|
|
|
2015-11-25 20:27:11 -05:00
|
|
|
err = psFilters.WalkValues("before", func(value string) error {
|
2015-12-11 12:39:28 -05:00
|
|
|
beforeContFilter, err = daemon.GetContainer(value)
|
2015-11-25 20:27:11 -05:00
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-11-05 02:08:00 -05:00
|
|
|
}
|
|
|
|
|
2015-11-25 20:27:11 -05:00
|
|
|
err = psFilters.WalkValues("since", func(value string) error {
|
2015-12-11 12:39:28 -05:00
|
|
|
sinceContFilter, err = daemon.GetContainer(value)
|
2015-11-25 20:27:11 -05:00
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-11-05 02:08:00 -05:00
|
|
|
}
|
|
|
|
|
2015-11-18 17:20:54 -05:00
|
|
|
imagesFilter := map[image.ID]bool{}
|
2015-08-26 04:16:53 -04:00
|
|
|
var ancestorFilter bool
|
2015-11-25 20:27:11 -05:00
|
|
|
if psFilters.Include("ancestor") {
|
2015-08-20 03:57:15 -04:00
|
|
|
ancestorFilter = true
|
2015-11-25 20:27:11 -05:00
|
|
|
psFilters.WalkValues("ancestor", func(ancestor string) error {
|
2015-11-18 17:20:54 -05:00
|
|
|
id, err := daemon.GetImageID(ancestor)
|
2015-08-20 03:57:15 -04:00
|
|
|
if err != nil {
|
|
|
|
logrus.Warnf("Error while looking up for image %v", ancestor)
|
2015-11-25 20:27:11 -05:00
|
|
|
return nil
|
2015-08-20 03:57:15 -04:00
|
|
|
}
|
2015-11-18 17:20:54 -05:00
|
|
|
if imagesFilter[id] {
|
2015-08-20 03:57:15 -04:00
|
|
|
// Already seen this ancestor, skip it
|
2015-11-25 20:27:11 -05:00
|
|
|
return nil
|
2015-08-20 03:57:15 -04:00
|
|
|
}
|
|
|
|
// Then walk down the graph and put the imageIds in imagesFilter
|
2015-11-18 17:20:54 -05:00
|
|
|
populateImageFilterByParents(imagesFilter, id, daemon.imageStore.Children)
|
2015-11-25 20:27:11 -05:00
|
|
|
return nil
|
|
|
|
})
|
2015-08-20 03:57:15 -04:00
|
|
|
}
|
|
|
|
|
2016-02-09 03:06:29 -05:00
|
|
|
// FIXME remove this for 1.12 as --since and --before are deprecated
|
|
|
|
if config.Before != "" {
|
|
|
|
beforeContainer, err = daemon.GetContainer(config.Before)
|
2014-12-16 18:06:35 -05:00
|
|
|
if err != nil {
|
2015-04-07 15:34:30 -04:00
|
|
|
return nil, err
|
2014-07-29 22:04:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-09 03:06:29 -05:00
|
|
|
// FIXME remove this for 1.12 as --since and --before are deprecated
|
|
|
|
if config.Since != "" {
|
|
|
|
sinceContainer, err = daemon.GetContainer(config.Since)
|
2014-12-16 18:06:35 -05:00
|
|
|
if err != nil {
|
2015-04-07 15:34:30 -04:00
|
|
|
return nil, err
|
2014-07-29 22:04:39 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
return &listContext{
|
2016-01-27 17:09:42 -05:00
|
|
|
filters: psFilters,
|
|
|
|
ancestorFilter: ancestorFilter,
|
|
|
|
images: imagesFilter,
|
|
|
|
exitAllowed: filtExited,
|
2016-02-09 03:06:29 -05:00
|
|
|
beforeContainer: beforeContainer,
|
|
|
|
sinceContainer: sinceContainer,
|
2016-01-27 17:09:42 -05:00
|
|
|
beforeFilter: beforeContFilter,
|
|
|
|
sinceFilter: sinceContFilter,
|
|
|
|
ContainerListOptions: config,
|
|
|
|
names: daemon.nameIndex.GetAll(),
|
2015-08-26 04:16:53 -04:00
|
|
|
}, nil
|
|
|
|
}
|
2014-10-13 02:12:44 -04:00
|
|
|
|
2016-03-17 13:15:32 -04:00
|
|
|
// includeContainerInList decides whether a container should be included in the output or not based in the filter.
|
2015-08-26 04:16:53 -04:00
|
|
|
// It also decides if the iteration should be stopped or not.
|
2015-11-12 14:55:17 -05:00
|
|
|
func includeContainerInList(container *container.Container, ctx *listContext) iterationAction {
|
2016-02-19 01:07:16 -05: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.
|
|
|
|
if ctx.beforeFilter != nil {
|
|
|
|
if container.ID == ctx.beforeFilter.ID {
|
|
|
|
ctx.beforeFilter = nil
|
|
|
|
}
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stop iteration when the container arrives to the filter container
|
|
|
|
if ctx.sinceFilter != nil {
|
|
|
|
if container.ID == ctx.sinceFilter.ID {
|
|
|
|
return stopIteration
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
// Do not include container if it's stopped and we're not filters
|
2016-02-19 01:07:16 -05:00
|
|
|
// FIXME remove the ctx.beforContainer and ctx.sinceContainer part of the condition for 1.12 as --since and --before are deprecated
|
2016-02-09 03:06:29 -05:00
|
|
|
if !container.Running && !ctx.All && ctx.Limit <= 0 && ctx.beforeContainer == nil && ctx.sinceContainer == nil {
|
2015-08-26 04:16:53 -04:00
|
|
|
return excludeContainer
|
|
|
|
}
|
2014-10-13 02:12:44 -04:00
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
// Do not include container if the name doesn't match
|
|
|
|
if !ctx.filters.Match("name", container.Name) {
|
|
|
|
return excludeContainer
|
|
|
|
}
|
2015-01-06 19:04:10 -05:00
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
// Do not include container if the id doesn't match
|
|
|
|
if !ctx.filters.Match("id", container.ID) {
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do not include container if any of the labels don't match
|
|
|
|
if !ctx.filters.MatchKVList("label", container.Config.Labels) {
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
2016-02-03 15:07:00 -05:00
|
|
|
// Do not include container if isolation doesn't match
|
2015-10-19 12:33:43 -04:00
|
|
|
if excludeContainer == excludeByIsolation(container, ctx) {
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
2016-02-09 03:06:29 -05:00
|
|
|
// FIXME remove this for 1.12 as --since and --before are deprecated
|
|
|
|
if ctx.beforeContainer != nil {
|
|
|
|
if container.ID == ctx.beforeContainer.ID {
|
|
|
|
ctx.beforeContainer = nil
|
|
|
|
}
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME remove this for 1.12 as --since and --before are deprecated
|
|
|
|
if ctx.sinceContainer != nil {
|
|
|
|
if container.ID == ctx.sinceContainer.ID {
|
|
|
|
return stopIteration
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-05 02:08:00 -05:00
|
|
|
// Stop iteration when the index is over the limit
|
|
|
|
if ctx.Limit > 0 && ctx.idx == ctx.Limit {
|
|
|
|
return stopIteration
|
|
|
|
}
|
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
// Do not include container if its exit code is not in the filter
|
|
|
|
if len(ctx.exitAllowed) > 0 {
|
|
|
|
shouldSkip := true
|
|
|
|
for _, code := range ctx.exitAllowed {
|
|
|
|
if code == container.ExitCode && !container.Running {
|
|
|
|
shouldSkip = false
|
|
|
|
break
|
2014-07-29 22:04:39 -04:00
|
|
|
}
|
|
|
|
}
|
2015-08-26 04:16:53 -04:00
|
|
|
if shouldSkip {
|
|
|
|
return excludeContainer
|
2014-06-02 15:08:39 -04:00
|
|
|
}
|
2015-08-26 04:16:53 -04:00
|
|
|
}
|
2014-10-13 02:12:44 -04:00
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
// Do not include container if its status doesn't match the filter
|
|
|
|
if !ctx.filters.Match("status", container.State.StateString()) {
|
|
|
|
return excludeContainer
|
|
|
|
}
|
2015-08-20 03:57:15 -04:00
|
|
|
|
2016-02-03 17:46:01 -05:00
|
|
|
if ctx.filters.Include("volume") {
|
|
|
|
volumesByName := make(map[string]*volume.MountPoint)
|
|
|
|
for _, m := range container.MountPoints {
|
2016-03-16 17:10:16 -04:00
|
|
|
if m.Name != "" {
|
|
|
|
volumesByName[m.Name] = m
|
|
|
|
} else {
|
|
|
|
volumesByName[m.Source] = m
|
|
|
|
}
|
2016-02-03 17:46:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
volumeExist := fmt.Errorf("volume mounted in container")
|
|
|
|
err := ctx.filters.WalkValues("volume", func(value string) error {
|
|
|
|
if _, exist := container.MountPoints[value]; exist {
|
|
|
|
return volumeExist
|
|
|
|
}
|
|
|
|
if _, exist := volumesByName[value]; exist {
|
|
|
|
return volumeExist
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != volumeExist {
|
|
|
|
return excludeContainer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
if ctx.ancestorFilter {
|
|
|
|
if len(ctx.images) == 0 {
|
|
|
|
return excludeContainer
|
2015-08-20 03:57:15 -04:00
|
|
|
}
|
2015-08-26 04:16:53 -04:00
|
|
|
if !ctx.images[container.ImageID] {
|
|
|
|
return excludeContainer
|
2015-04-03 20:39:06 -04:00
|
|
|
}
|
2015-08-26 04:16:53 -04:00
|
|
|
}
|
2015-04-30 21:13:18 -04:00
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
return includeContainer
|
|
|
|
}
|
2015-04-30 21:13:18 -04:00
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
// transformContainer generates the container type expected by the docker ps command.
|
2015-11-12 14:55:17 -05:00
|
|
|
func (daemon *Daemon) transformContainer(container *container.Container, ctx *listContext) (*types.Container, error) {
|
2015-08-26 04:16:53 -04:00
|
|
|
newC := &types.Container{
|
2015-10-02 10:48:35 -04:00
|
|
|
ID: container.ID,
|
|
|
|
Names: ctx.names[container.ID],
|
2015-11-18 17:20:54 -05:00
|
|
|
ImageID: container.ImageID.String(),
|
2015-08-26 04:16:53 -04:00
|
|
|
}
|
2015-10-02 16:54:27 -04:00
|
|
|
if newC.Names == nil {
|
2016-03-17 13:15:32 -04:00
|
|
|
// Dead containers will often have no name, so make sure the response isn't null
|
2015-10-02 16:54:27 -04:00
|
|
|
newC.Names = []string{}
|
|
|
|
}
|
2014-07-29 22:04:39 -04:00
|
|
|
|
2015-11-18 17:20:54 -05:00
|
|
|
image := container.Config.Image // if possible keep the original ref
|
|
|
|
if image != container.ImageID.String() {
|
|
|
|
id, err := daemon.GetImageID(image)
|
|
|
|
if _, isDNE := err.(ErrImageDoesNotExist); err != nil && !isDNE {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err != nil || id != container.ImageID {
|
|
|
|
image = container.ImageID.String()
|
|
|
|
}
|
2015-08-26 04:16:53 -04:00
|
|
|
}
|
2015-11-18 17:20:54 -05:00
|
|
|
newC.Image = image
|
2015-04-03 20:39:06 -04:00
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
if len(container.Args) > 0 {
|
|
|
|
args := []string{}
|
|
|
|
for _, arg := range container.Args {
|
|
|
|
if strings.Contains(arg, " ") {
|
|
|
|
args = append(args, fmt.Sprintf("'%s'", arg))
|
|
|
|
} else {
|
|
|
|
args = append(args, arg)
|
2015-04-03 20:39:06 -04:00
|
|
|
}
|
2014-07-29 22:04:39 -04:00
|
|
|
}
|
2015-08-26 04:16:53 -04:00
|
|
|
argsAsString := strings.Join(args, " ")
|
2015-04-03 20:39:06 -04:00
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
newC.Command = fmt.Sprintf("%s %s", container.Path, argsAsString)
|
|
|
|
} else {
|
|
|
|
newC.Command = container.Path
|
2014-07-29 22:04:39 -04:00
|
|
|
}
|
2015-08-26 04:16:53 -04:00
|
|
|
newC.Created = container.Created.Unix()
|
2015-12-29 09:35:37 -05:00
|
|
|
newC.State = container.State.StateString()
|
2015-08-26 04:16:53 -04:00
|
|
|
newC.Status = container.State.String()
|
2015-11-12 14:55:17 -05:00
|
|
|
newC.HostConfig.NetworkMode = string(container.HostConfig.NetworkMode)
|
2016-01-08 16:41:28 -05:00
|
|
|
// copy networks to avoid races
|
|
|
|
networks := make(map[string]*networktypes.EndpointSettings)
|
|
|
|
for name, network := range container.NetworkSettings.Networks {
|
|
|
|
if network == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
networks[name] = &networktypes.EndpointSettings{
|
|
|
|
EndpointID: network.EndpointID,
|
|
|
|
Gateway: network.Gateway,
|
|
|
|
IPAddress: network.IPAddress,
|
|
|
|
IPPrefixLen: network.IPPrefixLen,
|
|
|
|
IPv6Gateway: network.IPv6Gateway,
|
|
|
|
GlobalIPv6Address: network.GlobalIPv6Address,
|
|
|
|
GlobalIPv6PrefixLen: network.GlobalIPv6PrefixLen,
|
|
|
|
MacAddress: network.MacAddress,
|
|
|
|
}
|
|
|
|
if network.IPAMConfig != nil {
|
|
|
|
networks[name].IPAMConfig = &networktypes.EndpointIPAMConfig{
|
|
|
|
IPv4Address: network.IPAMConfig.IPv4Address,
|
|
|
|
IPv6Address: network.IPAMConfig.IPv6Address,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newC.NetworkSettings = &types.SummaryNetworkSettings{Networks: networks}
|
2014-07-29 22:04:39 -04:00
|
|
|
|
2015-08-26 04:16:53 -04:00
|
|
|
newC.Ports = []types.Port{}
|
|
|
|
for port, bindings := range container.NetworkSettings.Ports {
|
|
|
|
p, err := nat.ParsePort(port.Port())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(bindings) == 0 {
|
|
|
|
newC.Ports = append(newC.Ports, types.Port{
|
|
|
|
PrivatePort: p,
|
|
|
|
Type: port.Proto(),
|
|
|
|
})
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, binding := range bindings {
|
|
|
|
h, err := nat.ParsePort(binding.HostPort)
|
|
|
|
if err != nil {
|
2015-04-07 15:34:30 -04:00
|
|
|
return nil, err
|
2014-07-29 22:04:39 -04:00
|
|
|
}
|
2015-08-26 04:16:53 -04:00
|
|
|
newC.Ports = append(newC.Ports, types.Port{
|
|
|
|
PrivatePort: p,
|
|
|
|
PublicPort: h,
|
|
|
|
Type: port.Proto(),
|
|
|
|
IP: binding.HostIP,
|
|
|
|
})
|
2014-07-29 22:04:39 -04:00
|
|
|
}
|
|
|
|
}
|
2015-08-26 04:16:53 -04:00
|
|
|
|
2015-09-29 13:51:40 -04:00
|
|
|
if ctx.Size {
|
2015-11-02 20:06:09 -05:00
|
|
|
sizeRw, sizeRootFs := daemon.getSize(container)
|
2015-08-26 04:16:53 -04:00
|
|
|
newC.SizeRw = sizeRw
|
|
|
|
newC.SizeRootFs = sizeRootFs
|
|
|
|
}
|
|
|
|
newC.Labels = container.Config.Labels
|
2016-02-03 17:46:01 -05:00
|
|
|
newC.Mounts = addMountPoints(container)
|
2015-08-26 04:16:53 -04:00
|
|
|
|
|
|
|
return newC, nil
|
2014-07-29 22:04:39 -04:00
|
|
|
}
|
2015-06-12 09:25:32 -04:00
|
|
|
|
2015-07-30 17:01:53 -04:00
|
|
|
// Volumes lists known volumes, using the filter to restrict the range
|
|
|
|
// of volumes returned.
|
2015-09-23 16:29:14 -04:00
|
|
|
func (daemon *Daemon) Volumes(filter string) ([]*types.Volume, []string, error) {
|
2016-01-04 03:37:01 -05:00
|
|
|
var (
|
2016-03-21 03:39:48 -04:00
|
|
|
volumesOut []*types.Volume
|
2016-01-04 03:37:01 -05:00
|
|
|
)
|
2015-06-12 09:25:32 -04:00
|
|
|
volFilters, err := filters.FromParam(filter)
|
|
|
|
if err != nil {
|
2015-09-23 16:29:14 -04:00
|
|
|
return nil, nil, err
|
2015-06-12 09:25:32 -04:00
|
|
|
}
|
|
|
|
|
2016-01-04 03:37:01 -05:00
|
|
|
if err := volFilters.Validate(acceptedVolumeFilterTags); err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2015-09-23 16:29:14 -04:00
|
|
|
volumes, warnings, err := daemon.volumes.List()
|
2016-03-21 03:39:48 -04:00
|
|
|
filterVolumes, err := daemon.filterVolumes(volumes, volFilters)
|
2015-09-23 16:29:14 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2016-03-21 03:39:48 -04:00
|
|
|
for _, v := range filterVolumes {
|
2015-06-12 09:25:32 -04:00
|
|
|
volumesOut = append(volumesOut, volumeToAPIType(v))
|
|
|
|
}
|
2015-09-23 16:29:14 -04:00
|
|
|
return volumesOut, warnings, nil
|
2015-06-12 09:25:32 -04:00
|
|
|
}
|
2015-08-20 03:57:15 -04:00
|
|
|
|
2016-03-21 03:39:48 -04:00
|
|
|
// filterVolumes filters volume list according to user specified filter
|
|
|
|
// and returns user chosen volumes
|
|
|
|
func (daemon *Daemon) filterVolumes(vols []volume.Volume, filter filters.Args) ([]volume.Volume, error) {
|
|
|
|
// if filter is empty, return original volume list
|
|
|
|
if filter.Len() == 0 {
|
|
|
|
return vols, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var retVols []volume.Volume
|
|
|
|
for _, vol := range vols {
|
|
|
|
if filter.Include("name") {
|
|
|
|
if !filter.Match("name", vol.Name()) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if filter.Include("driver") {
|
|
|
|
if !filter.Match("driver", vol.DriverName()) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
retVols = append(retVols, vol)
|
|
|
|
}
|
|
|
|
danglingOnly := false
|
|
|
|
if filter.Include("dangling") {
|
|
|
|
if filter.ExactMatch("dangling", "true") || filter.ExactMatch("dangling", "1") {
|
|
|
|
danglingOnly = true
|
|
|
|
} else if !filter.ExactMatch("dangling", "false") && !filter.ExactMatch("dangling", "0") {
|
|
|
|
return nil, fmt.Errorf("Invalid filter 'dangling=%s'", filter.Get("dangling"))
|
|
|
|
}
|
|
|
|
retVols = daemon.volumes.FilterByUsed(retVols, !danglingOnly)
|
|
|
|
}
|
|
|
|
return retVols, nil
|
|
|
|
}
|
|
|
|
|
2015-11-18 17:20:54 -05:00
|
|
|
func populateImageFilterByParents(ancestorMap map[image.ID]bool, imageID image.ID, getChildren func(image.ID) []image.ID) {
|
2015-08-20 03:57:15 -04:00
|
|
|
if !ancestorMap[imageID] {
|
2015-11-18 17:20:54 -05:00
|
|
|
for _, id := range getChildren(imageID) {
|
|
|
|
populateImageFilterByParents(ancestorMap, id, getChildren)
|
2015-08-20 03:57:15 -04:00
|
|
|
}
|
|
|
|
ancestorMap[imageID] = true
|
|
|
|
}
|
|
|
|
}
|