Support multiple service IDs on "docker service ps"

This fix tries to address issue raised in 25228 to support
multiple service IDs on `docker service ps`.

Multiple IDs are allowed with `docker service ps ...`, and
related documentation has been updated.

A test has been added to cover the changes.

This fix fixes 25228.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
Yong Tang 2016-07-29 08:20:03 -07:00
parent a8bcef6fdd
commit 70942352d5
3 changed files with 119 additions and 11 deletions

View file

@ -1,7 +1,13 @@
package service
import (
"fmt"
"strings"
"golang.org/x/net/context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/cli"
"github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/command/idresolver"
@ -9,11 +15,10 @@ import (
"github.com/docker/docker/cli/command/task"
"github.com/docker/docker/opts"
"github.com/spf13/cobra"
"golang.org/x/net/context"
)
type psOptions struct {
serviceID string
services []string
quiet bool
noResolve bool
noTrunc bool
@ -24,11 +29,11 @@ func newPsCommand(dockerCli *command.DockerCli) *cobra.Command {
opts := psOptions{filter: opts.NewFilterOpt()}
cmd := &cobra.Command{
Use: "ps [OPTIONS] SERVICE",
Short: "List the tasks of a service",
Args: cli.ExactArgs(1),
Use: "ps [OPTIONS] SERVICE [SERVICE...]",
Short: "List the tasks of one or more services",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.serviceID = args[0]
opts.services = args
return runPS(dockerCli, opts)
},
}
@ -45,13 +50,46 @@ func runPS(dockerCli *command.DockerCli, opts psOptions) error {
client := dockerCli.Client()
ctx := context.Background()
service, _, err := client.ServiceInspectWithRaw(ctx, opts.serviceID)
filter := opts.filter.Value()
serviceIDFilter := filters.NewArgs()
serviceNameFilter := filters.NewArgs()
for _, service := range opts.services {
serviceIDFilter.Add("id", service)
serviceNameFilter.Add("name", service)
}
serviceByIDList, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: serviceIDFilter})
if err != nil {
return err
}
serviceByNameList, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: serviceNameFilter})
if err != nil {
return err
}
filter := opts.filter.Value()
filter.Add("service", service.ID)
for _, service := range opts.services {
serviceCount := 0
// Lookup by ID/Prefix
for _, serviceEntry := range serviceByIDList {
if strings.HasPrefix(serviceEntry.ID, service) {
filter.Add("service", serviceEntry.ID)
serviceCount++
}
}
// Lookup by Name/Prefix
for _, serviceEntry := range serviceByNameList {
if strings.HasPrefix(serviceEntry.Spec.Annotations.Name, service) {
filter.Add("service", serviceEntry.ID)
serviceCount++
}
}
// If nothing has been found, return immediately.
if serviceCount == 0 {
return fmt.Errorf("no such services: %s", service)
}
}
if filter.Include("node") {
nodeFilters := filter.Get("node")
for _, nodeFilter := range nodeFilters {

View file

@ -19,7 +19,7 @@ aliases: ["/engine/reference/commandline/service_tasks/"]
```Markdown
Usage: docker service ps [OPTIONS] SERVICE
List the tasks of a service
List the tasks of one or more services
Options:
-f, --filter filter Filter output based on conditions provided
@ -29,7 +29,7 @@ Options:
-q, --quiet Only display task IDs
```
Lists the tasks that are running as part of the specified service. This command
Lists the tasks that are running as part of the specified services. This command
has to be run targeting a manager node.
## Examples

View file

@ -1555,3 +1555,73 @@ func (s *DockerSwarmSuite) TestSwarmNetworkCreateDup(c *check.C) {
}
}
}
func (s *DockerSwarmSuite) TestSwarmServicePsMultipleServiceIDs(c *check.C) {
d := s.AddDaemon(c, true, true)
name1 := "top1"
out, err := d.Cmd("service", "create", "--name", name1, "--replicas=3", "busybox", "top")
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
id1 := strings.TrimSpace(out)
name2 := "top2"
out, err = d.Cmd("service", "create", "--name", name2, "--replicas=3", "busybox", "top")
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
id2 := strings.TrimSpace(out)
// make sure task has been deployed.
waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 6)
out, err = d.Cmd("service", "ps", name1)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, name1+".1")
c.Assert(out, checker.Contains, name1+".2")
c.Assert(out, checker.Contains, name1+".3")
c.Assert(out, checker.Not(checker.Contains), name2+".1")
c.Assert(out, checker.Not(checker.Contains), name2+".2")
c.Assert(out, checker.Not(checker.Contains), name2+".3")
out, err = d.Cmd("service", "ps", name1, name2)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, name1+".1")
c.Assert(out, checker.Contains, name1+".2")
c.Assert(out, checker.Contains, name1+".3")
c.Assert(out, checker.Contains, name2+".1")
c.Assert(out, checker.Contains, name2+".2")
c.Assert(out, checker.Contains, name2+".3")
// Name Prefix
out, err = d.Cmd("service", "ps", "to")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, name1+".1")
c.Assert(out, checker.Contains, name1+".2")
c.Assert(out, checker.Contains, name1+".3")
c.Assert(out, checker.Contains, name2+".1")
c.Assert(out, checker.Contains, name2+".2")
c.Assert(out, checker.Contains, name2+".3")
// Name Prefix (no hit)
out, err = d.Cmd("service", "ps", "noname")
c.Assert(err, checker.NotNil)
c.Assert(out, checker.Contains, "no such services: noname")
out, err = d.Cmd("service", "ps", id1)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, name1+".1")
c.Assert(out, checker.Contains, name1+".2")
c.Assert(out, checker.Contains, name1+".3")
c.Assert(out, checker.Not(checker.Contains), name2+".1")
c.Assert(out, checker.Not(checker.Contains), name2+".2")
c.Assert(out, checker.Not(checker.Contains), name2+".3")
out, err = d.Cmd("service", "ps", id1, id2)
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, name1+".1")
c.Assert(out, checker.Contains, name1+".2")
c.Assert(out, checker.Contains, name1+".3")
c.Assert(out, checker.Contains, name2+".1")
c.Assert(out, checker.Contains, name2+".2")
c.Assert(out, checker.Contains, name2+".3")
}