Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor source API #1846

Merged
merged 7 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.bin
CHANGELOG.md
VERSION
/test/results
Expand Down
48 changes: 28 additions & 20 deletions cmd/syft/cli/attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,39 +44,47 @@ func Run(_ context.Context, app *config.Application, args []string) error {
}
}()

// could be an image or a directory, with or without a scheme
// TODO: validate that source is image
// note: must be a container image
userInput := args[0]
si, err := source.ParseInputWithNameVersion(userInput, app.Platform, app.SourceName, app.SourceVersion, app.DefaultImagePullSource)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}

if si.Scheme != source.ImageScheme {
return fmt.Errorf("attestations are only supported for oci images at this time")
}

eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()

return eventloop.EventLoop(
execWorker(app, *si, writer),
execWorker(app, userInput, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
}

func buildSBOM(app *config.Application, si source.Input, writer sbom.Writer, errs chan error) ([]byte, error) {
src, cleanup, err := source.New(si, app.Registry.ToOptions(), app.Exclusions)
if cleanup != nil {
defer cleanup()
func buildSBOM(app *config.Application, userInput string, writer sbom.Writer, errs chan error) ([]byte, error) {
detection, err := source.Detect(userInput, app.DefaultImagePullSource)
if err != nil {
return nil, fmt.Errorf("could not deteremine source: %w", err)
}

if detection.Type != source.ContainerImageType {
return nil, fmt.Errorf("attestations are only supported for oci images at this time")
}

src, err := detection.NewSource(
&source.Alias{
Name: app.SourceName,
Version: app.SourceVersion,
},
app.Registry.ToOptions(),
app.Platform,
app.Exclusions,
)
if src != nil {
defer src.Close()
}
if err != nil {
return nil, fmt.Errorf("failed to construct source from user input %q: %w", si.UserInput, err)
return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
}

s, err := packages.GenerateSBOM(src, errs, app)
Expand All @@ -85,7 +93,7 @@ func buildSBOM(app *config.Application, si source.Input, writer sbom.Writer, err
}

if s == nil {
return nil, fmt.Errorf("no SBOM produced for %q", si.UserInput)
return nil, fmt.Errorf("no SBOM produced for %q", userInput)
}

// note: only works for single format no multi writer support
Expand All @@ -98,11 +106,11 @@ func buildSBOM(app *config.Application, si source.Input, writer sbom.Writer, err
}

//nolint:funlen
func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
sBytes, err := buildSBOM(app, si, writer, errs)
sBytes, err := buildSBOM(app, userInput, writer, errs)
if err != nil {
errs <- fmt.Errorf("unable to build SBOM: %w", err)
return
Expand Down Expand Up @@ -145,7 +153,7 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
predicateType = "custom"
}

args := []string{"attest", si.UserInput, "--predicate", f.Name(), "--type", predicateType}
args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType}
if app.Attest.Key != "" {
args = append(args, "--key", app.Attest.Key)
}
Expand Down
14 changes: 7 additions & 7 deletions cmd/syft/cli/eventloop/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
"github.com/anchore/syft/syft/source"
)

type Task func(*sbom.Artifacts, *source.Source) ([]artifact.Relationship, error)
type Task func(*sbom.Artifacts, source.Source) ([]artifact.Relationship, error)

func Tasks(app *config.Application) ([]Task, error) {
var tasks []Task
Expand Down Expand Up @@ -48,7 +48,7 @@ func generateCatalogPackagesTask(app *config.Application) (Task, error) {
return nil, nil
}

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
packageCatalog, relationships, theDistro, err := syft.CatalogPackages(src, app.ToCatalogerConfig())

results.Packages = packageCatalog
Expand All @@ -67,7 +67,7 @@ func generateCatalogFileMetadataTask(app *config.Application) (Task, error) {

metadataCataloger := filemetadata.NewCataloger()

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt)
if err != nil {
return nil, err
Expand Down Expand Up @@ -110,7 +110,7 @@ func generateCatalogFileDigestsTask(app *config.Application) (Task, error) {

digestsCataloger := filedigest.NewCataloger(hashes)

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt)
if err != nil {
return nil, err
Expand Down Expand Up @@ -142,7 +142,7 @@ func generateCatalogSecretsTask(app *config.Application) (Task, error) {
return nil, err
}

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.Secrets.Cataloger.ScopeOpt)
if err != nil {
return nil, err
Expand All @@ -169,7 +169,7 @@ func generateCatalogContentsTask(app *config.Application) (Task, error) {
return nil, err
}

task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
resolver, err := src.FileResolver(app.FileContents.Cataloger.ScopeOpt)
if err != nil {
return nil, err
Expand All @@ -186,7 +186,7 @@ func generateCatalogContentsTask(app *config.Application) (Task, error) {
return task, nil
}

func RunTask(t Task, a *sbom.Artifacts, src *source.Source, c chan<- artifact.Relationship, errs chan<- error) {
func RunTask(t Task, a *sbom.Artifacts, src source.Source, c chan<- artifact.Relationship, errs chan<- error) {
defer close(c)

relationships, err := t(a, src)
Expand Down
38 changes: 24 additions & 14 deletions cmd/syft/cli/packages/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,36 +42,46 @@ func Run(_ context.Context, app *config.Application, args []string) error {

// could be an image or a directory, with or without a scheme
userInput := args[0]
si, err := source.ParseInputWithNameVersion(userInput, app.Platform, app.SourceName, app.SourceVersion, app.DefaultImagePullSource)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}

eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()

return eventloop.EventLoop(
execWorker(app, *si, writer),
execWorker(app, userInput, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
}

func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)

src, cleanup, err := source.New(si, app.Registry.ToOptions(), app.Exclusions)
if cleanup != nil {
defer cleanup()
detection, err := source.Detect(userInput, app.DefaultImagePullSource)
if err != nil {
errs <- fmt.Errorf("could not deteremine source: %w", err)
return
}

src, err := detection.NewSource(
&source.Alias{
Name: app.SourceName,
Version: app.SourceVersion,
},
app.Registry.ToOptions(),
app.Platform,
app.Exclusions,
)
if src != nil {
defer src.Close()
}
if err != nil {
errs <- fmt.Errorf("failed to construct source from user input %q: %w", si.UserInput, err)
errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
return
}

Expand All @@ -82,7 +92,7 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
}

if s == nil {
errs <- fmt.Errorf("no SBOM produced for %q", si.UserInput)
errs <- fmt.Errorf("no SBOM produced for %q", userInput)
}

bus.Publish(partybus.Event{
Expand All @@ -93,14 +103,14 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
return errs
}

func GenerateSBOM(src *source.Source, errs chan error, app *config.Application) (*sbom.SBOM, error) {
func GenerateSBOM(src source.Source, errs chan error, app *config.Application) (*sbom.SBOM, error) {
tasks, err := eventloop.Tasks(app)
if err != nil {
return nil, err
}

s := sbom.SBOM{
Source: src.Metadata,
Source: src.Describe(),
Descriptor: sbom.Descriptor{
Name: internal.ApplicationName,
Version: version.FromBuild().Version,
Expand All @@ -113,7 +123,7 @@ func GenerateSBOM(src *source.Source, errs chan error, app *config.Application)
return &s, nil
}

func buildRelationships(s *sbom.SBOM, src *source.Source, tasks []eventloop.Task, errs chan error) {
func buildRelationships(s *sbom.SBOM, src source.Source, tasks []eventloop.Task, errs chan error) {
var relationships []<-chan artifact.Relationship
for _, task := range tasks {
c := make(chan artifact.Relationship)
Expand Down
32 changes: 21 additions & 11 deletions cmd/syft/cli/poweruser/poweruser.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,22 @@ func Run(_ context.Context, app *config.Application, args []string) error {
}()

userInput := args[0]
si, err := source.ParseInputWithNameVersion(userInput, app.Platform, app.SourceName, app.SourceVersion, app.DefaultImagePullSource)
if err != nil {
return fmt.Errorf("could not generate source input for packages command: %w", err)
}

eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()

return eventloop.EventLoop(
execWorker(app, *si, writer),
execWorker(app, userInput, writer),
eventloop.SetupSignals(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
}

func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
Expand All @@ -81,17 +77,31 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
return
}

src, cleanup, err := source.New(si, app.Registry.ToOptions(), app.Exclusions)
detection, err := source.Detect(userInput, app.DefaultImagePullSource)
if err != nil {
errs <- err
errs <- fmt.Errorf("could not deteremine source: %w", err)
return
}
if cleanup != nil {
defer cleanup()

src, err := detection.NewSource(
&source.Alias{
Name: app.SourceName,
Version: app.SourceVersion,
},
app.Registry.ToOptions(),
app.Platform,
app.Exclusions,
)
if src != nil {
defer src.Close()
}
if err != nil {
errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
return
}

s := sbom.SBOM{
Source: src.Metadata,
Source: src.Describe(),
Descriptor: sbom.Descriptor{
Name: internal.ApplicationName,
Version: version.FromBuild().Version,
Expand Down
2 changes: 1 addition & 1 deletion internal/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ const (

// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "8.0.1"
JSONSchemaVersion = "9.0.0"
)
Loading