Skip to content

Commit 994afbc

Browse files
committed
Add .IPAddresses as formatting option on docker ps
This allows showing the IP address for each network that the container is attached to, for example: docker network create foo docker run -d --name foo nginx:alpine docker network connect foo foo container container ls --format 'table {{.ID}}\\t{{join .IPAddresses ", "}}' CONTAINER ID IP ADDRESSES 17e7d1910fc0 bridge:172.17.0.2, foo:172.19.0.2 container container ls --format='{{json .IPAddresses}}' | jq . [ "bridge:172.17.0.2", "foo:172.19.0.2" ] Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent 98f1464 commit 994afbc

3 files changed

Lines changed: 64 additions & 1 deletion

File tree

cli/command/formatter/container.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const (
2929
networksHeader = "NETWORKS"
3030
platformHeader = "PLATFORM"
3131
healthStatusHeader = "HEALTH STATUS"
32+
ipAddressesHeader = "IP ADDRESSES"
3233
)
3334

3435
// Platform wraps a [ocispec.Platform] to implement the stringer interface.
@@ -123,6 +124,7 @@ func NewContainerContext() *ContainerContext {
123124
"Networks": networksHeader,
124125
"Platform": platformHeader,
125126
"HealthStatus": healthStatusHeader,
127+
"IPAddresses": ipAddressesHeader,
126128
}
127129
return &containerCtx
128130
}
@@ -383,6 +385,25 @@ func (c *ContainerContext) HealthStatus() string {
383385
}
384386
}
385387

388+
// IPAddresses returns the list of IP-addresses assigned to the container
389+
// IP-addresses are prefixed with the name of the network, separated with a colon.
390+
// For example: "bridge:192.168.1.10"
391+
func (c *ContainerContext) IPAddresses() []string {
392+
ipAddresses := []string{}
393+
if c.c.NetworkSettings == nil {
394+
return ipAddresses
395+
}
396+
for name, nw := range c.c.NetworkSettings.Networks {
397+
if nw.IPAddress.IsValid() {
398+
ipAddresses = append(ipAddresses, name+":"+nw.IPAddress.String())
399+
}
400+
if nw.GlobalIPv6Address.IsValid() {
401+
ipAddresses = append(ipAddresses, name+":"+nw.GlobalIPv6Address.String())
402+
}
403+
}
404+
return ipAddresses
405+
}
406+
386407
// DisplayablePorts returns formatted string representing open ports of container
387408
// e.g. "0.0.0.0:80->9090/tcp, 9988/tcp"
388409
// it's used by command 'docker ps'

cli/command/formatter/container_test.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/docker/cli/internal/test"
1616
"github.com/moby/moby/api/types/container"
17+
"github.com/moby/moby/api/types/network"
1718
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
1819
"gotest.tools/v3/assert"
1920
is "gotest.tools/v3/assert/cmp"
@@ -390,7 +391,7 @@ size: 0B
390391
}
391392

392393
containers := []container.Summary{
393-
{ID: "containerID1", Names: []string{"/foobar_baz"}, Image: "ubuntu", Created: unixTime, State: container.StateRunning},
394+
{ID: "containerID1", Names: []string{"/foobar_baz"}, Image: "ubuntu", Created: unixTime, State: container.StateRunning, NetworkSettings: &container.NetworkSettingsSummary{}},
394395
{ID: "containerID2", Names: []string{"/foobar_bar"}, Image: "ubuntu", Created: unixTime, State: container.StateRunning},
395396
}
396397

@@ -579,6 +580,36 @@ func TestContainerContextWriteJSONField(t *testing.T) {
579580
}
580581
}
581582

583+
func TestContainerContextIPAddresses(t *testing.T) {
584+
containers := []container.Summary{
585+
{
586+
ID: "containerID1",
587+
NetworkSettings: &container.NetworkSettingsSummary{
588+
Networks: map[string]*network.EndpointSettings{
589+
"one": {IPAddress: netip.MustParseAddr("192.168.1.2")},
590+
"two": {IPAddress: netip.MustParseAddr("192.168.178.2")},
591+
},
592+
},
593+
},
594+
{
595+
ID: "containerID2",
596+
NetworkSettings: &container.NetworkSettingsSummary{
597+
Networks: map[string]*network.EndpointSettings{
598+
"one": {IPAddress: netip.MustParseAddr("192.168.1.3")},
599+
"two": {IPAddress: netip.MustParseAddr("192.168.178.3")},
600+
},
601+
},
602+
},
603+
}
604+
605+
out := bytes.NewBufferString("")
606+
err := ContainerWrite(Context{Format: "{{.IPAddresses}}", Output: out}, containers)
607+
assert.NilError(t, err)
608+
assert.Equal(t, out.String(), `[one:192.168.1.2 two:192.168.178.2]
609+
[one:192.168.1.3 two:192.168.178.3]
610+
`)
611+
}
612+
582613
func TestContainerBackCompat(t *testing.T) {
583614
createdAtTime := time.Now().AddDate(-1, 0, 0) // 1 year ago
584615

docs/reference/commandline/container_ls.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ Valid placeholders for the Go template are listed below:
412412
| `.Label` | Value of a specific label for this container. For example `'{{.Label "com.docker.swarm.cpu"}}'` |
413413
| `.Mounts` | Names of the volumes mounted in this container. |
414414
| `.Networks` | Names of the networks attached to this container. |
415+
| `.IPAddresses` | List of IP-Addresses for each network that the container is attached to. |
415416

416417
When using the `--format` option, the `ps` command will either output the data
417418
exactly as the template declares or, when using the `table` directive, includes
@@ -447,3 +448,13 @@ To list all running containers in JSON format, use the `json` directive:
447448
$ docker ps --format json
448449
{"Command":"\"/docker-entrypoint.…\"","CreatedAt":"2021-03-10 00:15:05 +0100 CET","ID":"a762a2b37a1d","Image":"nginx","Labels":"maintainer=NGINX Docker Maintainers \u003cdocker-maint@nginx.com\u003e","LocalVolumes":"0","Mounts":"","Names":"boring_keldysh","Networks":"bridge","Ports":"80/tcp","RunningFor":"4 seconds ago","Size":"0B","State":"running","Status":"Up 3 seconds"}
449450
```
451+
452+
Show the IP-addresses that containers have:
453+
454+
```console
455+
$ docker ps --format "table {{.ID}}\\t{{join .IPAddresses \", \"}}"
456+
457+
CONTAINER ID IP ADDRESSES
458+
c0cf2877da71 bridge:172.17.0.3
459+
17e7d1910fc0 bridge:172.17.0.2, mynetwork:172.19.0.2
460+
```

0 commit comments

Comments
 (0)