Skip to content

Commit

Permalink
Refactor the stack services command to be uniform
Browse files Browse the repository at this point in the history
Running `docker stack services <STACK> --orchestrator swarm would yield
the message "Noting found in stack: asdf" with an exit code 0. The same
command with kubernetes orchestrator would yield "nothing found in
stack: adsf" (note the lower-case "nothing") and a non-zero exit code.
This change makes the `stack services` command uniform for both
orchestrators. The logic of getting and printing services is split to
reuse the same formatting code.

Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
  • Loading branch information
rumpl committed Oct 16, 2019
1 parent b14a42a commit ab837de
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 53 deletions.
34 changes: 10 additions & 24 deletions cli/command/stack/kubernetes/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"strings"

"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/compose-on-kubernetes/api/labels"
"github.com/docker/docker/api/types/filters"
Expand Down Expand Up @@ -79,60 +78,47 @@ func getResourcesForServiceList(dockerCli *KubeCli, filters filters.Args, labelS
return replicas, daemons, services, nil
}

// RunServices is the kubernetes implementation of docker stack services
func RunServices(dockerCli *KubeCli, opts options.Services) error {
// GetServices is the kubernetes implementation of listing stack services
func GetServices(dockerCli *KubeCli, opts options.Services) ([]swarm.Service, map[string]service.ListInfo, error) {
filters := opts.Filter.Value()
if err := filters.Validate(supportedServicesFilters); err != nil {
return err
return nil, nil, err
}
client, err := dockerCli.composeClient()
if err != nil {
return nil
return nil, nil, err
}
stacks, err := client.Stacks(false)
if err != nil {
return nil
return nil, nil, err
}
stackName := opts.Namespace
_, err = stacks.Get(stackName)
if apierrs.IsNotFound(err) {
return fmt.Errorf("nothing found in stack: %s", stackName)
return []swarm.Service{}, nil, nil
}
if err != nil {
return err
return nil, nil, err
}

labelSelector := generateLabelSelector(filters, stackName)
replicasList, daemonsList, servicesList, err := getResourcesForServiceList(dockerCli, filters, labelSelector)
if err != nil {
return err
return nil, nil, err
}

// Convert Replicas sets and kubernetes services to swarm services and formatter information
services, info, err := convertToServices(replicasList, daemonsList, servicesList)
if err != nil {
return err
return nil, nil, err
}
services = filterServicesByName(services, filters.Get("name"), stackName)

if opts.Quiet {
info = map[string]service.ListInfo{}
}

format := opts.Format
if len(format) == 0 {
if len(dockerCli.ConfigFile().ServicesFormat) > 0 && !opts.Quiet {
format = dockerCli.ConfigFile().ServicesFormat
} else {
format = formatter.TableFormatKey
}
}

servicesCtx := formatter.Context{
Output: dockerCli.Out(),
Format: service.NewListFormat(format, opts.Quiet),
}
return service.ListFormatWrite(servicesCtx, services, info)
return services, info, nil
}

func filterServicesByName(services []swarm.Service, names []string, stackName string) []swarm.Service {
Expand Down
52 changes: 49 additions & 3 deletions cli/command/stack/services.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package stack

import (
"fmt"

"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/service"
"github.com/docker/cli/cli/command/stack/formatter"
"github.com/docker/cli/cli/command/stack/kubernetes"
"github.com/docker/cli/cli/command/stack/options"
"github.com/docker/cli/cli/command/stack/swarm"
cliopts "github.com/docker/cli/opts"
swarmtypes "github.com/docker/docker/api/types/swarm"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
Expand Down Expand Up @@ -36,7 +41,48 @@ func newServicesCommand(dockerCli command.Cli, common *commonOptions) *cobra.Com

// RunServices performs a stack services against the specified orchestrator
func RunServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) error {
return runOrchestratedCommand(dockerCli, flags, commonOrchestrator,
func() error { return swarm.RunServices(dockerCli, opts) },
func(kli *kubernetes.KubeCli) error { return kubernetes.RunServices(kli, opts) })
services, info, err := GetServices(dockerCli, flags, commonOrchestrator, opts)
if err != nil {
return err
}
return formatWrite(dockerCli, services, opts, info)
}

// GetServices returns the services for the specified orchestrator
func GetServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) ([]swarm.Service, map[string]service.ListInfo, error) {
switch {
case commonOrchestrator.HasAll():
return nil, nil, errUnsupportedAllOrchestrator
case commonOrchestrator.HasKubernetes():
kli, err := kubernetes.WrapCli(dockerCli, kubernetes.NewOptions(flags, commonOrchestrator))
if err != nil {
return nil, nil, err
}
return kubernetes.GetServices(kli, opts)
default:
return swarm.GetServices(dockerCli, opts)
}
}

func formatWrite(dockerCli command.Cli, services []swarmtypes.Service, opts options.Services, info map[string]service.ListInfo) error {
// if no services in the stack, print message and exit 0
if len(services) == 0 {
fmt.Fprintf(dockerCli.Err(), "Nothing found in stack: %s\n", opts.Namespace)
return nil
}

format := opts.Format
if len(format) == 0 {
if len(dockerCli.ConfigFile().ServicesFormat) > 0 && !opts.Quiet {
format = dockerCli.ConfigFile().ServicesFormat
} else {
format = formatter.TableFormatKey
}
}

servicesCtx := formatter.Context{
Output: dockerCli.Out(),
Format: service.NewListFormat(format, opts.Quiet),
}
return service.ListFormatWrite(servicesCtx, services, info)
}
35 changes: 9 additions & 26 deletions cli/command/stack/swarm/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,35 @@ package swarm

import (
"context"
"fmt"
"sort"

"github.com/docker/cli/cli/command"
"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/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/swarm"
"vbom.ml/util/sortorder"
)

// RunServices is the swarm implementation of docker stack services
func RunServices(dockerCli command.Cli, opts options.Services) error {
// GetServices is the swarm implementation of listing stack services
func GetServices(dockerCli command.Cli, opts options.Services) ([]swarm.Service, map[string]service.ListInfo, error) {
ctx := context.Background()
client := dockerCli.Client()

filter := getStackFilterFromOpt(opts.Namespace, opts.Filter)
services, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: filter})
if err != nil {
return err
return nil, nil, err
}

// if no services in this stack, print message and exit 0
if len(services) == 0 {
fmt.Fprintf(dockerCli.Err(), "Nothing found in stack: %s\n", opts.Namespace)
return nil
return []swarm.Service{}, nil, nil
}

sort.Slice(services, func(i, j int) bool {
return sortorder.NaturalLess(services[i].Spec.Name, services[j].Spec.Name)
})

info := map[string]service.ListInfo{}
if !opts.Quiet {
taskFilter := filters.NewArgs()
Expand All @@ -43,29 +40,15 @@ func RunServices(dockerCli command.Cli, opts options.Services) error {

tasks, err := client.TaskList(ctx, types.TaskListOptions{Filters: taskFilter})
if err != nil {
return err
return nil, nil, err
}

nodes, err := client.NodeList(ctx, types.NodeListOptions{})
if err != nil {
return err
return nil, nil, err
}

info = service.GetServicesStatus(services, nodes, tasks)
}

format := opts.Format
if len(format) == 0 {
if len(dockerCli.ConfigFile().ServicesFormat) > 0 && !opts.Quiet {
format = dockerCli.ConfigFile().ServicesFormat
} else {
format = formatter.TableFormatKey
}
}

servicesCtx := formatter.Context{
Output: dockerCli.Out(),
Format: service.NewListFormat(format, opts.Quiet),
}
return service.ListFormatWrite(servicesCtx, services, info)
return services, info, nil
}

0 comments on commit ab837de

Please sign in to comment.