Merge pull request #6406 from thaJeztah/rm_GetStacks_StackWrite

cli/command/stack: move GetStacks and StackWrite internal
This commit is contained in:
Sebastiaan van Stijn
2025-09-01 13:55:09 +02:00
committed by GitHub
8 changed files with 76 additions and 112 deletions

View File

@ -6,7 +6,6 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/stack/swarm"
"github.com/docker/cli/internal/commands"
"github.com/spf13/cobra"
)
@ -53,7 +52,7 @@ func newStackCommand(dockerCLI command.Cli) *cobra.Command {
// completeNames offers completion for swarm stacks
func completeNames(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
list, err := swarm.GetStacks(cmd.Context(), dockerCLI.Client())
list, err := getStacks(cmd.Context(), dockerCLI.Client())
if err != nil {
return nil, cobra.ShellCompDirectiveError
}

View File

@ -39,3 +39,9 @@ func getStackFilterFromOpt(namespace string, opt opts.FilterOpt) filters.Args {
filter.Add("label", convert.LabelNamespace+"="+namespace)
return filter
}
func getAllStacksFilter() filters.Args {
filter := filters.NewArgs()
filter.Add("label", convert.LabelNamespace)
return filter
}

View File

@ -1,80 +0,0 @@
package formatter
import (
"strconv"
"github.com/docker/cli/cli/command/formatter"
)
const (
// SwarmStackTableFormat is the default Swarm stack format
//
// Deprecated: this type was for internal use and will be removed in the next release.
SwarmStackTableFormat formatter.Format = "table {{.Name}}\t{{.Services}}"
stackServicesHeader = "SERVICES"
// TableFormatKey is an alias for formatter.TableFormatKey
//
// Deprecated: this type was for internal use and will be removed in the next release.
TableFormatKey = formatter.TableFormatKey
)
// Context is an alias for formatter.Context
//
// Deprecated: this type was for internal use and will be removed in the next release.
type Context = formatter.Context
// Format is an alias for formatter.Format
//
// Deprecated: this type was for internal use and will be removed in the next release.
type Format = formatter.Format
// Stack contains deployed stack information.
//
// Deprecated: this type was for internal use and will be removed in the next release.
type Stack struct {
// Name is the name of the stack
Name string
// Services is the number of the services
Services int
}
// StackWrite writes formatted stacks using the Context
//
// Deprecated: this function was for internal use and will be removed in the next release.
func StackWrite(ctx formatter.Context, stacks []Stack) error {
fmtCtx := &stackContext{
HeaderContext: formatter.HeaderContext{
Header: formatter.SubHeaderContext{
"Name": formatter.NameHeader,
"Services": stackServicesHeader,
},
},
}
return ctx.Write(fmtCtx, func(format func(subContext formatter.SubContext) error) error {
for _, stack := range stacks {
if err := format(&stackContext{s: stack}); err != nil {
return err
}
}
return nil
})
}
type stackContext struct {
formatter.HeaderContext
s Stack
}
func (s *stackContext) MarshalJSON() ([]byte, error) {
return formatter.MarshalJSON(s)
}
func (s *stackContext) Name() string {
return s.s.Name
}
func (s *stackContext) Services() string {
return strconv.Itoa(s.s.Services)
}

View File

@ -2,13 +2,11 @@ package stack
import (
"context"
"io"
"sort"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/stack/formatter"
"github.com/docker/cli/cli/command/stack/swarm"
"github.com/docker/cli/cli/command/formatter"
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/fvbommel/sortorder"
"github.com/spf13/cobra"
@ -40,24 +38,21 @@ func newListCommand(dockerCLI command.Cli) *cobra.Command {
// 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())
stacks, err := getStacks(ctx, dockerCLI.Client())
if err != nil {
return err
}
return format(dockerCLI.Out(), opts, stacks)
}
func format(out io.Writer, opts listOptions, stacks []formatter.Stack) error {
fmt := formatter.Format(opts.format)
if fmt == "" || fmt == formatter.TableFormatKey {
fmt = formatter.SwarmStackTableFormat
format := formatter.Format(opts.format)
if format == "" || format == formatter.TableFormatKey {
format = stackTableFormat
}
stackCtx := formatter.Context{
Output: out,
Format: fmt,
Output: dockerCLI.Out(),
Format: format,
}
sort.Slice(stacks, func(i, j int) bool {
return sortorder.NaturalLess(stacks[i].Name, stacks[j].Name)
})
return formatter.StackWrite(stackCtx, stacks)
return stackWrite(stackCtx, stacks)
}

View File

@ -0,0 +1,53 @@
package stack
import (
"strconv"
"github.com/docker/cli/cli/command/formatter"
)
// stackTableFormat is the default Swarm stack format
const stackTableFormat formatter.Format = "table {{.Name}}\t{{.Services}}"
// stackSummary contains deployed stack information.
type stackSummary struct {
Name string // Name is the name of the stack.
Services int // Services is the number services in the stack.
}
// stackWrite writes formatted stacks using the Context
func stackWrite(fmtCtx formatter.Context, stacks []stackSummary) error {
stackCtx := &stackContext{
HeaderContext: formatter.HeaderContext{
Header: formatter.SubHeaderContext{
"Name": formatter.NameHeader,
"Services": "SERVICES",
},
},
}
return fmtCtx.Write(stackCtx, func(format func(subContext formatter.SubContext) error) error {
for _, stack := range stacks {
if err := format(&stackContext{s: stack}); err != nil {
return err
}
}
return nil
})
}
type stackContext struct {
formatter.HeaderContext
s stackSummary
}
func (s *stackContext) MarshalJSON() ([]byte, error) {
return formatter.MarshalJSON(s)
}
func (s *stackContext) Name() string {
return s.s.Name
}
func (s *stackContext) Services() string {
return strconv.Itoa(s.s.Services)
}

View File

@ -1,4 +1,4 @@
package formatter
package stack
import (
"bytes"
@ -26,7 +26,7 @@ func TestStackContextWrite(t *testing.T) {
},
{
name: "table format",
format: SwarmStackTableFormat,
format: stackTableFormat,
expected: `NAME SERVICES
baz 2
bar 1
@ -56,7 +56,7 @@ bar
Format: tc.format,
Output: &out,
}
if err := StackWrite(fmtCtx, []Stack{
if err := stackWrite(fmtCtx, []stackSummary{
{Name: "baz", Services: 2},
{Name: "bar", Services: 1},
}); err != nil {

View File

@ -1,18 +1,15 @@
package swarm
package stack
import (
"context"
"github.com/docker/cli/cli/command/stack/formatter"
"github.com/docker/cli/cli/compose/convert"
"github.com/moby/moby/client"
"github.com/pkg/errors"
)
// GetStacks lists the swarm stacks with the number of services they contain.
//
// Deprecated: this function was for internal use and will be removed in the next release.
func GetStacks(ctx context.Context, apiClient client.ServiceAPIClient) ([]formatter.Stack, error) {
// getStacks lists the swarm stacks with the number of services they contain.
func getStacks(ctx context.Context, apiClient client.ServiceAPIClient) ([]stackSummary, error) {
services, err := apiClient.ServiceList(ctx, client.ServiceListOptions{
Filters: getAllStacksFilter(),
})
@ -21,7 +18,7 @@ func GetStacks(ctx context.Context, apiClient client.ServiceAPIClient) ([]format
}
idx := make(map[string]int, len(services))
out := make([]formatter.Stack, 0, len(services))
out := make([]stackSummary, 0, len(services))
for _, svc := range services {
name, ok := svc.Spec.Labels[convert.LabelNamespace]
@ -33,7 +30,7 @@ func GetStacks(ctx context.Context, apiClient client.ServiceAPIClient) ([]format
continue
}
idx[name] = len(out)
out = append(out, formatter.Stack{Name: name, Services: 1})
out = append(out, stackSummary{Name: name, Services: 1})
}
return out, nil
}

View File

@ -16,12 +16,6 @@ func getStackFilter(namespace string) filters.Args {
return filter
}
func getAllStacksFilter() filters.Args {
filter := filters.NewArgs()
filter.Add("label", convert.LabelNamespace)
return filter
}
func getStackServices(ctx context.Context, apiclient client.APIClient, namespace string) ([]swarm.Service, error) {
return apiclient.ServiceList(ctx, client.ServiceListOptions{Filters: getStackFilter(namespace)})
}