@@ -65,6 +65,157 @@ func TestChecksumSymlinkNoParentScan(t *testing.T) {
65
65
dgst , err := cc .Checksum (context .TODO (), ref , "aa/ln/bb/cc/dd" , ChecksumOpts {FollowLinks : true }, nil )
66
66
require .NoError (t , err )
67
67
require .Equal (t , dgstFileData0 , dgst )
68
+
69
+ // The above checksum request should have only checksummed aa/bb/cc, and so
70
+ // any parent directories should need a scan but non-existent (or existent)
71
+ // children should not.
72
+ root := cc .tree .Root ()
73
+
74
+ for _ , path := range []string {
75
+ // Paths not within the scanned /aa/bb/cc/.
76
+ "/" , "/aa" , "/aa/bb" , "/aa/bb/ff" , "/non-exist" ,
77
+ } {
78
+ needs1 , err := cc .needsScan (root , path , false )
79
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=false)" , path )
80
+ require .Truef (t , needs1 , "needsScan(%q, followTrailing=false)" , path )
81
+
82
+ needs2 , err := cc .needsScan (root , path , true )
83
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=true)" , path )
84
+ require .Truef (t , needs2 , "needsScan(%q, followTrailing=true)" , path )
85
+ }
86
+
87
+ for _ , path := range []string {
88
+ // Paths within the scanned /aa/bb/cc, even if they don't exist.
89
+ "/aa/bb/cc" , "/aa/bb/cc/non-exist" , "/aa/bb/cc/dd/ee/ff" , "/aa/bb/cc/non-exist/xx/yy/zz" ,
90
+ } {
91
+ needs1 , err := cc .needsScan (root , path , false )
92
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=false)" , path )
93
+ require .Falsef (t , needs1 , "needsScan(%q, followTrailing=false)" , path )
94
+
95
+ needs2 , err := cc .needsScan (root , path , true )
96
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=true)" , path )
97
+ require .Falsef (t , needs2 , "needsScan(%q, followTrailing=true)" , path )
98
+ }
99
+
100
+ // /aa was not scanned, but during the walk we went through /aa/ln and so
101
+ // we know the contents of the link. However, if we want to scan it with
102
+ // followTrailing=true, we will need a scan because we didn't scan /aa.
103
+ path := "/aa/ln"
104
+ needs1 , err := cc .needsScan (root , path , false )
105
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=false)" , path )
106
+ require .Falsef (t , needs1 , "needsScan(%q, followTrailing=false)" , path )
107
+
108
+ needs2 , err := cc .needsScan (root , path , true )
109
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=true)" , path )
110
+ require .Truef (t , needs2 , "needsScan(%q, followTrailing=true)" , path )
111
+ }
112
+
113
+ // https://github.com/moby/buildkit/issues/5042
114
+ func TestNeedScanChecksumRegression (t * testing.T ) {
115
+ // This test cannot be run in parallel because we use scanCounter.
116
+ scanCounterEnable = true
117
+ defer func () {
118
+ scanCounterEnable = false
119
+ }()
120
+
121
+ tmpdir := t .TempDir ()
122
+
123
+ snapshotter , err := native .NewSnapshotter (filepath .Join (tmpdir , "snapshots" ))
124
+ require .NoError (t , err )
125
+ cm , cleanup := setupCacheManager (t , tmpdir , "native" , snapshotter )
126
+ t .Cleanup (cleanup )
127
+
128
+ ch := []string {
129
+ "ADD aa dir" ,
130
+ "ADD aa/bb dir" ,
131
+ "ADD aa/bb/cc file data0" ,
132
+ "ADD aa/ln symlink /aa" ,
133
+ "ADD aa/root symlink /" ,
134
+ "ADD bb symlink aa/bb" ,
135
+ }
136
+
137
+ ref := createRef (t , cm , ch )
138
+
139
+ cc , err := newCacheContext (ref )
140
+ require .NoError (t , err )
141
+
142
+ // Checksumming /aa/bb while following links will result in /aa being scanned.
143
+ _ , err = cc .Checksum (context .TODO (), ref , "/bb" , ChecksumOpts {FollowLinks : true }, nil )
144
+ require .NoError (t , err )
145
+
146
+ root := cc .tree .Root ()
147
+ for _ , test := range []struct {
148
+ path string
149
+ followTrailing , expectNeedsScan bool
150
+ }{
151
+ // Any path under /aa will not result in a re-scan.
152
+ {"/aa" , true , false },
153
+ {"/aa/ln" , true , false },
154
+ {"/aa/ln" , false , false },
155
+ {"/aa/non-exist" , true , false },
156
+ {"/aa/bb/non-exist" , true , false },
157
+ {"/aa/bb/cc" , true , false },
158
+ {"/aa/bb/cc/non-exist" , true , false },
159
+ // followTrailing=false on a symlink to /.
160
+ {"/aa/root" , false , false },
161
+ // /bb itself was scanned during the lookup in Checksum.
162
+ {"/bb" , true , false },
163
+ {"/bb" , false , false },
164
+ // A path outside /aa will need a scan.
165
+ {"/non-exist" , true , true },
166
+ {"/non-exist" , false , true },
167
+ {"/aa/root" , true , true },
168
+ {"/" , true , true },
169
+ } {
170
+ needs , err := cc .needsScan (root , test .path , test .followTrailing )
171
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=%v)" , test .path , test .followTrailing )
172
+ require .Equalf (t , test .expectNeedsScan , needs , "needsScan(%q, followTrailing=%v)" , test .path , test .followTrailing )
173
+ }
174
+
175
+ // Make sure trying to checksum a subpath results in no further scans.
176
+ initialScanCounter := scanCounter .Load ()
177
+ _ , err = cc .Checksum (context .TODO (), ref , "/bb/cc" , ChecksumOpts {FollowLinks : true }, nil )
178
+ require .NoError (t , err )
179
+ require .Equal (t , initialScanCounter , scanCounter .Load ())
180
+ _ , err = cc .Checksum (context .TODO (), ref , "/bb/non-existent" , ChecksumOpts {FollowLinks : true }, nil )
181
+ require .Error (t , err )
182
+ require .Equal (t , initialScanCounter , scanCounter .Load ())
183
+
184
+ // Looking up a non-existent path in / will checksum the whole tree. See
185
+ // <https://github.com/moby/buildkit/issues/5042> for more information.
186
+ // This means that needsScan will return true for any path.
187
+ _ , err = cc .Checksum (context .TODO (), ref , "/non-existent" , ChecksumOpts {FollowLinks : true }, nil )
188
+ require .Error (t , err )
189
+ fullScanCounter := scanCounter .Load ()
190
+ require .NotEqual (t , fullScanCounter , initialScanCounter )
191
+
192
+ root = cc .tree .Root ()
193
+ for _ , path := range []string {
194
+ "/" , "/non-exist" , "/ff" , "/aa/root" , "/non-exist/child" , "/different-non-exist" ,
195
+ } {
196
+ needs1 , err := cc .needsScan (root , path , false )
197
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=false)" , path )
198
+ require .Falsef (t , needs1 , "needsScan(%q, followTrailing=false)" , path )
199
+
200
+ needs2 , err := cc .needsScan (root , path , true )
201
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=true)" , path )
202
+ require .Falsef (t , needs2 , "needsScan(%q, followTrailing=true)" , path )
203
+ }
204
+
205
+ // Looking up any more paths should not result in any more scans because we
206
+ // already know / was scanned.
207
+ _ , err = cc .Checksum (context .TODO (), ref , "/non-existent" , ChecksumOpts {FollowLinks : true }, nil )
208
+ require .Error (t , err )
209
+ require .Equal (t , fullScanCounter , scanCounter .Load ())
210
+ _ , err = cc .Checksum (context .TODO (), ref , "/different/non/existent" , ChecksumOpts {FollowLinks : true }, nil )
211
+ require .Error (t , err )
212
+ require .Equal (t , fullScanCounter , scanCounter .Load ())
213
+ _ , err = cc .Checksum (context .TODO (), ref , "/aa/root/aa/non-exist" , ChecksumOpts {FollowLinks : true }, nil )
214
+ require .Error (t , err )
215
+ require .Equal (t , fullScanCounter , scanCounter .Load ())
216
+ _ , err = cc .Checksum (context .TODO (), ref , "/aa/root/bb/cc" , ChecksumOpts {FollowLinks : true }, nil )
217
+ require .NoError (t , err )
218
+ require .Equal (t , fullScanCounter , scanCounter .Load ())
68
219
}
69
220
70
221
func TestChecksumNonLexicalSymlinks (t * testing.T ) {
@@ -114,8 +265,8 @@ func TestChecksumNonLexicalSymlinks(t *testing.T) {
114
265
"link3/target/file" ,
115
266
} {
116
267
dgst , err := cc .Checksum (context .TODO (), ref , path , ChecksumOpts {FollowLinks : true }, nil )
117
- require .NoError (t , err )
118
- require .Equal (t , dgstFileData0 , dgst )
268
+ require .NoErrorf (t , err , "Checksum(%q)" , path )
269
+ require .Equalf (t , dgstFileData0 , dgst , "Checksum(%q)" , path )
119
270
}
120
271
121
272
// FollowLinks only affects final component resolution, so make sure that
@@ -130,8 +281,8 @@ func TestChecksumNonLexicalSymlinks(t *testing.T) {
130
281
"link3/target/file" ,
131
282
} {
132
283
dgst , err := cc .Checksum (context .TODO (), ref , path , ChecksumOpts {FollowLinks : false }, nil )
133
- require .NoError (t , err )
134
- require .Equal (t , dgstFileData0 , dgst )
284
+ require .NoErrorf (t , err , "Checksum(%q)" , path )
285
+ require .Equalf (t , dgstFileData0 , dgst , "Checksum(%q)" , path )
135
286
}
136
287
137
288
dgstLink1TargetFile , err := cc .Checksum (context .TODO (), ref , "link1/target_file" , ChecksumOpts {FollowLinks : false }, nil )
@@ -159,9 +310,9 @@ func TestChecksumNonLexicalSymlinks(t *testing.T) {
159
310
{"link3/target_file" , dgstLink3TargetFile },
160
311
} {
161
312
dgst , err := cc .Checksum (context .TODO (), ref , test .path , ChecksumOpts {FollowLinks : false }, nil )
162
- require .NoError (t , err )
163
- require .NotEqual (t , dgstFileData0 , dgst )
164
- require .Equal (t , test .expectedDgst , dgst )
313
+ require .NoErrorf (t , err , "Checksum(%q)" , test . path )
314
+ require .NotEqualf (t , dgstFileData0 , dgst , "Checksum(%q)" , test . path )
315
+ require .Equalf (t , test .expectedDgst , dgst , "Checksum(%q)" , test . path )
165
316
}
166
317
}
167
318
0 commit comments