mirror of
https://github.com/containers/podman.git
synced 2025-07-03 17:27:18 +08:00
Clean machine pull cache
Cache cleanups only happen if there is a cache miss, and we need to pull a new image For quay.io/podman/machine-os, we remove all old images from the cache dir. This means we will delete any file that exists in the cache dir; this should be safe to do since the machine pull code should be the only thing touching this cache dir. OCI machine images will always have a different manifest, and won’t be updated with the same manifest, so if the version moves on, there isn’t a reason to keep the old version in the cache, it really doesn’t change. For Fedora (WSL), we use the cache, so we go through the cache dir and remove any old cached images, on a cache miss. We also switch to using ~/.local/share/containers/podman/machine/wsl/cache as the cache dir rather than ~/.local/share/containers/podman/machine/wsl. Both these behaviors existed in v4.9, but are now added back into 5.x. For generic files pulled from a URL or a non-default OCI image, we shouldn’t actually cache, so we delete the pulled file immediately after creating a machine image. This restores the behavior from v4.9. For generic files from a local path, the original file will never be cleaned up Unsure how to test, so: [NO NEW TESTS NEEDED] Signed-off-by: Ashley Cui <acui@redhat.com>
This commit is contained in:
@ -26,7 +26,7 @@ func pullOCITestDisk(finalDir string, vmType define.VMType) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = ociArtPull.GetNoCompress()
|
_, err = ociArtPull.GetNoCompress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type OCIArtifactDisk struct {
|
type OCIArtifactDisk struct {
|
||||||
|
cache bool
|
||||||
cachedCompressedDiskPath *define.VMFile
|
cachedCompressedDiskPath *define.VMFile
|
||||||
name string
|
name string
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@ -91,12 +92,15 @@ func NewOCIArtifactPull(ctx context.Context, dirs *define.MachineDirs, endpoint
|
|||||||
os: machineOS,
|
os: machineOS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache := false
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
endpoint = fmt.Sprintf("docker://%s/%s/%s:%s", artifactRegistry, artifactRepo, artifactImageName, artifactVersion.majorMinor())
|
endpoint = fmt.Sprintf("docker://%s/%s/%s:%s", artifactRegistry, artifactRepo, artifactImageName, artifactVersion.majorMinor())
|
||||||
|
cache = true
|
||||||
}
|
}
|
||||||
|
|
||||||
ociDisk := OCIArtifactDisk{
|
ociDisk := OCIArtifactDisk{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
cache: cache,
|
||||||
dirs: dirs,
|
dirs: dirs,
|
||||||
diskArtifactOpts: &diskOpts,
|
diskArtifactOpts: &diskOpts,
|
||||||
finalPath: finalPath.GetPath(),
|
finalPath: finalPath.GetPath(),
|
||||||
@ -114,36 +118,46 @@ func (o *OCIArtifactDisk) OriginalFileName() (string, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *OCIArtifactDisk) Get() error {
|
func (o *OCIArtifactDisk) Get() error {
|
||||||
if err := o.get(); err != nil {
|
cleanCache, err := o.get()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if cleanCache != nil {
|
||||||
|
defer cleanCache()
|
||||||
|
}
|
||||||
return o.decompress()
|
return o.decompress()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OCIArtifactDisk) GetNoCompress() error {
|
func (o *OCIArtifactDisk) GetNoCompress() (func(), error) {
|
||||||
return o.get()
|
return o.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OCIArtifactDisk) get() error {
|
func (o *OCIArtifactDisk) get() (func(), error) {
|
||||||
|
cleanCache := func() {}
|
||||||
|
|
||||||
destRef, artifactDigest, err := o.getDestArtifact()
|
destRef, artifactDigest, err := o.getDestArtifact()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: the artifactDigest here is the hash of the most recent disk image available
|
// Note: the artifactDigest here is the hash of the most recent disk image available
|
||||||
cachedImagePath, err := o.dirs.ImageCacheDir.AppendToNewVMFile(fmt.Sprintf("%s.%s", artifactDigest.Encoded(), o.vmType.ImageFormat().KindWithCompression()), nil)
|
cachedImagePath, err := o.dirs.ImageCacheDir.AppendToNewVMFile(fmt.Sprintf("%s.%s", artifactDigest.Encoded(), o.vmType.ImageFormat().KindWithCompression()), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we have the latest and greatest disk image
|
// check if we have the latest and greatest disk image
|
||||||
if _, err = os.Stat(cachedImagePath.GetPath()); err != nil {
|
if _, err = os.Stat(cachedImagePath.GetPath()); err != nil {
|
||||||
if !errors.Is(err, os.ErrNotExist) {
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
return fmt.Errorf("unable to access cached image path %q: %q", cachedImagePath.GetPath(), err)
|
return nil, fmt.Errorf("unable to access cached image path %q: %q", cachedImagePath.GetPath(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On cache misses, we clean out the cache
|
||||||
|
cleanCache = o.cleanCache(cachedImagePath.GetPath())
|
||||||
|
|
||||||
// pull the image down to our local filesystem
|
// pull the image down to our local filesystem
|
||||||
if err := o.pull(destRef, artifactDigest); err != nil {
|
if err := o.pull(destRef, artifactDigest); err != nil {
|
||||||
return fmt.Errorf("failed to pull %s: %w", destRef.DockerReference(), err)
|
return nil, fmt.Errorf("failed to pull %s: %w", destRef.DockerReference(), err)
|
||||||
}
|
}
|
||||||
// grab the artifact disk out of the cache and lay
|
// grab the artifact disk out of the cache and lay
|
||||||
// it into our local cache in the format of
|
// it into our local cache in the format of
|
||||||
@ -153,13 +167,46 @@ func (o *OCIArtifactDisk) get() error {
|
|||||||
//
|
//
|
||||||
// i.e. 91d1e51...d28974.qcow2.xz
|
// i.e. 91d1e51...d28974.qcow2.xz
|
||||||
if err := o.unpack(artifactDigest); err != nil {
|
if err := o.unpack(artifactDigest); err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logrus.Debugf("cached image exists and is latest: %s", cachedImagePath.GetPath())
|
logrus.Debugf("cached image exists and is latest: %s", cachedImagePath.GetPath())
|
||||||
o.cachedCompressedDiskPath = cachedImagePath
|
o.cachedCompressedDiskPath = cachedImagePath
|
||||||
}
|
}
|
||||||
|
return cleanCache, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OCIArtifactDisk) cleanCache(cachedImagePath string) func() {
|
||||||
|
// cache miss while using an image that we cache, ie the default image
|
||||||
|
// clean out all old files fron the cache dir
|
||||||
|
if o.cache {
|
||||||
|
files, err := os.ReadDir(o.dirs.ImageCacheDir.GetPath())
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn("failed to clean machine image cache: ", err)
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
for _, file := range files {
|
||||||
|
path := filepath.Join(o.dirs.ImageCacheDir.GetPath(), file.Name())
|
||||||
|
logrus.Debugf("cleaning cached file: %s", path)
|
||||||
|
err := utils.GuardedRemoveAll(path)
|
||||||
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
logrus.Warn("failed to clean machine image cache: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// using an image that we don't cache, ie not the default image
|
||||||
|
// delete image after use and don't cache
|
||||||
|
return func() {
|
||||||
|
logrus.Debugf("cleaning cache: %s", o.dirs.ImageCacheDir.GetPath())
|
||||||
|
err := os.Remove(cachedImagePath)
|
||||||
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
logrus.Warn("failed to clean pulled machine image: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *OCIArtifactDisk) getDestArtifact() (types.ImageReference, digest.Digest, error) {
|
func (o *OCIArtifactDisk) getDestArtifact() (types.ImageReference, digest.Digest, error) {
|
||||||
|
@ -20,7 +20,7 @@ func GetDisk(userInputPath string, dirs *define.MachineDirs, imagePath *define.V
|
|||||||
} else {
|
} else {
|
||||||
if strings.HasPrefix(userInputPath, "http") {
|
if strings.HasPrefix(userInputPath, "http") {
|
||||||
// TODO probably should use tempdir instead of datadir
|
// TODO probably should use tempdir instead of datadir
|
||||||
mydisk, err = stdpull.NewDiskFromURL(userInputPath, imagePath, dirs.DataDir, nil)
|
mydisk, err = stdpull.NewDiskFromURL(userInputPath, imagePath, dirs.DataDir, nil, false)
|
||||||
} else {
|
} else {
|
||||||
mydisk, err = stdpull.NewStdDiskPull(userInputPath, imagePath)
|
mydisk, err = stdpull.NewStdDiskPull(userInputPath, imagePath)
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,10 @@ type DiskFromURL struct {
|
|||||||
u *url2.URL
|
u *url2.URL
|
||||||
finalPath *define.VMFile
|
finalPath *define.VMFile
|
||||||
tempLocation *define.VMFile
|
tempLocation *define.VMFile
|
||||||
|
cache bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDiskFromURL(inputPath string, finalPath *define.VMFile, tempDir *define.VMFile, optionalTempFileName *string) (*DiskFromURL, error) {
|
func NewDiskFromURL(inputPath string, finalPath *define.VMFile, tempDir *define.VMFile, optionalTempFileName *string, cache bool) (*DiskFromURL, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
@ -57,6 +58,7 @@ func NewDiskFromURL(inputPath string, finalPath *define.VMFile, tempDir *define.
|
|||||||
u: u,
|
u: u,
|
||||||
finalPath: finalPath,
|
finalPath: finalPath,
|
||||||
tempLocation: tempLocation,
|
tempLocation: tempLocation,
|
||||||
|
cache: cache,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +67,16 @@ func (d *DiskFromURL) Get() error {
|
|||||||
if err := d.pull(); err != nil {
|
if err := d.pull(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !d.cache {
|
||||||
|
defer func() {
|
||||||
|
if err := utils.GuardedRemoveAll(d.tempLocation.GetPath()); err != nil {
|
||||||
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
|
logrus.Warn("failed to clean machine image cache: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
logrus.Debugf("decompressing (if needed) %s to %s", d.tempLocation.GetPath(), d.finalPath.GetPath())
|
logrus.Debugf("decompressing (if needed) %s to %s", d.tempLocation.GetPath(), d.finalPath.GetPath())
|
||||||
return compression.Decompress(d.tempLocation, d.finalPath.GetPath())
|
return compression.Decompress(d.tempLocation, d.finalPath.GetPath())
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/containers/podman/v5/pkg/machine/shim/diskpull"
|
"github.com/containers/podman/v5/pkg/machine/shim/diskpull"
|
||||||
"github.com/containers/podman/v5/pkg/machine/stdpull"
|
"github.com/containers/podman/v5/pkg/machine/stdpull"
|
||||||
"github.com/containers/podman/v5/pkg/machine/wsl/wutil"
|
"github.com/containers/podman/v5/pkg/machine/wsl/wutil"
|
||||||
|
"github.com/containers/podman/v5/utils"
|
||||||
|
|
||||||
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
||||||
"github.com/containers/podman/v5/pkg/machine"
|
"github.com/containers/podman/v5/pkg/machine"
|
||||||
@ -303,8 +304,7 @@ func (w WSLStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *
|
|||||||
// i.e.v39.0.31-rootfs.tar.xz
|
// i.e.v39.0.31-rootfs.tar.xz
|
||||||
versionedBase := fmt.Sprintf("%s-%s", downloadVersion, filepath.Base(downloadURL.Path))
|
versionedBase := fmt.Sprintf("%s-%s", downloadVersion, filepath.Base(downloadURL.Path))
|
||||||
|
|
||||||
// TODO we need a mechanism for "flushing" old cache files
|
cachedFile, err := dirs.ImageCacheDir.AppendToNewVMFile(versionedBase, nil)
|
||||||
cachedFile, err := dirs.DataDir.AppendToNewVMFile(versionedBase, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -313,13 +313,31 @@ func (w WSLStubber) GetDisk(userInputPath string, dirs *define.MachineDirs, mc *
|
|||||||
if _, err = os.Stat(cachedFile.GetPath()); err == nil {
|
if _, err = os.Stat(cachedFile.GetPath()); err == nil {
|
||||||
logrus.Debugf("%q already exists locally", cachedFile.GetPath())
|
logrus.Debugf("%q already exists locally", cachedFile.GetPath())
|
||||||
myDisk, err = stdpull.NewStdDiskPull(cachedFile.GetPath(), mc.ImagePath)
|
myDisk, err = stdpull.NewStdDiskPull(cachedFile.GetPath(), mc.ImagePath)
|
||||||
} else {
|
|
||||||
// no cached file
|
|
||||||
myDisk, err = stdpull.NewDiskFromURL(downloadURL.String(), mc.ImagePath, dirs.DataDir, &versionedBase)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
files, err := os.ReadDir(dirs.ImageCacheDir.GetPath())
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn("failed to clean machine image cache: ", err)
|
||||||
|
} else {
|
||||||
|
defer func() {
|
||||||
|
for _, file := range files {
|
||||||
|
path := filepath.Join(dirs.ImageCacheDir.GetPath(), file.Name())
|
||||||
|
logrus.Debugf("cleaning cached image: %s", path)
|
||||||
|
err := utils.GuardedRemoveAll(path)
|
||||||
|
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||||
|
logrus.Warn("failed to clean machine image cache: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
myDisk, err = stdpull.NewDiskFromURL(downloadURL.String(), mc.ImagePath, dirs.ImageCacheDir, &versionedBase, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
// up until now, nothing has really happened
|
// up until now, nothing has really happened
|
||||||
// pull if needed and decompress to image location
|
// pull if needed and decompress to image location
|
||||||
return myDisk.Get()
|
return myDisk.Get()
|
||||||
|
Reference in New Issue
Block a user