1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package cache
16
17 import (
18 "errors"
19 "fmt"
20 "io"
21 "os"
22 "path/filepath"
23 "runtime"
24
25 v1 "github.com/google/go-containerregistry/pkg/v1"
26 "github.com/google/go-containerregistry/pkg/v1/tarball"
27 )
28
29 type fscache struct {
30 path string
31 }
32
33
34 func NewFilesystemCache(path string) Cache {
35 return &fscache{path}
36 }
37
38 func (fs *fscache) Put(l v1.Layer) (v1.Layer, error) {
39 digest, err := l.Digest()
40 if err != nil {
41 return nil, err
42 }
43 diffID, err := l.DiffID()
44 if err != nil {
45 return nil, err
46 }
47 return &layer{
48 Layer: l,
49 path: fs.path,
50 digest: digest,
51 diffID: diffID,
52 }, nil
53 }
54
55 type layer struct {
56 v1.Layer
57 path string
58 digest, diffID v1.Hash
59 }
60
61 func (l *layer) create(h v1.Hash) (io.WriteCloser, error) {
62 if err := os.MkdirAll(l.path, 0700); err != nil {
63 return nil, err
64 }
65 return os.Create(cachepath(l.path, h))
66 }
67
68 func (l *layer) Compressed() (io.ReadCloser, error) {
69 f, err := l.create(l.digest)
70 if err != nil {
71 return nil, err
72 }
73 rc, err := l.Layer.Compressed()
74 if err != nil {
75 return nil, err
76 }
77 return &readcloser{
78 t: io.TeeReader(rc, f),
79 closes: []func() error{rc.Close, f.Close},
80 }, nil
81 }
82
83 func (l *layer) Uncompressed() (io.ReadCloser, error) {
84 f, err := l.create(l.diffID)
85 if err != nil {
86 return nil, err
87 }
88 rc, err := l.Layer.Uncompressed()
89 if err != nil {
90 return nil, err
91 }
92 return &readcloser{
93 t: io.TeeReader(rc, f),
94 closes: []func() error{rc.Close, f.Close},
95 }, nil
96 }
97
98 type readcloser struct {
99 t io.Reader
100 closes []func() error
101 }
102
103 func (rc *readcloser) Read(b []byte) (int, error) {
104 return rc.t.Read(b)
105 }
106
107 func (rc *readcloser) Close() error {
108
109
110 var err error
111 for _, c := range rc.closes {
112 lastErr := c()
113 if err == nil {
114 err = lastErr
115 }
116 }
117 return err
118 }
119
120 func (fs *fscache) Get(h v1.Hash) (v1.Layer, error) {
121 l, err := tarball.LayerFromFile(cachepath(fs.path, h))
122 if os.IsNotExist(err) {
123 return nil, ErrNotFound
124 }
125 if errors.Is(err, io.ErrUnexpectedEOF) {
126
127 if err := fs.Delete(h); err != nil {
128 return nil, err
129 }
130 return nil, ErrNotFound
131 }
132 return l, err
133 }
134
135 func (fs *fscache) Delete(h v1.Hash) error {
136 err := os.Remove(cachepath(fs.path, h))
137 if os.IsNotExist(err) {
138 return ErrNotFound
139 }
140 return err
141 }
142
143 func cachepath(path string, h v1.Hash) string {
144 var file string
145 if runtime.GOOS == "windows" {
146 file = fmt.Sprintf("%s-%s", h.Algorithm, h.Hex)
147 } else {
148 file = h.String()
149 }
150 return filepath.Join(path, file)
151 }
152
View as plain text