mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-23 15:37:50 -08:00
analyzer_test: Adding seams for testability
Signed-off-by: Simarpreet Singh <simar@linux.com>
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/fanal/utils"
|
||||
|
||||
@@ -33,6 +32,10 @@ var (
|
||||
ErrNoPkgsDetected = xerrors.New("No packages detected")
|
||||
)
|
||||
|
||||
type AnalyzerConfig struct {
|
||||
Extractor extractor.Extractor
|
||||
}
|
||||
|
||||
type OSAnalyzer interface {
|
||||
Analyze(extractor.FileMap) (OS, error)
|
||||
RequiredFiles() []string
|
||||
@@ -113,28 +116,19 @@ func RequiredFilenames() []string {
|
||||
return filenames
|
||||
}
|
||||
|
||||
func Analyze(ctx context.Context, imageName string, opts ...types.DockerOption) (fileMap extractor.FileMap, err error) {
|
||||
// default docker option
|
||||
opt := types.DockerOption{
|
||||
Timeout: 600 * time.Second,
|
||||
SkipPing: true,
|
||||
}
|
||||
if len(opts) > 0 {
|
||||
opt = opts[0]
|
||||
}
|
||||
|
||||
e := docker.NewDockerExtractor(opt)
|
||||
r, err := e.SaveLocalImage(ctx, imageName)
|
||||
// TODO: Remove opts as they're no longer needed
|
||||
func (ac AnalyzerConfig) Analyze(ctx context.Context, imageName string, opts ...types.DockerOption) (fileMap extractor.FileMap, err error) {
|
||||
r, err := ac.Extractor.SaveLocalImage(ctx, imageName)
|
||||
if err != nil {
|
||||
// when no docker daemon is installed or no image exists in the local machine
|
||||
fileMap, err = e.Extract(ctx, imageName, RequiredFilenames())
|
||||
fileMap, err = ac.Extractor.Extract(ctx, imageName, RequiredFilenames())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to extract files: %w", err)
|
||||
}
|
||||
return fileMap, nil
|
||||
}
|
||||
|
||||
fileMap, err = e.ExtractFromFile(ctx, r, RequiredFilenames())
|
||||
fileMap, err = ac.Extractor.ExtractFromFile(ctx, r, RequiredFilenames())
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to extract files from saved tar: %w", err)
|
||||
}
|
||||
|
||||
45
analyzer/analyzer_test.go
Normal file
45
analyzer/analyzer_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/fanal/extractor"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type mockDockerExtractor struct {
|
||||
saveLocalImage func(ctx context.Context, imageName string) (io.Reader, error)
|
||||
extractFromFile func(ctx context.Context, r io.Reader, filenames []string) (extractor.FileMap, error)
|
||||
}
|
||||
|
||||
func (mde mockDockerExtractor) Extract(ctx context.Context, imageName string, filenames []string) (extractor.FileMap, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (mde mockDockerExtractor) ExtractFromFile(ctx context.Context, r io.Reader, filenames []string) (extractor.FileMap, error) {
|
||||
if mde.extractFromFile != nil {
|
||||
return mde.extractFromFile(ctx, r, filenames)
|
||||
}
|
||||
return extractor.FileMap{}, nil
|
||||
}
|
||||
|
||||
func (mde mockDockerExtractor) SaveLocalImage(ctx context.Context, imageName string) (io.Reader, error) {
|
||||
if mde.saveLocalImage != nil {
|
||||
return mde.saveLocalImage(ctx, imageName)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mde mockDockerExtractor) ExtractFiles(layer io.Reader, filenames []string) (extractor.FileMap, extractor.OPQDirs, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func TestAnalyze(t *testing.T) {
|
||||
ac := AnalyzerConfig{Extractor: mockDockerExtractor{}}
|
||||
fm, err := ac.Analyze(context.TODO(), "foo")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, fm)
|
||||
}
|
||||
@@ -60,19 +60,17 @@ type layer struct {
|
||||
Content io.ReadCloser
|
||||
}
|
||||
|
||||
type opqDirs []string
|
||||
|
||||
type DockerExtractor struct {
|
||||
Option types.DockerOption
|
||||
}
|
||||
|
||||
func NewDockerExtractor(option types.DockerOption) DockerExtractor {
|
||||
func NewDockerExtractor(option types.DockerOption) extractor.Extractor {
|
||||
RegisterRegistry(&gcr.GCR{})
|
||||
RegisterRegistry(&ecr.ECR{})
|
||||
return DockerExtractor{Option: option}
|
||||
}
|
||||
|
||||
func applyLayers(layerPaths []string, filesInLayers map[string]extractor.FileMap, opqInLayers map[string]opqDirs) (extractor.FileMap, error) {
|
||||
func applyLayers(layerPaths []string, filesInLayers map[string]extractor.FileMap, opqInLayers map[string]extractor.OPQDirs) (extractor.FileMap, error) {
|
||||
sep := "/"
|
||||
nestedMap := nested.Nested{}
|
||||
for _, layerPath := range layerPaths {
|
||||
@@ -190,7 +188,8 @@ func (d DockerExtractor) Extract(ctx context.Context, imageName string, filename
|
||||
|
||||
ch := make(chan layer)
|
||||
errCh := make(chan error)
|
||||
layerIDs := []string{}
|
||||
var layerIDs []string
|
||||
|
||||
for _, ref := range m.Manifest.Layers {
|
||||
layerIDs = append(layerIDs, string(ref.Digest))
|
||||
go func(d digest.Digest) {
|
||||
@@ -203,6 +202,7 @@ func (d DockerExtractor) Extract(ctx context.Context, imageName string, filename
|
||||
errCh <- xerrors.Errorf("failed to download the layer(%s): %w", d, err)
|
||||
return
|
||||
}
|
||||
|
||||
rc, err = cache.Set(string(d), rc)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@@ -218,7 +218,7 @@ func (d DockerExtractor) Extract(ctx context.Context, imageName string, filename
|
||||
}
|
||||
|
||||
filesInLayers := make(map[string]extractor.FileMap)
|
||||
opqInLayers := make(map[string]opqDirs)
|
||||
opqInLayers := make(map[string]extractor.OPQDirs)
|
||||
for i := 0; i < len(m.Manifest.Layers); i++ {
|
||||
var l layer
|
||||
select {
|
||||
@@ -262,7 +262,7 @@ func (d DockerExtractor) Extract(ctx context.Context, imageName string, filename
|
||||
func (d DockerExtractor) ExtractFromFile(ctx context.Context, r io.Reader, filenames []string) (extractor.FileMap, error) {
|
||||
manifests := make([]manifest, 0)
|
||||
filesInLayers := map[string]extractor.FileMap{}
|
||||
opqInLayers := make(map[string]opqDirs)
|
||||
opqInLayers := make(map[string]extractor.OPQDirs)
|
||||
|
||||
tarFiles := make(map[string][]byte)
|
||||
|
||||
@@ -330,9 +330,9 @@ func (d DockerExtractor) ExtractFromFile(ctx context.Context, r io.Reader, filen
|
||||
return fileMap, nil
|
||||
}
|
||||
|
||||
func (d DockerExtractor) ExtractFiles(layer io.Reader, filenames []string) (extractor.FileMap, opqDirs, error) {
|
||||
func (d DockerExtractor) ExtractFiles(layer io.Reader, filenames []string) (extractor.FileMap, extractor.OPQDirs, error) {
|
||||
data := make(map[string][]byte)
|
||||
opqDirs := opqDirs{}
|
||||
opqDirs := extractor.OPQDirs{}
|
||||
|
||||
tr := tar.NewReader(layer)
|
||||
for {
|
||||
|
||||
@@ -122,7 +122,7 @@ func TestExtractFiles(t *testing.T) {
|
||||
file string // Test input file
|
||||
filenames []string // Target files
|
||||
FileMap extractor.FileMap // Expected output
|
||||
opqDirs opqDirs // Expected output
|
||||
opqDirs OPQDirs // Expected output
|
||||
err error // Expected error to occur
|
||||
}{
|
||||
{
|
||||
@@ -167,7 +167,7 @@ func TestExtractFiles(t *testing.T) {
|
||||
t.Errorf("err: got %v, want %v", v.err, err)
|
||||
}
|
||||
if !reflect.DeepEqual(opqDirs, v.opqDirs) {
|
||||
t.Errorf("opqDirs: got %v, want %v", opqDirs, v.opqDirs)
|
||||
t.Errorf("OPQDirs: got %v, want %v", opqDirs, v.opqDirs)
|
||||
}
|
||||
if !reflect.DeepEqual(fm, v.FileMap) {
|
||||
t.Errorf("FilesMap: got %v, want %v", fm, v.FileMap)
|
||||
|
||||
@@ -6,8 +6,11 @@ import (
|
||||
)
|
||||
|
||||
type FileMap map[string][]byte
|
||||
type OPQDirs []string
|
||||
|
||||
type Extractor interface {
|
||||
Extract(ctx context.Context, imageName string, filenames []string) (FileMap, error)
|
||||
ExtractFromFile(ctx context.Context, r io.Reader, filenames []string) (FileMap, error)
|
||||
SaveLocalImage(ctx context.Context, imageName string) (io.Reader, error)
|
||||
ExtractFiles(layer io.Reader, filenames []string) (FileMap, OPQDirs, error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user