mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 15:06:36 +01:00
Add annotations for evidence on package locations (#1723)
* add location annotations + deb evidence annotations Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * rename LocationData struct and Annotation helper function Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add failing integration test for evidence coverage Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add evidence to aplm cataloger locations Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * change location annotation helper to return a location copy Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add evidence to binary cataloger locations Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * updated remaining catalogers with location annotations Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix unit tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix linting Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * bump json schema Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * partial addressing of review comments Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * rename location.WithAnnotation Signed-off-by: Alex Goodman <alex.goodman@anchore.com> --------- Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
@@ -765,30 +765,10 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
//VirtualPath: "file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
//VirtualPath: "file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
//VirtualPath: "file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "parent/file-4.txt",
|
||||
},
|
||||
//VirtualPath: "parent/file-4.txt",
|
||||
},
|
||||
NewLocation("file-1.txt"), // note: missing virtual path "file-1.txt"
|
||||
NewLocation("file-3.txt"), // note: missing virtual path "file-3.txt"
|
||||
NewLocation("file-2.txt"), // note: missing virtual path "file-2.txt"
|
||||
NewLocation("parent/file-4.txt"), // note: missing virtual path "file-4.txt"
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -801,31 +781,11 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
VirtualPath: "link-1",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
VirtualPath: "link-2",
|
||||
},
|
||||
NewVirtualLocation("file-1.txt", "link-1"),
|
||||
NewVirtualLocation("file-2.txt", "link-2"),
|
||||
// we already have this real file path via another link, so only one is returned
|
||||
//{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "link-indirect",
|
||||
//},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
VirtualPath: "link-within",
|
||||
},
|
||||
//NewVirtualLocation("file-2.txt", "link-indirect"),
|
||||
NewVirtualLocation("file-3.txt", "link-within"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -838,12 +798,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// this has two copies in the base image, which overwrites the same location
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
//VirtualPath: "file-2.txt",
|
||||
},
|
||||
NewLocation("file-2.txt"), // note: missing virtual path "file-2.txt",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -855,30 +810,10 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
//VirtualPath: "file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
//VirtualPath: "file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
//VirtualPath: "file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "parent/file-4.txt",
|
||||
},
|
||||
//VirtualPath: "parent/file-4.txt",
|
||||
},
|
||||
NewLocation("file-1.txt"), // note: missing virtual path "file-1.txt"
|
||||
NewLocation("file-2.txt"), // note: missing virtual path "file-2.txt"
|
||||
NewLocation("file-3.txt"), // note: missing virtual path "file-3.txt"
|
||||
NewLocation("parent/file-4.txt"), // note: missing virtual path "parent/file-4.txt"
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -890,33 +825,41 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
VirtualPath: "link-1",
|
||||
ref: file.Reference{RealPath: "file-1.txt"},
|
||||
},
|
||||
VirtualPath: "link-1",
|
||||
ref: file.Reference{RealPath: "file-1.txt"},
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
VirtualPath: "link-2",
|
||||
ref: file.Reference{RealPath: "file-2.txt"},
|
||||
},
|
||||
VirtualPath: "link-2",
|
||||
ref: file.Reference{RealPath: "file-2.txt"},
|
||||
},
|
||||
// we already have this real file path via another link, so only one is returned
|
||||
//{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "link-indirect",
|
||||
// ref: file.Reference{RealPath: "file-2.txt"},
|
||||
// LocationData: LocationData{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "link-indirect",
|
||||
// ref: file.Reference{RealPath: "file-2.txt"},
|
||||
// },
|
||||
//},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
VirtualPath: "link-within",
|
||||
ref: file.Reference{RealPath: "file-3.txt"},
|
||||
},
|
||||
VirtualPath: "link-within",
|
||||
ref: file.Reference{RealPath: "file-3.txt"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -929,30 +872,10 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-1.txt",
|
||||
},
|
||||
//VirtualPath: "file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
//VirtualPath: "file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-3.txt",
|
||||
},
|
||||
//VirtualPath: "file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "parent/file-4.txt",
|
||||
},
|
||||
//VirtualPath: "parent/file-4.txt",
|
||||
},
|
||||
NewLocation("file-1.txt"), // note: missing virtual path "file-1.txt"
|
||||
NewLocation("file-2.txt"), // note: missing virtual path "file-2.txt"
|
||||
NewLocation("file-3.txt"), // note: missing virtual path "file-3.txt"
|
||||
NewLocation("parent/file-4.txt"), // note: missing virtual path "parent/file-4.txt"
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -965,12 +888,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
VirtualPath: "link-2",
|
||||
},
|
||||
NewVirtualLocation("file-2.txt", "link-2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -983,12 +901,7 @@ func Test_directoryResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "file-2.txt",
|
||||
},
|
||||
VirtualPath: "link-indirect",
|
||||
},
|
||||
NewVirtualLocation("file-2.txt", "link-indirect"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
)
|
||||
|
||||
func TestExcludingResolver(t *testing.T) {
|
||||
@@ -79,25 +77,25 @@ func TestExcludingResolver(t *testing.T) {
|
||||
|
||||
for _, path := range diff {
|
||||
assert.False(t, er.HasPath(path))
|
||||
c, err := er.FileContentsByLocation(makeLocation(path))
|
||||
c, err := er.FileContentsByLocation(NewLocation(path))
|
||||
assert.Nil(t, c)
|
||||
assert.Error(t, err)
|
||||
m, err := er.FileMetadataByLocation(makeLocation(path))
|
||||
m, err := er.FileMetadataByLocation(NewLocation(path))
|
||||
assert.Empty(t, m.LinkDestination)
|
||||
assert.Error(t, err)
|
||||
l := er.RelativeFileByPath(makeLocation(""), path)
|
||||
l := er.RelativeFileByPath(NewLocation(""), path)
|
||||
assert.Nil(t, l)
|
||||
}
|
||||
|
||||
for _, path := range test.expected {
|
||||
assert.True(t, er.HasPath(path))
|
||||
c, err := er.FileContentsByLocation(makeLocation(path))
|
||||
c, err := er.FileContentsByLocation(NewLocation(path))
|
||||
assert.NotNil(t, c)
|
||||
assert.Nil(t, err)
|
||||
m, err := er.FileMetadataByLocation(makeLocation(path))
|
||||
m, err := er.FileMetadataByLocation(NewLocation(path))
|
||||
assert.NotEmpty(t, m.LinkDestination)
|
||||
assert.Nil(t, err)
|
||||
l := er.RelativeFileByPath(makeLocation(""), path)
|
||||
l := er.RelativeFileByPath(NewLocation(""), path)
|
||||
assert.NotNil(t, l)
|
||||
}
|
||||
})
|
||||
@@ -119,17 +117,6 @@ func difference(a, b []string) []string {
|
||||
return diff
|
||||
}
|
||||
|
||||
func makeLocation(path string) Location {
|
||||
return Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: path,
|
||||
FileSystemID: "",
|
||||
},
|
||||
VirtualPath: "",
|
||||
ref: file.Reference{},
|
||||
}
|
||||
}
|
||||
|
||||
func locationPaths(locations []Location) []string {
|
||||
paths := []string{}
|
||||
for _, l := range locations {
|
||||
@@ -145,7 +132,7 @@ type mockResolver struct {
|
||||
func (r *mockResolver) getLocations() ([]Location, error) {
|
||||
out := []Location{}
|
||||
for _, path := range r.locations {
|
||||
out = append(out, makeLocation(path))
|
||||
out = append(out, NewLocation(path))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
@@ -189,11 +176,8 @@ func (r *mockResolver) FilesByBasenameGlob(_ ...string) ([]Location, error) {
|
||||
}
|
||||
|
||||
func (r *mockResolver) RelativeFileByPath(_ Location, path string) *Location {
|
||||
return &Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: path,
|
||||
},
|
||||
}
|
||||
l := NewLocation(path)
|
||||
return &l
|
||||
}
|
||||
|
||||
func (r *mockResolver) AllLocations() <-chan Location {
|
||||
|
||||
@@ -398,66 +398,17 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/group",
|
||||
},
|
||||
VirtualPath: "/etc/group",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/passwd",
|
||||
},
|
||||
VirtualPath: "/etc/passwd",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/shadow",
|
||||
},
|
||||
VirtualPath: "/etc/shadow",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
NewVirtualLocation("/etc/group", "/etc/group"),
|
||||
NewVirtualLocation("/etc/passwd", "/etc/passwd"),
|
||||
NewVirtualLocation("/etc/shadow", "/etc/shadow"),
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 1
|
||||
// note: we're de-duping the redundant access to file-3.txt
|
||||
// ... (there would usually be two copies)
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 2
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"), // copy 1
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"), // copy 2
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -469,32 +420,10 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/link-1",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/link-within",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/link-1"),
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"), // copy 1
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"), // copy 2
|
||||
NewVirtualLocation("/file-3.txt", "/link-within"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -506,20 +435,8 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 1
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 2
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -531,45 +448,12 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
// when we copy into the link path, the same file-4.txt is copied
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 1
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 2
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"), // when we copy into the link path, the same file-4.txt is copied
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -581,45 +465,12 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
// copy 1
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
// copy 2
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
// when we copy into the link path, the same file-4.txt is copied
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 1
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"), // copy 2
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"), // when we copy into the link path, the same file-4.txt is copied
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -632,18 +483,8 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"),
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -656,18 +497,8 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-indirect",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-indirect",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/link-indirect"),
|
||||
NewVirtualLocation("/file-2.txt", "/link-indirect"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -382,48 +382,13 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/group",
|
||||
},
|
||||
VirtualPath: "/etc/group",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/passwd",
|
||||
},
|
||||
VirtualPath: "/etc/passwd",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/shadow",
|
||||
},
|
||||
VirtualPath: "/etc/shadow",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/etc/group", "/etc/group"),
|
||||
NewVirtualLocation("/etc/passwd", "/etc/passwd"),
|
||||
NewVirtualLocation("/etc/shadow", "/etc/shadow"),
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -435,32 +400,14 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/link-1",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/link-1"),
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"),
|
||||
|
||||
// though this is a link, and it matches to the file, the resolver de-duplicates files
|
||||
// by the real path, so it is not included in the results
|
||||
//{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "/file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "/link-indirect",
|
||||
//},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/link-within",
|
||||
},
|
||||
//NewVirtualLocation("/file-2.txt", "/link-indirect"),
|
||||
|
||||
NewVirtualLocation("/file-3.txt", "/link-within"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -473,12 +420,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// this has two copies in the base image, which overwrites the same location
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -490,30 +432,10 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"),
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -524,34 +446,44 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/link-1",
|
||||
ref: file.Reference{RealPath: "/file-1.txt"},
|
||||
},
|
||||
VirtualPath: "/link-1",
|
||||
ref: file.Reference{RealPath: "/file-1.txt"},
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
LocationData: LocationData{
|
||||
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
ref: file.Reference{RealPath: "/file-2.txt"},
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
ref: file.Reference{RealPath: "/file-2.txt"},
|
||||
},
|
||||
// we already have this real file path via another link, so only one is returned
|
||||
//{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "/file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "/link-indirect",
|
||||
// ref: file.Reference{RealPath: "/file-2.txt"},
|
||||
// LocationData: LocationData{
|
||||
// Coordinates: Coordinates{
|
||||
// RealPath: "/file-2.txt",
|
||||
// },
|
||||
// VirtualPath: "/link-indirect",
|
||||
// ref: file.Reference{RealPath: "/file-2.txt"},
|
||||
// },
|
||||
//},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/link-within",
|
||||
ref: file.Reference{RealPath: "/file-3.txt"},
|
||||
},
|
||||
VirtualPath: "/link-within",
|
||||
ref: file.Reference{RealPath: "/file-3.txt"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -564,30 +496,10 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
return actualLocations
|
||||
},
|
||||
expected: []Location{
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-1.txt",
|
||||
},
|
||||
VirtualPath: "/file-1.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/file-2.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-3.txt",
|
||||
},
|
||||
VirtualPath: "/file-3.txt",
|
||||
},
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/parent/file-4.txt",
|
||||
},
|
||||
VirtualPath: "/parent/file-4.txt",
|
||||
},
|
||||
NewVirtualLocation("/file-1.txt", "/file-1.txt"),
|
||||
NewVirtualLocation("/file-2.txt", "/file-2.txt"),
|
||||
NewVirtualLocation("/file-3.txt", "/file-3.txt"),
|
||||
NewVirtualLocation("/parent/file-4.txt", "/parent/file-4.txt"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -600,12 +512,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-2",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/link-2"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -618,12 +525,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
},
|
||||
expected: []Location{
|
||||
// we have multiple copies across layers
|
||||
{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/file-2.txt",
|
||||
},
|
||||
VirtualPath: "/link-indirect",
|
||||
},
|
||||
NewVirtualLocation("/file-2.txt", "/link-indirect"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -646,7 +548,8 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
|
||||
func compareLocations(t *testing.T, expected, actual []Location) {
|
||||
t.Helper()
|
||||
ignoreUnexported := cmpopts.IgnoreFields(Location{}, "ref")
|
||||
ignoreUnexported := cmpopts.IgnoreFields(LocationData{}, "ref")
|
||||
ignoreMetadata := cmpopts.IgnoreFields(LocationMetadata{}, "Annotations")
|
||||
ignoreFS := cmpopts.IgnoreFields(Coordinates{}, "FileSystemID")
|
||||
|
||||
sort.Sort(Locations(expected))
|
||||
@@ -655,6 +558,7 @@ func compareLocations(t *testing.T, expected, actual []Location) {
|
||||
if d := cmp.Diff(expected, actual,
|
||||
ignoreUnexported,
|
||||
ignoreFS,
|
||||
ignoreMetadata,
|
||||
); d != "" {
|
||||
|
||||
t.Errorf("unexpected locations (-want +got):\n%s", d)
|
||||
|
||||
+96
-22
@@ -3,6 +3,8 @@ package source
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
)
|
||||
@@ -10,18 +12,55 @@ import (
|
||||
// Location represents a path relative to a particular filesystem resolved to a specific file.Reference. This struct is used as a key
|
||||
// in content fetching to uniquely identify a file relative to a request (the VirtualPath).
|
||||
type Location struct {
|
||||
LocationData `cyclonedx:""`
|
||||
LocationMetadata `cyclonedx:""`
|
||||
}
|
||||
|
||||
type LocationData struct {
|
||||
Coordinates `cyclonedx:""` // Empty string here means there is no intermediate property name, e.g. syft:locations:0:path without "coordinates"
|
||||
// note: it is IMPORTANT to ignore anything but the coordinates for a Location when considering the ID (hash value)
|
||||
// since the coordinates are the minimally correct ID for a location (symlinks should not come into play)
|
||||
VirtualPath string `hash:"ignore" json:"virtualPath,omitempty"` // The path to the file which may or may not have hardlinks / symlinks
|
||||
ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location.
|
||||
VirtualPath string `hash:"ignore" json:"-"` // The path to the file which may or may not have hardlinks / symlinks
|
||||
ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location.
|
||||
}
|
||||
|
||||
type LocationMetadata struct {
|
||||
Annotations map[string]string `json:"annotations,omitempty"` // Arbitrary key-value pairs that can be used to annotate a location
|
||||
}
|
||||
|
||||
func (m *LocationMetadata) merge(other LocationMetadata) error {
|
||||
var errs error
|
||||
for k, v := range other.Annotations {
|
||||
if otherV, ok := m.Annotations[k]; ok {
|
||||
if v != otherV {
|
||||
err := fmt.Errorf("unable to merge location metadata: conflicting values for key=%q: %q != %q", k, v, otherV)
|
||||
errs = multierror.Append(errs, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
m.Annotations[k] = v
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func (l Location) WithAnnotation(key, value string) Location {
|
||||
if l.LocationMetadata.Annotations == nil {
|
||||
l.LocationMetadata.Annotations = map[string]string{}
|
||||
}
|
||||
l.LocationMetadata.Annotations[key] = value
|
||||
return l
|
||||
}
|
||||
|
||||
// NewLocation creates a new Location representing a path without denoting a filesystem or FileCatalog reference.
|
||||
func NewLocation(realPath string) Location {
|
||||
return Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: realPath,
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: realPath,
|
||||
},
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -29,40 +68,70 @@ func NewLocation(realPath string) Location {
|
||||
// NewVirtualLocation creates a new location for a path accessed by a virtual path (a path with a symlink or hardlink somewhere in the path)
|
||||
func NewVirtualLocation(realPath, virtualPath string) Location {
|
||||
return Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: realPath,
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: realPath,
|
||||
},
|
||||
VirtualPath: virtualPath,
|
||||
},
|
||||
VirtualPath: virtualPath,
|
||||
}
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
}}
|
||||
}
|
||||
|
||||
// NewLocationFromCoordinates creates a new location for the given Coordinates.
|
||||
func NewLocationFromCoordinates(coordinates Coordinates) Location {
|
||||
return Location{
|
||||
Coordinates: coordinates,
|
||||
}
|
||||
LocationData: LocationData{
|
||||
Coordinates: coordinates,
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
}}
|
||||
}
|
||||
|
||||
// NewVirtualLocationFromCoordinates creates a new location for the given Coordinates via a virtual path.
|
||||
func NewVirtualLocationFromCoordinates(coordinates Coordinates, virtualPath string) Location {
|
||||
return Location{
|
||||
LocationData: LocationData{
|
||||
Coordinates: coordinates,
|
||||
VirtualPath: virtualPath,
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
}}
|
||||
}
|
||||
|
||||
// NewLocationFromImage creates a new Location representing the given path (extracted from the ref) relative to the given image.
|
||||
func NewLocationFromImage(virtualPath string, ref file.Reference, img *image.Image) Location {
|
||||
layer := img.FileCatalog.Layer(ref)
|
||||
return Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: string(ref.RealPath),
|
||||
FileSystemID: layer.Metadata.Digest,
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: string(ref.RealPath),
|
||||
FileSystemID: layer.Metadata.Digest,
|
||||
},
|
||||
VirtualPath: virtualPath,
|
||||
ref: ref,
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
VirtualPath: virtualPath,
|
||||
ref: ref,
|
||||
}
|
||||
}
|
||||
|
||||
// NewLocationFromDirectory creates a new Location representing the given path (extracted from the ref) relative to the given directory.
|
||||
func NewLocationFromDirectory(responsePath string, ref file.Reference) Location {
|
||||
return Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: responsePath,
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: responsePath,
|
||||
},
|
||||
ref: ref,
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
ref: ref,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,11 +141,16 @@ func NewVirtualLocationFromDirectory(responsePath, virtualResponsePath string, r
|
||||
return NewLocationFromDirectory(responsePath, ref)
|
||||
}
|
||||
return Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: responsePath,
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: responsePath,
|
||||
},
|
||||
VirtualPath: virtualResponsePath,
|
||||
ref: ref,
|
||||
},
|
||||
LocationMetadata: LocationMetadata{
|
||||
Annotations: map[string]string{},
|
||||
},
|
||||
VirtualPath: virtualResponsePath,
|
||||
ref: ref,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/mitchellh/hashstructure/v2"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
type LocationSet struct {
|
||||
set map[Location]struct{}
|
||||
set map[LocationData]LocationMetadata
|
||||
}
|
||||
|
||||
func NewLocationSet(locations ...Location) (s LocationSet) {
|
||||
@@ -20,10 +22,18 @@ func NewLocationSet(locations ...Location) (s LocationSet) {
|
||||
|
||||
func (s *LocationSet) Add(locations ...Location) {
|
||||
if s.set == nil {
|
||||
s.set = make(map[Location]struct{})
|
||||
s.set = make(map[LocationData]LocationMetadata)
|
||||
}
|
||||
for _, l := range locations {
|
||||
s.set[l] = struct{}{}
|
||||
if m, ok := s.set[l.LocationData]; ok {
|
||||
err := m.merge(l.LocationMetadata)
|
||||
if err != nil {
|
||||
log.Debugf("partial merge of location metadata: %+v", err)
|
||||
}
|
||||
s.set[l.LocationData] = m
|
||||
} else {
|
||||
s.set[l.LocationData] = l.LocationMetadata
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +42,7 @@ func (s LocationSet) Remove(locations ...Location) {
|
||||
return
|
||||
}
|
||||
for _, l := range locations {
|
||||
delete(s.set, l)
|
||||
delete(s.set, l.LocationData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +50,7 @@ func (s LocationSet) Contains(l Location) bool {
|
||||
if s.set == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := s.set[l]
|
||||
_, ok := s.set[l.LocationData]
|
||||
return ok
|
||||
}
|
||||
|
||||
@@ -50,8 +60,11 @@ func (s LocationSet) ToSlice() []Location {
|
||||
}
|
||||
locations := make([]Location, len(s.set))
|
||||
idx := 0
|
||||
for v := range s.set {
|
||||
locations[idx] = v
|
||||
for dir := range s.set {
|
||||
locations[idx] = Location{
|
||||
LocationData: dir,
|
||||
LocationMetadata: s.set[dir],
|
||||
}
|
||||
idx++
|
||||
}
|
||||
sort.Sort(Locations(locations))
|
||||
|
||||
@@ -12,35 +12,43 @@ import (
|
||||
func TestLocationSet(t *testing.T) {
|
||||
|
||||
etcHostsLinkVar := Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/var/etc/hosts",
|
||||
},
|
||||
VirtualPath: "/var/etc/hosts",
|
||||
}
|
||||
|
||||
etcHostsLinkHome := Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/home/wagoodman/hosts",
|
||||
},
|
||||
VirtualPath: "/home/wagoodman/hosts",
|
||||
}
|
||||
|
||||
binA := Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "a",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
}
|
||||
|
||||
binB := Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "b",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "b",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
@@ -87,41 +95,51 @@ func TestLocationSet(t *testing.T) {
|
||||
|
||||
func TestLocationSet_Hash(t *testing.T) {
|
||||
etcAlink := Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/var/etc/hosts",
|
||||
},
|
||||
VirtualPath: "/var/etc/hosts",
|
||||
}
|
||||
|
||||
etcA := Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
etcB := Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "b",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/etc/hosts",
|
||||
FileSystemID: "b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
binA := Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "a",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "a",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
}
|
||||
|
||||
binB := Location{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "b",
|
||||
LocationData: LocationData{
|
||||
Coordinates: Coordinates{
|
||||
RealPath: "/bin",
|
||||
FileSystemID: "b",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
},
|
||||
VirtualPath: "/usr/bin",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
|
||||
@@ -37,9 +37,11 @@ func TestLocation_ID(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
l := Location{
|
||||
Coordinates: test.coordinates,
|
||||
VirtualPath: test.virtualPath,
|
||||
ref: test.ref,
|
||||
LocationData: LocationData{
|
||||
Coordinates: test.coordinates,
|
||||
VirtualPath: test.virtualPath,
|
||||
ref: test.ref,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, l.ID(), test.coordinates.ID())
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ var _ FileResolver = (*MockResolver)(nil)
|
||||
// paths, which are typically paths to test fixtures.
|
||||
type MockResolver struct {
|
||||
locations []Location
|
||||
metadata map[Location]FileMetadata
|
||||
metadata map[Coordinates]FileMetadata
|
||||
mimeTypeIndex map[string][]Location
|
||||
extension map[string][]Location
|
||||
basename map[string][]Location
|
||||
@@ -41,18 +41,19 @@ func NewMockResolverForPaths(paths ...string) *MockResolver {
|
||||
|
||||
return &MockResolver{
|
||||
locations: locations,
|
||||
metadata: make(map[Location]FileMetadata),
|
||||
metadata: make(map[Coordinates]FileMetadata),
|
||||
extension: extension,
|
||||
basename: basename,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMockResolverForPathsWithMetadata(metadata map[Location]FileMetadata) *MockResolver {
|
||||
func NewMockResolverForPathsWithMetadata(metadata map[Coordinates]FileMetadata) *MockResolver {
|
||||
var locations []Location
|
||||
var mimeTypeIndex = make(map[string][]Location)
|
||||
extension := make(map[string][]Location)
|
||||
basename := make(map[string][]Location)
|
||||
for l, m := range metadata {
|
||||
for c, m := range metadata {
|
||||
l := NewLocationFromCoordinates(c)
|
||||
locations = append(locations, l)
|
||||
mimeTypeIndex[m.MIMEType] = append(mimeTypeIndex[m.MIMEType], l)
|
||||
ext := path.Ext(l.RealPath)
|
||||
@@ -89,7 +90,7 @@ func (r MockResolver) String() string {
|
||||
// path does not exist, an error is returned.
|
||||
func (r MockResolver) FileContentsByLocation(location Location) (io.ReadCloser, error) {
|
||||
for _, l := range r.locations {
|
||||
if l == location {
|
||||
if l.Coordinates == location.Coordinates {
|
||||
return os.Open(location.RealPath)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user