@@ -229,6 +229,7 @@ func Download(ctx context.Context, local, remote string, opts ...Opt) (*Result,
229
229
}
230
230
if _ , err := os .Stat (shadData ); err == nil {
231
231
logrus .Debugf ("file %q is cached as %q" , localPath , shadData )
232
+ useCache := true
232
233
if _ , err := os .Stat (shadDigest ); err == nil {
233
234
logrus .Debugf ("Comparing digest %q with the cached digest file %q, not computing the actual digest of %q" ,
234
235
o .expectedDigest , shadDigest , shadData )
@@ -239,18 +240,27 @@ func Download(ctx context.Context, local, remote string, opts ...Opt) (*Result,
239
240
return nil , err
240
241
}
241
242
} else {
242
- if err := copyLocal (ctx , localPath , shadData , ext , o .decompress , o .description , o .expectedDigest ); err != nil {
243
- return nil , err
243
+ if match , lmCached , lmRemote , err := matchLastModified (ctx , shadTime , remote ); err != nil {
244
+ logrus .WithError (err ).Info ("Failed to retrieve last-modified for cached digest-less image; using cached image." )
245
+ } else if match {
246
+ if err := copyLocal (ctx , localPath , shadData , ext , o .decompress , o .description , o .expectedDigest ); err != nil {
247
+ return nil , err
248
+ }
249
+ } else {
250
+ logrus .Infof ("Re-downloading digest-less image: last-modified mismatch (cached: %q, remote: %q)" , lmCached , lmRemote )
251
+ useCache = false
244
252
}
245
253
}
246
- res := & Result {
247
- Status : StatusUsedCache ,
248
- CachePath : shadData ,
249
- LastModified : readTime (shadTime ),
250
- ContentType : readFile (shadType ),
251
- ValidatedDigest : o .expectedDigest != "" ,
254
+ if useCache {
255
+ res := & Result {
256
+ Status : StatusUsedCache ,
257
+ CachePath : shadData ,
258
+ LastModified : readTime (shadTime ),
259
+ ContentType : readFile (shadType ),
260
+ ValidatedDigest : o .expectedDigest != "" ,
261
+ }
262
+ return res , nil
252
263
}
253
- return res , nil
254
264
}
255
265
if err := os .RemoveAll (shad ); err != nil {
256
266
return nil , err
@@ -548,6 +558,43 @@ func validateLocalFileDigest(localPath string, expectedDigest digest.Digest) err
548
558
return nil
549
559
}
550
560
561
+ // mathLastModified takes params:
562
+ // - ctx: context for calling httpclientutil.Head
563
+ // - lastModifiedPath: path of the cached last-modified time file
564
+ // - url: URL to fetch the last-modified time
565
+ //
566
+ // returns:
567
+ // - matched: whether the last-modified time matches
568
+ // - lmCached: last-modified time string from the lastModifiedPath
569
+ // - lmRemote: last-modified time string from the URL
570
+ // - err: error if fetching the last-modified time from the URL fails
571
+ func matchLastModified (ctx context.Context , lastModifiedPath , url string ) (matched bool , lmCached , lmRemote string , err error ) {
572
+ lmCached = readFile (lastModifiedPath )
573
+ if lmCached == "" {
574
+ return false , "<not cached>" , "<not checked>" , nil
575
+ }
576
+ resp , err := httpclientutil .Head (ctx , http .DefaultClient , url )
577
+ if err != nil {
578
+ return false , lmCached , "<failed to fetch remote>" , err
579
+ }
580
+ defer resp .Body .Close ()
581
+ lmRemote = resp .Header .Get ("Last-Modified" )
582
+ if lmRemote == "" {
583
+ return false , lmCached , "<missing Last-Modified header>" , nil
584
+ }
585
+ lmCachedTime , errParsingCachedTime := time .Parse (http .TimeFormat , lmCached )
586
+ lmRemoteTime , errParsingRemoteTime := time .Parse (http .TimeFormat , lmRemote )
587
+ if errParsingCachedTime != nil && errParsingRemoteTime != nil {
588
+ // both time strings are failed to parse, so compare them as strings
589
+ return lmCached == lmRemote , lmCached , lmRemote , nil
590
+ } else if errParsingCachedTime == nil && errParsingRemoteTime == nil {
591
+ // both time strings are successfully parsed, so compare them as times
592
+ return lmRemoteTime .Equal (lmCachedTime ), lmCached , lmRemote , nil
593
+ }
594
+ // ignore parsing errors for either time string and assume they are different
595
+ return false , lmCached , lmRemote , nil
596
+ }
597
+
551
598
func downloadHTTP (ctx context.Context , localPath , lastModified , contentType , url , description string , expectedDigest digest.Digest ) error {
552
599
if localPath == "" {
553
600
return fmt .Errorf ("downloadHTTP: got empty localPath" )
0 commit comments