add symlink content fetching support

This commit is contained in:
Alex Goodman
2020-06-16 11:37:37 -04:00
parent 778754dd59
commit cd94179985
26 changed files with 855 additions and 161 deletions
+19 -20
View File
@@ -4,13 +4,11 @@ import (
"strings"
"github.com/anchore/imgbom/imgbom/pkg"
"github.com/anchore/imgbom/imgbom/scope"
"github.com/anchore/imgbom/internal/log"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/tree"
)
// TODO: put under test...
// GenericCataloger implements the Catalog interface and is responsible for dispatching the proper parser function for
// a given path or glob pattern. This is intended to be reusable across many package cataloger types.
type GenericCataloger struct {
@@ -45,25 +43,26 @@ func (a *GenericCataloger) clear() {
}
// SelectFiles takes a set of file trees and resolves and file references of interest for future cataloging
func (a *GenericCataloger) SelectFiles(trees []tree.FileTreeReader) []file.Reference {
for _, t := range trees {
// select by exact path
for path, parser := range a.pathParsers {
f := t.File(file.Path(path))
if f != nil {
a.register([]file.Reference{*f}, parser)
}
func (a *GenericCataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
// select by exact path
for path, parser := range a.pathParsers {
files, err := resolver.FilesByPath(file.Path(path))
if err != nil {
log.Errorf("cataloger failed to select files by path: %w", err)
}
if files != nil {
a.register(files, parser)
}
}
// select by pattern
for globPattern, parser := range a.globParsers {
fileMatches, err := t.FilesByGlob(globPattern)
if err != nil {
log.Errorf("failed to find files by glob: %s", globPattern)
}
if fileMatches != nil {
a.register(fileMatches, parser)
}
// select by glob pattern
for globPattern, parser := range a.globParsers {
fileMatches, err := resolver.FilesByGlob(globPattern)
if err != nil {
log.Errorf("failed to find files by glob: %s", globPattern)
}
if fileMatches != nil {
a.register(fileMatches, parser)
}
}
@@ -0,0 +1,117 @@
package common
import (
"fmt"
"io"
"io/ioutil"
"testing"
"github.com/anchore/imgbom/imgbom/pkg"
"github.com/anchore/imgbom/internal"
"github.com/anchore/stereoscope/pkg/file"
)
type testResolver struct {
contents map[file.Reference]string
}
func newTestResolver() *testResolver {
return &testResolver{
contents: make(map[file.Reference]string),
}
}
func (r *testResolver) FilesByPath(paths ...file.Path) ([]file.Reference, error) {
results := make([]file.Reference, len(paths))
for idx, p := range paths {
results[idx] = file.NewFileReference(p)
r.contents[results[idx]] = fmt.Sprintf("%s file contents!", p)
}
return results, nil
}
func (r *testResolver) FilesByGlob(patterns ...string) ([]file.Reference, error) {
path := "/a-path.txt"
ref := file.NewFileReference(file.Path(path))
r.contents[ref] = fmt.Sprintf("%s file contents!", path)
return []file.Reference{ref}, nil
}
func parser(reader io.Reader) ([]pkg.Package, error) {
contents, err := ioutil.ReadAll(reader)
if err != nil {
panic(err)
}
return []pkg.Package{
{
Name: string(contents),
},
}, nil
}
func TestGenericCataloger(t *testing.T) {
globParsers := map[string]ParserFn{
"**a-path.txt": parser,
}
pathParsers := map[string]ParserFn{
"/another-path.txt": parser,
"/last/path.txt": parser,
}
resolver := newTestResolver()
cataloger := NewGenericCataloger(pathParsers, globParsers)
selected := cataloger.SelectFiles(resolver)
if len(selected) != 3 {
t.Fatalf("unexpected selection length: %d", len(selected))
}
expectedSelection := internal.NewStringSetFromSlice([]string{"/last/path.txt", "/another-path.txt", "/a-path.txt"})
selectionByPath := make(map[string]file.Reference)
for _, s := range selected {
if !expectedSelection.Contains(string(s.Path)) {
t.Errorf("unexpected selection path: %+v", s.Path)
}
selectionByPath[string(s.Path)] = s
}
upstream := "some-other-cataloger"
expectedPkgs := make(map[file.Reference]pkg.Package)
for path, ref := range selectionByPath {
expectedPkgs[ref] = pkg.Package{
FoundBy: upstream,
Source: []file.Reference{ref},
Name: fmt.Sprintf("%s file contents!", path),
}
}
actualPkgs, err := cataloger.Catalog(resolver.contents, upstream)
if err != nil {
t.Fatalf("cataloger catalog action failed: %+v", err)
}
if len(actualPkgs) != len(expectedPkgs) {
t.Fatalf("unexpected packages len: %d", len(actualPkgs))
}
for _, p := range actualPkgs {
ref := p.Source[0]
exP, ok := expectedPkgs[ref]
if !ok {
t.Errorf("missing expected pkg: ref=%+v", ref)
continue
}
if p.FoundBy != exP.FoundBy {
t.Errorf("bad upstream: %s", p.FoundBy)
}
if exP.Name != p.Name {
t.Errorf("bad contents mapping: %+v", p.Source)
}
}
}