mirror of https://github.com/docker/cli.git
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> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
b3cde356f6
commit
568ea3a329
|
@ -4,8 +4,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"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/cli/cli/command/stack/options"
|
||||||
"github.com/docker/compose-on-kubernetes/api/labels"
|
"github.com/docker/compose-on-kubernetes/api/labels"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
@ -79,56 +77,43 @@ func getResourcesForServiceList(dockerCli *KubeCli, filters filters.Args, labelS
|
||||||
return replicas, daemons, services, nil
|
return replicas, daemons, services, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunServices is the kubernetes implementation of docker stack services
|
// GetServices is the kubernetes implementation of listing stack services
|
||||||
func RunServices(dockerCli *KubeCli, opts options.Services) error {
|
func GetServices(dockerCli *KubeCli, opts options.Services) ([]swarm.Service, error) {
|
||||||
filters := opts.Filter.Value()
|
filters := opts.Filter.Value()
|
||||||
if err := filters.Validate(supportedServicesFilters); err != nil {
|
if err := filters.Validate(supportedServicesFilters); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
client, err := dockerCli.composeClient()
|
client, err := dockerCli.composeClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
stacks, err := client.Stacks(false)
|
stacks, err := client.Stacks(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, err
|
||||||
}
|
}
|
||||||
stackName := opts.Namespace
|
stackName := opts.Namespace
|
||||||
_, err = stacks.Get(stackName)
|
_, err = stacks.Get(stackName)
|
||||||
if apierrs.IsNotFound(err) {
|
if apierrs.IsNotFound(err) {
|
||||||
return fmt.Errorf("nothing found in stack: %s", stackName)
|
return []swarm.Service{}, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
labelSelector := generateLabelSelector(filters, stackName)
|
labelSelector := generateLabelSelector(filters, stackName)
|
||||||
replicasList, daemonsList, servicesList, err := getResourcesForServiceList(dockerCli, filters, labelSelector)
|
replicasList, daemonsList, servicesList, err := getResourcesForServiceList(dockerCli, filters, labelSelector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert Replicas sets and kubernetes services to swarm services and formatter information
|
// Convert Replicas sets and kubernetes services to swarm services and formatter information
|
||||||
services, err := convertToServices(replicasList, daemonsList, servicesList)
|
services, err := convertToServices(replicasList, daemonsList, servicesList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
services = filterServicesByName(services, filters.Get("name"), stackName)
|
services = filterServicesByName(services, filters.Get("name"), stackName)
|
||||||
|
|
||||||
format := opts.Format
|
return services, nil
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterServicesByName(services []swarm.Service, names []string, stackName string) []swarm.Service {
|
func filterServicesByName(services []swarm.Service, names []string, stackName string) []swarm.Service {
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
package stack
|
package stack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"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/kubernetes"
|
||||||
"github.com/docker/cli/cli/command/stack/options"
|
"github.com/docker/cli/cli/command/stack/options"
|
||||||
"github.com/docker/cli/cli/command/stack/swarm"
|
"github.com/docker/cli/cli/command/stack/swarm"
|
||||||
cliopts "github.com/docker/cli/opts"
|
cliopts "github.com/docker/cli/opts"
|
||||||
|
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
"vbom.ml/util/sortorder"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newServicesCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command {
|
func newServicesCommand(dockerCli command.Cli, common *commonOptions) *cobra.Command {
|
||||||
|
@ -36,7 +43,51 @@ func newServicesCommand(dockerCli command.Cli, common *commonOptions) *cobra.Com
|
||||||
|
|
||||||
// RunServices performs a stack services against the specified orchestrator
|
// RunServices performs a stack services against the specified orchestrator
|
||||||
func RunServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) error {
|
func RunServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) error {
|
||||||
return runOrchestratedCommand(dockerCli, flags, commonOrchestrator,
|
services, err := GetServices(dockerCli, flags, commonOrchestrator, opts)
|
||||||
func() error { return swarm.RunServices(dockerCli, opts) },
|
if err != nil {
|
||||||
func(kli *kubernetes.KubeCli) error { return kubernetes.RunServices(kli, opts) })
|
return err
|
||||||
|
}
|
||||||
|
return formatWrite(dockerCli, services, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetServices returns the services for the specified orchestrator
|
||||||
|
func GetServices(dockerCli command.Cli, flags *pflag.FlagSet, commonOrchestrator command.Orchestrator, opts options.Services) ([]swarmtypes.Service, error) {
|
||||||
|
switch {
|
||||||
|
case commonOrchestrator.HasAll():
|
||||||
|
return nil, errUnsupportedAllOrchestrator
|
||||||
|
case commonOrchestrator.HasKubernetes():
|
||||||
|
kli, err := kubernetes.WrapCli(dockerCli, kubernetes.NewOptions(flags, commonOrchestrator))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return kubernetes.GetServices(kli, opts)
|
||||||
|
default:
|
||||||
|
return swarm.GetServices(dockerCli, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatWrite(dockerCli command.Cli, services []swarmtypes.Service, opts options.Services) 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
|
||||||
|
}
|
||||||
|
sort.Slice(services, func(i, j int) bool {
|
||||||
|
return sortorder.NaturalLess(services[i].Spec.Name, services[j].Spec.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,16 @@ package swarm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/command/service"
|
"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/options"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunServices is the swarm implementation of docker stack services
|
// GetServices is the swarm implementation of listing stack services
|
||||||
func RunServices(dockerCli command.Cli, opts options.Services) error {
|
func GetServices(dockerCli command.Cli, opts options.Services) ([]swarm.Service, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
|
@ -30,13 +29,7 @@ func RunServices(dockerCli command.Cli, opts options.Services) error {
|
||||||
|
|
||||||
services, err := client.ServiceList(ctx, listOpts)
|
services, err := client.ServiceList(ctx, listOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if listOpts.Status {
|
if listOpts.Status {
|
||||||
|
@ -54,22 +47,8 @@ func RunServices(dockerCli command.Cli, opts options.Services) error {
|
||||||
// a ServiceStatus set, and perform a lookup for those.
|
// a ServiceStatus set, and perform a lookup for those.
|
||||||
services, err = service.AppendServiceStatus(ctx, client, services)
|
services, err = service.AppendServiceStatus(ctx, client, services)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return services, 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)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue