mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
Merge pull request #305 from anchore/add-has-path-to-resolver
Add HasPath() to Resolver interface for existence check
This commit is contained in:
commit
bb70b0b43e
2
go.mod
2
go.mod
@ -9,7 +9,7 @@ require (
|
|||||||
github.com/anchore/go-rpmdb v0.0.0-20201106153645-0043963c2e12
|
github.com/anchore/go-rpmdb v0.0.0-20201106153645-0043963c2e12
|
||||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||||
github.com/anchore/stereoscope v0.0.0-20210104203718-4c1d1bd9a255
|
github.com/anchore/stereoscope v0.0.0-20210105001222-7beea73cb7e5
|
||||||
github.com/antihax/optional v1.0.0
|
github.com/antihax/optional v1.0.0
|
||||||
github.com/bmatcuk/doublestar/v2 v2.0.4
|
github.com/bmatcuk/doublestar/v2 v2.0.4
|
||||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible
|
github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -136,6 +136,10 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
|||||||
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-20210104203718-4c1d1bd9a255 h1:Ng7BDr9PQTCztANogjfEdEjjWUylhlPyZPhtarIGo00=
|
github.com/anchore/stereoscope v0.0.0-20210104203718-4c1d1bd9a255 h1:Ng7BDr9PQTCztANogjfEdEjjWUylhlPyZPhtarIGo00=
|
||||||
github.com/anchore/stereoscope v0.0.0-20210104203718-4c1d1bd9a255/go.mod h1:BMdPL0QEIYfpjQ3M7sHYZvuh6+vcomqF3TMHL8gr6Vw=
|
github.com/anchore/stereoscope v0.0.0-20210104203718-4c1d1bd9a255/go.mod h1:BMdPL0QEIYfpjQ3M7sHYZvuh6+vcomqF3TMHL8gr6Vw=
|
||||||
|
github.com/anchore/stereoscope v0.0.0-20210105000809-428eda0b2ec6 h1:JWpsV/8x1fuCYjJmNjT43cVFblLTpO/ISDnePukiTNw=
|
||||||
|
github.com/anchore/stereoscope v0.0.0-20210105000809-428eda0b2ec6/go.mod h1:BMdPL0QEIYfpjQ3M7sHYZvuh6+vcomqF3TMHL8gr6Vw=
|
||||||
|
github.com/anchore/stereoscope v0.0.0-20210105001222-7beea73cb7e5 h1:NGRfS6BZKElgiMbqdoH9iQn+6oxT7CJdZYrqgwvGkWY=
|
||||||
|
github.com/anchore/stereoscope v0.0.0-20210105001222-7beea73cb7e5/go.mod h1:BMdPL0QEIYfpjQ3M7sHYZvuh6+vcomqF3TMHL8gr6Vw=
|
||||||
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=
|
||||||
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
|
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
|
|||||||
@ -21,6 +21,10 @@ func newTestResolver() *testResolverMock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r testResolverMock) HasPath(path string) bool {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func (r *testResolverMock) FileContentsByLocation(_ source.Location) (io.ReadCloser, error) {
|
func (r *testResolverMock) FileContentsByLocation(_ source.Location) (io.ReadCloser, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,11 +45,6 @@ func parseRpmDB(resolver source.FileResolver, dbLocation source.Location, reader
|
|||||||
allPkgs := make([]pkg.Package, 0)
|
allPkgs := make([]pkg.Package, 0)
|
||||||
|
|
||||||
for _, entry := range pkgList {
|
for _, entry := range pkgList {
|
||||||
records, err := extractRpmdbFileRecords(resolver, entry)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
p := pkg.Package{
|
p := pkg.Package{
|
||||||
Name: entry.Name,
|
Name: entry.Name,
|
||||||
Version: fmt.Sprintf("%s-%s", entry.Version, entry.Release), // this is what engine does, instead of fmt.Sprintf("%d:%s-%s.%s", entry.Epoch, entry.Version, entry.Release, entry.Arch)
|
Version: fmt.Sprintf("%s-%s", entry.Version, entry.Release), // this is what engine does, instead of fmt.Sprintf("%d:%s-%s.%s", entry.Epoch, entry.Version, entry.Release, entry.Arch)
|
||||||
@ -67,7 +62,7 @@ func parseRpmDB(resolver source.FileResolver, dbLocation source.Location, reader
|
|||||||
Vendor: entry.Vendor,
|
Vendor: entry.Vendor,
|
||||||
License: entry.License,
|
License: entry.License,
|
||||||
Size: entry.Size,
|
Size: entry.Size,
|
||||||
Files: records,
|
Files: extractRpmdbFileRecords(resolver, entry),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,25 +72,19 @@ func parseRpmDB(resolver source.FileResolver, dbLocation source.Location, reader
|
|||||||
return allPkgs, nil
|
return allPkgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractRpmdbFileRecords(resolver source.FileResolver, entry *rpmdb.PackageInfo) ([]pkg.RpmdbFileRecord, error) {
|
func extractRpmdbFileRecords(resolver source.FileResolver, entry *rpmdb.PackageInfo) []pkg.RpmdbFileRecord {
|
||||||
var records = make([]pkg.RpmdbFileRecord, 0)
|
var records = make([]pkg.RpmdbFileRecord, 0)
|
||||||
|
|
||||||
for _, record := range entry.Files {
|
for _, record := range entry.Files {
|
||||||
refs, err := resolver.FilesByPath(record.Path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to resolve path=%+v: %w", record.Path, err)
|
|
||||||
}
|
|
||||||
//only persist RPMDB file records which exist in the image/directory, otherwise ignore them
|
//only persist RPMDB file records which exist in the image/directory, otherwise ignore them
|
||||||
if len(refs) == 0 {
|
if resolver.HasPath(record.Path) {
|
||||||
continue
|
records = append(records, pkg.RpmdbFileRecord{
|
||||||
|
Path: record.Path,
|
||||||
|
Mode: pkg.RpmdbFileMode(record.Mode),
|
||||||
|
Size: int(record.Size),
|
||||||
|
SHA256: record.SHA256,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
records = append(records, pkg.RpmdbFileRecord{
|
|
||||||
Path: record.Path,
|
|
||||||
Mode: pkg.RpmdbFileMode(record.Mode),
|
|
||||||
Size: int(record.Size),
|
|
||||||
SHA256: record.SHA256,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return records, nil
|
return records
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,10 @@ func newTestFileResolver(ignorePaths bool) *rpmdbTestFileResolverMock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r rpmdbTestFileResolverMock) HasPath(path string) bool {
|
||||||
|
return !r.ignorePaths
|
||||||
|
}
|
||||||
|
|
||||||
func (r *rpmdbTestFileResolverMock) FilesByPath(paths ...string) ([]source.Location, error) {
|
func (r *rpmdbTestFileResolverMock) FilesByPath(paths ...string) ([]source.Location, error) {
|
||||||
if r.ignorePaths {
|
if r.ignorePaths {
|
||||||
// act as if no paths exist
|
// act as if no paths exist
|
||||||
|
|||||||
@ -5,12 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/filetree"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
|
"github.com/anchore/stereoscope/pkg/filetree"
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Resolver = (*AllLayersResolver)(nil)
|
var _ Resolver = (*AllLayersResolver)(nil)
|
||||||
@ -37,6 +35,18 @@ func NewAllLayersResolver(img *image.Image) (*AllLayersResolver, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasPath indicates if the given path exists in the underlying source.
|
||||||
|
func (r *AllLayersResolver) HasPath(path string) bool {
|
||||||
|
p := file.Path(path)
|
||||||
|
for _, layerIdx := range r.layers {
|
||||||
|
tree := r.img.Layers[layerIdx].Tree
|
||||||
|
if tree.HasPath(p) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (r *AllLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs file.ReferenceSet, layerIdx int) ([]file.Reference, error) {
|
func (r *AllLayersResolver) fileByRef(ref file.Reference, uniqueFileIDs file.ReferenceSet, layerIdx int) ([]file.Reference, error) {
|
||||||
uniqueFiles := make([]file.Reference, 0)
|
uniqueFiles := make([]file.Reference, 0)
|
||||||
|
|
||||||
|
|||||||
@ -13,9 +13,10 @@ type resolution struct {
|
|||||||
|
|
||||||
func TestAllLayersResolver_FilesByPath(t *testing.T) {
|
func TestAllLayersResolver_FilesByPath(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
linkPath string
|
linkPath string
|
||||||
resolutions []resolution
|
resolutions []resolution
|
||||||
|
forcePositiveHasPath bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "link with previous data",
|
name: "link with previous data",
|
||||||
@ -66,14 +67,17 @@ func TestAllLayersResolver_FilesByPath(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "dead link",
|
name: "dead link",
|
||||||
linkPath: "/link-dead",
|
linkPath: "/link-dead",
|
||||||
resolutions: []resolution{},
|
resolutions: []resolution{},
|
||||||
|
forcePositiveHasPath: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ignore directories",
|
name: "ignore directories",
|
||||||
linkPath: "/bin",
|
linkPath: "/bin",
|
||||||
resolutions: []resolution{},
|
resolutions: []resolution{},
|
||||||
|
// directories don't resolve BUT do exist
|
||||||
|
forcePositiveHasPath: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@ -86,6 +90,17 @@ func TestAllLayersResolver_FilesByPath(t *testing.T) {
|
|||||||
t.Fatalf("could not create resolver: %+v", err)
|
t.Fatalf("could not create resolver: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasPath := resolver.HasPath(c.linkPath)
|
||||||
|
if !c.forcePositiveHasPath {
|
||||||
|
if len(c.resolutions) > 0 && !hasPath {
|
||||||
|
t.Errorf("expected HasPath() to indicate existance, but did not")
|
||||||
|
} else if len(c.resolutions) == 0 && hasPath {
|
||||||
|
t.Errorf("expeced HasPath() to NOT indicate existance, but does")
|
||||||
|
}
|
||||||
|
} else if !hasPath {
|
||||||
|
t.Errorf("expected HasPath() to indicate existance, but did not (force path)")
|
||||||
|
}
|
||||||
|
|
||||||
refs, err := resolver.FilesByPath(c.linkPath)
|
refs, err := resolver.FilesByPath(c.linkPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not use resolver: %+v", err)
|
t.Fatalf("could not use resolver: %+v", err)
|
||||||
|
|||||||
@ -19,27 +19,37 @@ type DirectoryResolver struct {
|
|||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r DirectoryResolver) requestPath(userPath string) string {
|
||||||
|
fullPath := userPath
|
||||||
|
if filepath.IsAbs(fullPath) {
|
||||||
|
// a path relative to root should be prefixed with the resolvers directory path, otherwise it should be left as is
|
||||||
|
fullPath = path.Join(r.Path, fullPath)
|
||||||
|
}
|
||||||
|
return fullPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPath indicates if the given path exists in the underlying source.
|
||||||
|
func (r *DirectoryResolver) HasPath(userPath string) bool {
|
||||||
|
_, err := os.Stat(r.requestPath(userPath))
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Stringer to represent a directory path data source
|
// Stringer to represent a directory path data source
|
||||||
func (s DirectoryResolver) String() string {
|
func (r DirectoryResolver) String() string {
|
||||||
return fmt.Sprintf("dir:%s", s.Path)
|
return fmt.Sprintf("dir:%s", r.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilesByPath returns all file.References that match the given paths from the directory.
|
// FilesByPath returns all file.References that match the given paths from the directory.
|
||||||
func (s DirectoryResolver) FilesByPath(userPaths ...string) ([]Location, error) {
|
func (r DirectoryResolver) FilesByPath(userPaths ...string) ([]Location, error) {
|
||||||
var references = make([]Location, 0)
|
var references = make([]Location, 0)
|
||||||
|
|
||||||
for _, userPath := range userPaths {
|
for _, userPath := range userPaths {
|
||||||
userStrPath := userPath
|
userStrPath := r.requestPath(userPath)
|
||||||
|
|
||||||
if filepath.IsAbs(userStrPath) {
|
|
||||||
// a path relative to root should be prefixed with the resolvers directory path, otherwise it should be left as is
|
|
||||||
userStrPath = path.Join(s.Path, userStrPath)
|
|
||||||
}
|
|
||||||
fileMeta, err := os.Stat(userStrPath)
|
fileMeta, err := os.Stat(userStrPath)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.Errorf("path (%s) is not valid: %v", userStrPath, err)
|
log.Errorf("path (%r) is not valid: %v", userStrPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't consider directories
|
// don't consider directories
|
||||||
@ -54,11 +64,11 @@ func (s DirectoryResolver) FilesByPath(userPaths ...string) ([]Location, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
|
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
|
||||||
func (s DirectoryResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
func (r DirectoryResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
||||||
result := make([]Location, 0)
|
result := make([]Location, 0)
|
||||||
|
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
pathPattern := path.Join(s.Path, pattern)
|
pathPattern := path.Join(r.Path, pattern)
|
||||||
pathMatches, err := doublestar.Glob(pathPattern)
|
pathMatches, err := doublestar.Glob(pathPattern)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -84,8 +94,8 @@ func (s DirectoryResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
|||||||
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
|
||||||
// This is helpful when attempting to find a file that is in the same layer or lower as another file. For the
|
// This is helpful when attempting to find a file that is in the same layer or lower as another file. For the
|
||||||
// DirectoryResolver, this is a simple path lookup.
|
// DirectoryResolver, this is a simple path lookup.
|
||||||
func (s *DirectoryResolver) RelativeFileByPath(_ Location, path string) *Location {
|
func (r *DirectoryResolver) RelativeFileByPath(_ Location, path string) *Location {
|
||||||
paths, err := s.FilesByPath(path)
|
paths, err := r.FilesByPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -97,7 +107,7 @@ func (s *DirectoryResolver) RelativeFileByPath(_ Location, path string) *Locatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MultipleFileContentsByLocation returns the file contents for all file.References relative a directory.
|
// MultipleFileContentsByLocation returns the file contents for all file.References relative a directory.
|
||||||
func (s DirectoryResolver) MultipleFileContentsByLocation(locations []Location) (map[Location]io.ReadCloser, error) {
|
func (r DirectoryResolver) MultipleFileContentsByLocation(locations []Location) (map[Location]io.ReadCloser, error) {
|
||||||
refContents := make(map[Location]io.ReadCloser)
|
refContents := make(map[Location]io.ReadCloser)
|
||||||
for _, location := range locations {
|
for _, location := range locations {
|
||||||
refContents[location] = file.NewDeferredReadCloser(location.Path)
|
refContents[location] = file.NewDeferredReadCloser(location.Path)
|
||||||
@ -107,6 +117,6 @@ func (s DirectoryResolver) MultipleFileContentsByLocation(locations []Location)
|
|||||||
|
|
||||||
// FileContentsByLocation fetches file contents for a single file reference relative to a directory.
|
// FileContentsByLocation fetches file contents for a single file reference relative to a directory.
|
||||||
// If the path does not exist an error is returned.
|
// If the path does not exist an error is returned.
|
||||||
func (s DirectoryResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
func (r DirectoryResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
||||||
return file.NewDeferredReadCloser(location.Path), nil
|
return file.NewDeferredReadCloser(location.Path), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,11 +6,12 @@ import (
|
|||||||
|
|
||||||
func TestDirectoryResolver_FilesByPath(t *testing.T) {
|
func TestDirectoryResolver_FilesByPath(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
root string
|
root string
|
||||||
input string
|
input string
|
||||||
expected string
|
expected string
|
||||||
refCount int
|
refCount int
|
||||||
|
forcePositiveHasPath bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "finds a file (relative)",
|
name: "finds a file (relative)",
|
||||||
@ -47,15 +48,28 @@ func TestDirectoryResolver_FilesByPath(t *testing.T) {
|
|||||||
refCount: 1,
|
refCount: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "directories ignored",
|
name: "directories ignored",
|
||||||
root: "./test-fixtures/",
|
root: "./test-fixtures/",
|
||||||
input: "/image-symlinks",
|
input: "/image-symlinks",
|
||||||
refCount: 0,
|
refCount: 0,
|
||||||
|
forcePositiveHasPath: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
resolver := DirectoryResolver{c.root}
|
resolver := DirectoryResolver{c.root}
|
||||||
|
|
||||||
|
hasPath := resolver.HasPath(c.input)
|
||||||
|
if !c.forcePositiveHasPath {
|
||||||
|
if c.refCount != 0 && !hasPath {
|
||||||
|
t.Errorf("expected HasPath() to indicate existance, but did not")
|
||||||
|
} else if c.refCount == 0 && hasPath {
|
||||||
|
t.Errorf("expeced HasPath() to NOT indicate existance, but does")
|
||||||
|
}
|
||||||
|
} else if !hasPath {
|
||||||
|
t.Errorf("expected HasPath() to indicate existance, but did not (force path)")
|
||||||
|
}
|
||||||
|
|
||||||
refs, err := resolver.FilesByPath(c.input)
|
refs, err := resolver.FilesByPath(c.input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not use resolver: %+v, %+v", err, refs)
|
t.Fatalf("could not use resolver: %+v, %+v", err, refs)
|
||||||
|
|||||||
@ -4,9 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/filetree"
|
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
|
"github.com/anchore/stereoscope/pkg/filetree"
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,6 +24,11 @@ func NewImageSquashResolver(img *image.Image) (*ImageSquashResolver, error) {
|
|||||||
return &ImageSquashResolver{img: img}, nil
|
return &ImageSquashResolver{img: img}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasPath indicates if the given path exists in the underlying source.
|
||||||
|
func (r *ImageSquashResolver) HasPath(path string) bool {
|
||||||
|
return r.img.SquashedTree().HasPath(file.Path(path))
|
||||||
|
}
|
||||||
|
|
||||||
// FilesByPath returns all file.References that match the given paths within the squashed representation of the image.
|
// FilesByPath returns all file.References that match the given paths within the squashed representation of the image.
|
||||||
func (r *ImageSquashResolver) FilesByPath(paths ...string) ([]Location, error) {
|
func (r *ImageSquashResolver) FilesByPath(paths ...string) ([]Location, error) {
|
||||||
uniqueFileIDs := file.NewFileReferenceSet()
|
uniqueFileIDs := file.NewFileReferenceSet()
|
||||||
|
|||||||
@ -8,10 +8,11 @@ import (
|
|||||||
|
|
||||||
func TestImageSquashResolver_FilesByPath(t *testing.T) {
|
func TestImageSquashResolver_FilesByPath(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
linkPath string
|
linkPath string
|
||||||
resolveLayer uint
|
resolveLayer uint
|
||||||
resolvePath string
|
resolvePath string
|
||||||
|
forcePositiveHasPath bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "link with previous data",
|
name: "link with previous data",
|
||||||
@ -42,11 +43,15 @@ func TestImageSquashResolver_FilesByPath(t *testing.T) {
|
|||||||
linkPath: "/link-dead",
|
linkPath: "/link-dead",
|
||||||
resolveLayer: 8,
|
resolveLayer: 8,
|
||||||
resolvePath: "",
|
resolvePath: "",
|
||||||
|
// the path should exist, even if the link is dead
|
||||||
|
forcePositiveHasPath: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ignore directories",
|
name: "ignore directories",
|
||||||
linkPath: "/bin",
|
linkPath: "/bin",
|
||||||
resolvePath: "",
|
resolvePath: "",
|
||||||
|
// the path should exist, even if we ignore it
|
||||||
|
forcePositiveHasPath: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "parent is a link (with overridden data)",
|
name: "parent is a link (with overridden data)",
|
||||||
@ -65,6 +70,17 @@ func TestImageSquashResolver_FilesByPath(t *testing.T) {
|
|||||||
t.Fatalf("could not create resolver: %+v", err)
|
t.Fatalf("could not create resolver: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasPath := resolver.HasPath(c.linkPath)
|
||||||
|
if !c.forcePositiveHasPath {
|
||||||
|
if c.resolvePath != "" && !hasPath {
|
||||||
|
t.Errorf("expected HasPath() to indicate existance, but did not")
|
||||||
|
} else if c.resolvePath == "" && hasPath {
|
||||||
|
t.Errorf("expeced HasPath() to NOT indicate existance, but does")
|
||||||
|
}
|
||||||
|
} else if !hasPath {
|
||||||
|
t.Errorf("expected HasPath() to indicate existance, but did not (force path)")
|
||||||
|
}
|
||||||
|
|
||||||
refs, err := resolver.FilesByPath(c.linkPath)
|
refs, err := resolver.FilesByPath(c.linkPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not use resolver: %+v", err)
|
t.Fatalf("could not use resolver: %+v", err)
|
||||||
|
|||||||
@ -28,6 +28,16 @@ func NewMockResolverForPaths(paths ...string) *MockResolver {
|
|||||||
return &MockResolver{Locations: locations}
|
return &MockResolver{Locations: locations}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasPath indicates if the given path exists in the underlying source.
|
||||||
|
func (r MockResolver) HasPath(path string) bool {
|
||||||
|
for _, l := range r.Locations {
|
||||||
|
if l.Path == path {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// String returns the string representation of the MockResolver.
|
// String returns the string representation of the MockResolver.
|
||||||
func (r MockResolver) String() string {
|
func (r MockResolver) String() string {
|
||||||
return fmt.Sprintf("mock:(%s,...)", r.Locations[0].Path)
|
return fmt.Sprintf("mock:(%s,...)", r.Locations[0].Path)
|
||||||
|
|||||||
@ -20,8 +20,10 @@ type ContentResolver interface {
|
|||||||
// TODO: we should consider refactoring to return a set of io.Readers or file.Openers instead of the full contents themselves (allow for optional buffering).
|
// TODO: we should consider refactoring to return a set of io.Readers or file.Openers instead of the full contents themselves (allow for optional buffering).
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileResolver knows how to get file.References for given string paths and globs
|
// FileResolver knows how to get a Location for given string paths and globs
|
||||||
type FileResolver interface {
|
type FileResolver interface {
|
||||||
|
// HasPath indicates if the given path exists in the underlying source.
|
||||||
|
HasPath(path string) bool
|
||||||
// FilesByPath fetches a set of file references which have the given path (for an image, there may be multiple matches)
|
// FilesByPath fetches a set of file references which have the given path (for an image, there may be multiple matches)
|
||||||
FilesByPath(paths ...string) ([]Location, error)
|
FilesByPath(paths ...string) ([]Location, error)
|
||||||
// FilesByGlob fetches a set of file references which the given glob matches
|
// FilesByGlob fetches a set of file references which the given glob matches
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user