mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
use common entry point for integration tests; refactor cmd pkg (#86)
This commit is contained in:
parent
b5a353349f
commit
9e285fd0e2
2
Makefile
2
Makefile
@ -75,7 +75,7 @@ unit: ## Run unit tests (with coverage)
|
|||||||
|
|
||||||
integration: ## Run integration tests
|
integration: ## Run integration tests
|
||||||
$(call title,Running integration tests)
|
$(call title,Running integration tests)
|
||||||
go test -tags=integration ./integration
|
go test -v -tags=integration ./integration
|
||||||
|
|
||||||
integration/test-fixtures/tar-cache.key, integration-fingerprint:
|
integration/test-fixtures/tar-cache.key, integration-fingerprint:
|
||||||
find integration/test-fixtures/image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | tee integration/test-fixtures/tar-cache.fingerprint
|
find integration/test-fixtures/image-* -type f -exec md5sum {} + | awk '{print $1}' | sort | md5sum | tee integration/test-fixtures/tar-cache.fingerprint
|
||||||
|
|||||||
50
cmd/cli.go
50
cmd/cli.go
@ -1,50 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/anchore/imgbom/imgbom/presenter"
|
|
||||||
"github.com/anchore/imgbom/imgbom/scope"
|
|
||||||
"github.com/anchore/imgbom/internal/config"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cliOpts = config.CliOnlyOptions{}
|
|
||||||
|
|
||||||
func setCliOptions() {
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&cliOpts.ConfigPath, "config", "c", "", "application config file")
|
|
||||||
|
|
||||||
// scan options
|
|
||||||
flag := "scope"
|
|
||||||
rootCmd.Flags().StringP(
|
|
||||||
"scope", "s", scope.AllLayersScope.String(),
|
|
||||||
fmt.Sprintf("selection of layers to catalog, options=%v", scope.Options))
|
|
||||||
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
|
||||||
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// output & formatting options
|
|
||||||
flag = "output"
|
|
||||||
rootCmd.Flags().StringP(
|
|
||||||
flag, "o", presenter.TextPresenter.String(),
|
|
||||||
fmt.Sprintf("report output formatter, options=%v", presenter.Options),
|
|
||||||
)
|
|
||||||
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
|
||||||
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
flag = "quiet"
|
|
||||||
rootCmd.Flags().BoolP(
|
|
||||||
flag, "q", false,
|
|
||||||
"suppress all logging output",
|
|
||||||
)
|
|
||||||
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
|
||||||
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootCmd.Flags().CountVarP(&cliOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)")
|
|
||||||
}
|
|
||||||
108
cmd/cmd.go
108
cmd/cmd.go
@ -1,14 +1,122 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/anchore/imgbom/imgbom/presenter"
|
||||||
|
"github.com/anchore/imgbom/imgbom/scope"
|
||||||
|
|
||||||
|
"github.com/anchore/imgbom/imgbom"
|
||||||
|
"github.com/anchore/imgbom/internal/config"
|
||||||
|
"github.com/anchore/imgbom/internal/format"
|
||||||
"github.com/anchore/imgbom/internal/log"
|
"github.com/anchore/imgbom/internal/log"
|
||||||
|
"github.com/anchore/imgbom/internal/logger"
|
||||||
|
"github.com/anchore/stereoscope"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"github.com/wagoodman/go-partybus"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var appConfig *config.Application
|
||||||
|
var eventBus *partybus.Bus
|
||||||
|
var eventSubscription *partybus.Subscription
|
||||||
|
var cliOpts = config.CliOnlyOptions{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
setGlobalCliOptions()
|
||||||
|
|
||||||
|
cobra.OnInitialize(
|
||||||
|
initAppConfig,
|
||||||
|
initLogging,
|
||||||
|
logAppConfig,
|
||||||
|
initEventBus,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
log.Errorf("could not start application: %w", err)
|
log.Errorf("could not start application: %w", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setGlobalCliOptions() {
|
||||||
|
rootCmd.PersistentFlags().StringVarP(&cliOpts.ConfigPath, "config", "c", "", "application config file")
|
||||||
|
|
||||||
|
// scan options
|
||||||
|
flag := "scope"
|
||||||
|
rootCmd.Flags().StringP(
|
||||||
|
"scope", "s", scope.AllLayersScope.String(),
|
||||||
|
fmt.Sprintf("selection of layers to catalog, options=%v", scope.Options))
|
||||||
|
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
||||||
|
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// output & formatting options
|
||||||
|
flag = "output"
|
||||||
|
rootCmd.Flags().StringP(
|
||||||
|
flag, "o", presenter.TextPresenter.String(),
|
||||||
|
fmt.Sprintf("report output formatter, options=%v", presenter.Options),
|
||||||
|
)
|
||||||
|
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
||||||
|
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
flag = "quiet"
|
||||||
|
rootCmd.Flags().BoolP(
|
||||||
|
flag, "q", false,
|
||||||
|
"suppress all logging output",
|
||||||
|
)
|
||||||
|
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
|
||||||
|
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCmd.Flags().CountVarP(&cliOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func initAppConfig() {
|
||||||
|
cfg, err := config.LoadConfigFromFile(viper.GetViper(), &cliOpts)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to load application config: \n\t%+v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
appConfig = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func initLogging() {
|
||||||
|
config := logger.LogConfig{
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
logWrapper := logger.NewZapLogger(config)
|
||||||
|
imgbom.SetLogger(logWrapper)
|
||||||
|
stereoscope.SetLogger(logWrapper)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logAppConfig() {
|
||||||
|
appCfgStr, err := yaml.Marshal(&appConfig)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Could not display application config: %+v", err)
|
||||||
|
} else {
|
||||||
|
log.Debugf("Application config:\n%+v", format.Magenta.Format(string(appCfgStr)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initEventBus() {
|
||||||
|
eventBus = partybus.NewBus()
|
||||||
|
eventSubscription = eventBus.Subscribe()
|
||||||
|
|
||||||
|
stereoscope.SetBus(eventBus)
|
||||||
|
imgbom.SetBus(eventBus)
|
||||||
|
}
|
||||||
|
|||||||
61
cmd/init.go
61
cmd/init.go
@ -1,61 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/anchore/imgbom/imgbom"
|
|
||||||
"github.com/anchore/imgbom/internal/config"
|
|
||||||
"github.com/anchore/imgbom/internal/format"
|
|
||||||
"github.com/anchore/imgbom/internal/log"
|
|
||||||
"github.com/anchore/imgbom/internal/logger"
|
|
||||||
"github.com/anchore/stereoscope"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/wagoodman/go-partybus"
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var appConfig *config.Application
|
|
||||||
var eventBus *partybus.Bus
|
|
||||||
var eventSubscription *partybus.Subscription
|
|
||||||
|
|
||||||
func initAppConfig() {
|
|
||||||
cfg, err := config.LoadConfigFromFile(viper.GetViper(), &cliOpts)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("failed to load application config: \n\t%+v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
appConfig = cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func initLogging() {
|
|
||||||
config := logger.LogConfig{
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
logWrapper := logger.NewZapLogger(config)
|
|
||||||
imgbom.SetLogger(logWrapper)
|
|
||||||
stereoscope.SetLogger(logWrapper)
|
|
||||||
}
|
|
||||||
|
|
||||||
func logAppConfig() {
|
|
||||||
appCfgStr, err := yaml.Marshal(&appConfig)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("Could not display application config: %+v", err)
|
|
||||||
} else {
|
|
||||||
log.Debugf("Application config:\n%+v", format.Magenta.Format(string(appCfgStr)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initEventBus() {
|
|
||||||
eventBus = partybus.NewBus()
|
|
||||||
eventSubscription = eventBus.Subscribe()
|
|
||||||
|
|
||||||
stereoscope.SetBus(eventBus)
|
|
||||||
imgbom.SetBus(eventBus)
|
|
||||||
}
|
|
||||||
35
cmd/root.go
35
cmd/root.go
@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/anchore/imgbom/imgbom/presenter"
|
"github.com/anchore/imgbom/imgbom/presenter"
|
||||||
"github.com/anchore/imgbom/internal"
|
"github.com/anchore/imgbom/internal"
|
||||||
"github.com/anchore/imgbom/internal/bus"
|
"github.com/anchore/imgbom/internal/bus"
|
||||||
"github.com/anchore/imgbom/internal/log"
|
|
||||||
"github.com/anchore/imgbom/internal/ui"
|
"github.com/anchore/imgbom/internal/ui"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
@ -33,46 +32,20 @@ Supports the following image sources:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
setCliOptions()
|
|
||||||
|
|
||||||
cobra.OnInitialize(
|
|
||||||
initAppConfig,
|
|
||||||
initLogging,
|
|
||||||
logAppConfig,
|
|
||||||
initEventBus,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func startWorker(userInput string) <-chan error {
|
func startWorker(userInput string) <-chan error {
|
||||||
errs := make(chan error)
|
errs := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(errs)
|
defer close(errs)
|
||||||
|
|
||||||
s, cleanup, err := imgbom.NewScope(userInput, appConfig.ScopeOpt)
|
catalog, scope, _, err := imgbom.Catalog(userInput, appConfig.ScopeOpt)
|
||||||
defer cleanup()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("could not produce catalog: %w", err)
|
errs <- fmt.Errorf("failed to catalog input: %+v", err)
|
||||||
}
|
return
|
||||||
log.Info("Identifying Distro")
|
|
||||||
distro := imgbom.IdentifyDistro(s)
|
|
||||||
|
|
||||||
if distro == nil {
|
|
||||||
log.Errorf("error identifying distro")
|
|
||||||
} else {
|
|
||||||
log.Infof(" Distro: %s", distro)
|
|
||||||
}
|
|
||||||
log.Info("Creating the Catalog")
|
|
||||||
catalog, err := imgbom.Catalog(s)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("could not produce catalog: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bus.Publish(partybus.Event{
|
bus.Publish(partybus.Event{
|
||||||
Type: event.CatalogerFinished,
|
Type: event.CatalogerFinished,
|
||||||
Value: presenter.GetPresenter(appConfig.PresenterOpt, s, catalog),
|
Value: presenter.GetPresenter(appConfig.PresenterOpt, *scope, catalog),
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
return errs
|
return errs
|
||||||
|
|||||||
@ -12,10 +12,10 @@ type Distro struct {
|
|||||||
RawVersion string
|
RawVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUnknownDistro creates a standardized UnkownDistro with a "0.0.0" version
|
// NewUnknownDistro creates a standardized Distro object for unidentifiable distros
|
||||||
func NewUnknownDistro() Distro {
|
func NewUnknownDistro() Distro {
|
||||||
return Distro{
|
return Distro{
|
||||||
Type: UnknownDistro,
|
Type: UnknownDistroType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,9 @@ import (
|
|||||||
type parseFunc func(string) *Distro
|
type parseFunc func(string) *Distro
|
||||||
|
|
||||||
// Identify parses distro-specific files to determine distro metadata like version and release
|
// Identify parses distro-specific files to determine distro metadata like version and release
|
||||||
func Identify(s scope.Scope) *Distro {
|
func Identify(s scope.Scope) Distro {
|
||||||
|
distro := NewUnknownDistro()
|
||||||
|
|
||||||
identityFiles := map[file.Path]parseFunc{
|
identityFiles := map[file.Path]parseFunc{
|
||||||
"/etc/os-release": parseOsRelease,
|
"/etc/os-release": parseOsRelease,
|
||||||
// Debian and Debian-based distros have the same contents linked from this path
|
// Debian and Debian-based distros have the same contents linked from this path
|
||||||
@ -25,7 +27,7 @@ func Identify(s scope.Scope) *Distro {
|
|||||||
refs, err := s.FilesByPath(path)
|
refs, err := s.FilesByPath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to get path refs from %s: %s", path, err)
|
log.Errorf("unable to get path refs from %s: %s", path, err)
|
||||||
return nil
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(refs) == 0 {
|
if len(refs) == 0 {
|
||||||
@ -51,21 +53,17 @@ func Identify(s scope.Scope) *Distro {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
distro := fn(content)
|
if candidateDistro := fn(content); candidateDistro != nil {
|
||||||
|
distro = *candidateDistro
|
||||||
if distro == nil {
|
break
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return distro
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: is it useful to know partially detected distros? where the ID is known but not the version (and viceversa?)
|
|
||||||
distro := NewUnknownDistro()
|
return distro
|
||||||
return &distro
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func assembleDistro(name, version string) *Distro {
|
func assemble(name, version string) *Distro {
|
||||||
distroType, ok := Mappings[name]
|
distroType, ok := Mappings[name]
|
||||||
|
|
||||||
// Both distro and version must be present
|
// Both distro and version must be present
|
||||||
@ -99,7 +97,7 @@ func parseOsRelease(contents string) *Distro {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return assembleDistro(id, vers)
|
return assemble(id, vers)
|
||||||
}
|
}
|
||||||
|
|
||||||
var busyboxVersionMatcher = regexp.MustCompile(`BusyBox v[\d\.]+`)
|
var busyboxVersionMatcher = regexp.MustCompile(`BusyBox v[\d\.]+`)
|
||||||
@ -109,7 +107,7 @@ func parseBusyBox(contents string) *Distro {
|
|||||||
for _, match := range matches {
|
for _, match := range matches {
|
||||||
parts := strings.Split(match, " ")
|
parts := strings.Split(match, " ")
|
||||||
version := strings.ReplaceAll(parts[1], "v", "")
|
version := strings.ReplaceAll(parts[1], "v", "")
|
||||||
distro := assembleDistro("busybox", version)
|
distro := assemble("busybox", version)
|
||||||
if distro != nil {
|
if distro != nil {
|
||||||
return distro
|
return distro
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,12 +24,12 @@ func TestIdentifyDistro(t *testing.T) {
|
|||||||
{
|
{
|
||||||
fixture: "test-fixtures/os/empty",
|
fixture: "test-fixtures/os/empty",
|
||||||
name: "No OS files",
|
name: "No OS files",
|
||||||
Type: UnknownDistro,
|
Type: UnknownDistroType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fixture: "test-fixtures/os/unmatchable",
|
fixture: "test-fixtures/os/unmatchable",
|
||||||
name: "Unmatchable distro",
|
name: "Unmatchable distro",
|
||||||
Type: UnknownDistro,
|
Type: UnknownDistroType,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
package distro
|
package distro
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UnknownDistro Type = iota
|
UnknownDistroType Type = iota
|
||||||
Debian
|
Debian
|
||||||
Ubuntu
|
Ubuntu
|
||||||
RedHat
|
RedHat
|
||||||
@ -22,7 +22,7 @@ const (
|
|||||||
type Type int
|
type Type int
|
||||||
|
|
||||||
var distroStr = []string{
|
var distroStr = []string{
|
||||||
"UnknownDistro",
|
"UnknownDistroType",
|
||||||
"debian",
|
"debian",
|
||||||
"ubuntu",
|
"ubuntu",
|
||||||
"redhat",
|
"redhat",
|
||||||
|
|||||||
@ -10,60 +10,33 @@ import (
|
|||||||
"github.com/anchore/imgbom/imgbom/scope"
|
"github.com/anchore/imgbom/imgbom/scope"
|
||||||
"github.com/anchore/imgbom/internal/bus"
|
"github.com/anchore/imgbom/internal/bus"
|
||||||
"github.com/anchore/imgbom/internal/log"
|
"github.com/anchore/imgbom/internal/log"
|
||||||
"github.com/anchore/stereoscope"
|
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
|
||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IdentifyDistro(s scope.Scope) *distro.Distro {
|
func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scope, *distro.Distro, error) {
|
||||||
|
s, cleanup, err := scope.NewScope(userInput, scoptOpt)
|
||||||
|
defer cleanup()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("failed to create scope: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d := IdentifyDistro(s)
|
||||||
|
|
||||||
|
catalog, err := CatalogFromScope(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("failed to produce catalog: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return catalog, &s, &d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IdentifyDistro(s scope.Scope) distro.Distro {
|
||||||
|
log.Info("Identifying Distro")
|
||||||
return distro.Identify(s)
|
return distro.Identify(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewScope produces a Scope based on userInput like dir:// or image:tag
|
func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {
|
||||||
func NewScope(userInput string, o scope.Option) (scope.Scope, func(), error) {
|
log.Info("Building the Catalog")
|
||||||
protocol := NewProtocol(userInput)
|
|
||||||
log.Debugf("protocol: %+v", protocol)
|
|
||||||
|
|
||||||
switch protocol.Type {
|
|
||||||
case DirProtocol:
|
|
||||||
// populate the scope object for dir
|
|
||||||
s, err := GetScopeFromDir(protocol.Value, o)
|
|
||||||
if err != nil {
|
|
||||||
return scope.Scope{}, func() {}, fmt.Errorf("could not populate scope from path (%s): %w", protocol.Value, err)
|
|
||||||
}
|
|
||||||
return s, func() {}, nil
|
|
||||||
|
|
||||||
case ImageProtocol:
|
|
||||||
log.Infof("Fetching image '%s'", userInput)
|
|
||||||
img, err := stereoscope.GetImage(userInput)
|
|
||||||
cleanup := func() {
|
|
||||||
stereoscope.Cleanup()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil || img == nil {
|
|
||||||
return scope.Scope{}, cleanup, fmt.Errorf("could not fetch image '%s': %w", userInput, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := GetScopeFromImage(img, o)
|
|
||||||
if err != nil {
|
|
||||||
return scope.Scope{}, cleanup, fmt.Errorf("could not populate scope with image: %w", err)
|
|
||||||
}
|
|
||||||
return s, cleanup, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
return scope.Scope{}, func() {}, fmt.Errorf("unable to process input for scanning: '%s'", userInput)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetScopeFromDir(d string, o scope.Option) (scope.Scope, error) {
|
|
||||||
return scope.NewScopeFromDir(d, o)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetScopeFromImage(img *image.Image, o scope.Option) (scope.Scope, error) {
|
|
||||||
return scope.NewScopeFromImage(img, o)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Catalog(s scope.Scope) (*pkg.Catalog, error) {
|
|
||||||
return cataloger.Catalog(s)
|
return cataloger.Catalog(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package imgbom
|
package scope
|
||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
@ -7,52 +7,53 @@ import "strings"
|
|||||||
// and return an Option type.
|
// and return an Option type.
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UnknownProtocol ProtocolType = iota
|
// nolint:varcheck,deadcode
|
||||||
ImageProtocol
|
unknownProtocol protocolType = iota
|
||||||
DirProtocol
|
imageProtocol
|
||||||
|
directoryProtocol
|
||||||
)
|
)
|
||||||
|
|
||||||
var optionStr = []string{
|
var protocolStr = []string{
|
||||||
"UnknownProtocol",
|
"UnknownProtocol",
|
||||||
"image",
|
"Image",
|
||||||
"dir",
|
"Directory",
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProtocolType int
|
type protocolType int
|
||||||
|
|
||||||
type Protocol struct {
|
type protocol struct {
|
||||||
Type ProtocolType
|
Type protocolType
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProtocol(userStr string) Protocol {
|
func newProtocol(userStr string) protocol {
|
||||||
candidates := strings.Split(userStr, "://")
|
candidates := strings.Split(userStr, "://")
|
||||||
|
|
||||||
switch len(candidates) {
|
switch len(candidates) {
|
||||||
case 2:
|
case 2:
|
||||||
if strings.HasPrefix(userStr, "dir://") {
|
if strings.HasPrefix(userStr, "dir://") {
|
||||||
return Protocol{
|
return protocol{
|
||||||
Type: DirProtocol,
|
Type: directoryProtocol,
|
||||||
Value: strings.TrimPrefix(userStr, "dir://"),
|
Value: strings.TrimPrefix(userStr, "dir://"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// default to an Image for anything else since stereoscope can handle this
|
// default to an Image for anything else since stereoscope can handle this
|
||||||
return Protocol{
|
return protocol{
|
||||||
Type: ImageProtocol,
|
Type: imageProtocol,
|
||||||
Value: userStr,
|
Value: userStr,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return Protocol{
|
return protocol{
|
||||||
Type: ImageProtocol,
|
Type: imageProtocol,
|
||||||
Value: userStr,
|
Value: userStr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o ProtocolType) String() string {
|
func (o protocolType) String() string {
|
||||||
if int(o) >= len(optionStr) || o < 0 {
|
if int(o) >= len(protocolStr) || o < 0 {
|
||||||
return optionStr[0]
|
return protocolStr[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return optionStr[o]
|
return protocolStr[o]
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package imgbom
|
package scope
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
@ -6,31 +6,31 @@ func TestNewProtocol(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
input string
|
input string
|
||||||
expType ProtocolType
|
expType protocolType
|
||||||
expValue string
|
expValue string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "directory protocol",
|
desc: "directory protocol",
|
||||||
input: "dir:///opt/",
|
input: "dir:///opt/",
|
||||||
expType: DirProtocol,
|
expType: directoryProtocol,
|
||||||
expValue: "/opt/",
|
expValue: "/opt/",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "unknown protocol",
|
desc: "unknown protocol",
|
||||||
input: "s4:///opt/",
|
input: "s4:///opt/",
|
||||||
expType: ImageProtocol,
|
expType: imageProtocol,
|
||||||
expValue: "s4:///opt/",
|
expValue: "s4:///opt/",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "docker protocol",
|
desc: "docker protocol",
|
||||||
input: "docker://ubuntu:20.04",
|
input: "docker://ubuntu:20.04",
|
||||||
expType: ImageProtocol,
|
expType: imageProtocol,
|
||||||
expValue: "docker://ubuntu:20.04",
|
expValue: "docker://ubuntu:20.04",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
p := NewProtocol(test.input)
|
p := newProtocol(test.input)
|
||||||
if p.Type != test.expType {
|
if p.Type != test.expType {
|
||||||
t.Errorf("mismatched type in protocol: '%v' != '%v'", p.Type, test.expType)
|
t.Errorf("mismatched type in protocol: '%v' != '%v'", p.Type, test.expType)
|
||||||
}
|
}
|
||||||
@ -3,7 +3,10 @@ package scope
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/anchore/stereoscope"
|
||||||
|
|
||||||
"github.com/anchore/imgbom/imgbom/scope/resolvers"
|
"github.com/anchore/imgbom/imgbom/scope/resolvers"
|
||||||
|
"github.com/anchore/imgbom/internal/log"
|
||||||
"github.com/anchore/stereoscope/pkg/file"
|
"github.com/anchore/stereoscope/pkg/file"
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
)
|
)
|
||||||
@ -23,6 +26,41 @@ type Scope struct {
|
|||||||
DirSrc DirSource
|
DirSrc DirSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewScope produces a Scope based on userInput like dir:// or image:tag
|
||||||
|
func NewScope(userInput string, o Option) (Scope, func(), error) {
|
||||||
|
protocol := newProtocol(userInput)
|
||||||
|
|
||||||
|
switch protocol.Type {
|
||||||
|
case directoryProtocol:
|
||||||
|
// populate the scope object for dir
|
||||||
|
s, err := NewScopeFromDir(protocol.Value, o)
|
||||||
|
if err != nil {
|
||||||
|
return Scope{}, func() {}, fmt.Errorf("could not populate scope from path (%s): %w", protocol.Value, err)
|
||||||
|
}
|
||||||
|
return s, func() {}, nil
|
||||||
|
|
||||||
|
case imageProtocol:
|
||||||
|
log.Infof("Fetching image '%s'", userInput)
|
||||||
|
img, err := stereoscope.GetImage(userInput)
|
||||||
|
cleanup := func() {
|
||||||
|
stereoscope.Cleanup()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil || img == nil {
|
||||||
|
return Scope{}, cleanup, fmt.Errorf("could not fetch image '%s': %w", userInput, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := NewScopeFromImage(img, o)
|
||||||
|
if err != nil {
|
||||||
|
return Scope{}, cleanup, fmt.Errorf("could not populate scope with image: %w", err)
|
||||||
|
}
|
||||||
|
return s, cleanup, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Scope{}, func() {}, fmt.Errorf("unable to process input for scanning: '%s'", userInput)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewScopeFromDir(path string, option Option) (Scope, error) {
|
func NewScopeFromDir(path string, option Option) (Scope, error) {
|
||||||
return Scope{
|
return Scope{
|
||||||
Option: option,
|
Option: option,
|
||||||
|
|||||||
@ -5,24 +5,25 @@ package integration
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/go-testutils"
|
|
||||||
"github.com/anchore/imgbom/imgbom"
|
"github.com/anchore/imgbom/imgbom"
|
||||||
|
|
||||||
|
"github.com/anchore/go-testutils"
|
||||||
"github.com/anchore/imgbom/imgbom/distro"
|
"github.com/anchore/imgbom/imgbom/distro"
|
||||||
"github.com/anchore/imgbom/imgbom/scope"
|
"github.com/anchore/imgbom/imgbom/scope"
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDistroImage(t *testing.T) {
|
func TestDistroImage(t *testing.T) {
|
||||||
img, cleanup := testutils.GetFixtureImage(t, "docker-archive", "image-distro-id")
|
fixtureImageName := "image-distro-id"
|
||||||
|
_, cleanup := testutils.GetFixtureImage(t, "docker-archive", fixtureImageName)
|
||||||
|
tarPath := testutils.GetFixtureImageTarPath(t, fixtureImageName)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
s, err := imgbom.GetScopeFromImage(img, scope.AllLayersScope)
|
_, _, actualDistro, err := imgbom.Catalog("docker-archive://"+tarPath, scope.AllLayersScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not populate scope with image: %+v", err)
|
t.Fatalf("failed to catalog image: %+v", err)
|
||||||
}
|
}
|
||||||
|
if actualDistro == nil {
|
||||||
actual := imgbom.IdentifyDistro(s)
|
|
||||||
if actual == nil {
|
|
||||||
t.Fatalf("could not find distro")
|
t.Fatalf("could not find distro")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ func TestDistroImage(t *testing.T) {
|
|||||||
t.Fatalf("could not create distro: %+v", err)
|
t.Fatalf("could not create distro: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
diffs := deep.Equal(*actual, expected)
|
diffs := deep.Equal(*actualDistro, expected)
|
||||||
if len(diffs) != 0 {
|
if len(diffs) != 0 {
|
||||||
for _, d := range diffs {
|
for _, d := range diffs {
|
||||||
t.Errorf("found distro difference: %+v", d)
|
t.Errorf("found distro difference: %+v", d)
|
||||||
|
|||||||
@ -5,21 +5,22 @@ package integration
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/imgbom/imgbom"
|
||||||
|
|
||||||
"github.com/anchore/imgbom/internal"
|
"github.com/anchore/imgbom/internal"
|
||||||
|
|
||||||
"github.com/anchore/go-testutils"
|
"github.com/anchore/go-testutils"
|
||||||
"github.com/anchore/imgbom/imgbom/cataloger"
|
|
||||||
"github.com/anchore/imgbom/imgbom/pkg"
|
"github.com/anchore/imgbom/imgbom/pkg"
|
||||||
"github.com/anchore/imgbom/imgbom/scope"
|
"github.com/anchore/imgbom/imgbom/scope"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLanguageImage(t *testing.T) {
|
func TestPkgCoverageImage(t *testing.T) {
|
||||||
img, cleanup := testutils.GetFixtureImage(t, "docker-archive", "image-pkg-coverage")
|
fixtureImageName := "image-pkg-coverage"
|
||||||
|
_, cleanup := testutils.GetFixtureImage(t, "docker-archive", fixtureImageName)
|
||||||
|
tarPath := testutils.GetFixtureImageTarPath(t, fixtureImageName)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
catalog, _, _, err := imgbom.Catalog("docker-archive://"+tarPath, scope.AllLayersScope)
|
||||||
|
|
||||||
catalog, err := cataloger.Catalog(s)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to catalog image: %+v", err)
|
t.Fatalf("failed to catalog image: %+v", err)
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user