From 17b4b26fbbe7b1094d702eb2dc965602ffb93cf3 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 20 May 2020 18:29:06 -0400 Subject: [PATCH] update sterescope + add json presenter --- cmd/root.go | 27 ++++- go.mod | 12 +- go.sum | 36 ++++-- imgbom/analyzer/analyzer.go | 1 + imgbom/analyzer/dpkg/analyzer.go | 1 - imgbom/analyzer/dummy/analyzer.go | 30 ----- imgbom/pkg/catalog.go | 36 +++++- imgbom/pkg/dummy_package.go | 6 - imgbom/pkg/package.go | 1 + imgbom/pkg/type.go | 19 ++- imgbom/presenter/json/presenter.go | 114 ++++++++++++++++++ imgbom/presenter/json/presenter_test.go | 90 ++++++++++++++ .../test-fixtures/image-simple/Dockerfile | 6 + .../test-fixtures/image-simple/file-1.txt | 1 + .../test-fixtures/image-simple/file-2.txt | 1 + .../target/really/nested/file-3.txt | 2 + .../snapshot/TestJsonPresenter.golden | 1 + .../anchore-fixture-image-simple.golden | Bin 0 -> 23552 bytes imgbom/presenter/option.go | 25 ++++ imgbom/presenter/presenter.go | 23 ++++ imgbom/scope/scope_test.go | 7 +- 21 files changed, 372 insertions(+), 67 deletions(-) delete mode 100644 imgbom/analyzer/dummy/analyzer.go delete mode 100644 imgbom/pkg/dummy_package.go create mode 100644 imgbom/presenter/json/presenter.go create mode 100644 imgbom/presenter/json/presenter_test.go create mode 100644 imgbom/presenter/json/test-fixtures/image-simple/Dockerfile create mode 100644 imgbom/presenter/json/test-fixtures/image-simple/file-1.txt create mode 100644 imgbom/presenter/json/test-fixtures/image-simple/file-2.txt create mode 100644 imgbom/presenter/json/test-fixtures/image-simple/target/really/nested/file-3.txt create mode 100644 imgbom/presenter/json/test-fixtures/snapshot/TestJsonPresenter.golden create mode 100644 imgbom/presenter/json/test-fixtures/snapshot/anchore-fixture-image-simple.golden create mode 100644 imgbom/presenter/option.go create mode 100644 imgbom/presenter/presenter.go diff --git a/cmd/root.go b/cmd/root.go index 62a6ad3bf..8a50ad450 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,6 +4,8 @@ import ( "fmt" "os" + "github.com/anchore/imgbom/imgbom/presenter" + "github.com/anchore/imgbom/internal" "github.com/anchore/imgbom/imgbom" @@ -18,6 +20,7 @@ const ApplicationName = "imgbom" var rootOptions struct { cfgFile string scope string + output string } var rootCmd = &cobra.Command{ @@ -50,6 +53,10 @@ func init() { // scan options rootCmd.Flags().StringVarP(&rootOptions.scope, "scope", "s", scope.AllLayersScope.String(), fmt.Sprintf("selection of layers to analyze, options=%v", scope.Options)) + + // output & formatting options + rootCmd.Flags().StringVarP(&rootOptions.output, "output", "o", presenter.JSONOption.String(), + fmt.Sprintf("report output formatter, options=%v", presenter.Options)) } func loadApplicationConfig() { @@ -57,10 +64,10 @@ func loadApplicationConfig() { } func doRunCmd(cmd *cobra.Command, args []string) { - img, err := stereoscope.GetImage(args[0]) - if err != nil { + var pres = presenter.GetPresenter(rootOptions.output) + if pres == nil { // TODO: replace with log and exit - panic(err) + panic("could not determine presenter") } scopeOption := scope.ParseOption(rootOptions.scope) @@ -69,12 +76,22 @@ func doRunCmd(cmd *cobra.Command, args []string) { panic(scopeOption) } + img, err := stereoscope.GetImage(args[0]) + if err != nil { + // TODO: replace with log and exit + panic(err) + } + defer stereoscope.Cleanup() + catalog, err := imgbom.CatalogImage(img, scopeOption) if err != nil { // TODO: replace with log and exit panic(err) } - // TODO: remove this with presenter implementation - fmt.Printf("%+v\n", catalog) + err = pres.Present(os.Stdout, img, catalog) + if err != nil { + // TODO: replace with log and exit + panic(err) + } } diff --git a/go.mod b/go.mod index 75ed9a058..e73b810a8 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,17 @@ module github.com/anchore/imgbom go 1.14 require ( - github.com/anchore/stereoscope v0.0.0-20200518155435-f6c722e4572b + github.com/anchore/go-testutils v0.0.0-20200520222037-edc2bf1864fe + github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e github.com/go-test/deep v1.0.6 github.com/golang/protobuf v1.4.2 // indirect github.com/hashicorp/go-multierror v1.1.0 - github.com/mitchellh/mapstructure v1.1.2 + github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 + github.com/mitchellh/mapstructure v1.3.0 github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/sergi/go-diff v1.1.0 github.com/sirupsen/logrus v1.6.0 // indirect github.com/spf13/cobra v1.0.0 - golang.org/x/net v0.0.0-20200513185701-a91f0712d120 // indirect - golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 // indirect - google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587 // indirect + golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 // indirect + google.golang.org/genproto v0.0.0-20200519141106-08726f379972 // indirect ) diff --git a/go.sum b/go.sum index 38291def2..24f1f23ac 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v38.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= @@ -36,9 +37,12 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/anchore/stereoscope v0.0.0-20200512135733-57b1df994b57/go.mod h1:3Ty8a3pRmrp/VIC2NQnlXG4rr3Bh6LYOTHJgcob14Nw= -github.com/anchore/stereoscope v0.0.0-20200518155435-f6c722e4572b h1:kuCcqn0R6QOGJiMRotbnE/tQ2VrcpkFXCjcz0YGpb6w= -github.com/anchore/stereoscope v0.0.0-20200518155435-f6c722e4572b/go.mod h1:TYHkOkwGBJPopUbfoqzuj2+n/xtuL6PApHs0YuNOcRQ= +github.com/anchore/go-test-utils v0.0.0-20200520221427-04c6897623bb h1:zWVhvAoAWqxs5i2tSUswY8ZNfPWLqTw8gPho81+WxCI= +github.com/anchore/go-test-utils v0.0.0-20200520221427-04c6897623bb/go.mod h1:f14qo67+xDFlUkHaXd4K5QLmcUa3z5vCS6vaF8TDKgo= +github.com/anchore/go-testutils v0.0.0-20200520222037-edc2bf1864fe h1:YMXe4RA3qy4Ri5fmGQii/Gn+Pxv3oBfiS/LqzeOVuwo= +github.com/anchore/go-testutils v0.0.0-20200520222037-edc2bf1864fe/go.mod h1:D3rc2L/q4Hcp9eeX6AIJH4Q+kPjOtJCFhG9za90j+nU= +github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e h1:QBwtrM0MXi0z+GcHk3RoSyzaQ+CLgas0bC/uOd1P+PQ= +github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e/go.mod h1:bkyLl5VITnrmgErv4S1vDfVz/TGAZ5il6161IQo7w2g= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -179,6 +183,7 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +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/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -224,6 +229,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= +github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -237,11 +244,14 @@ github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88J github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.0 h1:iDwIio/3gk2QtLLEsqU5lInaMzos0hDTz8a6lazSFVw= +github.com/mitchellh/mapstructure v1.3.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -302,6 +312,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -404,8 +416,10 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5 h1:WQ8q63x+f/zpC8Ac1s9wLElVoHhm32p6tudrU72n1QA= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200519113804-d87ec0cfa476 h1:E7ct1C6/33eOdrGZKMoyntcEvs2dwZnDe30crG5vpYU= +golang.org/x/net v0.0.0-20200519113804-d87ec0cfa476/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -438,10 +452,8 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI= -golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -449,6 +461,7 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -486,8 +499,8 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84 h1:pSLkPbrjnPyLDYUO2VM9mDLqo2V6CFBY84lFSZAfoi4= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587 h1:1Ym+vvUpq1ZHvxzn34gENJX8U4aKO+vhy2P/2+Xl6qQ= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +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/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.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -511,6 +524,7 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4 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 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= diff --git a/imgbom/analyzer/analyzer.go b/imgbom/analyzer/analyzer.go index 2602fc890..4eb9f6955 100644 --- a/imgbom/analyzer/analyzer.go +++ b/imgbom/analyzer/analyzer.go @@ -7,6 +7,7 @@ import ( ) type Analyzer interface { + // TODO: add ID / Name for analyze for uniquely identifying this analyzer type SelectFiles([]*tree.FileTree) []file.Reference // NOTE: one of the errors which is returned is "IterationNeeded", which indicates to the driver to // continue with another Select/Analyze pass diff --git a/imgbom/analyzer/dpkg/analyzer.go b/imgbom/analyzer/dpkg/analyzer.go index 0ee2994dc..380c86e4c 100644 --- a/imgbom/analyzer/dpkg/analyzer.go +++ b/imgbom/analyzer/dpkg/analyzer.go @@ -54,7 +54,6 @@ func (a *Analyzer) Analyze(contents map[file.Reference]string) ([]pkg.Package, e Metadata: entry, }) } - } return packages, nil } diff --git a/imgbom/analyzer/dummy/analyzer.go b/imgbom/analyzer/dummy/analyzer.go deleted file mode 100644 index 26ba417d4..000000000 --- a/imgbom/analyzer/dummy/analyzer.go +++ /dev/null @@ -1,30 +0,0 @@ -package dummy - -import ( - "github.com/anchore/imgbom/imgbom/pkg" - "github.com/anchore/stereoscope/pkg/file" - "github.com/anchore/stereoscope/pkg/tree" -) - -// TODO: delete me - -type Analyzer struct{} - -func NewAnalyzer() *Analyzer { - return &Analyzer{} -} - -func (a *Analyzer) SelectFiles(trees []*tree.FileTree) []file.Reference { - return []file.Reference{*trees[0].File("/etc/centos-release")} -} - -func (a *Analyzer) Analyze(contents map[file.Reference]string) ([]pkg.Package, error) { - return []pkg.Package{ - { - Name: "dummy", - Version: "1.0.0", - Type: pkg.DebPkg, - Metadata: pkg.DummyPackage{Extra: "some extra metadata"}, - }, - }, nil -} diff --git a/imgbom/pkg/catalog.go b/imgbom/pkg/catalog.go index 18455dddd..94f277f68 100644 --- a/imgbom/pkg/catalog.go +++ b/imgbom/pkg/catalog.go @@ -4,19 +4,45 @@ package pkg type Catalog struct { // TODO: catalog by package ID for potential indexing - Packages map[Type][]Package + packages map[Type][]Package } func NewCatalog() Catalog { return Catalog{ - Packages: make(map[Type][]Package), + packages: make(map[Type][]Package), } } func (c *Catalog) Add(p Package) { - _, ok := c.Packages[p.Type] + _, ok := c.packages[p.Type] if !ok { - c.Packages[p.Type] = make([]Package, 0) + c.packages[p.Type] = make([]Package, 0) } - c.Packages[p.Type] = append(c.Packages[p.Type], p) + c.packages[p.Type] = append(c.packages[p.Type], p) } + +func (c *Catalog) Enumerate(types ...Type) <-chan Package { + channel := make(chan Package) + go func() { + defer close(channel) + for ty, packages := range c.packages { + if len(types) != 0 { + found := false + typeCheck: + for _, t := range types { + if t == ty { + found = true + break typeCheck + } + } + if !found { + continue + } + } + for _, p := range packages { + channel <- p + } + } + }() + return channel +} \ No newline at end of file diff --git a/imgbom/pkg/dummy_package.go b/imgbom/pkg/dummy_package.go deleted file mode 100644 index 868968f67..000000000 --- a/imgbom/pkg/dummy_package.go +++ /dev/null @@ -1,6 +0,0 @@ -package pkg - -// TODO: delete me -type DummyPackage struct { - Extra string -} diff --git a/imgbom/pkg/package.go b/imgbom/pkg/package.go index 7380c9116..fc1dbb030 100644 --- a/imgbom/pkg/package.go +++ b/imgbom/pkg/package.go @@ -4,6 +4,7 @@ import "github.com/anchore/stereoscope/pkg/file" // TODO: add package ID (random/incremental) +// TODO: add field to trace which analyzer detected this type Package struct { Name string Version string diff --git a/imgbom/pkg/type.go b/imgbom/pkg/type.go index 0de13947f..69ae13f45 100644 --- a/imgbom/pkg/type.go +++ b/imgbom/pkg/type.go @@ -14,4 +14,21 @@ const ( type Type uint -// TODO: stringer... +var typeStr = []string{ + "UnknownPackage", + "apk", + "deb", + "java", + "node", + "pacman", + "python", + "rpm", + "ruby", +} + +func (t Type) String() string { + if int(t) >= len(typeStr) { + return typeStr[0] + } + return typeStr[t] +} diff --git a/imgbom/presenter/json/presenter.go b/imgbom/presenter/json/presenter.go new file mode 100644 index 000000000..6d06c4ecb --- /dev/null +++ b/imgbom/presenter/json/presenter.go @@ -0,0 +1,114 @@ +package json + +import ( + "encoding/json" + "io" + + "github.com/anchore/imgbom/imgbom/pkg" + stereoscopeImg "github.com/anchore/stereoscope/pkg/image" +) + +type Presenter struct{} + +func NewPresenter() *Presenter { + return &Presenter{} +} + +type document struct { + Image image `json:"image"` + Artifacts []artifact `json:"artifacts"` +} + +type image struct { + Layers []layer `json:"layers"` + Size int64 `json:"size"` + Digest string `json:"digest"` + MediaType string `json:"mediaType"` + Tags []string `json:"tags"` +} + +type layer struct { + MediaType string `json:"mediaType"` + Digest string `json:"digest"` + Size int64 `json:"size"` +} + +type source struct { + Source string `json:"source"` + Layer int `json:"layer"` + Effects []string `json:"effects"` +} + +type artifact struct { + Name string `json:"name"` + Version string `json:"version"` + Type string `json:"type"` + Analyzer string `json:"analyzer"` + Sources []source `json:"sources"` + Metadata interface{} `json:"metadata"` +} + +func (pres *Presenter) Present(output io.Writer, img *stereoscopeImg.Image, catalog pkg.Catalog) error { + tags := make([]string, len(img.Metadata.Tags)) + for idx, tag := range img.Metadata.Tags { + tags[idx] = tag.String() + } + + doc := document{ + Image: image{ + Digest: img.Metadata.Digest, + Size: img.Metadata.Size, + MediaType: string(img.Metadata.MediaType), + Tags: tags, + Layers: make([]layer, len(img.Layers)), + }, + Artifacts: make([]artifact, 0), + } + + // populate image... + for idx, l := range img.Layers { + doc.Image.Layers[idx] = layer{ + MediaType: string(l.Metadata.MediaType), + Digest: l.Metadata.Digest, + Size: l.Metadata.Size, + } + } + + // populate artifacts... + for p := range catalog.Enumerate() { + art := artifact{ + Name: p.Name, + Version: p.Version, + Type: p.Type.String(), + Analyzer: "TODO", // TODO + Sources: make([]source, len(p.Source)), + Metadata: p.Metadata, + } + + for idx, src := range p.Source { + fileMetadata, err := img.FileCatalog.Get(src) + if err != nil { + // TODO: replace + panic(err) + } + + srcObj := source{ + Source: "", + Layer: int(fileMetadata.Source.Metadata.Index), + Effects: []string{}, // TODO + } + art.Sources[idx] = srcObj + } + + doc.Artifacts = append(doc.Artifacts, art) + } + + bytes, err := json.Marshal(&doc) + if err != nil { + // TODO: replace + panic(err) + } + + _, err = output.Write(bytes) + return err +} diff --git a/imgbom/presenter/json/presenter_test.go b/imgbom/presenter/json/presenter_test.go new file mode 100644 index 000000000..646dd6a41 --- /dev/null +++ b/imgbom/presenter/json/presenter_test.go @@ -0,0 +1,90 @@ +package json + +import ( + "bytes" + "flag" + "testing" + + "github.com/anchore/go-testutils" + "github.com/anchore/imgbom/imgbom/pkg" + "github.com/anchore/stereoscope/pkg/file" + "github.com/sergi/go-diff/diffmatchpatch" +) + +var update = flag.Bool("update", false, "update the *.golden files for json presenters") + +// TODO: add a JSON schema and write a test that validates output against the schema +// func validateAgainstV1Schema(t *testing.T, json string) { +// fullSchemaPath, err := filepath.Abs("v1-schema.json") +// if err != nil { +// t.Fatal("could not get path to schema:", err) +// } +// schemaLoader := gojsonschema.NewReferenceLoader(fmt.Sprintf("file://%s", fullSchemaPath)) +// documentLoader := gojsonschema.NewStringLoader(json) + +// result, err := gojsonschema.Validate(schemaLoader, documentLoader) +// if err != nil { +// t.Fatal("unable to validate json schema:", err.Error()) +// } + +// if !result.Valid() { +// t.Errorf("failed json schema validation:") +// for _, desc := range result.Errors() { +// t.Errorf(" - %s\n", desc) +// } +// } +// } + +func TestJsonPresenter(t *testing.T) { + pres := NewPresenter() + var buffer bytes.Buffer + + testImage := "image-simple" + + if *update { + testutils.UpdateGoldenFixtureImage(t, testImage) + } + + catalog := pkg.NewCatalog() + img := testutils.GetGoldenFixtureImage(t, testImage) + + // populate catalog with test data + catalog.Add(pkg.Package{ + Name: "package-1", + Version: "1.0.1", + Source: []file.Reference{ + *img.SquashedTree.File("/somefile-1.txt"), + }, + Type: pkg.DebPkg, + }) + catalog.Add(pkg.Package{ + Name: "package-2", + Version: "2.0.1", + Source: []file.Reference{ + *img.SquashedTree.File("/somefile-2.txt"), + }, + Type: pkg.DebPkg, + }) + + // run presenter + err := pres.Present(&buffer, img, catalog) + if err != nil { + t.Fatal(err) + } + actual := buffer.Bytes() + + if *update { + testutils.UpdateGoldenFileContents(t, actual) + } + + var expected = testutils.GetGoldenFileContents(t) + + if !bytes.Equal(expected, actual) { + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(string(actual), string(expected), true) + t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs)) + } + + // TODO: add me back in when there is a JSON schema + // validateAgainstV1Schema(t, string(actual)) +} diff --git a/imgbom/presenter/json/test-fixtures/image-simple/Dockerfile b/imgbom/presenter/json/test-fixtures/image-simple/Dockerfile new file mode 100644 index 000000000..62fb151e4 --- /dev/null +++ b/imgbom/presenter/json/test-fixtures/image-simple/Dockerfile @@ -0,0 +1,6 @@ +# Note: changes to this file will result in updating several test values. Consider making a new image fixture instead of editing this one. +FROM scratch +ADD file-1.txt /somefile-1.txt +ADD file-2.txt /somefile-2.txt +# note: adding a directory will behave differently on docker engine v18 vs v19 +ADD target / diff --git a/imgbom/presenter/json/test-fixtures/image-simple/file-1.txt b/imgbom/presenter/json/test-fixtures/image-simple/file-1.txt new file mode 100644 index 000000000..985d3408e --- /dev/null +++ b/imgbom/presenter/json/test-fixtures/image-simple/file-1.txt @@ -0,0 +1 @@ +this file has contents \ No newline at end of file diff --git a/imgbom/presenter/json/test-fixtures/image-simple/file-2.txt b/imgbom/presenter/json/test-fixtures/image-simple/file-2.txt new file mode 100644 index 000000000..396d08bbc --- /dev/null +++ b/imgbom/presenter/json/test-fixtures/image-simple/file-2.txt @@ -0,0 +1 @@ +file-2 contents! \ No newline at end of file diff --git a/imgbom/presenter/json/test-fixtures/image-simple/target/really/nested/file-3.txt b/imgbom/presenter/json/test-fixtures/image-simple/target/really/nested/file-3.txt new file mode 100644 index 000000000..f85472c93 --- /dev/null +++ b/imgbom/presenter/json/test-fixtures/image-simple/target/really/nested/file-3.txt @@ -0,0 +1,2 @@ +another file! +with lines... \ No newline at end of file diff --git a/imgbom/presenter/json/test-fixtures/snapshot/TestJsonPresenter.golden b/imgbom/presenter/json/test-fixtures/snapshot/TestJsonPresenter.golden new file mode 100644 index 000000000..ab63044a8 --- /dev/null +++ b/imgbom/presenter/json/test-fixtures/snapshot/TestJsonPresenter.golden @@ -0,0 +1 @@ +{"image":{"layers":[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:056c0789fa9ad629ceae6d09713fb035f84115af3c4a88a43aa60f13bc683053","size":22},{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:b461c48116592c570a66fed71d5b09662a8172e168b7938cf317af47872cdc9b","size":16},{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:00b80053e05c01da485015610d288ce3185fac00d251e2ada02b45a7a7c5f589","size":27}],"size":65,"digest":"sha256:3c53d2d891940f8d8e95acb77b58752f54dc5de9d91d19dd90ced2db76256cea","mediaType":"application/vnd.docker.distribution.manifest.v2+json","tags":["anchore-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7"]},"artifacts":[{"name":"package-1","version":"1.0.1","type":"deb","analyzer":"TODO","sources":[{"source":"","layer":0,"effects":[]}],"metadata":null},{"name":"package-2","version":"2.0.1","type":"deb","analyzer":"TODO","sources":[{"source":"","layer":1,"effects":[]}],"metadata":null}]} \ No newline at end of file diff --git a/imgbom/presenter/json/test-fixtures/snapshot/anchore-fixture-image-simple.golden b/imgbom/presenter/json/test-fixtures/snapshot/anchore-fixture-image-simple.golden new file mode 100644 index 0000000000000000000000000000000000000000..739b614870d13721615fb355e03552da1525bc68 GIT binary patch literal 23552 zcmeHPZExc?63*xT3Rm~#3d9bF?=)~9_PPtSK+!GQTo34Cv7q>}*437BlG1Jv3CWH|VzcPsCC;1H*4U(> zlJ39UP;2{&kz?!nr$kaX8V%OTV~x7G_ZQbc zOb&wb4ch7d3qxRV|Nl=@l^fn^Q~y_--tYfhsMP;Az`B#$ms#PnX*Sdhvg7Q`)}d@s zmXN}EXPAw|G7=!?M)3efIp;vq7L0PU2whdjwz(_L& zI%aSRW=aVDjETU%U8$;UZBd5$10KcVV>Z3azNwnFw2OdMvBc|Yfdy{Q|J8*0`p&Di zwQlxP>x(j*Mmul9akjcvo#T7lzZhz)y1aPWUaXGao`rHL z&yTaO7x-G&hu<#j$7Mr*uIA^9(A?blcUAvdlpkLg%htXr+xp_HDmr|x>%J}jd0xy{ zqQ0|vm@lhe9cL9DX7i#v|CC)lZYDz&e=@fhp`LVes0TkA`al1_`5y(QWa|G90=mN+ zO|=NIn1?Z)w4d4?3ce$%*S~yZ{zv=yzfc4u>&gK-03zxC4upFV4o=X~ubuPON3d#Sn<#j0l-C ziQR?>5@Ynr<&J`IO6QItd2YRS0vrOlW7qC8b9#5Fp?BE{D(s}0A*$mJ+Q0q1tj@j~ zy?Xt6KFo(H9XgLK++X?X|{3pwDP2pzn4qoN$0 zj5>q3@yuVY6|4vnN@&N3j43&#WD0T$W+J&kB#0KjAV|J)zamBQTd13&Lb3xXl?h>! zj%Jw^O(v+k>)r*@jogo+qH6dv!r%a&aN z$3I7uHqjdEB{et|2+|XThsB&Q5j6)8HZsSp)|NACC5gatCp7}gQ0h`rj%MJv29TnQ zMJZyXjKO1+QRKu(NiAxC29P>ehG`cWC>y!bigq+(ek+NPToaUy1GFRHEgCHWA%S?R zwF?Zih}IE26%eTPme8CFtE_S&3T?9YR~y)}gM1|)^dUpZ+P%boaRkCJaEkvPz!C=Q ze=qT0pfbgO51{>{qZI!=2+S|`A%pw>aQ`0{d&qn7UjU>8()y1BSj{s2JJbwry@KAs zNHN>vY=1G^U7E#E#qTHnO9@Ny-(9NtL0RkW^@GI*-Pj!e?Z^MM_^+gC{2v7RmX%xN zfJPl#Gn(0E6$ak2aw?V(NC+eZ5(1ws0$r>`=O)sectHA905g!|pMW7)2+|-!Pr9%d zF6HErMr2|Q#)^@k!BxB5hQ08Apt!*KU&{a3zgZ2||6ce%B7mp)-~P3q_`gG7{F0wB zxc?95|1j1g?(u&ZKurAqLCj{!{|z+**Cr(Q zBuxzjGXuH9>_AK&RG6~qF?-ONsJc|m@4Eem`bX_+SwFNVT)N3vmx+WRk_ zq~HyOYt&U!U>0Z*nhiyteS1^?$8@|qdqMX7A2UtU{Qud2KcOP#|3A`0yJXP8opd_b Z{= len(optionStr) || int(o) < 0 { + return optionStr[0] + } + + return optionStr[o] +} \ No newline at end of file diff --git a/imgbom/presenter/presenter.go b/imgbom/presenter/presenter.go new file mode 100644 index 000000000..c86a999c6 --- /dev/null +++ b/imgbom/presenter/presenter.go @@ -0,0 +1,23 @@ +package presenter + +import ( + "io" + "strings" + + "github.com/anchore/imgbom/imgbom/pkg" + "github.com/anchore/imgbom/imgbom/presenter/json" + "github.com/anchore/stereoscope/pkg/image" +) + +type Presenter interface { + Present(io.Writer, *image.Image, pkg.Catalog) error +} + +func GetPresenter(userStr string) Presenter { + switch strings.ToLower(userStr) { + case JSONOption.String(): + return json.NewPresenter() + default: + return nil + } +} \ No newline at end of file diff --git a/imgbom/scope/scope_test.go b/imgbom/scope/scope_test.go index 5bead2430..3131f0316 100644 --- a/imgbom/scope/scope_test.go +++ b/imgbom/scope/scope_test.go @@ -1,19 +1,20 @@ package scope import ( + "testing" + "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 := image.NewLayer(nil) one.Tree = tree.NewFileTree() one.Tree.AddPath("/tree/first/path.txt") - two := image.NewLayer(1, nil) + two := image.NewLayer(nil) two.Tree = tree.NewFileTree() two.Tree.AddPath("/tree/second/path.txt")