- Allow to filter containers by volume with `--filter volume=name` and `filter volume=/dest`.
- Show their names in the list with the custom format `{{ .Mounts }}`.
Signed-off-by: David Calavera <david.calavera@gmail.com>
| ... | ... |
@@ -31,6 +31,7 @@ const ( |
| 31 | 31 |
repositoryHeader = "REPOSITORY" |
| 32 | 32 |
tagHeader = "TAG" |
| 33 | 33 |
digestHeader = "DIGEST" |
| 34 |
+ mountsHeader = "MOUNTS" |
|
| 34 | 35 |
) |
| 35 | 36 |
|
| 36 | 37 |
type containerContext struct {
|
| ... | ... |
@@ -142,6 +143,20 @@ func (c *containerContext) Label(name string) string {
|
| 142 | 142 |
return c.c.Labels[name] |
| 143 | 143 |
} |
| 144 | 144 |
|
| 145 |
+func (c *containerContext) Mounts() string {
|
|
| 146 |
+ c.addHeader(mountsHeader) |
|
| 147 |
+ |
|
| 148 |
+ var mounts []string |
|
| 149 |
+ for _, m := range c.c.Mounts {
|
|
| 150 |
+ name := m.Name |
|
| 151 |
+ if c.trunc {
|
|
| 152 |
+ name = stringutils.Truncate(name, 15) |
|
| 153 |
+ } |
|
| 154 |
+ mounts = append(mounts, name) |
|
| 155 |
+ } |
|
| 156 |
+ return strings.Join(mounts, ",") |
|
| 157 |
+} |
|
| 158 |
+ |
|
| 145 | 159 |
type imageContext struct {
|
| 146 | 160 |
baseSubContext |
| 147 | 161 |
trunc bool |
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"github.com/Sirupsen/logrus" |
| 10 | 10 |
"github.com/docker/docker/container" |
| 11 | 11 |
"github.com/docker/docker/image" |
| 12 |
+ "github.com/docker/docker/volume" |
|
| 12 | 13 |
"github.com/docker/engine-api/types" |
| 13 | 14 |
"github.com/docker/engine-api/types/filters" |
| 14 | 15 |
networktypes "github.com/docker/engine-api/types/network" |
| ... | ... |
@@ -306,6 +307,27 @@ func includeContainerInList(container *container.Container, ctx *listContext) it |
| 306 | 306 |
return excludeContainer |
| 307 | 307 |
} |
| 308 | 308 |
|
| 309 |
+ if ctx.filters.Include("volume") {
|
|
| 310 |
+ volumesByName := make(map[string]*volume.MountPoint) |
|
| 311 |
+ for _, m := range container.MountPoints {
|
|
| 312 |
+ volumesByName[m.Name] = m |
|
| 313 |
+ } |
|
| 314 |
+ |
|
| 315 |
+ volumeExist := fmt.Errorf("volume mounted in container")
|
|
| 316 |
+ err := ctx.filters.WalkValues("volume", func(value string) error {
|
|
| 317 |
+ if _, exist := container.MountPoints[value]; exist {
|
|
| 318 |
+ return volumeExist |
|
| 319 |
+ } |
|
| 320 |
+ if _, exist := volumesByName[value]; exist {
|
|
| 321 |
+ return volumeExist |
|
| 322 |
+ } |
|
| 323 |
+ return nil |
|
| 324 |
+ }) |
|
| 325 |
+ if err != volumeExist {
|
|
| 326 |
+ return excludeContainer |
|
| 327 |
+ } |
|
| 328 |
+ } |
|
| 329 |
+ |
|
| 309 | 330 |
if ctx.ancestorFilter {
|
| 310 | 331 |
if len(ctx.images) == 0 {
|
| 311 | 332 |
return excludeContainer |
| ... | ... |
@@ -419,6 +441,7 @@ func (daemon *Daemon) transformContainer(container *container.Container, ctx *li |
| 419 | 419 |
newC.SizeRootFs = sizeRootFs |
| 420 | 420 |
} |
| 421 | 421 |
newC.Labels = container.Config.Labels |
| 422 |
+ newC.Mounts = addMountPoints(container) |
|
| 422 | 423 |
|
| 423 | 424 |
return newC, nil |
| 424 | 425 |
} |
| ... | ... |
@@ -116,6 +116,7 @@ This section lists each version from latest to oldest. Each listing includes a |
| 116 | 116 |
[Docker Remote API v1.23](docker_remote_api_v1.23.md) documentation |
| 117 | 117 |
|
| 118 | 118 |
* `GET /containers/json` returns the state of the container, one of `created`, `restarting`, `running`, `paused`, `exited` or `dead`. |
| 119 |
+* `GET /containers/json` returns the mount points for the container. |
|
| 119 | 120 |
* `GET /networks/(name)` now returns an `Internal` field showing whether the network is internal or not. |
| 120 | 121 |
|
| 121 | 122 |
|
| ... | ... |
@@ -73,7 +73,18 @@ List containers |
| 73 | 73 |
"MacAddress": "02:42:ac:11:00:02" |
| 74 | 74 |
} |
| 75 | 75 |
} |
| 76 |
- } |
|
| 76 |
+ }, |
|
| 77 |
+ "Mounts": [ |
|
| 78 |
+ {
|
|
| 79 |
+ "Name": "fac362...80535", |
|
| 80 |
+ "Source": "/data", |
|
| 81 |
+ "Destination": "/data", |
|
| 82 |
+ "Driver": "local", |
|
| 83 |
+ "Mode": "ro,Z", |
|
| 84 |
+ "RW": false, |
|
| 85 |
+ "Propagation": "" |
|
| 86 |
+ } |
|
| 87 |
+ ] |
|
| 77 | 88 |
}, |
| 78 | 89 |
{
|
| 79 | 90 |
"Id": "9cd87474be90", |
| ... | ... |
@@ -102,8 +113,8 @@ List containers |
| 102 | 102 |
"MacAddress": "02:42:ac:11:00:08" |
| 103 | 103 |
} |
| 104 | 104 |
} |
| 105 |
- } |
|
| 106 |
- |
|
| 105 |
+ }, |
|
| 106 |
+ "Mounts": [] |
|
| 107 | 107 |
}, |
| 108 | 108 |
{
|
| 109 | 109 |
"Id": "3176a2479c92", |
| ... | ... |
@@ -132,8 +143,8 @@ List containers |
| 132 | 132 |
"MacAddress": "02:42:ac:11:00:06" |
| 133 | 133 |
} |
| 134 | 134 |
} |
| 135 |
- } |
|
| 136 |
- |
|
| 135 |
+ }, |
|
| 136 |
+ "Mounts": [] |
|
| 137 | 137 |
}, |
| 138 | 138 |
{
|
| 139 | 139 |
"Id": "4cb07b47f9fb", |
| ... | ... |
@@ -162,8 +173,8 @@ List containers |
| 162 | 162 |
"MacAddress": "02:42:ac:11:00:05" |
| 163 | 163 |
} |
| 164 | 164 |
} |
| 165 |
- } |
|
| 166 |
- |
|
| 165 |
+ }, |
|
| 166 |
+ "Mounts": [] |
|
| 167 | 167 |
} |
| 168 | 168 |
] |
| 169 | 169 |
|
| ... | ... |
@@ -184,6 +195,10 @@ Query Parameters: |
| 184 | 184 |
- `status=`(`created`|`restarting`|`running`|`paused`|`exited`|`dead`) |
| 185 | 185 |
- `label=key` or `label="key=value"` of a container label |
| 186 | 186 |
- `isolation=`(`default`|`process`|`hyperv`) (Windows daemon only) |
| 187 |
+ - `ancestor`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`) |
|
| 188 |
+ - `before`=(`<container id>` or `<container name>`) |
|
| 189 |
+ - `since`=(`<container id>` or `<container name>`) |
|
| 190 |
+ - `volume`=(`<volume name>` or `<mount point destination>`) |
|
| 187 | 191 |
|
| 188 | 192 |
Status Codes: |
| 189 | 193 |
|
| ... | ... |
@@ -60,6 +60,7 @@ The currently supported filters are: |
| 60 | 60 |
* before (container's id or name) - filters containers created before given id or name |
| 61 | 61 |
* since (container's id or name) - filters containers created since given id or name |
| 62 | 62 |
* isolation (default|process|hyperv) (Windows daemon only) |
| 63 |
+* volume (volume name or mount point) - filters containers that mount volumes. |
|
| 63 | 64 |
|
| 64 | 65 |
|
| 65 | 66 |
#### Label |
| ... | ... |
@@ -193,6 +194,18 @@ with the same containers as in `before` filter: |
| 193 | 193 |
9c3527ed70ce busybox "top" 10 minutes ago Up 10 minutes desperate_dubinsky |
| 194 | 194 |
4aace5031105 busybox "top" 10 minutes ago Up 10 minutes focused_hamilton |
| 195 | 195 |
|
| 196 |
+#### Volume |
|
| 197 |
+ |
|
| 198 |
+The `volume` filter shows only containers that mount a specific volume or have a volume mounted in a specific path: |
|
| 199 |
+ |
|
| 200 |
+ $ docker ps --filter volume=remote-volume --format "table {{.ID}}\t{{.Mounts}}"
|
|
| 201 |
+ CONTAINER ID MOUNTS |
|
| 202 |
+ 9c3527ed70ce remote-volume |
|
| 203 |
+ |
|
| 204 |
+ $ docker ps --filter volume=/data --format "table {{.ID}}\t{{.Mounts}}"
|
|
| 205 |
+ CONTAINER ID MOUNTS |
|
| 206 |
+ 9c3527ed70ce remote-volume |
|
| 207 |
+ |
|
| 196 | 208 |
|
| 197 | 209 |
## Formatting |
| 198 | 210 |
|
| ... | ... |
@@ -213,6 +226,7 @@ Placeholder | Description |
| 213 | 213 |
`.Names` | Container names. |
| 214 | 214 |
`.Labels` | All labels assigned to the container. |
| 215 | 215 |
`.Label` | Value of a specific label for this container. For example `{{.Label "com.docker.swarm.cpu"}}`
|
| 216 |
+`.Mounts` | Names of the volumes mounted in this container. |
|
| 216 | 217 |
|
| 217 | 218 |
When using the `--format` option, the `ps` command will either output the data exactly as the template |
| 218 | 219 |
declares or, when using the `table` directive, will include column headers as well. |
| ... | ... |
@@ -734,3 +734,56 @@ func (s *DockerSuite) TestPsNotShowPortsOfStoppedContainer(c *check.C) {
|
| 734 | 734 |
fields = strings.Fields(lines[1]) |
| 735 | 735 |
c.Assert(fields[len(fields)-2], checker.Not(checker.Equals), expected, check.Commentf("Should not got %v", expected))
|
| 736 | 736 |
} |
| 737 |
+ |
|
| 738 |
+func (s *DockerSuite) TestPsShowMounts(c *check.C) {
|
|
| 739 |
+ prefix, slash := getPrefixAndSlashFromDaemonPlatform() |
|
| 740 |
+ |
|
| 741 |
+ mp := prefix + slash + "test" |
|
| 742 |
+ |
|
| 743 |
+ dockerCmd(c, "volume", "create", "--name", "ps-volume-test") |
|
| 744 |
+ runSleepingContainer(c, "--name=volume-test-1", "--volume", "ps-volume-test:"+mp) |
|
| 745 |
+ c.Assert(waitRun("volume-test-1"), checker.IsNil)
|
|
| 746 |
+ runSleepingContainer(c, "--name=volume-test-2", "--volume", mp) |
|
| 747 |
+ c.Assert(waitRun("volume-test-2"), checker.IsNil)
|
|
| 748 |
+ |
|
| 749 |
+ out, _ := dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}")
|
|
| 750 |
+ |
|
| 751 |
+ lines := strings.Split(strings.TrimSpace(string(out)), "\n") |
|
| 752 |
+ c.Assert(lines, checker.HasLen, 2) |
|
| 753 |
+ |
|
| 754 |
+ fields := strings.Fields(lines[0]) |
|
| 755 |
+ c.Assert(fields, checker.HasLen, 2) |
|
| 756 |
+ |
|
| 757 |
+ annonymounsVolumeID := fields[1] |
|
| 758 |
+ |
|
| 759 |
+ fields = strings.Fields(lines[1]) |
|
| 760 |
+ c.Assert(fields[1], checker.Equals, "ps-volume-test") |
|
| 761 |
+ |
|
| 762 |
+ // filter by volume name |
|
| 763 |
+ out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume=ps-volume-test")
|
|
| 764 |
+ |
|
| 765 |
+ lines = strings.Split(strings.TrimSpace(string(out)), "\n") |
|
| 766 |
+ c.Assert(lines, checker.HasLen, 1) |
|
| 767 |
+ |
|
| 768 |
+ fields = strings.Fields(lines[0]) |
|
| 769 |
+ c.Assert(fields[1], checker.Equals, "ps-volume-test") |
|
| 770 |
+ |
|
| 771 |
+ // empty results filtering by unknown volume |
|
| 772 |
+ out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume=this-volume-should-not-exist")
|
|
| 773 |
+ c.Assert(strings.TrimSpace(string(out)), checker.HasLen, 0) |
|
| 774 |
+ |
|
| 775 |
+ // filter by mount destination |
|
| 776 |
+ out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+mp)
|
|
| 777 |
+ |
|
| 778 |
+ lines = strings.Split(strings.TrimSpace(string(out)), "\n") |
|
| 779 |
+ c.Assert(lines, checker.HasLen, 2) |
|
| 780 |
+ |
|
| 781 |
+ fields = strings.Fields(lines[0]) |
|
| 782 |
+ c.Assert(fields[1], checker.Equals, annonymounsVolumeID) |
|
| 783 |
+ fields = strings.Fields(lines[1]) |
|
| 784 |
+ c.Assert(fields[1], checker.Equals, "ps-volume-test") |
|
| 785 |
+ |
|
| 786 |
+ // empty results filtering by unknown mount point |
|
| 787 |
+ out, _ = dockerCmd(c, "ps", "--format", "{{.Names}} {{.Mounts}}", "--filter", "volume="+prefix+slash+"this-path-was-never-mounted")
|
|
| 788 |
+ c.Assert(strings.TrimSpace(string(out)), checker.HasLen, 0) |
|
| 789 |
+} |
| ... | ... |
@@ -35,6 +35,7 @@ the running containers. |
| 35 | 35 |
- before=(<container-name>|<container-id>) |
| 36 | 36 |
- since=(<container-name>|<container-id>) |
| 37 | 37 |
- ancestor=(<image-name>[:tag]|<image-id>|<image@digest>) - containers created from an image or a descendant. |
| 38 |
+ - volume=(<volume-name>|<mount-point-destination>) |
|
| 38 | 39 |
|
| 39 | 40 |
**--format**="*TEMPLATE*" |
| 40 | 41 |
Pretty-print containers using a Go template. |
| ... | ... |
@@ -50,6 +51,7 @@ the running containers. |
| 50 | 50 |
.Names - Container names. |
| 51 | 51 |
.Labels - All labels assigned to the container. |
| 52 | 52 |
.Label - Value of a specific label for this container. For example `{{.Label "com.docker.swarm.cpu"}}`
|
| 53 |
+ .Mounts - Names of the volumes mounted in this container. |
|
| 53 | 54 |
|
| 54 | 55 |
**--help** |
| 55 | 56 |
Print usage statement |
| ... | ... |
@@ -118,6 +120,18 @@ the running containers. |
| 118 | 118 |
c1d3b0166030 debian |
| 119 | 119 |
41d50ecd2f57 fedora |
| 120 | 120 |
|
| 121 |
+# Display containers with `remote-volume` mounted |
|
| 122 |
+ |
|
| 123 |
+ $ docker ps --filter volume=remote-volume --format "table {{.ID}}\t{{.Mounts}}"
|
|
| 124 |
+ CONTAINER ID MOUNTS |
|
| 125 |
+ 9c3527ed70ce remote-volume |
|
| 126 |
+ |
|
| 127 |
+# Display containers with a volume mounted in `/data` |
|
| 128 |
+ |
|
| 129 |
+ $ docker ps --filter volume=/data --format "table {{.ID}}\t{{.Mounts}}"
|
|
| 130 |
+ CONTAINER ID MOUNTS |
|
| 131 |
+ 9c3527ed70ce remote-volume |
|
| 132 |
+ |
|
| 121 | 133 |
# HISTORY |
| 122 | 134 |
April 2014, Originally compiled by William Henry (whenry at redhat dot com) |
| 123 | 135 |
based on docker.com source material and internal work. |