Skip to content

Commit abc4faf

Browse files
authored
Merge pull request #2569 from norio-nomura/prune-objects-that-are-not-used-by-any-instances-or-templates
`limactl prune`: add `--keep-referred` option to keep objects that are referred by some instances or templates
2 parents a694081 + 779a241 commit abc4faf

File tree

2 files changed

+145
-7
lines changed

2 files changed

+145
-7
lines changed

cmd/limactl/prune.go

+92-6
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package main
22

33
import (
44
"os"
5-
"path/filepath"
65

6+
"github.com/lima-vm/lima/pkg/downloader"
7+
"github.com/lima-vm/lima/pkg/limayaml"
8+
"github.com/lima-vm/lima/pkg/store"
9+
"github.com/lima-vm/lima/pkg/templatestore"
710
"github.com/sirupsen/logrus"
811
"github.com/spf13/cobra"
912
)
@@ -17,15 +20,98 @@ func newPruneCommand() *cobra.Command {
1720
ValidArgsFunction: cobra.NoFileCompletions,
1821
GroupID: advancedCommand,
1922
}
23+
pruneCommand.Flags().Bool("keep-referred", false, "Keep objects that are referred by some instances or templates")
2024
return pruneCommand
2125
}
2226

23-
func pruneAction(_ *cobra.Command, _ []string) error {
24-
ucd, err := os.UserCacheDir()
27+
func pruneAction(cmd *cobra.Command, _ []string) error {
28+
keepReferred, err := cmd.Flags().GetBool("keep-referred")
2529
if err != nil {
2630
return err
2731
}
28-
cacheDir := filepath.Join(ucd, "lima")
29-
logrus.Infof("Pruning %q", cacheDir)
30-
return os.RemoveAll(cacheDir)
32+
opt := downloader.WithCache()
33+
if !keepReferred {
34+
return downloader.RemoveAllCacheDir(opt)
35+
}
36+
37+
// Prune downloads that are not used by any instances or templates
38+
cacheEntries, err := downloader.CacheEntries(opt)
39+
if err != nil {
40+
return err
41+
}
42+
knownLocations, err := knownLocations()
43+
if err != nil {
44+
return err
45+
}
46+
for cacheKey, cachePath := range cacheEntries {
47+
if file, exists := knownLocations[cacheKey]; exists {
48+
logrus.Debugf("Keep %q caching %q", cacheKey, file.Location)
49+
} else {
50+
logrus.Debug("Deleting ", cacheKey)
51+
if err := os.RemoveAll(cachePath); err != nil {
52+
logrus.Warnf("Failed to delete %q: %v", cacheKey, err)
53+
return err
54+
}
55+
}
56+
}
57+
return nil
58+
}
59+
60+
func knownLocations() (map[string]limayaml.File, error) {
61+
locations := make(map[string]limayaml.File)
62+
63+
// Collect locations from instances
64+
instances, err := store.Instances()
65+
if err != nil {
66+
return nil, err
67+
}
68+
for _, instanceName := range instances {
69+
instance, err := store.Inspect(instanceName)
70+
if err != nil {
71+
return nil, err
72+
}
73+
for k, v := range locationsFromLimaYAML(instance.Config) {
74+
locations[k] = v
75+
}
76+
}
77+
78+
// Collect locations from templates
79+
templates, err := templatestore.Templates()
80+
if err != nil {
81+
return nil, err
82+
}
83+
for _, t := range templates {
84+
b, err := templatestore.Read(t.Name)
85+
if err != nil {
86+
return nil, err
87+
}
88+
y, err := limayaml.Load(b, t.Name)
89+
if err != nil {
90+
return nil, err
91+
}
92+
for k, v := range locationsFromLimaYAML(y) {
93+
locations[k] = v
94+
}
95+
}
96+
return locations, nil
97+
}
98+
99+
func locationsFromLimaYAML(y *limayaml.LimaYAML) map[string]limayaml.File {
100+
locations := make(map[string]limayaml.File)
101+
for _, f := range y.Images {
102+
locations[downloader.CacheKey(f.Location)] = f.File
103+
if f.Kernel != nil {
104+
locations[downloader.CacheKey(f.Kernel.Location)] = f.Kernel.File
105+
}
106+
if f.Initrd != nil {
107+
locations[downloader.CacheKey(f.Initrd.Location)] = *f.Initrd
108+
}
109+
}
110+
for _, f := range y.Containerd.Archives {
111+
locations[downloader.CacheKey(f.Location)] = f
112+
}
113+
for _, f := range y.Firmware.Images {
114+
locations[downloader.CacheKey(f.Location)] = f.File
115+
}
116+
return locations
31117
}

pkg/downloader/downloader.go

+53-1
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ func Cached(remote string, opts ...Opt) (*Result, error) {
341341
// - "time" file contains the time (Last-Modified header)
342342
// - "type" file contains the type (Content-Type header)
343343
func cacheDirectoryPath(cacheDir, remote string) string {
344-
return filepath.Join(cacheDir, "download", "by-url-sha256", fmt.Sprintf("%x", sha256.Sum256([]byte(remote))))
344+
return filepath.Join(cacheDir, "download", "by-url-sha256", CacheKey(remote))
345345
}
346346

347347
// cacheDigestPath returns the cache digest file path.
@@ -601,3 +601,55 @@ func downloadHTTP(ctx context.Context, localPath, lastModified, contentType, url
601601
}
602602
return os.Rename(localPathTmp, localPath)
603603
}
604+
605+
// CacheEntries returns a map of cache entries.
606+
// The key is the SHA256 of the URL.
607+
// The value is the path to the cache entry.
608+
func CacheEntries(opt ...Opt) (map[string]string, error) {
609+
entries := make(map[string]string)
610+
var o options
611+
for _, f := range opt {
612+
if err := f(&o); err != nil {
613+
return nil, err
614+
}
615+
}
616+
if o.cacheDir == "" {
617+
return entries, nil
618+
}
619+
downloadDir := filepath.Join(o.cacheDir, "download", "by-url-sha256")
620+
_, err := os.Stat(downloadDir)
621+
if err != nil {
622+
if errors.Is(err, os.ErrNotExist) {
623+
return entries, nil
624+
}
625+
return nil, err
626+
}
627+
cacheEntries, err := os.ReadDir(downloadDir)
628+
if err != nil {
629+
return nil, err
630+
}
631+
for _, entry := range cacheEntries {
632+
entries[entry.Name()] = filepath.Join(downloadDir, entry.Name())
633+
}
634+
return entries, nil
635+
}
636+
637+
// CacheKey returns the key for a cache entry of the remote URL.
638+
func CacheKey(remote string) string {
639+
return fmt.Sprintf("%x", sha256.Sum256([]byte(remote)))
640+
}
641+
642+
// RemoveAllCacheDir removes the cache directory.
643+
func RemoveAllCacheDir(opt ...Opt) error {
644+
var o options
645+
for _, f := range opt {
646+
if err := f(&o); err != nil {
647+
return err
648+
}
649+
}
650+
if o.cacheDir == "" {
651+
return nil
652+
}
653+
logrus.Infof("Pruning %q", o.cacheDir)
654+
return os.RemoveAll(o.cacheDir)
655+
}

0 commit comments

Comments
 (0)