-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
prune.go
159 lines (143 loc) · 4.99 KB
/
prune.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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/docker/errdefs"
"github.com/docker/go-units"
"github.com/fvbommel/sortorder"
"github.com/pkg/errors"
"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 {
// TODO version this once "until" filter is supported for volumes
if options.pruneVolumes && options.filter.Value().Contains("until") {
return errors.New(`ERROR: The "until" filter is not supported with "--volumes"`)
}
if !options.force {
r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), confirmationMessage(dockerCli, options))
if err != nil {
return err
}
if !r {
return errdefs.Cancelled(errors.New("system prune has been cancelled"))
}
}
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.
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
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)
}
}
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()
}