Merge pull request #5791 from thaJeztah/multi_errors
cli/command: use errors.Join instead of our own implementation
This commit is contained in:
commit
7c3fa8172b
@ -2,12 +2,11 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -35,23 +34,17 @@ func newConfigRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunConfigRemove removes the given Swarm configs.
|
||||
func RunConfigRemove(ctx context.Context, dockerCli command.Cli, opts RemoveOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
var errs []string
|
||||
func RunConfigRemove(ctx context.Context, dockerCLI command.Cli, opts RemoveOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
var errs []error
|
||||
for _, name := range opts.Names {
|
||||
if err := client.ConfigRemove(ctx, name); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := apiClient.ConfigRemove(ctx, name); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintln(dockerCli.Out(), name)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), name)
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -2,13 +2,12 @@ package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -44,20 +43,19 @@ func NewKillCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runKill(ctx context.Context, dockerCli command.Cli, opts *killOptions) error {
|
||||
var errs []string
|
||||
func runKill(ctx context.Context, dockerCLI command.Cli, opts *killOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
|
||||
return dockerCli.Client().ContainerKill(ctx, container, opts.signal)
|
||||
return apiClient.ContainerKill(ctx, container, opts.signal)
|
||||
})
|
||||
|
||||
var errs []error
|
||||
for _, name := range opts.containers {
|
||||
if err := <-errChan; err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
} else {
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), name)
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), name)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -2,14 +2,13 @@ package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -38,18 +37,17 @@ func NewPauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func runPause(ctx context.Context, dockerCli command.Cli, opts *pauseOptions) error {
|
||||
var errs []string
|
||||
errChan := parallelOperation(ctx, opts.containers, dockerCli.Client().ContainerPause)
|
||||
func runPause(ctx context.Context, dockerCLI command.Cli, opts *pauseOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
errChan := parallelOperation(ctx, opts.containers, apiClient.ContainerPause)
|
||||
|
||||
var errs []error
|
||||
for _, ctr := range opts.containers {
|
||||
if err := <-errChan; err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), ctr)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), ctr)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -2,14 +2,13 @@ package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -56,27 +55,25 @@ func NewRestartCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRestart(ctx context.Context, dockerCli command.Cli, opts *restartOptions) error {
|
||||
var errs []string
|
||||
func runRestart(ctx context.Context, dockerCLI command.Cli, opts *restartOptions) error {
|
||||
var timeout *int
|
||||
if opts.timeoutChanged {
|
||||
timeout = &opts.timeout
|
||||
}
|
||||
|
||||
apiClient := dockerCLI.Client()
|
||||
var errs []error
|
||||
// TODO(thaJeztah): consider using parallelOperation for restart, similar to "stop" and "remove"
|
||||
for _, name := range opts.containers {
|
||||
err := dockerCli.Client().ContainerRestart(ctx, name, container.StopOptions{
|
||||
err := apiClient.ContainerRestart(ctx, name, container.StopOptions{
|
||||
Signal: opts.signal,
|
||||
Timeout: timeout,
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), name)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), name)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@ -10,7 +11,6 @@ import (
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -50,33 +50,31 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRm(ctx context.Context, dockerCli command.Cli, opts *rmOptions) error {
|
||||
var errs []string
|
||||
func runRm(ctx context.Context, dockerCLI command.Cli, opts *rmOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, ctrID string) error {
|
||||
ctrID = strings.Trim(ctrID, "/")
|
||||
if ctrID == "" {
|
||||
return errors.New("Container name cannot be empty")
|
||||
return errors.New("container name cannot be empty")
|
||||
}
|
||||
return dockerCli.Client().ContainerRemove(ctx, ctrID, container.RemoveOptions{
|
||||
return apiClient.ContainerRemove(ctx, ctrID, container.RemoveOptions{
|
||||
RemoveVolumes: opts.rmVolumes,
|
||||
RemoveLinks: opts.rmLink,
|
||||
Force: opts.force,
|
||||
})
|
||||
})
|
||||
|
||||
var errs []error
|
||||
for _, name := range opts.containers {
|
||||
if err := <-errChan; err != nil {
|
||||
if opts.force && errdefs.IsNotFound(err) {
|
||||
fmt.Fprintln(dockerCli.Err(), err)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Err(), err)
|
||||
continue
|
||||
}
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out(), name)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), name)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package container
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
@ -17,7 +18,6 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/events"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -238,16 +238,16 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
|
||||
// make sure each container get at least one valid stat data
|
||||
waitFirst.Wait()
|
||||
|
||||
var errs []string
|
||||
var errs []error
|
||||
cStats.mu.RLock()
|
||||
for _, c := range cStats.cs {
|
||||
if err := c.GetError(); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
cStats.mu.RUnlock()
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
if err := errors.Join(errs...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,13 @@ package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -56,28 +55,26 @@ func NewStopCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runStop(ctx context.Context, dockerCli command.Cli, opts *stopOptions) error {
|
||||
func runStop(ctx context.Context, dockerCLI command.Cli, opts *stopOptions) error {
|
||||
var timeout *int
|
||||
if opts.timeoutChanged {
|
||||
timeout = &opts.timeout
|
||||
}
|
||||
|
||||
apiClient := dockerCLI.Client()
|
||||
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, id string) error {
|
||||
return dockerCli.Client().ContainerStop(ctx, id, container.StopOptions{
|
||||
return apiClient.ContainerStop(ctx, id, container.StopOptions{
|
||||
Signal: opts.signal,
|
||||
Timeout: timeout,
|
||||
})
|
||||
})
|
||||
var errs []string
|
||||
var errs []error
|
||||
for _, ctr := range opts.containers {
|
||||
if err := <-errChan; err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), ctr)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), ctr)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -2,14 +2,13 @@ package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -39,18 +38,16 @@ func NewUnpauseCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runUnpause(ctx context.Context, dockerCli command.Cli, opts *unpauseOptions) error {
|
||||
var errs []string
|
||||
errChan := parallelOperation(ctx, opts.containers, dockerCli.Client().ContainerUnpause)
|
||||
func runUnpause(ctx context.Context, dockerCLI command.Cli, opts *unpauseOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
errChan := parallelOperation(ctx, opts.containers, apiClient.ContainerUnpause)
|
||||
var errs []error
|
||||
for _, ctr := range opts.containers {
|
||||
if err := <-errChan; err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), ctr)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), ctr)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -2,13 +2,12 @@ package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -37,20 +36,19 @@ func NewWaitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWait(ctx context.Context, dockerCli command.Cli, opts *waitOptions) error {
|
||||
var errs []string
|
||||
func runWait(ctx context.Context, dockerCLI command.Cli, opts *waitOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
var errs []error
|
||||
for _, ctr := range opts.containers {
|
||||
resultC, errC := dockerCli.Client().ContainerWait(ctx, ctr, "")
|
||||
resultC, errC := apiClient.ContainerWait(ctx, ctr, "")
|
||||
|
||||
select {
|
||||
case result := <-resultC:
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), "%d\n", result.StatusCode)
|
||||
_, _ = fmt.Fprintf(dockerCLI.Out(), "%d\n", result.StatusCode)
|
||||
case err := <-errC:
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/context"
|
||||
"github.com/docker/cli/cli/context/docker"
|
||||
"github.com/docker/cli/cli/context/store"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -68,20 +68,17 @@ func parseBool(config map[string]string, name string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
res, err := strconv.ParseBool(strVal)
|
||||
return res, errors.Wrap(err, name)
|
||||
return res, fmt.Errorf("name: %w", err)
|
||||
}
|
||||
|
||||
func validateConfig(config map[string]string, allowedKeys map[string]struct{}) error {
|
||||
var errs []string
|
||||
var errs []error
|
||||
for k := range config {
|
||||
if _, ok := allowedKeys[k]; !ok {
|
||||
errs = append(errs, "unrecognized config key: "+k)
|
||||
errs = append(errs, errors.New("unrecognized config key: "+k))
|
||||
}
|
||||
}
|
||||
if len(errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func getDockerEndpoint(contextStore store.Reader, config map[string]string) (docker.Endpoint, error) {
|
||||
@ -96,7 +93,7 @@ func getDockerEndpoint(contextStore store.Reader, config map[string]string) (doc
|
||||
if ep, ok := metadata.Endpoints[docker.DockerEndpoint].(docker.EndpointMeta); ok {
|
||||
return docker.Endpoint{EndpointMeta: ep}, nil
|
||||
}
|
||||
return docker.Endpoint{}, errors.Errorf("unable to get endpoint from context %q", contextName)
|
||||
return docker.Endpoint{}, fmt.Errorf("unable to get endpoint from context %q", contextName)
|
||||
}
|
||||
tlsData, err := context.TLSDataFromFiles(config[keyCA], config[keyCert], config[keyKey])
|
||||
if err != nil {
|
||||
@ -116,10 +113,11 @@ func getDockerEndpoint(contextStore store.Reader, config map[string]string) (doc
|
||||
// try to resolve a docker client, validating the configuration
|
||||
opts, err := ep.ClientOpts()
|
||||
if err != nil {
|
||||
return docker.Endpoint{}, errors.Wrap(err, "invalid docker endpoint options")
|
||||
return docker.Endpoint{}, fmt.Errorf("invalid docker endpoint options: %w", err)
|
||||
}
|
||||
// FIXME(thaJeztah): this creates a new client (but discards it) only to validate the options; are the validation steps above not enough?
|
||||
if _, err := client.NewClientWithOpts(opts...); err != nil {
|
||||
return docker.Endpoint{}, errors.Wrap(err, "unable to apply docker endpoint options")
|
||||
return docker.Endpoint{}, fmt.Errorf("unable to apply docker endpoint options: %w", err)
|
||||
}
|
||||
return ep, nil
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -33,28 +32,25 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunRemove removes one or more contexts
|
||||
func RunRemove(dockerCli command.Cli, opts RemoveOptions, names []string) error {
|
||||
var errs []string
|
||||
currentCtx := dockerCli.CurrentContext()
|
||||
func RunRemove(dockerCLI command.Cli, opts RemoveOptions, names []string) error {
|
||||
var errs []error
|
||||
currentCtx := dockerCLI.CurrentContext()
|
||||
for _, name := range names {
|
||||
if name == "default" {
|
||||
errs = append(errs, `default: context "default" cannot be removed`)
|
||||
} else if err := doRemove(dockerCli, name, name == currentCtx, opts.Force); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, errors.New(`context "default" cannot be removed`))
|
||||
} else if err := doRemove(dockerCLI, name, name == currentCtx, opts.Force); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
fmt.Fprintln(dockerCli.Out(), name)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), name)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func doRemove(dockerCli command.Cli, name string, isCurrent, force bool) error {
|
||||
if isCurrent {
|
||||
if !force {
|
||||
return errors.Errorf("context %q is in use, set -f flag to force remove", name)
|
||||
return fmt.Errorf("context %q is in use, set -f flag to force remove", name)
|
||||
}
|
||||
// fallback to DOCKER_HOST
|
||||
cfg := dockerCli.ConfigFile()
|
||||
@ -65,6 +61,7 @@ func doRemove(dockerCli command.Cli, name string, isCurrent, force bool) error {
|
||||
}
|
||||
|
||||
if !force {
|
||||
// TODO(thaJeztah): instead of checking before removing, can we make ContextStore().Remove() return a proper errdef and ignore "not found" errors?
|
||||
if err := checkContextExists(dockerCli, name); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -77,7 +74,7 @@ func checkContextExists(dockerCli command.Cli, name string) error {
|
||||
contextDir := dockerCli.ContextStore().GetStorageInfo(name).MetadataPath
|
||||
_, err := os.Stat(contextDir)
|
||||
if os.IsNotExist(err) {
|
||||
return errdefs.NotFound(errors.Errorf("context %q does not exist", name))
|
||||
return errdefs.NotFound(fmt.Errorf("context %q does not exist", name))
|
||||
}
|
||||
// Ignore other errors; if relevant, they will produce an error when
|
||||
// performing the actual delete.
|
||||
|
@ -60,5 +60,5 @@ func TestRemoveDefault(t *testing.T) {
|
||||
createTestContext(t, cli, "other", nil)
|
||||
cli.SetCurrentContext("current")
|
||||
err := RunRemove(cli, RemoveOptions{}, []string{"default"})
|
||||
assert.ErrorContains(t, err, `default: context "default" cannot be removed`)
|
||||
assert.ErrorContains(t, err, `context "default" cannot be removed`)
|
||||
}
|
||||
|
@ -2,15 +2,14 @@ package image
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -59,15 +58,16 @@ func runRemove(ctx context.Context, dockerCLI command.Cli, opts removeOptions, i
|
||||
PruneChildren: !opts.noPrune,
|
||||
}
|
||||
|
||||
var errs []string
|
||||
// TODO(thaJeztah): this logic can likely be simplified: do we want to print "not found" errors at all when using "force"?
|
||||
fatalErr := false
|
||||
var errs []error
|
||||
for _, img := range images {
|
||||
dels, err := apiClient.ImageRemove(ctx, img, options)
|
||||
if err != nil {
|
||||
if !errdefs.IsNotFound(err) {
|
||||
fatalErr = true
|
||||
}
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
for _, del := range dels {
|
||||
if del.Deleted != "" {
|
||||
@ -79,12 +79,11 @@ func runRemove(ctx context.Context, dockerCLI command.Cli, opts removeOptions, i
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
msg := strings.Join(errs, "\n")
|
||||
if err := errors.Join(errs...); err != nil {
|
||||
if !opts.force || fatalErr {
|
||||
return errors.New(msg)
|
||||
return err
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCLI.Err(), msg)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Err(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -6,13 +6,13 @@ package inspect
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/templates"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -53,7 +53,7 @@ func NewTemplateInspectorFromString(out io.Writer, tmplStr string) (Inspector, e
|
||||
|
||||
tmpl, err := templates.Parse(tmplStr)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("template parsing error: %s", err)
|
||||
return nil, fmt.Errorf("template parsing error: %w", err)
|
||||
}
|
||||
return NewTemplateInspector(out, tmpl), nil
|
||||
}
|
||||
@ -70,16 +70,16 @@ func Inspect(out io.Writer, references []string, tmplStr string, getRef GetRefFu
|
||||
return cli.StatusError{StatusCode: 64, Status: err.Error()}
|
||||
}
|
||||
|
||||
var inspectErrs []string
|
||||
var errs []error
|
||||
for _, ref := range references {
|
||||
element, raw, err := getRef(ref)
|
||||
if err != nil {
|
||||
inspectErrs = append(inspectErrs, err.Error())
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := inspector.Inspect(element, raw); err != nil {
|
||||
inspectErrs = append(inspectErrs, err.Error())
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,10 +87,10 @@ func Inspect(out io.Writer, references []string, tmplStr string, getRef GetRefFu
|
||||
logrus.Error(err)
|
||||
}
|
||||
|
||||
if len(inspectErrs) != 0 {
|
||||
if err := errors.Join(errs...); err != nil {
|
||||
return cli.StatusError{
|
||||
StatusCode: 1,
|
||||
Status: strings.Join(inspectErrs, "\n"),
|
||||
Status: err.Error(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -103,7 +103,7 @@ func (i *TemplateInspector) Inspect(typedElement any, rawElement []byte) error {
|
||||
buffer := new(bytes.Buffer)
|
||||
if err := i.tmpl.Execute(buffer, typedElement); err != nil {
|
||||
if rawElement == nil {
|
||||
return errors.Errorf("template parsing error: %v", err)
|
||||
return fmt.Errorf("template parsing error: %w", err)
|
||||
}
|
||||
return i.tryRawInspectFallback(rawElement)
|
||||
}
|
||||
@ -121,13 +121,13 @@ func (i *TemplateInspector) tryRawInspectFallback(rawElement []byte) error {
|
||||
dec := json.NewDecoder(rdr)
|
||||
dec.UseNumber()
|
||||
|
||||
if rawErr := dec.Decode(&raw); rawErr != nil {
|
||||
return errors.Errorf("unable to read inspect data: %v", rawErr)
|
||||
if err := dec.Decode(&raw); err != nil {
|
||||
return fmt.Errorf("unable to read inspect data: %w", err)
|
||||
}
|
||||
|
||||
tmplMissingKey := i.tmpl.Option("missingkey=error")
|
||||
if rawErr := tmplMissingKey.Execute(buffer, raw); rawErr != nil {
|
||||
return errors.Errorf("template parsing error: %v", rawErr)
|
||||
if err := tmplMissingKey.Execute(buffer, raw); err != nil {
|
||||
return fmt.Errorf("template parsing error: %w", err)
|
||||
}
|
||||
|
||||
i.buffer.Write(buffer.Bytes())
|
||||
|
@ -2,47 +2,47 @@ package manifest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"errors"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/pkg/errors"
|
||||
manifeststore "github.com/docker/cli/cli/manifest/store"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newRmManifestListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newRmManifestListCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "rm MANIFEST_LIST [MANIFEST_LIST...]",
|
||||
Short: "Delete one or more manifest lists from local storage",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runRm(cmd.Context(), dockerCli, args)
|
||||
return runRemove(cmd.Context(), dockerCLI.ManifestStore(), args)
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runRm(_ context.Context, dockerCli command.Cli, targets []string) error {
|
||||
var errs []string
|
||||
func runRemove(ctx context.Context, store manifeststore.Store, targets []string) error {
|
||||
var errs []error
|
||||
for _, target := range targets {
|
||||
targetRef, refErr := normalizeReference(target)
|
||||
if refErr != nil {
|
||||
errs = append(errs, refErr.Error())
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
targetRef, err := normalizeReference(target)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, searchErr := dockerCli.ManifestStore().GetList(targetRef)
|
||||
if searchErr != nil {
|
||||
errs = append(errs, searchErr.Error())
|
||||
_, err = store.GetList(targetRef)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
rmErr := dockerCli.ManifestStore().Remove(targetRef)
|
||||
if rmErr != nil {
|
||||
errs = append(errs, rmErr.Error())
|
||||
err = store.Remove(targetRef)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -2,13 +2,12 @@ package node
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -36,20 +35,13 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func runRemove(ctx context.Context, dockerCLI command.Cli, nodeIDs []string, opts removeOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
var errs []string
|
||||
|
||||
var errs []error
|
||||
for _, id := range nodeIDs {
|
||||
err := apiClient.NodeRemove(ctx, id, types.NodeRemoveOptions{Force: opts.force})
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := apiClient.NodeRemove(ctx, id, types.NodeRemoveOptions{Force: opts.force}); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), id)
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -2,12 +2,11 @@ package secret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -15,7 +14,7 @@ type removeOptions struct {
|
||||
names []string
|
||||
}
|
||||
|
||||
func newSecretRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func newSecretRemoveCommand(dockerCLI command.Cli) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "rm SECRET [SECRET...]",
|
||||
Aliases: []string{"remove"},
|
||||
@ -25,31 +24,24 @@ func newSecretRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
opts := removeOptions{
|
||||
names: args,
|
||||
}
|
||||
return runSecretRemove(cmd.Context(), dockerCli, opts)
|
||||
return runRemove(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)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runSecretRemove(ctx context.Context, dockerCli command.Cli, opts removeOptions) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
var errs []string
|
||||
func runRemove(ctx context.Context, dockerCLI command.Cli, opts removeOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
var errs []error
|
||||
for _, name := range opts.names {
|
||||
if err := client.SecretRemove(ctx, name); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := apiClient.SecretRemove(ctx, name); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintln(dockerCli.Out(), name)
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), name)
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -2,12 +2,11 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -32,17 +31,13 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func runRemove(ctx context.Context, dockerCLI command.Cli, serviceIDs []string) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
var errs []string
|
||||
var errs []error
|
||||
for _, id := range serviceIDs {
|
||||
err := apiClient.ServiceRemove(ctx, id)
|
||||
if err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
if err := apiClient.ServiceRemove(ctx, id); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), id)
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
@ -42,12 +42,9 @@ func runRollback(ctx context.Context, dockerCLI command.Cli, options *serviceOpt
|
||||
return err
|
||||
}
|
||||
|
||||
spec := &service.Spec
|
||||
updateOpts := types.ServiceUpdateOptions{
|
||||
Rollback: "previous",
|
||||
}
|
||||
|
||||
response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, *spec, updateOpts)
|
||||
response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{
|
||||
Rollback: "previous", // TODO(thaJeztah): this should have a const defined
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -10,7 +11,7 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -44,8 +45,8 @@ func scaleArgs(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
for _, arg := range args {
|
||||
if k, v, ok := strings.Cut(arg, "="); !ok || k == "" || v == "" {
|
||||
return errors.Errorf(
|
||||
"Invalid scale specifier '%s'.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
|
||||
return fmt.Errorf(
|
||||
"invalid scale specifier '%s'.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
|
||||
arg,
|
||||
cmd.CommandPath(),
|
||||
cmd.UseLine(),
|
||||
@ -56,49 +57,48 @@ func scaleArgs(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runScale(ctx context.Context, dockerCli command.Cli, options *scaleOptions, args []string) error {
|
||||
var errs []string
|
||||
var serviceIDs []string
|
||||
|
||||
func runScale(ctx context.Context, dockerCLI command.Cli, options *scaleOptions, args []string) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
var (
|
||||
errs []error
|
||||
serviceIDs = make([]string, 0, len(args))
|
||||
)
|
||||
for _, arg := range args {
|
||||
serviceID, scaleStr, _ := strings.Cut(arg, "=")
|
||||
|
||||
// validate input arg scale number
|
||||
scale, err := strconv.ParseUint(scaleStr, 10, 64)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: invalid replicas value %s: %v", serviceID, scaleStr, err))
|
||||
errs = append(errs, fmt.Errorf("%s: invalid replicas value %s: %v", serviceID, scaleStr, err))
|
||||
continue
|
||||
}
|
||||
|
||||
if err := runServiceScale(ctx, dockerCli, serviceID, scale); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: %v", serviceID, err))
|
||||
} else {
|
||||
serviceIDs = append(serviceIDs, serviceID)
|
||||
warnings, err := runServiceScale(ctx, apiClient, serviceID, scale)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("%s: %v", serviceID, err))
|
||||
continue
|
||||
}
|
||||
for _, warning := range warnings {
|
||||
_, _ = fmt.Fprintln(dockerCLI.Err(), warning)
|
||||
}
|
||||
_, _ = fmt.Fprintf(dockerCLI.Out(), "%s scaled to %d\n", serviceID, scale)
|
||||
serviceIDs = append(serviceIDs, serviceID)
|
||||
}
|
||||
|
||||
if len(serviceIDs) > 0 {
|
||||
if !options.detach && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.29") {
|
||||
for _, serviceID := range serviceIDs {
|
||||
if err := WaitOnService(ctx, dockerCli, serviceID, false); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("%s: %v", serviceID, err))
|
||||
}
|
||||
if len(serviceIDs) > 0 && !options.detach && versions.GreaterThanOrEqualTo(dockerCLI.Client().ClientVersion(), "1.29") {
|
||||
for _, serviceID := range serviceIDs {
|
||||
if err := WaitOnService(ctx, dockerCLI, serviceID, false); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%s: %v", serviceID, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func runServiceScale(ctx context.Context, dockerCli command.Cli, serviceID string, scale uint64) error {
|
||||
client := dockerCli.Client()
|
||||
|
||||
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
||||
func runServiceScale(ctx context.Context, apiClient client.ServiceAPIClient, serviceID string, scale uint64) (warnings []string, _ error) {
|
||||
service, _, err := apiClient.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serviceMode := &service.Spec.Mode
|
||||
@ -108,18 +108,12 @@ func runServiceScale(ctx context.Context, dockerCli command.Cli, serviceID strin
|
||||
case serviceMode.ReplicatedJob != nil:
|
||||
serviceMode.ReplicatedJob.TotalCompletions = &scale
|
||||
default:
|
||||
return errors.Errorf("scale can only be used with replicated or replicated-job mode")
|
||||
return nil, errors.New("scale can only be used with replicated or replicated-job mode")
|
||||
}
|
||||
|
||||
response, err := client.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
||||
response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, warning := range response.Warnings {
|
||||
_, _ = fmt.Fprintln(dockerCli.Err(), warning)
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), "%s scaled to %d\n", serviceID, scale)
|
||||
return nil
|
||||
return response.Warnings, nil
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ func TestRemoveContinueAfterError(t *testing.T) {
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"foo", "bar"})
|
||||
|
||||
assert.Error(t, cmd.Execute(), "Failed to remove some resources from stack: foo")
|
||||
assert.Error(t, cmd.Execute(), "failed to remove some resources from stack: foo")
|
||||
assert.Check(t, is.DeepEqual(allServiceIDs, removedServices))
|
||||
assert.Check(t, is.DeepEqual(allNetworkIDs, cli.removedNetworks))
|
||||
assert.Check(t, is.DeepEqual(allSecretIDs, cli.removedSecrets))
|
||||
|
@ -2,9 +2,9 @@ package swarm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/stack/options"
|
||||
@ -12,14 +12,13 @@ import (
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// RunRemove is the swarm implementation of docker stack remove
|
||||
func RunRemove(ctx context.Context, dockerCli command.Cli, opts options.Remove) error {
|
||||
apiClient := dockerCli.Client()
|
||||
|
||||
var errs []string
|
||||
var errs []error
|
||||
for _, namespace := range opts.Namespaces {
|
||||
services, err := getStackServices(ctx, apiClient, namespace)
|
||||
if err != nil {
|
||||
@ -52,28 +51,25 @@ func RunRemove(ctx context.Context, dockerCli command.Cli, opts options.Remove)
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO(thaJeztah): change this "hasError" boolean to return a (multi-)error for each of these functions instead.
|
||||
hasError := removeServices(ctx, dockerCli, services)
|
||||
hasError = removeSecrets(ctx, dockerCli, secrets) || hasError
|
||||
hasError = removeConfigs(ctx, dockerCli, configs) || hasError
|
||||
hasError = removeNetworks(ctx, dockerCli, networks) || hasError
|
||||
|
||||
if hasError {
|
||||
errs = append(errs, "Failed to remove some resources from stack: "+namespace)
|
||||
errs = append(errs, errors.New("failed to remove some resources from stack: "+namespace))
|
||||
continue
|
||||
}
|
||||
|
||||
if !opts.Detach {
|
||||
err = waitOnTasks(ctx, apiClient, namespace)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Sprintf("Failed to wait on tasks of stack: %s: %s", namespace, err))
|
||||
errs = append(errs, fmt.Errorf("failed to wait on tasks of stack: %s: %w", namespace, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func sortServiceByName(services []swarm.Service) func(i, j int) bool {
|
||||
|
@ -2,13 +2,12 @@ package volume
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/completion"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -43,18 +42,13 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
|
||||
func runRemove(ctx context.Context, dockerCLI command.Cli, opts *removeOptions) error {
|
||||
apiClient := dockerCLI.Client()
|
||||
|
||||
var errs []string
|
||||
|
||||
var errs []error
|
||||
for _, name := range opts.volumes {
|
||||
if err := apiClient.VolumeRemove(ctx, name, opts.force); err != nil {
|
||||
errs = append(errs, err.Error())
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCLI.Out(), name)
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user