Skip to content

Commit 901f621

Browse files
committed
prune: support clearing build cache using CleanCacheMount
`podman builder prune` and `podman image prune` should also support cleaning build cache using buildah's public `CleanCacheMount` API. Reference: https://docs.docker.com/reference/cli/docker/builder/prune/ Context: containers#15612 (reply in thread) Context: containers/buildah#4490 Signed-off-by: flouthoc <[email protected]>
1 parent efd4971 commit 901f621

File tree

12 files changed

+87
-9
lines changed

12 files changed

+87
-9
lines changed

cmd/podman/images/prune.go

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func init() {
4646

4747
flags := pruneCmd.Flags()
4848
flags.BoolVarP(&pruneOpts.All, "all", "a", false, "Remove all images not in use by containers, not just dangling ones")
49+
flags.BoolVarP(&pruneOpts.BuildCache, "build-cache", "", false, "Remove persistent build cache created by --mount=type=cache")
4950
flags.BoolVarP(&pruneOpts.External, "external", "", false, "Remove images even when they are used by external containers (e.g., by build containers)")
5051
flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation")
5152

docs/source/markdown/podman-image-prune.1.md

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ The image prune command does not prune cache images that only use layers that ar
1717

1818
Remove dangling images and images that have no associated containers.
1919

20+
#### **--build-cache**
21+
22+
Remove persistent build cache create for `--mount=type=cache`.
23+
2024
#### **--external**
2125

2226
Remove images even when they are used by external containers (e.g., build containers).

pkg/api/handlers/libpod/images.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
118118
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
119119
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
120120
query := struct {
121-
All bool `schema:"all"`
122-
External bool `schema:"external"`
121+
All bool `schema:"all"`
122+
External bool `schema:"external"`
123+
BuildCache bool `schema:"buildcache"`
123124
}{
124125
// override any golang type defaults
125126
}
@@ -157,9 +158,10 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
157158
imageEngine := abi.ImageEngine{Libpod: runtime}
158159

159160
pruneOptions := entities.ImagePruneOptions{
160-
All: query.All,
161-
External: query.External,
162-
Filter: libpodFilters,
161+
All: query.All,
162+
External: query.External,
163+
Filter: libpodFilters,
164+
BuildCache: query.BuildCache,
163165
}
164166
imagePruneReports, err := imageEngine.Prune(r.Context(), pruneOptions)
165167
if err != nil {

pkg/api/server/register_images.go

+6
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
11291129
// description: |
11301130
// Remove images even when they are used by external containers (e.g, by build containers)
11311131
// - in: query
1132+
// name: buildcache
1133+
// default: false
1134+
// type: boolean
1135+
// description: |
1136+
// Remove persistent build cache created by build instructions such as `--mount=type=cache`.
1137+
// - in: query
11321138
// name: filters
11331139
// type: string
11341140
// description: |

pkg/bindings/images/types.go

+2
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ type PruneOptions struct {
9393
All *bool
9494
// Prune images even when they're used by external containers
9595
External *bool
96+
// Prune persistent build cache
97+
BuildCache *bool
9698
// Filters to apply when pruning images
9799
Filters map[string][]string
98100
}

pkg/bindings/images/types_prune_options.go

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/domain/entities/images.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,10 @@ type ImageListOptions struct {
249249
}
250250

251251
type ImagePruneOptions struct {
252-
All bool `json:"all" schema:"all"`
253-
External bool `json:"external" schema:"external"`
254-
Filter []string `json:"filter" schema:"filter"`
252+
All bool `json:"all" schema:"all"`
253+
External bool `json:"external" schema:"external"`
254+
BuildCache bool `json:"buildcache" schema:"buildcache"`
255+
Filter []string `json:"filter" schema:"filter"`
255256
}
256257

257258
type ImageTagOptions struct{}

pkg/domain/infra/abi/images.go

+8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"time"
2020

2121
bdefine "github.com/containers/buildah/define"
22+
"github.com/containers/buildah/pkg/volumes"
2223
"github.com/containers/common/libimage"
2324
"github.com/containers/common/libimage/filter"
2425
"github.com/containers/common/pkg/config"
@@ -107,6 +108,13 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
107108
numPreviouslyRemovedImages = numRemovedImages
108109
}
109110

111+
if opts.BuildCache || opts.All {
112+
// Clean build cache if any
113+
if err := volumes.CleanCacheMount(); err != nil {
114+
return nil, err
115+
}
116+
}
117+
110118
return pruneReports, nil
111119
}
112120

pkg/domain/infra/tunnel/images.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
102102
f := strings.Split(filter, "=")
103103
filters[f[0]] = f[1:]
104104
}
105-
options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters).WithExternal(opts.External)
105+
options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters).WithExternal(opts.External).WithBuildCache(opts.BuildCache)
106106
reports, err := images.Prune(ir.ClientCtx, options)
107107
if err != nil {
108108
return nil, err
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM alpine
2+
RUN mkdir /test
3+
# use option z if selinux is enabled
4+
RUN --mount=type=cache,target=/test,z cat /test/world
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
FROM alpine
2+
RUN mkdir /test
3+
# use option z if selinux is enabled
4+
RUN --mount=type=cache,target=/test,z echo hello > /test/world

test/e2e/build_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,37 @@ var _ = Describe("Podman build", func() {
4141
Expect(session).Should(ExitCleanly())
4242
})
4343

44+
It("podman image prune should clean build cache", Serial, func() {
45+
// try writing something to persistent cache
46+
session := podmanTest.Podman([]string{"build", "-f", "build/buildkit-mount/Containerfilecachewrite"})
47+
session.WaitWithDefaultTimeout()
48+
Expect(session).Should(ExitCleanly())
49+
50+
// try reading something from persistent cache
51+
session = podmanTest.Podman([]string{"build", "-f", "build/buildkit-mount/Containerfilecacheread"})
52+
session.WaitWithDefaultTimeout()
53+
Expect(session).Should(ExitCleanly())
54+
Expect(session.OutputToString()).To(ContainSubstring("hello"))
55+
56+
// Prune build cache
57+
session = podmanTest.Podman([]string{"image", "prune", "-f", "--build-cache"})
58+
session.WaitWithDefaultTimeout()
59+
Expect(session).Should(ExitCleanly())
60+
61+
expectedErr := "open '/test/world': No such file or directory"
62+
// try reading something from persistent cache should fail
63+
session = podmanTest.Podman([]string{"build", "-f", "build/buildkit-mount/Containerfilecacheread"})
64+
session.WaitWithDefaultTimeout()
65+
if IsRemote() {
66+
// In the case of podman remote the error from build is not being propogated to `stderr` instead it appears
67+
// on the `stdout` this could be a potential bug in `remote build` which needs to be fixed and visited.
68+
Expect(session.OutputToString()).To(ContainSubstring(expectedErr))
69+
Expect(session).Should(ExitWithError(1, "exit status 1"))
70+
} else {
71+
Expect(session).Should(ExitWithError(1, expectedErr))
72+
}
73+
})
74+
4475
It("podman build and remove basic alpine with TMPDIR as relative", func() {
4576
// preserve TMPDIR if it was originally set
4677
if cacheDir, found := os.LookupEnv("TMPDIR"); found {

0 commit comments

Comments
 (0)