analyzer_test: Adding seams for testability

Signed-off-by: Simarpreet Singh <simar@linux.com>
This commit is contained in:
Simarpreet Singh
2019-11-18 14:42:36 -08:00
parent 95e89a424e
commit 985442dc1c
5 changed files with 68 additions and 26 deletions

View File

@@ -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
View 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)
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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)
}