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
22 changed files with 156 additions and 42 deletions
+10 -2
View File
@@ -1,6 +1,7 @@
package filemetadata
import (
"context"
"fmt"
"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) {
results := make(map[file.Coordinates]file.Metadata)
var locations <-chan file.Location
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
if len(coordinates) == 0 {
locations = resolver.AllLocations()
locations = resolver.AllLocations(ctx)
} else {
locations = func() <-chan file.Location {
ch := make(chan file.Location)
@@ -35,7 +38,12 @@ func (i *Cataloger) Catalog(resolver file.Resolver, coordinates ...file.Coordina
continue
}
for _, loc := range locs {
ch <- loc
select {
case <-ctx.Done():
return
case ch <- loc:
continue
}
}
}
}()
@@ -1,13 +1,17 @@
package internal
import (
"context"
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
)
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)
if err != nil {
log.Warnf("unable to resolve %+v: %+v", location, err)
+8 -2
View File
@@ -1,6 +1,7 @@
package file
import (
"context"
"fmt"
"io"
"os"
@@ -144,12 +145,17 @@ func (r MockResolver) RelativeFileByPath(_ Location, path string) *Location {
return &paths[0]
}
func (r MockResolver) AllLocations() <-chan Location {
func (r MockResolver) AllLocations(ctx context.Context) <-chan Location {
results := make(chan Location)
go func() {
defer close(results)
for _, l := range r.locations {
results <- l
select {
case <-ctx.Done():
return
case results <- l:
continue
}
}
}()
return results
+5 -2
View File
@@ -1,6 +1,9 @@
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.
type Resolver interface {
@@ -53,7 +56,7 @@ type LocationResolver interface {
// The implementation for this may vary, however, generally the following considerations should be made:
// - NO symlink resolution should be performed on results
// - returns locations for any file or directory
AllLocations() <-chan Location
AllLocations(ctx context.Context) <-chan Location
}
type WritableResolver interface {