Skip to content

Commit 3a42cb4

Browse files
leodidoona-agent
andcommitted
refactor: add dedicated helpers for provenance bundle operations
Extract provenance upload/download logic into dedicated helper functions with improved error handling and verification: uploadProvenanceBundle(): - Checks if provenance file exists before attempting upload - Proper rate limiting and timeout handling - Non-blocking operation with clear logging - Returns early on any error to avoid cascading failures downloadProvenanceBundle(): - Rate limiting and timeout protection - Verifies downloaded file exists and has content - Atomic move to final location - Returns bool to indicate success/failure - Graceful handling of missing provenance (expected for older artifacts) Benefits: - Better separation of concerns - More robust error handling - Easier to test and maintain - Clear success/failure indicators - Improved logging at each step Co-authored-by: Ona <no-reply@ona.com>
1 parent 3bf7c45 commit 3a42cb4

File tree

1 file changed

+118
-65
lines changed
  • pkg/leeway/cache/remote

1 file changed

+118
-65
lines changed

pkg/leeway/cache/remote/s3.go

Lines changed: 118 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -579,40 +579,7 @@ func (s *S3Cache) downloadWithSLSAVerification(ctx context.Context, p cache.Pack
579579
}
580580

581581
// Step 6: Download provenance bundle if it exists (best effort, non-blocking)
582-
// Provenance bundles are stored alongside artifacts as <artifact>.provenance.jsonl
583-
// This is needed for dependency provenance collection during local builds
584-
provenanceKey := artifactKey + ".provenance.jsonl"
585-
provenancePath := localPath + ".provenance.jsonl"
586-
tmpProvenancePath := provenancePath + ".tmp"
587-
588-
// Try to download provenance bundle (non-critical, don't fail if missing)
589-
if err := s.waitForRateLimit(ctx); err == nil {
590-
downloadCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
591-
defer cancel()
592-
593-
if _, err := s.storage.GetObject(downloadCtx, provenanceKey, tmpProvenancePath); err == nil {
594-
// Successfully downloaded, move to final location
595-
if err := s.atomicMove(tmpProvenancePath, provenancePath); err != nil {
596-
log.WithError(err).WithFields(log.Fields{
597-
"package": p.FullName(),
598-
"key": provenanceKey,
599-
}).Debug("Failed to move provenance bundle, continuing")
600-
s.cleanupTempFiles(tmpProvenancePath)
601-
} else {
602-
log.WithFields(log.Fields{
603-
"package": p.FullName(),
604-
"key": provenanceKey,
605-
}).Debug("Successfully downloaded provenance bundle")
606-
}
607-
} else {
608-
// Provenance not found - this is expected for older artifacts
609-
log.WithFields(log.Fields{
610-
"package": p.FullName(),
611-
"key": provenanceKey,
612-
}).Debug("Provenance bundle not found in remote cache (expected for older artifacts)")
613-
s.cleanupTempFiles(tmpProvenancePath)
614-
}
615-
}
582+
s.downloadProvenanceBundle(ctx, p.FullName(), artifactKey, localPath)
616583

617584
// Clean up temporary attestation file
618585
s.cleanupTempFiles(tmpAttestationPath)
@@ -1004,37 +971,8 @@ func (s *S3Cache) Upload(ctx context.Context, src cache.LocalCache, pkgs []cache
1004971
"key": key,
1005972
}).Debug("successfully uploaded package to remote cache")
1006973

1007-
// Upload provenance bundle if it exists
1008-
// Provenance bundles are stored alongside artifacts as <artifact>.provenance.jsonl
1009-
provenancePath := localPath + ".provenance.jsonl"
1010-
if fileExists(provenancePath) {
1011-
provenanceKey := key + ".provenance.jsonl"
1012-
1013-
// Wait for rate limiter permission
1014-
if err := s.waitForRateLimit(ctx); err != nil {
1015-
log.WithError(err).WithFields(log.Fields{
1016-
"package": p.FullName(),
1017-
"key": provenanceKey,
1018-
}).Warn("rate limiter error during provenance upload - continuing")
1019-
// Don't add to uploadErrors - provenance is optional
1020-
return nil
1021-
}
1022-
1023-
timeoutCtx, cancel := context.WithTimeout(ctx, 60*time.Second)
1024-
defer cancel()
1025-
if err := s.storage.UploadObject(timeoutCtx, provenanceKey, provenancePath); err != nil {
1026-
log.WithError(err).WithFields(log.Fields{
1027-
"package": p.FullName(),
1028-
"key": provenanceKey,
1029-
}).Warn("failed to upload provenance bundle to remote cache - continuing")
1030-
// Don't add to uploadErrors - provenance is optional
1031-
} else {
1032-
log.WithFields(log.Fields{
1033-
"package": p.FullName(),
1034-
"key": provenanceKey,
1035-
}).Debug("successfully uploaded provenance bundle to remote cache")
1036-
}
1037-
}
974+
// Upload provenance bundle if it exists (non-blocking)
975+
s.uploadProvenanceBundle(ctx, p.FullName(), key, localPath)
1038976

1039977
return nil
1040978
})
@@ -1300,3 +1238,118 @@ func fileExists(filename string) bool {
13001238
}
13011239
return !info.IsDir()
13021240
}
1241+
1242+
// uploadProvenanceBundle uploads a provenance bundle to S3 with retry logic.
1243+
// This is a non-blocking operation - failures are logged but don't fail the build.
1244+
// Provenance bundles are stored alongside artifacts as <artifact>.provenance.jsonl
1245+
// and are needed for dependency provenance collection during local builds.
1246+
func (s *S3Cache) uploadProvenanceBundle(ctx context.Context, packageName, artifactKey, localPath string) {
1247+
provenancePath := localPath + ".provenance.jsonl"
1248+
1249+
// Check if provenance file exists
1250+
if !fileExists(provenancePath) {
1251+
log.WithFields(log.Fields{
1252+
"package": packageName,
1253+
"path": provenancePath,
1254+
}).Debug("Provenance bundle not found locally, skipping upload")
1255+
return
1256+
}
1257+
1258+
provenanceKey := artifactKey + ".provenance.jsonl"
1259+
1260+
// Wait for rate limiter permission
1261+
if err := s.waitForRateLimit(ctx); err != nil {
1262+
log.WithError(err).WithFields(log.Fields{
1263+
"package": packageName,
1264+
"key": provenanceKey,
1265+
}).Warn("Rate limiter error during provenance upload, skipping")
1266+
return
1267+
}
1268+
1269+
// Upload with timeout and retry logic (via storage layer)
1270+
uploadCtx, cancel := context.WithTimeout(ctx, 60*time.Second)
1271+
defer cancel()
1272+
1273+
if err := s.storage.UploadObject(uploadCtx, provenanceKey, provenancePath); err != nil {
1274+
log.WithError(err).WithFields(log.Fields{
1275+
"package": packageName,
1276+
"key": provenanceKey,
1277+
"path": provenancePath,
1278+
}).Warn("Failed to upload provenance bundle to remote cache")
1279+
return
1280+
}
1281+
1282+
log.WithFields(log.Fields{
1283+
"package": packageName,
1284+
"key": provenanceKey,
1285+
}).Debug("Successfully uploaded provenance bundle to remote cache")
1286+
}
1287+
1288+
// downloadProvenanceBundle downloads a provenance bundle from S3 with verification.
1289+
// This is a best-effort operation - missing provenance is expected for older artifacts.
1290+
// Returns true if provenance was successfully downloaded, false otherwise.
1291+
func (s *S3Cache) downloadProvenanceBundle(ctx context.Context, packageName, artifactKey, localPath string) bool {
1292+
provenanceKey := artifactKey + ".provenance.jsonl"
1293+
provenancePath := localPath + ".provenance.jsonl"
1294+
tmpProvenancePath := provenancePath + ".tmp"
1295+
1296+
// Wait for rate limiter permission
1297+
if err := s.waitForRateLimit(ctx); err != nil {
1298+
log.WithError(err).WithFields(log.Fields{
1299+
"package": packageName,
1300+
"key": provenanceKey,
1301+
}).Debug("Rate limiter error during provenance download, skipping")
1302+
return false
1303+
}
1304+
1305+
// Download with timeout
1306+
downloadCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
1307+
defer cancel()
1308+
1309+
bytesDownloaded, err := s.storage.GetObject(downloadCtx, provenanceKey, tmpProvenancePath)
1310+
if err != nil {
1311+
// Provenance not found - this is expected for older artifacts
1312+
log.WithFields(log.Fields{
1313+
"package": packageName,
1314+
"key": provenanceKey,
1315+
}).Debug("Provenance bundle not found in remote cache (expected for older artifacts)")
1316+
s.cleanupTempFiles(tmpProvenancePath)
1317+
return false
1318+
}
1319+
1320+
// Verify the downloaded file exists and has content
1321+
if !fileExists(tmpProvenancePath) {
1322+
log.WithFields(log.Fields{
1323+
"package": packageName,
1324+
"key": provenanceKey,
1325+
}).Warn("Provenance bundle download reported success but file not found")
1326+
s.cleanupTempFiles(tmpProvenancePath)
1327+
return false
1328+
}
1329+
1330+
if bytesDownloaded == 0 {
1331+
log.WithFields(log.Fields{
1332+
"package": packageName,
1333+
"key": provenanceKey,
1334+
}).Warn("Provenance bundle downloaded but file is empty")
1335+
s.cleanupTempFiles(tmpProvenancePath)
1336+
return false
1337+
}
1338+
1339+
// Atomically move to final location
1340+
if err := s.atomicMove(tmpProvenancePath, provenancePath); err != nil {
1341+
log.WithError(err).WithFields(log.Fields{
1342+
"package": packageName,
1343+
"key": provenanceKey,
1344+
}).Warn("Failed to move provenance bundle to final location")
1345+
s.cleanupTempFiles(tmpProvenancePath)
1346+
return false
1347+
}
1348+
1349+
log.WithFields(log.Fields{
1350+
"package": packageName,
1351+
"key": provenanceKey,
1352+
"bytes": bytesDownloaded,
1353+
}).Debug("Successfully downloaded provenance bundle")
1354+
return true
1355+
}

0 commit comments

Comments
 (0)