Skip to content

Commit 5564fcc

Browse files
committed
performance improvements
1 parent 2f6c92a commit 5564fcc

File tree

3 files changed

+119
-28
lines changed

3 files changed

+119
-28
lines changed

cmd/validate/vsa.go

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -375,8 +375,8 @@ func validateImagesFromRekor(ctx context.Context, cmd *cobra.Command, data struc
375375
continue
376376
}
377377

378-
// Call the validation function
379-
validationResult, err := validate(ctx, comp.ContainerImage, data.policy, rekorRetriever, data.publicKey)
378+
// Call the validation function with content retrieval
379+
validationResult, vsaContent, err := vsa.ValidateVSAWithContent(ctx, comp.ContainerImage, data.policy, rekorRetriever, data.publicKey)
380380
if err != nil {
381381
err = fmt.Errorf("validation failed for %s: %w", comp.ContainerImage, err)
382382
results <- result{err: err, component: comp, validationResult: nil, vsaComponents: nil}
@@ -386,18 +386,14 @@ func validateImagesFromRekor(ctx context.Context, cmd *cobra.Command, data struc
386386
continue
387387
}
388388

389-
// Extract actual components from VSA attestation data
389+
// Extract actual components from VSA attestation data (no redundant retrieval)
390390
var vsaComponents []applicationsnapshot.Component
391-
if validationResult != nil {
392-
// Try to retrieve VSA data to extract actual components
393-
vsaContent, err := rekorRetriever.RetrieveVSAData(ctx)
394-
if err == nil {
395-
predicate, err := vsa.ParseVSAContent(vsaContent)
396-
if err == nil && predicate.Results != nil {
397-
// Use actual components from VSA attestation if available
398-
vsaComponents = predicate.Results.Components
399-
logrus.Debugf("Extracted %d actual components from VSA attestation for %s", len(vsaComponents), comp.ContainerImage)
400-
}
391+
if validationResult != nil && vsaContent != "" {
392+
predicate, err := vsa.ParseVSAContent(vsaContent)
393+
if err == nil && predicate.Results != nil {
394+
// Use actual components from VSA attestation if available
395+
vsaComponents = predicate.Results.Components
396+
logrus.Debugf("Extracted %d actual components from VSA attestation for %s", len(vsaComponents), comp.ContainerImage)
401397
}
402398
}
403399

internal/validate/vsa/rekor_retriever.go

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"strconv"
2828
"strings"
2929
"sync"
30+
"time"
3031

3132
"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
3233
"github.com/sigstore/rekor/pkg/generated/client"
@@ -725,10 +726,10 @@ func (rc *rekorClient) worker(ctx context.Context, uuidChan <-chan string, resul
725726
// Continue processing
726727
}
727728

728-
// Fetch the log entry
729-
entry, err := rc.GetLogEntryByUUID(ctx, uuid)
729+
// Fetch the log entry with retry logic
730+
entry, err := rc.GetLogEntryByUUIDWithRetry(ctx, uuid, 3)
730731
if err != nil {
731-
log.Debugf("Worker %d: Failed to fetch log entry for UUID %s: %v", workerID, uuid, err)
732+
log.Debugf("Worker %d: Failed to fetch log entry for UUID %s after retries: %v", workerID, uuid, err)
732733
select {
733734
case resultChan <- fetchResult{entry: nil, err: err}:
734735
case <-ctx.Done():
@@ -748,8 +749,8 @@ func (rc *rekorClient) worker(ctx context.Context, uuidChan <-chan string, resul
748749

749750
// getWorkerCount returns the number of workers to use for parallel operations
750751
func (rc *rekorClient) getWorkerCount() int {
751-
// Default to 8 workers
752-
defaultWorkers := 8
752+
// Default to 4 workers (optimized for Rekor rate limits)
753+
defaultWorkers := 4
753754

754755
// Check environment variable
755756
if workerStr := os.Getenv("EC_REKOR_WORKERS"); workerStr != "" {
@@ -840,6 +841,65 @@ func (rc *rekorClient) GetLogEntryByUUID(ctx context.Context, uuid string) (*mod
840841
return nil, fmt.Errorf("log entry not found for UUID: %s", uuid)
841842
}
842843

844+
// GetLogEntryByUUIDWithRetry fetches a log entry with exponential backoff retry logic
845+
func (rc *rekorClient) GetLogEntryByUUIDWithRetry(ctx context.Context, uuid string, maxRetries int) (*models.LogEntryAnon, error) {
846+
var lastErr error
847+
848+
for attempt := 0; attempt <= maxRetries; attempt++ {
849+
if attempt > 0 {
850+
// Exponential backoff: 100ms, 200ms, 400ms
851+
backoff := time.Duration(100*attempt) * time.Millisecond
852+
log.Debugf("Retrying GetLogEntryByUUID for UUID %s (attempt %d/%d) after %v", uuid, attempt+1, maxRetries+1, backoff)
853+
854+
select {
855+
case <-time.After(backoff):
856+
// Continue with retry
857+
case <-ctx.Done():
858+
return nil, ctx.Err()
859+
}
860+
}
861+
862+
entry, err := rc.GetLogEntryByUUID(ctx, uuid)
863+
if err == nil {
864+
if attempt > 0 {
865+
log.Debugf("GetLogEntryByUUID succeeded for UUID %s on attempt %d", uuid, attempt+1)
866+
}
867+
return entry, nil
868+
}
869+
870+
lastErr = err
871+
872+
// Don't retry on certain types of errors (e.g., not found, authentication)
873+
if isNonRetryableError(err) {
874+
log.Debugf("Non-retryable error for UUID %s: %v", uuid, err)
875+
break
876+
}
877+
}
878+
879+
return nil, fmt.Errorf("failed to fetch log entry for UUID %s after %d attempts: %w", uuid, maxRetries+1, lastErr)
880+
}
881+
882+
// isNonRetryableError determines if an error should not be retried
883+
func isNonRetryableError(err error) bool {
884+
if err == nil {
885+
return false
886+
}
887+
888+
errStr := err.Error()
889+
// Don't retry on authentication, authorization, or not found errors
890+
nonRetryablePatterns := []string{
891+
"401", "403", "404", "not found", "unauthorized", "forbidden",
892+
}
893+
894+
for _, pattern := range nonRetryablePatterns {
895+
if strings.Contains(strings.ToLower(errStr), pattern) {
896+
return true
897+
}
898+
}
899+
900+
return false
901+
}
902+
843903
// RekorVSADataRetriever implements VSADataRetriever for Rekor-based VSA retrieval
844904
type RekorVSADataRetriever struct {
845905
rekorRetriever *RekorVSARetriever

internal/validate/vsa/validation.go

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"encoding/json"
2424
"fmt"
2525
"strings"
26+
"sync"
2627

2728
"github.com/google/go-containerregistry/pkg/name"
2829
ssldsse "github.com/secure-systems-lab/go-securesystemslib/dsse"
@@ -279,27 +280,39 @@ func compareRules(vsaRuleResults map[string][]RuleResult, requiredRules map[stri
279280
return result
280281
}
281282

283+
// ValidationResultWithContent contains both validation result and VSA content
284+
type ValidationResultWithContent struct {
285+
*ValidationResult
286+
VSAContent string
287+
}
288+
282289
// ValidateVSA is the main validation function called by the command
283290
func ValidateVSA(ctx context.Context, imageRef string, policy policy.Policy, retriever VSADataRetriever, publicKey string) (*ValidationResult, error) {
291+
result, _, err := ValidateVSAWithContent(ctx, imageRef, policy, retriever, publicKey)
292+
return result, err
293+
}
294+
295+
// ValidateVSAWithContent returns both validation result and VSA content to avoid redundant retrieval
296+
func ValidateVSAWithContent(ctx context.Context, imageRef string, policy policy.Policy, retriever VSADataRetriever, publicKey string) (*ValidationResult, string, error) {
284297
// Extract digest from image reference
285298
ref, err := name.ParseReference(imageRef)
286299
if err != nil {
287-
return nil, fmt.Errorf("invalid image reference: %w", err)
300+
return nil, "", fmt.Errorf("invalid image reference: %w", err)
288301
}
289302

290303
digest := ref.Identifier()
291304

292305
// Retrieve VSA data using the provided retriever
293306
vsaContent, err := retriever.RetrieveVSAData(ctx)
294307
if err != nil {
295-
return nil, fmt.Errorf("failed to retrieve VSA data: %w", err)
308+
return nil, "", fmt.Errorf("failed to retrieve VSA data: %w", err)
296309
}
297310

298311
// Verify signature if public key is provided
299312
signatureVerified := false
300313
if publicKey != "" {
301314
if vsaContent == "" {
302-
return nil, fmt.Errorf("signature verification not supported for this VSA retriever")
315+
return nil, "", fmt.Errorf("signature verification not supported for this VSA retriever")
303316
}
304317
if err := verifyVSASignature(vsaContent, publicKey); err != nil {
305318
// For now, log the error but don't fail the validation
@@ -314,7 +327,7 @@ func ValidateVSA(ctx context.Context, imageRef string, policy policy.Policy, ret
314327
// Parse the VSA content to extract violations and successes
315328
predicate, err := ParseVSAContent(vsaContent)
316329
if err != nil {
317-
return nil, fmt.Errorf("failed to parse VSA content: %w", err)
330+
return nil, "", fmt.Errorf("failed to parse VSA content: %w", err)
318331
}
319332

320333
// Create policy resolver and discover available rules
@@ -335,7 +348,7 @@ func ValidateVSA(ctx context.Context, imageRef string, policy policy.Policy, ret
335348
ruleDiscovery := evaluator.NewRuleDiscoveryService()
336349
rules, nonAnnotatedRules, err := ruleDiscovery.DiscoverRulesWithNonAnnotated(ctx, policySources)
337350
if err != nil {
338-
return nil, fmt.Errorf("failed to discover rules from policy sources: %w", err)
351+
return nil, "", fmt.Errorf("failed to discover rules from policy sources: %w", err)
339352
}
340353

341354
// Combine rules for filtering
@@ -356,7 +369,7 @@ func ValidateVSA(ctx context.Context, imageRef string, policy policy.Policy, ret
356369
if vsaPolicyResolver != nil {
357370
requiredRules, err = vsaPolicyResolver.GetRequiredRules(ctx, digest)
358371
if err != nil {
359-
return nil, fmt.Errorf("failed to get required rules from policy: %w", err)
372+
return nil, "", fmt.Errorf("failed to get required rules from policy: %w", err)
360373
}
361374
} else {
362375
// If no policy resolver is available, consider all rules in VSA as required
@@ -370,15 +383,37 @@ func ValidateVSA(ctx context.Context, imageRef string, policy policy.Policy, ret
370383
result := compareRules(vsaRuleResults, requiredRules, digest)
371384
result.SignatureVerified = signatureVerified
372385

373-
return result, nil
386+
return result, vsaContent, nil
374387
}
375388

376-
// extractPackageFromCode extracts the package name from a rule code
389+
// packageCache caches package name extractions to avoid repeated string operations
390+
var packageCache = make(map[string]string)
391+
var packageCacheMutex sync.RWMutex
392+
393+
// extractPackageFromCode extracts the package name from a rule code with caching
377394
func extractPackageFromCode(code string) string {
395+
// Check cache first
396+
packageCacheMutex.RLock()
397+
if cached, exists := packageCache[code]; exists {
398+
packageCacheMutex.RUnlock()
399+
return cached
400+
}
401+
packageCacheMutex.RUnlock()
402+
403+
// Extract package name
404+
var packageName string
378405
if idx := strings.Index(code, "."); idx != -1 {
379-
return code[:idx]
406+
packageName = code[:idx]
407+
} else {
408+
packageName = code
380409
}
381-
return code
410+
411+
// Cache the result
412+
packageCacheMutex.Lock()
413+
packageCache[code] = packageName
414+
packageCacheMutex.Unlock()
415+
416+
return packageName
382417
}
383418

384419
// verifyVSASignature verifies the signature of a VSA file using cosign's DSSE verification

0 commit comments

Comments
 (0)