move source metadata upstream and fix tests

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman
2020-11-17 12:37:12 -05:00
parent aa0d444fd4
commit 6f7a4fd3e4
36 changed files with 305 additions and 294 deletions
+44
View File
@@ -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
}
+6 -8
View File
@@ -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,
}
}
+8
View File
@@ -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
View File
@@ -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)
}
-17
View File
@@ -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
View File
@@ -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
View File
@@ -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