Browse code

Merge pull request #25234 from yongtang/25228-service-ps-multiple-ID

Support multiple service IDs on "docker service ps"

Vincent Demeester authored on 2017/01/03 20:40:24
Showing 3 changed files
... ...
@@ -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
+}