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