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

packages lib support test #7

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
Binary file added cmd/__debug_bin
Binary file not shown.
13 changes: 7 additions & 6 deletions cmd/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"strings"

"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/formats/cyclonedxjson"
"github.com/anchore/syft/internal/formats/spdx22json"
"github.com/anchore/syft/internal/formats/syftjson"
Expand Down Expand Up @@ -84,7 +85,7 @@ var (
defer profile.Start(profile.MemProfile).Stop()
}

return attestExec(cmd.Context(), cmd, args)
return attestExec(cmd.Context(), cmd, args, appConfig)
},
}
)
Expand Down Expand Up @@ -128,7 +129,7 @@ func selectPassFunc(keypath string) (cosign.PassFunc, error) {
return fn, nil
}

func attestExec(ctx context.Context, _ *cobra.Command, args []string) error {
func attestExec(ctx context.Context, _ *cobra.Command, args []string, cfg *config.Application) error {
// can only be an image for attestation or OCI DIR
userInput := args[0]
si, err := source.ParseInput(userInput, appConfig.Platform, false)
Expand Down Expand Up @@ -180,15 +181,15 @@ func attestExec(ctx context.Context, _ *cobra.Command, args []string) error {
defer sv.Close()

return eventLoop(
attestationExecWorker(*si, format, predicateType, sv),
attestationExecWorker(*si, cfg, format, predicateType, sv),
setupSignals(),
eventSubscription,
stereoscope.Cleanup,
ui.Select(isVerbose(), appConfig.Quiet)...,
ui.Select(isVerbose(appConfig), appConfig.Quiet)...,
)
}

func attestationExecWorker(sourceInput source.Input, format sbom.Format, predicateType string, sv *sign.SignerVerifier) <-chan error {
func attestationExecWorker(sourceInput source.Input, cfg *config.Application, format sbom.Format, predicateType string, sv *sign.SignerVerifier) <-chan error {
errs := make(chan error)
go func() {
defer close(errs)
Expand All @@ -202,7 +203,7 @@ func attestationExecWorker(sourceInput source.Input, format sbom.Format, predica
return
}

s, err := generateSBOM(src, errs)
s, err := generateSBOM(src, cfg, errs)
if err != nil {
errs <- err
return
Expand Down
15 changes: 10 additions & 5 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/anchore/syft/internal/logger"
"github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft"

"github.com/gookit/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -113,12 +114,16 @@ func initAppConfig() {
}

func initLogging() {
initLoggingConfig(appConfig)
}

func initLoggingConfig(appCfg *config.Application) {
cfg := logger.LogrusConfig{
EnableConsole: (appConfig.Log.FileLocation == "" || appConfig.CliOptions.Verbosity > 0) && !appConfig.Quiet,
EnableFile: appConfig.Log.FileLocation != "",
Level: appConfig.Log.LevelOpt,
Structured: appConfig.Log.Structured,
FileLocation: appConfig.Log.FileLocation,
EnableConsole: (appCfg.Log.FileLocation == "" || appCfg.CliOptions.Verbosity > 0) && !appCfg.Quiet,
EnableFile: appCfg.Log.FileLocation != "",
Level: appCfg.Log.LevelOpt,
Structured: appCfg.Log.Structured,
FileLocation: appCfg.Log.FileLocation,
}

logWrapper := logger.NewLogrusLogger(cfg)
Expand Down
104 changes: 104 additions & 0 deletions cmd/lib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package cmd

import (
"fmt"
"os"

"github.com/anchore/stereoscope"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/logger"
syftLogger "github.com/anchore/syft/syft/logger"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
"github.com/wagoodman/go-partybus"
)

func libInitBase(cfg *config.Application, l logger.Logger, enable_ui bool) ([]ui.UI, error) {
if err := cfg.LibParseConfigValues(); err != nil {
return nil, fmt.Errorf("invalid application config: %w", err)
}

var uis []ui.UI
uis = append(uis, ui.NewlibUI())
if l == nil {
initLoggingConfig(cfg)
} else {
libInitLoggingConfig(l)
}

if enable_ui {
uis = ui.Select(isVerbose(cfg), cfg.Quiet)
}

return uis, nil
}

func libInitLoggingConfig(logWrapper syftLogger.Logger) {
syft.SetLogger(logWrapper)
stereoscope.SetLogger(logWrapper)
}

func libInitEventBus() {
if eventSubscription == nil {
initEventBus()
}
}

// LibPackagesExec run packages command as a library
// userInput: target
// cfg: syft configuration structure
// l: logger to attach to, nil for default syft logger
// enable_ui: enable disable ui output
// Function return sbom or errors.
func LibPackagesExec(userInput string, cfg *config.Application, l logger.Logger, enable_ui bool) (*sbom.SBOM, error) {
writer, err := makeWriter(cfg.Outputs, cfg.File)
if err != nil {
return nil, err
}

uis, err := libInitBase(cfg, l, enable_ui)
if err != nil {
return nil, err
}

defer func() {
if err := writer.Close(); err != nil {
log.Warnf("unable to write to report destination: %w", err)
}
}()

// could be an image or a directory, with or without a scheme
si, err := source.ParseInput(userInput, cfg.Platform, true)
if err != nil {
return nil, fmt.Errorf("could not generate source input for packages command: %w", err)
}

libInitEventBus()
outSbom, errs := packagesExecWorker(*si, cfg, writer)
return sbomEventLoop(
outSbom, errs,
setupSignals(),
eventSubscription,
stereoscope.Cleanup,
uis...,
)
}

func sbomEventLoop(outSbom <-chan *sbom.SBOM, workerErrs <-chan error, signals <-chan os.Signal, subscription *partybus.Subscription, cleanupFn func(), uxs ...ui.UI) (*sbom.SBOM, error) {
err := eventLoop(workerErrs,
signals,
subscription,
cleanupFn,
uxs...)

var out *sbom.SBOM
if err == nil {
out = <-outSbom
}

return out, err

}
72 changes: 41 additions & 31 deletions cmd/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/anchore"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/formats/table"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
Expand Down Expand Up @@ -82,7 +83,7 @@ var (
defer profile.Start(profile.MemProfile).Stop()
}

return packagesExec(cmd, args)
return packagesExec(cmd, args, appConfig)
},
ValidArgsFunction: dockerImageValidArgsFunction,
}
Expand Down Expand Up @@ -221,8 +222,8 @@ func validateInputArgs(cmd *cobra.Command, args []string) error {
return cobra.MaximumNArgs(1)(cmd, args)
}

func packagesExec(_ *cobra.Command, args []string) error {
writer, err := makeWriter(appConfig.Outputs, appConfig.File)
func packagesExec(_ *cobra.Command, args []string, cfg *config.Application) error {
writer, err := makeWriter(cfg.Outputs, cfg.File)
if err != nil {
return err
}
Expand All @@ -235,33 +236,35 @@ func packagesExec(_ *cobra.Command, args []string) error {

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

_, errs := packagesExecWorker(*si, cfg, writer)

return eventLoop(
packagesExecWorker(*si, writer),
errs,
setupSignals(),
eventSubscription,
stereoscope.Cleanup,
ui.Select(isVerbose(), appConfig.Quiet)...,
ui.Select(isVerbose(cfg), cfg.Quiet)...,
)
}

func isVerbose() (result bool) {
func isVerbose(cfg *config.Application) (result bool) {
isPipedInput, err := internal.IsPipedInput()
if err != nil {
// since we can't tell if there was piped input we assume that there could be to disable the ETUI
log.Warnf("unable to determine if there is piped input: %+v", err)
return true
}
// verbosity should consider if there is piped input (in which case we should not show the ETUI)
return appConfig.CliOptions.Verbosity > 0 || isPipedInput
return cfg.CliOptions.Verbosity > 0 || isPipedInput
}

func generateSBOM(src *source.Source, errs chan error) (*sbom.SBOM, error) {
tasks, err := tasks()
func generateSBOM(src *source.Source, cfg *config.Application, errs chan error) (*sbom.SBOM, error) {
tasks, err := tasks(cfg)
if err != nil {
return nil, err
}
Expand All @@ -271,7 +274,7 @@ func generateSBOM(src *source.Source, errs chan error) (*sbom.SBOM, error) {
Descriptor: sbom.Descriptor{
Name: internal.ApplicationName,
Version: version.FromBuild().Version,
Configuration: appConfig,
Configuration: cfg,
},
}

Expand All @@ -291,12 +294,14 @@ func buildRelationships(s *sbom.SBOM, src *source.Source, tasks []task, errs cha
s.Relationships = append(s.Relationships, mergeRelationships(relationships...)...)
}

func packagesExecWorker(si source.Input, writer sbom.Writer) <-chan error {
func packagesExecWorker(si source.Input, cfg *config.Application, writer sbom.Writer) (chan *sbom.SBOM, <-chan error) {
errs := make(chan error)
outSbom := make(chan *sbom.SBOM, 1)

go func() {
defer close(errs)

src, cleanup, err := source.New(si, appConfig.Registry.ToOptions(), appConfig.Exclusions)
src, cleanup, err := source.New(si, cfg.Registry.ToOptions(), cfg.Exclusions)
if cleanup != nil {
defer cleanup()
}
Expand All @@ -305,7 +310,7 @@ func packagesExecWorker(si source.Input, writer sbom.Writer) <-chan error {
return
}

s, err := generateSBOM(src, errs)
s, err := generateSBOM(src, cfg, errs)
if err != nil {
errs <- err
return
Expand All @@ -315,8 +320,8 @@ func packagesExecWorker(si source.Input, writer sbom.Writer) <-chan error {
errs <- fmt.Errorf("no SBOM produced for %q", si.UserInput)
}

if appConfig.Anchore.Host != "" {
if err := runPackageSbomUpload(src, *s); err != nil {
if cfg.Anchore.Host != "" {
if err := runPackageSbomUpload(src, cfg, *s); err != nil {
errs <- err
return
}
Expand All @@ -326,8 +331,13 @@ func packagesExecWorker(si source.Input, writer sbom.Writer) <-chan error {
Type: event.Exit,
Value: func() error { return writer.Write(*s) },
})

if s != nil {
outSbom <- s
}
}()
return errs

return outSbom, errs
}

func mergeRelationships(cs ...<-chan artifact.Relationship) (relationships []artifact.Relationship) {
Expand All @@ -340,34 +350,34 @@ func mergeRelationships(cs ...<-chan artifact.Relationship) (relationships []art
return relationships
}

func runPackageSbomUpload(src *source.Source, s sbom.SBOM) error {
log.Infof("uploading results to %s", appConfig.Anchore.Host)
func runPackageSbomUpload(src *source.Source, cfg *config.Application, s sbom.SBOM) error {
log.Infof("uploading results to %s", cfg.Anchore.Host)

if src.Metadata.Scheme != source.ImageScheme {
return fmt.Errorf("unable to upload results: only images are supported")
}

var dockerfileContents []byte
if appConfig.Anchore.Dockerfile != "" {
if _, err := os.Stat(appConfig.Anchore.Dockerfile); os.IsNotExist(err) {
return fmt.Errorf("unable dockerfile=%q does not exist: %w", appConfig.Anchore.Dockerfile, err)
if cfg.Anchore.Dockerfile != "" {
if _, err := os.Stat(cfg.Anchore.Dockerfile); os.IsNotExist(err) {
return fmt.Errorf("unable dockerfile=%q does not exist: %w", cfg.Anchore.Dockerfile, err)
}

fh, err := os.Open(appConfig.Anchore.Dockerfile)
fh, err := os.Open(cfg.Anchore.Dockerfile)
if err != nil {
return fmt.Errorf("unable to open dockerfile=%q: %w", appConfig.Anchore.Dockerfile, err)
return fmt.Errorf("unable to open dockerfile=%q: %w", cfg.Anchore.Dockerfile, err)
}

dockerfileContents, err = ioutil.ReadAll(fh)
if err != nil {
return fmt.Errorf("unable to read dockerfile=%q: %w", appConfig.Anchore.Dockerfile, err)
return fmt.Errorf("unable to read dockerfile=%q: %w", cfg.Anchore.Dockerfile, err)
}
}

c, err := anchore.NewClient(anchore.Configuration{
BaseURL: appConfig.Anchore.Host,
Username: appConfig.Anchore.Username,
Password: appConfig.Anchore.Password,
BaseURL: cfg.Anchore.Host,
Username: cfg.Anchore.Username,
Password: cfg.Anchore.Password,
})
if err != nil {
return fmt.Errorf("failed to create anchore client: %w", err)
Expand All @@ -377,12 +387,12 @@ func runPackageSbomUpload(src *source.Source, s sbom.SBOM) error {
ImageMetadata: src.Image.Metadata,
SBOM: s,
Dockerfile: dockerfileContents,
OverwriteExistingUpload: appConfig.Anchore.OverwriteExistingImage,
Timeout: appConfig.Anchore.ImportTimeout,
OverwriteExistingUpload: cfg.Anchore.OverwriteExistingImage,
Timeout: cfg.Anchore.ImportTimeout,
}

if err := c.Import(context.Background(), importCfg); err != nil {
return fmt.Errorf("failed to upload results to host=%s: %+v", appConfig.Anchore.Host, err)
return fmt.Errorf("failed to upload results to host=%s: %+v", cfg.Anchore.Host, err)
}

return nil
Expand Down
Loading