Support multiple service IDs on "docker service ps"
| ... | ... |
@@ -1,7 +1,13 @@ |
| 1 | 1 |
package service |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 5 |
+ "strings" |
|
| 6 |
+ |
|
| 7 |
+ "golang.org/x/net/context" |
|
| 8 |
+ |
|
| 4 | 9 |
"github.com/docker/docker/api/types" |
| 10 |
+ "github.com/docker/docker/api/types/filters" |
|
| 5 | 11 |
"github.com/docker/docker/cli" |
| 6 | 12 |
"github.com/docker/docker/cli/command" |
| 7 | 13 |
"github.com/docker/docker/cli/command/idresolver" |
| ... | ... |
@@ -9,11 +15,10 @@ import ( |
| 9 | 9 |
"github.com/docker/docker/cli/command/task" |
| 10 | 10 |
"github.com/docker/docker/opts" |
| 11 | 11 |
"github.com/spf13/cobra" |
| 12 |
- "golang.org/x/net/context" |
|
| 13 | 12 |
) |
| 14 | 13 |
|
| 15 | 14 |
type psOptions struct {
|
| 16 |
- serviceID string |
|
| 15 |
+ services []string |
|
| 17 | 16 |
quiet bool |
| 18 | 17 |
noResolve bool |
| 19 | 18 |
noTrunc bool |
| ... | ... |
@@ -24,11 +29,11 @@ func newPsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 24 | 24 |
opts := psOptions{filter: opts.NewFilterOpt()}
|
| 25 | 25 |
|
| 26 | 26 |
cmd := &cobra.Command{
|
| 27 |
- Use: "ps [OPTIONS] SERVICE", |
|
| 28 |
- Short: "List the tasks of a service", |
|
| 29 |
- Args: cli.ExactArgs(1), |
|
| 27 |
+ Use: "ps [OPTIONS] SERVICE [SERVICE...]", |
|
| 28 |
+ Short: "List the tasks of one or more services", |
|
| 29 |
+ Args: cli.RequiresMinArgs(1), |
|
| 30 | 30 |
RunE: func(cmd *cobra.Command, args []string) error {
|
| 31 |
- opts.serviceID = args[0] |
|
| 31 |
+ opts.services = args |
|
| 32 | 32 |
return runPS(dockerCli, opts) |
| 33 | 33 |
}, |
| 34 | 34 |
} |
| ... | ... |
@@ -45,13 +50,46 @@ func runPS(dockerCli *command.DockerCli, opts psOptions) error {
|
| 45 | 45 |
client := dockerCli.Client() |
| 46 | 46 |
ctx := context.Background() |
| 47 | 47 |
|
| 48 |
- service, _, err := client.ServiceInspectWithRaw(ctx, opts.serviceID) |
|
| 48 |
+ filter := opts.filter.Value() |
|
| 49 |
+ |
|
| 50 |
+ serviceIDFilter := filters.NewArgs() |
|
| 51 |
+ serviceNameFilter := filters.NewArgs() |
|
| 52 |
+ for _, service := range opts.services {
|
|
| 53 |
+ serviceIDFilter.Add("id", service)
|
|
| 54 |
+ serviceNameFilter.Add("name", service)
|
|
| 55 |
+ } |
|
| 56 |
+ serviceByIDList, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: serviceIDFilter})
|
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ return err |
|
| 59 |
+ } |
|
| 60 |
+ serviceByNameList, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: serviceNameFilter})
|
|
| 49 | 61 |
if err != nil {
|
| 50 | 62 |
return err |
| 51 | 63 |
} |
| 52 | 64 |
|
| 53 |
- filter := opts.filter.Value() |
|
| 54 |
- filter.Add("service", service.ID)
|
|
| 65 |
+ for _, service := range opts.services {
|
|
| 66 |
+ serviceCount := 0 |
|
| 67 |
+ // Lookup by ID/Prefix |
|
| 68 |
+ for _, serviceEntry := range serviceByIDList {
|
|
| 69 |
+ if strings.HasPrefix(serviceEntry.ID, service) {
|
|
| 70 |
+ filter.Add("service", serviceEntry.ID)
|
|
| 71 |
+ serviceCount++ |
|
| 72 |
+ } |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ // Lookup by Name/Prefix |
|
| 76 |
+ for _, serviceEntry := range serviceByNameList {
|
|
| 77 |
+ if strings.HasPrefix(serviceEntry.Spec.Annotations.Name, service) {
|
|
| 78 |
+ filter.Add("service", serviceEntry.ID)
|
|
| 79 |
+ serviceCount++ |
|
| 80 |
+ } |
|
| 81 |
+ } |
|
| 82 |
+ // If nothing has been found, return immediately. |
|
| 83 |
+ if serviceCount == 0 {
|
|
| 84 |
+ return fmt.Errorf("no such services: %s", service)
|
|
| 85 |
+ } |
|
| 86 |
+ } |
|
| 87 |
+ |
|
| 55 | 88 |
if filter.Include("node") {
|
| 56 | 89 |
nodeFilters := filter.Get("node")
|
| 57 | 90 |
for _, nodeFilter := range nodeFilters {
|
| ... | ... |
@@ -19,7 +19,7 @@ aliases: ["/engine/reference/commandline/service_tasks/"] |
| 19 | 19 |
```Markdown |
| 20 | 20 |
Usage: docker service ps [OPTIONS] SERVICE |
| 21 | 21 |
|
| 22 |
-List the tasks of a service |
|
| 22 |
+List the tasks of one or more services |
|
| 23 | 23 |
|
| 24 | 24 |
Options: |
| 25 | 25 |
-f, --filter filter Filter output based on conditions provided |
| ... | ... |
@@ -29,7 +29,7 @@ Options: |
| 29 | 29 |
-q, --quiet Only display task IDs |
| 30 | 30 |
``` |
| 31 | 31 |
|
| 32 |
-Lists the tasks that are running as part of the specified service. This command |
|
| 32 |
+Lists the tasks that are running as part of the specified services. This command |
|
| 33 | 33 |
has to be run targeting a manager node. |
| 34 | 34 |
|
| 35 | 35 |
## Examples |
| ... | ... |
@@ -1555,3 +1555,73 @@ func (s *DockerSwarmSuite) TestSwarmNetworkCreateDup(c *check.C) {
|
| 1555 | 1555 |
} |
| 1556 | 1556 |
} |
| 1557 | 1557 |
} |
| 1558 |
+ |
|
| 1559 |
+func (s *DockerSwarmSuite) TestSwarmServicePsMultipleServiceIDs(c *check.C) {
|
|
| 1560 |
+ d := s.AddDaemon(c, true, true) |
|
| 1561 |
+ |
|
| 1562 |
+ name1 := "top1" |
|
| 1563 |
+ out, err := d.Cmd("service", "create", "--name", name1, "--replicas=3", "busybox", "top")
|
|
| 1564 |
+ c.Assert(err, checker.IsNil) |
|
| 1565 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") |
|
| 1566 |
+ id1 := strings.TrimSpace(out) |
|
| 1567 |
+ |
|
| 1568 |
+ name2 := "top2" |
|
| 1569 |
+ out, err = d.Cmd("service", "create", "--name", name2, "--replicas=3", "busybox", "top")
|
|
| 1570 |
+ c.Assert(err, checker.IsNil) |
|
| 1571 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") |
|
| 1572 |
+ id2 := strings.TrimSpace(out) |
|
| 1573 |
+ |
|
| 1574 |
+ // make sure task has been deployed. |
|
| 1575 |
+ waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 6) |
|
| 1576 |
+ |
|
| 1577 |
+ out, err = d.Cmd("service", "ps", name1)
|
|
| 1578 |
+ c.Assert(err, checker.IsNil) |
|
| 1579 |
+ c.Assert(out, checker.Contains, name1+".1") |
|
| 1580 |
+ c.Assert(out, checker.Contains, name1+".2") |
|
| 1581 |
+ c.Assert(out, checker.Contains, name1+".3") |
|
| 1582 |
+ c.Assert(out, checker.Not(checker.Contains), name2+".1") |
|
| 1583 |
+ c.Assert(out, checker.Not(checker.Contains), name2+".2") |
|
| 1584 |
+ c.Assert(out, checker.Not(checker.Contains), name2+".3") |
|
| 1585 |
+ |
|
| 1586 |
+ out, err = d.Cmd("service", "ps", name1, name2)
|
|
| 1587 |
+ c.Assert(err, checker.IsNil) |
|
| 1588 |
+ c.Assert(out, checker.Contains, name1+".1") |
|
| 1589 |
+ c.Assert(out, checker.Contains, name1+".2") |
|
| 1590 |
+ c.Assert(out, checker.Contains, name1+".3") |
|
| 1591 |
+ c.Assert(out, checker.Contains, name2+".1") |
|
| 1592 |
+ c.Assert(out, checker.Contains, name2+".2") |
|
| 1593 |
+ c.Assert(out, checker.Contains, name2+".3") |
|
| 1594 |
+ |
|
| 1595 |
+ // Name Prefix |
|
| 1596 |
+ out, err = d.Cmd("service", "ps", "to")
|
|
| 1597 |
+ c.Assert(err, checker.IsNil) |
|
| 1598 |
+ c.Assert(out, checker.Contains, name1+".1") |
|
| 1599 |
+ c.Assert(out, checker.Contains, name1+".2") |
|
| 1600 |
+ c.Assert(out, checker.Contains, name1+".3") |
|
| 1601 |
+ c.Assert(out, checker.Contains, name2+".1") |
|
| 1602 |
+ c.Assert(out, checker.Contains, name2+".2") |
|
| 1603 |
+ c.Assert(out, checker.Contains, name2+".3") |
|
| 1604 |
+ |
|
| 1605 |
+ // Name Prefix (no hit) |
|
| 1606 |
+ out, err = d.Cmd("service", "ps", "noname")
|
|
| 1607 |
+ c.Assert(err, checker.NotNil) |
|
| 1608 |
+ c.Assert(out, checker.Contains, "no such services: noname") |
|
| 1609 |
+ |
|
| 1610 |
+ out, err = d.Cmd("service", "ps", id1)
|
|
| 1611 |
+ c.Assert(err, checker.IsNil) |
|
| 1612 |
+ c.Assert(out, checker.Contains, name1+".1") |
|
| 1613 |
+ c.Assert(out, checker.Contains, name1+".2") |
|
| 1614 |
+ c.Assert(out, checker.Contains, name1+".3") |
|
| 1615 |
+ c.Assert(out, checker.Not(checker.Contains), name2+".1") |
|
| 1616 |
+ c.Assert(out, checker.Not(checker.Contains), name2+".2") |
|
| 1617 |
+ c.Assert(out, checker.Not(checker.Contains), name2+".3") |
|
| 1618 |
+ |
|
| 1619 |
+ out, err = d.Cmd("service", "ps", id1, id2)
|
|
| 1620 |
+ c.Assert(err, checker.IsNil) |
|
| 1621 |
+ c.Assert(out, checker.Contains, name1+".1") |
|
| 1622 |
+ c.Assert(out, checker.Contains, name1+".2") |
|
| 1623 |
+ c.Assert(out, checker.Contains, name1+".3") |
|
| 1624 |
+ c.Assert(out, checker.Contains, name2+".1") |
|
| 1625 |
+ c.Assert(out, checker.Contains, name2+".2") |
|
| 1626 |
+ c.Assert(out, checker.Contains, name2+".3") |
|
| 1627 |
+} |