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:
parent
a8bcef6fdd
commit
70942352d5
3 changed files with 119 additions and 11 deletions
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue