Skip to content

Commit 43a1bd5

Browse files
committed
Support --filter mode=global|replicated for docker service ls
This fix tries to address the request in 31325 by adding `--filter mode=global|replicated` to `docker service ls`. As `docker service ls` has a `MODE` column by default, it is natural to support `--filter mode=global|replicated` for `docker service ls`. There are multiple ways to address the issue. One way is to pass the filter of mode to SwarmKit, another way is to process the filter of mode in the daemon. This fix process the filter in the daemon. Related docs has been updated. An integration test has been added. This fix fixes 31325. Signed-off-by: Yong Tang <[email protected]>
1 parent 297786f commit 43a1bd5

6 files changed

Lines changed: 77 additions & 18 deletions

File tree

api/swagger.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7398,6 +7398,7 @@ paths:
73987398
73997399
- `id=<service id>`
74007400
- `label=<service label>`
7401+
- `mode=["replicated"|"global"]`
74017402
- `name=<service name>`
74027403
tags: ["Service"]
74037404
/services/create:

daemon/cluster/filters.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,6 @@ func newListNodesFilters(filter filters.Args) (*swarmapi.ListNodesRequest_Filter
4545
return f, nil
4646
}
4747

48-
func newListServicesFilters(filter filters.Args) (*swarmapi.ListServicesRequest_Filters, error) {
49-
accepted := map[string]bool{
50-
"name": true,
51-
"id": true,
52-
"label": true,
53-
}
54-
if err := filter.Validate(accepted); err != nil {
55-
return nil, err
56-
}
57-
return &swarmapi.ListServicesRequest_Filters{
58-
NamePrefixes: filter.Get("name"),
59-
IDPrefixes: filter.Get("id"),
60-
Labels: runconfigopts.ConvertKVStringsToMap(filter.Get("label")),
61-
}, nil
62-
}
63-
6448
func newListTasksFilters(filter filters.Args, transformFunc func(filters.Args) error) (*swarmapi.ListTasksRequest_Filters, error) {
6549
accepted := map[string]bool{
6650
"name": true,

daemon/cluster/services.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/docker/docker/daemon/logger"
2222
"github.com/docker/docker/pkg/ioutils"
2323
"github.com/docker/docker/pkg/stdcopy"
24+
runconfigopts "github.com/docker/docker/runconfig/opts"
2425
swarmapi "github.com/docker/swarmkit/api"
2526
gogotypes "github.com/gogo/protobuf/types"
2627
"github.com/pkg/errors"
@@ -37,10 +38,25 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
3738
return nil, c.errNoManager(state)
3839
}
3940

40-
filters, err := newListServicesFilters(options.Filters)
41-
if err != nil {
41+
// We move the accepted filter check here as "mode" filter
42+
// is processed in the daemon, not in SwarmKit. So it might
43+
// be good to have accepted file check in the same file as
44+
// the filter processing (in the for loop below).
45+
accepted := map[string]bool{
46+
"name": true,
47+
"id": true,
48+
"label": true,
49+
"mode": true,
50+
}
51+
if err := options.Filters.Validate(accepted); err != nil {
4252
return nil, err
4353
}
54+
filters := &swarmapi.ListServicesRequest_Filters{
55+
NamePrefixes: options.Filters.Get("name"),
56+
IDPrefixes: options.Filters.Get("id"),
57+
Labels: runconfigopts.ConvertKVStringsToMap(options.Filters.Get("label")),
58+
}
59+
4460
ctx, cancel := c.getRequestContext()
4561
defer cancel()
4662

@@ -54,6 +70,19 @@ func (c *Cluster) GetServices(options apitypes.ServiceListOptions) ([]types.Serv
5470
services := []types.Service{}
5571

5672
for _, service := range r.Services {
73+
if options.Filters.Include("mode") {
74+
var mode string
75+
switch service.Spec.GetMode().(type) {
76+
case *swarmapi.ServiceSpec_Global:
77+
mode = "global"
78+
case *swarmapi.ServiceSpec_Replicated:
79+
mode = "replicated"
80+
}
81+
82+
if !options.Filters.ExactMatch("mode", mode) {
83+
continue
84+
}
85+
}
5786
services = append(services, convert.ServiceFromGRPC(*service))
5887
}
5988

docs/api/version-history.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ keywords: "API, Docker, rcli, REST, documentation"
2727
* `POST /build` now accepts `extrahosts` parameter to specify a host to ip mapping to use during the build.
2828
* `POST /services/create` and `POST /services/(id or name)/update` now accept a `rollback` value for `FailureAction`.
2929
* `POST /services/create` and `POST /services/(id or name)/update` now accept an optional `RollbackConfig` object which specifies rollback options.
30+
* `GET /services` now supports a `mode` filter to filter services based on the service mode (either `global` or `replicated`).
3031

3132
## v1.27 API changes
3233

docs/reference/commandline/service_ls.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ The currently supported filters are:
6060

6161
* [id](service_ls.md#id)
6262
* [label](service_ls.md#label)
63+
* [mode](service_ls.md#mode)
6364
* [name](service_ls.md#name)
6465

6566
#### id
@@ -98,6 +99,18 @@ ID NAME MODE REPLICAS IMAGE
9899
74nzcxxjv6fq backend replicated 3/3 redis:3.0.6
99100
```
100101

102+
#### mode
103+
104+
The `mode` filter matches on the mode (either `replicated` or `global`) of a service.
105+
106+
The following filter matches only `global` services.
107+
108+
```bash
109+
$ docker service ls --filter mode=global
110+
ID NAME MODE REPLICAS IMAGE
111+
w7y0v2yrn620 top global 1/1 busybox
112+
```
113+
101114
#### name
102115

103116
The `name` filter matches on all or part of a service's name.

integration-cli/docker_cli_swarm_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,3 +1797,34 @@ func (s *DockerSwarmSuite) TestSwarmStopSignal(c *check.C) {
17971797
c.Assert(err, checker.IsNil, check.Commentf(out))
17981798
c.Assert(strings.TrimSpace(out), checker.Equals, "SIGUSR1")
17991799
}
1800+
1801+
func (s *DockerSwarmSuite) TestSwarmServiceLsFilterMode(c *check.C) {
1802+
d := s.AddDaemon(c, true, true)
1803+
1804+
out, err := d.Cmd("service", "create", "--name", "top1", "busybox", "top")
1805+
c.Assert(err, checker.IsNil, check.Commentf(out))
1806+
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
1807+
1808+
out, err = d.Cmd("service", "create", "--name", "top2", "--mode=global", "busybox", "top")
1809+
c.Assert(err, checker.IsNil, check.Commentf(out))
1810+
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
1811+
1812+
// make sure task has been deployed.
1813+
waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 2)
1814+
1815+
out, err = d.Cmd("service", "ls")
1816+
c.Assert(err, checker.IsNil, check.Commentf(out))
1817+
c.Assert(out, checker.Contains, "top1")
1818+
c.Assert(out, checker.Contains, "top2")
1819+
c.Assert(out, checker.Not(checker.Contains), "localnet")
1820+
1821+
out, err = d.Cmd("service", "ls", "--filter", "mode=global")
1822+
c.Assert(out, checker.Not(checker.Contains), "top1")
1823+
c.Assert(out, checker.Contains, "top2")
1824+
c.Assert(err, checker.IsNil, check.Commentf(out))
1825+
1826+
out, err = d.Cmd("service", "ls", "--filter", "mode=replicated")
1827+
c.Assert(err, checker.IsNil, check.Commentf(out))
1828+
c.Assert(out, checker.Contains, "top1")
1829+
c.Assert(out, checker.Not(checker.Contains), "top2")
1830+
}

0 commit comments

Comments
 (0)