mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Add publish
and expose
filter for docker ps --filter
This fix tries to address the enhancement proposal raised in 27178 for filtering based on published or exposed ports of `docker ps --filter`. In this fix, two filter options, `publish` and `expose` have been added to take either `<port>[/<protocol>]` or `<from>-<to>[/<protocol>]` and filtering on containers. An integration test has been added to cover the changes. This fix fixes 27178. Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
parent
865b3b7652
commit
743943f636
3 changed files with 160 additions and 0 deletions
|
@ -38,6 +38,8 @@ var acceptedPsFilterTags = map[string]bool{
|
||||||
"volume": true,
|
"volume": true,
|
||||||
"network": true,
|
"network": true,
|
||||||
"is-task": true,
|
"is-task": true,
|
||||||
|
"publish": true,
|
||||||
|
"expose": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterationAction represents possible outcomes happening during the container iteration.
|
// iterationAction represents possible outcomes happening during the container iteration.
|
||||||
|
@ -89,6 +91,12 @@ type listContext struct {
|
||||||
taskFilter bool
|
taskFilter bool
|
||||||
// isTask tells us if the we should filter container that are a task (true) or not (false)
|
// isTask tells us if the we should filter container that are a task (true) or not (false)
|
||||||
isTask bool
|
isTask bool
|
||||||
|
|
||||||
|
// publish is a list of published ports to filter with
|
||||||
|
publish map[nat.Port]bool
|
||||||
|
// expose is a list of exposed ports to filter with
|
||||||
|
expose map[nat.Port]bool
|
||||||
|
|
||||||
// ContainerListOptions is the filters set by the user
|
// ContainerListOptions is the filters set by the user
|
||||||
*types.ContainerListOptions
|
*types.ContainerListOptions
|
||||||
}
|
}
|
||||||
|
@ -311,6 +319,54 @@ func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listConte
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
publishFilter := map[nat.Port]bool{}
|
||||||
|
err = psFilters.WalkValues("publish", func(value string) error {
|
||||||
|
if strings.Contains(value, ":") {
|
||||||
|
return fmt.Errorf("filter for 'publish' should not contain ':': %v", value)
|
||||||
|
}
|
||||||
|
//support two formats, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
|
||||||
|
proto, port := nat.SplitProtoPort(value)
|
||||||
|
start, end, err := nat.ParsePortRange(port)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while looking up for publish %v: %s", value, err)
|
||||||
|
}
|
||||||
|
for i := start; i <= end; i++ {
|
||||||
|
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while looking up for publish %v: %s", value, err)
|
||||||
|
}
|
||||||
|
publishFilter[p] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
exposeFilter := map[nat.Port]bool{}
|
||||||
|
err = psFilters.WalkValues("expose", func(value string) error {
|
||||||
|
if strings.Contains(value, ":") {
|
||||||
|
return fmt.Errorf("filter for 'expose' should not contain ':': %v", value)
|
||||||
|
}
|
||||||
|
//support two formats, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
|
||||||
|
proto, port := nat.SplitProtoPort(value)
|
||||||
|
start, end, err := nat.ParsePortRange(port)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while looking up for 'expose' %v: %s", value, err)
|
||||||
|
}
|
||||||
|
for i := start; i <= end; i++ {
|
||||||
|
p, err := nat.NewPort(proto, strconv.FormatUint(i, 10))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while looking up for 'expose' %v: %s", value, err)
|
||||||
|
}
|
||||||
|
exposeFilter[p] = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &listContext{
|
return &listContext{
|
||||||
filters: psFilters,
|
filters: psFilters,
|
||||||
ancestorFilter: ancestorFilter,
|
ancestorFilter: ancestorFilter,
|
||||||
|
@ -320,6 +376,8 @@ func (daemon *Daemon) foldFilter(config *types.ContainerListOptions) (*listConte
|
||||||
sinceFilter: sinceContFilter,
|
sinceFilter: sinceContFilter,
|
||||||
taskFilter: taskFilter,
|
taskFilter: taskFilter,
|
||||||
isTask: isTask,
|
isTask: isTask,
|
||||||
|
publish: publishFilter,
|
||||||
|
expose: exposeFilter,
|
||||||
ContainerListOptions: config,
|
ContainerListOptions: config,
|
||||||
names: daemon.nameIndex.GetAll(),
|
names: daemon.nameIndex.GetAll(),
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -459,6 +517,32 @@ func includeContainerInList(container *container.Container, ctx *listContext) it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(ctx.publish) > 0 {
|
||||||
|
shouldSkip := true
|
||||||
|
for port := range ctx.publish {
|
||||||
|
if _, ok := container.HostConfig.PortBindings[port]; ok {
|
||||||
|
shouldSkip = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if shouldSkip {
|
||||||
|
return excludeContainer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ctx.expose) > 0 {
|
||||||
|
shouldSkip := true
|
||||||
|
for port := range ctx.expose {
|
||||||
|
if _, ok := container.Config.ExposedPorts[port]; ok {
|
||||||
|
shouldSkip = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if shouldSkip {
|
||||||
|
return excludeContainer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return includeContainer
|
return includeContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ Options:
|
||||||
- since=(<container-name>|<container-id>)
|
- since=(<container-name>|<container-id>)
|
||||||
- ancestor=(<image-name>[:tag]|<image-id>|<image@digest>)
|
- ancestor=(<image-name>[:tag]|<image-id>|<image@digest>)
|
||||||
containers created from an image or a descendant.
|
containers created from an image or a descendant.
|
||||||
|
- publish=(<port>[/<proto>]|<startport-endport>/[<proto>])
|
||||||
|
- expose=(<port>[/<proto>]|<startport-endport>/[<proto>])
|
||||||
- is-task=(true|false)
|
- is-task=(true|false)
|
||||||
- health=(starting|healthy|unhealthy|none)
|
- health=(starting|healthy|unhealthy|none)
|
||||||
--format string Pretty-print containers using a Go template
|
--format string Pretty-print containers using a Go template
|
||||||
|
@ -83,6 +85,8 @@ The currently supported filters are:
|
||||||
* volume (volume name or mount point) - filters containers that mount volumes.
|
* volume (volume name or mount point) - filters containers that mount volumes.
|
||||||
* network (network id or name) - filters containers connected to the provided network
|
* network (network id or name) - filters containers connected to the provided network
|
||||||
* health (starting|healthy|unhealthy|none) - filters containers based on healthcheck status
|
* health (starting|healthy|unhealthy|none) - filters containers based on healthcheck status
|
||||||
|
* publish=(container's published port) - filters published ports by containers
|
||||||
|
* expose=(container's exposed port) - filters exposed ports by containers
|
||||||
|
|
||||||
#### Label
|
#### Label
|
||||||
|
|
||||||
|
@ -328,6 +332,44 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS
|
||||||
9d4893ed80fe ubuntu "top" 10 minutes ago Up 10 minutes test1
|
9d4893ed80fe ubuntu "top" 10 minutes ago Up 10 minutes test1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Publish and Expose
|
||||||
|
|
||||||
|
The `publish` and `expose` filters show only containers that have published or exposed port with a given port
|
||||||
|
number, port range, and/or protocol. The default protocol is `tcp` when not specified.
|
||||||
|
|
||||||
|
The following filter matches all containers that have published port of 80:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker run -d --publish=80 busybox top
|
||||||
|
$ docker run -d --expose=8080 busybox top
|
||||||
|
|
||||||
|
$ docker ps -a
|
||||||
|
|
||||||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
9833437217a5 busybox "top" 5 seconds ago Up 4 seconds 8080/tcp dreamy_mccarthy
|
||||||
|
fc7e477723b7 busybox "top" 50 seconds ago Up 50 seconds 0.0.0.0:32768->80/tcp admiring_roentgen
|
||||||
|
|
||||||
|
$ docker ps --filter publish=80
|
||||||
|
|
||||||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
fc7e477723b7 busybox "top" About a minute ago Up About a minute 0.0.0.0:32768->80/tcp admiring_roentgen
|
||||||
|
```
|
||||||
|
|
||||||
|
The following filter matches all containers that have exposed TCP port in the range of `8000-8080`:
|
||||||
|
```bash
|
||||||
|
$ docker ps --filter expose=8000-8080/tcp
|
||||||
|
|
||||||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
9833437217a5 busybox "top" 21 seconds ago Up 19 seconds 8080/tcp dreamy_mccarthy
|
||||||
|
```
|
||||||
|
|
||||||
|
The following filter matches all containers that have exposed UDP port `80`:
|
||||||
|
```bash
|
||||||
|
$ docker ps --filter publish=80/udp
|
||||||
|
|
||||||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
```
|
||||||
|
|
||||||
## Formatting
|
## Formatting
|
||||||
|
|
||||||
The formatting option (`--format`) pretty-prints container output using a Go
|
The formatting option (`--format`) pretty-prints container output using a Go
|
||||||
|
|
|
@ -924,3 +924,37 @@ func (s *DockerSuite) TestPsFormatTemplateWithArg(c *check.C) {
|
||||||
out, _ := dockerCmd(c, "ps", "--format", `{{.Names}} {{.Label "some.label"}}`)
|
out, _ := dockerCmd(c, "ps", "--format", `{{.Names}} {{.Label "some.label"}}`)
|
||||||
c.Assert(strings.TrimSpace(out), checker.Equals, "top label.foo-bar")
|
c.Assert(strings.TrimSpace(out), checker.Equals, "top label.foo-bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestPsListContainersFilterPorts(c *check.C) {
|
||||||
|
testRequires(c, DaemonIsLinux)
|
||||||
|
|
||||||
|
out, _ := dockerCmd(c, "run", "-d", "--publish=80", "busybox", "top")
|
||||||
|
id1 := strings.TrimSpace(out)
|
||||||
|
|
||||||
|
out, _ = dockerCmd(c, "run", "-d", "--expose=8080", "busybox", "top")
|
||||||
|
id2 := strings.TrimSpace(out)
|
||||||
|
|
||||||
|
out, _ = dockerCmd(c, "ps", "--no-trunc", "-q")
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Contains, id1)
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Contains, id2)
|
||||||
|
|
||||||
|
out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "publish=80-8080/udp")
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id1)
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id2)
|
||||||
|
|
||||||
|
out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "expose=8081")
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id1)
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id2)
|
||||||
|
|
||||||
|
out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "publish=80-81")
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Equals, id1)
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id2)
|
||||||
|
|
||||||
|
out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "expose=80/tcp")
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Equals, id1)
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id2)
|
||||||
|
|
||||||
|
out, _ = dockerCmd(c, "ps", "--no-trunc", "-q", "--filter", "expose=8080/tcp")
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), id1)
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Equals, id2)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue