mirror of
https://github.com/anchore/syft.git
synced 2026-10-31 22:46:36 +01:00
move source metadata upstream and fix tests
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package source
|
||||
|
||||
import "github.com/anchore/stereoscope/pkg/image"
|
||||
|
||||
type ImageMetadata struct {
|
||||
UserInput string `json:"userInput"`
|
||||
Layers []LayerMetadata `json:"layers"`
|
||||
Size int64 `json:"size"`
|
||||
Digest string `json:"digest"`
|
||||
MediaType string `json:"mediaType"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
type LayerMetadata struct {
|
||||
MediaType string `json:"mediaType"`
|
||||
Digest string `json:"digest"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
func NewImageMetadata(img *image.Image, userInput string) ImageMetadata {
|
||||
// populate artifacts...
|
||||
tags := make([]string, len(img.Metadata.Tags))
|
||||
for idx, tag := range img.Metadata.Tags {
|
||||
tags[idx] = tag.String()
|
||||
}
|
||||
theImg := ImageMetadata{
|
||||
UserInput: userInput,
|
||||
Digest: img.Metadata.Digest,
|
||||
Size: img.Metadata.Size,
|
||||
MediaType: string(img.Metadata.MediaType),
|
||||
Tags: tags,
|
||||
Layers: make([]LayerMetadata, len(img.Layers)),
|
||||
}
|
||||
|
||||
// populate image metadata
|
||||
for idx, l := range img.Layers {
|
||||
theImg.Layers[idx] = LayerMetadata{
|
||||
MediaType: string(l.Metadata.MediaType),
|
||||
Digest: l.Metadata.Digest,
|
||||
Size: l.Metadata.Size,
|
||||
}
|
||||
}
|
||||
return theImg
|
||||
}
|
||||
@@ -8,10 +8,9 @@ import (
|
||||
)
|
||||
|
||||
type Location struct {
|
||||
Path string `json:"path"`
|
||||
LayerIndex uint `json:"layerIndex"`
|
||||
LayerID string `json:"layerID"`
|
||||
ref file.Reference
|
||||
Path string `json:"path"`
|
||||
FileSystemID string `json:"layerID,omitempty"` // TODO: comment
|
||||
ref file.Reference
|
||||
}
|
||||
|
||||
func NewLocation(path string) Location {
|
||||
@@ -31,9 +30,8 @@ func NewLocationFromImage(ref file.Reference, img *image.Image) Location {
|
||||
}
|
||||
|
||||
return Location{
|
||||
Path: string(ref.Path),
|
||||
LayerIndex: entry.Source.Metadata.Index,
|
||||
LayerID: entry.Source.Metadata.Digest,
|
||||
ref: ref,
|
||||
Path: string(ref.Path),
|
||||
FileSystemID: entry.Source.Metadata.Digest,
|
||||
ref: ref,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package source
|
||||
|
||||
type Metadata struct {
|
||||
Scope Scope // specific perspective to catalog
|
||||
Scheme Scheme // the source data scheme type (directory or image)
|
||||
ImageMetadata ImageMetadata // all image info (image only)
|
||||
Path string // the root path to be cataloged (directory only)
|
||||
}
|
||||
+7
-17
@@ -2,21 +2,15 @@ package source
|
||||
|
||||
import "strings"
|
||||
|
||||
type Scope string
|
||||
|
||||
const (
|
||||
UnknownScope Scope = iota
|
||||
SquashedScope
|
||||
AllLayersScope
|
||||
UnknownScope Scope = "UnknownScope"
|
||||
SquashedScope Scope = "Squashed"
|
||||
AllLayersScope Scope = "AllLayers"
|
||||
)
|
||||
|
||||
type Scope int
|
||||
|
||||
var optionStr = []string{
|
||||
"UnknownScope",
|
||||
"Squashed",
|
||||
"AllLayers",
|
||||
}
|
||||
|
||||
var Options = []Scope{
|
||||
var AllScopes = []Scope{
|
||||
SquashedScope,
|
||||
AllLayersScope,
|
||||
}
|
||||
@@ -32,9 +26,5 @@ func ParseScope(userStr string) Scope {
|
||||
}
|
||||
|
||||
func (o Scope) String() string {
|
||||
if int(o) >= len(optionStr) || o < 0 {
|
||||
return optionStr[0]
|
||||
}
|
||||
|
||||
return optionStr[o]
|
||||
return string(o)
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package source
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOptionStringerBoundary(t *testing.T) {
|
||||
var _ fmt.Stringer = Scope(0)
|
||||
|
||||
for _, c := range []int{-1, 0, 3} {
|
||||
option := Scope(c)
|
||||
if option.String() != UnknownScope.String() {
|
||||
t.Errorf("expected Scope(%d) to be unknown, found '%+v'", c, option)
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-25
@@ -15,29 +15,18 @@ import (
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
)
|
||||
|
||||
// ImageSource represents a data source that is a container image
|
||||
type ImageSource struct {
|
||||
Img *image.Image // the image object to be cataloged
|
||||
}
|
||||
|
||||
// DirSource represents a data source that is a filesystem directory tree
|
||||
type DirSource struct {
|
||||
Path string // the root path to be cataloged
|
||||
}
|
||||
|
||||
// Source is an object that captures the data source to be cataloged, configuration, and a specific resolver used
|
||||
// in cataloging (based on the data source and configuration)
|
||||
type Source struct {
|
||||
Scope Scope // specific perspective to catalog
|
||||
Resolver Resolver // a Resolver object to use in file path/glob resolution and file contents resolution
|
||||
Target interface{} // the specific source object to be cataloged
|
||||
Scheme Scheme // the source data scheme type (directory or image)
|
||||
Resolver Resolver // a Resolver object to use in file path/glob resolution and file contents resolution
|
||||
Image *image.Image // the image object to be cataloged (image only)
|
||||
Metadata Metadata
|
||||
}
|
||||
|
||||
type sourceDetector func(string) (image.Source, string, error)
|
||||
|
||||
// NewSource produces a Source based on userInput like dir: or image:tag
|
||||
func NewSource(userInput string, o Scope) (Source, func(), error) {
|
||||
// New produces a Source based on userInput like dir: or image:tag
|
||||
func New(userInput string, o Scope) (Source, func(), error) {
|
||||
fs := afero.NewOsFs()
|
||||
parsedScheme, location, err := detectScheme(fs, image.DetectSource, userInput)
|
||||
if err != nil {
|
||||
@@ -71,7 +60,7 @@ func NewSource(userInput string, o Scope) (Source, func(), error) {
|
||||
return Source{}, cleanup, fmt.Errorf("could not fetch image '%s': %w", location, err)
|
||||
}
|
||||
|
||||
s, err := NewFromImage(img, o)
|
||||
s, err := NewFromImage(img, o, location)
|
||||
if err != nil {
|
||||
return Source{}, cleanup, fmt.Errorf("could not populate source with image: %w", err)
|
||||
}
|
||||
@@ -87,16 +76,16 @@ func NewFromDirectory(path string) (Source, error) {
|
||||
Resolver: &DirectoryResolver{
|
||||
Path: path,
|
||||
},
|
||||
Target: DirSource{
|
||||
Path: path,
|
||||
Metadata: Metadata{
|
||||
Scheme: DirectoryScheme,
|
||||
Path: path,
|
||||
},
|
||||
Scheme: DirectoryScheme,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewFromImage creates a new source object tailored to catalog a given container image, relative to the
|
||||
// option given (e.g. all-layers, squashed, etc)
|
||||
func NewFromImage(img *image.Image, option Scope) (Source, error) {
|
||||
func NewFromImage(img *image.Image, option Scope, userImageStr string) (Source, error) {
|
||||
if img == nil {
|
||||
return Source{}, fmt.Errorf("no image given")
|
||||
}
|
||||
@@ -107,11 +96,12 @@ func NewFromImage(img *image.Image, option Scope) (Source, error) {
|
||||
}
|
||||
|
||||
return Source{
|
||||
Scope: option,
|
||||
Resolver: resolver,
|
||||
Target: ImageSource{
|
||||
Img: img,
|
||||
Image: img,
|
||||
Metadata: Metadata{
|
||||
Scope: option,
|
||||
Scheme: ImageScheme,
|
||||
ImageMetadata: NewImageMetadata(img, userImageStr),
|
||||
},
|
||||
Scheme: ImageScheme,
|
||||
}, nil
|
||||
}
|
||||
|
||||
+15
-14
@@ -9,41 +9,41 @@ import (
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
func TestNewScopeFromImageFails(t *testing.T) {
|
||||
func TestNewFromImageFails(t *testing.T) {
|
||||
t.Run("no image given", func(t *testing.T) {
|
||||
_, err := NewFromImage(nil, AllLayersScope)
|
||||
_, err := NewFromImage(nil, AllLayersScope, "")
|
||||
if err == nil {
|
||||
t.Errorf("expected an error condition but none was given")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewScopeFromImageUnknownOption(t *testing.T) {
|
||||
func TestNewFromImageUnknownOption(t *testing.T) {
|
||||
img := image.Image{}
|
||||
|
||||
t.Run("unknown option is an error", func(t *testing.T) {
|
||||
_, err := NewFromImage(&img, UnknownScope)
|
||||
_, err := NewFromImage(&img, UnknownScope, "")
|
||||
if err == nil {
|
||||
t.Errorf("expected an error condition but none was given")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewScopeFromImage(t *testing.T) {
|
||||
func TestNewFromImage(t *testing.T) {
|
||||
layer := image.NewLayer(nil)
|
||||
img := image.Image{
|
||||
Layers: []*image.Layer{layer},
|
||||
}
|
||||
|
||||
t.Run("create a new Locations object from image", func(t *testing.T) {
|
||||
_, err := NewFromImage(&img, AllLayersScope)
|
||||
_, err := NewFromImage(&img, AllLayersScope, "")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error when creating a new Locations from img: %+v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDirectoryScope(t *testing.T) {
|
||||
func TestNewFromDirectory(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
input string
|
||||
@@ -78,16 +78,16 @@ func TestDirectoryScope(t *testing.T) {
|
||||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
p, err := NewFromDirectory(test.input)
|
||||
src, err := NewFromDirectory(test.input)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("could not create NewDirScope: %+v", err)
|
||||
}
|
||||
if p.Target.(DirSource).Path != test.input {
|
||||
t.Errorf("mismatched stringer: '%s' != '%s'", p.Target.(DirSource).Path, test.input)
|
||||
if src.Metadata.Path != test.input {
|
||||
t.Errorf("mismatched stringer: '%s' != '%s'", src.Metadata.Path, test.input)
|
||||
}
|
||||
|
||||
refs, err := p.Resolver.FilesByPath(test.inputPaths...)
|
||||
refs, err := src.Resolver.FilesByPath(test.inputPaths...)
|
||||
if err != nil {
|
||||
t.Errorf("FilesByPath call produced an error: %+v", err)
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func TestDirectoryScope(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleFileContentsByRefContents(t *testing.T) {
|
||||
func TestMultipleFileContentsByLocation(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
input string
|
||||
@@ -136,7 +136,8 @@ func TestMultipleFileContentsByRefContents(t *testing.T) {
|
||||
}
|
||||
location := locations[0]
|
||||
|
||||
content, err := p.Resolver.FileContentsByLocation(location)
|
||||
contents, err := p.Resolver.MultipleFileContentsByLocation([]Location{location})
|
||||
content := contents[location]
|
||||
|
||||
if content != test.expected {
|
||||
t.Errorf("unexpected contents from file: '%s' != '%s'", content, test.expected)
|
||||
@@ -146,7 +147,7 @@ func TestMultipleFileContentsByRefContents(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleFileContentsByRefNoContents(t *testing.T) {
|
||||
func TestFilesByPathDoesNotExist(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
input string
|
||||
|
||||
Reference in New Issue
Block a user