From 11b2b1ab456f2fbccdabf2fa821e174c65a0eed3 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 12 May 2020 20:43:46 -0400 Subject: [PATCH] add scope feature + lint fixes --- .golangci.yaml | 20 ++++----- cmd/root.go | 9 ++-- cmd/version.go | 6 +-- go.mod | 2 +- imgbom/scope/option.go | 23 ++++++++++ imgbom/scope/option_test.go | 17 +++++++ imgbom/scope/scope.go | 44 ++++++++++++++++++ imgbom/scope/scope_test.go | 90 +++++++++++++++++++++++++++++++++++++ main.go | 2 +- 9 files changed, 194 insertions(+), 19 deletions(-) create mode 100644 imgbom/scope/option.go create mode 100644 imgbom/scope/option_test.go create mode 100644 imgbom/scope/scope.go create mode 100644 imgbom/scope/scope_test.go diff --git a/.golangci.yaml b/.golangci.yaml index 765941f70..bc922b9c3 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -9,41 +9,41 @@ linters: - dogsled - dupl - errcheck -# - funlen - - gochecknoinits -# - gocognit + - funlen + - gocognit - goconst -# - gocritic + - gocritic - gocyclo - gofmt - goimports -# - golint + - golint - gomnd - goprintffuncname - gosec - gosimple - govet - ineffassign -# - interfacer -# - lll + - interfacer + - lll - misspell - nakedret - nolintlint - rowserrcheck -# - scopelint + - scopelint - staticcheck - structcheck -# - stylecheck + - stylecheck - typecheck - unconvert - unparam - unused - varcheck -# - whitespace + - whitespace - prealloc - asciicheck # do not enable... +# - gochecknoinits # - gochecknoglobals # - godot # - godox diff --git a/cmd/root.go b/cmd/root.go index 8fde03ad3..92e77560c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,21 +2,22 @@ package cmd import ( "fmt" - "github.com/spf13/cobra" "os" + + "github.com/spf13/cobra" ) const ApplicationName = "imgbom" var rootOptions struct { - cfgFile string + cfgFile string } var rootCmd = &cobra.Command{ Use: ApplicationName, Short: "A container image BOM tool", Long: `todo.`, - Run: doRunCmd, + Run: doRunCmd, } func Execute() { @@ -37,4 +38,4 @@ func loadApplicationConfig() { func doRunCmd(cmd *cobra.Command, args []string) { -} \ No newline at end of file +} diff --git a/cmd/version.go b/cmd/version.go index 606857b2b..f5e5141ea 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -1,9 +1,9 @@ package cmd import ( -"fmt" + "fmt" -"github.com/spf13/cobra" + "github.com/spf13/cobra" ) type Version struct { @@ -30,4 +30,4 @@ func SetVersion(v *Version) { func printVersion(cmd *cobra.Command, args []string) { fmt.Printf("%s %s\n", ApplicationName, version.Version) -} \ No newline at end of file +} diff --git a/go.mod b/go.mod index c2fb0232a..dbb2cb9f1 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/anchore/imgbom go 1.14 require ( - github.com/anchore/stereoscope v0.0.0-20200512135733-57b1df994b57 // indirect + github.com/anchore/stereoscope v0.0.0-20200512135733-57b1df994b57 github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/mitchellh/mapstructure v1.3.0 // indirect github.com/pelletier/go-toml v1.7.0 // indirect diff --git a/imgbom/scope/option.go b/imgbom/scope/option.go new file mode 100644 index 000000000..29fa4bdec --- /dev/null +++ b/imgbom/scope/option.go @@ -0,0 +1,23 @@ +package scope + +const ( + UnknownScope Option = iota + SquashedScope + AllLayersScope +) + +type Option int + +var optionStr = []string{ + "UnknownScope", + "Squashed", + "AllLayers", +} + +func (o Option) String() string { + if int(o) >= len(optionStr) || int(o) < 0 { + return optionStr[0] + } + + return optionStr[o] +} diff --git a/imgbom/scope/option_test.go b/imgbom/scope/option_test.go new file mode 100644 index 000000000..a684c2260 --- /dev/null +++ b/imgbom/scope/option_test.go @@ -0,0 +1,17 @@ +package scope + +import ( + "fmt" + "testing" +) + +func TestOptionStringerBoundary(t *testing.T) { + var _ fmt.Stringer = Option(0) + + for _, c := range []int{-1, 0, 3} { + option := Option(c) + if option.String() != UnknownScope.String() { + t.Errorf("expected Option(%d) to be unknown, found '%+v'", c, option) + } + } +} diff --git a/imgbom/scope/scope.go b/imgbom/scope/scope.go new file mode 100644 index 000000000..f5833a6f2 --- /dev/null +++ b/imgbom/scope/scope.go @@ -0,0 +1,44 @@ +package scope + +import ( + "fmt" + + "github.com/anchore/stereoscope/pkg/image" + "github.com/anchore/stereoscope/pkg/tree" +) + +type Scope struct { + Option Option + Trees []*tree.FileTree +} + +func NewScope(img *image.Image, option Option) (Scope, error) { + var trees = make([]*tree.FileTree, 0) + + if img == nil { + return Scope{}, fmt.Errorf("no image given") + } + + switch option { + case SquashedScope: + if img.SquashedTree == nil { + return Scope{}, fmt.Errorf("the image does not have have a squashed tree") + } + trees = append(trees, img.SquashedTree) + + case AllLayersScope: + if len(img.Layers) == 0 { + return Scope{}, fmt.Errorf("the image does not contain any layers") + } + for _, layer := range img.Layers { + trees = append(trees, layer.Tree) + } + default: + return Scope{}, fmt.Errorf("bad option provided: %+v", option) + } + + return Scope{ + Option: option, + Trees: trees, + }, nil +} diff --git a/imgbom/scope/scope_test.go b/imgbom/scope/scope_test.go new file mode 100644 index 000000000..5bead2430 --- /dev/null +++ b/imgbom/scope/scope_test.go @@ -0,0 +1,90 @@ +package scope + +import ( + "github.com/anchore/stereoscope/pkg/image" + "github.com/anchore/stereoscope/pkg/tree" + "testing" +) + +func testScopeImage(t *testing.T) *image.Image { + t.Helper() + + one := image.NewLayer(0, nil) + one.Tree = tree.NewFileTree() + one.Tree.AddPath("/tree/first/path.txt") + + two := image.NewLayer(1, nil) + two.Tree = tree.NewFileTree() + two.Tree.AddPath("/tree/second/path.txt") + + i := image.NewImage(nil) + i.Layers = []image.Layer{one, two} + err := i.Squash() + if err != nil { + t.Fatal("could not squash test image trees") + } + + return i +} + +func TestScope(t *testing.T) { + refImg := testScopeImage(t) + + cases := []struct { + name string + img *image.Image + option Option + expectedTrees []*tree.FileTree + err bool + }{ + { + name: "AllLayersGoCase", + option: AllLayersScope, + img: testScopeImage(t), + expectedTrees: []*tree.FileTree{refImg.Layers[0].Tree, refImg.Layers[1].Tree}, + }, + { + name: "SquashedGoCase", + option: SquashedScope, + img: testScopeImage(t), + expectedTrees: []*tree.FileTree{refImg.SquashedTree}, + }, + { + name: "MissingImage", + option: SquashedScope, + err: true, + }, + { + name: "MissingSquashedTree", + option: SquashedScope, + img: image.NewImage(nil), + err: true, + }, + { + name: "NoLayers", + option: AllLayersScope, + img: image.NewImage(nil), + err: true, + }, + } + + for _, c := range cases { + actual, err := NewScope(c.img, c.option) + if err == nil && c.err { + t.Fatal("expected an error but did not find one") + } else if err != nil && !c.err { + t.Fatal("expected no error but found one:", err) + } + + if len(actual.Trees) != len(c.expectedTrees) { + t.Fatalf("mismatched tree lengths: %d!=%d", len(actual.Trees), len(c.expectedTrees)) + } + + for idx, at := range actual.Trees { + if !at.Equal(c.expectedTrees[idx]) { + t.Error("mismatched tree @ idx", idx) + } + } + } + +} diff --git a/main.go b/main.go index 0b1b0f262..4529faf7d 100644 --- a/main.go +++ b/main.go @@ -18,4 +18,4 @@ func main() { }) cmd.Execute() -} \ No newline at end of file +}