Skip to content

Commit f7b6ff3

Browse files
committed
Refactor the stack services command to be uniform
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>
1 parent 3e07fa7 commit f7b6ff3

3 files changed

Lines changed: 69 additions & 54 deletions

File tree

cli/command/stack/kubernetes/services.go

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"strings"
66

77
"github.com/docker/cli/cli/command/service"
8-
"github.com/docker/cli/cli/command/stack/formatter"
98
"github.com/docker/cli/cli/command/stack/options"
109
"github.com/docker/compose-on-kubernetes/api/labels"
1110
"github.com/docker/docker/api/types/filters"
@@ -79,60 +78,47 @@ func getResourcesForServiceList(dockerCli *KubeCli, filters filters.Args, labelS
7978
return replicas, daemons, services, nil
8079
}
8180

82-
// RunServices is the kubernetes implementation of docker stack services
83-
func RunServices(dockerCli *KubeCli, opts options.Services) error {
81+
// GetServices is the kubernetes implementation of listing stack services
82+
func GetServices(dockerCli *KubeCli, opts options.Services) ([]swarm.Service, map[string]service.ListInfo, error) {
8483
filters := opts.Filter.Value()
8584
if err := filters.Validate(supportedServicesFilters); err != nil {
86-
return err
85+
return nil, nil, err
8786
}
8887
client, err := dockerCli.composeClient()
8988
if err != nil {
90-
return nil
89+
return nil, nil, err
9190
}
9291
stacks, err := client.Stacks(false)
9392
if err != nil {
94-
return nil
93+
return nil, nil, err
9594
}
9695
stackName := opts.Namespace
9796
_, err = stacks.Get(stackName)
9897
if apierrs.IsNotFound(err) {
99-
return fmt.Errorf("nothing found in stack: %s", stackName)
98+
return []swarm.Service{}, nil, nil
10099
}
101100
if err != nil {
102-
return err
101+
return nil, nil, err
103102
}
104103

105104
labelSelector := generateLabelSelector(filters, stackName)
106105
replicasList, daemonsList, servicesList, err := getResourcesForServiceList(dockerCli, filters, labelSelector)
107106
if err != nil {
108-
return err
107+
return nil, nil, err
109108
}
110109

111110
// Convert Replicas sets and kubernetes services to swarm services and formatter information
112111
services, info, err := convertToServices(replicasList, daemonsList, servicesList)
113112
if err != nil {
114-
return err
113+
return nil, nil, err
115114
}
116115
services = filterServicesByName(services, filters.Get("name"), stackName)
117116

118117
if opts.Quiet {
119118
info = map[string]service.ListInfo{}
120119
}
121120

122-
format := opts.Format
123-
if len(format) == 0 {
124-
if len(dockerCli.ConfigFile().ServicesFormat) > 0 && !opts.Quiet {
125-
format = dockerCli.ConfigFile().ServicesFormat
126-
} else {
127-
format = formatter.TableFormatKey
128-
}
129-
}
130-
131-
servicesCtx := formatter.Context{
132-
Output: dockerCli.Out(),
133-
Format: service.NewListFormat(format, opts.Quiet),
134-
}
135-
return service.ListFormatWrite(servicesCtx, services, info)
121+
return services, info, nil
136122
}
137123

138124
func filterServicesByName(services []swarm.Service, names []string, stackName string) []swarm.Service {

cli/command/stack/services.go

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package stack
22

33
import (
4+
"fmt"
5+
46
"github.com/docker/cli/cli"
57
"github.com/docker/cli/cli/command"
8+
"github.com/docker/cli/cli/command/service"
9+
"github.com/docker/cli/cli/command/stack/formatter"
610
"github.com/docker/cli/cli/command/stack/kubernetes"
711
"github.com/docker/cli/cli/command/stack/options"
8-
"github.com/docker/cli/cli/command/stack/swarm"
12+
swarmStack "github.com/docker/cli/cli/command/stack/swarm"
913
cliopts "github.com/docker/cli/opts"
14+
"github.com/docker/docker/api/types/swarm"
1015
"github.com/spf13/cobra"
1116
"github.com/spf13/pflag"
1217
)
@@ -36,7 +41,48 @@ func newServicesCommand(dockerCli command.Cli, common *commonOptions) *cobra.Com
3641

3742
// RunServices performs a stack services against the specified orchestrator
3843
func RunServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) error {
39-
return runOrchestratedCommand(dockerCli, flags, commonOrchestrator,
40-
func() error { return swarm.RunServices(dockerCli, opts) },
41-
func(kli *kubernetes.KubeCli) error { return kubernetes.RunServices(kli, opts) })
44+
services, info, err := GetServices(dockerCli, flags, commonOrchestrator, opts)
45+
if err != nil {
46+
return err
47+
}
48+
return formatWrite(dockerCli, services, opts, info)
49+
}
50+
51+
// GetServices returns the services for the specified orchestrator
52+
func GetServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) ([]swarm.Service, map[string]service.ListInfo, error) {
53+
switch {
54+
case commonOrchestrator.HasAll():
55+
return nil, nil, errUnsupportedAllOrchestrator
56+
case commonOrchestrator.HasKubernetes():
57+
kli, err := kubernetes.WrapCli(dockerCli, kubernetes.NewOptions(flags, commonOrchestrator))
58+
if err != nil {
59+
return nil, nil, err
60+
}
61+
return kubernetes.GetServices(kli, opts)
62+
default:
63+
return swarmStack.GetServices(dockerCli, opts)
64+
}
65+
}
66+
67+
func formatWrite(dockerCli command.Cli, services []swarm.Service, opts options.Services, info map[string]service.ListInfo) error {
68+
// if no services in the stack, print message and exit 0
69+
if len(services) == 0 {
70+
fmt.Fprintf(dockerCli.Err(), "Nothing found in stack: %s\n", opts.Namespace)
71+
return nil
72+
}
73+
74+
format := opts.Format
75+
if len(format) == 0 {
76+
if len(dockerCli.ConfigFile().ServicesFormat) > 0 && !opts.Quiet {
77+
format = dockerCli.ConfigFile().ServicesFormat
78+
} else {
79+
format = formatter.TableFormatKey
80+
}
81+
}
82+
83+
servicesCtx := formatter.Context{
84+
Output: dockerCli.Out(),
85+
Format: service.NewListFormat(format, opts.Quiet),
86+
}
87+
return service.ListFormatWrite(servicesCtx, services, info)
4288
}

cli/command/stack/swarm/services.go

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,35 @@ package swarm
22

33
import (
44
"context"
5-
"fmt"
65
"sort"
76

87
"github.com/docker/cli/cli/command"
98
"github.com/docker/cli/cli/command/service"
10-
"github.com/docker/cli/cli/command/stack/formatter"
119
"github.com/docker/cli/cli/command/stack/options"
1210
"github.com/docker/docker/api/types"
1311
"github.com/docker/docker/api/types/filters"
12+
"github.com/docker/docker/api/types/swarm"
1413
"vbom.ml/util/sortorder"
1514
)
1615

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

2221
filter := getStackFilterFromOpt(opts.Namespace, opts.Filter)
2322
services, err := client.ServiceList(ctx, types.ServiceListOptions{Filters: filter})
2423
if err != nil {
25-
return err
24+
return nil, nil, err
2625
}
27-
28-
// if no services in this stack, print message and exit 0
2926
if len(services) == 0 {
30-
fmt.Fprintf(dockerCli.Err(), "Nothing found in stack: %s\n", opts.Namespace)
31-
return nil
27+
return []swarm.Service{}, nil, nil
3228
}
3329

3430
sort.Slice(services, func(i, j int) bool {
3531
return sortorder.NaturalLess(services[i].Spec.Name, services[j].Spec.Name)
3632
})
33+
3734
info := map[string]service.ListInfo{}
3835
if !opts.Quiet {
3936
taskFilter := filters.NewArgs()
@@ -43,29 +40,15 @@ func RunServices(dockerCli command.Cli, opts options.Services) error {
4340

4441
tasks, err := client.TaskList(ctx, types.TaskListOptions{Filters: taskFilter})
4542
if err != nil {
46-
return err
43+
return nil, nil, err
4744
}
4845

4946
nodes, err := client.NodeList(ctx, types.NodeListOptions{})
5047
if err != nil {
51-
return err
48+
return nil, nil, err
5249
}
5350

5451
info = service.GetServicesStatus(services, nodes, tasks)
5552
}
56-
57-
format := opts.Format
58-
if len(format) == 0 {
59-
if len(dockerCli.ConfigFile().ServicesFormat) > 0 && !opts.Quiet {
60-
format = dockerCli.ConfigFile().ServicesFormat
61-
} else {
62-
format = formatter.TableFormatKey
63-
}
64-
}
65-
66-
servicesCtx := formatter.Context{
67-
Output: dockerCli.Out(),
68-
Format: service.NewListFormat(format, opts.Quiet),
69-
}
70-
return service.ListFormatWrite(servicesCtx, services, info)
53+
return services, info, nil
7154
}

0 commit comments

Comments
 (0)