mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/cli"
|
"github.com/docker/docker/cli"
|
||||||
"github.com/docker/docker/cli/command"
|
"github.com/docker/docker/cli/command"
|
||||||
"github.com/docker/docker/cli/command/idresolver"
|
"github.com/docker/docker/cli/command/idresolver"
|
||||||
|
@ -9,11 +15,10 @@ import (
|
||||||
"github.com/docker/docker/cli/command/task"
|
"github.com/docker/docker/cli/command/task"
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type psOptions struct {
|
type psOptions struct {
|
||||||
serviceID string
|
services []string
|
||||||
quiet bool
|
quiet bool
|
||||||
noResolve bool
|
noResolve bool
|
||||||
noTrunc bool
|
noTrunc bool
|
||||||
|
@ -24,11 +29,11 @@ func newPsCommand(dockerCli *command.DockerCli) *cobra.Command {
|
||||||
opts := psOptions{filter: opts.NewFilterOpt()}
|
opts := psOptions{filter: opts.NewFilterOpt()}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "ps [OPTIONS] SERVICE",
|
Use: "ps [OPTIONS] SERVICE [SERVICE...]",
|
||||||
Short: "List the tasks of a service",
|
Short: "List the tasks of one or more services",
|
||||||
Args: cli.ExactArgs(1),
|
Args: cli.RequiresMinArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
opts.serviceID = args[0]
|
opts.services = args
|
||||||
return runPS(dockerCli, opts)
|
return runPS(dockerCli, opts)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -45,13 +50,46 @@ func runPS(dockerCli *command.DockerCli, opts psOptions) error {
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
ctx := context.Background()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
filter := opts.filter.Value()
|
for _, service := range opts.services {
|
||||||
filter.Add("service", service.ID)
|
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") {
|
if filter.Include("node") {
|
||||||
nodeFilters := filter.Get("node")
|
nodeFilters := filter.Get("node")
|
||||||
for _, nodeFilter := range nodeFilters {
|
for _, nodeFilter := range nodeFilters {
|
||||||
|
|
|
@ -19,7 +19,7 @@ aliases: ["/engine/reference/commandline/service_tasks/"]
|
||||||
```Markdown
|
```Markdown
|
||||||
Usage: docker service ps [OPTIONS] SERVICE
|
Usage: docker service ps [OPTIONS] SERVICE
|
||||||
|
|
||||||
List the tasks of a service
|
List the tasks of one or more services
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-f, --filter filter Filter output based on conditions provided
|
-f, --filter filter Filter output based on conditions provided
|
||||||
|
@ -29,7 +29,7 @@ Options:
|
||||||
-q, --quiet Only display task IDs
|
-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.
|
has to be run targeting a manager node.
|
||||||
|
|
||||||
## Examples
|
## 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