Add detailed location info to json artifact (#127)

* add detailed location info to json artifact

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* decompose json presenter

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2020-08-07 10:05:16 -04:00 committed by GitHub
parent dc8dfc8457
commit 817ce61036
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 232 additions and 124 deletions

View File

@ -4,6 +4,37 @@
"artifacts": {
"items": {
"properties": {
"found-by": {
"items": {
"type": "string"
},
"type": "array"
},
"locations": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"properties": {
"layer-index": {
"type": "integer"
},
"path": {
"type": "string"
}
},
"required": [
"layer-index",
"path"
],
"type": "object"
}
]
},
"type": "array"
},
"metadata": {
"properties": {
"architecture": {
@ -425,27 +456,6 @@
"name": {
"type": "string"
},
"sources": {
"items": {
"properties": {
"found-by": {
"type": "string"
},
"locations": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"found-by",
"locations"
],
"type": "object"
},
"type": "array"
},
"type": {
"type": "string"
},
@ -454,8 +464,9 @@
}
},
"required": [
"found-by",
"locations",
"name",
"sources",
"type",
"version"
],

View File

@ -0,0 +1,31 @@
package json
import (
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
type Artifact struct {
Name string `json:"name"`
Version string `json:"version"`
Type string `json:"type"`
FoundBy []string `json:"found-by"`
Locations Locations `json:"locations,omitempty"`
Metadata interface{} `json:"metadata,omitempty"`
}
func NewArtifact(p *pkg.Package, s scope.Scope) (Artifact, error) {
locations, err := NewLocations(p, s)
if err != nil {
return Artifact{}, err
}
return Artifact{
Name: p.Name,
Version: p.Version,
Type: string(p.Type),
FoundBy: []string{p.FoundBy},
Locations: locations,
Metadata: p.Metadata,
}, nil
}

View File

@ -0,0 +1,40 @@
package json
import (
"fmt"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
type Document struct {
Artifacts []Artifact `json:"artifacts"`
Image *Image `json:"image,omitempty"`
Directory *string `json:"directory,omitempty"`
}
func NewDocument(catalog *pkg.Catalog, s scope.Scope) (Document, error) {
doc := Document{
Artifacts: make([]Artifact, 0),
}
srcObj := s.Source()
switch src := srcObj.(type) {
case scope.ImageSource:
doc.Image = NewImage(src)
case scope.DirSource:
doc.Directory = &s.DirSrc.Path
default:
return Document{}, fmt.Errorf("unsupported source: %T", src)
}
for _, p := range catalog.Sorted() {
art, err := NewArtifact(p, s)
if err != nil {
return Document{}, err
}
doc.Artifacts = append(doc.Artifacts, art)
}
return doc, nil
}

View File

@ -0,0 +1,42 @@
package json
import "github.com/anchore/syft/syft/scope"
type Image struct {
Layers []Layer `json:"layers"`
Size int64 `json:"size"`
Digest string `json:"digest"`
MediaType string `json:"media-type"`
Tags []string `json:"tags"`
}
type Layer struct {
MediaType string `json:"media-type"`
Digest string `json:"digest"`
Size int64 `json:"size"`
}
func NewImage(src scope.ImageSource) *Image {
// populate artifacts...
tags := make([]string, len(src.Img.Metadata.Tags))
for idx, tag := range src.Img.Metadata.Tags {
tags[idx] = tag.String()
}
img := Image{
Digest: src.Img.Metadata.Digest,
Size: src.Img.Metadata.Size,
MediaType: string(src.Img.Metadata.MediaType),
Tags: tags,
Layers: make([]Layer, len(src.Img.Layers)),
}
// populate image metadata
for idx, l := range src.Img.Layers {
img.Layers[idx] = Layer{
MediaType: string(l.Metadata.MediaType),
Digest: l.Metadata.Digest,
Size: l.Metadata.Size,
}
}
return &img
}

View File

@ -0,0 +1,46 @@
package json
import (
"fmt"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/scope"
)
type Locations interface{}
type ImageLocation struct {
Path string `json:"path"`
LayerIndex uint `json:"layer-index"`
}
func NewLocations(p *pkg.Package, s scope.Scope) (Locations, error) {
srcObj := s.Source()
switch src := srcObj.(type) {
case scope.ImageSource:
locations := make([]ImageLocation, len(p.Source))
for idx := range p.Source {
entry, err := src.Img.FileCatalog.Get(p.Source[idx])
if err != nil {
return nil, fmt.Errorf("unable to find layer index for source-idx=%d package=%s", idx, p.Name)
}
artifactSource := ImageLocation{
LayerIndex: entry.Source.Metadata.Index,
Path: string(p.Source[idx].Path),
}
locations[idx] = artifactSource
}
return locations, nil
case scope.DirSource:
locations := make([]string, len(p.Source))
for idx := range p.Source {
locations[idx] = string(p.Source[idx].Path)
}
return locations, nil
default:
return nil, fmt.Errorf("unable to determine source: %T", src)
}
}

View File

@ -2,7 +2,6 @@ package json
import (
"encoding/json"
"fmt"
"io"
"github.com/anchore/syft/syft/pkg"
@ -21,93 +20,10 @@ func NewPresenter(catalog *pkg.Catalog, s scope.Scope) *Presenter {
}
}
type document struct {
Artifacts []artifact `json:"artifacts"`
Image *image `json:"image,omitempty"`
Directory *string `json:"directory,omitempty"`
}
type image struct {
Layers []layer `json:"layers"`
Size int64 `json:"size"`
Digest string `json:"digest"`
MediaType string `json:"media-type"`
Tags []string `json:"tags"`
}
type layer struct {
MediaType string `json:"media-type"`
Digest string `json:"digest"`
Size int64 `json:"size"`
}
type source struct {
FoundBy string `json:"found-by"`
Locations []string `json:"locations"`
}
type artifact struct {
Name string `json:"name"`
Version string `json:"version"`
Type string `json:"type"`
Sources []source `json:"sources"`
Metadata interface{} `json:"metadata,omitempty"`
}
func (pres *Presenter) Present(output io.Writer) error {
doc := document{
Artifacts: make([]artifact, 0),
}
srcObj := pres.scope.Source()
switch src := srcObj.(type) {
case scope.ImageSource:
// populate artifacts...
tags := make([]string, len(src.Img.Metadata.Tags))
for idx, tag := range src.Img.Metadata.Tags {
tags[idx] = tag.String()
}
doc.Image = &image{
Digest: src.Img.Metadata.Digest,
Size: src.Img.Metadata.Size,
MediaType: string(src.Img.Metadata.MediaType),
Tags: tags,
Layers: make([]layer, len(src.Img.Layers)),
}
// populate image metadata
for idx, l := range src.Img.Layers {
doc.Image.Layers[idx] = layer{
MediaType: string(l.Metadata.MediaType),
Digest: l.Metadata.Digest,
Size: l.Metadata.Size,
}
}
case scope.DirSource:
doc.Directory = &pres.scope.DirSrc.Path
default:
return fmt.Errorf("unsupported source: %T", src)
}
for _, p := range pres.catalog.Sorted() {
art := artifact{
Name: p.Name,
Version: p.Version,
Type: string(p.Type),
Sources: make([]source, len(p.Source)),
Metadata: p.Metadata,
}
for idx := range p.Source {
srcObj := source{
FoundBy: p.FoundBy,
Locations: []string{string(p.Source[idx].Path)},
}
art.Sources[idx] = srcObj
}
doc.Artifacts = append(doc.Artifacts, art)
doc, err := NewDocument(pres.catalog, pres.scope)
if err != nil {
return err
}
enc := json.NewEncoder(output)

View File

@ -24,11 +24,19 @@ func TestJsonDirsPresenter(t *testing.T) {
Name: "package-1",
Version: "1.0.1",
Type: pkg.DebPkg,
FoundBy: "the-cataloger-1",
Source: []file.Reference{
{Path: "/some/path/pkg1"},
},
})
catalog.Add(pkg.Package{
Name: "package-2",
Version: "2.0.1",
Type: pkg.DebPkg,
FoundBy: "the-cataloger-2",
Source: []file.Reference{
{Path: "/some/path/pkg1"},
},
})
s, err := scope.NewScopeFromDir("/some/path", scope.AllLayersScope)
@ -78,6 +86,7 @@ func TestJsonImgsPresenter(t *testing.T) {
*img.SquashedTree().File("/somefile-1.txt"),
},
Type: pkg.DebPkg,
FoundBy: "the-cataloger-1",
})
catalog.Add(pkg.Package{
Name: "package-2",
@ -86,6 +95,7 @@ func TestJsonImgsPresenter(t *testing.T) {
*img.SquashedTree().File("/somefile-2.txt"),
},
Type: pkg.DebPkg,
FoundBy: "the-cataloger-2",
})
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)

View File

@ -4,13 +4,23 @@
"name": "package-1",
"version": "1.0.1",
"type": "deb",
"sources": []
"found-by": [
"the-cataloger-1"
],
"locations": [
"/some/path/pkg1"
]
},
{
"name": "package-2",
"version": "2.0.1",
"type": "deb",
"sources": []
"found-by": [
"the-cataloger-2"
],
"locations": [
"/some/path/pkg1"
]
}
],
"directory": "/some/path"

View File

@ -4,12 +4,13 @@
"name": "package-1",
"version": "1.0.1",
"type": "deb",
"sources": [
{
"found-by": "",
"found-by": [
"the-cataloger-1"
],
"locations": [
"/somefile-1.txt"
]
{
"path": "/somefile-1.txt",
"layer-index": 0
}
]
},
@ -17,12 +18,13 @@
"name": "package-2",
"version": "2.0.1",
"type": "deb",
"sources": [
{
"found-by": "",
"found-by": [
"the-cataloger-2"
],
"locations": [
"/somefile-2.txt"
]
{
"path": "/somefile-2.txt",
"layer-index": 1
}
]
}