From 702524732427ce028277f99f215e1fab297e6001 Mon Sep 17 00:00:00 2001 From: Yong Tang Date: Sat, 4 Feb 2017 09:10:05 -0800 Subject: [PATCH] Add `label` filter for `docker system prune` This fix tries to address the issue raised in 29999 where it was not possible to mask these items (like important non-removable stuff) from `docker system prune`. This fix adds `label` and `label!` field for `--filter` in `system prune`, so that it is possible to selectively prune items like: ``` $ docker container prune --filter label=foo $ docker container prune --filter label!=bar ``` Additional unit tests and integration tests have been added. This fix fixes 29999. Signed-off-by: Yong Tang --- api/server/router/volume/volume_routes.go | 7 +- cli/command/container/prune.go | 2 +- cli/command/image/prune.go | 1 + cli/command/network/prune.go | 2 +- cli/command/prune/prune.go | 2 +- cli/command/utils.go | 32 ++++ cli/command/volume/prune.go | 16 +- cli/config/configfile/file.go | 1 + client/container_prune_test.go | 13 ++ client/image_prune_test.go | 13 ++ client/network_prune_test.go | 13 ++ daemon/prune.go | 40 +++- daemon/volumes.go | 5 +- integration-cli/docker_cli_prune_unix_test.go | 177 ++++++++++++++++++ 14 files changed, 308 insertions(+), 16 deletions(-) diff --git a/api/server/router/volume/volume_routes.go b/api/server/router/volume/volume_routes.go index cfd4618a4d..21d12d283e 100644 --- a/api/server/router/volume/volume_routes.go +++ b/api/server/router/volume/volume_routes.go @@ -72,7 +72,12 @@ func (v *volumeRouter) postVolumesPrune(ctx context.Context, w http.ResponseWrit return err } - pruneReport, err := v.backend.VolumesPrune(filters.Args{}) + pruneFilters, err := filters.FromParam(r.Form.Get("filters")) + if err != nil { + return err + } + + pruneReport, err := v.backend.VolumesPrune(pruneFilters) if err != nil { return err } diff --git a/cli/command/container/prune.go b/cli/command/container/prune.go index ca50e2e159..cf12dc71fe 100644 --- a/cli/command/container/prune.go +++ b/cli/command/container/prune.go @@ -49,7 +49,7 @@ const warning = `WARNING! This will remove all stopped containers. Are you sure you want to continue?` func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) { - pruneFilters := opts.filter.Value() + pruneFilters := command.PruneFilters(dockerCli, opts.filter.Value()) if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) { return diff --git a/cli/command/image/prune.go b/cli/command/image/prune.go index f17aed7410..f86bae39cc 100644 --- a/cli/command/image/prune.go +++ b/cli/command/image/prune.go @@ -58,6 +58,7 @@ Are you sure you want to continue?` func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) { pruneFilters := opts.filter.Value() pruneFilters.Add("dangling", fmt.Sprintf("%v", !opts.all)) + pruneFilters = command.PruneFilters(dockerCli, pruneFilters) warning := danglingWarning if opts.all { diff --git a/cli/command/network/prune.go b/cli/command/network/prune.go index c5c5359926..ec363ab914 100644 --- a/cli/command/network/prune.go +++ b/cli/command/network/prune.go @@ -48,7 +48,7 @@ const warning = `WARNING! This will remove all networks not used by at least one Are you sure you want to continue?` func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (output string, err error) { - pruneFilters := opts.filter.Value() + pruneFilters := command.PruneFilters(dockerCli, opts.filter.Value()) if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) { return diff --git a/cli/command/prune/prune.go b/cli/command/prune/prune.go index 6314718c69..26153ed7c1 100644 --- a/cli/command/prune/prune.go +++ b/cli/command/prune/prune.go @@ -37,7 +37,7 @@ func RunContainerPrune(dockerCli *command.DockerCli, filter opts.FilterOpt) (uin // RunVolumePrune executes a prune command for volumes func RunVolumePrune(dockerCli *command.DockerCli, filter opts.FilterOpt) (uint64, string, error) { - return volume.RunPrune(dockerCli) + return volume.RunPrune(dockerCli, filter) } // RunImagePrune executes a prune command for images diff --git a/cli/command/utils.go b/cli/command/utils.go index 4c52ce61b2..853fe11c78 100644 --- a/cli/command/utils.go +++ b/cli/command/utils.go @@ -9,6 +9,7 @@ import ( "runtime" "strings" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/pkg/system" ) @@ -85,3 +86,34 @@ func PromptForConfirmation(ins *InStream, outs *OutStream, message string) bool answer, _, _ := reader.ReadLine() return strings.ToLower(string(answer)) == "y" } + +// PruneFilters returns consolidated prune filters obtained from config.json and cli +func PruneFilters(dockerCli Cli, pruneFilters filters.Args) filters.Args { + if dockerCli.ConfigFile() == nil { + return pruneFilters + } + for _, f := range dockerCli.ConfigFile().PruneFilters { + parts := strings.SplitN(f, "=", 2) + if len(parts) != 2 { + continue + } + if parts[0] == "label" { + // CLI label filter supersede config.json. + // If CLI label filter conflict with config.json, + // skip adding label! filter in config.json. + if pruneFilters.Include("label!") && pruneFilters.ExactMatch("label!", parts[1]) { + continue + } + } else if parts[0] == "label!" { + // CLI label! filter supersede config.json. + // If CLI label! filter conflict with config.json, + // skip adding label filter in config.json. + if pruneFilters.Include("label") && pruneFilters.ExactMatch("label", parts[1]) { + continue + } + } + pruneFilters.Add(parts[0], parts[1]) + } + + return pruneFilters +} diff --git a/cli/command/volume/prune.go b/cli/command/volume/prune.go index 7e78c66e07..f7d823ffac 100644 --- a/cli/command/volume/prune.go +++ b/cli/command/volume/prune.go @@ -3,21 +3,22 @@ package volume import ( "fmt" - "github.com/docker/docker/api/types/filters" "github.com/docker/docker/cli" "github.com/docker/docker/cli/command" + "github.com/docker/docker/opts" units "github.com/docker/go-units" "github.com/spf13/cobra" "golang.org/x/net/context" ) type pruneOptions struct { - force bool + force bool + filter opts.FilterOpt } // NewPruneCommand returns a new cobra prune command for volumes func NewPruneCommand(dockerCli command.Cli) *cobra.Command { - var opts pruneOptions + opts := pruneOptions{filter: opts.NewFilterOpt()} cmd := &cobra.Command{ Use: "prune [OPTIONS]", @@ -39,6 +40,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command { flags := cmd.Flags() flags.BoolVarP(&opts.force, "force", "f", false, "Do not prompt for confirmation") + flags.Var(&opts.filter, "filter", "Provide filter values (e.g. 'label=