generalize common analyzer elements

This commit is contained in:
Alex Goodman
2020-06-04 17:49:52 -04:00
parent e88669c536
commit 83e96e8880
8 changed files with 137 additions and 138 deletions
+9 -65
View File
@@ -1,30 +1,23 @@
package bundler
import (
"io"
"strings"
"github.com/anchore/imgbom/imgbom/analyzer/common"
"github.com/anchore/imgbom/imgbom/pkg"
"github.com/anchore/imgbom/internal/log"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/tree"
)
var parserDispatch = map[string]parserFn{
"*/Gemfile.lock": ParseGemfileLockEntries,
}
type parserFn func(io.Reader) ([]pkg.Package, error)
type Analyzer struct {
selectedFiles []file.Reference
parsers map[file.Reference]parserFn
analyzer common.GenericAnalyzer
}
func NewAnalyzer() *Analyzer {
globParserDispatch := map[string]common.ParserFn{
"*/Gemfile.lock": ParseGemfileLockEntries,
}
return &Analyzer{
selectedFiles: make([]file.Reference, 0),
parsers: make(map[file.Reference]parserFn),
analyzer: common.NewGenericAnalyzer(nil, globParserDispatch),
}
}
@@ -32,59 +25,10 @@ func (a *Analyzer) Name() string {
return "bundler-analyzer"
}
func (a *Analyzer) register(files []file.Reference, parser parserFn) {
a.selectedFiles = append(a.selectedFiles, files...)
for _, f := range files {
a.parsers[f] = parser
}
}
func (a *Analyzer) clear() {
a.selectedFiles = make([]file.Reference, 0)
a.parsers = make(map[file.Reference]parserFn)
}
func (a *Analyzer) SelectFiles(trees []tree.FileTreeReader) []file.Reference {
for _, tree := range trees {
for globPattern, parser := range parserDispatch {
fileMatches, err := tree.FilesByGlob(globPattern)
if err != nil {
log.Errorf("'%s' failed to find files by glob: %s", a.Name(), globPattern)
}
if fileMatches != nil {
a.register(fileMatches, parser)
}
}
}
return a.selectedFiles
return a.analyzer.SelectFiles(trees)
}
func (a *Analyzer) Analyze(contents map[file.Reference]string) ([]pkg.Package, error) {
defer a.clear()
packages := make([]pkg.Package, 0)
for reference, parser := range a.parsers {
content, ok := contents[reference]
if !ok {
log.Errorf("analyzer '%s' file content missing: %+v", a.Name(), reference)
continue
}
entries, err := parser(strings.NewReader(content))
if err != nil {
log.Errorf("analyzer failed to parse entries (reference=%+v): %w", reference, err)
continue
}
for _, entry := range entries {
entry.FoundBy = a.Name()
entry.Source = []file.Reference{reference}
packages = append(packages, entry)
}
}
return packages, nil
return a.analyzer.Analyze(contents, a.Name())
}
@@ -0,0 +1,92 @@
package common
import (
"strings"
"github.com/anchore/imgbom/imgbom/pkg"
"github.com/anchore/imgbom/internal/log"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/tree"
)
type GenericAnalyzer struct {
globParserDispatch map[string]ParserFn
pathParserDispatch map[string]ParserFn
selectedFiles []file.Reference
parsers map[file.Reference]ParserFn
}
func NewGenericAnalyzer(pathParserDispatch map[string]ParserFn, globParserDispatch map[string]ParserFn) GenericAnalyzer {
return GenericAnalyzer{
globParserDispatch: globParserDispatch,
pathParserDispatch: pathParserDispatch,
selectedFiles: make([]file.Reference, 0),
parsers: make(map[file.Reference]ParserFn),
}
}
func (a *GenericAnalyzer) register(files []file.Reference, parser ParserFn) {
a.selectedFiles = append(a.selectedFiles, files...)
for _, f := range files {
a.parsers[f] = parser
}
}
func (a *GenericAnalyzer) clear() {
a.selectedFiles = make([]file.Reference, 0)
a.parsers = make(map[file.Reference]ParserFn)
}
func (a *GenericAnalyzer) SelectFiles(trees []tree.FileTreeReader) []file.Reference {
for _, tree := range trees {
// select by exact path
for path, parser := range a.globParserDispatch {
f := tree.File(file.Path(path))
if f != nil {
a.register([]file.Reference{*f}, parser)
}
}
// select by pattern
for globPattern, parser := range a.globParserDispatch {
fileMatches, err := tree.FilesByGlob(globPattern)
if err != nil {
log.Errorf("failed to find files by glob: %s", globPattern)
}
if fileMatches != nil {
a.register(fileMatches, parser)
}
}
}
return a.selectedFiles
}
func (a *GenericAnalyzer) Analyze(contents map[file.Reference]string, upstreamMatcher string) ([]pkg.Package, error) {
defer a.clear()
packages := make([]pkg.Package, 0)
for reference, parser := range a.parsers {
content, ok := contents[reference]
if !ok {
log.Errorf("analyzer '%s' missing file content: %+v", upstreamMatcher, reference)
continue
}
entries, err := parser(strings.NewReader(content))
if err != nil {
log.Errorf("analyzer '%s' failed to parse entries (reference=%+v): %w", upstreamMatcher, reference, err)
continue
}
for _, entry := range entries {
entry.FoundBy = upstreamMatcher
entry.Source = []file.Reference{reference}
packages = append(packages, entry)
}
}
return packages, nil
}
+9
View File
@@ -0,0 +1,9 @@
package common
import (
"io"
"github.com/anchore/imgbom/imgbom/pkg"
)
type ParserFn func(io.Reader) ([]pkg.Package, error)
+9 -64
View File
@@ -1,30 +1,23 @@
package dpkg
import (
"io"
"strings"
"github.com/anchore/imgbom/imgbom/analyzer/common"
"github.com/anchore/imgbom/imgbom/pkg"
"github.com/anchore/imgbom/internal/log"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/tree"
)
var parserDispatch = map[string]parserFn{
"/var/lib/dpkg/status": ParseDpkgStatusEntries,
}
type parserFn func(io.Reader) ([]pkg.DpkgMetadata, error)
type Analyzer struct {
selectedFiles []file.Reference
parsers map[file.Reference]parserFn
analyzer common.GenericAnalyzer
}
func NewAnalyzer() *Analyzer {
pathParserDispatch := map[string]common.ParserFn{
"/var/lib/dpkg/status": ParseDpkgStatus,
}
return &Analyzer{
selectedFiles: make([]file.Reference, 0),
parsers: make(map[file.Reference]parserFn),
analyzer: common.NewGenericAnalyzer(pathParserDispatch, nil),
}
}
@@ -32,58 +25,10 @@ func (a *Analyzer) Name() string {
return "dpkg-analyzer"
}
func (a *Analyzer) register(f file.Reference, parser parserFn) {
a.selectedFiles = append(a.selectedFiles, f)
a.parsers[f] = parser
}
func (a *Analyzer) clear() {
a.selectedFiles = make([]file.Reference, 0)
a.parsers = make(map[file.Reference]parserFn)
}
func (a *Analyzer) SelectFiles(trees []tree.FileTreeReader) []file.Reference {
for _, tree := range trees {
for exactPath, parser := range parserDispatch {
match := tree.File(file.Path(exactPath))
if match != nil {
a.register(*match, parser)
}
}
}
return a.selectedFiles
return a.analyzer.SelectFiles(trees)
}
func (a *Analyzer) Analyze(contents map[file.Reference]string) ([]pkg.Package, error) {
defer a.clear()
packages := make([]pkg.Package, 0)
for _, reference := range a.selectedFiles {
content, ok := contents[reference]
if !ok {
log.Errorf("analyzer '%s' file content missing: %+v", a.Name(), reference)
continue
}
entries, err := ParseDpkgStatusEntries(strings.NewReader(content))
if err != nil {
log.Errorf("analyzer failed to parse entries (reference=%+v): %w", reference, err)
continue
}
for _, entry := range entries {
packages = append(packages, pkg.Package{
Name: entry.Package,
Version: entry.Version,
Type: pkg.DebPkg,
FoundBy: a.Name(),
Source: []file.Reference{reference},
Metadata: entry,
})
}
}
return packages, nil
return a.analyzer.Analyze(contents, a.Name())
}
+9 -4
View File
@@ -12,9 +12,9 @@ import (
var errEndOfPackages = fmt.Errorf("no more packages to read")
func ParseDpkgStatusEntries(reader io.Reader) ([]pkg.DpkgMetadata, error) {
func ParseDpkgStatus(reader io.Reader) ([]pkg.Package, error) {
buffedReader := bufio.NewReader(reader)
var entries = make([]pkg.DpkgMetadata, 0)
var packages = make([]pkg.Package, 0)
for {
entry, err := parseDpkgStatusEntry(buffedReader)
@@ -24,10 +24,15 @@ func ParseDpkgStatusEntries(reader io.Reader) ([]pkg.DpkgMetadata, error) {
}
return nil, err
}
entries = append(entries, entry)
packages = append(packages, pkg.Package{
Name: entry.Package,
Version: entry.Version,
Type: pkg.DebPkg,
Metadata: entry,
})
}
return entries, nil
return packages, nil
}
func parseDpkgStatusEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err error) {
@@ -90,17 +90,17 @@ func TestMultiplePackages(t *testing.T) {
}
}()
entries, err := ParseDpkgStatusEntries(file)
pkgs, err := ParseDpkgStatus(file)
if err != nil {
t.Fatal("Unable to read file contents: ", err)
}
if len(entries) != 2 {
t.Fatalf("unexpected number of entries: %d", len(entries))
if len(pkgs) != 2 {
t.Fatalf("unexpected number of entries: %d", len(pkgs))
}
for idx, entry := range entries {
compareEntries(t, entry, test.expected[idx])
for idx, entry := range pkgs {
compareEntries(t, entry.Metadata.(pkg.DpkgMetadata), test.expected[idx])
}
})