mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
102 lines
2.3 KiB
Go
102 lines
2.3 KiB
Go
package syft
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/anchore/syft/syft/source"
|
|
)
|
|
|
|
// GetSource uses all of Syft's known source providers to attempt to resolve the user input to a usable source.Source
|
|
func GetSource(ctx context.Context, userInput string, cfg *GetSourceConfig) (source.Source, error) {
|
|
if cfg == nil {
|
|
cfg = DefaultGetSourceConfig()
|
|
}
|
|
|
|
providers, err := cfg.getProviders(userInput)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var errs []error
|
|
var fileNotfound error
|
|
|
|
// call each source provider until we find a valid source
|
|
for _, p := range providers {
|
|
src, err := p.Provide(ctx)
|
|
if err != nil {
|
|
err = eachError(err, func(err error) error {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
fileNotfound = err
|
|
return nil
|
|
}
|
|
return err
|
|
})
|
|
if err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
if src != nil {
|
|
// if we have a non-image type and platform is specified, it's an error
|
|
if cfg.SourceProviderConfig.Platform != nil {
|
|
meta := src.Describe().Metadata
|
|
switch meta.(type) {
|
|
case *source.ImageMetadata, source.ImageMetadata:
|
|
default:
|
|
return src, fmt.Errorf("platform specified with non-image source")
|
|
}
|
|
}
|
|
return src, nil
|
|
}
|
|
}
|
|
|
|
if fileNotfound != nil {
|
|
errs = append([]error{fileNotfound}, errs...)
|
|
}
|
|
return nil, sourceError(userInput, errs...)
|
|
}
|
|
|
|
func sourceError(userInput string, errs ...error) error {
|
|
switch len(errs) {
|
|
case 0:
|
|
return nil
|
|
case 1:
|
|
return fmt.Errorf("an error occurred attempting to resolve '%s': %w", userInput, errs[0])
|
|
}
|
|
errorTexts := ""
|
|
for _, e := range errs {
|
|
errorTexts += fmt.Sprintf("\n - %s", e)
|
|
}
|
|
return fmt.Errorf("errors occurred attempting to resolve '%s':%s", userInput, errorTexts)
|
|
}
|
|
|
|
func eachError(err error, fn func(error) error) error {
|
|
out := fn(err)
|
|
// unwrap singly wrapped errors
|
|
if e, ok := err.(interface {
|
|
Unwrap() error
|
|
}); ok {
|
|
wrapped := e.Unwrap()
|
|
got := eachError(wrapped, fn)
|
|
// return the outer error if received the same wrapped error
|
|
if errors.Is(got, wrapped) {
|
|
return err
|
|
}
|
|
return got
|
|
}
|
|
// unwrap errors from errors.Join
|
|
if errs, ok := err.(interface {
|
|
Unwrap() []error
|
|
}); ok {
|
|
for _, e := range errs.Unwrap() {
|
|
e = eachError(e, fn)
|
|
if e != nil {
|
|
out = errors.Join(out, e)
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|