@@ -243,6 +243,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
243243 testMetadataOnlyLocal ,
244244 testGitResolveSourceMetadata ,
245245 testHTTPResolveSourceMetadata ,
246+ testHTTPPruneAfterCacheKey ,
246247}
247248
248249func TestIntegration (t * testing.T ) {
@@ -3223,13 +3224,13 @@ func testBuildHTTPSource(t *testing.T, sb integration.Sandbox) {
32233224
32243225 modTime := time .Now ().Add (- 24 * time .Hour ) // avoid falso positive with current time
32253226
3226- resp := httpserver.Response {
3227+ resp := & httpserver.Response {
32273228 Etag : identity .NewID (),
32283229 Content : []byte ("content1" ),
32293230 LastModified : & modTime ,
32303231 }
32313232
3232- server := httpserver .NewTestServer (map [string ]httpserver.Response {
3233+ server := httpserver .NewTestServer (map [string ]* httpserver.Response {
32333234 "/foo" : resp ,
32343235 })
32353236 defer server .Close ()
@@ -3293,7 +3294,7 @@ func testBuildHTTPSource(t *testing.T, sb integration.Sandbox) {
32933294 require .NoError (t , err )
32943295 require .NoError (t , gw .Close ())
32953296 gzipBytes := buf .Bytes ()
3296- respGzip := httpserver.Response {
3297+ respGzip := & httpserver.Response {
32973298 Etag : identity .NewID (),
32983299 Content : gzipBytes ,
32993300 LastModified : & modTime ,
@@ -3373,18 +3374,18 @@ func testBuildHTTPSourceEtagScope(t *testing.T, sb integration.Sandbox) {
33733374 modTime := time .Now ().Add (- 24 * time .Hour ) // avoid falso positive with current time
33743375
33753376 sharedEtag := identity .NewID ()
3376- resp := httpserver.Response {
3377+ resp := & httpserver.Response {
33773378 Etag : sharedEtag ,
33783379 Content : []byte ("content1" ),
33793380 LastModified : & modTime ,
33803381 }
3381- resp2 := httpserver.Response {
3382+ resp2 := & httpserver.Response {
33823383 Etag : sharedEtag ,
33833384 Content : []byte ("another" ),
33843385 LastModified : & modTime ,
33853386 }
33863387
3387- server := httpserver .NewTestServer (map [string ]httpserver.Response {
3388+ server := httpserver .NewTestServer (map [string ]* httpserver.Response {
33883389 "/one/foo" : resp ,
33893390 "/two/foo" : resp2 ,
33903391 })
@@ -3468,13 +3469,13 @@ func testBuildHTTPSourceAuthHeaderSecret(t *testing.T, sb integration.Sandbox) {
34683469
34693470 modTime := time .Now ().Add (- 24 * time .Hour ) // avoid false positive with current time
34703471
3471- resp := httpserver.Response {
3472+ resp := & httpserver.Response {
34723473 Etag : identity .NewID (),
34733474 Content : []byte ("content1" ),
34743475 LastModified : & modTime ,
34753476 }
34763477
3477- server := httpserver .NewTestServer (map [string ]httpserver.Response {
3478+ server := httpserver .NewTestServer (map [string ]* httpserver.Response {
34783479 "/foo" : resp ,
34793480 })
34803481 defer server .Close ()
@@ -3509,13 +3510,13 @@ func testBuildHTTPSourceHostTokenSecret(t *testing.T, sb integration.Sandbox) {
35093510
35103511 modTime := time .Now ().Add (- 24 * time .Hour ) // avoid false positive with current time
35113512
3512- resp := httpserver.Response {
3513+ resp := & httpserver.Response {
35133514 Etag : identity .NewID (),
35143515 Content : []byte ("content1" ),
35153516 LastModified : & modTime ,
35163517 }
35173518
3518- server := httpserver .NewTestServer (map [string ]httpserver.Response {
3519+ server := httpserver .NewTestServer (map [string ]* httpserver.Response {
35193520 "/foo" : resp ,
35203521 })
35213522 defer server .Close ()
@@ -3550,13 +3551,13 @@ func testBuildHTTPSourceHeader(t *testing.T, sb integration.Sandbox) {
35503551
35513552 modTime := time .Now ().Add (- 24 * time .Hour ) // avoid falso positive with current time
35523553
3553- resp := httpserver.Response {
3554+ resp := & httpserver.Response {
35543555 Etag : identity .NewID (),
35553556 Content : []byte ("content1" ),
35563557 LastModified : & modTime ,
35573558 }
35583559
3559- server := httpserver .NewTestServer (map [string ]httpserver.Response {
3560+ server := httpserver .NewTestServer (map [string ]* httpserver.Response {
35603561 "/foo" : resp ,
35613562 })
35623563 defer server .Close ()
@@ -12066,19 +12067,19 @@ func testHTTPResolveSourceMetadata(t *testing.T, sb integration.Sandbox) {
1206612067
1206712068 modTime := time .Now ().Add (- 24 * time .Hour ) // avoid falso positive with current time
1206812069
12069- resp := httpserver.Response {
12070+ resp := & httpserver.Response {
1207012071 Etag : identity .NewID (),
1207112072 Content : []byte ("content1" ),
1207212073 LastModified : & modTime ,
1207312074 }
1207412075
12075- resp2 := httpserver.Response {
12076+ resp2 := & httpserver.Response {
1207612077 Etag : identity .NewID (),
1207712078 Content : []byte ("content2" ),
1207812079 ContentDisposition : "attachment; filename=\" my img.jpg\" " ,
1207912080 }
1208012081
12081- server := httpserver .NewTestServer (map [string ]httpserver.Response {
12082+ server := httpserver .NewTestServer (map [string ]* httpserver.Response {
1208212083 "/foo" : resp ,
1208312084 "/bar" : resp2 ,
1208412085 })
@@ -12116,6 +12117,96 @@ func testHTTPResolveSourceMetadata(t *testing.T, sb integration.Sandbox) {
1211612117 require .NoError (t , err )
1211712118}
1211812119
12120+ func testHTTPPruneAfterCacheKey (t * testing.T , sb integration.Sandbox ) {
12121+ // this test depends on hitting race condition in internal functions.
12122+ // If debugging and expecting failure you can add small sleep in beginning of source/http.Exec() to hit reliably
12123+ ctx := sb .Context ()
12124+ c , err := New (ctx , sb .Address ())
12125+ require .NoError (t , err )
12126+ defer c .Close ()
12127+
12128+ resp := & httpserver.Response {
12129+ Etag : identity .NewID (),
12130+ Content : []byte ("content1" ),
12131+ }
12132+ server := httpserver .NewTestServer (map [string ]* httpserver.Response {
12133+ "/foo" : resp ,
12134+ })
12135+ defer server .Close ()
12136+
12137+ done := make (chan struct {})
12138+
12139+ startScan := make (chan struct {})
12140+ stopScan := make (chan struct {})
12141+ pauseScan := make (chan struct {})
12142+
12143+ go func () {
12144+ // attempt to prune the HTTP record in between cachekey and snapshot
12145+ defer close (done )
12146+ for {
12147+ select {
12148+ case <- startScan :
12149+ scan:
12150+ for {
12151+ select {
12152+ case <- pauseScan :
12153+ break scan
12154+ default :
12155+ du , err := c .DiskUsage (ctx )
12156+ require .NoError (t , err )
12157+ for _ , entry := range du {
12158+ if entry .Description == "http url " + server .URL + "/foo" {
12159+ if ! entry .InUse {
12160+ t .Logf ("entry no longer in use, pruning" )
12161+ err = c .Prune (ctx , nil )
12162+ require .NoError (t , err )
12163+
12164+ resp .Etag = identity .NewID ()
12165+ resp .Content = []byte ("content2" )
12166+ }
12167+ }
12168+ }
12169+ }
12170+ }
12171+ case <- stopScan :
12172+ return
12173+ }
12174+ }
12175+ }()
12176+
12177+ const iterations = 10
12178+ for range iterations {
12179+ startScan <- struct {}{}
12180+ resp .Etag = identity .NewID ()
12181+ resp .Content = []byte ("content1" )
12182+ _ , err = c .Build (ctx , SolveOpt {}, "test" , func (ctx context.Context , c gateway.Client ) (* gateway.Result , error ) {
12183+ st := llb .Scratch ().File (llb .Copy (llb .HTTP (server .URL + "/foo" ), "foo" , "bar" ))
12184+ def , err := st .Marshal (sb .Context ())
12185+ if err != nil {
12186+ return nil , err
12187+ }
12188+ resp , err := c .Solve (ctx , gateway.SolveRequest {
12189+ Definition : def .ToPB (),
12190+ })
12191+ if err != nil {
12192+ return nil , err
12193+ }
12194+
12195+ return resp , nil
12196+ }, nil )
12197+ require .NoError (t , err )
12198+
12199+ pauseScan <- struct {}{}
12200+
12201+ err = c .Prune (ctx , nil )
12202+ require .NoError (t , err )
12203+
12204+ checkAllReleasable (t , c , sb , false )
12205+ }
12206+ close (stopScan )
12207+ <- done
12208+ }
12209+
1211912210func runInDir (dir string , cmds ... string ) error {
1212012211 for _ , args := range cmds {
1212112212 var cmd * exec.Cmd
0 commit comments