Skip to content

Commit 796118d

Browse files
authored
Merge pull request #20 from eunomie/smart-cache
store last access date in cache and delete 30 days
2 parents 09899c9 + bb6db60 commit 796118d

File tree

6 files changed

+100
-18
lines changed

6 files changed

+100
-18
lines changed

docs/reference/docker_runx_cache_prune.yaml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
command: docker runx cache prune
2-
short: Remove all cache entries
3-
long: Remove all cache entries
2+
short: Remove cache entries not accessed recently
3+
long: |
4+
By default remove cache entries not accessed in the last 30 days. Use --all/-a to remove all cache entries.
45
usage: docker runx cache prune
56
pname: docker runx cache
67
plink: docker_runx_cache.yaml
78
options:
9+
- option: all
10+
shorthand: a
11+
value_type: bool
12+
default_value: "false"
13+
description: Remove all cache entries
14+
deprecated: false
15+
hidden: false
16+
experimental: false
17+
experimentalcli: false
18+
kubernetes: false
19+
swarm: false
820
- option: force
921
shorthand: f
1022
value_type: bool

docs/reference/runx_cache.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ Manage Docker RunX cache and temporary files
55

66
### Subcommands
77

8-
| Name | Description |
9-
|:-------------------------------|:-------------------------|
10-
| [`df`](runx_cache_df.md) | Show disk usage |
11-
| [`prune`](runx_cache_prune.md) | Remove all cache entries |
8+
| Name | Description |
9+
|:-------------------------------|:-------------------------------------------|
10+
| [`df`](runx_cache_df.md) | Show disk usage |
11+
| [`prune`](runx_cache_prune.md) | Remove cache entries not accessed recently |
1212

1313

1414

docs/reference/runx_cache_prune.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
# docker runx cache prune
22

33
<!---MARKER_GEN_START-->
4-
Remove all cache entries
4+
By default remove cache entries not accessed in the last 30 days. Use --all/-a to remove all cache entries.
55

66
### Options
77

88
| Name | Type | Default | Description |
99
|:----------------|:-------|:--------|:-------------------------------|
10+
| `-a`, `--all` | `bool` | | Remove all cache entries |
1011
| `-f`, `--force` | `bool` | | Do not prompt for confirmation |
1112

1213

internal/commands/cache/df.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/spf13/cobra"
99

1010
"github.com/docker/cli/cli/command"
11+
"github.com/docker/cli/cli/command/formatter/tabwriter"
1112
"github.com/eunomie/docker-runx/runkit"
1213
)
1314

@@ -31,9 +32,21 @@ func dfNewCmd(dockerCli command.Cli) *cobra.Command {
3132
str := strings.Builder{}
3233
str.WriteString("Cache directory: " + cacheDir + "\n")
3334
str.WriteString("\n")
35+
36+
w := tabwriter.NewWriter(&str, 0, 0, 1, ' ', 0)
37+
_, _ = fmt.Fprintln(w, "Digest\tSize\tLast Access")
3438
for _, e := range entries {
35-
str.WriteString(fmt.Sprintf("%s: %s\n", e.Digest, humanize.Bytes(uint64(e.Size))))
39+
t := "--"
40+
if e.LastAccess != nil {
41+
t = e.LastAccess.Format("2006-01-02 15:04:05")
42+
}
43+
_, _ = fmt.Fprintf(w,
44+
"%s\t%s\t%s\n",
45+
e.Digest,
46+
humanize.Bytes(uint64(e.Size)),
47+
t)
3648
}
49+
_ = w.Flush()
3750
str.WriteString(fmt.Sprintf("Total: %s\n", humanize.Bytes(uint64(totalSize))))
3851

3952
_, _ = fmt.Fprintln(dockerCli.Out(), str.String())

internal/commands/cache/prune.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,28 @@ import (
77
"github.com/spf13/cobra"
88

99
"github.com/docker/cli/cli/command"
10+
"github.com/eunomie/docker-runx/internal/sugar"
1011
"github.com/eunomie/docker-runx/runkit"
1112
)
1213

13-
var force bool
14+
var (
15+
force bool
16+
all bool
17+
)
1418

1519
func pruneNewCmd(dockerCli command.Cli) *cobra.Command {
1620
cmd := &cobra.Command{
1721
Use: "prune",
18-
Short: "Remove all cache entries",
22+
Short: "Remove cache entries not accessed recently",
23+
Long: "By default remove cache entries not accessed in the last 30 days. Use --all/-a to remove all cache entries.",
1924
Args: cobra.NoArgs,
2025
RunE: func(cmd *cobra.Command, _ []string) error {
26+
var err error
2127
cache := runkit.NewLocalCache(dockerCli)
2228

2329
if !force {
24-
err := huh.NewConfirm().
25-
Title("Are you sure you want to remove all cache entries?").
30+
err = huh.NewConfirm().
31+
Title(sugar.If(all, "Are you sure you want to remove all cache entries?", "Are you sure you want to remove cache entries not accessed in the last 30 days?")).
2632
Value(&force).Run()
2733
if err != nil {
2834
return err
@@ -34,7 +40,11 @@ func pruneNewCmd(dockerCli command.Cli) *cobra.Command {
3440
return nil
3541
}
3642

37-
err := cache.Erase()
43+
if !all {
44+
err = cache.EraseNotAccessedInLast30Days()
45+
} else {
46+
err = cache.EraseAll()
47+
}
3848
if err != nil {
3949
return err
4050
}
@@ -46,6 +56,7 @@ func pruneNewCmd(dockerCli command.Cli) *cobra.Command {
4656

4757
flags := cmd.Flags()
4858
flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation")
59+
flags.BoolVarP(&all, "all", "a", false, "Remove all cache entries")
4960

5061
return cmd
5162
}

runkit/cache.go

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"io/fs"
66
"os"
77
"path/filepath"
8+
"strings"
9+
"time"
810

911
"github.com/docker/cli/cli/command"
1012
"github.com/eunomie/docker-runx/internal/constants"
@@ -13,6 +15,7 @@ import (
1315
const (
1416
runxConfigFile = "runx.yaml"
1517
runxDocFile = "README.md"
18+
accessFile = "access"
1619
)
1720

1821
var subCacheDir = filepath.Join(constants.SubCommandName, "cache", "sha256")
@@ -23,8 +26,9 @@ type (
2326
}
2427

2528
CacheEntry struct {
26-
Digest string
27-
Size int64
29+
LastAccess *time.Time
30+
Digest string
31+
Size int64
2832
}
2933
)
3034

@@ -66,12 +70,21 @@ func (c *LocalCache) Get(digest, src string) (*RunKit, error) {
6670
}
6771

6872
if found {
73+
if err := c.writeAccessFile(digest); err != nil {
74+
return nil, err
75+
}
76+
6977
rk.src = src
7078
return rk, nil
7179
}
7280
return nil, nil
7381
}
7482

83+
func (c *LocalCache) writeAccessFile(digest string) error {
84+
accessDate := time.Now().Format(time.RFC3339)
85+
return os.WriteFile(filepath.Join(c.cacheDir, digest, accessFile), []byte(accessDate), 0o644)
86+
}
87+
7588
func (c *LocalCache) Set(digest string, runxConfig, runxDoc []byte) error {
7689
digestDir := filepath.Join(c.cacheDir, digest)
7790
if err := os.MkdirAll(digestDir, 0o755); err != nil {
@@ -89,6 +102,9 @@ func (c *LocalCache) Set(digest string, runxConfig, runxDoc []byte) error {
89102
return err
90103
}
91104
}
105+
if err := c.writeAccessFile(digest); err != nil {
106+
return err
107+
}
92108
return nil
93109
}
94110

@@ -105,9 +121,11 @@ func (c *LocalCache) ListCache() (string, []CacheEntry, int64, error) {
105121
return e
106122
}
107123
totalSize += s
124+
t := c.lastAccess(path)
108125
entries = append(entries, CacheEntry{
109-
Digest: filepath.Base(path),
110-
Size: s,
126+
Digest: filepath.Base(path),
127+
Size: s,
128+
LastAccess: t,
111129
})
112130
return fs.SkipDir
113131
}
@@ -122,10 +140,37 @@ func (c *LocalCache) ListCache() (string, []CacheEntry, int64, error) {
122140
return c.cacheDir, entries, totalSize, nil
123141
}
124142

125-
func (c *LocalCache) Erase() error {
143+
func (c *LocalCache) EraseAll() error {
126144
return os.RemoveAll(c.cacheDir)
127145
}
128146

147+
func (c *LocalCache) EraseNotAccessedInLast30Days() error {
148+
_, entries, _, err := c.ListCache()
149+
if err != nil {
150+
return err
151+
}
152+
for _, e := range entries {
153+
if e.LastAccess != nil && time.Since(*e.LastAccess) > 30*24*time.Hour {
154+
if err := os.RemoveAll(filepath.Join(c.cacheDir, e.Digest)); err != nil {
155+
return err
156+
}
157+
}
158+
}
159+
return nil
160+
}
161+
162+
func (c *LocalCache) lastAccess(path string) *time.Time {
163+
b, err := os.ReadFile(filepath.Join(path, accessFile))
164+
if err != nil {
165+
return nil
166+
}
167+
if t, err := time.Parse(time.RFC3339, strings.TrimSpace(string(b))); err != nil {
168+
return nil
169+
} else {
170+
return &t
171+
}
172+
}
173+
129174
func dirSize(path string) (int64, error) {
130175
var size int64
131176
err := filepath.Walk(path, func(path string, info fs.FileInfo, err error) error {

0 commit comments

Comments
 (0)