@@ -40,36 +40,6 @@ func TestExecOpCacheMap(t *testing.T) {
4040 xMatch bool
4141 }
4242
43- newExecOp := func (opts ... func (* ExecOp )) * ExecOp {
44- op := & ExecOp {op : & pb.ExecOp {Meta : & pb.Meta {}}}
45- for _ , opt := range opts {
46- opt (op )
47- }
48- return op
49- }
50-
51- withNewMount := func (p string , cache * pb.CacheOpt ) func (* ExecOp ) {
52- return func (op * ExecOp ) {
53- m := & pb.Mount {
54- Dest : p ,
55- Input : pb .InputIndex (op .numInputs ),
56- // Generate a new selector for each mount since this should not effect the cache key.
57- // This helps exercise that code path.
58- Selector : identity .NewID (),
59- }
60- if cache != nil {
61- m .CacheOpt = cache
62- m .MountType = pb .MountType_CACHE
63- }
64- op .op .Mounts = append (op .op .Mounts , m )
65- op .numInputs ++
66- }
67- }
68-
69- withEmptyMounts := func (op * ExecOp ) {
70- op .op .Mounts = []* pb.Mount {}
71- }
72-
7343 testCases := []testCase {
7444 {name : "empty" , op1 : newExecOp (), op2 : newExecOp (), xMatch : true },
7545 {
@@ -87,50 +57,50 @@ func TestExecOpCacheMap(t *testing.T) {
8757 {
8858 name : "non-nil but empty mounts vs with mounts should not match" ,
8959 op1 : newExecOp (withEmptyMounts ),
90- op2 : newExecOp (withNewMount ("/foo" , nil )),
60+ op2 : newExecOp (withNewMount ("/foo" )),
9161 xMatch : false ,
9262 },
9363 {
9464 name : "mounts to different paths should not match" ,
95- op1 : newExecOp (withNewMount ("/foo" , nil )),
96- op2 : newExecOp (withNewMount ("/bar" , nil )),
65+ op1 : newExecOp (withNewMount ("/foo" )),
66+ op2 : newExecOp (withNewMount ("/bar" )),
9767 xMatch : false ,
9868 },
9969 {
10070 name : "mounts to same path should match" ,
101- op1 : newExecOp (withNewMount ("/foo" , nil )),
102- op2 : newExecOp (withNewMount ("/foo" , nil )),
71+ op1 : newExecOp (withNewMount ("/foo" )),
72+ op2 : newExecOp (withNewMount ("/foo" )),
10373 xMatch : true ,
10474 },
10575 {
10676 name : "cache mount should not match non-cache mount at same path" ,
107- op1 : newExecOp (withNewMount ("/foo" , & pb.CacheOpt {ID : "someID" })),
108- op2 : newExecOp (withNewMount ("/foo" , nil )),
77+ op1 : newExecOp (withNewMount ("/foo" , withCache ( & pb.CacheOpt {ID : "someID" }) )),
78+ op2 : newExecOp (withNewMount ("/foo" )),
10979 xMatch : false ,
11080 },
11181 {
11282 name : "different cache id's at the same path should match" ,
113- op1 : newExecOp (withNewMount ("/foo" , & pb.CacheOpt {ID : "someID" })),
114- op2 : newExecOp (withNewMount ("/foo" , & pb.CacheOpt {ID : "someOtherID" })),
83+ op1 : newExecOp (withNewMount ("/foo" , withCache ( & pb.CacheOpt {ID : "someID" }) )),
84+ op2 : newExecOp (withNewMount ("/foo" , withCache ( & pb.CacheOpt {ID : "someOtherID" }) )),
11585 xMatch : true ,
11686 },
11787 {
11888 // This is a special case for default dockerfile cache mounts for backwards compatibility.
11989 name : "default dockerfile cache mount should not match the same cache mount but with different sharing" ,
120- op1 : newExecOp (withNewMount ("/foo" , & pb.CacheOpt {ID : "/foo" })),
121- op2 : newExecOp (withNewMount ("/foo" , & pb.CacheOpt {ID : "/foo" , Sharing : pb .CacheSharingOpt_LOCKED })),
90+ op1 : newExecOp (withNewMount ("/foo" , withCache ( & pb.CacheOpt {ID : "/foo" }) )),
91+ op2 : newExecOp (withNewMount ("/foo" , withCache ( & pb.CacheOpt {ID : "/foo" , Sharing : pb .CacheSharingOpt_LOCKED }) )),
12292 xMatch : false ,
12393 },
12494 {
12595 name : "cache mounts with the same ID but different sharing options should match" ,
126- op1 : newExecOp (withNewMount ("/foo" , & pb.CacheOpt {ID : "someID" , Sharing : 0 })),
127- op2 : newExecOp (withNewMount ("/foo" , & pb.CacheOpt {ID : "someID" , Sharing : 1 })),
96+ op1 : newExecOp (withNewMount ("/foo" , withCache ( & pb.CacheOpt {ID : "someID" , Sharing : 0 }) )),
97+ op2 : newExecOp (withNewMount ("/foo" , withCache ( & pb.CacheOpt {ID : "someID" , Sharing : 1 }) )),
12898 xMatch : true ,
12999 },
130100 {
131101 name : "cache mounts with different IDs and different sharing should match at the same path" ,
132- op1 : newExecOp (withNewMount ("/foo" , & pb.CacheOpt {ID : "someID" , Sharing : 0 })),
133- op2 : newExecOp (withNewMount ("/foo" , & pb.CacheOpt {ID : "someOtherID" , Sharing : 1 })),
102+ op1 : newExecOp (withNewMount ("/foo" , withCache ( & pb.CacheOpt {ID : "someID" , Sharing : 0 }) )),
103+ op2 : newExecOp (withNewMount ("/foo" , withCache ( & pb.CacheOpt {ID : "someOtherID" , Sharing : 1 }) )),
134104 xMatch : true ,
135105 },
136106 }
@@ -157,3 +127,167 @@ func TestExecOpCacheMap(t *testing.T) {
157127 })
158128 }
159129}
130+
131+ func TestExecOpContentCache (t * testing.T ) {
132+ type testCase struct {
133+ name string
134+ op * ExecOp
135+
136+ // cacheByDefault is whether content-caching is enabled by default for this mount
137+ cacheByDefault bool
138+ // cacheIsSafe is whether content-cachine can be safely enabled for this mount
139+ cacheIsSafe bool
140+ }
141+
142+ testCases := []testCase {
143+ {
144+ name : "with sub mount" ,
145+ op : newExecOp (withNewMount ("/foo" , withSelector ("/bar" ))),
146+ cacheByDefault : false ,
147+ cacheIsSafe : false ,
148+ },
149+ {
150+ name : "with read-only sub mount" ,
151+ op : newExecOp (withNewMount ("/foo" , withSelector ("/bar" ), withReadonly ())),
152+ cacheByDefault : true ,
153+ cacheIsSafe : true ,
154+ },
155+ {
156+ name : "with no-output sub mount" ,
157+ op : newExecOp (withNewMount ("/foo" , withSelector ("/bar" ), withoutOutput ())),
158+ cacheByDefault : true ,
159+ cacheIsSafe : true ,
160+ },
161+ {
162+ name : "with root sub mount" ,
163+ op : newExecOp (withNewMount ("/foo" , withSelector ("/" ))),
164+ cacheByDefault : true ,
165+ cacheIsSafe : true ,
166+ },
167+ {
168+ name : "with root mount" ,
169+ op : newExecOp (withNewMount ("/" , withSelector ("/bar" ))),
170+ cacheByDefault : false ,
171+ cacheIsSafe : false ,
172+ },
173+ {
174+ name : "with root read-only mount" ,
175+ op : newExecOp (withNewMount ("/" , withSelector ("/bar" ), withReadonly ())),
176+ cacheByDefault : false ,
177+ cacheIsSafe : true ,
178+ },
179+ {
180+ name : "with root no-output mount" ,
181+ op : newExecOp (withNewMount ("/" , withSelector ("/bar" ), withoutOutput ())),
182+ cacheByDefault : false ,
183+ cacheIsSafe : true ,
184+ },
185+ {
186+ name : "with root mount" ,
187+ op : newExecOp (withNewMount ("/" , withSelector ("/" ))),
188+ cacheByDefault : false ,
189+ cacheIsSafe : true ,
190+ },
191+ }
192+
193+ ctx := context .Background ()
194+ for _ , tc := range testCases {
195+ tc := tc
196+ t .Run (tc .name , func (t * testing.T ) {
197+ t .Parallel ()
198+
199+ // default is always valid, and can sometimes have slow-cache
200+ m , ok , err := tc .op .CacheMap (ctx , session .NewGroup (t .Name ()), 1 )
201+ require .NoError (t , err )
202+ require .True (t , ok )
203+ for _ , dep := range m .Deps {
204+ if tc .cacheByDefault {
205+ require .NotZero (t , dep .ComputeDigestFunc )
206+ } else {
207+ require .Zero (t , dep .ComputeDigestFunc )
208+ }
209+ }
210+
211+ // off is always valid, and never has slow-cache
212+ for _ , mnt := range tc .op .op .Mounts {
213+ mnt .ContentCache = pb .MountContentCache_OFF
214+ }
215+ m , ok , err = tc .op .CacheMap (ctx , session .NewGroup (t .Name ()), 1 )
216+ require .NoError (t , err )
217+ require .True (t , ok )
218+ for _ , dep := range m .Deps {
219+ require .Zero (t , dep .ComputeDigestFunc )
220+ }
221+
222+ // on is sometimes valid, and always has slow-cache if valid
223+ for _ , mnt := range tc .op .op .Mounts {
224+ mnt .ContentCache = pb .MountContentCache_ON
225+ }
226+ m , ok , err = tc .op .CacheMap (ctx , session .NewGroup (t .Name ()), 1 )
227+ if tc .cacheIsSafe {
228+ require .NoError (t , err )
229+ require .True (t , ok )
230+ for _ , dep := range m .Deps {
231+ require .NotZero (t , dep .ComputeDigestFunc )
232+ }
233+ } else {
234+ require .False (t , ok )
235+ require .ErrorContains (t , err , "invalid mount" )
236+ }
237+ })
238+ }
239+ }
240+
241+ func newExecOp (opts ... func (* ExecOp )) * ExecOp {
242+ op := & ExecOp {op : & pb.ExecOp {Meta : & pb.Meta {}}}
243+ for _ , opt := range opts {
244+ opt (op )
245+ }
246+ return op
247+ }
248+
249+ func withEmptyMounts (op * ExecOp ) {
250+ op .op .Mounts = []* pb.Mount {}
251+ }
252+
253+ func withNewMount (p string , opts ... func (* pb.Mount )) func (* ExecOp ) {
254+ return func (op * ExecOp ) {
255+ m := & pb.Mount {
256+ Dest : p ,
257+ Input : pb .InputIndex (op .numInputs ),
258+ // Generate a new selector for each mount since this should not effect the cache key.
259+ // This helps exercise that code path.
260+ Selector : identity .NewID (),
261+ }
262+ for _ , opt := range opts {
263+ opt (m )
264+ }
265+ op .op .Mounts = append (op .op .Mounts , m )
266+ op .numInputs ++
267+ }
268+ }
269+
270+ func withSelector (selector string ) func (* pb.Mount ) {
271+ return func (m * pb.Mount ) {
272+ m .Selector = selector
273+ }
274+ }
275+
276+ func withCache (cache * pb.CacheOpt ) func (* pb.Mount ) {
277+ return func (m * pb.Mount ) {
278+ m .CacheOpt = cache
279+ m .MountType = pb .MountType_CACHE
280+ }
281+ }
282+
283+ func withReadonly () func (* pb.Mount ) {
284+ return func (m * pb.Mount ) {
285+ m .Readonly = true
286+ }
287+ }
288+
289+ func withoutOutput () func (* pb.Mount ) {
290+ return func (m * pb.Mount ) {
291+ m .Output = pb .SkipOutput
292+ }
293+ }
0 commit comments