mirror of
https://github.com/anchore/syft
synced 2024-11-10 06:14:16 +00:00
add squashed with all layers resolver
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
2fa238af7c
commit
51bb710861
7 changed files with 121 additions and 14 deletions
|
@ -59,6 +59,8 @@ func (p *Package) merge(other Package) error {
|
||||||
return fmt.Errorf("cannot merge packages with different IDs: %q vs %q", p.id, other.id)
|
return fmt.Errorf("cannot merge packages with different IDs: %q vs %q", p.id, other.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.WithFields("id", p.id, "purl", p.PURL).Trace("merging similar packages")
|
||||||
|
|
||||||
if p.PURL != other.PURL {
|
if p.PURL != other.PURL {
|
||||||
log.Warnf("merging packages have with different pURLs: %q=%q vs %q=%q", p.id, p.PURL, other.id, other.PURL)
|
log.Warnf("merging packages have with different pURLs: %q=%q vs %q=%q", p.id, p.PURL, other.id, other.PURL)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@ type imageAllLayersResolver struct {
|
||||||
layers []int
|
layers []int
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAllLayersResolver returns a new resolver from the perspective of all image layers for the given image.
|
// newImageAllLayersResolver returns a new resolver from the perspective of all image layers for the given image.
|
||||||
func newAllLayersResolver(img *image.Image) (*imageAllLayersResolver, error) {
|
func newImageAllLayersResolver(img *image.Image) (*imageAllLayersResolver, 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")
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ func (r *imageAllLayersResolver) FileContentsByLocation(location Location) (io.R
|
||||||
return nil, fmt.Errorf("cannot read contents of non-file %q", location.ref.RealPath)
|
return nil, fmt.Errorf("cannot read contents of non-file %q", location.ref.RealPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.img.FileContentsByRef(location.ref)
|
return r.img.OpenReference(location.ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *imageAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) {
|
func (r *imageAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) {
|
||||||
|
|
|
@ -91,7 +91,7 @@ func TestAllLayersResolver_FilesByPath(t *testing.T) {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||||
|
|
||||||
resolver, err := newAllLayersResolver(img)
|
resolver, err := newImageAllLayersResolver(img)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create resolver: %+v", err)
|
t.Fatalf("could not create resolver: %+v", err)
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ func TestAllLayersResolver_FilesByGlob(t *testing.T) {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||||
|
|
||||||
resolver, err := newAllLayersResolver(img)
|
resolver, err := newImageAllLayersResolver(img)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not create resolver: %+v", err)
|
t.Fatalf("could not create resolver: %+v", err)
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,7 @@ func Test_imageAllLayersResolver_FilesByMIMEType(t *testing.T) {
|
||||||
t.Run(test.fixtureName, func(t *testing.T) {
|
t.Run(test.fixtureName, func(t *testing.T) {
|
||||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName)
|
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName)
|
||||||
|
|
||||||
resolver, err := newAllLayersResolver(img)
|
resolver, err := newImageAllLayersResolver(img)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
locations, err := resolver.FilesByMIMEType(test.mimeType)
|
locations, err := resolver.FilesByMIMEType(test.mimeType)
|
||||||
|
@ -274,7 +274,7 @@ func Test_imageAllLayersResolver_FilesByMIMEType(t *testing.T) {
|
||||||
func Test_imageAllLayersResolver_hasFilesystemIDInLocation(t *testing.T) {
|
func Test_imageAllLayersResolver_hasFilesystemIDInLocation(t *testing.T) {
|
||||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path")
|
img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path")
|
||||||
|
|
||||||
resolver, err := newAllLayersResolver(img)
|
resolver, err := newImageAllLayersResolver(img)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
locations, err := resolver.FilesByMIMEType("text/plain")
|
locations, err := resolver.FilesByMIMEType("text/plain")
|
||||||
|
@ -334,7 +334,7 @@ func TestAllLayersImageResolver_FilesContents(t *testing.T) {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||||
|
|
||||||
resolver, err := newAllLayersResolver(img)
|
resolver, err := newImageAllLayersResolver(img)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
refs, err := resolver.FilesByPath(test.fixture)
|
refs, err := resolver.FilesByPath(test.fixture)
|
||||||
|
@ -361,7 +361,7 @@ func TestAllLayersImageResolver_FilesContents_errorOnDirRequest(t *testing.T) {
|
||||||
|
|
||||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||||
|
|
||||||
resolver, err := newAllLayersResolver(img)
|
resolver, err := newImageAllLayersResolver(img)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
var dirLoc *Location
|
var dirLoc *Location
|
||||||
|
@ -675,7 +675,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||||
|
|
||||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||||
|
|
||||||
resolver, err := newAllLayersResolver(img)
|
resolver, err := newImageAllLayersResolver(img)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
actual := test.runner(resolver)
|
actual := test.runner(resolver)
|
||||||
|
@ -689,7 +689,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||||
func TestAllLayersResolver_AllLocations(t *testing.T) {
|
func TestAllLayersResolver_AllLocations(t *testing.T) {
|
||||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted")
|
img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted")
|
||||||
|
|
||||||
resolver, err := newAllLayersResolver(img)
|
resolver, err := newImageAllLayersResolver(img)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
paths := strset.New()
|
paths := strset.New()
|
||||||
|
|
|
@ -168,7 +168,7 @@ func (r *imageSquashResolver) FileContentsByLocation(location Location) (io.Read
|
||||||
return nil, fmt.Errorf("unable to get file contents for directory: %+v", location)
|
return nil, fmt.Errorf("unable to get file contents for directory: %+v", location)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.img.FileContentsByRef(location.ref)
|
return r.img.OpenReference(location.ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *imageSquashResolver) AllLocations() <-chan Location {
|
func (r *imageSquashResolver) AllLocations() <-chan Location {
|
||||||
|
|
98
syft/source/image_squash_with_all_layers_resolver.go
Normal file
98
syft/source/image_squash_with_all_layers_resolver.go
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package source
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ FileResolver = (*imageSquashWithAllLayersResolver)(nil)
|
||||||
|
|
||||||
|
// imageSquashWithAllLayersResolver acts like a squash resolver, but additionally returns all paths in earlier layers
|
||||||
|
// that have been added/modified (like the all-layers resolver).
|
||||||
|
type imageSquashWithAllLayersResolver struct {
|
||||||
|
squashed *imageSquashResolver
|
||||||
|
allLayers *imageAllLayersResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
// newImageSquashWithAllLayersResolver returns a new resolver from the perspective of the squashed representation for
|
||||||
|
// the given image, but additionally returns all instances of a path that have been added/modified.
|
||||||
|
func newImageSquashWithAllLayersResolver(img *image.Image) (*imageSquashWithAllLayersResolver, error) {
|
||||||
|
squashed, err := newImageSquashResolver(img)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allLayers, err := newImageAllLayersResolver(img)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &imageSquashWithAllLayersResolver{
|
||||||
|
squashed: squashed,
|
||||||
|
allLayers: allLayers,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i imageSquashWithAllLayersResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
||||||
|
return i.squashed.FileContentsByLocation(location)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i imageSquashWithAllLayersResolver) HasPath(s string) bool {
|
||||||
|
return i.squashed.HasPath(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i imageSquashWithAllLayersResolver) filterLocations(locations []Location, err error) ([]Location, error) {
|
||||||
|
if err != nil {
|
||||||
|
return locations, err
|
||||||
|
}
|
||||||
|
var ret []Location
|
||||||
|
for _, l := range locations {
|
||||||
|
if i.squashed.HasPath(l.RealPath) {
|
||||||
|
// not only should the real path to the file exist, but the way we took to get there should also exist
|
||||||
|
// (e.g. if we are looking for /etc/passwd, but the real path is /etc/passwd -> /etc/passwd-1, then we should
|
||||||
|
// make certain that /etc/passwd-1 exists)
|
||||||
|
if l.VirtualPath != "" && !i.squashed.HasPath(l.VirtualPath) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ret = append(ret, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i imageSquashWithAllLayersResolver) FilesByPath(paths ...string) ([]Location, error) {
|
||||||
|
return i.filterLocations(i.allLayers.FilesByPath(paths...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i imageSquashWithAllLayersResolver) FilesByGlob(patterns ...string) ([]Location, error) {
|
||||||
|
return i.filterLocations(i.allLayers.FilesByGlob(patterns...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i imageSquashWithAllLayersResolver) FilesByMIMEType(types ...string) ([]Location, error) {
|
||||||
|
return i.filterLocations(i.allLayers.FilesByMIMEType(types...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i imageSquashWithAllLayersResolver) RelativeFileByPath(l Location, path string) *Location {
|
||||||
|
if !i.squashed.HasPath(path) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return i.allLayers.RelativeFileByPath(l, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i imageSquashWithAllLayersResolver) AllLocations() <-chan Location {
|
||||||
|
var ret = make(chan Location)
|
||||||
|
go func() {
|
||||||
|
defer close(ret)
|
||||||
|
for l := range i.allLayers.AllLocations() {
|
||||||
|
if i.squashed.HasPath(l.RealPath) {
|
||||||
|
ret <- l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i imageSquashWithAllLayersResolver) FileMetadataByLocation(location Location) (FileMetadata, error) {
|
||||||
|
return fileMetadataByLocation(i.squashed.img, location)
|
||||||
|
}
|
|
@ -10,14 +10,17 @@ const (
|
||||||
UnknownScope Scope = "UnknownScope"
|
UnknownScope Scope = "UnknownScope"
|
||||||
// SquashedScope indicates to only catalog content visible from the squashed filesystem representation (what can be seen only within the container at runtime)
|
// SquashedScope indicates to only catalog content visible from the squashed filesystem representation (what can be seen only within the container at runtime)
|
||||||
SquashedScope Scope = "Squashed"
|
SquashedScope Scope = "Squashed"
|
||||||
// AllLayersScope indicates to catalog content on all layers, irregardless if it is visible from the container at runtime.
|
// AllLayersScope indicates to catalog content on all layers, regardless if it is visible from the container at runtime.
|
||||||
AllLayersScope Scope = "AllLayers"
|
AllLayersScope Scope = "AllLayers"
|
||||||
|
// SquashedWithAllLayersScope indicates to catalog content on all layers, but only include content visible from the squashed filesystem representation.
|
||||||
|
SquashedWithAllLayersScope Scope = "SquashedWithAllLayers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AllScopes is a slice containing all possible scope options
|
// AllScopes is a slice containing all possible scope options
|
||||||
var AllScopes = []Scope{
|
var AllScopes = []Scope{
|
||||||
SquashedScope,
|
SquashedScope,
|
||||||
AllLayersScope,
|
AllLayersScope,
|
||||||
|
SquashedWithAllLayersScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseScope returns a scope as indicated from the given string.
|
// ParseScope returns a scope as indicated from the given string.
|
||||||
|
@ -27,6 +30,8 @@ func ParseScope(userStr string) Scope {
|
||||||
return SquashedScope
|
return SquashedScope
|
||||||
case "all-layers", strings.ToLower(AllLayersScope.String()):
|
case "all-layers", strings.ToLower(AllLayersScope.String()):
|
||||||
return AllLayersScope
|
return AllLayersScope
|
||||||
|
case "squashed-with-all-layers", strings.ToLower(SquashedWithAllLayersScope.String()):
|
||||||
|
return SquashedWithAllLayersScope
|
||||||
}
|
}
|
||||||
return UnknownScope
|
return UnknownScope
|
||||||
}
|
}
|
||||||
|
|
|
@ -466,7 +466,9 @@ func (s *Source) FileResolver(scope Scope) (FileResolver, error) {
|
||||||
case SquashedScope:
|
case SquashedScope:
|
||||||
resolver, err = newImageSquashResolver(s.Image)
|
resolver, err = newImageSquashResolver(s.Image)
|
||||||
case AllLayersScope:
|
case AllLayersScope:
|
||||||
resolver, err = newAllLayersResolver(s.Image)
|
resolver, err = newImageAllLayersResolver(s.Image)
|
||||||
|
case SquashedWithAllLayersScope:
|
||||||
|
resolver, err = newImageSquashWithAllLayersResolver(s.Image)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("bad image scope provided: %+v", scope)
|
return nil, fmt.Errorf("bad image scope provided: %+v", scope)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue