make AllLocations accept a context (#2518)

The previous implementation would leak a goroutine if the caller of
AllLocations stopped iterating early. Now, accept a context so that the
caller can cancel the AllLocations iterator rather than leak the
goroutine.

Signed-off-by: Will Murphy <will.murphy@anchore.com>
This commit is contained in:
William Murphy 2024-01-22 11:05:59 -05:00 committed by GitHub
parent 3046d43a8a
commit c6ce1de928
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 156 additions and 42 deletions

1
go.mod
View File

@ -73,6 +73,7 @@ require (
github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0
github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeipuuv/gojsonschema v1.2.0
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
go.uber.org/goleak v1.3.0
golang.org/x/mod v0.14.0 golang.org/x/mod v0.14.0
golang.org/x/net v0.20.0 golang.org/x/net v0.20.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1

4
go.sum
View File

@ -854,8 +854,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=

View File

@ -1,6 +1,7 @@
package filemetadata package filemetadata
import ( import (
"context"
"fmt" "fmt"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
@ -21,8 +22,10 @@ func NewCataloger() *Cataloger {
func (i *Cataloger) Catalog(resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates]file.Metadata, error) { func (i *Cataloger) Catalog(resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates]file.Metadata, error) {
results := make(map[file.Coordinates]file.Metadata) results := make(map[file.Coordinates]file.Metadata)
var locations <-chan file.Location var locations <-chan file.Location
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
if len(coordinates) == 0 { if len(coordinates) == 0 {
locations = resolver.AllLocations() locations = resolver.AllLocations(ctx)
} else { } else {
locations = func() <-chan file.Location { locations = func() <-chan file.Location {
ch := make(chan file.Location) ch := make(chan file.Location)
@ -35,7 +38,12 @@ func (i *Cataloger) Catalog(resolver file.Resolver, coordinates ...file.Coordina
continue continue
} }
for _, loc := range locs { for _, loc := range locs {
ch <- loc select {
case <-ctx.Done():
return
case ch <- loc:
continue
}
} }
} }
}() }()

View File

@ -1,13 +1,17 @@
package internal package internal
import ( import (
"context"
stereoscopeFile "github.com/anchore/stereoscope/pkg/file" stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
) )
func AllRegularFiles(resolver file.Resolver) (locations []file.Location) { func AllRegularFiles(resolver file.Resolver) (locations []file.Location) {
for location := range resolver.AllLocations() { ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for location := range resolver.AllLocations(ctx) {
resolvedLocations, err := resolver.FilesByPath(location.RealPath) resolvedLocations, err := resolver.FilesByPath(location.RealPath)
if err != nil { if err != nil {
log.Warnf("unable to resolve %+v: %+v", location, err) log.Warnf("unable to resolve %+v: %+v", location, err)

View File

@ -1,6 +1,7 @@
package file package file
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -144,12 +145,17 @@ func (r MockResolver) RelativeFileByPath(_ Location, path string) *Location {
return &paths[0] return &paths[0]
} }
func (r MockResolver) AllLocations() <-chan Location { func (r MockResolver) AllLocations(ctx context.Context) <-chan Location {
results := make(chan Location) results := make(chan Location)
go func() { go func() {
defer close(results) defer close(results)
for _, l := range r.locations { for _, l := range r.locations {
results <- l select {
case <-ctx.Done():
return
case results <- l:
continue
}
} }
}() }()
return results return results

View File

@ -1,6 +1,9 @@
package file package file
import "io" import (
"context"
"io"
)
// Resolver is an interface that encompasses how to get specific file references and file contents for a generic data source. // 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 {
@ -53,7 +56,7 @@ type LocationResolver interface {
// The implementation for this may vary, however, generally the following considerations should be made: // The implementation for this may vary, however, generally the following considerations should be made:
// - NO symlink resolution should be performed on results // - NO symlink resolution should be performed on results
// - returns locations for any file or directory // - returns locations for any file or directory
AllLocations() <-chan Location AllLocations(ctx context.Context) <-chan Location
} }
type WritableResolver interface { type WritableResolver interface {

View File

@ -1,6 +1,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"fmt" "fmt"
"io" "io"
@ -234,14 +235,19 @@ func (r *ContainerImageAllLayers) FilesByMIMEType(types ...string) ([]file.Locat
return uniqueLocations, nil return uniqueLocations, nil
} }
func (r *ContainerImageAllLayers) AllLocations() <-chan file.Location { func (r *ContainerImageAllLayers) AllLocations(ctx context.Context) <-chan file.Location {
results := make(chan file.Location) results := make(chan file.Location)
go func() { go func() {
defer close(results) defer close(results)
for _, layerIdx := range r.layers { for _, layerIdx := range r.layers {
tree := r.img.Layers[layerIdx].Tree tree := r.img.Layers[layerIdx].Tree
for _, ref := range tree.AllFiles(stereoscopeFile.AllTypes()...) { for _, ref := range tree.AllFiles(stereoscopeFile.AllTypes()...) {
results <- file.NewLocationFromImage(string(ref.RealPath), ref, r.img) select {
case <-ctx.Done():
return
case results <- file.NewLocationFromImage(string(ref.RealPath), ref, r.img):
continue
}
} }
} }
}() }()

View File

@ -1,6 +1,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"io" "io"
"sort" "sort"
"testing" "testing"
@ -358,7 +359,9 @@ func TestAllLayersImageResolver_FilesContents_errorOnDirRequest(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
var dirLoc *file.Location var dirLoc *file.Location
for loc := range resolver.AllLocations() { ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for loc := range resolver.AllLocations(ctx) {
entry, err := resolver.img.FileCatalog.Get(loc.Reference()) entry, err := resolver.img.FileCatalog.Get(loc.Reference())
require.NoError(t, err) require.NoError(t, err)
if entry.Metadata.IsDir() { if entry.Metadata.IsDir() {
@ -517,7 +520,9 @@ func TestAllLayersResolver_AllLocations(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
paths := strset.New() paths := strset.New()
for loc := range resolver.AllLocations() { ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for loc := range resolver.AllLocations(ctx) {
paths.Add(loc.RealPath) paths.Add(loc.RealPath)
} }
expected := []string{ expected := []string{

View File

@ -1,6 +1,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"fmt" "fmt"
"io" "io"
@ -172,12 +173,17 @@ func (r *ContainerImageSquash) FileContentsByLocation(location file.Location) (i
return r.img.OpenReference(location.Reference()) return r.img.OpenReference(location.Reference())
} }
func (r *ContainerImageSquash) AllLocations() <-chan file.Location { func (r *ContainerImageSquash) AllLocations(ctx context.Context) <-chan file.Location {
results := make(chan file.Location) results := make(chan file.Location)
go func() { go func() {
defer close(results) defer close(results)
for _, ref := range r.img.SquashedTree().AllFiles(stereoscopeFile.AllTypes()...) { for _, ref := range r.img.SquashedTree().AllFiles(stereoscopeFile.AllTypes()...) {
results <- file.NewLocationFromImage(string(ref.RealPath), ref, r.img) select {
case <-ctx.Done():
return
case results <- file.NewLocationFromImage(string(ref.RealPath), ref, r.img):
continue
}
} }
}() }()
return results return results

View File

@ -1,6 +1,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"io" "io"
"sort" "sort"
"testing" "testing"
@ -343,7 +344,9 @@ func TestSquashImageResolver_FilesContents_errorOnDirRequest(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
var dirLoc *file.Location var dirLoc *file.Location
for loc := range resolver.AllLocations() { ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for loc := range resolver.AllLocations(ctx) {
entry, err := resolver.img.FileCatalog.Get(loc.Reference()) entry, err := resolver.img.FileCatalog.Get(loc.Reference())
require.NoError(t, err) require.NoError(t, err)
if entry.Metadata.IsDir() { if entry.Metadata.IsDir() {
@ -533,7 +536,9 @@ func TestSquashResolver_AllLocations(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
paths := strset.New() paths := strset.New()
for loc := range resolver.AllLocations() { ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for loc := range resolver.AllLocations(ctx) {
paths.Add(loc.RealPath) paths.Add(loc.RealPath)
} }
expected := []string{ expected := []string{

View File

@ -1,6 +1,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"io" "io"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
@ -80,13 +81,13 @@ func (d *Deferred) RelativeFileByPath(location file.Location, path string) *file
return r.RelativeFileByPath(location, path) return r.RelativeFileByPath(location, path)
} }
func (d *Deferred) AllLocations() <-chan file.Location { func (d *Deferred) AllLocations(ctx context.Context) <-chan file.Location {
r, err := d.getResolver() r, err := d.getResolver()
if err != nil { if err != nil {
log.Debug("unable to get resolver: %v", err) log.Debug("unable to get resolver: %v", err)
return nil return nil
} }
return r.AllLocations() return r.AllLocations(ctx)
} }
func (d *Deferred) FileMetadataByLocation(location file.Location) (file.Metadata, error) { func (d *Deferred) FileMetadataByLocation(location file.Location) (file.Metadata, error) {

View File

@ -1,6 +1,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -229,12 +230,17 @@ func (r Directory) FileContentsByLocation(location file.Location) (io.ReadCloser
return stereoscopeFile.NewLazyReadCloser(filePath), nil return stereoscopeFile.NewLazyReadCloser(filePath), nil
} }
func (r *Directory) AllLocations() <-chan file.Location { func (r *Directory) AllLocations(ctx context.Context) <-chan file.Location {
results := make(chan file.Location) results := make(chan file.Location)
go func() { go func() {
defer close(results) defer close(results)
for _, ref := range r.tree.AllFiles(stereoscopeFile.AllTypes()...) { for _, ref := range r.tree.AllFiles(stereoscopeFile.AllTypes()...) {
results <- file.NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref) select {
case <-ctx.Done():
return
case results <- file.NewLocationFromDirectory(r.responsePath(string(ref.RealPath)), ref):
continue
}
} }
}() }()
return results return results

View File

@ -4,6 +4,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"io" "io"
"io/fs" "io/fs"
"os" "os"
@ -17,6 +18,7 @@ import (
"github.com/scylladb/go-set/strset" "github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/goleak"
stereoscopeFile "github.com/anchore/stereoscope/pkg/file" stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
@ -1380,7 +1382,7 @@ func TestDirectoryResolver_DoNotAddVirtualPathsToTree(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
var allRealPaths []stereoscopeFile.Path var allRealPaths []stereoscopeFile.Path
for l := range resolver.AllLocations() { for l := range resolver.AllLocations(context.Background()) {
allRealPaths = append(allRealPaths, stereoscopeFile.Path(l.RealPath)) allRealPaths = append(allRealPaths, stereoscopeFile.Path(l.RealPath))
} }
pathSet := stereoscopeFile.NewPathSet(allRealPaths...) pathSet := stereoscopeFile.NewPathSet(allRealPaths...)
@ -1398,11 +1400,14 @@ func TestDirectoryResolver_DoNotAddVirtualPathsToTree(t *testing.T) {
} }
func TestDirectoryResolver_FilesContents_errorOnDirRequest(t *testing.T) { func TestDirectoryResolver_FilesContents_errorOnDirRequest(t *testing.T) {
defer goleak.VerifyNone(t)
resolver, err := NewFromDirectory("./test-fixtures/system_paths", "") resolver, err := NewFromDirectory("./test-fixtures/system_paths", "")
assert.NoError(t, err) assert.NoError(t, err)
var dirLoc *file.Location var dirLoc *file.Location
for loc := range resolver.AllLocations() { ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for loc := range resolver.AllLocations(ctx) {
entry, err := resolver.index.Get(loc.Reference()) entry, err := resolver.index.Get(loc.Reference())
require.NoError(t, err) require.NoError(t, err)
if entry.Metadata.IsDir() { if entry.Metadata.IsDir() {
@ -1423,7 +1428,7 @@ func TestDirectoryResolver_AllLocations(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
paths := strset.New() paths := strset.New()
for loc := range resolver.AllLocations() { for loc := range resolver.AllLocations(context.Background()) {
if strings.HasPrefix(loc.RealPath, "/") { if strings.HasPrefix(loc.RealPath, "/") {
// ignore outside the fixture root for now // ignore outside the fixture root for now
continue continue
@ -1449,3 +1454,14 @@ func TestDirectoryResolver_AllLocations(t *testing.T) {
assert.ElementsMatchf(t, expected, pathsList, "expected all paths to be indexed, but found different paths: \n%s", cmp.Diff(expected, paths.List())) assert.ElementsMatchf(t, expected, pathsList, "expected all paths to be indexed, but found different paths: \n%s", cmp.Diff(expected, paths.List()))
} }
func TestAllLocationsDoesNotLeakGoRoutine(t *testing.T) {
defer goleak.VerifyNone(t)
resolver, err := NewFromDirectory("./test-fixtures/symlinks-from-image-symlinks-fixture", "")
require.NoError(t, err)
ctx, cancel := context.WithCancel(context.Background())
for range resolver.AllLocations(ctx) {
break
}
cancel()
}

View File

@ -1,6 +1,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"io" "io"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
@ -34,7 +35,7 @@ func (e Empty) RelativeFileByPath(_ file.Location, _ string) *file.Location {
return nil return nil
} }
func (e Empty) AllLocations() <-chan file.Location { func (e Empty) AllLocations(_ context.Context) <-chan file.Location {
return nil return nil
} }

View File

@ -1,6 +1,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"fmt" "fmt"
"io" "io"
@ -69,13 +70,18 @@ func (r *excluding) RelativeFileByPath(location file.Location, path string) *fil
return l return l
} }
func (r *excluding) AllLocations() <-chan file.Location { func (r *excluding) AllLocations(ctx context.Context) <-chan file.Location {
c := make(chan file.Location) c := make(chan file.Location)
go func() { go func() {
defer close(c) defer close(c)
for location := range r.delegate.AllLocations() { for location := range r.delegate.AllLocations(ctx) {
if !locationMatches(&location, r.excludeFn) { if !locationMatches(&location, r.excludeFn) {
c <- location select {
case <-ctx.Done():
return
case c <- location:
continue
}
} }
} }
}() }()

View File

@ -1,6 +1,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"io" "io"
"strings" "strings"
"testing" "testing"
@ -69,7 +70,9 @@ func TestExcludingResolver(t *testing.T) {
locations = []file.Location{} locations = []file.Location{}
channel := er.AllLocations() ctx, cancel := context.WithCancel(context.Background())
defer cancel()
channel := er.AllLocations(ctx)
for location := range channel { for location := range channel {
locations = append(locations, location) locations = append(locations, location)
} }
@ -182,13 +185,18 @@ func (r *mockResolver) RelativeFileByPath(_ file.Location, path string) *file.Lo
return &l return &l
} }
func (r *mockResolver) AllLocations() <-chan file.Location { func (r *mockResolver) AllLocations(ctx context.Context) <-chan file.Location {
c := make(chan file.Location) c := make(chan file.Location)
go func() { go func() {
defer close(c) defer close(c)
locations, _ := r.getLocations() locations, _ := r.getLocations()
for _, location := range locations { for _, location := range locations {
c <- location select {
case <-ctx.Done():
return
case c <- location:
continue
}
} }
}() }()
return c return c

View File

@ -1,6 +1,8 @@
package fileresolver package fileresolver
import ( import (
"context"
"errors"
"fmt" "fmt"
"io" "io"
"io/fs" "io/fs"
@ -225,8 +227,9 @@ func (u UnindexedDirectory) RelativeFileByPath(l file.Location, p string) *file.
// - NO symlink resolution should be performed on results // - NO symlink resolution should be performed on results
// - returns locations for any file or directory // - returns locations for any file or directory
func (u UnindexedDirectory) AllLocations() <-chan file.Location { func (u UnindexedDirectory) AllLocations(ctx context.Context) <-chan file.Location {
out := make(chan file.Location) out := make(chan file.Location)
errWalkCanceled := fmt.Errorf("walk canceled")
go func() { go func() {
defer close(out) defer close(out)
err := afero.Walk(u.fs, u.absPath("."), func(p string, info fs.FileInfo, err error) error { err := afero.Walk(u.fs, u.absPath("."), func(p string, info fs.FileInfo, err error) error {
@ -235,10 +238,14 @@ func (u UnindexedDirectory) AllLocations() <-chan file.Location {
return nil return nil
} }
p = strings.TrimPrefix(p, "/") p = strings.TrimPrefix(p, "/")
out <- file.NewLocation(p) select {
return nil case out <- file.NewLocation(p):
return nil
case <-ctx.Done():
return errWalkCanceled
}
}) })
if err != nil { if err != nil && !errors.Is(err, errWalkCanceled) {
log.Debug(err) log.Debug(err)
} }
}() }()

View File

@ -4,6 +4,7 @@
package fileresolver package fileresolver
import ( import (
"context"
"io" "io"
"os" "os"
"path" "path"
@ -17,6 +18,7 @@ import (
"github.com/scylladb/go-set/strset" "github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/goleak"
stereoscopeFile "github.com/anchore/stereoscope/pkg/file" stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/file"
@ -538,6 +540,19 @@ func Test_UnindexedDirectoryResolver_Basic(t *testing.T) {
require.Len(t, locations, 5) require.Len(t, locations, 5)
} }
func Test_UnindexedDirectoryResolver_NoGoroutineLeak(t *testing.T) {
defer goleak.VerifyNone(t)
wd, err := os.Getwd()
require.NoError(t, err)
r := NewFromUnindexedDirectory(path.Join(wd, "test-fixtures"))
ctx, cancel := context.WithCancel(context.Background())
for range r.AllLocations(ctx) {
break
}
cancel()
}
func Test_UnindexedDirectoryResolver_FilesByPath_relativeRoot(t *testing.T) { func Test_UnindexedDirectoryResolver_FilesByPath_relativeRoot(t *testing.T) {
cases := []struct { cases := []struct {
name string name string
@ -1168,7 +1183,9 @@ func Test_UnindexedDirectoryResolver_resolvesLinks(t *testing.T) {
func Test_UnindexedDirectoryResolver_DoNotAddVirtualPathsToTree(t *testing.T) { func Test_UnindexedDirectoryResolver_DoNotAddVirtualPathsToTree(t *testing.T) {
resolver := NewFromUnindexedDirectory("./test-fixtures/symlinks-prune-indexing") resolver := NewFromUnindexedDirectory("./test-fixtures/symlinks-prune-indexing")
allLocations := resolver.AllLocations() ctx, cancel := context.WithCancel(context.Background())
defer cancel()
allLocations := resolver.AllLocations(ctx)
var allRealPaths []stereoscopeFile.Path var allRealPaths []stereoscopeFile.Path
for l := range allLocations { for l := range allLocations {
allRealPaths = append(allRealPaths, stereoscopeFile.Path(l.RealPath)) allRealPaths = append(allRealPaths, stereoscopeFile.Path(l.RealPath))
@ -1200,7 +1217,9 @@ func Test_UnindexedDirectoryResolver_AllLocations(t *testing.T) {
resolver := NewFromUnindexedDirectory("./test-fixtures/symlinks-from-image-symlinks-fixture") resolver := NewFromUnindexedDirectory("./test-fixtures/symlinks-from-image-symlinks-fixture")
paths := strset.New() paths := strset.New()
for loc := range resolver.AllLocations() { ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for loc := range resolver.AllLocations(ctx) {
if strings.HasPrefix(loc.RealPath, "/") { if strings.HasPrefix(loc.RealPath, "/") {
// ignore outside of the fixture root for now // ignore outside of the fixture root for now
continue continue

View File

@ -1,6 +1,7 @@
package binary package binary
import ( import (
"context"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
@ -1237,7 +1238,7 @@ func (p *panicyResolver) RelativeFileByPath(_ file.Location, _ string) *file.Loc
return nil return nil
} }
func (p *panicyResolver) AllLocations() <-chan file.Location { func (p *panicyResolver) AllLocations(_ context.Context) <-chan file.Location {
return nil return nil
} }

View File

@ -1,6 +1,7 @@
package pkgtest package pkgtest
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"sort" "sort"
@ -209,8 +210,8 @@ func (r *ObservingResolver) FileContentsByLocation(location file.Location) (io.R
// For the remaining resolver methods... // For the remaining resolver methods...
func (r *ObservingResolver) AllLocations() <-chan file.Location { func (r *ObservingResolver) AllLocations(ctx context.Context) <-chan file.Location {
return r.decorated.AllLocations() return r.decorated.AllLocations(ctx)
} }
func (r *ObservingResolver) HasPath(s string) bool { func (r *ObservingResolver) HasPath(s string) bool {

View File

@ -4,6 +4,7 @@ Package nix provides a concrete Cataloger implementation for packages within the
package nix package nix
import ( import (
"context"
"fmt" "fmt"
"github.com/bmatcuk/doublestar/v4" "github.com/bmatcuk/doublestar/v4"
@ -31,7 +32,9 @@ func (c *StoreCataloger) Catalog(resolver file.Resolver) ([]pkg.Package, []artif
// we want to search for only directories, which isn't possible via the stereoscope API, so we need to apply the glob manually on all returned paths // we want to search for only directories, which isn't possible via the stereoscope API, so we need to apply the glob manually on all returned paths
var pkgs []pkg.Package var pkgs []pkg.Package
var filesByPath = make(map[string]*file.LocationSet) var filesByPath = make(map[string]*file.LocationSet)
for location := range resolver.AllLocations() { ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for location := range resolver.AllLocations(ctx) {
matchesStorePath, err := doublestar.Match("**/nix/store/*", location.RealPath) matchesStorePath, err := doublestar.Match("**/nix/store/*", location.RealPath)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to match nix store path: %w", err) return nil, nil, fmt.Errorf("failed to match nix store path: %w", err)

View File

@ -1,6 +1,7 @@
package redhat package redhat
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"testing" "testing"
@ -34,7 +35,7 @@ func (r rpmdbTestFileResolverMock) FileContentsByLocation(location file.Location
panic("not implemented") panic("not implemented")
} }
func (r rpmdbTestFileResolverMock) AllLocations() <-chan file.Location { func (r rpmdbTestFileResolverMock) AllLocations(_ context.Context) <-chan file.Location {
panic("not implemented") panic("not implemented")
} }