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…
	
	Add table
		Add a link
		
	
		Reference in a new issue