Skip to content

Commit 495332c

Browse files
knqyf263simar7
andauthored
refactor: replace genuinetools/reg with containers/image (fanal#70)
* chore(ci): remove unused lines * feat(cache): add SetBytes * refactor(cache): replace Initialize with New * fix(cache): use ReadCloser instead of Reader * fix(option): update options according to containers/image * feat(image): add struct to manipulate an image * refactor(token): move the directory * chore(Makefile): fix test * chore(Makefile): add containers_image_storage_stub tag * refactor(docker): use Image * refactor(docker): remove unused functions * refactor(docker): update imports * test(docker): fix tests * refactor(analyer): use containers/image * chore(mod): update dependencies * fix(extractor): update interface * fix(main): use updated functions * test(integration): fix * refactor(image): remove unused definition * refactor(error): wrap errors * test(image): add TestNewImage * test(mock): prepare interfaces * test(mock): generate mocks * test(image): add TestImage_LayerInfos * test(image): add TestImage_ConfigBlob * test(image): add TestImage_GetBlob * chore(mod): update dependencies * refactor(error): wrap errors * fix(auth): pass nil when auth is empty * chore(Makefile): add a tag * test(bench): fix * chore(bench): introduce cob * chore(ci): restrict a push trigger * chore(bench): run benchmarks 10 times * test(bench): use a random tag * test(integration): remove ImageRemove * chore(cob): set threshold to 0.7 * image_test: Add unhappy paths for GetBlob Signed-off-by: Simarpreet Singh <[email protected]> * refactor(image): remove unused fuction * fix(image): close io.ReadCloser via cleanup function * test(image): do not skip populateSource Co-authored-by: Simarpreet Singh <[email protected]>
1 parent 285e1f1 commit 495332c

File tree

26 files changed

+1584
-742
lines changed

26 files changed

+1584
-742
lines changed

.github/workflows/test.yml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
name: Test
2-
on: [push, pull_request]
2+
on:
3+
push:
4+
branches:
5+
- master
6+
pull_request:
37
jobs:
48
unittest:
59
name: Unit Test
@@ -13,8 +17,7 @@ jobs:
1317
id: go
1418

1519
- name: Check out code into the Go module directory
16-
uses: actions/checkout@v1
17-
20+
uses: actions/checkout@v1
1821
- name: Install dependencies
1922
run: sudo apt-get install libdb-dev
2023

@@ -58,5 +61,8 @@ jobs:
5861
- name: Install dependencies
5962
run: sudo apt-get install libdb-dev
6063

64+
- name: Install cob
65+
run: curl -sfL https://raw.githubusercontent.com/knqyf263/cob/master/install.sh | sudo sh -s -- -b /usr/local/bin
66+
6167
- name: Run Benchmark
62-
run: make test-performance
68+
run: cob --threshold 0.7 --base origin/master --bench-cmd make --bench-args test-performance

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ deps:
44

55
.PHONY: test
66
test:
7-
go test ./...
7+
go test -tags="containers_image_storage_stub" ./...
88

99
.PHONY: lint
1010
lint: devel-deps
@@ -20,8 +20,8 @@ integration/testdata/fixtures/*.tar.gz:
2020

2121
.PHONY: test-integration
2222
test-integration: integration/testdata/fixtures/*.tar.gz
23-
go test -v -tags=integration ./integration/...
23+
go test -v -tags="integration containers_image_storage_stub" ./integration/...
2424

2525
.PHONY: test-performance
2626
test-performance: integration/testdata/fixtures/*.tar.gz
27-
go test -v -tags=performance -bench=. ./integration/...
27+
go test -v -benchtime=10x -tags="performance containers_image_storage_stub" -bench=. ./integration/...

analyzer/analyzer.go

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
package analyzer
22

33
import (
4-
"bufio"
5-
"compress/gzip"
64
"context"
7-
"io"
8-
"os"
9-
10-
"github.com/aquasecurity/fanal/utils"
11-
12-
"github.com/aquasecurity/fanal/types"
135

146
"golang.org/x/xerrors"
157

168
"github.com/aquasecurity/fanal/extractor"
9+
"github.com/aquasecurity/fanal/extractor/image"
10+
"github.com/aquasecurity/fanal/types"
1711
godeptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
1812
)
1913

@@ -118,37 +112,21 @@ func RequiredFilenames() []string {
118112

119113
// TODO: Remove opts as they're no longer needed
120114
func (ac Config) Analyze(ctx context.Context, imageName string, opts ...types.DockerOption) (fileMap extractor.FileMap, err error) {
121-
r, err := ac.Extractor.SaveLocalImage(ctx, imageName)
122-
if err != nil {
123-
// when no docker daemon is installed or no image exists in the local machine
124-
fileMap, err = ac.Extractor.Extract(ctx, imageName, RequiredFilenames())
125-
if err != nil {
126-
return nil, xerrors.Errorf("failed to extract files: %w", err)
127-
}
128-
return fileMap, nil
129-
}
130-
131-
fileMap, err = ac.Extractor.ExtractFromFile(ctx, r, RequiredFilenames())
115+
transports := []string{"docker-daemon:", "docker://"}
116+
ref := image.Reference{Name: imageName, IsFile: false}
117+
fileMap, err = ac.Extractor.Extract(ctx, ref, transports, RequiredFilenames())
132118
if err != nil {
133-
return nil, xerrors.Errorf("failed to extract files from saved tar: %w", err)
119+
return nil, xerrors.Errorf("failed to extract files: %w", err)
134120
}
135121
return fileMap, nil
136122
}
137123

138-
func (ac Config) AnalyzeFile(ctx context.Context, f *os.File) (fileMap extractor.FileMap, err error) {
139-
var r io.Reader
140-
br := bufio.NewReader(f)
141-
if utils.IsGzip(br) {
142-
r, err = gzip.NewReader(br)
143-
if err != nil {
144-
return nil, xerrors.Errorf("failed to open gzip: %w", err)
145-
}
146-
} else {
147-
r = br
148-
}
149-
fileMap, err = ac.Extractor.ExtractFromFile(ctx, r, RequiredFilenames())
124+
func (ac Config) AnalyzeFile(ctx context.Context, filePath string) (fileMap extractor.FileMap, err error) {
125+
transports := []string{"docker-archive:"}
126+
ref := image.Reference{Name: filePath, IsFile: true}
127+
fileMap, err = ac.Extractor.Extract(ctx, ref, transports, RequiredFilenames())
150128
if err != nil {
151-
return nil, xerrors.Errorf("failed to extract files from tar: %w", err)
129+
return nil, xerrors.Errorf("failed to extract files: %w", err)
152130
}
153131
return fileMap, nil
154132
}

analyzer/analyzer_test.go

Lines changed: 21 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,25 @@ import (
44
"context"
55
"errors"
66
"io"
7-
"os"
87
"testing"
98

10-
"github.com/aquasecurity/fanal/extractor"
11-
129
"github.com/stretchr/testify/assert"
10+
11+
"github.com/aquasecurity/fanal/extractor"
12+
"github.com/aquasecurity/fanal/extractor/image"
1313
)
1414

1515
type mockDockerExtractor struct {
16-
saveLocalImage func(ctx context.Context, imageName string) (io.Reader, error)
17-
extractFromFile func(ctx context.Context, r io.Reader, filenames []string) (extractor.FileMap, error)
18-
extract func(ctx context.Context, imageName string, filenames []string) (extractor.FileMap, error)
16+
extract func(ctx context.Context, imageRef image.Reference, transports, filenames []string) (extractor.FileMap, error)
1917
}
2018

21-
func (mde mockDockerExtractor) Extract(ctx context.Context, imageName string, filenames []string) (extractor.FileMap, error) {
19+
func (mde mockDockerExtractor) Extract(ctx context.Context, imageRef image.Reference, transports, filenames []string) (extractor.FileMap, error) {
2220
if mde.extract != nil {
23-
return mde.extract(ctx, imageName, filenames)
24-
}
25-
return extractor.FileMap{}, nil
26-
}
27-
28-
func (mde mockDockerExtractor) ExtractFromFile(ctx context.Context, r io.Reader, filenames []string) (extractor.FileMap, error) {
29-
if mde.extractFromFile != nil {
30-
return mde.extractFromFile(ctx, r, filenames)
21+
return mde.extract(ctx, imageRef, transports, filenames)
3122
}
3223
return extractor.FileMap{}, nil
3324
}
3425

35-
func (mde mockDockerExtractor) SaveLocalImage(ctx context.Context, imageName string) (io.Reader, error) {
36-
if mde.saveLocalImage != nil {
37-
return mde.saveLocalImage(ctx, imageName)
38-
}
39-
return nil, nil
40-
}
41-
4226
func (mde mockDockerExtractor) ExtractFiles(layer io.Reader, filenames []string) (extractor.FileMap, extractor.OPQDirs, error) {
4327
panic("implement me")
4428
}
@@ -55,32 +39,14 @@ func (m mockOSAnalyzer) RequiredFiles() []string {
5539

5640
func TestConfig_Analyze(t *testing.T) {
5741
testCases := []struct {
58-
name string
59-
saveLocalImageFunc func(ctx context.Context, imageName string) (io.Reader, error)
60-
extractFunc func(ctx context.Context, imageName string, filenames []string) (extractor.FileMap, error)
61-
extractFromFileFunc func(ctx context.Context, r io.Reader, filenames []string) (maps extractor.FileMap, e error)
62-
expectedError error
63-
expectedFileMap extractor.FileMap
42+
name string
43+
extractFunc func(ctx context.Context, imageRef image.Reference, transports, filenames []string) (extractor.FileMap, error)
44+
expectedError error
45+
expectedFileMap extractor.FileMap
6446
}{
65-
{
66-
name: "happy path with docker installed and image found",
67-
extractFromFileFunc: func(ctx context.Context, r io.Reader, filenames []string) (maps extractor.FileMap, e error) {
68-
return extractor.FileMap{
69-
"file1": []byte{0x1, 0x2, 0x3},
70-
"file2": []byte{0x4, 0x5, 0x6},
71-
}, nil
72-
},
73-
expectedFileMap: extractor.FileMap{
74-
"file1": []byte{0x1, 0x2, 0x3},
75-
"file2": []byte{0x4, 0x5, 0x6},
76-
},
77-
},
7847
{
7948
name: "happy path with no docker installed or no image found",
80-
saveLocalImageFunc: func(ctx context.Context, imageName string) (reader io.Reader, e error) {
81-
return nil, errors.New("couldn't save local image")
82-
},
83-
extractFunc: func(ctx context.Context, imageName string, filenames []string) (maps extractor.FileMap, e error) {
49+
extractFunc: func(ctx context.Context, imageRef image.Reference, transports, filenames []string) (maps extractor.FileMap, e error) {
8450
return extractor.FileMap{
8551
"file1": []byte{0x1, 0x2, 0x3},
8652
"file2": []byte{0x4, 0x5, 0x6},
@@ -97,9 +63,7 @@ func TestConfig_Analyze(t *testing.T) {
9763
RegisterOSAnalyzer(mockOSAnalyzer{})
9864

9965
ac := Config{Extractor: mockDockerExtractor{
100-
extractFromFile: tc.extractFromFileFunc,
101-
extract: tc.extractFunc,
102-
saveLocalImage: tc.saveLocalImageFunc,
66+
extract: tc.extractFunc,
10367
}}
10468
fm, err := ac.Analyze(context.TODO(), "fooimage")
10569
assert.Equal(t, tc.expectedError, err, tc.name)
@@ -112,11 +76,11 @@ func TestConfig_Analyze(t *testing.T) {
11276

11377
func TestConfig_AnalyzeFile(t *testing.T) {
11478
testCases := []struct {
115-
name string
116-
extractFromFileFunc func(ctx context.Context, r io.Reader, filenames []string) (fileMap extractor.FileMap, err error)
117-
inputFile string
118-
expectedError error
119-
expectedFileMap extractor.FileMap
79+
name string
80+
extractFunc func(ctx context.Context, imageReference image.Reference, transports, filenames []string) (extractor.FileMap, error)
81+
inputFile string
82+
expectedError error
83+
expectedFileMap extractor.FileMap
12084
}{
12185
{
12286
name: "happy path, valid tar.gz file",
@@ -130,8 +94,8 @@ func TestConfig_AnalyzeFile(t *testing.T) {
13094
},
13195
{
13296
name: "sad path, valid file but ExtractFromFile fails",
133-
expectedError: errors.New("failed to extract files from tar: extract from file failed"),
134-
extractFromFileFunc: func(ctx context.Context, r io.Reader, filenames []string) (fileMap extractor.FileMap, err error) {
97+
expectedError: errors.New("failed to extract files: extract from file failed"),
98+
extractFunc: func(ctx context.Context, imageRef image.Reference, transports, filenames []string) (fileMap extractor.FileMap, err error) {
13599
return nil, errors.New("extract from file failed")
136100
},
137101
},
@@ -140,13 +104,11 @@ func TestConfig_AnalyzeFile(t *testing.T) {
140104
for _, tc := range testCases {
141105
ac := Config{
142106
Extractor: mockDockerExtractor{
143-
extractFromFile: tc.extractFromFileFunc,
107+
extract: tc.extractFunc,
144108
},
145109
}
146110

147-
f, _ := os.Open(tc.inputFile)
148-
defer f.Close()
149-
fm, err := ac.AnalyzeFile(context.TODO(), f)
111+
fm, err := ac.AnalyzeFile(context.Background(), tc.inputFile)
150112
switch {
151113
case tc.expectedError != nil:
152114
assert.Equal(t, tc.expectedError.Error(), err.Error(), tc.name)

cache/cache.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,21 @@ var (
1818
)
1919

2020
type Cache interface {
21-
Get(key string) io.Reader
22-
Set(key string, file io.Reader) (io.Reader, error)
23-
Clear() error
21+
Get(key string) (reader io.ReadCloser)
22+
Set(key string, file io.Reader) (reader io.Reader, err error)
23+
SetBytes(key string, value []byte) (err error)
24+
Clear() (err error)
2425
}
2526

2627
type FSCache struct {
2728
directory string
2829
}
2930

30-
func Initialize(cacheDir string) Cache {
31+
func New(cacheDir string) Cache {
3132
return &FSCache{directory: filepath.Join(cacheDir, cacheDirName)}
3233
}
3334

34-
func (fs FSCache) Get(key string) io.Reader {
35+
func (fs FSCache) Get(key string) io.ReadCloser {
3536
filePath := filepath.Join(fs.directory, replacer.Replace(key))
3637
f, err := os.Open(filePath)
3738
if err != nil {
@@ -40,20 +41,36 @@ func (fs FSCache) Get(key string) io.Reader {
4041
return f
4142
}
4243

43-
func (fs FSCache) Set(key string, file io.Reader) (io.Reader, error) {
44+
func (fs FSCache) Set(key string, r io.Reader) (io.Reader, error) {
4445
filePath := filepath.Join(fs.directory, replacer.Replace(key))
4546
if err := os.MkdirAll(fs.directory, os.ModePerm); err != nil {
4647
return nil, xerrors.Errorf("failed to mkdir all: %w", err)
4748
}
4849
cacheFile, err := os.Create(filePath)
4950
if err != nil {
50-
return file, xerrors.Errorf("failed to create cache file: %w", err)
51+
return r, xerrors.Errorf("failed to create cache file: %w", err)
5152
}
5253

53-
tee := io.TeeReader(file, cacheFile)
54+
tee := io.TeeReader(r, cacheFile)
5455
return tee, nil
5556
}
5657

58+
func (fs FSCache) SetBytes(key string, b []byte) error {
59+
filePath := filepath.Join(fs.directory, replacer.Replace(key))
60+
if err := os.MkdirAll(fs.directory, os.ModePerm); err != nil {
61+
return xerrors.Errorf("failed to mkdir all: %w", err)
62+
}
63+
cacheFile, err := os.Create(filePath)
64+
if err != nil {
65+
return xerrors.Errorf("failed to create cache file: %w", err)
66+
}
67+
68+
if _, err := cacheFile.Write(b); err != nil {
69+
return xerrors.Errorf("cache write error: %w", err)
70+
}
71+
return nil
72+
}
73+
5774
func (fs FSCache) Clear() error {
5875
if err := os.RemoveAll(fs.directory); err != nil {
5976
return xerrors.New("failed to remove cache")

cache/cache_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ func TestSetAndGetAndClear(t *testing.T) {
1313
tempCacheDir, _ := ioutil.TempDir("", "TestCacheDir-*")
1414
f, _ := ioutil.TempFile(tempCacheDir, "foo.bar.baz-*")
1515

16-
c := Initialize(tempCacheDir)
16+
c := New(tempCacheDir)
1717

1818
// set
1919
expectedCacheContents := "foo bar baz"

0 commit comments

Comments
 (0)