Merge pull request #27 from anchore/add-package-id

Add package id
This commit is contained in:
Alex Goodman 2020-06-01 08:55:22 -04:00 committed by GitHub
commit 1f272c2bda
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 97 additions and 35 deletions

View file

@ -68,5 +68,6 @@ func doRunCmd(cmd *cobra.Command, args []string) int {
log.Errorf("could not format catalog results: %w", err) log.Errorf("could not format catalog results: %w", err)
return 1 return 1
} }
return 0 return 0
} }

3
go.mod
View file

@ -8,6 +8,7 @@ require (
github.com/anchore/stereoscope v0.0.0-20200523232006-be5f3c18958f github.com/anchore/stereoscope v0.0.0-20200523232006-be5f3c18958f
github.com/go-test/deep v1.0.6 github.com/go-test/deep v1.0.6
github.com/google/go-containerregistry v0.0.0-20200521151920-a873a21aff23 // indirect github.com/google/go-containerregistry v0.0.0-20200521151920-a873a21aff23 // indirect
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect
github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-multierror v1.1.0
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.3.1 github.com/mitchellh/mapstructure v1.3.1
@ -16,6 +17,6 @@ require (
github.com/spf13/viper v1.7.0 github.com/spf13/viper v1.7.0
go.uber.org/zap v1.15.0 go.uber.org/zap v1.15.0
golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect
google.golang.org/genproto v0.0.0-20200521103424-e9a78aa275b7 // indirect google.golang.org/protobuf v1.24.0 // indirect
gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v2 v2.3.0
) )

9
go.sum
View file

@ -210,6 +210,8 @@ github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@ -628,8 +630,8 @@ google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84 h1:pSLkPbrjnPyLDYU
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200519141106-08726f379972 h1:6ydLqG65DIMNJf6p97WudGsmd1w3Ickm/LiZnBrREPI= google.golang.org/genproto v0.0.0-20200519141106-08726f379972 h1:6ydLqG65DIMNJf6p97WudGsmd1w3Ickm/LiZnBrREPI=
google.golang.org/genproto v0.0.0-20200519141106-08726f379972/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200519141106-08726f379972/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200521103424-e9a78aa275b7 h1:JUs1uIDQ46c7iI0QuMPzAHqXaSmqKF0f9freFMk2ivs= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200521103424-e9a78aa275b7/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -650,6 +652,9 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -18,7 +18,7 @@ func init() {
controllerInstance.add(dpkg.NewAnalyzer()) controllerInstance.add(dpkg.NewAnalyzer())
} }
func Analyze(s scope.Scope) (pkg.Catalog, error) { func Analyze(s scope.Scope) (*pkg.Catalog, error) {
return controllerInstance.analyze(s) return controllerInstance.analyze(s)
} }
@ -31,7 +31,7 @@ func (c *controller) add(a Analyzer) {
c.analyzers = append(c.analyzers, a) c.analyzers = append(c.analyzers, a)
} }
func (c *controller) analyze(s scope.Scope) (pkg.Catalog, error) { func (c *controller) analyze(s scope.Scope) (*pkg.Catalog, error) {
catalog := pkg.NewCatalog() catalog := pkg.NewCatalog()
fileSelection := make([]file.Reference, 0) fileSelection := make([]file.Reference, 0)
@ -44,7 +44,7 @@ func (c *controller) analyze(s scope.Scope) (pkg.Catalog, error) {
// fetch contents for requested selection by analyzers // fetch contents for requested selection by analyzers
contents, err := s.Image.MultipleFileContentsByRef(fileSelection...) contents, err := s.Image.MultipleFileContentsByRef(fileSelection...)
if err != nil { if err != nil {
return pkg.Catalog{}, err return nil, err
} }
// perform analysis, accumulating errors for each failed analysis // perform analysis, accumulating errors for each failed analysis
@ -65,7 +65,7 @@ func (c *controller) analyze(s scope.Scope) (pkg.Catalog, error) {
} }
if errs != nil { if errs != nil {
return pkg.Catalog{}, errs return nil, errs
} }
return catalog, nil return catalog, nil

View file

@ -10,7 +10,7 @@ import (
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
) )
var endOfPackages = fmt.Errorf("no more packages to read") var errEndOfPackages = fmt.Errorf("no more packages to read")
func ParseEntries(reader io.Reader) ([]pkg.DpkgMetadata, error) { func ParseEntries(reader io.Reader) ([]pkg.DpkgMetadata, error) {
buffedReader := bufio.NewReader(reader) buffedReader := bufio.NewReader(reader)
@ -19,7 +19,7 @@ func ParseEntries(reader io.Reader) ([]pkg.DpkgMetadata, error) {
for { for {
entry, err := parseEntry(buffedReader) entry, err := parseEntry(buffedReader)
if err != nil { if err != nil {
if err == endOfPackages { if err == errEndOfPackages {
break break
} }
return nil, err return nil, err
@ -38,7 +38,7 @@ func parseEntry(reader *bufio.Reader) (entry pkg.DpkgMetadata, err error) {
line, err := reader.ReadString('\n') line, err := reader.ReadString('\n')
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
return pkg.DpkgMetadata{}, endOfPackages return pkg.DpkgMetadata{}, errEndOfPackages
} }
return pkg.DpkgMetadata{}, err return pkg.DpkgMetadata{}, err
} }

View file

@ -8,10 +8,10 @@ import (
) )
// TODO: add os detection results as return value // TODO: add os detection results as return value
func CatalogImage(img *image.Image, o scope.Option) (pkg.Catalog, error) { func CatalogImage(img *image.Image, o scope.Option) (*pkg.Catalog, error) {
s, err := scope.NewScope(img, o) s, err := scope.NewScope(img, o)
if err != nil { if err != nil {
return pkg.Catalog{}, err return nil, err
} }
// TODO: add OS detection here... // TODO: add OS detection here...

View file

@ -1,31 +1,75 @@
package pkg package pkg
import (
"sync"
"github.com/anchore/imgbom/internal/log"
"github.com/anchore/stereoscope/pkg/file"
)
// TODO: add reader methods (by type, id, fuzzy search, etc) // TODO: add reader methods (by type, id, fuzzy search, etc)
var nextPackageID int64
type Catalog struct { type Catalog struct {
// TODO: catalog by package ID for potential indexing byID map[ID]*Package
packages map[Type][]Package byType map[Type][]*Package
byFile map[file.Reference][]*Package
lock sync.RWMutex
} }
func NewCatalog() Catalog { func NewCatalog() *Catalog {
return Catalog{ return &Catalog{
packages: make(map[Type][]Package), byID: make(map[ID]*Package),
byType: make(map[Type][]*Package),
byFile: make(map[file.Reference][]*Package),
} }
} }
func (c *Catalog) Package(id ID) *Package {
return c.byID[id]
}
func (c *Catalog) PackagesByFile(ref file.Reference) []*Package {
return c.byFile[ref]
}
func (c *Catalog) Add(p Package) { func (c *Catalog) Add(p Package) {
_, ok := c.packages[p.Type] if p.id != 0 {
if !ok { log.Errorf("package already added to catalog: %s", p)
c.packages[p.Type] = make([]Package, 0) return
}
c.lock.Lock()
defer c.lock.Unlock()
p.id = ID(nextPackageID)
nextPackageID++
// store by package ID
c.byID[p.id] = &p
// store by package type
_, ok := c.byType[p.Type]
if !ok {
c.byType[p.Type] = make([]*Package, 0)
}
c.byType[p.Type] = append(c.byType[p.Type], &p)
// store by file references
for _, s := range p.Source {
_, ok := c.byFile[s]
if !ok {
c.byFile[s] = make([]*Package, 0)
}
c.byFile[s] = append(c.byFile[s], &p)
} }
c.packages[p.Type] = append(c.packages[p.Type], p)
} }
func (c *Catalog) Enumerate(types ...Type) <-chan Package { func (c *Catalog) Enumerate(types ...Type) <-chan *Package {
channel := make(chan Package) channel := make(chan *Package)
go func() { go func() {
defer close(channel) defer close(channel)
for ty, packages := range c.packages { for ty, packages := range c.byType {
if len(types) != 0 { if len(types) != 0 {
found := false found := false
typeCheck: typeCheck:

View file

@ -1,11 +1,16 @@
package pkg package pkg
import "github.com/anchore/stereoscope/pkg/file" import (
"fmt"
// TODO: add package ID (random/incremental) "github.com/anchore/stereoscope/pkg/file"
)
type ID int64
// TODO: add field to trace which analyzer detected this // TODO: add field to trace which analyzer detected this
type Package struct { type Package struct {
id ID
Name string Name string
Version string Version string
Source []file.Reference Source []file.Reference
@ -14,4 +19,10 @@ type Package struct {
Metadata interface{} Metadata interface{}
} }
// TODO: stringer... func (p Package) ID() ID {
return p.id
}
func (p Package) String() string {
return fmt.Sprintf("Pkg(type=%s, name=%s, version=%s)", p.Type, p.Name, p.Version)
}

View file

@ -5,7 +5,7 @@ const (
ApkPkg ApkPkg
DebPkg DebPkg
JavaPkg JavaPkg
NodePkg JavaScriptPkg
PacmanPkg PacmanPkg
PythonPkg PythonPkg
RpmPkg RpmPkg
@ -19,7 +19,7 @@ var typeStr = []string{
"apk", "apk",
"deb", "deb",
"java", "java",
"node", "javascript",
"pacman", "pacman",
"python", "python",
"rpm", "rpm",

View file

@ -49,7 +49,7 @@ type artifact struct {
Metadata interface{} `json:"metadata"` Metadata interface{} `json:"metadata"`
} }
func (pres *Presenter) Present(output io.Writer, img *stereoscopeImg.Image, catalog pkg.Catalog) error { func (pres *Presenter) Present(output io.Writer, img *stereoscopeImg.Image, catalog *pkg.Catalog) error {
tags := make([]string, len(img.Metadata.Tags)) tags := make([]string, len(img.Metadata.Tags))
for idx, tag := range img.Metadata.Tags { for idx, tag := range img.Metadata.Tags {
tags[idx] = tag.String() tags[idx] = tag.String()

View file

@ -16,7 +16,7 @@ var Options = []Option{
JSONPresenter, JSONPresenter,
} }
type Option uint type Option int
func ParseOption(userStr string) Option { func ParseOption(userStr string) Option {
switch strings.ToLower(userStr) { switch strings.ToLower(userStr) {
@ -28,7 +28,7 @@ func ParseOption(userStr string) Option {
} }
func (o Option) String() string { func (o Option) String() string {
if int(o) >= len(optionStr) { if int(o) >= len(optionStr) || o < 0 {
return optionStr[0] return optionStr[0]
} }

View file

@ -9,7 +9,7 @@ import (
) )
type Presenter interface { type Presenter interface {
Present(io.Writer, *image.Image, pkg.Catalog) error Present(io.Writer, *image.Image, *pkg.Catalog) error
} }
func GetPresenter(option Option) Presenter { func GetPresenter(option Option) Presenter {

View file

@ -8,7 +8,7 @@ const (
AllLayersScope AllLayersScope
) )
type Option uint type Option int
var optionStr = []string{ var optionStr = []string{
"UnknownScope", "UnknownScope",
@ -32,7 +32,7 @@ func ParseOption(userStr string) Option {
} }
func (o Option) String() string { func (o Option) String() string {
if int(o) >= len(optionStr) { if int(o) >= len(optionStr) || o < 0 {
return optionStr[0] return optionStr[0]
} }