Compare commits

...
Author SHA1 Message Date
Alex Goodman 206fd8b7d3 working
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-08 08:37:40 -04:00
Alex Goodman c348d97fe0 not working
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-05 11:16:46 -04:00
Alex Goodman 30bb7cf992 pin stereoscope to dev branch
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-03 18:34:23 -04:00
Alex Goodman 54e0ee4c47 finish repository + make syft client
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-03 18:32:20 -04:00
Alex Goodman c231a9d7cb add collection
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-03 09:28:45 -04:00
Alex Goodman 3e27b6e93f rough draft for plugin system
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-09-02 23:34:00 -04:00
Alex Goodman 63ee5ba098 refactor catalogers to consume io.Readers instead of strings
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
2020-08-31 17:41:05 -04:00
43 changed files with 2225 additions and 154 deletions
+1 -1
View File
@@ -31,7 +31,6 @@ linters:
- misspell - misspell
- nakedret - nakedret
- nolintlint - nolintlint
- prealloc
- rowserrcheck - rowserrcheck
- scopelint - scopelint
- staticcheck - staticcheck
@@ -55,5 +54,6 @@ linters:
# - lll # without a way to specify per-line exception cases, this is not usable # - lll # without a way to specify per-line exception cases, this is not usable
# - maligned # this is an excellent linter, but tricky to optimize and we are not sensitive to memory layout optimizations # - maligned # this is an excellent linter, but tricky to optimize and we are not sensitive to memory layout optimizations
# - nestif # - nestif
# - prealloc # this unnecessarily requires preallocations for slices
# - testpackage # - testpackage
# - wsl # - wsl
+3
View File
@@ -169,6 +169,9 @@ check-pipeline: ## Run local CircleCI pipeline locally (sanity check)
circleci local execute -c .tmp/circleci.yml --job "Unit & Integration Tests (go-latest)" circleci local execute -c .tmp/circleci.yml --job "Unit & Integration Tests (go-latest)"
@printf '$(SUCCESS)Pipeline checks pass!$(RESET)\n' @printf '$(SUCCESS)Pipeline checks pass!$(RESET)\n'
proto:
protoc -I syft/plugin/proto/ syft/plugin/proto/cataloger.proto --go_out=plugins=grpc:syft/plugin/proto/
.PHONY: build .PHONY: build
build: $(SNAPSHOTDIR) ## Build release snapshot binaries and packages build: $(SNAPSHOTDIR) ## Build release snapshot binaries and packages
+17 -1
View File
@@ -3,6 +3,10 @@ package cmd
import ( import (
"fmt" "fmt"
"os" "os"
"path"
"github.com/adrg/xdg"
"github.com/anchore/syft/internal"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -78,6 +82,18 @@ func setGlobalCliOptions() {
} }
rootCmd.Flags().CountVarP(&cliOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)") rootCmd.Flags().CountVarP(&cliOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)")
// plugins
defaultPluginDir := path.Join(xdg.CacheHome, internal.ApplicationName, "plugins")
flag = "plugins.dir"
rootCmd.Flags().String(
flag, defaultPluginDir,
"plugin storage directory",
)
if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
fmt.Printf("unable to bind flag '%s': %+v", flag, err)
os.Exit(1)
}
} }
func initAppConfig() { func initAppConfig() {
@@ -101,7 +117,7 @@ func initLogging() {
logWrapper := logger.NewLogrusLogger(cfg) logWrapper := logger.NewLogrusLogger(cfg)
syft.SetLogger(logWrapper) syft.SetLogger(logWrapper)
stereoscope.SetLogger(&logger.LogrusNestedLogger{ stereoscope.SetLogger(&logger.LogrusNestedLogger{
Logger: logWrapper.Logger.WithField("from-lib", "steroscope"), Logger: logWrapper.Logger.WithField("from-lib", "stereoscope"),
}) })
} }
+11 -1
View File
@@ -68,7 +68,17 @@ func startWorker(userInput string) <-chan error {
} }
} }
catalog, scope, distro, err := syft.Catalog(userInput, appConfig.ScopeOpt) config := syft.Config{
PluginDirectory: appConfig.Plugins.Directory,
}
client, err := syft.NewClient(config)
if err != nil {
errs <- fmt.Errorf("could not initialize syft: %+v", err)
return
}
catalog, scope, distro, err := client.Catalog(userInput, appConfig.ScopeOpt)
if err != nil { if err != nil {
errs <- fmt.Errorf("failed to catalog input: %+v", err) errs <- fmt.Errorf("failed to catalog input: %+v", err)
return return
+5 -1
View File
@@ -7,16 +7,19 @@ require (
github.com/anchore/go-rpmdb v0.0.0-20200811175839-cbc751c28e8e github.com/anchore/go-rpmdb v0.0.0-20200811175839-cbc751c28e8e
github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/stereoscope v0.0.0-20200813152757-548b22c8a0b3 github.com/anchore/stereoscope v0.0.0-20200903223322-a3806f515470
github.com/bmatcuk/doublestar v1.3.1 github.com/bmatcuk/doublestar v1.3.1
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe // indirect github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe // indirect
github.com/dustin/go-humanize v1.0.0 github.com/dustin/go-humanize v1.0.0
github.com/go-test/deep v1.0.6 github.com/go-test/deep v1.0.6
github.com/golang/protobuf v1.4.2
github.com/google/go-containerregistry v0.1.1 // indirect github.com/google/go-containerregistry v0.1.1 // indirect
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/gookit/color v1.2.7 github.com/gookit/color v1.2.7
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 // indirect
github.com/hashicorp/go-hclog v0.14.1
github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-multierror v1.1.0
github.com/hashicorp/go-plugin v1.3.0
github.com/hashicorp/go-version v1.2.0 github.com/hashicorp/go-version v1.2.0
github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.3.1 github.com/mitchellh/mapstructure v1.3.1
@@ -38,6 +41,7 @@ require (
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect
golang.org/x/sys v0.0.0-20200610111108-226ff32320da // indirect golang.org/x/sys v0.0.0-20200610111108-226ff32320da // indirect
google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 // indirect google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 // indirect
google.golang.org/grpc v1.31.1
gopkg.in/ini.v1 v1.57.0 // indirect gopkg.in/ini.v1 v1.57.0 // indirect
gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v2 v2.3.0
) )
+23 -2
View File
@@ -131,8 +131,8 @@ github.com/anchore/go-testutils v0.0.0-20200624184116-66aa578126db/go.mod h1:D3r
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods= github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e/go.mod h1:bkyLl5VITnrmgErv4S1vDfVz/TGAZ5il6161IQo7w2g= github.com/anchore/stereoscope v0.0.0-20200520221116-025e07f1c93e/go.mod h1:bkyLl5VITnrmgErv4S1vDfVz/TGAZ5il6161IQo7w2g=
github.com/anchore/stereoscope v0.0.0-20200813152757-548b22c8a0b3 h1:pl+txuYlhK8Mmio4d+4zQI/1xg8X6BtNErTASrx23Wk= github.com/anchore/stereoscope v0.0.0-20200903223322-a3806f515470 h1:apXyPyfiaL/YQF7VeXRbDDB9TA7dA9rxdq8YbzPk9ko=
github.com/anchore/stereoscope v0.0.0-20200813152757-548b22c8a0b3/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs= github.com/anchore/stereoscope v0.0.0-20200903223322-a3806f515470/go.mod h1:WntReQTI/I27FOQ87UgLVVzWgku6+ZsqfOTLxpIZFCs=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ= github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ=
github.com/apex/log v1.3.0 h1:1fyfbPvUwD10nMoh3hY6MXzvZShJQn9/ck7ATgAt5pA= github.com/apex/log v1.3.0 h1:1fyfbPvUwD10nMoh3hY6MXzvZShJQn9/ck7ATgAt5pA=
@@ -474,12 +474,18 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-plugin v1.3.0 h1:4d/wJojzvHV1I4i/rrjVaeuyxWrLzDE1mDCyDy8fXS8=
github.com/hashicorp/go-plugin v1.3.0/go.mod h1:F9eH4LrE/ZsRdbwhfjs9k9HoDUwAHnYtXdgmf1AVNs0=
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
@@ -500,6 +506,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -510,6 +518,8 @@ github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk= github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s= github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
@@ -589,6 +599,7 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@@ -611,6 +622,8 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
@@ -639,6 +652,8 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
@@ -923,6 +938,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1012,6 +1028,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1148,6 +1165,7 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -1181,6 +1199,7 @@ google.golang.org/genproto v0.0.0-20200604104852-0b0486081ffb h1:ek2py5bOqzR7MR/
google.golang.org/genproto v0.0.0-20200604104852-0b0486081ffb/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200604104852-0b0486081ffb/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 h1:1N7l1PuXZwEK7OhHdmKQROOM75PnUjABGwvVRbLBgFk= google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 h1:1N7l1PuXZwEK7OhHdmKQROOM75PnUjABGwvVRbLBgFk=
google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1195,6 +1214,8 @@ google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+7 -1
View File
@@ -28,7 +28,13 @@ type Application struct {
Quiet bool `mapstructure:"quiet"` Quiet bool `mapstructure:"quiet"`
Log Logging `mapstructure:"log"` Log Logging `mapstructure:"log"`
CliOptions CliOnlyOptions CliOptions CliOnlyOptions
CheckForAppUpdate bool `mapstructure:"check-for-app-update"` CheckForAppUpdate bool `mapstructure:"check-for-app-update"`
Plugins Plugins `mapstructure:"plugins"`
}
type Plugins struct {
Enabled bool `mapstructure:"enabled"`
Directory string `mapstructure:"dir"`
} }
type Logging struct { type Logging struct {
+7 -1
View File
@@ -1,6 +1,8 @@
package log package log
import "github.com/anchore/syft/syft/logger" import (
"github.com/anchore/syft/syft/logger"
)
var Log logger.Logger = &nopLogger{} var Log logger.Logger = &nopLogger{}
@@ -8,6 +10,10 @@ func Errorf(format string, args ...interface{}) {
Log.Errorf(format, args...) Log.Errorf(format, args...)
} }
func Error(args ...interface{}) {
Log.Error(args...)
}
func Infof(format string, args ...interface{}) { func Infof(format string, args ...interface{}) {
Log.Infof(format, args...) Log.Infof(format, args...)
} }
+1
View File
@@ -3,6 +3,7 @@ package log
type nopLogger struct{} type nopLogger struct{}
func (l *nopLogger) Errorf(format string, args ...interface{}) {} func (l *nopLogger) Errorf(format string, args ...interface{}) {}
func (l *nopLogger) Error(args ...interface{}) {}
func (l *nopLogger) Infof(format string, args ...interface{}) {} func (l *nopLogger) Infof(format string, args ...interface{}) {}
func (l *nopLogger) Info(args ...interface{}) {} func (l *nopLogger) Info(args ...interface{}) {}
func (l *nopLogger) Debugf(format string, args ...interface{}) {} func (l *nopLogger) Debugf(format string, args ...interface{}) {}
+78
View File
@@ -0,0 +1,78 @@
package logger
import (
"bytes"
"strings"
"github.com/hashicorp/go-hclog"
)
// Source: https://github.com/hashicorp/go-hclog/blob/master/stdlog.go (MIT License)
// Provides a io.Writer to shim the data out of *log.Logger
// and back into our Logger. This is basically the only way to
// build upon *log.Logger.
type HCLogStdlogAdapter struct {
log hclog.Logger
inferLevels bool
forceLevel hclog.Level
}
// Take the data, infer the levels if configured, and send it through
// a regular Logger.
func (s *HCLogStdlogAdapter) Write(data []byte) (int, error) {
str := string(bytes.TrimRight(data, " \t\n"))
if s.forceLevel != hclog.NoLevel {
// Use pickLevel to strip log levels included in the line since we are
// forcing the level
_, str := s.pickLevel(str)
// Log at the forced level
s.dispatch(str, s.forceLevel)
} else if s.inferLevels {
level, str := s.pickLevel(str)
s.dispatch(str, level)
} else {
s.log.Info(str)
}
return len(data), nil
}
func (s *HCLogStdlogAdapter) dispatch(str string, level hclog.Level) {
switch level {
case hclog.Trace:
s.log.Trace(str)
case hclog.Debug:
s.log.Debug(str)
case hclog.Info:
s.log.Info(str)
case hclog.Warn:
s.log.Warn(str)
case hclog.Error:
s.log.Error(str)
default:
s.log.Info(str)
}
}
// Detect, based on conventions, what log level this is.
func (s *HCLogStdlogAdapter) pickLevel(str string) (hclog.Level, string) {
switch {
case strings.HasPrefix(str, "[DEBUG]"):
return hclog.Debug, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[TRACE]"):
return hclog.Trace, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[INFO]"):
return hclog.Info, strings.TrimSpace(str[6:])
case strings.HasPrefix(str, "[WARN]"):
return hclog.Warn, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[ERROR]"):
return hclog.Error, strings.TrimSpace(str[7:])
case strings.HasPrefix(str, "[ERR]"):
return hclog.Error, strings.TrimSpace(str[5:])
default:
return hclog.Info, str
}
}
+148
View File
@@ -0,0 +1,148 @@
package logger
import (
"io"
"log"
"github.com/anchore/syft/syft/logger"
"github.com/hashicorp/go-hclog"
)
// integrity check
var _ hclog.Logger = &LoggerHCLogAdapter{}
// LoggerHCLogAdapter is used to (partially) adapt a syft logger.Logger object to the hclog.Logger interface for syft plugins.
// Note: this does not implement all functionality required by the hclog.Logger.
type LoggerHCLogAdapter struct {
Logger logger.Logger
}
func NewLoggerHCLogAdapter(logger logger.Logger) *LoggerHCLogAdapter {
return &LoggerHCLogAdapter{
Logger: logger,
}
}
// Args are alternating key, val pairs
// keys must be strings
// vals can be any type, but display is implementation specific
// Emit a message and key/value pairs at a provided log level
func (l *LoggerHCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
l.Logger.Info(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the TRACE level
func (l *LoggerHCLogAdapter) Trace(msg string, args ...interface{}) {
l.Logger.Debug(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the DEBUG level
func (l *LoggerHCLogAdapter) Debug(msg string, args ...interface{}) {
l.Logger.Debug(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the INFO level
func (l *LoggerHCLogAdapter) Info(msg string, args ...interface{}) {
l.Logger.Info(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the WARN level
func (l *LoggerHCLogAdapter) Warn(msg string, args ...interface{}) {
l.Logger.Error(append([]interface{}{msg}, args...)...)
}
// Emit a message and key/value pairs at the ERROR level
func (l *LoggerHCLogAdapter) Error(msg string, args ...interface{}) {
l.Logger.Error(append([]interface{}{msg}, args...)...)
}
// Indicate if TRACE logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsTrace() bool {
// TODO: not implemented
return true
}
// Indicate if DEBUG logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsDebug() bool {
// TODO: not implemented
return true
}
// Indicate if INFO logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsInfo() bool {
// TODO: not implemented
return true
}
// Indicate if WARN logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsWarn() bool {
// TODO: not implemented
return true
}
// Indicate if ERROR logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LoggerHCLogAdapter) IsError() bool {
// TODO: not implemented
return true
}
// ImpliedArgs returns With key/value pairs
func (l *LoggerHCLogAdapter) ImpliedArgs() []interface{} {
// TODO: not implemented
return nil
}
// Creates a sublogger that will always have the given key/value pairs
func (l *LoggerHCLogAdapter) With(args ...interface{}) hclog.Logger {
// TODO: not implemented
return l
}
// Returns the Name of the logger
func (l *LoggerHCLogAdapter) Name() string {
// TODO: not implemented
return ""
}
// Create a logger that will prepend the name string on the front of all messages.
// If the logger already has a name, the new value will be appended to the current
// name. That way, a major subsystem can use this to decorate all it's own logs
// without losing context.
func (l *LoggerHCLogAdapter) Named(name string) hclog.Logger {
// TODO: not implemented
return l
}
// Create a logger that will prepend the name string on the front of all messages.
// This sets the name of the logger to the value directly, unlike Named which honor
// the current name as well.
func (l *LoggerHCLogAdapter) ResetNamed(name string) hclog.Logger {
// TODO: not implemented
return l
}
// Updates the level. This should affect all sub-loggers as well. If an
// implementation cannot update the level on the fly, it should no-op.
func (l *LoggerHCLogAdapter) SetLevel(level hclog.Level) {
// TODO: not implemented
}
// Return a value that conforms to the stdlib log.Logger interface
func (l *LoggerHCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
return log.New(l.StandardWriter(opts), "", log.LstdFlags)
}
// Return a value that conforms to io.Writer, which can be passed into log.SetOutput()
func (l *LoggerHCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
return &HCLogStdlogAdapter{
log: l,
inferLevels: opts.InferLevels,
forceLevel: opts.ForceLevel,
}
}
+25 -12
View File
@@ -6,10 +6,15 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/anchore/syft/syft/logger"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
prefixed "github.com/x-cray/logrus-prefixed-formatter" prefixed "github.com/x-cray/logrus-prefixed-formatter"
) )
// integrity check
var _ logger.Logger = &LogrusLogger{}
var _ logger.Logger = &LogrusNestedLogger{}
type LogrusConfig struct { type LogrusConfig struct {
EnableConsole bool EnableConsole bool
EnableFile bool EnableFile bool
@@ -76,42 +81,50 @@ func NewLogrusLogger(cfg LogrusConfig) *LogrusLogger {
} }
} }
func (l *LogrusLogger) Debugf(format string, args ...interface{}) { func (l *LogrusLogger) Errorf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...) l.Logger.Errorf(format, args...)
} }
func (l *LogrusLogger) Infof(format string, args ...interface{}) { func (l *LogrusLogger) Infof(format string, args ...interface{}) {
l.Logger.Infof(format, args...) l.Logger.Infof(format, args...)
} }
func (l *LogrusLogger) Debug(args ...interface{}) { func (l *LogrusLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debug(args...) l.Logger.Debugf(format, args...)
}
func (l *LogrusLogger) Error(args ...interface{}) {
l.Logger.Error(args...)
} }
func (l *LogrusLogger) Info(args ...interface{}) { func (l *LogrusLogger) Info(args ...interface{}) {
l.Logger.Info(args...) l.Logger.Info(args...)
} }
func (l *LogrusLogger) Errorf(format string, args ...interface{}) { func (l *LogrusLogger) Debug(args ...interface{}) {
l.Logger.Errorf(format, args...) l.Logger.Debug(args...)
} }
func (l *LogrusNestedLogger) Debugf(format string, args ...interface{}) { func (l *LogrusNestedLogger) Errorf(format string, args ...interface{}) {
l.Logger.Debugf(format, args...) l.Logger.Errorf(format, args...)
} }
func (l *LogrusNestedLogger) Infof(format string, args ...interface{}) { func (l *LogrusNestedLogger) Infof(format string, args ...interface{}) {
l.Logger.Infof(format, args...) l.Logger.Infof(format, args...)
} }
func (l *LogrusNestedLogger) Debug(args ...interface{}) { func (l *LogrusNestedLogger) Debugf(format string, args ...interface{}) {
l.Logger.Debug(args...) l.Logger.Debugf(format, args...)
}
func (l *LogrusNestedLogger) Error(args ...interface{}) {
l.Logger.Error(args...)
} }
func (l *LogrusNestedLogger) Info(args ...interface{}) { func (l *LogrusNestedLogger) Info(args ...interface{}) {
l.Logger.Info(args...) l.Logger.Info(args...)
} }
func (l *LogrusNestedLogger) Errorf(format string, args ...interface{}) { func (l *LogrusNestedLogger) Debug(args ...interface{}) {
l.Logger.Errorf(format, args...) l.Logger.Debug(args...)
} }
+181
View File
@@ -0,0 +1,181 @@
package logger
import (
"fmt"
"io"
"log"
"github.com/hashicorp/go-hclog"
"github.com/sirupsen/logrus"
)
// integrity check
var _ hclog.Logger = &LogrusHCLogAdapter{}
var levels = map[hclog.Level]logrus.Level{
hclog.NoLevel: logrus.PanicLevel,
hclog.Error: logrus.ErrorLevel,
hclog.Warn: logrus.WarnLevel,
hclog.Info: logrus.InfoLevel,
hclog.Debug: logrus.DebugLevel,
hclog.Trace: logrus.TraceLevel,
}
// LogrusHCLogAdapter is used to adapt a logrus logger object to the hclog.Logger interface for syft plugins.
type LogrusHCLogAdapter struct {
logger *logrus.Logger
entry *logrus.Entry
}
func NewLogrusHCLogAdapter(logger *logrus.Logger, fields map[string]interface{}) *LogrusHCLogAdapter {
if fields != nil {
return &LogrusHCLogAdapter{
logger: logger,
entry: logger.WithFields(fields),
}
}
return &LogrusHCLogAdapter{
logger: logger,
}
}
func (l *LogrusHCLogAdapter) log(level hclog.Level, msg string, args ...interface{}) {
if l.entry != nil {
l.entry.WithFields(makeArgFields(args...)).Log(levels[level], msg)
return
}
l.logger.WithFields(makeArgFields(args...)).Log(levels[level], msg)
}
// Args are alternating key, val pairs
// keys must be strings
// vals can be any type, but display is implementation specific
// Emit a message and key/value pairs at a provided log level
func (l *LogrusHCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) {
l.log(level, msg, args...)
}
// Emit a message and key/value pairs at the TRACE level
func (l *LogrusHCLogAdapter) Trace(msg string, args ...interface{}) {
l.log(hclog.Trace, msg, args...)
}
// Emit a message and key/value pairs at the DEBUG level
func (l *LogrusHCLogAdapter) Debug(msg string, args ...interface{}) {
l.log(hclog.Debug, msg, args...)
}
// Emit a message and key/value pairs at the INFO level
func (l *LogrusHCLogAdapter) Info(msg string, args ...interface{}) {
l.log(hclog.Info, msg, args...)
}
// Emit a message and key/value pairs at the WARN level
func (l *LogrusHCLogAdapter) Warn(msg string, args ...interface{}) {
l.log(hclog.Warn, msg, args...)
}
// Emit a message and key/value pairs at the ERROR level
func (l *LogrusHCLogAdapter) Error(msg string, args ...interface{}) {
l.log(hclog.Error, msg, args...)
}
// Indicate if TRACE logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsTrace() bool {
return l.logger.Level <= logrus.TraceLevel
}
// Indicate if DEBUG logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsDebug() bool {
return l.logger.Level <= logrus.DebugLevel
}
// Indicate if INFO logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsInfo() bool {
return l.logger.Level <= logrus.InfoLevel
}
// Indicate if WARN logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsWarn() bool {
return l.logger.Level <= logrus.WarnLevel
}
// Indicate if ERROR logs would be emitted. This and the other Is* guards
// are used to elide expensive logging code based on the current level.
func (l *LogrusHCLogAdapter) IsError() bool {
return l.logger.Level <= logrus.ErrorLevel
}
// ImpliedArgs returns With key/value pairs
func (l *LogrusHCLogAdapter) ImpliedArgs() []interface{} {
// TODO: not implemented
return nil
}
// Creates a sublogger that will always have the given key/value pairs
func (l *LogrusHCLogAdapter) With(args ...interface{}) hclog.Logger {
l.entry = l.logger.WithFields(makeArgFields(args...))
return l
}
// Returns the Name of the logger
func (l *LogrusHCLogAdapter) Name() string {
// TODO: not implemented
return ""
}
// Create a logger that will prepend the name string on the front of all messages.
// If the logger already has a name, the new value will be appended to the current
// name. That way, a major subsystem can use this to decorate all it's own logs
// without losing context.
func (l *LogrusHCLogAdapter) Named(name string) hclog.Logger {
return NewLogrusHCLogAdapter(l.logger, logrus.Fields{"name": name})
}
// Create a logger that will prepend the name string on the front of all messages.
// This sets the name of the logger to the value directly, unlike Named which honor
// the current name as well.
func (l *LogrusHCLogAdapter) ResetNamed(name string) hclog.Logger {
return NewLogrusHCLogAdapter(l.logger, logrus.Fields{"name": name})
}
// Updates the level. This should affect all sub-loggers as well. If an
// implementation cannot update the level on the fly, it should no-op.
func (l *LogrusHCLogAdapter) SetLevel(level hclog.Level) {
l.logger.SetLevel(levels[level])
}
// Return a value that conforms to the stdlib log.Logger interface
func (l *LogrusHCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger {
return log.New(l.StandardWriter(opts), "", log.LstdFlags)
}
// Return a value that conforms to io.Writer, which can be passed into log.SetOutput()
func (l *LogrusHCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer {
return &HCLogStdlogAdapter{
log: l,
inferLevels: opts.InferLevels,
forceLevel: opts.ForceLevel,
}
}
func makeArgFields(args ...interface{}) logrus.Fields {
if len(args)%2 != 0 {
panic(fmt.Errorf("odd number of logger key-value pairs (%d): %+v", len(args), args))
}
var key string
var fields = make(logrus.Fields)
for i, arg := range args {
if i%2 == 0 {
key = arg.(string)
} else {
fields[key] = arg
}
}
return fields
}
+7 -5
View File
@@ -4,6 +4,8 @@ Package apkdb provides a concrete Cataloger implementation for Alpine DB files.
package apkdb package apkdb
import ( import (
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common" "github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@@ -27,16 +29,16 @@ func New() *Cataloger {
} }
// Name returns a string that uniquely describes this cataloger. // Name returns a string that uniquely describes this cataloger.
func (a *Cataloger) Name() string { func (c *Cataloger) Name() string {
return "apkdb-cataloger" return "apkdb-cataloger"
} }
// SelectFiles returns a set of discovered Alpine DB files from the user content source. // SelectFiles returns a set of discovered Alpine DB files from the user content source.
func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference { func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return a.cataloger.SelectFiles(resolver) return c.cataloger.SelectFiles(resolver)
} }
// Catalog returns the Packages indexed from all Alpine DB files discovered. // Catalog returns the Packages indexed from all Alpine DB files discovered.
func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) { func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return a.cataloger.Catalog(contents, a.Name()) return c.cataloger.Catalog(contents)
} }
+7 -5
View File
@@ -4,6 +4,8 @@ Package bundler provides a concrete Cataloger implementation for Ruby Gemfile.lo
package bundler package bundler
import ( import (
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common" "github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@@ -27,16 +29,16 @@ func New() *Cataloger {
} }
// Name returns a string that uniquely describes this cataloger. // Name returns a string that uniquely describes this cataloger.
func (a *Cataloger) Name() string { func (c *Cataloger) Name() string {
return "bundler-cataloger" return "bundler-cataloger"
} }
// SelectFiles returns a set of discovered Gemfile.lock files from the user content source. // SelectFiles returns a set of discovered Gemfile.lock files from the user content source.
func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference { func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return a.cataloger.SelectFiles(resolver) return c.cataloger.SelectFiles(resolver)
} }
// Catalog returns the Packages indexed from all Gemfile.lock files discovered. // Catalog returns the Packages indexed from all Gemfile.lock files discovered.
func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) { func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return a.cataloger.Catalog(contents, a.Name()) return c.cataloger.Catalog(contents)
} }
+55 -52
View File
@@ -12,6 +12,61 @@ import (
"github.com/wagoodman/go-progress" "github.com/wagoodman/go-progress"
) )
// Catalog a given scope (container image or filesystem) with the given catalogers, returning all discovered packages.
// In order to efficiently retrieve contents from a underlying container image the content fetch requests are
// done in bulk. Specifically, all files of interest are collected from each catalogers and accumulated into a single
// request.
func Catalog(resolver scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
catalog := pkg.NewCatalog()
fileSelection := make([]file.Reference, 0)
filesProcessed, packagesDiscovered := newMonitor()
// ask catalogers for files to extract from the image tar
for _, a := range catalogers {
catalogerSelection := a.SelectFiles(resolver)
fileSelection = append(fileSelection, catalogerSelection...)
log.Debugf("cataloger '%s' selected '%d' files", a.Name(), len(catalogerSelection))
filesProcessed.N += int64(len(fileSelection))
}
// fetch contents for requested selection by catalogers
// TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering).
contents, err := resolver.MultipleFileContentsByRef(fileSelection...)
if err != nil {
return nil, err
}
// perform analysis, accumulating errors for each failed analysis
var errs error
for _, c := range catalogers {
// TODO: check for multiple rounds of analyses by Iterate error
packages, err := c.Catalog(contents)
if err != nil {
errs = multierror.Append(errs, err)
continue
}
log.Debugf("cataloger '%s' discovered '%d' packages", c.Name(), len(packages))
packagesDiscovered.N += int64(len(packages))
catalogerName := c.Name()
for _, p := range packages {
p.FoundBy = catalogerName
catalog.Add(p)
}
}
if errs != nil {
return nil, errs
}
filesProcessed.SetCompleted()
packagesDiscovered.SetCompleted()
return catalog, nil
}
// Monitor provides progress-related data for observing the progress of a Catalog() call (published on the event bus). // Monitor provides progress-related data for observing the progress of a Catalog() call (published on the event bus).
type Monitor struct { type Monitor struct {
FilesProcessed progress.Monitorable // the number of files selected and contents analyzed from all registered catalogers FilesProcessed progress.Monitorable // the number of files selected and contents analyzed from all registered catalogers
@@ -32,55 +87,3 @@ func newMonitor() (*progress.Manual, *progress.Manual) {
}) })
return &filesProcessed, &packagesDiscovered return &filesProcessed, &packagesDiscovered
} }
// Catalog a given scope (container image or filesystem) with the given catalogers, returning all discovered packages.
// In order to efficiently retrieve contents from a underlying container image the content fetch requests are
// done in bulk. Specifically, all files of interest are collected from each catalogers and accumulated into a single
// request.
func Catalog(resolver scope.Resolver, catalogers ...Cataloger) (*pkg.Catalog, error) {
catalog := pkg.NewCatalog()
fileSelection := make([]file.Reference, 0)
filesProcessed, packagesDiscovered := newMonitor()
// ask catalogers for files to extract from the image tar
for _, a := range catalogers {
fileSelection = append(fileSelection, a.SelectFiles(resolver)...)
log.Debugf("cataloger '%s' selected '%d' files", a.Name(), len(fileSelection))
filesProcessed.N += int64(len(fileSelection))
}
// fetch contents for requested selection by catalogers
// TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering).
contents, err := resolver.MultipleFileContentsByRef(fileSelection...)
if err != nil {
return nil, err
}
// perform analysis, accumulating errors for each failed analysis
var errs error
for _, a := range catalogers {
// TODO: check for multiple rounds of analyses by Iterate error
packages, err := a.Catalog(contents)
if err != nil {
errs = multierror.Append(errs, err)
continue
}
log.Debugf("cataloger '%s' discovered '%d' packages", a.Name(), len(packages))
packagesDiscovered.N += int64(len(packages))
for _, p := range packages {
catalog.Add(p)
}
}
if errs != nil {
return nil, errs
}
filesProcessed.SetCompleted()
packagesDiscovered.SetCompleted()
return catalog, nil
}
+6 -4
View File
@@ -6,10 +6,13 @@ catalogers defined in child packages as well as the interface definition to impl
package cataloger package cataloger
import ( import (
"io"
"github.com/anchore/syft/syft/cataloger/dpkg"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/apkdb" "github.com/anchore/syft/syft/cataloger/apkdb"
"github.com/anchore/syft/syft/cataloger/bundler" "github.com/anchore/syft/syft/cataloger/bundler"
"github.com/anchore/syft/syft/cataloger/dpkg"
"github.com/anchore/syft/syft/cataloger/golang" "github.com/anchore/syft/syft/cataloger/golang"
"github.com/anchore/syft/syft/cataloger/java" "github.com/anchore/syft/syft/cataloger/java"
"github.com/anchore/syft/syft/cataloger/javascript" "github.com/anchore/syft/syft/cataloger/javascript"
@@ -28,13 +31,12 @@ type Cataloger interface {
// SelectFiles discovers and returns specific files that the cataloger would like to inspect the contents of. // SelectFiles discovers and returns specific files that the cataloger would like to inspect the contents of.
SelectFiles(scope.FileResolver) []file.Reference SelectFiles(scope.FileResolver) []file.Reference
// Catalog is given the file contents and should return any discovered Packages after analyzing the contents. // Catalog is given the file contents and should return any discovered Packages after analyzing the contents.
Catalog(map[file.Reference]string) ([]pkg.Package, error) Catalog(map[file.Reference]io.Reader) ([]pkg.Package, error)
// TODO: add "IterationNeeded" error to indicate to the driver to continue with another Select/Catalog pass // TODO: add "IterationNeeded" error to indicate to the driver to continue with another Select/Catalog pass
// TODO: we should consider refactoring to return a set of io.Readers instead of the full contents themselves (allow for optional buffering).
} }
// All returns a slice of all locally defined catalogers (defined in child packages). // All returns a slice of all locally defined catalogers (defined in child packages).
func All() []Cataloger { func BuiltIn() []Cataloger {
return []Cataloger{ return []Cataloger{
dpkg.New(), dpkg.New(),
bundler.New(), bundler.New(),
+5 -6
View File
@@ -4,7 +4,7 @@ Package common provides generic utilities used by multiple catalogers.
package common package common
import ( import (
"strings" "io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
@@ -73,7 +73,7 @@ func (a *GenericCataloger) SelectFiles(resolver scope.FileResolver) []file.Refer
} }
// Catalog takes a set of file contents and uses any configured parser functions to resolve and return discovered packages // Catalog takes a set of file contents and uses any configured parser functions to resolve and return discovered packages
func (a *GenericCataloger) Catalog(contents map[file.Reference]string, upstreamMatcher string) ([]pkg.Package, error) { func (a *GenericCataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
defer a.clear() defer a.clear()
packages := make([]pkg.Package, 0) packages := make([]pkg.Package, 0)
@@ -81,19 +81,18 @@ func (a *GenericCataloger) Catalog(contents map[file.Reference]string, upstreamM
for reference, parser := range a.parsers { for reference, parser := range a.parsers {
content, ok := contents[reference] content, ok := contents[reference]
if !ok { if !ok {
log.Errorf("cataloger '%s' missing file content: %+v", upstreamMatcher, reference) log.Errorf("cataloger missing file content: %+v", reference)
continue continue
} }
entries, err := parser(string(reference.Path), strings.NewReader(content)) entries, err := parser(string(reference.Path), content)
if err != nil { if err != nil {
// TODO: should we fail? or only log? // TODO: should we fail? or only log?
log.Errorf("cataloger '%s' failed to parse entries (reference=%+v): %w", upstreamMatcher, reference, err) log.Errorf("cataloger failed to parse entries (reference=%+v): %w", reference, err)
continue continue
} }
for _, entry := range entries { for _, entry := range entries {
entry.FoundBy = upstreamMatcher
entry.Source = []file.Reference{reference} entry.Source = []file.Reference{reference}
packages = append(packages, entry) packages = append(packages, entry)
@@ -79,17 +79,15 @@ func TestGenericCataloger(t *testing.T) {
selectionByPath[string(s.Path)] = s selectionByPath[string(s.Path)] = s
} }
upstream := "some-other-cataloger"
expectedPkgs := make(map[file.Reference]pkg.Package) expectedPkgs := make(map[file.Reference]pkg.Package)
for path, ref := range selectionByPath { for path, ref := range selectionByPath {
expectedPkgs[ref] = pkg.Package{ expectedPkgs[ref] = pkg.Package{
FoundBy: upstream, Source: []file.Reference{ref},
Source: []file.Reference{ref}, Name: fmt.Sprintf("%s file contents!", path),
Name: fmt.Sprintf("%s file contents!", path),
} }
} }
actualPkgs, err := cataloger.Catalog(resolver.contents, upstream) actualPkgs, err := cataloger.Catalog(resolver.contents)
if err != nil { if err != nil {
t.Fatalf("cataloger catalog action failed: %+v", err) t.Fatalf("cataloger catalog action failed: %+v", err)
} }
+7 -5
View File
@@ -4,6 +4,8 @@ Package dpkg provides a concrete Cataloger implementation for Debian package DB
package dpkg package dpkg
import ( import (
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common" "github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@@ -27,16 +29,16 @@ func New() *Cataloger {
} }
// Name returns a string that uniquely describes this cataloger. // Name returns a string that uniquely describes this cataloger.
func (a *Cataloger) Name() string { func (c *Cataloger) Name() string {
return "dpkg-cataloger" return "dpkg-cataloger"
} }
// SelectFiles returns a set of discovered DPKG status files from the user content source. // SelectFiles returns a set of discovered DPKG status files from the user content source.
func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference { func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return a.cataloger.SelectFiles(resolver) return c.cataloger.SelectFiles(resolver)
} }
// Catalog returns the Packages indexed from all DPKG status files discovered. // Catalog returns the Packages indexed from all DPKG status files discovered.
func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) { func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return a.cataloger.Catalog(contents, a.Name()) return c.cataloger.Catalog(contents)
} }
+7 -5
View File
@@ -4,6 +4,8 @@ Package golang provides a concrete Cataloger implementation for go.mod files.
package golang package golang
import ( import (
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common" "github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@@ -27,16 +29,16 @@ func New() *Cataloger {
} }
// Name returns a string that uniquely describes this cataloger. // Name returns a string that uniquely describes this cataloger.
func (a *Cataloger) Name() string { func (c *Cataloger) Name() string {
return "go-cataloger" return "go-cataloger"
} }
// SelectFiles returns a set of discovered go.mod files from the user content source. // SelectFiles returns a set of discovered go.mod files from the user content source.
func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference { func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return a.cataloger.SelectFiles(resolver) return c.cataloger.SelectFiles(resolver)
} }
// Catalog returns the Packages indexed from all go.mod files discovered. // Catalog returns the Packages indexed from all go.mod files discovered.
func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) { func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return a.cataloger.Catalog(contents, a.Name()) return c.cataloger.Catalog(contents)
} }
+7 -5
View File
@@ -4,6 +4,8 @@ Package java provides a concrete Cataloger implementation for Java archives (jar
package java package java
import ( import (
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common" "github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@@ -28,16 +30,16 @@ func New() *Cataloger {
} }
// Name returns a string that uniquely describes this cataloger. // Name returns a string that uniquely describes this cataloger.
func (a *Cataloger) Name() string { func (c *Cataloger) Name() string {
return "java-cataloger" return "java-cataloger"
} }
// SelectFiles returns a set of discovered Java archive files from the user content source. // SelectFiles returns a set of discovered Java archive files from the user content source.
func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference { func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return a.cataloger.SelectFiles(resolver) return c.cataloger.SelectFiles(resolver)
} }
// Catalog returns the Packages indexed from all Java archive files discovered. // Catalog returns the Packages indexed from all Java archive files discovered.
func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) { func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return a.cataloger.Catalog(contents, a.Name()) return c.cataloger.Catalog(contents)
} }
+7 -5
View File
@@ -4,6 +4,8 @@ Package javascript provides a concrete Cataloger implementation for JavaScript e
package javascript package javascript
import ( import (
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common" "github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@@ -28,16 +30,16 @@ func New() *Cataloger {
} }
// Name returns a string that uniquely describes this cataloger. // Name returns a string that uniquely describes this cataloger.
func (a *Cataloger) Name() string { func (c *Cataloger) Name() string {
return "javascript-cataloger" return "javascript-cataloger"
} }
// SelectFiles returns a set of discovered Javascript ecosystem files from the user content source. // SelectFiles returns a set of discovered Javascript ecosystem files from the user content source.
func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference { func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return a.cataloger.SelectFiles(resolver) return c.cataloger.SelectFiles(resolver)
} }
// Catalog returns the Packages indexed from all Javascript ecosystem files discovered. // Catalog returns the Packages indexed from all Javascript ecosystem files discovered.
func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) { func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return a.cataloger.Catalog(contents, a.Name()) return c.cataloger.Catalog(contents)
} }
+7 -5
View File
@@ -4,6 +4,8 @@ Package python provides a concrete Cataloger implementation for Python ecosystem
package python package python
import ( import (
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common" "github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@@ -31,16 +33,16 @@ func New() *Cataloger {
} }
// Name returns a string that uniquely describes this cataloger. // Name returns a string that uniquely describes this cataloger.
func (a *Cataloger) Name() string { func (c *Cataloger) Name() string {
return "python-cataloger" return "python-cataloger"
} }
// SelectFiles returns a set of discovered Python ecosystem files from the user content source. // SelectFiles returns a set of discovered Python ecosystem files from the user content source.
func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference { func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return a.cataloger.SelectFiles(resolver) return c.cataloger.SelectFiles(resolver)
} }
// Catalog returns the Packages indexed from all Python ecosystem files discovered. // Catalog returns the Packages indexed from all Python ecosystem files discovered.
func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) { func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return a.cataloger.Catalog(contents, a.Name()) return c.cataloger.Catalog(contents)
} }
+7 -5
View File
@@ -4,6 +4,8 @@ Package rpmdb provides a concrete Cataloger implementation for RPM "Package" DB
package rpmdb package rpmdb
import ( import (
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger/common" "github.com/anchore/syft/syft/cataloger/common"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
@@ -27,16 +29,16 @@ func New() *Cataloger {
} }
// Name returns a string that uniquely describes this cataloger. // Name returns a string that uniquely describes this cataloger.
func (a *Cataloger) Name() string { func (c *Cataloger) Name() string {
return "rpmdb-cataloger" return "rpmdb-cataloger"
} }
// SelectFiles returns a set of discovered RPM DB files from the user content source. // SelectFiles returns a set of discovered RPM DB files from the user content source.
func (a *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference { func (c *Cataloger) SelectFiles(resolver scope.FileResolver) []file.Reference {
return a.cataloger.SelectFiles(resolver) return c.cataloger.SelectFiles(resolver)
} }
// Catalog returns the Packages indexed from all RPM DB files discovered. // Catalog returns the Packages indexed from all RPM DB files discovered.
func (a *Cataloger) Catalog(contents map[file.Reference]string) ([]pkg.Package, error) { func (c *Cataloger) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
return a.cataloger.Catalog(contents, a.Name()) return c.cataloger.Catalog(contents)
} }
+34 -6
View File
@@ -10,13 +10,35 @@ import (
"github.com/anchore/syft/syft/distro" "github.com/anchore/syft/syft/distro"
"github.com/anchore/syft/syft/logger" "github.com/anchore/syft/syft/logger"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/plugin"
"github.com/anchore/syft/syft/scope" "github.com/anchore/syft/syft/scope"
"github.com/wagoodman/go-partybus" "github.com/wagoodman/go-partybus"
) )
type Config struct {
PluginDirectory string
}
type Client struct {
config Config
plugins *plugin.Repository
}
func NewClient(config Config) (Client, error) {
plugins, err := plugin.NewRepositoryFromDirectory(config.PluginDirectory)
if err != nil {
return Client{}, err
}
return Client{
config: config,
plugins: plugins,
}, nil
}
// Catalog the given image from a particular perspective (e.g. squashed scope, all-layers scope). Returns the discovered // Catalog the given image from a particular perspective (e.g. squashed scope, all-layers scope). Returns the discovered
// set of packages, the identified Linux distribution, and the scope object used to wrap the data source. // set of packages, the identified Linux distribution, and the scope object used to wrap the data source.
func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scope, *distro.Distro, error) { func (c *Client) Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scope, *distro.Distro, error) {
log.Info("cataloging image") log.Info("cataloging image")
s, cleanup, err := scope.NewScope(userInput, scoptOpt) s, cleanup, err := scope.NewScope(userInput, scoptOpt)
defer cleanup() defer cleanup()
@@ -24,9 +46,9 @@ func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scop
return nil, nil, nil, err return nil, nil, nil, err
} }
d := IdentifyDistro(s) d := c.IdentifyDistro(s)
catalog, err := CatalogFromScope(s) catalog, err := c.CatalogFromScope(s)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
@@ -36,7 +58,7 @@ func Catalog(userInput string, scoptOpt scope.Option) (*pkg.Catalog, *scope.Scop
// IdentifyDistro attempts to discover what the underlying Linux distribution may be from the available flat files // IdentifyDistro attempts to discover what the underlying Linux distribution may be from the available flat files
// provided by the given scope object. If results are inconclusive a "UnknownDistro" Type is returned. // provided by the given scope object. If results are inconclusive a "UnknownDistro" Type is returned.
func IdentifyDistro(s scope.Scope) distro.Distro { func (c *Client) IdentifyDistro(s scope.Scope) distro.Distro {
d := distro.Identify(s.Resolver) d := distro.Identify(s.Resolver)
if d.Type != distro.UnknownDistroType { if d.Type != distro.UnknownDistroType {
log.Infof("identified distro: %s", d.String()) log.Infof("identified distro: %s", d.String())
@@ -47,9 +69,15 @@ func IdentifyDistro(s scope.Scope) distro.Distro {
} }
// Catalog the given scope, which may represent a container image or filesystem. Returns the discovered set of packages. // Catalog the given scope, which may represent a container image or filesystem. Returns the discovered set of packages.
func CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) { func (c *Client) CatalogFromScope(s scope.Scope) (*pkg.Catalog, error) {
log.Info("building the catalog") log.Info("building the catalog")
return cataloger.Catalog(s.Resolver, cataloger.All()...) pluginCatalogers, deactivatePluginsFn, err := c.plugins.ActivateCatalogers()
defer deactivatePluginsFn()
if err != nil {
return nil, err
}
return cataloger.Catalog(s.Resolver, append(pluginCatalogers, cataloger.BuiltIn()...)...)
} }
// SetLogger sets the logger object used for all syft logging calls. // SetLogger sets the logger object used for all syft logging calls.
+13 -2
View File
@@ -1,6 +1,7 @@
package distro package distro
import ( import (
"io/ioutil"
"regexp" "regexp"
"strings" "strings"
@@ -10,6 +11,7 @@ import (
) )
// returns a distro or nil // returns a distro or nil
// TODO: refactor to io.Reader
type parseFunc func(string) *Distro type parseFunc func(string) *Distro
type parseEntry struct { type parseEntry struct {
@@ -17,6 +19,7 @@ type parseEntry struct {
fn parseFunc fn parseFunc
} }
// nolint: funlen
// Identify parses distro-specific files to determine distro metadata like version and release. // Identify parses distro-specific files to determine distro metadata like version and release.
func Identify(resolver scope.Resolver) Distro { func Identify(resolver scope.Resolver) Distro {
distro := NewUnknownDistro() distro := NewUnknownDistro()
@@ -52,8 +55,8 @@ identifyLoop:
} }
for _, ref := range refs { for _, ref := range refs {
contents, err := resolver.MultipleFileContentsByRef(ref) contentReaders, err := resolver.MultipleFileContentsByRef(ref)
content, ok := contents[ref] contentReader, ok := contentReaders[ref]
if !ok { if !ok {
log.Infof("no content present for ref: %s", ref) log.Infof("no content present for ref: %s", ref)
@@ -65,6 +68,14 @@ identifyLoop:
continue continue
} }
bytes, err := ioutil.ReadAll(contentReader)
if err != nil {
log.Debugf("unable to read contents from %s: %s", entry.path, err)
continue
}
content := string(bytes)
if content == "" { if content == "" {
log.Debugf("no contents in file, skipping: %s", entry.path) log.Debugf("no contents in file, skipping: %s", entry.path)
continue continue
+1
View File
@@ -5,6 +5,7 @@ package logger
type Logger interface { type Logger interface {
Errorf(format string, args ...interface{}) Errorf(format string, args ...interface{})
Error(args ...interface{})
Infof(format string, args ...interface{}) Infof(format string, args ...interface{})
Info(args ...interface{}) Info(args ...interface{})
Debugf(format string, args ...interface{}) Debugf(format string, args ...interface{})
+29
View File
@@ -0,0 +1,29 @@
package plugin
import (
"context"
"github.com/anchore/syft/syft/cataloger"
syftPluginGrpc "github.com/anchore/syft/syft/plugin/grpc"
"github.com/anchore/syft/syft/plugin/proto"
"github.com/hashicorp/go-plugin"
"google.golang.org/grpc"
)
// integrity check
var _ plugin.GRPCPlugin = &CatalogerPlugin{}
// CatalogerPlugin is the implementation of plugin.Plugin that is served/consumed.
type CatalogerPlugin struct {
plugin.NetRPCUnsupportedPlugin
Impl cataloger.Cataloger
}
func (p *CatalogerPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
proto.RegisterCatalogerServer(s, syftPluginGrpc.NewCatalogServer(p.Impl, broker))
return nil
}
func (p *CatalogerPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
return syftPluginGrpc.NewCatalogerClient(proto.NewCatalogerClient(c), broker), nil
}
+10
View File
@@ -0,0 +1,10 @@
package plugin
type Config struct {
Name string
Type Type
Command string
Args []string
Env []string
Sha256 []byte
}
+117
View File
@@ -0,0 +1,117 @@
package grpc
import (
"context"
"fmt"
"io"
"io/ioutil"
"github.com/anchore/syft/syft/cataloger"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/plugin/proto"
"github.com/anchore/syft/syft/scope"
"github.com/hashicorp/go-plugin"
"google.golang.org/grpc"
)
var _ cataloger.Cataloger = &CatalogerClient{}
type CatalogerClient struct {
broker *plugin.GRPCBroker
client proto.CatalogerClient
}
func NewCatalogerClient(client proto.CatalogerClient, broker *plugin.GRPCBroker) *CatalogerClient {
return &CatalogerClient{
broker: broker,
client: client,
}
}
func (c *CatalogerClient) Name() string {
resp, err := c.client.Name(context.Background(), &proto.Empty{})
if err != nil {
return fmt.Sprintf("error (%s)", err.Error())
}
return resp.Name
}
func (c *CatalogerClient) SelectFiles(resolver scope.FileResolver) []file.Reference {
fileResolverServer := &FileResolverServer{Impl: resolver}
var s *grpc.Server
serverFunc := func(opts []grpc.ServerOption) *grpc.Server {
s = grpc.NewServer(opts...)
proto.RegisterFileResolverServer(s, fileResolverServer)
return s
}
brokerID := c.broker.NextId()
go c.broker.AcceptAndServe(brokerID, serverFunc)
resp, err := c.client.SelectFiles(context.Background(), &proto.SelectFilesRequest{
FileResolverBrokerId: brokerID,
})
if err != nil {
// TODO: nope
panic(err)
}
s.Stop()
var result []file.Reference
for _, f := range resp.Files {
result = append(result, file.NewFileReferenceWithID(file.Path(f.Path), uint64(f.Id)))
}
return result
}
func (c *CatalogerClient) Catalog(contents map[file.Reference]io.Reader) ([]pkg.Package, error) {
var fileRefContents []*proto.FileReferenceContents
for ref, reader := range contents {
readerContents, err := ioutil.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("could not read %+v contents: %+v", ref, err)
}
fileRefContents = append(fileRefContents, &proto.FileReferenceContents{
Id: int64(ref.ID()),
Path: string(ref.Path),
Contents: string(readerContents),
})
}
resp, err := c.client.Catalog(context.Background(), &proto.CatalogRequest{
Contents: fileRefContents,
})
if err != nil {
return nil, err
}
var result []pkg.Package
for _, p := range resp.Package {
if p != nil {
var sources []file.Reference
for _, s := range p.Source {
sources = append(sources, file.NewFileReferenceWithID(file.Path(s.Path), uint64(s.Id)))
}
// TODO: this is potentially brittle
result = append(result, pkg.Package{
Name: p.Name,
Version: p.Version,
FoundBy: p.FoundBy,
Source: sources,
Licenses: p.Licenses,
Language: pkg.Language(p.Language),
Type: pkg.Type(p.Type),
Metadata: p.Metadata,
})
}
}
return result, nil
}
+99
View File
@@ -0,0 +1,99 @@
package grpc
import (
"context"
"io"
"strings"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/cataloger"
"github.com/anchore/syft/syft/plugin/proto"
"github.com/hashicorp/go-plugin"
)
type CatalogerServer struct {
Impl cataloger.Cataloger
broker *plugin.GRPCBroker
}
func NewCatalogServer(impl cataloger.Cataloger, broker *plugin.GRPCBroker) *CatalogerServer {
return &CatalogerServer{
Impl: impl,
broker: broker,
}
}
func (s *CatalogerServer) Name(ctx context.Context, req *proto.Empty) (*proto.NameResponse, error) {
name := s.Impl.Name()
return &proto.NameResponse{
Name: name,
}, nil
}
func (s *CatalogerServer) SelectFiles(ctx context.Context, req *proto.SelectFilesRequest) (*proto.SelectFilesResponse, error) {
conn, err := s.broker.Dial(req.FileResolverBrokerId)
if err != nil {
return nil, err
}
defer conn.Close()
fileResolverClient := &FileResolverClient{proto.NewFileResolverClient(conn)}
fileRefs := s.Impl.SelectFiles(fileResolverClient)
var refs []*proto.FileReference
for _, ref := range fileRefs {
refs = append(refs, &proto.FileReference{
Id: int64(ref.ID()),
Path: string(ref.Path),
})
}
return &proto.SelectFilesResponse{
Files: refs,
}, nil
}
func (s *CatalogerServer) Catalog(ctx context.Context, req *proto.CatalogRequest) (*proto.CatalogResponse, error) {
contents := make(map[file.Reference]io.Reader)
for _, f := range req.Contents {
contents[file.NewFileReferenceWithID(file.Path(f.Path), uint64(f.Id))] = strings.NewReader(f.Contents)
}
packages, err := s.Impl.Catalog(contents)
if err != nil {
return nil, err
}
var results []*proto.Package
for _, p := range packages {
var sources []*proto.FileReference
for _, s := range p.Source {
sources = append(sources, &proto.FileReference{
Id: int64(s.ID()),
Path: string(s.Path),
})
}
var metadata map[string]string
if v, ok := p.Metadata.(map[string]string); ok {
metadata = v
}
// TODO: this is potentially brittle
results = append(results, &proto.Package{
Name: p.Name,
Version: p.Version,
FoundBy: p.FoundBy,
Source: sources,
Licenses: p.Licenses,
Language: uint64(p.Language),
Type: string(p.Type),
// TODO: metadata needs to be more thoroughly thought through
Metadata: metadata,
})
}
return &proto.CatalogResponse{
Package: results,
}, nil
}
+50
View File
@@ -0,0 +1,50 @@
package grpc
import (
"context"
"github.com/anchore/syft/internal/log"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/plugin/proto"
)
type FileResolverClient struct{ client proto.FileResolverClient }
func (m *FileResolverClient) FilesByPath(paths ...file.Path) ([]file.Reference, error) {
var pathStrs []string
for _, p := range paths {
pathStrs = append(pathStrs, string(p))
}
resp, err := m.client.FilesByPath(context.Background(), &proto.FileResolverRequest{
Paths: pathStrs,
})
if err != nil {
return nil, err
}
var result []file.Reference
for _, ref := range resp.Files {
result = append(result, file.NewFileReferenceWithID(file.Path(ref.Path), uint64(ref.Id)))
}
return result, err
}
func (m *FileResolverClient) FilesByGlob(patterns ...string) ([]file.Reference, error) {
resp, err := m.client.FilesByGlob(context.Background(), &proto.FileResolverRequest{
Paths: patterns,
})
if err != nil {
return nil, err
}
log.Debugf("FilesByGlob Response: %+v", resp)
var result []file.Reference
for _, ref := range resp.Files {
result = append(result, file.NewFileReferenceWithID(file.Path(ref.Path), uint64(ref.Id)))
}
return result, err
}
+55
View File
@@ -0,0 +1,55 @@
package grpc
import (
"context"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/plugin/proto"
"github.com/anchore/syft/syft/scope"
)
type FileResolverServer struct {
Impl scope.FileResolver
}
func (m *FileResolverServer) FilesByPath(ctx context.Context, req *proto.FileResolverRequest) (resp *proto.FileResolverResponse, err error) {
var paths []file.Path
for _, p := range req.Paths {
paths = append(paths, file.Path(p))
}
r, err := m.Impl.FilesByPath(paths...)
if err != nil {
return nil, err
}
var refs []*proto.FileReference
for _, ref := range r {
refs = append(refs, &proto.FileReference{
Id: int64(ref.ID()),
Path: string(ref.Path),
})
}
return &proto.FileResolverResponse{
Files: refs,
}, nil
}
func (m *FileResolverServer) FilesByGlob(ctx context.Context, req *proto.FileResolverRequest) (resp *proto.FileResolverResponse, err error) {
r, err := m.Impl.FilesByGlob(req.Paths...)
if err != nil {
return nil, err
}
var refs []*proto.FileReference
for _, ref := range r {
refs = append(refs, &proto.FileReference{
Id: int64(ref.ID()),
Path: string(ref.Path),
})
}
return &proto.FileResolverResponse{
Files: refs,
}, nil
}
+96
View File
@@ -0,0 +1,96 @@
package plugin
import (
"crypto/sha256"
"fmt"
"os/exec"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/logger"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
)
var versionedPlugins = map[int]plugin.PluginSet{
1: {
TypeCataloger.String(): &CatalogerPlugin{},
},
}
type Plugin struct {
Config Config
clientConfig plugin.ClientConfig
client *plugin.Client
}
// TODO: type should be in the name like with terraform "terraform-<TYPE>-<NAME>"
func NewPlugin(config Config) Plugin {
//cmd := exec.Command("sh", "-c", config.Command) //, config.Args...)
cmd := exec.Command(config.Command)
cmd.Env = append(cmd.Env, config.Env...)
var secureConfig *plugin.SecureConfig
if len(config.Sha256) > 0 {
secureConfig = &plugin.SecureConfig{
Checksum: config.Sha256,
Hash: sha256.New(),
}
}
var pluginLogger hclog.Logger
if logrusLogger, ok := log.Log.(*logger.LogrusLogger); ok {
pluginLogger = logger.NewLogrusHCLogAdapter(logrusLogger.Logger, map[string]interface{}{"plugin": config.Name})
} else {
// TODO: this does not fully map features, thus logging will be awkward
// TODO: find a better way to map loggers (expand our interface?)
pluginLogger = logger.NewLoggerHCLogAdapter(log.Log)
}
clientConfig := plugin.ClientConfig{
HandshakeConfig: config.Type.HandshakeConfig(),
VersionedPlugins: versionedPlugins,
SecureConfig: secureConfig,
Cmd: cmd,
Logger: pluginLogger,
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolGRPC,
},
}
return Plugin{
Config: config,
clientConfig: clientConfig,
}
}
func (p *Plugin) Start() (interface{}, error) {
if p.client != nil {
return nil, fmt.Errorf("plugin already started")
}
// start the plugin in a sub process
p.client = plugin.NewClient(&p.clientConfig)
// connect to the sub process via RPC
rpcClient, err := p.client.Client()
if err != nil {
return nil, err
}
// fetch the plugin object meeting the requested interface
raw, err := rpcClient.Dispense(p.Config.Type.String())
if err != nil {
return nil, err
}
return raw, nil
}
func (p *Plugin) Stop() error {
if p.client == nil {
return fmt.Errorf("plugin has not been started")
}
p.client.Kill()
return nil
}
+841
View File
@@ -0,0 +1,841 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: cataloger.proto
package proto
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type FileReference struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FileReference) Reset() { *m = FileReference{} }
func (m *FileReference) String() string { return proto.CompactTextString(m) }
func (*FileReference) ProtoMessage() {}
func (*FileReference) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{0}
}
func (m *FileReference) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FileReference.Unmarshal(m, b)
}
func (m *FileReference) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FileReference.Marshal(b, m, deterministic)
}
func (m *FileReference) XXX_Merge(src proto.Message) {
xxx_messageInfo_FileReference.Merge(m, src)
}
func (m *FileReference) XXX_Size() int {
return xxx_messageInfo_FileReference.Size(m)
}
func (m *FileReference) XXX_DiscardUnknown() {
xxx_messageInfo_FileReference.DiscardUnknown(m)
}
var xxx_messageInfo_FileReference proto.InternalMessageInfo
func (m *FileReference) GetId() int64 {
if m != nil {
return m.Id
}
return 0
}
func (m *FileReference) GetPath() string {
if m != nil {
return m.Path
}
return ""
}
type FileReferenceContents struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
Contents string `protobuf:"bytes,3,opt,name=contents,proto3" json:"contents,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FileReferenceContents) Reset() { *m = FileReferenceContents{} }
func (m *FileReferenceContents) String() string { return proto.CompactTextString(m) }
func (*FileReferenceContents) ProtoMessage() {}
func (*FileReferenceContents) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{1}
}
func (m *FileReferenceContents) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FileReferenceContents.Unmarshal(m, b)
}
func (m *FileReferenceContents) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FileReferenceContents.Marshal(b, m, deterministic)
}
func (m *FileReferenceContents) XXX_Merge(src proto.Message) {
xxx_messageInfo_FileReferenceContents.Merge(m, src)
}
func (m *FileReferenceContents) XXX_Size() int {
return xxx_messageInfo_FileReferenceContents.Size(m)
}
func (m *FileReferenceContents) XXX_DiscardUnknown() {
xxx_messageInfo_FileReferenceContents.DiscardUnknown(m)
}
var xxx_messageInfo_FileReferenceContents proto.InternalMessageInfo
func (m *FileReferenceContents) GetId() int64 {
if m != nil {
return m.Id
}
return 0
}
func (m *FileReferenceContents) GetPath() string {
if m != nil {
return m.Path
}
return ""
}
func (m *FileReferenceContents) GetContents() string {
if m != nil {
return m.Contents
}
return ""
}
type Empty struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{2}
}
func (m *Empty) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Empty.Unmarshal(m, b)
}
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
}
func (m *Empty) XXX_Merge(src proto.Message) {
xxx_messageInfo_Empty.Merge(m, src)
}
func (m *Empty) XXX_Size() int {
return xxx_messageInfo_Empty.Size(m)
}
func (m *Empty) XXX_DiscardUnknown() {
xxx_messageInfo_Empty.DiscardUnknown(m)
}
var xxx_messageInfo_Empty proto.InternalMessageInfo
type FileResolverRequest struct {
Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FileResolverRequest) Reset() { *m = FileResolverRequest{} }
func (m *FileResolverRequest) String() string { return proto.CompactTextString(m) }
func (*FileResolverRequest) ProtoMessage() {}
func (*FileResolverRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{3}
}
func (m *FileResolverRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FileResolverRequest.Unmarshal(m, b)
}
func (m *FileResolverRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FileResolverRequest.Marshal(b, m, deterministic)
}
func (m *FileResolverRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_FileResolverRequest.Merge(m, src)
}
func (m *FileResolverRequest) XXX_Size() int {
return xxx_messageInfo_FileResolverRequest.Size(m)
}
func (m *FileResolverRequest) XXX_DiscardUnknown() {
xxx_messageInfo_FileResolverRequest.DiscardUnknown(m)
}
var xxx_messageInfo_FileResolverRequest proto.InternalMessageInfo
func (m *FileResolverRequest) GetPaths() []string {
if m != nil {
return m.Paths
}
return nil
}
type FileResolverResponse struct {
Files []*FileReference `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *FileResolverResponse) Reset() { *m = FileResolverResponse{} }
func (m *FileResolverResponse) String() string { return proto.CompactTextString(m) }
func (*FileResolverResponse) ProtoMessage() {}
func (*FileResolverResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{4}
}
func (m *FileResolverResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FileResolverResponse.Unmarshal(m, b)
}
func (m *FileResolverResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_FileResolverResponse.Marshal(b, m, deterministic)
}
func (m *FileResolverResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_FileResolverResponse.Merge(m, src)
}
func (m *FileResolverResponse) XXX_Size() int {
return xxx_messageInfo_FileResolverResponse.Size(m)
}
func (m *FileResolverResponse) XXX_DiscardUnknown() {
xxx_messageInfo_FileResolverResponse.DiscardUnknown(m)
}
var xxx_messageInfo_FileResolverResponse proto.InternalMessageInfo
func (m *FileResolverResponse) GetFiles() []*FileReference {
if m != nil {
return m.Files
}
return nil
}
type NameResponse struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *NameResponse) Reset() { *m = NameResponse{} }
func (m *NameResponse) String() string { return proto.CompactTextString(m) }
func (*NameResponse) ProtoMessage() {}
func (*NameResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{5}
}
func (m *NameResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NameResponse.Unmarshal(m, b)
}
func (m *NameResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_NameResponse.Marshal(b, m, deterministic)
}
func (m *NameResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_NameResponse.Merge(m, src)
}
func (m *NameResponse) XXX_Size() int {
return xxx_messageInfo_NameResponse.Size(m)
}
func (m *NameResponse) XXX_DiscardUnknown() {
xxx_messageInfo_NameResponse.DiscardUnknown(m)
}
var xxx_messageInfo_NameResponse proto.InternalMessageInfo
func (m *NameResponse) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type SelectFilesRequest struct {
FileResolverBrokerId uint32 `protobuf:"varint,1,opt,name=fileResolverBrokerId,proto3" json:"fileResolverBrokerId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SelectFilesRequest) Reset() { *m = SelectFilesRequest{} }
func (m *SelectFilesRequest) String() string { return proto.CompactTextString(m) }
func (*SelectFilesRequest) ProtoMessage() {}
func (*SelectFilesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{6}
}
func (m *SelectFilesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SelectFilesRequest.Unmarshal(m, b)
}
func (m *SelectFilesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SelectFilesRequest.Marshal(b, m, deterministic)
}
func (m *SelectFilesRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_SelectFilesRequest.Merge(m, src)
}
func (m *SelectFilesRequest) XXX_Size() int {
return xxx_messageInfo_SelectFilesRequest.Size(m)
}
func (m *SelectFilesRequest) XXX_DiscardUnknown() {
xxx_messageInfo_SelectFilesRequest.DiscardUnknown(m)
}
var xxx_messageInfo_SelectFilesRequest proto.InternalMessageInfo
func (m *SelectFilesRequest) GetFileResolverBrokerId() uint32 {
if m != nil {
return m.FileResolverBrokerId
}
return 0
}
type SelectFilesResponse struct {
Files []*FileReference `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SelectFilesResponse) Reset() { *m = SelectFilesResponse{} }
func (m *SelectFilesResponse) String() string { return proto.CompactTextString(m) }
func (*SelectFilesResponse) ProtoMessage() {}
func (*SelectFilesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{7}
}
func (m *SelectFilesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SelectFilesResponse.Unmarshal(m, b)
}
func (m *SelectFilesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SelectFilesResponse.Marshal(b, m, deterministic)
}
func (m *SelectFilesResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_SelectFilesResponse.Merge(m, src)
}
func (m *SelectFilesResponse) XXX_Size() int {
return xxx_messageInfo_SelectFilesResponse.Size(m)
}
func (m *SelectFilesResponse) XXX_DiscardUnknown() {
xxx_messageInfo_SelectFilesResponse.DiscardUnknown(m)
}
var xxx_messageInfo_SelectFilesResponse proto.InternalMessageInfo
func (m *SelectFilesResponse) GetFiles() []*FileReference {
if m != nil {
return m.Files
}
return nil
}
type Package struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
FoundBy string `protobuf:"bytes,3,opt,name=foundBy,proto3" json:"foundBy,omitempty"`
Source []*FileReference `protobuf:"bytes,4,rep,name=source,proto3" json:"source,omitempty"`
Licenses []string `protobuf:"bytes,5,rep,name=licenses,proto3" json:"licenses,omitempty"`
Language uint64 `protobuf:"varint,6,opt,name=language,proto3" json:"language,omitempty"`
Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"`
Metadata map[string]string `protobuf:"bytes,8,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Package) Reset() { *m = Package{} }
func (m *Package) String() string { return proto.CompactTextString(m) }
func (*Package) ProtoMessage() {}
func (*Package) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{8}
}
func (m *Package) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Package.Unmarshal(m, b)
}
func (m *Package) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Package.Marshal(b, m, deterministic)
}
func (m *Package) XXX_Merge(src proto.Message) {
xxx_messageInfo_Package.Merge(m, src)
}
func (m *Package) XXX_Size() int {
return xxx_messageInfo_Package.Size(m)
}
func (m *Package) XXX_DiscardUnknown() {
xxx_messageInfo_Package.DiscardUnknown(m)
}
var xxx_messageInfo_Package proto.InternalMessageInfo
func (m *Package) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Package) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Package) GetFoundBy() string {
if m != nil {
return m.FoundBy
}
return ""
}
func (m *Package) GetSource() []*FileReference {
if m != nil {
return m.Source
}
return nil
}
func (m *Package) GetLicenses() []string {
if m != nil {
return m.Licenses
}
return nil
}
func (m *Package) GetLanguage() uint64 {
if m != nil {
return m.Language
}
return 0
}
func (m *Package) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *Package) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
type CatalogRequest struct {
Contents []*FileReferenceContents `protobuf:"bytes,1,rep,name=contents,proto3" json:"contents,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CatalogRequest) Reset() { *m = CatalogRequest{} }
func (m *CatalogRequest) String() string { return proto.CompactTextString(m) }
func (*CatalogRequest) ProtoMessage() {}
func (*CatalogRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{9}
}
func (m *CatalogRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CatalogRequest.Unmarshal(m, b)
}
func (m *CatalogRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CatalogRequest.Marshal(b, m, deterministic)
}
func (m *CatalogRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CatalogRequest.Merge(m, src)
}
func (m *CatalogRequest) XXX_Size() int {
return xxx_messageInfo_CatalogRequest.Size(m)
}
func (m *CatalogRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CatalogRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CatalogRequest proto.InternalMessageInfo
func (m *CatalogRequest) GetContents() []*FileReferenceContents {
if m != nil {
return m.Contents
}
return nil
}
type CatalogResponse struct {
Package []*Package `protobuf:"bytes,1,rep,name=package,proto3" json:"package,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CatalogResponse) Reset() { *m = CatalogResponse{} }
func (m *CatalogResponse) String() string { return proto.CompactTextString(m) }
func (*CatalogResponse) ProtoMessage() {}
func (*CatalogResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_346aaad5d7f2c092, []int{10}
}
func (m *CatalogResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CatalogResponse.Unmarshal(m, b)
}
func (m *CatalogResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CatalogResponse.Marshal(b, m, deterministic)
}
func (m *CatalogResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CatalogResponse.Merge(m, src)
}
func (m *CatalogResponse) XXX_Size() int {
return xxx_messageInfo_CatalogResponse.Size(m)
}
func (m *CatalogResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CatalogResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CatalogResponse proto.InternalMessageInfo
func (m *CatalogResponse) GetPackage() []*Package {
if m != nil {
return m.Package
}
return nil
}
func init() {
proto.RegisterType((*FileReference)(nil), "proto.FileReference")
proto.RegisterType((*FileReferenceContents)(nil), "proto.FileReferenceContents")
proto.RegisterType((*Empty)(nil), "proto.Empty")
proto.RegisterType((*FileResolverRequest)(nil), "proto.FileResolverRequest")
proto.RegisterType((*FileResolverResponse)(nil), "proto.FileResolverResponse")
proto.RegisterType((*NameResponse)(nil), "proto.NameResponse")
proto.RegisterType((*SelectFilesRequest)(nil), "proto.SelectFilesRequest")
proto.RegisterType((*SelectFilesResponse)(nil), "proto.SelectFilesResponse")
proto.RegisterType((*Package)(nil), "proto.Package")
proto.RegisterMapType((map[string]string)(nil), "proto.Package.MetadataEntry")
proto.RegisterType((*CatalogRequest)(nil), "proto.CatalogRequest")
proto.RegisterType((*CatalogResponse)(nil), "proto.CatalogResponse")
}
func init() { proto.RegisterFile("cataloger.proto", fileDescriptor_346aaad5d7f2c092) }
var fileDescriptor_346aaad5d7f2c092 = []byte{
// 541 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x53, 0xcb, 0x8e, 0xd3, 0x3c,
0x14, 0x56, 0x7a, 0x4b, 0x7b, 0x7a, 0x99, 0x5f, 0x6e, 0xe7, 0x57, 0x08, 0x2c, 0xaa, 0xac, 0xca,
0x45, 0x5d, 0x74, 0x36, 0x15, 0xb3, 0xa2, 0x65, 0x86, 0x8b, 0x04, 0x1a, 0x99, 0x05, 0x6b, 0x4f,
0x7a, 0x5a, 0xa2, 0xa6, 0x71, 0x88, 0xdd, 0x4a, 0x79, 0x19, 0x1e, 0x83, 0x07, 0xe0, 0xc9, 0x50,
0x6c, 0x27, 0xd4, 0x10, 0x21, 0x34, 0xab, 0xf8, 0xe4, 0xfb, 0xfc, 0x9d, 0xcf, 0xe7, 0x02, 0x17,
0x21, 0x93, 0x2c, 0xe6, 0x3b, 0xcc, 0xe6, 0x69, 0xc6, 0x25, 0x27, 0x6d, 0xf5, 0x09, 0xae, 0x60,
0x78, 0x1b, 0xc5, 0x48, 0x71, 0x8b, 0x19, 0x26, 0x21, 0x92, 0x11, 0x34, 0xa2, 0x8d, 0xe7, 0x4c,
0x9d, 0x59, 0x93, 0x36, 0xa2, 0x0d, 0x21, 0xd0, 0x4a, 0x99, 0xfc, 0xe2, 0x35, 0xa6, 0xce, 0xac,
0x47, 0xd5, 0x39, 0xf8, 0x0c, 0x97, 0xd6, 0xa5, 0x35, 0x4f, 0x24, 0x26, 0x52, 0xfc, 0xcb, 0x65,
0xe2, 0x43, 0x37, 0x34, 0x7c, 0xaf, 0xa9, 0xfe, 0x57, 0x71, 0xe0, 0x42, 0xfb, 0xe6, 0x90, 0xca,
0x3c, 0x78, 0x0e, 0x63, 0x9d, 0x41, 0xf0, 0xf8, 0x84, 0x19, 0xc5, 0xaf, 0x47, 0x14, 0x92, 0x4c,
0xa0, 0x5d, 0x68, 0x08, 0xcf, 0x99, 0x36, 0x67, 0x3d, 0xaa, 0x83, 0x60, 0x05, 0x13, 0x9b, 0x2c,
0x52, 0x9e, 0x08, 0x24, 0xcf, 0xa0, 0xbd, 0x8d, 0x62, 0xd4, 0xec, 0xfe, 0x62, 0xa2, 0x5f, 0x3e,
0xb7, 0xac, 0x53, 0x4d, 0x09, 0x02, 0x18, 0x7c, 0x64, 0x07, 0xac, 0xee, 0x12, 0x68, 0x25, 0xec,
0x80, 0xea, 0x2d, 0x3d, 0xaa, 0xce, 0xc1, 0x5b, 0x20, 0x9f, 0x30, 0xc6, 0x50, 0x16, 0x0a, 0xa2,
0xf4, 0xb4, 0x80, 0xc9, 0xf6, 0x2c, 0xfb, 0x2a, 0xe3, 0x7b, 0xcc, 0xde, 0xe9, 0x2a, 0x0c, 0x69,
0x2d, 0x16, 0xbc, 0x82, 0xb1, 0xa5, 0xf4, 0x00, 0xc3, 0x3f, 0x1a, 0xe0, 0xde, 0xb1, 0x70, 0xcf,
0x76, 0xb5, 0x66, 0x89, 0x07, 0xee, 0x09, 0x33, 0x11, 0xf1, 0xc4, 0x54, 0xbf, 0x0c, 0x0b, 0x64,
0xcb, 0x8f, 0xc9, 0x66, 0x95, 0x9b, 0xfa, 0x97, 0x21, 0x79, 0x01, 0x1d, 0xc1, 0x8f, 0x59, 0x88,
0x5e, 0xeb, 0x2f, 0x06, 0x0c, 0xa7, 0x68, 0x64, 0x1c, 0x85, 0x98, 0x08, 0x14, 0x5e, 0x5b, 0xf5,
0xa3, 0x8a, 0x15, 0xc6, 0x92, 0xdd, 0x91, 0xed, 0xd0, 0xeb, 0x4c, 0x9d, 0x59, 0x8b, 0x56, 0x71,
0xe1, 0x56, 0xe6, 0x29, 0x7a, 0xae, 0x76, 0x5b, 0x9c, 0xc9, 0x12, 0xba, 0x07, 0x94, 0x6c, 0xc3,
0x24, 0xf3, 0xba, 0x2a, 0xf7, 0x13, 0x93, 0xdb, 0xbc, 0x71, 0xfe, 0xc1, 0xc0, 0x37, 0x89, 0xcc,
0x72, 0x5a, 0xb1, 0xfd, 0x6b, 0x18, 0x5a, 0x10, 0xf9, 0x0f, 0x9a, 0x7b, 0xcc, 0x4d, 0x2d, 0x8a,
0x63, 0x31, 0x35, 0x27, 0x16, 0x1f, 0xd1, 0x14, 0x42, 0x07, 0x2f, 0x1b, 0x4b, 0x27, 0x78, 0x0f,
0xa3, 0xb5, 0xde, 0x8b, 0xb2, 0x9b, 0xcb, 0xb3, 0xe9, 0x74, 0x2c, 0x23, 0xb5, 0x13, 0x7f, 0x36,
0xbb, 0xd7, 0x70, 0x51, 0x69, 0x99, 0x7e, 0xce, 0xc0, 0x4d, 0xb5, 0x7d, 0xa3, 0x35, 0xb2, 0x1f,
0x45, 0x4b, 0x78, 0xf1, 0xdd, 0x81, 0xde, 0xba, 0xdc, 0x50, 0xf2, 0x14, 0x5a, 0xc5, 0x30, 0x92,
0x81, 0xa1, 0xab, 0x9d, 0xf0, 0xc7, 0x26, 0xb2, 0xe6, 0xf4, 0x35, 0xf4, 0xcf, 0x26, 0x89, 0x3c,
0x32, 0x9c, 0x3f, 0xe7, 0xd4, 0xf7, 0xeb, 0x20, 0xa3, 0xb2, 0x04, 0xd7, 0x64, 0x27, 0x97, 0x86,
0x66, 0xd7, 0xc5, 0xff, 0xff, 0xf7, 0xdf, 0xfa, 0xe6, 0xe2, 0x9b, 0x03, 0x83, 0xf3, 0xe5, 0x23,
0xb7, 0xd0, 0x57, 0xda, 0xab, 0xfc, 0x4e, 0x6d, 0xbb, 0x55, 0x3d, 0x6b, 0x9b, 0xfd, 0xc7, 0xb5,
0x98, 0xb1, 0xf4, 0x4b, 0xe7, 0x4d, 0xcc, 0xef, 0x1f, 0xac, 0x73, 0xdf, 0x51, 0xd8, 0xd5, 0xcf,
0x00, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xfe, 0x04, 0x3e, 0x01, 0x05, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// CatalogerClient is the client API for Cataloger service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type CatalogerClient interface {
Name(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*NameResponse, error)
SelectFiles(ctx context.Context, in *SelectFilesRequest, opts ...grpc.CallOption) (*SelectFilesResponse, error)
Catalog(ctx context.Context, in *CatalogRequest, opts ...grpc.CallOption) (*CatalogResponse, error)
}
type catalogerClient struct {
cc grpc.ClientConnInterface
}
func NewCatalogerClient(cc grpc.ClientConnInterface) CatalogerClient {
return &catalogerClient{cc}
}
func (c *catalogerClient) Name(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*NameResponse, error) {
out := new(NameResponse)
err := c.cc.Invoke(ctx, "/proto.Cataloger/Name", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *catalogerClient) SelectFiles(ctx context.Context, in *SelectFilesRequest, opts ...grpc.CallOption) (*SelectFilesResponse, error) {
out := new(SelectFilesResponse)
err := c.cc.Invoke(ctx, "/proto.Cataloger/SelectFiles", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *catalogerClient) Catalog(ctx context.Context, in *CatalogRequest, opts ...grpc.CallOption) (*CatalogResponse, error) {
out := new(CatalogResponse)
err := c.cc.Invoke(ctx, "/proto.Cataloger/Catalog", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// CatalogerServer is the server API for Cataloger service.
type CatalogerServer interface {
Name(context.Context, *Empty) (*NameResponse, error)
SelectFiles(context.Context, *SelectFilesRequest) (*SelectFilesResponse, error)
Catalog(context.Context, *CatalogRequest) (*CatalogResponse, error)
}
// UnimplementedCatalogerServer can be embedded to have forward compatible implementations.
type UnimplementedCatalogerServer struct {
}
func (*UnimplementedCatalogerServer) Name(ctx context.Context, req *Empty) (*NameResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Name not implemented")
}
func (*UnimplementedCatalogerServer) SelectFiles(ctx context.Context, req *SelectFilesRequest) (*SelectFilesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SelectFiles not implemented")
}
func (*UnimplementedCatalogerServer) Catalog(ctx context.Context, req *CatalogRequest) (*CatalogResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Catalog not implemented")
}
func RegisterCatalogerServer(s *grpc.Server, srv CatalogerServer) {
s.RegisterService(&_Cataloger_serviceDesc, srv)
}
func _Cataloger_Name_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CatalogerServer).Name(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.Cataloger/Name",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CatalogerServer).Name(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Cataloger_SelectFiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SelectFilesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CatalogerServer).SelectFiles(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.Cataloger/SelectFiles",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CatalogerServer).SelectFiles(ctx, req.(*SelectFilesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Cataloger_Catalog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CatalogRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(CatalogerServer).Catalog(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.Cataloger/Catalog",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(CatalogerServer).Catalog(ctx, req.(*CatalogRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Cataloger_serviceDesc = grpc.ServiceDesc{
ServiceName: "proto.Cataloger",
HandlerType: (*CatalogerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Name",
Handler: _Cataloger_Name_Handler,
},
{
MethodName: "SelectFiles",
Handler: _Cataloger_SelectFiles_Handler,
},
{
MethodName: "Catalog",
Handler: _Cataloger_Catalog_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "cataloger.proto",
}
// FileResolverClient is the client API for FileResolver service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type FileResolverClient interface {
FilesByPath(ctx context.Context, in *FileResolverRequest, opts ...grpc.CallOption) (*FileResolverResponse, error)
FilesByGlob(ctx context.Context, in *FileResolverRequest, opts ...grpc.CallOption) (*FileResolverResponse, error)
}
type fileResolverClient struct {
cc grpc.ClientConnInterface
}
func NewFileResolverClient(cc grpc.ClientConnInterface) FileResolverClient {
return &fileResolverClient{cc}
}
func (c *fileResolverClient) FilesByPath(ctx context.Context, in *FileResolverRequest, opts ...grpc.CallOption) (*FileResolverResponse, error) {
out := new(FileResolverResponse)
err := c.cc.Invoke(ctx, "/proto.FileResolver/FilesByPath", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *fileResolverClient) FilesByGlob(ctx context.Context, in *FileResolverRequest, opts ...grpc.CallOption) (*FileResolverResponse, error) {
out := new(FileResolverResponse)
err := c.cc.Invoke(ctx, "/proto.FileResolver/FilesByGlob", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// FileResolverServer is the server API for FileResolver service.
type FileResolverServer interface {
FilesByPath(context.Context, *FileResolverRequest) (*FileResolverResponse, error)
FilesByGlob(context.Context, *FileResolverRequest) (*FileResolverResponse, error)
}
// UnimplementedFileResolverServer can be embedded to have forward compatible implementations.
type UnimplementedFileResolverServer struct {
}
func (*UnimplementedFileResolverServer) FilesByPath(ctx context.Context, req *FileResolverRequest) (*FileResolverResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FilesByPath not implemented")
}
func (*UnimplementedFileResolverServer) FilesByGlob(ctx context.Context, req *FileResolverRequest) (*FileResolverResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method FilesByGlob not implemented")
}
func RegisterFileResolverServer(s *grpc.Server, srv FileResolverServer) {
s.RegisterService(&_FileResolver_serviceDesc, srv)
}
func _FileResolver_FilesByPath_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FileResolverRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(FileResolverServer).FilesByPath(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.FileResolver/FilesByPath",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FileResolverServer).FilesByPath(ctx, req.(*FileResolverRequest))
}
return interceptor(ctx, in, info, handler)
}
func _FileResolver_FilesByGlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FileResolverRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(FileResolverServer).FilesByGlob(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.FileResolver/FilesByGlob",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(FileResolverServer).FilesByGlob(ctx, req.(*FileResolverRequest))
}
return interceptor(ctx, in, info, handler)
}
var _FileResolver_serviceDesc = grpc.ServiceDesc{
ServiceName: "proto.FileResolver",
HandlerType: (*FileResolverServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "FilesByPath",
Handler: _FileResolver_FilesByPath_Handler,
},
{
MethodName: "FilesByGlob",
Handler: _FileResolver_FilesByGlob_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "cataloger.proto",
}
+67
View File
@@ -0,0 +1,67 @@
syntax = "proto3";
package proto;
message FileReference {
int64 id = 1;
string path = 2;
}
message FileReferenceContents {
int64 id = 1;
string path = 2;
string contents = 3;
}
message Empty {
}
message FileResolverRequest {
repeated string paths = 1;
}
message FileResolverResponse {
repeated FileReference files = 1;
}
message NameResponse {
string name = 1;
}
message SelectFilesRequest {
uint32 fileResolverBrokerId = 1;
}
message SelectFilesResponse {
repeated FileReference files = 1;
}
message Package {
string name = 1;
string version = 2;
string foundBy = 3;
repeated FileReference source = 4;
repeated string licenses = 5;
uint64 language = 6;
string type = 7;
map<string, string> metadata = 8;
}
message CatalogRequest {
repeated FileReferenceContents contents = 1;
}
message CatalogResponse {
repeated Package package = 1;
}
service Cataloger {
rpc Name(Empty) returns (NameResponse);
rpc SelectFiles(SelectFilesRequest) returns (SelectFilesResponse);
rpc Catalog(CatalogRequest) returns (CatalogResponse);
}
service FileResolver {
rpc FilesByPath(FileResolverRequest) returns (FileResolverResponse);
rpc FilesByGlob(FileResolverRequest) returns (FileResolverResponse);
}
+119
View File
@@ -0,0 +1,119 @@
package plugin
import (
"fmt"
"path/filepath"
"strings"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cataloger"
)
type Repository struct {
byType map[Type][]Plugin
}
func NewRepository() *Repository {
return &Repository{
byType: make(map[Type][]Plugin),
}
}
func NewRepositoryFromDirectory(dir string) (*Repository, error) {
repo := NewRepository()
if dir == "" {
return repo, nil
}
return repo, repo.AddFromDirectory(dir)
}
func (c *Repository) Add(plugin Plugin) {
c.byType[plugin.Config.Type] = append(c.byType[plugin.Config.Type], plugin)
}
func (c *Repository) AddFromDirectory(dir string, pluginTypes ...Type) error {
plugins, err := Discover(dir, pluginTypes...)
if err != nil {
return err
}
for _, plugin := range plugins {
log.Debugf("added plugin: %q", plugin.Config.Name)
c.Add(plugin)
}
log.Debugf("%d plugins added", len(plugins))
return nil
}
func (c *Repository) Get(pluginType Type) []Plugin {
return c.byType[pluginType]
}
func (c *Repository) ActivateCatalogers() ([]cataloger.Cataloger, func(), error) {
var result []cataloger.Cataloger
var plugins []Plugin
var deactivateFn = func() {
for _, plugin := range plugins {
if err := plugin.Stop(); err != nil {
log.Errorf("failed to stop plugin: %w", err)
}
}
}
for _, plugin := range c.Get(TypeCataloger) {
raw, err := plugin.Start()
if err != nil {
return nil, deactivateFn, err
}
plugins = append(plugins, plugin)
theCataloger, ok := raw.(cataloger.Cataloger)
if !ok {
return nil, deactivateFn, fmt.Errorf("activation of cataloger did not return a cataloger object (name=%s type=%T)", plugin.Config.Name, theCataloger)
}
result = append(result, theCataloger)
}
return result, deactivateFn, nil
}
func Discover(dir string, pluginTypes ...Type) ([]Plugin, error) {
var err error
if len(pluginTypes) == 0 {
pluginTypes = AllTypes
}
if !filepath.IsAbs(dir) {
dir, err = filepath.Abs(dir)
if err != nil {
return nil, err
}
}
var plugins []Plugin
for _, pluginType := range pluginTypes {
pluginPrefix := pluginType.String()
pluginGlob := fmt.Sprintf("%s-*", pluginPrefix)
paths, err := filepath.Glob(filepath.Join(dir, pluginGlob))
if err != nil {
return nil, err
}
for _, path := range paths {
// TODO: should we use a config for some of this?
plugins = append(plugins, NewPlugin(Config{
// TODO: should the name be org/name instead of just name? this implies changing the dir storage too
Name: strings.TrimPrefix(filepath.Base(path), pluginPrefix+"-"),
Type: pluginType,
Command: path,
Args: nil, // TODO
Env: nil, // TODO
}))
}
}
return plugins, nil
}
+49
View File
@@ -0,0 +1,49 @@
package plugin
import (
"fmt"
"github.com/hashicorp/go-plugin"
)
var AllTypes = []Type{
TypeCataloger,
}
type Type uint64
const (
TypeUnknown Type = iota
TypeCataloger
)
func (p Type) String() string {
switch p {
case TypeCataloger:
return "cataloger"
default:
return "unknown"
}
}
func (p Type) HandshakeConfig() plugin.HandshakeConfig {
switch p {
case TypeCataloger:
return plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "SYFT_PLUGIN_MAGIC_COOKIE",
MagicCookieValue: "0f86cc7f-6f97-410e-a844-087cd12e36e3",
}
default:
panic(fmt.Errorf("plugin type unsupported"))
}
}
func ParseType(pluginType string) Type {
switch pluginType {
case "cataloger":
return TypeCataloger
default:
return TypeUnknown
}
}
+2 -1
View File
@@ -2,6 +2,7 @@ package scope
import ( import (
"fmt" "fmt"
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/image" "github.com/anchore/stereoscope/pkg/image"
@@ -16,7 +17,7 @@ type Resolver interface {
// ContentResolver knows how to get file content for given file.References // ContentResolver knows how to get file content for given file.References
type ContentResolver interface { type ContentResolver interface {
MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]io.Reader, error)
} }
// FileResolver knows how to get file.References for given string paths and globs // FileResolver knows how to get file.References for given string paths and globs
+2 -1
View File
@@ -3,6 +3,7 @@ package resolvers
import ( import (
"archive/tar" "archive/tar"
"fmt" "fmt"
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/image" "github.com/anchore/stereoscope/pkg/image"
@@ -111,6 +112,6 @@ func (r *AllLayersResolver) FilesByGlob(patterns ...string) ([]file.Reference, e
// MultipleFileContentsByRef returns the file contents for all file.References relative to the image. Note that a // MultipleFileContentsByRef returns the file contents for all file.References relative to the image. Note that a
// file.Reference is a path relative to a particular layer. // file.Reference is a path relative to a particular layer.
func (r *AllLayersResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) { func (r *AllLayersResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]io.Reader, error) {
return r.img.MultipleFileContentsByRef(f...) return r.img.MultipleFileContentsByRef(f...)
} }
+7 -16
View File
@@ -2,7 +2,7 @@ package resolvers
import ( import (
"fmt" "fmt"
"io/ioutil" "io"
"os" "os"
"path" "path"
@@ -40,15 +40,6 @@ func (s DirectoryResolver) FilesByPath(userPaths ...file.Path) ([]file.Reference
return references, nil return references, nil
} }
func fileContents(path file.Path) ([]byte, error) {
contents, err := ioutil.ReadFile(string(path))
if err != nil {
return nil, err
}
return contents, nil
}
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image. // FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
func (s DirectoryResolver) FilesByGlob(patterns ...string) ([]file.Reference, error) { func (s DirectoryResolver) FilesByGlob(patterns ...string) ([]file.Reference, error) {
result := make([]file.Reference, 0) result := make([]file.Reference, 0)
@@ -76,15 +67,15 @@ func (s DirectoryResolver) FilesByGlob(patterns ...string) ([]file.Reference, er
} }
// MultipleFileContentsByRef returns the file contents for all file.References relative a directory. // MultipleFileContentsByRef returns the file contents for all file.References relative a directory.
func (s DirectoryResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) { func (s DirectoryResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]io.Reader, error) {
refContents := make(map[file.Reference]string) refContents := make(map[file.Reference]io.Reader)
for _, fileRef := range f { for _, fileRef := range f {
contents, err := fileContents(fileRef.Path) targetFile, err := os.Open(string(fileRef.Path))
if err != nil { if err != nil {
return refContents, fmt.Errorf("could not read contents of file: %s", fileRef.Path) return refContents, fmt.Errorf("could not open file=%q: %w", fileRef.Path, err)
} }
refContents[fileRef] = string(contents)
refContents[fileRef] = targetFile
} }
return refContents, nil return refContents, nil
} }
@@ -2,6 +2,7 @@ package resolvers
import ( import (
"fmt" "fmt"
"io"
"github.com/anchore/stereoscope/pkg/file" "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/image" "github.com/anchore/stereoscope/pkg/image"
@@ -75,6 +76,6 @@ func (r *ImageSquashResolver) FilesByGlob(patterns ...string) ([]file.Reference,
// MultipleFileContentsByRef returns the file contents for all file.References relative to the image. Note that a // MultipleFileContentsByRef returns the file contents for all file.References relative to the image. Note that a
// file.Reference is a path relative to a particular layer, in this case only from the squashed representation. // file.Reference is a path relative to a particular layer, in this case only from the squashed representation.
func (r *ImageSquashResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]string, error) { func (r *ImageSquashResolver) MultipleFileContentsByRef(f ...file.Reference) (map[file.Reference]io.Reader, error) {
return r.img.MultipleFileContentsByRef(f...) return r.img.MultipleFileContentsByRef(f...)
} }