github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/artifact/vm/ebs.go (about) 1 package vm 2 3 import ( 4 "context" 5 "io" 6 7 lru "github.com/hashicorp/golang-lru/v2" 8 ebsfile "github.com/masahiro331/go-ebs-file" 9 "golang.org/x/xerrors" 10 11 "github.com/devseccon/trivy/pkg/cloud/aws/config" 12 "github.com/devseccon/trivy/pkg/fanal/cache" 13 "github.com/devseccon/trivy/pkg/fanal/types" 14 "github.com/devseccon/trivy/pkg/log" 15 ) 16 17 // default block size 512 KB 18 // Max cache memory size 64 MB 19 const storageEBSCacheSize = 128 20 21 // EBS represents an artifact for AWS EBS snapshots 22 type EBS struct { 23 Storage 24 snapshotID string 25 ebs ebsfile.EBSAPI 26 } 27 28 func newEBS(snapshotID string, vm Storage, region, endpoint string) (*EBS, error) { 29 ebs, err := ebsfile.New(context.TODO(), config.MakeAWSOptions(region, endpoint)...) 30 if err != nil { 31 return nil, xerrors.Errorf("new ebsfile error: %w", err) 32 } 33 34 return &EBS{ 35 Storage: vm, 36 snapshotID: snapshotID, 37 ebs: ebs, 38 }, nil 39 } 40 41 func (a *EBS) Inspect(ctx context.Context) (types.ArtifactReference, error) { 42 sr, err := a.openEBS(ctx) 43 if err != nil { 44 return types.ArtifactReference{}, xerrors.Errorf("EBS open error: %w", err) 45 } 46 47 cacheKey, err := a.calcCacheKey(a.snapshotID) 48 if err != nil { 49 return types.ArtifactReference{}, xerrors.Errorf("cache key calculation error: %w", err) 50 } 51 52 if a.hasCache(cacheKey) { 53 return types.ArtifactReference{ 54 Name: a.snapshotID, 55 Type: types.ArtifactVM, 56 ID: cacheKey, // use a cache key as pseudo artifact ID 57 BlobIDs: []string{cacheKey}, 58 }, nil 59 } 60 61 blobInfo, err := a.Analyze(ctx, sr) 62 if err != nil { 63 return types.ArtifactReference{}, xerrors.Errorf("inspection error: %w", err) 64 } 65 66 if err = a.cache.PutBlob(cacheKey, blobInfo); err != nil { 67 return types.ArtifactReference{}, xerrors.Errorf("failed to store blob (%s) in cache: %w", cacheKey, err) 68 } 69 70 return types.ArtifactReference{ 71 Name: a.snapshotID, 72 Type: types.ArtifactVM, 73 ID: cacheKey, // use a cache key as pseudo artifact ID 74 BlobIDs: []string{cacheKey}, 75 }, nil 76 } 77 78 func (a *EBS) openEBS(ctx context.Context) (*io.SectionReader, error) { 79 c, err := lru.New[string, []byte](storageEBSCacheSize) 80 if err != nil { 81 return nil, xerrors.Errorf("lru cache error: %w", err) 82 } 83 84 r, err := ebsfile.Open(a.snapshotID, ctx, c, a.ebs) 85 if err != nil { 86 return nil, xerrors.Errorf("EBS error: %w", err) 87 } 88 return r, nil 89 } 90 91 func (a *EBS) Clean(_ types.ArtifactReference) error { 92 return nil 93 } 94 95 func (a *EBS) SetEBS(ebs ebsfile.EBSAPI) { 96 a.ebs = ebs 97 } 98 99 func (a *EBS) calcCacheKey(key string) (string, error) { 100 s, err := cache.CalcKey(key, a.analyzer.AnalyzerVersions(), a.handlerManager.Versions(), a.artifactOption) 101 if err != nil { 102 return "", xerrors.Errorf("failed to calculate cache key: %w", err) 103 } 104 return s, nil 105 } 106 107 func (a *EBS) hasCache(cacheKey string) bool { 108 _, missingCacheKeys, err := a.cache.MissingBlobs(cacheKey, []string{cacheKey}) 109 if err != nil { 110 log.Logger.Debugf("Unable to query missing cache: %s", err) 111 return false 112 } 113 114 // Cache exists 115 if len(missingCacheKeys) == 0 { 116 return true 117 } 118 119 log.Logger.Debugf("Missing virtual machine cache: %s", cacheKey) 120 return false 121 }