[19.03 backport] daemon/config: fix filter type in BuildKit GC config
| ... | ... |
@@ -36,6 +36,15 @@ func NewArgs(initialArgs ...KeyValuePair) Args {
|
| 36 | 36 |
return args |
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 |
+// Keys returns all the keys in list of Args |
|
| 40 |
+func (args Args) Keys() []string {
|
|
| 41 |
+ keys := make([]string, 0, len(args.fields)) |
|
| 42 |
+ for k := range args.fields {
|
|
| 43 |
+ keys = append(keys, k) |
|
| 44 |
+ } |
|
| 45 |
+ return keys |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 39 | 48 |
// MarshalJSON returns a JSON byte representation of the Args |
| 40 | 49 |
func (args Args) MarshalJSON() ([]byte, error) {
|
| 41 | 50 |
if len(args.fields) == 0 {
|
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"github.com/containerd/containerd/content/local" |
| 9 | 9 |
"github.com/containerd/containerd/platforms" |
| 10 | 10 |
"github.com/docker/docker/api/types" |
| 11 |
+ "github.com/docker/docker/api/types/filters" |
|
| 11 | 12 |
"github.com/docker/docker/builder/builder-next/adapters/containerimage" |
| 12 | 13 |
"github.com/docker/docker/builder/builder-next/adapters/localinlinecache" |
| 13 | 14 |
"github.com/docker/docker/builder/builder-next/adapters/snapshot" |
| ... | ... |
@@ -232,7 +233,7 @@ func getGCPolicy(conf config.BuilderConfig, root string) ([]client.PruneInfo, er |
| 232 | 232 |
gcPolicy[i], err = toBuildkitPruneInfo(types.BuildCachePruneOptions{
|
| 233 | 233 |
All: p.All, |
| 234 | 234 |
KeepStorage: b, |
| 235 |
- Filters: p.Filter, |
|
| 235 |
+ Filters: filters.Args(p.Filter), |
|
| 236 | 236 |
}) |
| 237 | 237 |
if err != nil {
|
| 238 | 238 |
return nil, err |
| ... | ... |
@@ -1,12 +1,57 @@ |
| 1 | 1 |
package config |
| 2 | 2 |
|
| 3 |
-import "github.com/docker/docker/api/types/filters" |
|
| 3 |
+import ( |
|
| 4 |
+ "encoding/json" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "sort" |
|
| 7 |
+ "strings" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/docker/docker/api/types/filters" |
|
| 10 |
+) |
|
| 4 | 11 |
|
| 5 | 12 |
// BuilderGCRule represents a GC rule for buildkit cache |
| 6 | 13 |
type BuilderGCRule struct {
|
| 7 |
- All bool `json:",omitempty"` |
|
| 8 |
- Filter filters.Args `json:",omitempty"` |
|
| 9 |
- KeepStorage string `json:",omitempty"` |
|
| 14 |
+ All bool `json:",omitempty"` |
|
| 15 |
+ Filter BuilderGCFilter `json:",omitempty"` |
|
| 16 |
+ KeepStorage string `json:",omitempty"` |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+// BuilderGCFilter contains garbage-collection filter rules for a BuildKit builder |
|
| 20 |
+type BuilderGCFilter filters.Args |
|
| 21 |
+ |
|
| 22 |
+// MarshalJSON returns a JSON byte representation of the BuilderGCFilter |
|
| 23 |
+func (x *BuilderGCFilter) MarshalJSON() ([]byte, error) {
|
|
| 24 |
+ f := filters.Args(*x) |
|
| 25 |
+ keys := f.Keys() |
|
| 26 |
+ sort.Strings(keys) |
|
| 27 |
+ arr := make([]string, 0, len(keys)) |
|
| 28 |
+ for _, k := range keys {
|
|
| 29 |
+ values := f.Get(k) |
|
| 30 |
+ for _, v := range values {
|
|
| 31 |
+ arr = append(arr, fmt.Sprintf("%s=%s", k, v))
|
|
| 32 |
+ } |
|
| 33 |
+ } |
|
| 34 |
+ return json.Marshal(arr) |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+// UnmarshalJSON fills the BuilderGCFilter values structure from JSON input |
|
| 38 |
+func (x *BuilderGCFilter) UnmarshalJSON(data []byte) error {
|
|
| 39 |
+ var arr []string |
|
| 40 |
+ f := filters.NewArgs() |
|
| 41 |
+ if err := json.Unmarshal(data, &arr); err != nil {
|
|
| 42 |
+ // backwards compat for deprecated buggy form |
|
| 43 |
+ err := json.Unmarshal(data, &f) |
|
| 44 |
+ *x = BuilderGCFilter(f) |
|
| 45 |
+ return err |
|
| 46 |
+ } |
|
| 47 |
+ for _, s := range arr {
|
|
| 48 |
+ fields := strings.SplitN(s, "=", 2) |
|
| 49 |
+ name := strings.ToLower(strings.TrimSpace(fields[0])) |
|
| 50 |
+ value := strings.TrimSpace(fields[1]) |
|
| 51 |
+ f.Add(name, value) |
|
| 52 |
+ } |
|
| 53 |
+ *x = BuilderGCFilter(f) |
|
| 54 |
+ return nil |
|
| 10 | 55 |
} |
| 11 | 56 |
|
| 12 | 57 |
// BuilderGCConfig contains GC config for a buildkit builder |
| 13 | 58 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,44 @@ |
| 0 |
+package config |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/docker/docker/api/types/filters" |
|
| 6 |
+ "github.com/google/go-cmp/cmp" |
|
| 7 |
+ "gotest.tools/assert" |
|
| 8 |
+ "gotest.tools/fs" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+func TestBuilderGC(t *testing.T) {
|
|
| 12 |
+ tempFile := fs.NewFile(t, "config", fs.WithContent(`{
|
|
| 13 |
+ "builder": {
|
|
| 14 |
+ "gc": {
|
|
| 15 |
+ "enabled": true, |
|
| 16 |
+ "policy": [ |
|
| 17 |
+ {"keepStorage": "10GB", "filter": ["unused-for=2200h"]},
|
|
| 18 |
+ {"keepStorage": "50GB", "filter": {"unused-for": {"3300h": true}}},
|
|
| 19 |
+ {"keepStorage": "100GB", "all": true}
|
|
| 20 |
+ ] |
|
| 21 |
+ } |
|
| 22 |
+ } |
|
| 23 |
+}`)) |
|
| 24 |
+ defer tempFile.Remove() |
|
| 25 |
+ configFile := tempFile.Path() |
|
| 26 |
+ |
|
| 27 |
+ cfg, err := MergeDaemonConfigurations(&Config{}, nil, configFile)
|
|
| 28 |
+ assert.NilError(t, err) |
|
| 29 |
+ assert.Assert(t, cfg.Builder.GC.Enabled) |
|
| 30 |
+ f1 := filters.NewArgs() |
|
| 31 |
+ f1.Add("unused-for", "2200h")
|
|
| 32 |
+ f2 := filters.NewArgs() |
|
| 33 |
+ f2.Add("unused-for", "3300h")
|
|
| 34 |
+ expectedPolicy := []BuilderGCRule{
|
|
| 35 |
+ {KeepStorage: "10GB", Filter: BuilderGCFilter(f1)},
|
|
| 36 |
+ {KeepStorage: "50GB", Filter: BuilderGCFilter(f2)}, /* parsed from deprecated form */
|
|
| 37 |
+ {KeepStorage: "100GB", All: true},
|
|
| 38 |
+ } |
|
| 39 |
+ assert.DeepEqual(t, cfg.Builder.GC.Policy, expectedPolicy, cmp.AllowUnexported(BuilderGCFilter{}))
|
|
| 40 |
+ // double check to please the skeptics |
|
| 41 |
+ assert.Assert(t, filters.Args(cfg.Builder.GC.Policy[0].Filter).UniqueExactMatch("unused-for", "2200h"))
|
|
| 42 |
+ assert.Assert(t, filters.Args(cfg.Builder.GC.Policy[1].Filter).UniqueExactMatch("unused-for", "3300h"))
|
|
| 43 |
+} |