diff --git a/cli/command/builder/prune.go b/cli/command/builder/prune.go index 3748228e6..6add072ad 100644 --- a/cli/command/builder/prune.go +++ b/cli/command/builder/prune.go @@ -14,6 +14,7 @@ import ( "github.com/docker/cli/opts" "github.com/docker/go-units" "github.com/moby/moby/api/types/build" + "github.com/moby/moby/api/types/versions" "github.com/spf13/cobra" ) @@ -112,6 +113,10 @@ type cancelledErr struct{ error } func (cancelledErr) Cancelled() {} +type errNotImplemented struct{ error } + +func (errNotImplemented) NotImplemented() {} + // CachePrune executes a prune command for build cache // // Deprecated: this function was only used internally and will be removed in the next release. @@ -122,6 +127,10 @@ func CachePrune(ctx context.Context, dockerCli command.Cli, all bool, filter opt // pruneFn prunes the build cache for use in "docker system prune" and // returns the amount of space reclaimed and a detailed output string. func pruneFn(ctx context.Context, dockerCLI command.Cli, options pruner.PruneOptions) (uint64, string, error) { + if ver := dockerCLI.Client().ClientVersion(); ver != "" && versions.LessThan(ver, "1.31") { + // Not supported on older daemons. + return 0, "", errNotImplemented{errors.New("builder prune requires API version 1.31 or greater")} + } if !options.Confirmed { // Dry-run: perform validation and produce confirmation before pruning. var confirmMsg string diff --git a/cli/command/system/prune.go b/cli/command/system/prune.go index f06772417..c6e77f41e 100644 --- a/cli/command/system/prune.go +++ b/cli/command/system/prune.go @@ -17,16 +17,14 @@ import ( "github.com/docker/cli/opts" "github.com/docker/go-units" "github.com/fvbommel/sortorder" - "github.com/moby/moby/api/types/versions" "github.com/spf13/cobra" ) type pruneOptions struct { - force bool - all bool - pruneVolumes bool - pruneBuildCache bool - filter opts.FilterOpt + force bool + all bool + pruneVolumes bool + filter opts.FilterOpt } // newPruneCommand creates a new cobra.Command for `docker prune` @@ -38,7 +36,6 @@ func newPruneCommand(dockerCli command.Cli) *cobra.Command { Short: "Remove unused data", Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - options.pruneBuildCache = versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.31") return runPrune(cmd.Context(), dockerCli, options) }, Annotations: map[string]string{"version": "1.25"}, @@ -95,11 +92,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) if !options.pruneVolumes { continue } - case pruner.TypeBuildCache: - if !options.pruneBuildCache { - continue - } - case pruner.TypeContainer, pruner.TypeNetwork, pruner.TypeImage: + case pruner.TypeContainer, pruner.TypeNetwork, pruner.TypeImage, pruner.TypeBuildCache: // no special handling; keeping the "exhaustive" linter happy. default: // other pruners; no special handling; keeping the "exhaustive" linter happy. @@ -110,7 +103,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) All: options.all, Filter: options.filter, }) - if err != nil { + if err != nil && !errdefs.IsNotImplemented(err) { return err } spaceReclaimed += spc @@ -141,10 +134,10 @@ func dryRun(ctx context.Context, dockerCli command.Cli, options pruneOptions) (s if !options.pruneVolumes { continue } - case pruner.TypeBuildCache: - if !options.pruneBuildCache { - continue - } + case pruner.TypeContainer, pruner.TypeNetwork, pruner.TypeImage, pruner.TypeBuildCache: + // no special handling; keeping the "exhaustive" linter happy. + default: + // other pruners; no special handling; keeping the "exhaustive" linter happy. } // Always run with "[pruner.PruneOptions.Confirmed] = false" // to perform validation of the given options and produce @@ -155,7 +148,7 @@ func dryRun(ctx context.Context, dockerCli command.Cli, options pruneOptions) (s }) // A "canceled" error is expected in dry-run mode; any other error // must be returned as a "fatal" error. - if err != nil && !errdefs.IsCanceled(err) { + if err != nil && !errdefs.IsCanceled(err) && !errdefs.IsNotImplemented(err) { errs = append(errs, err) } if confirmMsg != "" { diff --git a/cli/command/system/pruner/pruner.go b/cli/command/system/pruner/pruner.go index e8a4e6fcd..f82857b92 100644 --- a/cli/command/system/pruner/pruner.go +++ b/cli/command/system/pruner/pruner.go @@ -50,9 +50,10 @@ var pruneOrder = []ContentType{ // will be pruned (for example, "all stopped containers") instead of // executing the prune. This summary is presented to the user as a // confirmation message. It may return a [ErrCancelled] to indicate -// the operation was canceled. Any other error is considered a -// validation error of the given options (such as a filter that -// is not supported. +// the operation was canceled or a [ErrNotImplemented] if the prune +// function is not implemented for the daemon's API version. Any +// other error is considered a validation error for the given options +// (such as a filter that is not supported). // - If [PruneOptions.Confirmed] is "true", the PruneFunc must execute // the prune with the given options. // @@ -64,6 +65,7 @@ var pruneOrder = []ContentType{ // presented to the user. // // [ErrCancelled]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/errdefs#ErrCancelled +// [ErrNotImplemented]: https://pkg.go.dev/github.com/docker/docker@v28.3.3+incompatible/errdefs#ErrNotImplemented type PruneFunc func(ctx context.Context, dockerCLI command.Cli, pruneOpts PruneOptions) (spaceReclaimed uint64, details string, _ error) type PruneOptions struct {