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 <yong.tang.github@outlook.com>
This commit is contained in:
Yong Tang 2017-02-04 09:10:05 -08:00
parent fbddea6cac
commit 41471dfe1c
7 changed files with 47 additions and 9 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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=<label>')")
return cmd
}
@ -47,11 +49,13 @@ const warning = `WARNING! This will remove all volumes not used by at least one
Are you sure you want to continue?`
func runPrune(dockerCli command.Cli, opts pruneOptions) (spaceReclaimed uint64, output string, err error) {
pruneFilters := command.PruneFilters(dockerCli, opts.filter.Value())
if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
return
}
report, err := dockerCli.Client().VolumesPrune(context.Background(), filters.Args{})
report, err := dockerCli.Client().VolumesPrune(context.Background(), pruneFilters)
if err != nil {
return
}
@ -69,6 +73,6 @@ func runPrune(dockerCli command.Cli, opts pruneOptions) (spaceReclaimed uint64,
// RunPrune calls the Volume Prune API
// This returns the amount of space reclaimed and a detailed output string
func RunPrune(dockerCli *command.DockerCli) (uint64, string, error) {
return runPrune(dockerCli, pruneOptions{force: true})
func RunPrune(dockerCli *command.DockerCli, filter opts.FilterOpt) (uint64, string, error) {
return runPrune(dockerCli, pruneOptions{force: true, filter: filter})
}

View File

@ -39,6 +39,7 @@ type ConfigFile struct {
TasksFormat string `json:"tasksFormat,omitempty"`
SecretFormat string `json:"secretFormat,omitempty"`
NodesFormat string `json:"nodesFormat,omitempty"`
PruneFilters []string `json:"pruneFilters,omitempty"`
}
// LegacyLoadFromReader reads the non-nested configuration data given and sets up the