diff --git a/command/container/prune.go b/command/container/prune.go index ca50e2e159..cf12dc71fe 100644 --- a/command/container/prune.go +++ b/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/command/image/prune.go b/command/image/prune.go index f17aed7410..f86bae39cc 100644 --- a/command/image/prune.go +++ b/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/command/network/prune.go b/command/network/prune.go index c5c5359926..ec363ab914 100644 --- a/command/network/prune.go +++ b/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/command/prune/prune.go b/command/prune/prune.go index 6314718c69..26153ed7c1 100644 --- a/command/prune/prune.go +++ b/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/command/utils.go b/command/utils.go index 4c52ce61b2..853fe11c78 100644 --- a/command/utils.go +++ b/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/command/volume/prune.go b/command/volume/prune.go index 7e78c66e07..f7d823ffac 100644 --- a/command/volume/prune.go +++ b/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=