DockerCLI/cli/command/system/prune.go

152 lines
4.8 KiB
Go
Raw Normal View History

package system
import (
"bytes"
"context"
"fmt"
"sort"
"text/template"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/builder"
"github.com/docker/cli/cli/command/completion"
"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"
"github.com/docker/go-units"
"github.com/fvbommel/sortorder"
"github.com/spf13/cobra"
)
type pruneOptions struct {
force bool
all bool
pruneVolumes bool
pruneBuildCache 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()}
cmd := &cobra.Command{
Use: "prune [OPTIONS]",
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"},
ValidArgsFunction: completion.NoComplete,
}
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 anonymous 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 := .warnings }}
- {{ $warning }}
{{- end }}
{{if .filters}}
Items to be pruned will be filtered with:
{{- range $_, $filters := .filters }}
- {{ $filters }}
{{- end }}
{{end}}
Are you sure you want to continue?`
func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) error {
Error if "until" filter is combined with "--volumes" on system prune 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>
2017-07-08 19:11:57 -04:00
// TODO version this once "until" filter is supported for volumes
if options.pruneVolumes && options.filter.Value().Contains("until") {
Error if "until" filter is combined with "--volumes" on system prune 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>
2017-07-08 19:11:57 -04:00
return fmt.Errorf(`ERROR: The "until" filter is not supported with "--volumes"`)
}
Fix system prune warning missing filters from config-file The warning, printed before runing docker system prune was missing any filter that was set in the configuration file. In addition, the warning prefixes the filters with `label=`, which is no longer accurate, now that the prune command also supports "until" as a filter. Before this change, only the filters set on the command-line were shown, and any filter set in the configuration file was missing; ``` mkdir -p ./test-config echo '{"pruneFilters": ["label!=never=remove-me", "label=remove=me"]}' > test-config/config.json docker --config=./test-config system prune --filter until=24h --filter label=hello-world --filter label!=foo=bar --filter label=bar=baz WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all dangling build cache - Elements to be pruned will be filtered with: - label={"label":{"bar=baz":true,"hello-world":true},"label!":{"foo=bar":true},"until":{"24h":true}} Are you sure you want to continue? [y/N] ``` With this patch applied, both options from the commandline and options set in the configuration file are shown; ``` mkdir -p ./test-config echo '{"pruneFilters": ["label!=never=remove-me", "label=remove=me"]}' > test-config/config.json docker --config=./test-config system prune --filter until=24h --filter label=hello-world --filter label!=foo=bar --filter label=bar=baz WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all dangling build cache - Elements to be pruned will be filtered with: - filter={"label":{"bar=baz":true,"hello-world":true,"remove=me":true},"label!":{"foo=bar":true,"never=remove-me":true},"until":{"24h":true}} ``` Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-11-17 10:00:37 -05:00
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), confirmationMessage(dockerCli, options)) {
return nil
}
pruneFuncs := []func(ctx context.Context, dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error){
container.RunPrune,
network.RunPrune,
}
if options.pruneVolumes {
pruneFuncs = append(pruneFuncs, volume.RunPrune)
}
pruneFuncs = append(pruneFuncs, image.RunPrune)
if options.pruneBuildCache {
pruneFuncs = append(pruneFuncs, builder.CachePrune)
}
var spaceReclaimed uint64
for _, pruneFn := range pruneFuncs {
spc, output, err := pruneFn(ctx, dockerCli, options.all, 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.
Fix system prune warning missing filters from config-file The warning, printed before runing docker system prune was missing any filter that was set in the configuration file. In addition, the warning prefixes the filters with `label=`, which is no longer accurate, now that the prune command also supports "until" as a filter. Before this change, only the filters set on the command-line were shown, and any filter set in the configuration file was missing; ``` mkdir -p ./test-config echo '{"pruneFilters": ["label!=never=remove-me", "label=remove=me"]}' > test-config/config.json docker --config=./test-config system prune --filter until=24h --filter label=hello-world --filter label!=foo=bar --filter label=bar=baz WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all dangling build cache - Elements to be pruned will be filtered with: - label={"label":{"bar=baz":true,"hello-world":true},"label!":{"foo=bar":true},"until":{"24h":true}} Are you sure you want to continue? [y/N] ``` With this patch applied, both options from the commandline and options set in the configuration file are shown; ``` mkdir -p ./test-config echo '{"pruneFilters": ["label!=never=remove-me", "label=remove=me"]}' > test-config/config.json docker --config=./test-config system prune --filter until=24h --filter label=hello-world --filter label!=foo=bar --filter label=bar=baz WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all dangling build cache - Elements to be pruned will be filtered with: - filter={"label":{"bar=baz":true,"hello-world":true,"remove=me":true},"label!":{"foo=bar":true,"never=remove-me":true},"until":{"24h":true}} ``` Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-11-17 10:00:37 -05:00
func confirmationMessage(dockerCli command.Cli, 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 anonymous 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 {
if options.all {
warnings = append(warnings, "all build cache")
} else {
warnings = append(warnings, "unused build cache")
}
}
var filters []string
Fix system prune warning missing filters from config-file The warning, printed before runing docker system prune was missing any filter that was set in the configuration file. In addition, the warning prefixes the filters with `label=`, which is no longer accurate, now that the prune command also supports "until" as a filter. Before this change, only the filters set on the command-line were shown, and any filter set in the configuration file was missing; ``` mkdir -p ./test-config echo '{"pruneFilters": ["label!=never=remove-me", "label=remove=me"]}' > test-config/config.json docker --config=./test-config system prune --filter until=24h --filter label=hello-world --filter label!=foo=bar --filter label=bar=baz WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all dangling build cache - Elements to be pruned will be filtered with: - label={"label":{"bar=baz":true,"hello-world":true},"label!":{"foo=bar":true},"until":{"24h":true}} Are you sure you want to continue? [y/N] ``` With this patch applied, both options from the commandline and options set in the configuration file are shown; ``` mkdir -p ./test-config echo '{"pruneFilters": ["label!=never=remove-me", "label=remove=me"]}' > test-config/config.json docker --config=./test-config system prune --filter until=24h --filter label=hello-world --filter label!=foo=bar --filter label=bar=baz WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all dangling build cache - Elements to be pruned will be filtered with: - filter={"label":{"bar=baz":true,"hello-world":true,"remove=me":true},"label!":{"foo=bar":true,"never=remove-me":true},"until":{"24h":true}} ``` Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-11-17 10:00:37 -05:00
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
if pruneFilters.Len() > 0 {
// TODO remove fixed list of filters, and print all filters instead,
// because the list of filters that is supported by the engine may evolve over time.
for _, name := range []string{"label", "label!", "until"} {
for _, v := range pruneFilters.Get(name) {
filters = append(filters, name+"="+v)
}
Fix system prune warning missing filters from config-file The warning, printed before runing docker system prune was missing any filter that was set in the configuration file. In addition, the warning prefixes the filters with `label=`, which is no longer accurate, now that the prune command also supports "until" as a filter. Before this change, only the filters set on the command-line were shown, and any filter set in the configuration file was missing; ``` mkdir -p ./test-config echo '{"pruneFilters": ["label!=never=remove-me", "label=remove=me"]}' > test-config/config.json docker --config=./test-config system prune --filter until=24h --filter label=hello-world --filter label!=foo=bar --filter label=bar=baz WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all dangling build cache - Elements to be pruned will be filtered with: - label={"label":{"bar=baz":true,"hello-world":true},"label!":{"foo=bar":true},"until":{"24h":true}} Are you sure you want to continue? [y/N] ``` With this patch applied, both options from the commandline and options set in the configuration file are shown; ``` mkdir -p ./test-config echo '{"pruneFilters": ["label!=never=remove-me", "label=remove=me"]}' > test-config/config.json docker --config=./test-config system prune --filter until=24h --filter label=hello-world --filter label!=foo=bar --filter label=bar=baz WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all dangling build cache - Elements to be pruned will be filtered with: - filter={"label":{"bar=baz":true,"hello-world":true,"remove=me":true},"label!":{"foo=bar":true,"never=remove-me":true},"until":{"24h":true}} ``` Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2018-11-17 10:00:37 -05:00
}
sort.Slice(filters, func(i, j int) bool {
return sortorder.NaturalLess(filters[i], filters[j])
})
}
var buffer bytes.Buffer
t.Execute(&buffer, map[string][]string{"warnings": warnings, "filters": filters})
return buffer.String()
}