Improve overall documentation (#148)

* improve overall documentation

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* fix tests to use scope.Resolver over scope

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2020-08-13 16:34:32 -04:00 committed by GitHub
parent 47a0454084
commit 95517d131a
35 changed files with 177 additions and 72 deletions

View file

@ -1,3 +1,9 @@
# TODO: enable this when we have coverage on docstring comments
#issues:
# # The list of ids of default excludes to include or disable.
# include:
# - EXC0002 # disable excluding of issues about comments from golint
linters: linters:
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true disable-all: true

5
go.mod
View file

@ -20,7 +20,6 @@ require (
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.3.1 github.com/mitchellh/mapstructure v1.3.1
github.com/olekukonko/tablewriter v0.0.4 github.com/olekukonko/tablewriter v0.0.4
github.com/opencontainers/runc v0.1.1 // indirect
github.com/pelletier/go-toml v1.8.0 github.com/pelletier/go-toml v1.8.0
github.com/rogpeppe/go-internal v1.5.2 github.com/rogpeppe/go-internal v1.5.2
github.com/sergi/go-diff v1.1.0 github.com/sergi/go-diff v1.1.0
@ -32,7 +31,9 @@ require (
github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163 github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163
github.com/x-cray/logrus-prefixed-formatter v0.5.2 github.com/x-cray/logrus-prefixed-formatter v0.5.2
github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect
golang.org/x/sys v0.0.0-20200610111108-226ff32320da // indirect golang.org/x/sys v0.0.0-20200610111108-226ff32320da // indirect
google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 // indirect google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 // indirect
gopkg.in/ini.v1 v1.57.0 // indirect gopkg.in/ini.v1 v1.57.0 // indirect

10
go.sum
View file

@ -131,8 +131,6 @@ github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db/go.mod h1:D3r
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods= github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e/go.mod h1:bkyLl5VITnrmgErv4S1vDfVz/TGAZ5il6161IQo7w2g= github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e/go.mod h1:bkyLl5VITnrmgErv4S1vDfVz/TGAZ5il6161IQo7w2g=
github.com/anchore/stereoscope v0.0.0-20200803190343-146f38f8cc19 h1:iJ6du/cA9KJ0ctP905u2zCcpJubsy6kxLZBvG4XG+uY=
github.com/anchore/stereoscope v0.0.0-20200803190343-146f38f8cc19/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs=
github.com/anchore/stereoscope v0.0.0-20200813152757-548b22c8a0b3 h1:pl+txuYlhK8Mmio4d+4zQI/1xg8X6BtNErTASrx23Wk= github.com/anchore/stereoscope v0.0.0-20200813152757-548b22c8a0b3 h1:pl+txuYlhK8Mmio4d+4zQI/1xg8X6BtNErTASrx23Wk=
github.com/anchore/stereoscope v0.0.0-20200813152757-548b22c8a0b3/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs= github.com/anchore/stereoscope v0.0.0-20200813152757-548b22c8a0b3/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
@ -886,8 +884,8 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -962,6 +960,8 @@ golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -978,6 +978,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View file

@ -1,3 +1,8 @@
/*
Syft is a CLI tool and go library for generating a Software Bill of Materials (SBOM) from container images and filesystems.
Note that Syft is both a command line tool as well as a library. See the syft/ child package for library functionality.
*/
package main package main
import ( import (

View file

@ -1,3 +1,6 @@
/*
Package apkdb provides a concrete Cataloger implementation for Alpine DB files.
*/
package apkdb package apkdb
import ( import (

View file

@ -1,3 +1,6 @@
/*
Package bundler provides a concrete Cataloger implementation for Ruby Gemfile.lock bundler files.
*/
package bundler package bundler
import ( import (

View file

@ -37,7 +37,7 @@ func newMonitor() (*progress.Manual, *progress.Manual) {
// In order to efficiently retrieve contents from a underlying container image the content fetch requests are // In order to efficiently retrieve contents from a underlying container image the content fetch requests are
// done in bulk. Specifically, all files of interest are collected from each catalogers and accumulated into a single // done in bulk. Specifically, all files of interest are collected from each catalogers and accumulated into a single
// request. // request.
func Catalog(s scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) { func Catalog(resolver scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
catalog := pkg.NewCatalog() catalog := pkg.NewCatalog()
fileSelection := make([]file.Reference, 0) fileSelection := make([]file.Reference, 0)
@ -45,14 +45,14 @@ func Catalog(s scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
// ask catalogers for files to extract from the image tar // ask catalogers for files to extract from the image tar
for _, a := range catalogers { for _, a := range catalogers {
fileSelection = append(fileSelection, a.SelectFiles(s)...) fileSelection = append(fileSelection, a.SelectFiles(resolver)...)
log.Debugf("cataloger '%s' selected '%d' files", a.Name(), len(fileSelection)) log.Debugf("cataloger '%s' selected '%d' files", a.Name(), len(fileSelection))
filesProcessed.N += int64(len(fileSelection)) filesProcessed.N += int64(len(fileSelection))
} }
// fetch contents for requested selection by catalogers // fetch contents for requested selection by catalogers
// TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering). // TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering).
contents, err := s.MultipleFileContentsByRef(fileSelection...) contents, err := resolver.MultipleFileContentsByRef(fileSelection...)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -1,6 +1,6 @@
/* /*
Package cataloger provides the ability to process files from a container image or file system and discover packages Package cataloger provides the ability to process files from a container image or file system and discover packages
(e.g. gems, wheels, jars, rpms, debs, etc.). Specifically, this package contains both a catalog function to utilize all (gems, wheels, jars, rpms, debs, etc). Specifically, this package contains both a catalog function to utilize all
catalogers defined in child packages as well as the interface definition to implement a cataloger. catalogers defined in child packages as well as the interface definition to implement a cataloger.
*/ */
package cataloger package cataloger

View file

@ -1,3 +1,6 @@
/*
Package common provides generic utilities used by multiple catalogers.
*/
package common package common
import ( import (

View file

@ -1,3 +1,6 @@
/*
Package dpkg provides a concrete Cataloger implementation for Debian package DB status files.
*/
package dpkg package dpkg
import ( import (

View file

@ -1,3 +1,6 @@
/*
Package golang provides a concrete Cataloger implementation for go.mod files.
*/
package golang package golang
import ( import (

View file

@ -1,3 +1,6 @@
/*
Package java provides a concrete Cataloger implementation for Java archives (jar, war, ear, jpi, hpi formats).
*/
package java package java
import ( import (

View file

@ -1,3 +1,6 @@
/*
Package javascript provides a concrete Cataloger implementation for JavaScript ecosystem files (yarn and npm).
*/
package javascript package javascript
import ( import (

View file

@ -1,3 +1,6 @@
/*
Package python provides a concrete Cataloger implementation for Python ecosystem files (egg, wheel, requirements.txt).
*/
package python package python
import ( import (

View file

@ -1,3 +1,6 @@
/*
Package rpmdb provides a concrete Cataloger implementation for RPM "Package" DB files.
*/
package rpmdb package rpmdb
import ( import (

View file

@ -17,8 +17,8 @@ type parseEntry struct {
fn parseFunc fn parseFunc
} }
// Identify parses distro-specific files to determine distro metadata like version and release // Identify parses distro-specific files to determine distro metadata like version and release.
func Identify(s scope.Scope) Distro { func Identify(resolver scope.Resolver) Distro {
distro := NewUnknownDistro() distro := NewUnknownDistro()
identityFiles := []parseEntry{ identityFiles := []parseEntry{
@ -41,7 +41,7 @@ func Identify(s scope.Scope) Distro {
identifyLoop: identifyLoop:
for _, entry := range identityFiles { for _, entry := range identityFiles {
refs, err := s.FilesByPath(entry.path) refs, err := resolver.FilesByPath(entry.path)
if err != nil { if err != nil {
log.Errorf("unable to get path refs from %s: %s", entry.path, err) log.Errorf("unable to get path refs from %s: %s", entry.path, err)
break break
@ -52,7 +52,7 @@ identifyLoop:
} }
for _, ref := range refs { for _, ref := range refs {
contents, err := s.MultipleFileContentsByRef(ref) contents, err := resolver.MultipleFileContentsByRef(ref)
content, ok := contents[ref] content, ok := contents[ref]
if !ok { if !ok {

View file

@ -75,12 +75,12 @@ func TestIdentifyDistro(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.fixture, func(t *testing.T) { t.Run(test.fixture, func(t *testing.T) {
s, err := scope.NewScopeFromDir(test.fixture, scope.AllLayersScope) s, err := scope.NewScopeFromDir(test.fixture)
if err != nil { if err != nil {
t.Fatalf("unable to produce a new scope for testing: %s", test.fixture) t.Fatalf("unable to produce a new scope for testing: %s", test.fixture)
} }
d := Identify(s) d := Identify(s.Resolver)
observedDistros.Add(d.String()) observedDistros.Add(d.String())
if d.Type != test.Type { if d.Type != test.Type {

View file

@ -1,3 +1,7 @@
/*
Package event provides event types for all events that the syft library published onto the event bus. By convention, for each event
defined here there should be a corresponding event parser defined in the parsers/ child package.
*/
package event package event
import "github.com/wagoodman/go-partybus" import "github.com/wagoodman/go-partybus"

View file

@ -1,3 +1,6 @@
/*
Package parsers provides parser helpers to extract payloads for each event type that the syft library publishes onto the event bus.
*/
package parsers package parsers
import ( import (

View file

@ -1,3 +1,6 @@
/*
A "one-stop-shop" for helper utilities for all major functionality provided by child packages of the syft library.
*/
package syft package syft
import ( import (
@ -11,6 +14,8 @@ import (
"github.com/wagoodman/go-partybus" "github.com/wagoodman/go-partybus"
) )
// Catalog the given image from a particular perspective (e.g. squashed scope, all-layers scope). Returns the discovered
// set of packages, the identified Linux distribution, and the scope object used to wrap the data source.
func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scope, *distro.Distro, error) { func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scope, *distro.Distro, error) {
log.Info("cataloging image") log.Info("cataloging image")
s, cleanup, err := scope.NewScope(userInput, scoptOpt) s, cleanup, err := scope.NewScope(userInput, scoptOpt)
@ -29,8 +34,10 @@ func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scop
return catalog, &s, &d, nil return catalog, &s, &d, nil
} }
// IdentifyDistro attempts to discover what the underlying Linux distribution may be from the available flat files
// provided by the given scope object. If results are inconclusive a "UnknownDistro" Type is returned.
func IdentifyDistro(s scope.Scope) distro.Distro { func IdentifyDistro(s scope.Scope) distro.Distro {
d := distro.Identify(s) d := distro.Identify(s.Resolver)
if d.Type != distro.UnknownDistroType { if d.Type != distro.UnknownDistroType {
log.Infof("identified distro: %s", d.String()) log.Infof("identified distro: %s", d.String())
} else { } else {
@ -39,15 +46,18 @@ func IdentifyDistro(s scope.Scope) distro.Distro {
return d return d
} }
// Catalog the given scope, which may represent a container image or filesystem. Returns the discovered set of packages.
func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) { func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {
log.Info("building the catalog") log.Info("building the catalog")
return cataloger.Catalog(s, cataloger.All()...) return cataloger.Catalog(s.Resolver, cataloger.All()...)
} }
// SetLogger sets the logger object used for all syft logging calls.
func SetLogger(logger logger.Logger) { func SetLogger(logger logger.Logger) {
log.Log = logger log.Log = logger
} }
// SetBus sets the event bus for all syft library bus publish events onto (in-library subscriptions are not allowed).
func SetBus(b *partybus.Bus) { func SetBus(b *partybus.Bus) {
bus.SetPublisher(b) bus.SetPublisher(b)
} }

View file

@ -1,3 +1,6 @@
/*
Defines the logging interface which is used throughout the syft library.
*/
package logger package logger
type Logger interface { type Logger interface {

View file

@ -12,6 +12,7 @@ import (
var nextPackageID int64 var nextPackageID int64
// Catalog represents a collection of Packages.
type Catalog struct { type Catalog struct {
byID map[ID]*Package byID map[ID]*Package
byType map[Type][]*Package byType map[Type][]*Package
@ -19,6 +20,7 @@ type Catalog struct {
lock sync.RWMutex lock sync.RWMutex
} }
// NewCatalog returns a new empty Catalog
func NewCatalog() *Catalog { func NewCatalog() *Catalog {
return &Catalog{ return &Catalog{
byID: make(map[ID]*Package), byID: make(map[ID]*Package),
@ -27,18 +29,22 @@ func NewCatalog() *Catalog {
} }
} }
// PackageCount returns the total number of packages that have been added.
func (c *Catalog) PackageCount() int { func (c *Catalog) PackageCount() int {
return len(c.byID) return len(c.byID)
} }
// Package returns the package with the given ID.
func (c *Catalog) Package(id ID) *Package { func (c *Catalog) Package(id ID) *Package {
return c.byID[id] return c.byID[id]
} }
// PackagesByFile returns all packages that were discovered from the given source file reference.
func (c *Catalog) PackagesByFile(ref file.Reference) []*Package { func (c *Catalog) PackagesByFile(ref file.Reference) []*Package {
return c.byFile[ref] return c.byFile[ref]
} }
// Add a package to the Catalog.
func (c *Catalog) Add(p Package) { func (c *Catalog) Add(p Package) {
if p.id != 0 { if p.id != 0 {
log.Errorf("package already added to catalog: %s", p) log.Errorf("package already added to catalog: %s", p)
@ -70,6 +76,7 @@ func (c *Catalog) Add(p Package) {
} }
} }
// Enumerate all packages for the given type(s), enumerating all packages if no type is specified.
func (c *Catalog) Enumerate(types ...Type) <-chan *Package { func (c *Catalog) Enumerate(types ...Type) <-chan *Package {
channel := make(chan *Package) channel := make(chan *Package)
go func() { go func() {
@ -96,6 +103,8 @@ func (c *Catalog) Enumerate(types ...Type) <-chan *Package {
return channel return channel
} }
// Sorted enumerates all packages for the given types sorted by package name. Enumerates all packages if no type
// is specified.
func (c *Catalog) Sorted(types ...Type) []*Package { func (c *Catalog) Sorted(types ...Type) []*Package {
pkgs := make([]*Package, 0) pkgs := make([]*Package, 0)
for p := range c.Enumerate(types...) { for p := range c.Enumerate(types...) {

View file

@ -9,7 +9,7 @@ type DpkgMetadata struct {
// TODO: consider keeping the remaining values as an embedded map // TODO: consider keeping the remaining values as an embedded map
} }
// RpmMetadata represents all captured data for a RHEL package DB entry. // RpmMetadata represents all captured data for a RPM DB package entry.
type RpmMetadata struct { type RpmMetadata struct {
Version string `mapstructure:"Version" json:"version"` Version string `mapstructure:"Version" json:"version"`
Epoch int `mapstructure:"Epoch" json:"epoch"` Epoch int `mapstructure:"Epoch" json:"epoch"`
@ -21,6 +21,7 @@ type RpmMetadata struct {
Vendor string `mapstructure:"Vendor" json:"vendor"` Vendor string `mapstructure:"Vendor" json:"vendor"`
} }
// JavaManifest represents the fields of interest extracted from a Java archive's META-INF/MANIFEST.MF file.
type JavaManifest struct { type JavaManifest struct {
Name string `mapstructure:"Name" json:"name"` Name string `mapstructure:"Name" json:"name"`
ManifestVersion string `mapstructure:"Manifest-Version" json:"manifest-version"` ManifestVersion string `mapstructure:"Manifest-Version" json:"manifest-version"`
@ -33,6 +34,7 @@ type JavaManifest struct {
Extra map[string]string `mapstructure:",remain" json:"extra-fields"` Extra map[string]string `mapstructure:",remain" json:"extra-fields"`
} }
// PomProperties represents the fields of interest extracted from a Java archive's pom.xml file.
type PomProperties struct { type PomProperties struct {
Path string Path string
Name string `mapstructure:"name" json:"name"` Name string `mapstructure:"name" json:"name"`
@ -42,13 +44,14 @@ type PomProperties struct {
Extra map[string]string `mapstructure:",remain" json:"extra-fields"` Extra map[string]string `mapstructure:",remain" json:"extra-fields"`
} }
// JavaMetadata encapsulates all Java ecosystem metadata for a package as well as an (optional) parent relationship.
type JavaMetadata struct { type JavaMetadata struct {
Manifest *JavaManifest `mapstructure:"Manifest" json:"manifest"` Manifest *JavaManifest `mapstructure:"Manifest" json:"manifest"`
PomProperties *PomProperties `mapstructure:"PomProperties" json:"pom-properties"` PomProperties *PomProperties `mapstructure:"PomProperties" json:"pom-properties"`
Parent *Package `json:"parent-package"` Parent *Package `json:"parent-package"`
} }
// source: https://wiki.alpinelinux.org/wiki/Apk_spec // ApkMetadata represents all captured data for a Alpine DB package entry. See https://wiki.alpinelinux.org/wiki/Apk_spec for more information.
type ApkMetadata struct { type ApkMetadata struct {
Package string `mapstructure:"P" json:"package"` Package string `mapstructure:"P" json:"package"`
OriginPackage string `mapstructure:"o" json:"origin-package"` OriginPackage string `mapstructure:"o" json:"origin-package"`
@ -66,6 +69,7 @@ type ApkMetadata struct {
Files []ApkFileRecord `json:"files"` Files []ApkFileRecord `json:"files"`
} }
// ApkFileRecord represents a single file listing and metadata from a APK DB entry (which may have many of these file records).
type ApkFileRecord struct { type ApkFileRecord struct {
Path string `json:"path"` Path string `json:"path"`
OwnerUID string `json:"owner-uid"` OwnerUID string `json:"owner-uid"`

View file

@ -1,3 +1,6 @@
/*
Package pkg provides the data structures for a package, a package catalog, package types, and domain-specific metadata.
*/
package pkg package pkg
import ( import (
@ -8,25 +11,26 @@ import (
type ID int64 type ID int64
// TODO: add field to trace which cataloger detected this // Package represents an application or library that has been bundled into a distributable format.
// Package represents an application or library that has been bundled into a distributable format
type Package struct { type Package struct {
id ID // this is set when a package is added to the catalog id ID // uniquely identifies a package, set by the cataloger
Name string `json:"manifest"` Name string `json:"manifest"` // the package name
Version string `json:"version"` Version string `json:"version"` // the version of the package
FoundBy string `json:"found-by"` // FoundBy is the cataloger that discovered this package FoundBy string `json:"found-by"` // the specific cataloger that discovered this package
Source []file.Reference `json:"sources"` Source []file.Reference `json:"sources"` // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
Licenses []string `json:"licenses"` // TODO: should we move this into metadata? // TODO: should we move licenses into metadata?
Language Language `json:"language"` // TODO: should this support multiple languages as a slice? Licenses []string `json:"licenses"` // licenses discovered with the package metadata
Type Type `json:"type"` Language Language `json:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
Metadata interface{} `json:"metadata,omitempty"` Type Type `json:"type"` // the package type (e.g. Npm, Yarn, Egg, Wheel, Rpm, Deb, etc)
Metadata interface{} `json:"metadata,omitempty"` // additional data found while parsing the package source
} }
// ID returns the package ID, which is unique relative to a package catalog.
func (p Package) ID() ID { func (p Package) ID() ID {
return p.id return p.id
} }
// Stringer to represent a package.
func (p Package) String() string { func (p Package) String() string {
return fmt.Sprintf("Pkg(type=%s, name=%s, version=%s)", p.Type, p.Name, p.Version) return fmt.Sprintf("Pkg(type=%s, name=%s, version=%s)", p.Type, p.Name, p.Version)
} }

View file

@ -1,5 +1,6 @@
package pkg package pkg
// Type represents a Package Type for or within a language ecosystem (there may be multiple package types within a language ecosystem)
type Type string type Type string
const ( const (

View file

@ -39,7 +39,7 @@ func TestJsonDirsPresenter(t *testing.T) {
}, },
}) })
s, err := scope.NewScopeFromDir("/some/path", scope.AllLayersScope) s, err := scope.NewScopeFromDir("/some/path")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -1,3 +1,7 @@
/*
Defines a Presenter interface for displaying catalog results to an io.Writer as well as a helper utility to obtain
a specific Presenter implementation given user configuration.
*/
package presenter package presenter
import ( import (
@ -10,6 +14,8 @@ import (
"github.com/anchore/syft/syft/scope" "github.com/anchore/syft/syft/scope"
) )
// Presenter defines the expected behavior for an object responsible for displaying arbitrary input and processed data
// to a given io.Writer.
type Presenter interface { type Presenter interface {
Present(io.Writer) error Present(io.Writer) error
} }

View file

@ -31,7 +31,7 @@ func TestTextDirPresenter(t *testing.T) {
Type: pkg.DebPkg, Type: pkg.DebPkg,
}) })
s, err := scope.NewScopeFromDir("/some/path", scope.AllLayersScope) s, err := scope.NewScopeFromDir("/some/path")
if err != nil { if err != nil {
t.Fatalf("unable to create scope: %+v", err) t.Fatalf("unable to create scope: %+v", err)
} }

View file

@ -8,22 +8,24 @@ import (
"github.com/anchore/syft/syft/scope/resolvers" "github.com/anchore/syft/syft/scope/resolvers"
) )
// Resolver is an interface that encompasses how to get specific file references and file contents for a generic data source.
type Resolver interface { type Resolver interface {
ContentResolver ContentResolver
FileResolver FileResolver
} }
// ContentResolver knows how to get content from file.References // ContentResolver knows how to get file content for given file.References
type ContentResolver interface { type ContentResolver interface {
MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error)
} }
// FileResolver knows how to get file.References from string paths and globs // FileResolver knows how to get file.References for given string paths and globs
type FileResolver interface { type FileResolver interface {
FilesByPath(paths ...file.Path) ([]file.Reference, error) FilesByPath(paths ...file.Path) ([]file.Reference, error)
FilesByGlob(patterns ...string) ([]file.Reference, error) FilesByGlob(patterns ...string) ([]file.Reference, error)
} }
// getImageResolver returns the appropriate resolve for a container image given the scope option
func getImageResolver(img *image.Image, option Option) (Resolver, error) { func getImageResolver(img *image.Image, option Option) (Resolver, error) {
switch option { switch option {
case SquashedScope: case SquashedScope:

View file

@ -8,11 +8,13 @@ import (
"github.com/anchore/stereoscope/pkg/image" "github.com/anchore/stereoscope/pkg/image"
) )
// AllLayersResolver implements path and content access for the AllLayers scope option for container image data sources.
type AllLayersResolver struct { type AllLayersResolver struct {
img *image.Image img *image.Image
layers []int layers []int
} }
// NewAllLayersResolver returns a new resolver from the perspective of all image layers for the given image.
func NewAllLayersResolver(img *image.Image) (*AllLayersResolver, error) { func NewAllLayersResolver(img *image.Image) (*AllLayersResolver, error) {
if len(img.Layers) == 0 { if len(img.Layers) == 0 {
return nil, fmt.Errorf("the image does not contain any layers") return nil, fmt.Errorf("the image does not contain any layers")
@ -58,6 +60,7 @@ func (r *AllLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs file.Ref
return uniqueFiles, nil return uniqueFiles, nil
} }
// FilesByPath returns all file.References that match the given paths from any layer in the image.
func (r *AllLayersResolver) FilesByPath(paths ...file.Path) ([]file.Reference, error) { func (r *AllLayersResolver) FilesByPath(paths ...file.Path) ([]file.Reference, error) {
uniqueFileIDs := file.NewFileReferenceSet() uniqueFileIDs := file.NewFileReferenceSet()
uniqueFiles := make([]file.Reference, 0) uniqueFiles := make([]file.Reference, 0)
@ -81,6 +84,7 @@ func (r *AllLayersResolver) FilesByPath(paths ...file.Path) ([]file.Reference, e
return uniqueFiles, nil return uniqueFiles, nil
} }
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
func (r *AllLayersResolver) FilesByGlob(patterns ...string) ([]file.Reference, error) { func (r *AllLayersResolver) FilesByGlob(patterns ...string) ([]file.Reference, error) {
uniqueFileIDs := file.NewFileReferenceSet() uniqueFileIDs := file.NewFileReferenceSet()
uniqueFiles := make([]file.Reference, 0) uniqueFiles := make([]file.Reference, 0)
@ -105,6 +109,8 @@ func (r *AllLayersResolver) FilesByGlob(patterns ...string) ([]file.Reference, e
return uniqueFiles, nil return uniqueFiles, nil
} }
// MultipleFileContentsByRef returns the file contents for all file.References relative to the image. Note that a
// file.Reference is a path relative to a particular layer.
func (r *AllLayersResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) { func (r *AllLayersResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) {
return r.img.MultipleFileContentsByRef(f...) return r.img.MultipleFileContentsByRef(f...)
} }

View file

@ -11,14 +11,17 @@ import (
"github.com/bmatcuk/doublestar" "github.com/bmatcuk/doublestar"
) )
// DirectoryResolver implements path and content access for the directory data source.
type DirectoryResolver struct { type DirectoryResolver struct {
Path string Path string
} }
// Stringer to represent a directory path data source
func (s DirectoryResolver) String() string { func (s DirectoryResolver) String() string {
return fmt.Sprintf("dir://%s", s.Path) return fmt.Sprintf("dir://%s", s.Path)
} }
// FilesByPath returns all file.References that match the given paths from the directory.
func (s DirectoryResolver) FilesByPath(userPaths ...file.Path) ([]file.Reference, error) { func (s DirectoryResolver) FilesByPath(userPaths ...file.Path) ([]file.Reference, error) {
var references = make([]file.Reference, 0) var references = make([]file.Reference, 0)
@ -46,6 +49,7 @@ func fileContents(path file.Path) ([]byte, error) {
return contents, nil return contents, nil
} }
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
func (s DirectoryResolver) FilesByGlob(patterns ...string) ([]file.Reference, error) { func (s DirectoryResolver) FilesByGlob(patterns ...string) ([]file.Reference, error) {
result := make([]file.Reference, 0) result := make([]file.Reference, 0)
@ -71,6 +75,7 @@ func (s DirectoryResolver) FilesByGlob(patterns ...string) ([]file.Reference, er
return result, nil return result, nil
} }
// MultipleFileContentsByRef returns the file contents for all file.References relative a directory.
func (s DirectoryResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) { func (s DirectoryResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) {
refContents := make(map[file.Reference]string) refContents := make(map[file.Reference]string)
for _, fileRef := range f { for _, fileRef := range f {

View file

@ -0,0 +1,4 @@
/*
Package resolvers provides concrete implementations for the scope.Resolver interface for all supported data sources and scope options.
*/
package resolvers

View file

@ -7,10 +7,12 @@ import (
"github.com/anchore/stereoscope/pkg/image" "github.com/anchore/stereoscope/pkg/image"
) )
// ImageSquashResolver implements path and content access for the Squashed scope option for container image data sources.
type ImageSquashResolver struct { type ImageSquashResolver struct {
img *image.Image img *image.Image
} }
// NewImageSquashResolver returns a new resolver from the perspective of the squashed representation for the given image.
func NewImageSquashResolver(img *image.Image) (*ImageSquashResolver, error) { func NewImageSquashResolver(img *image.Image) (*ImageSquashResolver, error) {
if img.SquashedTree() == nil { if img.SquashedTree() == nil {
return nil, fmt.Errorf("the image does not have have a squashed tree") return nil, fmt.Errorf("the image does not have have a squashed tree")
@ -18,6 +20,7 @@ func NewImageSquashResolver(img *image.Image) (*ImageSquashResolver, error) {
return &ImageSquashResolver{img: img}, nil return &ImageSquashResolver{img: img}, nil
} }
// FilesByPath returns all file.References that match the given paths within the squashed representation of the image.
func (r *ImageSquashResolver) FilesByPath(paths ...file.Path) ([]file.Reference, error) { func (r *ImageSquashResolver) FilesByPath(paths ...file.Path) ([]file.Reference, error) {
uniqueFileIDs := file.NewFileReferenceSet() uniqueFileIDs := file.NewFileReferenceSet()
uniqueFiles := make([]file.Reference, 0) uniqueFiles := make([]file.Reference, 0)
@ -42,6 +45,7 @@ func (r *ImageSquashResolver) FilesByPath(paths ...file.Path) ([]file.Reference,
return uniqueFiles, nil return uniqueFiles, nil
} }
// FilesByGlob returns all file.References that match the given path glob pattern within the squashed representation of the image.
func (r *ImageSquashResolver) FilesByGlob(patterns ...string) ([]file.Reference, error) { func (r *ImageSquashResolver) FilesByGlob(patterns ...string) ([]file.Reference, error) {
uniqueFileIDs := file.NewFileReferenceSet() uniqueFileIDs := file.NewFileReferenceSet()
uniqueFiles := make([]file.Reference, 0) uniqueFiles := make([]file.Reference, 0)
@ -69,6 +73,8 @@ func (r *ImageSquashResolver) FilesByGlob(patterns ...string) ([]file.Reference,
return uniqueFiles, nil return uniqueFiles, nil
} }
// MultipleFileContentsByRef returns the file contents for all file.References relative to the image. Note that a
// file.Reference is a path relative to a particular layer, in this case only from the squashed representation.
func (r *ImageSquashResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) { func (r *ImageSquashResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) {
return r.img.MultipleFileContentsByRef(f...) return r.img.MultipleFileContentsByRef(f...)
} }

View file

@ -1,3 +1,8 @@
/*
Package scope provides an abstraction to allow a user to loosely define a data source to catalog and expose a common interface that
catalogers and use explore and analyze data from the data source. All valid (cataloggable) data sources are defined
within this package.
*/
package scope package scope
import ( import (
@ -6,24 +11,27 @@ import (
"github.com/anchore/stereoscope" "github.com/anchore/stereoscope"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/image" "github.com/anchore/stereoscope/pkg/image"
"github.com/anchore/syft/syft/scope/resolvers" "github.com/anchore/syft/syft/scope/resolvers"
) )
// ImageSource represents a data source that is a container image
type ImageSource struct { type ImageSource struct {
Img *image.Image Img *image.Image // the image object to be cataloged
} }
// DirSource represents a data source that is a filesystem directory tree
type DirSource struct { type DirSource struct {
Path string Path string // the root path to be cataloged
} }
// Scope is an object that captures the data source to be cataloged, configuration, and a specific resolver used
// in cataloging (based on the data source and configuration)
type Scope struct { type Scope struct {
Option Option Option Option // specific perspective to catalog
resolver Resolver Resolver Resolver // a Resolver object to use in file path/glob resolution and file contents resolution
ImgSrc ImageSource ImgSrc ImageSource // the specific image to be cataloged
DirSrc DirSource DirSrc DirSource // the specific directory to be cataloged
} }
// NewScope produces a Scope based on userInput like dir:// or image:tag // NewScope produces a Scope based on userInput like dir:// or image:tag
@ -37,7 +45,7 @@ func NewScope(userInput string, o Option) (Scope, func(), error) {
return Scope{}, func() {}, fmt.Errorf("unable to process path, must exist and be a directory: %w", err) return Scope{}, func() {}, fmt.Errorf("unable to process path, must exist and be a directory: %w", err)
} }
s, err := NewScopeFromDir(protocol.Value, o) s, err := NewScopeFromDir(protocol.Value)
if err != nil { if err != nil {
return Scope{}, func() {}, fmt.Errorf("could not populate scope from path (%s): %w", protocol.Value, err) return Scope{}, func() {}, fmt.Errorf("could not populate scope from path (%s): %w", protocol.Value, err)
} }
@ -64,10 +72,10 @@ func NewScope(userInput string, o Option) (Scope, func(), error) {
} }
} }
func NewScopeFromDir(path string, option Option) (Scope, error) { // NewScopeFromDir creates a new scope object tailored to catalog a given filesystem directory recursively.
func NewScopeFromDir(path string) (Scope, error) {
return Scope{ return Scope{
Option: option, Resolver: &resolvers.DirectoryResolver{
resolver: &resolvers.DirectoryResolver{
Path: path, Path: path,
}, },
DirSrc: DirSource{ DirSrc: DirSource{
@ -76,6 +84,8 @@ func NewScopeFromDir(path string, option Option) (Scope, error) {
}, nil }, nil
} }
// NewScopeFromImage creates a new scope object tailored to catalog a given container image, relative to the
// option given (e.g. all-layers, squashed, etc)
func NewScopeFromImage(img *image.Image, option Option) (Scope, error) { func NewScopeFromImage(img *image.Image, option Option) (Scope, error) {
if img == nil { if img == nil {
return Scope{}, fmt.Errorf("no image given") return Scope{}, fmt.Errorf("no image given")
@ -88,26 +98,14 @@ func NewScopeFromImage(img *image.Image, option Option) (Scope, error) {
return Scope{ return Scope{
Option: option, Option: option,
resolver: resolver, Resolver: resolver,
ImgSrc: ImageSource{ ImgSrc: ImageSource{
Img: img, Img: img,
}, },
}, nil }, nil
} }
func (s Scope) FilesByPath(paths ...file.Path) ([]file.Reference, error) { // Source returns the configured data source (either a dir source or container image source)
return s.resolver.FilesByPath(paths...)
}
func (s Scope) FilesByGlob(patterns ...string) ([]file.Reference, error) {
return s.resolver.FilesByGlob(patterns...)
}
func (s Scope) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) {
return s.resolver.MultipleFileContentsByRef(f...)
}
// return either a dir source or img source
func (s Scope) Source() interface{} { func (s Scope) Source() interface{} {
if s.ImgSrc != (ImageSource{}) { if s.ImgSrc != (ImageSource{}) {
return s.ImgSrc return s.ImgSrc
@ -119,8 +117,7 @@ func (s Scope) Source() interface{} {
return nil return nil
} }
// isValidPath ensures that the user-provided input will correspond to a path // isValidPath ensures that the user-provided input will correspond to a path that exists and is a directory
// that exists and is a directory
func isValidPath(userInput string) error { func isValidPath(userInput string) error {
fileMeta, err := os.Stat(userInput) fileMeta, err := os.Stat(userInput)
if err != nil { if err != nil {

View file

@ -70,7 +70,7 @@ func TestDirectoryScope(t *testing.T) {
} }
for _, test := range testCases { for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
p, err := NewScopeFromDir(test.input, AllLayersScope) p, err := NewScopeFromDir(test.input)
if err != nil { if err != nil {
t.Errorf("could not create NewDirScope: %w", err) t.Errorf("could not create NewDirScope: %w", err)
@ -79,7 +79,7 @@ func TestDirectoryScope(t *testing.T) {
t.Errorf("mismatched stringer: '%s' != '%s'", p.DirSrc.Path, test.input) t.Errorf("mismatched stringer: '%s' != '%s'", p.DirSrc.Path, test.input)
} }
refs, err := p.FilesByPath(test.inputPaths...) refs, err := p.Resolver.FilesByPath(test.inputPaths...)
if err != nil { if err != nil {
t.Errorf("FilesByPath call produced an error: %w", err) t.Errorf("FilesByPath call produced an error: %w", err)
} }
@ -114,11 +114,11 @@ func TestMultipleFileContentsByRefContents(t *testing.T) {
} }
for _, test := range testCases { for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
p, err := NewScopeFromDir(test.input, AllLayersScope) p, err := NewScopeFromDir(test.input)
if err != nil { if err != nil {
t.Errorf("could not create NewDirScope: %w", err) t.Errorf("could not create NewDirScope: %w", err)
} }
refs, err := p.FilesByPath(file.Path(test.path)) refs, err := p.Resolver.FilesByPath(file.Path(test.path))
if err != nil { if err != nil {
t.Errorf("could not get file references from path: %s, %v", test.path, err) t.Errorf("could not get file references from path: %s, %v", test.path, err)
} }
@ -128,7 +128,7 @@ func TestMultipleFileContentsByRefContents(t *testing.T) {
} }
ref := refs[0] ref := refs[0]
contents, err := p.MultipleFileContentsByRef(ref) contents, err := p.Resolver.MultipleFileContentsByRef(ref)
content := contents[ref] content := contents[ref]
if content != test.expected { if content != test.expected {
@ -154,11 +154,11 @@ func TestMultipleFileContentsByRefNoContents(t *testing.T) {
} }
for _, test := range testCases { for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
p, err := NewScopeFromDir(test.input, AllLayersScope) p, err := NewScopeFromDir(test.input)
if err != nil { if err != nil {
t.Errorf("could not create NewDirScope: %w", err) t.Errorf("could not create NewDirScope: %w", err)
} }
refs, err := p.FilesByPath(file.Path(test.path)) refs, err := p.Resolver.FilesByPath(file.Path(test.path))
if err != nil { if err != nil {
t.Errorf("could not get file references from path: %s, %v", test.path, err) t.Errorf("could not get file references from path: %s, %v", test.path, err)
} }
@ -199,12 +199,12 @@ func TestFilesByGlob(t *testing.T) {
} }
for _, test := range testCases { for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
p, err := NewScopeFromDir(test.input, AllLayersScope) p, err := NewScopeFromDir(test.input)
if err != nil { if err != nil {
t.Errorf("could not create NewDirScope: %w", err) t.Errorf("could not create NewDirScope: %w", err)
} }
contents, err := p.FilesByGlob(test.glob) contents, err := p.Resolver.FilesByGlob(test.glob)
if len(contents) != test.expected { if len(contents) != test.expected {
t.Errorf("unexpected number of files found by glob (%s): %d != %d", test.glob, len(contents), test.expected) t.Errorf("unexpected number of files found by glob (%s): %d != %d", test.glob, len(contents), test.expected)