The "until" filter is supported by all object types, except for
volumes.
Before this patch, the "until" filter would attempted to be used for the volume
prune endpoint, resulting in an error being returned by the daemon, and
further prune endpoints (networks, images) to be skipped.
$ docker system prune --filter until=24h --filter label=label.foo=bar
WARNING! This will remove:
- all stopped containers
- all volumes not used by at least one container
- all networks not used by at least one container
- all dangling images
Are you sure you want to continue? [y/N] y
Error response from daemon: Invalid filter 'until'
Calling POST /v1.30/containers/prune?filters=%7B%22label%22%3A%7B%22label.foo%3D%3Dbar%22%3Atrue%7D%2C%22until%22%3A%7B%2224h%22%3Atrue%7D%7D
Calling POST /v1.30/volumes/prune?filters=%7B%22label%22%3A%7B%22label.foo%3D%3Dbar%22%3Atrue%7D%2C%22until%22%3A%7B%2224h%22%3Atrue%7D%7D
Handler for POST /v1.30/volumes/prune returned error: Invalid filter 'until'
Error response from daemon: Invalid filter 'until'
With this patch, an error is produced instead, preventing "partial" prune.
$ docker system prune --filter until=24h --filter label=foo==bar --volumes
ERROR: The "until" filter is not supported with "--volumes"
Note that `docker volume prune` does not have this problem, and produces an
error if the `until` filter is used;
$ docker volume prune --filter until=24h
WARNING! This will remove all volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Error response from daemon: Invalid filter 'until'
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
136 lines
4.2 KiB
Go
136 lines
4.2 KiB
Go
package system
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"text/template"
|
|
|
|
"github.com/docker/cli/cli"
|
|
"github.com/docker/cli/cli/command"
|
|
"github.com/docker/cli/cli/command/container"
|
|
"github.com/docker/cli/cli/command/image"
|
|
"github.com/docker/cli/cli/command/network"
|
|
"github.com/docker/cli/cli/command/volume"
|
|
"github.com/docker/cli/opts"
|
|
"github.com/docker/docker/api/types/versions"
|
|
units "github.com/docker/go-units"
|
|
"github.com/spf13/cobra"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
type pruneOptions struct {
|
|
force bool
|
|
all bool
|
|
pruneBuildCache bool
|
|
pruneVolumes bool
|
|
filter opts.FilterOpt
|
|
}
|
|
|
|
// newPruneCommand creates a new cobra.Command for `docker prune`
|
|
func newPruneCommand(dockerCli command.Cli) *cobra.Command {
|
|
options := pruneOptions{filter: opts.NewFilterOpt(), pruneBuildCache: true}
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "prune [OPTIONS]",
|
|
Short: "Remove unused data",
|
|
Args: cli.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return runPrune(dockerCli, options)
|
|
},
|
|
Tags: map[string]string{"version": "1.25"},
|
|
}
|
|
|
|
flags := cmd.Flags()
|
|
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
|
flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused images not just dangling ones")
|
|
flags.BoolVar(&options.pruneVolumes, "volumes", false, "Prune volumes")
|
|
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'label=<key>=<value>')")
|
|
// "filter" flag is available in 1.28 (docker 17.04) and up
|
|
flags.SetAnnotation("filter", "version", []string{"1.28"})
|
|
|
|
return cmd
|
|
}
|
|
|
|
const confirmationTemplate = `WARNING! This will remove:
|
|
{{- range $_, $warning := . }}
|
|
- {{ $warning }}
|
|
{{- end }}
|
|
Are you sure you want to continue?`
|
|
|
|
// runBuildCachePrune executes a prune command for build cache
|
|
func runBuildCachePrune(dockerCli command.Cli, _ opts.FilterOpt) (uint64, string, error) {
|
|
report, err := dockerCli.Client().BuildCachePrune(context.Background())
|
|
if err != nil {
|
|
return 0, "", err
|
|
}
|
|
return report.SpaceReclaimed, "", nil
|
|
}
|
|
|
|
func runPrune(dockerCli command.Cli, options pruneOptions) error {
|
|
// TODO version this once "until" filter is supported for volumes
|
|
if options.pruneVolumes && options.filter.Value().Include("until") {
|
|
return fmt.Errorf(`ERROR: The "until" filter is not supported with "--volumes"`)
|
|
}
|
|
if versions.LessThan(dockerCli.Client().ClientVersion(), "1.31") {
|
|
options.pruneBuildCache = false
|
|
}
|
|
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), confirmationMessage(options)) {
|
|
return nil
|
|
}
|
|
imagePrune := func(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error) {
|
|
return image.RunPrune(dockerCli, options.all, options.filter)
|
|
}
|
|
pruneFuncs := []func(dockerCli command.Cli, filter opts.FilterOpt) (uint64, string, error){
|
|
container.RunPrune,
|
|
network.RunPrune,
|
|
}
|
|
if options.pruneVolumes {
|
|
pruneFuncs = append(pruneFuncs, volume.RunPrune)
|
|
}
|
|
pruneFuncs = append(pruneFuncs, imagePrune)
|
|
if options.pruneBuildCache {
|
|
pruneFuncs = append(pruneFuncs, runBuildCachePrune)
|
|
}
|
|
|
|
var spaceReclaimed uint64
|
|
for _, pruneFn := range pruneFuncs {
|
|
spc, output, err := pruneFn(dockerCli, options.filter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
spaceReclaimed += spc
|
|
if output != "" {
|
|
fmt.Fprintln(dockerCli.Out(), output)
|
|
}
|
|
}
|
|
|
|
fmt.Fprintln(dockerCli.Out(), "Total reclaimed space:", units.HumanSize(float64(spaceReclaimed)))
|
|
|
|
return nil
|
|
}
|
|
|
|
// confirmationMessage constructs a confirmation message that depends on the cli options.
|
|
func confirmationMessage(options pruneOptions) string {
|
|
t := template.Must(template.New("confirmation message").Parse(confirmationTemplate))
|
|
|
|
warnings := []string{
|
|
"all stopped containers",
|
|
"all networks not used by at least one container",
|
|
}
|
|
if options.pruneVolumes {
|
|
warnings = append(warnings, "all volumes not used by at least one container")
|
|
}
|
|
if options.all {
|
|
warnings = append(warnings, "all images without at least one container associated to them")
|
|
} else {
|
|
warnings = append(warnings, "all dangling images")
|
|
}
|
|
if options.pruneBuildCache {
|
|
warnings = append(warnings, "all build cache")
|
|
}
|
|
|
|
var buffer bytes.Buffer
|
|
t.Execute(&buffer, &warnings)
|
|
return buffer.String()
|
|
}
|