Merge pull request #23846 from flouthoc/prune-cleancachemount

prune: add `--build-cache` to support clearing build cache using `CleanCacheMount`
This commit is contained in:
openshift-merge-bot[bot]
2024-09-05 18:47:56 +00:00
committed by GitHub
12 changed files with 87 additions and 9 deletions

View File

@ -46,6 +46,7 @@ func init() {
flags := pruneCmd.Flags() flags := pruneCmd.Flags()
flags.BoolVarP(&pruneOpts.All, "all", "a", false, "Remove all images not in use by containers, not just dangling ones") flags.BoolVarP(&pruneOpts.All, "all", "a", false, "Remove all images not in use by containers, not just dangling ones")
flags.BoolVarP(&pruneOpts.BuildCache, "build-cache", "", false, "Remove persistent build cache created by --mount=type=cache")
flags.BoolVarP(&pruneOpts.External, "external", "", false, "Remove images even when they are used by external containers (e.g., by build containers)") flags.BoolVarP(&pruneOpts.External, "external", "", false, "Remove images even when they are used by external containers (e.g., by build containers)")
flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation") flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation")

View File

@ -17,6 +17,10 @@ The image prune command does not prune cache images that only use layers that ar
Remove dangling images and images that have no associated containers. Remove dangling images and images that have no associated containers.
#### **--build-cache**
Remove persistent build cache create for `--mount=type=cache`.
#### **--external** #### **--external**
Remove images even when they are used by external containers (e.g., build containers). Remove images even when they are used by external containers (e.g., build containers).

View File

@ -118,8 +118,9 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct { query := struct {
All bool `schema:"all"` All bool `schema:"all"`
External bool `schema:"external"` External bool `schema:"external"`
BuildCache bool `schema:"buildcache"`
}{ }{
// override any golang type defaults // override any golang type defaults
} }
@ -157,9 +158,10 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
imageEngine := abi.ImageEngine{Libpod: runtime} imageEngine := abi.ImageEngine{Libpod: runtime}
pruneOptions := entities.ImagePruneOptions{ pruneOptions := entities.ImagePruneOptions{
All: query.All, All: query.All,
External: query.External, External: query.External,
Filter: libpodFilters, Filter: libpodFilters,
BuildCache: query.BuildCache,
} }
imagePruneReports, err := imageEngine.Prune(r.Context(), pruneOptions) imagePruneReports, err := imageEngine.Prune(r.Context(), pruneOptions)
if err != nil { if err != nil {

View File

@ -1129,6 +1129,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: | // description: |
// Remove images even when they are used by external containers (e.g, by build containers) // Remove images even when they are used by external containers (e.g, by build containers)
// - in: query // - in: query
// name: buildcache
// default: false
// type: boolean
// description: |
// Remove persistent build cache created by build instructions such as `--mount=type=cache`.
// - in: query
// name: filters // name: filters
// type: string // type: string
// description: | // description: |

View File

@ -93,6 +93,8 @@ type PruneOptions struct {
All *bool All *bool
// Prune images even when they're used by external containers // Prune images even when they're used by external containers
External *bool External *bool
// Prune persistent build cache
BuildCache *bool
// Filters to apply when pruning images // Filters to apply when pruning images
Filters map[string][]string Filters map[string][]string
} }

View File

@ -47,6 +47,21 @@ func (o *PruneOptions) GetExternal() bool {
return *o.External return *o.External
} }
// WithBuildCache set field BuildCache to given value
func (o *PruneOptions) WithBuildCache(value bool) *PruneOptions {
o.BuildCache = &value
return o
}
// GetBuildCache returns value of field BuildCache
func (o *PruneOptions) GetBuildCache() bool {
if o.BuildCache == nil {
var z bool
return z
}
return *o.BuildCache
}
// WithFilters set field Filters to given value // WithFilters set field Filters to given value
func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions {
o.Filters = value o.Filters = value

View File

@ -249,9 +249,10 @@ type ImageListOptions struct {
} }
type ImagePruneOptions struct { type ImagePruneOptions struct {
All bool `json:"all" schema:"all"` All bool `json:"all" schema:"all"`
External bool `json:"external" schema:"external"` External bool `json:"external" schema:"external"`
Filter []string `json:"filter" schema:"filter"` BuildCache bool `json:"buildcache" schema:"buildcache"`
Filter []string `json:"filter" schema:"filter"`
} }
type ImageTagOptions struct{} type ImageTagOptions struct{}

View File

@ -19,6 +19,7 @@ import (
"time" "time"
bdefine "github.com/containers/buildah/define" bdefine "github.com/containers/buildah/define"
"github.com/containers/buildah/pkg/volumes"
"github.com/containers/common/libimage" "github.com/containers/common/libimage"
"github.com/containers/common/libimage/filter" "github.com/containers/common/libimage/filter"
"github.com/containers/common/pkg/config" "github.com/containers/common/pkg/config"
@ -107,6 +108,13 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
numPreviouslyRemovedImages = numRemovedImages numPreviouslyRemovedImages = numRemovedImages
} }
if opts.BuildCache || opts.All {
// Clean build cache if any
if err := volumes.CleanCacheMount(); err != nil {
return nil, err
}
}
return pruneReports, nil return pruneReports, nil
} }

View File

@ -102,7 +102,7 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
f := strings.Split(filter, "=") f := strings.Split(filter, "=")
filters[f[0]] = f[1:] filters[f[0]] = f[1:]
} }
options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters).WithExternal(opts.External) options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters).WithExternal(opts.External).WithBuildCache(opts.BuildCache)
reports, err := images.Prune(ir.ClientCtx, options) reports, err := images.Prune(ir.ClientCtx, options)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -0,0 +1,4 @@
FROM alpine
RUN mkdir /test
# use option z if selinux is enabled
RUN --mount=type=cache,target=/test,z cat /test/world

View File

@ -0,0 +1,4 @@
FROM alpine
RUN mkdir /test
# use option z if selinux is enabled
RUN --mount=type=cache,target=/test,z echo hello > /test/world

View File

@ -41,6 +41,37 @@ var _ = Describe("Podman build", func() {
Expect(session).Should(ExitCleanly()) Expect(session).Should(ExitCleanly())
}) })
It("podman image prune should clean build cache", Serial, func() {
// try writing something to persistent cache
session := podmanTest.Podman([]string{"build", "-f", "build/buildkit-mount/Containerfilecachewrite"})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
// try reading something from persistent cache
session = podmanTest.Podman([]string{"build", "-f", "build/buildkit-mount/Containerfilecacheread"})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
Expect(session.OutputToString()).To(ContainSubstring("hello"))
// Prune build cache
session = podmanTest.Podman([]string{"image", "prune", "-f", "--build-cache"})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
expectedErr := "open '/test/world': No such file or directory"
// try reading something from persistent cache should fail
session = podmanTest.Podman([]string{"build", "-f", "build/buildkit-mount/Containerfilecacheread"})
session.WaitWithDefaultTimeout()
if IsRemote() {
// In the case of podman remote the error from build is not being propogated to `stderr` instead it appears
// on the `stdout` this could be a potential bug in `remote build` which needs to be fixed and visited.
Expect(session.OutputToString()).To(ContainSubstring(expectedErr))
Expect(session).Should(ExitWithError(1, "exit status 1"))
} else {
Expect(session).Should(ExitWithError(1, expectedErr))
}
})
It("podman build and remove basic alpine with TMPDIR as relative", func() { It("podman build and remove basic alpine with TMPDIR as relative", func() {
// preserve TMPDIR if it was originally set // preserve TMPDIR if it was originally set
if cacheDir, found := os.LookupEnv("TMPDIR"); found { if cacheDir, found := os.LookupEnv("TMPDIR"); found {