@@ -65,6 +65,125 @@ 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
+ tmpdir := t .TempDir ()
116
+
117
+ snapshotter , err := native .NewSnapshotter (filepath .Join (tmpdir , "snapshots" ))
118
+ require .NoError (t , err )
119
+ cm , cleanup := setupCacheManager (t , tmpdir , "native" , snapshotter )
120
+ t .Cleanup (cleanup )
121
+
122
+ ch := []string {
123
+ "ADD aa dir" ,
124
+ "ADD aa/bb dir" ,
125
+ "ADD aa/bb/cc file data0" ,
126
+ "ADD aa/ln symlink /aa" ,
127
+ "ADD aa/root symlink /" ,
128
+ "ADD bb symlink aa/bb" ,
129
+ }
130
+
131
+ ref := createRef (t , cm , ch )
132
+
133
+ cc , err := newCacheContext (ref )
134
+ require .NoError (t , err )
135
+
136
+ // Checksumming /aa/bb while following links will result in /aa being scanned.
137
+ _ , err = cc .Checksum (context .TODO (), ref , "/bb" , ChecksumOpts {FollowLinks : true }, nil )
138
+ require .NoError (t , err )
139
+
140
+ root := cc .tree .Root ()
141
+ for _ , test := range []struct {
142
+ path string
143
+ followTrailing , expectNeedsScan bool
144
+ }{
145
+ // Any path under /aa will not result in a re-scan.
146
+ {"/aa" , true , false },
147
+ {"/aa/ln" , true , false },
148
+ {"/aa/ln" , false , false },
149
+ {"/aa/non-exist" , true , false },
150
+ {"/aa/bb/non-exist" , true , false },
151
+ {"/aa/bb/cc" , true , false },
152
+ {"/aa/bb/cc/non-exist" , true , false },
153
+ // followTrailing=false on a symlink to /.
154
+ {"/aa/root" , false , false },
155
+ // /bb itself was scanned during the lookup in Checksum.
156
+ {"/bb" , true , false },
157
+ {"/bb" , false , false },
158
+ // A path outside /aa will need a scan.
159
+ {"/non-exist" , true , true },
160
+ {"/non-exist" , false , true },
161
+ {"/aa/root" , true , true },
162
+ {"/" , true , true },
163
+ } {
164
+ needs , err := cc .needsScan (root , test .path , test .followTrailing )
165
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=%v)" , test .path , test .followTrailing )
166
+ require .Equalf (t , test .expectNeedsScan , needs , "needsScan(%q, followTrailing=%v)" , test .path , test .followTrailing )
167
+ }
168
+
169
+ // Looking up a non-existent path in / will checksum the whole tree. See
170
+ // <https://github.com/moby/buildkit/issues/5042> for more information.
171
+ // This means that needsScan will return true for any path.
172
+ _ , err = cc .Checksum (context .TODO (), ref , "/non-existent" , ChecksumOpts {FollowLinks : true }, nil )
173
+ require .Error (t , err )
174
+
175
+ root = cc .tree .Root ()
176
+ for _ , path := range []string {
177
+ "/" , "/non-exist" , "/ff" , "/aa/root" , "/non-exist/child" , "/different-non-exist" ,
178
+ } {
179
+ needs1 , err := cc .needsScan (root , path , false )
180
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=false)" , path )
181
+ require .Falsef (t , needs1 , "needsScan(%q, followTrailing=false)" , path )
182
+
183
+ needs2 , err := cc .needsScan (root , path , true )
184
+ require .NoErrorf (t , err , "needsScan(%q, followTrailing=true)" , path )
185
+ require .Falsef (t , needs2 , "needsScan(%q, followTrailing=true)" , path )
186
+ }
68
187
}
69
188
70
189
func TestChecksumNonLexicalSymlinks (t * testing.T ) {
@@ -114,8 +233,8 @@ func TestChecksumNonLexicalSymlinks(t *testing.T) {
114
233
"link3/target/file" ,
115
234
} {
116
235
dgst , err := cc .Checksum (context .TODO (), ref , path , ChecksumOpts {FollowLinks : true }, nil )
117
- require .NoError (t , err )
118
- require .Equal (t , dgstFileData0 , dgst )
236
+ require .NoErrorf (t , err , "Checksum(%q)" , path )
237
+ require .Equalf (t , dgstFileData0 , dgst , "Checksum(%q)" , path )
119
238
}
120
239
121
240
// FollowLinks only affects final component resolution, so make sure that
@@ -130,8 +249,8 @@ func TestChecksumNonLexicalSymlinks(t *testing.T) {
130
249
"link3/target/file" ,
131
250
} {
132
251
dgst , err := cc .Checksum (context .TODO (), ref , path , ChecksumOpts {FollowLinks : false }, nil )
133
- require .NoError (t , err )
134
- require .Equal (t , dgstFileData0 , dgst )
252
+ require .NoErrorf (t , err , "Checksum(%q)" , path )
253
+ require .Equalf (t , dgstFileData0 , dgst , "Checksum(%q)" , path )
135
254
}
136
255
137
256
dgstLink1TargetFile , err := cc .Checksum (context .TODO (), ref , "link1/target_file" , ChecksumOpts {FollowLinks : false }, nil )
@@ -159,9 +278,9 @@ func TestChecksumNonLexicalSymlinks(t *testing.T) {
159
278
{"link3/target_file" , dgstLink3TargetFile },
160
279
} {
161
280
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 )
281
+ require .NoErrorf (t , err , "Checksum(%q)" , test . path )
282
+ require .NotEqualf (t , dgstFileData0 , dgst , "Checksum(%q)" , test . path )
283
+ require .Equalf (t , test .expectedDgst , dgst , "Checksum(%q)" , test . path )
165
284
}
166
285
}
167
286
0 commit comments