Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions cli/command/stack/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import (
"fmt"
"strings"
"unicode"

"github.com/docker/cli/cli/compose/convert"
"github.com/docker/cli/opts"
"github.com/moby/moby/api/types/filters"
)

// validateStackName checks if the provided string is a valid stack name (namespace).
Expand All @@ -29,3 +33,9 @@ func validateStackNames(namespaces []string) error {
func quotesOrWhitespace(r rune) bool {
return unicode.IsSpace(r) || r == '"' || r == '\''
}

func getStackFilterFromOpt(namespace string, opt opts.FilterOpt) filters.Args {
filter := opt.Value()
filter.Add("label", convert.LabelNamespace+"="+namespace)
return filter
}
21 changes: 8 additions & 13 deletions cli/command/stack/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/stack/formatter"
"github.com/docker/cli/cli/command/stack/options"
"github.com/docker/cli/cli/command/stack/swarm"
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/fvbommel/sortorder"
"github.com/spf13/cobra"
)

type listOptions = options.List
// listOptions holds docker stack ls options
type listOptions struct {
format string
}

func newListCommand(dockerCli command.Cli) *cobra.Command {
func newListCommand(dockerCLI command.Cli) *cobra.Command {
opts := listOptions{}

cmd := &cobra.Command{
Expand All @@ -27,23 +29,16 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
Short: "List stacks",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runList(cmd.Context(), dockerCli, opts)
return runList(cmd.Context(), dockerCLI, opts)
},
ValidArgsFunction: completion.NoComplete,
}

flags := cmd.Flags()
flags.StringVar(&opts.Format, "format", "", flagsHelper.FormatHelp)
flags.StringVar(&opts.format, "format", "", flagsHelper.FormatHelp)
return cmd
}

// RunList performs a stack list against the specified swarm cluster
//
// Deprecated: this function was for internal use and will be removed in the next release.
func RunList(ctx context.Context, dockerCLI command.Cli, opts options.List) error {
return runList(ctx, dockerCLI, opts)
}

// runList performs a stack list against the specified swarm cluster
func runList(ctx context.Context, dockerCLI command.Cli, opts listOptions) error {
stacks, err := swarm.GetStacks(ctx, dockerCLI.Client())
Expand All @@ -54,7 +49,7 @@ func runList(ctx context.Context, dockerCLI command.Cli, opts listOptions) error
}

func format(out io.Writer, opts listOptions, stacks []formatter.Stack) error {
fmt := formatter.Format(opts.Format)
fmt := formatter.Format(opts.format)
if fmt == "" || fmt == formatter.TableFormatKey {
fmt = formatter.SwarmStackTableFormat
}
Expand Down
32 changes: 0 additions & 32 deletions cli/command/stack/options/opts.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package options

import "github.com/docker/cli/opts"

// Deploy holds docker stack deploy options
//
// Deprecated: this type was for internal use and will be removed in the next release.
Expand All @@ -23,40 +21,10 @@ type Config struct {
SkipInterpolation bool
}

// List holds docker stack ls options
//
// Deprecated: this type was for internal use and will be removed in the next release.
type List struct {
Format string
AllNamespaces bool
}

// PS holds docker stack ps options
//
// Deprecated: this type was for internal use and will be removed in the next release.
type PS struct {
Filter opts.FilterOpt
NoTrunc bool
Namespace string
NoResolve bool
Quiet bool
Format string
}

// Remove holds docker stack remove options
//
// Deprecated: this type was for internal use and will be removed in the next release.
type Remove struct {
Namespaces []string
Detach bool
}

// Services holds docker stack services options
//
// Deprecated: this type was for internal use and will be removed in the next release.
type Services struct {
Quiet bool
Format string
Filter opts.FilterOpt
Namespace string
}
61 changes: 48 additions & 13 deletions cli/command/stack/ps.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,73 @@
package stack

import (
"context"
"fmt"

"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/stack/options"
"github.com/docker/cli/cli/command/stack/swarm"
"github.com/docker/cli/cli/command/idresolver"
"github.com/docker/cli/cli/command/task"
flagsHelper "github.com/docker/cli/cli/flags"
cliopts "github.com/docker/cli/opts"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)

func newPsCommand(dockerCli command.Cli) *cobra.Command {
opts := options.PS{Filter: cliopts.NewFilterOpt()}
// psOptions holds docker stack ps options
type psOptions struct {
filter cliopts.FilterOpt
noTrunc bool
namespace string
noResolve bool
quiet bool
format string
}

func newPsCommand(dockerCLI command.Cli) *cobra.Command {
opts := psOptions{filter: cliopts.NewFilterOpt()}

cmd := &cobra.Command{
Use: "ps [OPTIONS] STACK",
Short: "List the tasks in the stack",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.Namespace = args[0]
if err := validateStackName(opts.Namespace); err != nil {
opts.namespace = args[0]
if err := validateStackName(opts.namespace); err != nil {
return err
}
return swarm.RunPS(cmd.Context(), dockerCli, opts)
return runPS(cmd.Context(), dockerCLI, opts)
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return completeNames(dockerCli)(cmd, args, toComplete)
return completeNames(dockerCLI)(cmd, args, toComplete)
},
}
flags := cmd.Flags()
flags.BoolVar(&opts.NoTrunc, "no-trunc", false, "Do not truncate output")
flags.BoolVar(&opts.NoResolve, "no-resolve", false, "Do not map IDs to Names")
flags.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided")
flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display task IDs")
flags.StringVar(&opts.Format, "format", "", flagsHelper.FormatHelp)
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate output")
flags.BoolVar(&opts.noResolve, "no-resolve", false, "Do not map IDs to Names")
flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display task IDs")
flags.StringVar(&opts.format, "format", "", flagsHelper.FormatHelp)
return cmd
}

// runPS is the swarm implementation of docker stack ps
func runPS(ctx context.Context, dockerCLI command.Cli, opts psOptions) error {
apiClient := dockerCLI.Client()
tasks, err := apiClient.TaskList(ctx, client.TaskListOptions{
Filters: getStackFilterFromOpt(opts.namespace, opts.filter),
})
if err != nil {
return err
}

if len(tasks) == 0 {
return fmt.Errorf("nothing found in stack: %s", opts.namespace)
}

if opts.format == "" {
opts.format = task.DefaultFormat(dockerCLI.ConfigFile(), opts.quiet)
}

return task.Print(ctx, dockerCLI, tasks, idresolver.New(apiClient, opts.noResolve), !opts.noTrunc, opts.quiet, opts.format)
}
48 changes: 22 additions & 26 deletions cli/command/stack/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,33 @@ import (

"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/cli/cli/command/service"
"github.com/docker/cli/cli/command/stack/formatter"
"github.com/docker/cli/cli/command/stack/options"
"github.com/docker/cli/cli/command/stack/swarm"
flagsHelper "github.com/docker/cli/cli/flags"
cliopts "github.com/docker/cli/opts"
"github.com/fvbommel/sortorder"
swarmtypes "github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/api/types/swarm"
"github.com/spf13/cobra"
)

// servicesOptions holds docker stack services options
type servicesOptions = options.Services
// serviceListOptions holds docker stack services options
type serviceListOptions = struct {
quiet bool
format string
filter cliopts.FilterOpt
namespace string
}

func newServicesCommand(dockerCLI command.Cli) *cobra.Command {
opts := servicesOptions{Filter: cliopts.NewFilterOpt()}
opts := serviceListOptions{filter: cliopts.NewFilterOpt()}

cmd := &cobra.Command{
Use: "services [OPTIONS] STACK",
Short: "List the services in the stack",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.Namespace = args[0]
if err := validateStackName(opts.Namespace); err != nil {
opts.namespace = args[0]
if err := validateStackName(opts.namespace); err != nil {
return err
}
return runServices(cmd.Context(), dockerCLI, opts)
Expand All @@ -40,41 +43,34 @@ func newServicesCommand(dockerCLI command.Cli) *cobra.Command {
},
}
flags := cmd.Flags()
flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
flags.StringVar(&opts.Format, "format", "", flagsHelper.FormatHelp)
flags.VarP(&opts.Filter, "filter", "f", "Filter output based on conditions provided")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only display IDs")
flags.StringVar(&opts.format, "format", "", flagsHelper.FormatHelp)
flags.VarP(&opts.filter, "filter", "f", "Filter output based on conditions provided")
return cmd
}

// RunServices performs a stack services against the specified swarm cluster
//
// Deprecated: this function was for internal use and will be removed in the next release.
func RunServices(ctx context.Context, dockerCLI command.Cli, opts options.Services) error {
return runServices(ctx, dockerCLI, opts)
}

// runServices performs a stack services against the specified swarm cluster
func runServices(ctx context.Context, dockerCLI command.Cli, opts servicesOptions) error {
services, err := swarm.GetServices(ctx, dockerCLI, opts)
func runServices(ctx context.Context, dockerCLI command.Cli, opts serviceListOptions) error {
services, err := getServices(ctx, dockerCLI.Client(), opts)
if err != nil {
return err
}
return formatWrite(dockerCLI, services, opts)
}

func formatWrite(dockerCLI command.Cli, services []swarmtypes.Service, opts servicesOptions) error {
func formatWrite(dockerCLI command.Cli, services []swarm.Service, opts serviceListOptions) error {
// if no services in the stack, print message and exit 0
if len(services) == 0 {
_, _ = fmt.Fprintln(dockerCLI.Err(), "Nothing found in stack:", opts.Namespace)
_, _ = fmt.Fprintln(dockerCLI.Err(), "Nothing found in stack:", opts.namespace)
return nil
}
sort.Slice(services, func(i, j int) bool {
return sortorder.NaturalLess(services[i].Spec.Name, services[j].Spec.Name)
})

f := opts.Format
f := opts.format
if len(f) == 0 {
if len(dockerCLI.ConfigFile().ServicesFormat) > 0 && !opts.Quiet {
if len(dockerCLI.ConfigFile().ServicesFormat) > 0 && !opts.quiet {
f = dockerCLI.ConfigFile().ServicesFormat
} else {
f = formatter.TableFormatKey
Expand All @@ -83,7 +79,7 @@ func formatWrite(dockerCLI command.Cli, services []swarmtypes.Service, opts serv

servicesCtx := formatter.Context{
Output: dockerCLI.Out(),
Format: service.NewListFormat(f, opts.Quiet),
Format: service.NewListFormat(f, opts.quiet),
}
return service.ListFormatWrite(servicesCtx, services)
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
package swarm
package stack

import (
"context"

"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/service"
"github.com/docker/cli/cli/command/stack/options"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
)

// GetServices is the swarm implementation of listing stack services
//
// Deprecated: this function was for internal use and will be removed in the next release.
func GetServices(ctx context.Context, dockerCLI command.Cli, opts options.Services) ([]swarm.Service, error) {
var (
err error
apiClient = dockerCLI.Client()
)

// getServices is the swarm implementation of listing stack services
func getServices(ctx context.Context, apiClient client.APIClient, opts serviceListOptions) ([]swarm.Service, error) {
listOpts := client.ServiceListOptions{
Filters: getStackFilterFromOpt(opts.Namespace, opts.Filter),
Filters: getStackFilterFromOpt(opts.namespace, opts.filter),
// When not running "quiet", also get service status (number of running
// and desired tasks). Note that this is only supported on API v1.41 and
// up; older API versions ignore this option, and we will have to collect
// the information manually below.
Status: !opts.Quiet,
Status: !opts.quiet,
}

services, err := apiClient.ServiceList(ctx, listOpts)
Expand Down
7 changes: 0 additions & 7 deletions cli/command/stack/swarm/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"

"github.com/docker/cli/cli/compose/convert"
"github.com/docker/cli/opts"
"github.com/moby/moby/api/types/filters"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/api/types/swarm"
Expand All @@ -17,12 +16,6 @@ func getStackFilter(namespace string) filters.Args {
return filter
}

func getStackFilterFromOpt(namespace string, opt opts.FilterOpt) filters.Args {
filter := opt.Value()
filter.Add("label", convert.LabelNamespace+"="+namespace)
return filter
}

func getAllStacksFilter() filters.Args {
filter := filters.NewArgs()
filter.Add("label", convert.LabelNamespace)
Expand Down
Loading
Loading