From d6dc56732babbc9d7f788c280a768d8648aa093d Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Fri, 17 May 2024 13:29:19 +0400 Subject: [PATCH] feat(plugin): specify plugin version (#6683) Signed-off-by: knqyf263 Co-authored-by: DmitriyLewen --- docs/docs/plugin/developer-guide.md | 10 + docs/docs/plugin/user-guide.md | 11 + .../configuration/cli/trivy_plugin_install.md | 13 ++ pkg/commands/app.go | 14 +- pkg/fanal/artifact/repo/git_test.go | 2 +- pkg/plugin/index.go | 1 + pkg/plugin/index_test.go | 2 +- pkg/plugin/manager.go | 62 ++++-- pkg/plugin/manager_test.go | 172 ++++----------- pkg/plugin/manager_unix_test.go | 203 ++++++++++++++++++ pkg/plugin/testdata/plugin/index.yaml | 2 +- pkg/plugin/testdata/test_plugin.git/HEAD | 1 + pkg/plugin/testdata/test_plugin.git/config | 8 + .../testdata/test_plugin.git/description | 1 + .../testdata/test_plugin.git/info/exclude | 6 + .../08/6aefb548a1150b765d1e163a5e542fc80bd660 | Bin 0 -> 147 bytes .../0a/e1413e3807e024dbc7de4129d12bdcae7dea61 | 3 + .../49/0535e047e795a5c95306ce281c9f08cdb35b7c | Bin 0 -> 37 bytes .../92/9b4718db99b64a38b4e8c3ec8e673976821c08 | Bin 0 -> 344 bytes .../a0/82cf7b16998b8f048e7d2bf8207d9525688a9f | Bin 0 -> 90 bytes .../d7/8abde66b1d35bdac65402f0e2cddf3a96cd377 | Bin 0 -> 380 bytes .../dc/135ebfc7f680300c981029184a492bbdfa6db3 | Bin 0 -> 91 bytes .../testdata/test_plugin.git/packed-refs | 4 + .../test_plugin.git/refs/heads/.gitkeep | 0 .../test_plugin.git/refs/tags/.gitkeep | 0 pkg/plugin/testdata/test_plugin/plugin.yaml | 7 +- 26 files changed, 368 insertions(+), 154 deletions(-) create mode 100644 pkg/plugin/manager_unix_test.go create mode 100644 pkg/plugin/testdata/test_plugin.git/HEAD create mode 100644 pkg/plugin/testdata/test_plugin.git/config create mode 100644 pkg/plugin/testdata/test_plugin.git/description create mode 100644 pkg/plugin/testdata/test_plugin.git/info/exclude create mode 100644 pkg/plugin/testdata/test_plugin.git/objects/08/6aefb548a1150b765d1e163a5e542fc80bd660 create mode 100644 pkg/plugin/testdata/test_plugin.git/objects/0a/e1413e3807e024dbc7de4129d12bdcae7dea61 create mode 100644 pkg/plugin/testdata/test_plugin.git/objects/49/0535e047e795a5c95306ce281c9f08cdb35b7c create mode 100644 pkg/plugin/testdata/test_plugin.git/objects/92/9b4718db99b64a38b4e8c3ec8e673976821c08 create mode 100644 pkg/plugin/testdata/test_plugin.git/objects/a0/82cf7b16998b8f048e7d2bf8207d9525688a9f create mode 100644 pkg/plugin/testdata/test_plugin.git/objects/d7/8abde66b1d35bdac65402f0e2cddf3a96cd377 create mode 100644 pkg/plugin/testdata/test_plugin.git/objects/dc/135ebfc7f680300c981029184a492bbdfa6db3 create mode 100644 pkg/plugin/testdata/test_plugin.git/packed-refs create mode 100644 pkg/plugin/testdata/test_plugin.git/refs/heads/.gitkeep create mode 100644 pkg/plugin/testdata/test_plugin.git/refs/tags/.gitkeep diff --git a/docs/docs/plugin/developer-guide.md b/docs/docs/plugin/developer-guide.md index 5080bab9ede9..bf77edee00b0 100644 --- a/docs/docs/plugin/developer-guide.md +++ b/docs/docs/plugin/developer-guide.md @@ -130,6 +130,16 @@ The following rules will apply in deciding which platform to select: After determining platform, Trivy will download the execution file from `uri` and store it in the plugin cache. When the plugin is called via Trivy CLI, `bin` command will be executed. +#### Tagging plugin repositories +If you are hosting your plugin in a Git repository, it is strongly recommended to tag your releases with a version number. +By tagging your releases, Trivy can install specific versions of your plugin. + +```bash +$ trivy plugin install referrer@v0.3.0 +``` + +When tagging versions, you must follow [the Semantic Versioning][semver] and prefix the tag with `v`, like `v1.2.3`. + #### Plugin arguments/flags The plugin is responsible for handling flags and arguments. Any arguments are passed to the plugin from the `trivy` command. diff --git a/docs/docs/plugin/user-guide.md b/docs/docs/plugin/user-guide.md index b216f3d63c85..f26f741f7e6e 100644 --- a/docs/docs/plugin/user-guide.md +++ b/docs/docs/plugin/user-guide.md @@ -40,6 +40,8 @@ $ trivy plugin install referrer This command will download the plugin and install it in the plugin cache. + + Trivy adheres to the XDG specification, so the location depends on whether XDG_DATA_HOME is set. Trivy will now search XDG_DATA_HOME for the location of the Trivy plugins cache. The preference order is as follows: @@ -56,6 +58,15 @@ $ trivy plugin install github.com/aquasecurity/trivy-plugin-kubectl $ trivy plugin install myplugin.tar.gz ``` +If the plugin's Git repository is [properly tagged](./developer-guide.md#tagging-plugin-repositories), you can specify the version to install like this: + +```bash +$ trivy plugin install referrer@v0.3.0 +``` + +!!! note + The leading `v` in the version is required. Also, the version must follow the [Semantic Versioning](https://semver.org/). + Under the hood Trivy leverages [go-getter][go-getter] to download plugins. This means the following protocols are supported for downloading plugins: diff --git a/docs/docs/references/configuration/cli/trivy_plugin_install.md b/docs/docs/references/configuration/cli/trivy_plugin_install.md index dbd5f21797b8..ec3afd77a680 100644 --- a/docs/docs/references/configuration/cli/trivy_plugin_install.md +++ b/docs/docs/references/configuration/cli/trivy_plugin_install.md @@ -6,6 +6,19 @@ Install a plugin trivy plugin install NAME | URL | FILE_PATH ``` +### Examples + +``` + # Install a plugin from the plugin index + $ trivy plugin install referrer + + # Specify the version of the plugin to install + $ trivy plugin install referrer@v0.3.0 + + # Install a plugin from a URL + $ trivy plugin install github.com/aquasecurity/trivy-plugin-referrer +``` + ### Options ``` diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 6ae687276f2a..cac211fa1484 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -724,9 +724,17 @@ func NewPluginCommand() *cobra.Command { } cmd.AddCommand( &cobra.Command{ - Use: "install NAME | URL | FILE_PATH", - Aliases: []string{"i"}, - Short: "Install a plugin", + Use: "install NAME | URL | FILE_PATH", + Aliases: []string{"i"}, + Short: "Install a plugin", + Example: ` # Install a plugin from the plugin index + $ trivy plugin install referrer + + # Specify the version of the plugin to install + $ trivy plugin install referrer@v0.3.0 + + # Install a plugin from a URL + $ trivy plugin install github.com/aquasecurity/trivy-plugin-referrer`, SilenceErrors: true, SilenceUsage: true, DisableFlagsInUseLine: true, diff --git a/pkg/fanal/artifact/repo/git_test.go b/pkg/fanal/artifact/repo/git_test.go index e5ea1ad3a230..06dab92fb318 100644 --- a/pkg/fanal/artifact/repo/git_test.go +++ b/pkg/fanal/artifact/repo/git_test.go @@ -4,7 +4,6 @@ package repo import ( "context" - "github.com/aquasecurity/trivy/pkg/fanal/walker" "net/http/httptest" "testing" @@ -16,6 +15,7 @@ import ( _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/secret" "github.com/aquasecurity/trivy/pkg/fanal/artifact" "github.com/aquasecurity/trivy/pkg/fanal/cache" + "github.com/aquasecurity/trivy/pkg/fanal/walker" ) func setupGitServer() (*httptest.Server, error) { diff --git a/pkg/plugin/index.go b/pkg/plugin/index.go index c825c16e67af..980d4ef1e41f 100644 --- a/pkg/plugin/index.go +++ b/pkg/plugin/index.go @@ -24,6 +24,7 @@ type Index struct { Version int `yaml:"version"` Plugins []struct { Name string `yaml:"name"` + Version string `yaml:"version"` Maintainer string `yaml:"maintainer"` Summary string `yaml:"summary"` Repository string `yaml:"repository"` diff --git a/pkg/plugin/index_test.go b/pkg/plugin/index_test.go index d918a9dac7ef..028c46a10ca4 100644 --- a/pkg/plugin/index_test.go +++ b/pkg/plugin/index_test.go @@ -51,7 +51,7 @@ func TestManager_Search(t *testing.T) { want: `NAME DESCRIPTION MAINTAINER OUTPUT foo A foo plugin aquasecurity ✓ bar A bar plugin aquasecurity -test A test plugin aquasecurity +test_plugin A test plugin aquasecurity `, }, { diff --git a/pkg/plugin/manager.go b/pkg/plugin/manager.go index 8f79d744bfb0..741d245d6b62 100644 --- a/pkg/plugin/manager.go +++ b/pkg/plugin/manager.go @@ -13,6 +13,7 @@ import ( "golang.org/x/xerrors" "gopkg.in/yaml.v3" + "github.com/aquasecurity/go-version/pkg/semver" "github.com/aquasecurity/trivy/pkg/downloader" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" @@ -30,14 +31,20 @@ var ( type ManagerOption func(indexer *Manager) func WithWriter(w io.Writer) ManagerOption { - return func(indexer *Manager) { - indexer.w = w + return func(manager *Manager) { + manager.w = w + } +} + +func WithLogger(logger *log.Logger) ManagerOption { + return func(manager *Manager) { + manager.logger = logger } } func WithIndexURL(indexURL string) ManagerOption { - return func(indexer *Manager) { - indexer.indexURL = indexURL + return func(manager *Manager) { + manager.indexURL = indexURL } } @@ -88,17 +95,18 @@ func Update(ctx context.Context) error { return defaultManager( func Search(ctx context.Context, keyword string) error { return defaultManager().Search(ctx, keyword) } // Install installs a plugin -func (m *Manager) Install(ctx context.Context, name string, opts Options) (Plugin, error) { - src := m.tryIndex(ctx, name) +func (m *Manager) Install(ctx context.Context, arg string, opts Options) (Plugin, error) { + input := m.parseArg(ctx, arg) + input.name = m.tryIndex(ctx, input.name) // If the plugin is already installed, it skips installing the plugin. - if p, installed := m.isInstalled(ctx, src); installed { + if p, installed := m.isInstalled(ctx, input.name, input.version); installed { m.logger.InfoContext(ctx, "The plugin is already installed", log.String("name", p.Name)) return p, nil } - m.logger.InfoContext(ctx, "Installing the plugin...", log.String("src", src)) - return m.install(ctx, src, opts) + m.logger.InfoContext(ctx, "Installing the plugin...", log.String("src", input.name)) + return m.install(ctx, input.String(), opts) } func (m *Manager) install(ctx context.Context, src string, opts Options) (Plugin, error) { @@ -129,7 +137,8 @@ func (m *Manager) install(ctx context.Context, src string, opts Options) (Plugin return Plugin{}, xerrors.Errorf("yaml encode error: %w", err) } - m.logger.InfoContext(ctx, "Plugin successfully installed", log.String("name", plugin.Name)) + m.logger.InfoContext(ctx, "Plugin successfully installed", + log.String("name", plugin.Name), log.String("version", plugin.Version)) return plugin, nil } @@ -340,16 +349,45 @@ func (m *Manager) loadMetadata(dir string) (Plugin, error) { return plugin, nil } -func (m *Manager) isInstalled(ctx context.Context, url string) (Plugin, bool) { +func (m *Manager) isInstalled(ctx context.Context, url, version string) (Plugin, bool) { installedPlugins, err := m.LoadAll(ctx) if err != nil { return Plugin{}, false } for _, plugin := range installedPlugins { - if plugin.Repository == url { + if plugin.Repository == url && (version == "" || plugin.Version == version) { return plugin, true } } return Plugin{}, false } + +// Input represents the user-specified Input. +type Input struct { + name string + version string +} + +func (i *Input) String() string { + if i.version != "" { + // cf. https://github.com/hashicorp/go-getter/blob/268c11cae8cf0d9374783e06572679796abe9ce9/README.md#git-git + return i.name + "?ref=v" + i.version + } + return i.name +} + +func (m *Manager) parseArg(ctx context.Context, arg string) Input { + before, after, found := strings.Cut(arg, "@v") + if !found { + return Input{name: arg} + } else if _, err := semver.Parse(after); err != nil { + m.logger.DebugContext(ctx, "Unable to identify the plugin version", log.String("name", arg), log.Err(err)) + return Input{name: arg} + } + // cf. https://github.com/hashicorp/go-getter/blob/268c11cae8cf0d9374783e06572679796abe9ce9/README.md#git-git + return Input{ + name: before, + version: after, + } +} diff --git a/pkg/plugin/manager_test.go b/pkg/plugin/manager_test.go index 958a320512d5..19b80ee01c0b 100644 --- a/pkg/plugin/manager_test.go +++ b/pkg/plugin/manager_test.go @@ -1,16 +1,15 @@ +//go:build unix + package plugin_test import ( - "archive/zip" "bytes" "context" + "fmt" "github.com/aquasecurity/trivy/pkg/clock" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" - "github.com/aquasecurity/trivy/pkg/utils/fsutils" v1 "github.com/google/go-containerregistry/pkg/v1" "log/slog" - "net/http" - "net/http/httptest" "os" "path/filepath" "runtime" @@ -24,11 +23,40 @@ import ( "github.com/aquasecurity/trivy/pkg/plugin" ) +func setupInstalledPlugin(t *testing.T, homeDir string, p plugin.Plugin) { + pluginDir := filepath.Join(homeDir, ".trivy", "plugins", p.Name) + + // Create the test plugin directory + err := os.MkdirAll(pluginDir, os.ModePerm) + require.NoError(t, err) + + // write the plugin name + pluginMetadata := fmt.Sprintf(`name: "%s" +repository: "%s" +version: "%s" +usage: test +description: test +platforms: + - selector: + os: linux + arch: amd64 + uri: ./test.sh + bin: ./test.sh +installed: + platform: + os: linux + arch: amd64`, p.Name, p.Repository, p.Version) + + err = os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(pluginMetadata), os.ModePerm) + require.NoError(t, err) +} + func TestManager_Run(t *testing.T) { if runtime.GOOS == "windows" { // the test.sh script can't be run on windows so skipping t.Skip("Test satisfied adequately by Linux tests") } + type fields struct { Name string Repository string @@ -183,110 +211,6 @@ func TestManager_Run(t *testing.T) { } } -func TestManager_Install(t *testing.T) { - if runtime.GOOS == "windows" { - // the test.sh script can't be run on windows so skipping - t.Skip("Test satisfied adequately by Linux tests") - } - wantPlugin := plugin.Plugin{ - Name: "test_plugin", - Repository: "github.com/aquasecurity/trivy-plugin-test", - Version: "0.1.0", - Summary: "test", - Description: "test", - Platforms: []plugin.Platform{ - { - Selector: &plugin.Selector{ - OS: "linux", - Arch: "amd64", - }, - URI: "./test.sh", - Bin: "./test.sh", - }, - }, - Installed: plugin.Installed{ - Platform: plugin.Selector{ - OS: "linux", - Arch: "amd64", - }, - }, - } - - tests := []struct { - name string - pluginName string - want plugin.Plugin - wantFile string - wantErr string - }{ - { - name: "http", - want: wantPlugin, - wantFile: ".trivy/plugins/test_plugin/test.sh", - }, - { - name: "local path", - pluginName: "testdata/test_plugin", - want: wantPlugin, - wantFile: ".trivy/plugins/test_plugin/test.sh", - }, - { - name: "index", - pluginName: "test", - want: wantPlugin, - wantFile: ".trivy/plugins/test_plugin/test.sh", - }, - { - name: "plugin not found", - pluginName: "testdata/not_found", - wantErr: "no such file or directory", - }, - { - name: "no plugin.yaml", - pluginName: "testdata/no_yaml", - wantErr: "file open error", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // The test plugin will be installed here - dst := t.TempDir() - t.Setenv("XDG_DATA_HOME", dst) - - // For plugin index - fsutils.SetCacheDir("testdata") - - if tt.pluginName == "" { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - zr := zip.NewWriter(w) - require.NoError(t, zr.AddFS(os.DirFS("testdata/test_plugin"))) - require.NoError(t, zr.Close()) - })) - t.Cleanup(ts.Close) - tt.pluginName = ts.URL + "/test_plugin.zip" - } - - got, err := plugin.NewManager().Install(context.Background(), tt.pluginName, plugin.Options{ - Platform: ftypes.Platform{ - Platform: &v1.Platform{ - Architecture: "amd64", - OS: "linux", - }, - }, - }) - if tt.wantErr != "" { - require.ErrorContains(t, err, tt.wantErr) - return - } - assert.NoError(t, err) - - assert.EqualExportedValues(t, tt.want, got) - assert.FileExists(t, filepath.Join(dst, tt.wantFile)) - }) - } -} - func TestManager_Uninstall(t *testing.T) { ctx := clock.With(context.Background(), time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) pluginName := "test_plugin" @@ -423,42 +347,28 @@ func TestManager_Upgrade(t *testing.T) { t.Skip("Test satisfied adequately by Linux tests") } pluginName := "test_plugin" + pluginVersion := "0.0.5" tempDir := t.TempDir() - pluginDir := filepath.Join(tempDir, ".trivy", "plugins", pluginName) - t.Setenv("XDG_DATA_HOME", tempDir) - - // Create the test plugin directory - err := os.MkdirAll(pluginDir, os.ModePerm) - require.NoError(t, err) - - // write the plugin name - pluginMetadata := `name: "test_plugin" -repository: testdata/test_plugin -version: "0.0.5" -usage: test -description: A simple test plugin -installed: - platform: - os: linux - arch: amd64` - - err = os.WriteFile(filepath.Join(pluginDir, "plugin.yaml"), []byte(pluginMetadata), os.ModePerm) - require.NoError(t, err) + setupInstalledPlugin(t, tempDir, plugin.Plugin{ + Name: pluginName, + Version: pluginVersion, + Repository: "testdata/test_plugin", + }) ctx := context.Background() m := plugin.NewManager() // verify initial version - verifyVersion(t, ctx, m, pluginName, "0.0.5") + verifyVersion(t, ctx, m, pluginName, pluginVersion) // Upgrade the existing plugin - err = m.Upgrade(ctx, nil) + err := m.Upgrade(ctx, nil) require.NoError(t, err) // verify plugin updated - verifyVersion(t, ctx, m, pluginName, "0.1.0") + verifyVersion(t, ctx, m, pluginName, "0.2.0") } func verifyVersion(t *testing.T, ctx context.Context, m *plugin.Manager, pluginName, expectedVersion string) { diff --git a/pkg/plugin/manager_unix_test.go b/pkg/plugin/manager_unix_test.go new file mode 100644 index 000000000000..52f4b0d4e2c3 --- /dev/null +++ b/pkg/plugin/manager_unix_test.go @@ -0,0 +1,203 @@ +//go:build unix + +package plugin_test + +import ( + "archive/zip" + "bytes" + "context" + "fmt" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + "time" + + "github.com/google/go-containerregistry/pkg/v1" + "github.com/sosedoff/gitkit" // Not work on Windows + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/clock" + ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/plugin" + "github.com/aquasecurity/trivy/pkg/utils/fsutils" +) + +func setupGitServer() (*httptest.Server, error) { + service := gitkit.New(gitkit.Config{ + Dir: "./testdata", + AutoCreate: false, + }) + + if err := service.Setup(); err != nil { + return nil, err + } + + ts := httptest.NewServer(service) + + return ts, nil +} + +func TestManager_Install(t *testing.T) { + gs, err := setupGitServer() + require.NoError(t, err) + t.Cleanup(gs.Close) + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + zr := zip.NewWriter(w) + require.NoError(t, zr.AddFS(os.DirFS("testdata/test_plugin"))) + require.NoError(t, zr.Close()) + })) + t.Cleanup(ts.Close) + + wantPlugin := plugin.Plugin{ + Name: "test_plugin", + Repository: "testdata/test_plugin", + Version: "0.2.0", + Summary: "test", + Description: "test", + Platforms: []plugin.Platform{ + { + Selector: &plugin.Selector{ + OS: "linux", + Arch: "amd64", + }, + URI: "./test.sh", + Bin: "./test.sh", + }, + }, + Installed: plugin.Installed{ + Platform: plugin.Selector{ + OS: "linux", + Arch: "amd64", + }, + }, + } + wantPluginWithVersion := wantPlugin + wantPluginWithVersion.Version = "0.1.0" + + wantLogs := `2021-08-25T12:20:30Z INFO Installing the plugin... src="%s" +2021-08-25T12:20:30Z INFO Plugin successfully installed name="test_plugin" version="%s" +` + + tests := []struct { + name string + pluginName string + installed *plugin.Plugin + want plugin.Plugin + wantFile string + wantLogs string + wantErr string + }{ + { + name: "http", + pluginName: ts.URL + "/test_plugin.zip", + want: wantPlugin, + wantFile: ".trivy/plugins/test_plugin/test.sh", + wantLogs: fmt.Sprintf(wantLogs, ts.URL+"/test_plugin.zip", "0.2.0"), + }, + { + name: "local path", + pluginName: "testdata/test_plugin", + want: wantPlugin, + wantFile: ".trivy/plugins/test_plugin/test.sh", + wantLogs: fmt.Sprintf(wantLogs, "testdata/test_plugin", "0.2.0"), + }, + { + name: "git", + pluginName: "git::" + gs.URL + "/test_plugin.git", + want: wantPlugin, + wantFile: ".trivy/plugins/test_plugin/test.sh", + wantLogs: fmt.Sprintf(wantLogs, "git::"+gs.URL+"/test_plugin.git", "0.2.0"), + }, + { + name: "with version", + pluginName: "git::" + gs.URL + "/test_plugin.git@v0.1.0", + want: wantPluginWithVersion, + wantFile: ".trivy/plugins/test_plugin/test.sh", + wantLogs: fmt.Sprintf(wantLogs, "git::"+gs.URL+"/test_plugin.git", "0.1.0"), + }, + { + name: "via index", + pluginName: "test_plugin", + want: wantPlugin, + wantFile: ".trivy/plugins/test_plugin/test.sh", + wantLogs: fmt.Sprintf(wantLogs, "testdata/test_plugin", "0.2.0"), + }, + { + name: "installed", + pluginName: "test_plugin", + installed: &plugin.Plugin{ + Name: "test_plugin", + Repository: "testdata/test_plugin", + Version: "0.2.0", + }, + want: wantPlugin, + wantLogs: "2021-08-25T12:20:30Z INFO The plugin is already installed name=\"test_plugin\"\n", + }, + { + name: "different version installed", + pluginName: "test_plugin@v0.2.0", + installed: &plugin.Plugin{ + Name: "test_plugin", + Repository: "testdata/test_plugin", + Version: "0.1.0", + }, + want: wantPlugin, + wantLogs: fmt.Sprintf(wantLogs, "testdata/test_plugin", "0.2.0"), + }, + { + name: "plugin not found", + pluginName: "testdata/not_found", + wantErr: "no such file or directory", + }, + { + name: "no plugin.yaml", + pluginName: "testdata/no_yaml", + wantErr: "file open error", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // The test plugin will be installed here + dst := t.TempDir() + t.Setenv("XDG_DATA_HOME", dst) + + // For plugin index + fsutils.SetCacheDir("testdata") + + if tt.installed != nil { + setupInstalledPlugin(t, dst, *tt.installed) + } + + var gotLogs bytes.Buffer + logger := log.New(log.NewHandler(&gotLogs, nil)) + + ctx := clock.With(context.Background(), time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) + + got, err := plugin.NewManager(plugin.WithLogger(logger)).Install(ctx, tt.pluginName, plugin.Options{ + Platform: ftypes.Platform{ + Platform: &v1.Platform{ + Architecture: "amd64", + OS: "linux", + }, + }, + }) + if tt.wantErr != "" { + require.ErrorContains(t, err, tt.wantErr) + return + } + assert.NoError(t, err) + + assert.EqualExportedValues(t, tt.want, got) + if tt.wantFile != "" { + assert.FileExists(t, filepath.Join(dst, tt.wantFile)) + } + assert.Equal(t, tt.wantLogs, gotLogs.String()) + }) + } +} diff --git a/pkg/plugin/testdata/plugin/index.yaml b/pkg/plugin/testdata/plugin/index.yaml index 17fbb1987939..b37b86e176a0 100644 --- a/pkg/plugin/testdata/plugin/index.yaml +++ b/pkg/plugin/testdata/plugin/index.yaml @@ -9,7 +9,7 @@ plugins: maintainer: aquasecurity summary: A bar plugin repository: github.com/aquasecurity/trivy-plugin-bar - - name: test + - name: test_plugin maintainer: aquasecurity summary: A test plugin repository: testdata/test_plugin \ No newline at end of file diff --git a/pkg/plugin/testdata/test_plugin.git/HEAD b/pkg/plugin/testdata/test_plugin.git/HEAD new file mode 100644 index 000000000000..b870d82622c1 --- /dev/null +++ b/pkg/plugin/testdata/test_plugin.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/pkg/plugin/testdata/test_plugin.git/config b/pkg/plugin/testdata/test_plugin.git/config new file mode 100644 index 000000000000..9512a718938b --- /dev/null +++ b/pkg/plugin/testdata/test_plugin.git/config @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true + ignorecase = true + precomposeunicode = true +[remote "origin"] + url = /Users/teppei/src/github.com/aquasecurity/trivy/pkg/plugin/testdata/test_plugin diff --git a/pkg/plugin/testdata/test_plugin.git/description b/pkg/plugin/testdata/test_plugin.git/description new file mode 100644 index 000000000000..498b267a8c78 --- /dev/null +++ b/pkg/plugin/testdata/test_plugin.git/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/pkg/plugin/testdata/test_plugin.git/info/exclude b/pkg/plugin/testdata/test_plugin.git/info/exclude new file mode 100644 index 000000000000..a5196d1be8fb --- /dev/null +++ b/pkg/plugin/testdata/test_plugin.git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/pkg/plugin/testdata/test_plugin.git/objects/08/6aefb548a1150b765d1e163a5e542fc80bd660 b/pkg/plugin/testdata/test_plugin.git/objects/08/6aefb548a1150b765d1e163a5e542fc80bd660 new file mode 100644 index 0000000000000000000000000000000000000000..e7d696256b86d9c3323ac0740015d918dae20479 GIT binary patch literal 147 zcmV;E0Brww0Zooe4#F@DL|Nw)R`-O05K?m)}bpc2I(oI`pUXp0py9{vdRpd=i8&5in`$3aPP4!^isNtVPhcckT1 zw3u%pIWY=g4?>ofdcUbQK@3>-<@wQ=mdyaV^>Xe`No+v(7qo2Mnz`2htT&%uLTVfd BMbrQQ literal 0 HcmV?d00001 diff --git a/pkg/plugin/testdata/test_plugin.git/objects/0a/e1413e3807e024dbc7de4129d12bdcae7dea61 b/pkg/plugin/testdata/test_plugin.git/objects/0a/e1413e3807e024dbc7de4129d12bdcae7dea61 new file mode 100644 index 000000000000..b16c882bc30f --- /dev/null +++ b/pkg/plugin/testdata/test_plugin.git/objects/0a/e1413e3807e024dbc7de4129d12bdcae7dea61 @@ -0,0 +1,3 @@ +xM +0 S[7a}ɺ +#iE޵(s,>-0!b C!)A1$$~h  AI ZI,\:r*{,A8'oMhd^ݩ [Bi \ No newline at end of file diff --git a/pkg/plugin/testdata/test_plugin.git/objects/49/0535e047e795a5c95306ce281c9f08cdb35b7c b/pkg/plugin/testdata/test_plugin.git/objects/49/0535e047e795a5c95306ce281c9f08cdb35b7c new file mode 100644 index 0000000000000000000000000000000000000000..964cbf82f49bbc0825b9a3b0ce88f1ab88927e94 GIT binary patch literal 37 tcmbGR=C3>B9+OaM$w5k~+3 literal 0 HcmV?d00001 diff --git a/pkg/plugin/testdata/test_plugin.git/objects/92/9b4718db99b64a38b4e8c3ec8e673976821c08 b/pkg/plugin/testdata/test_plugin.git/objects/92/9b4718db99b64a38b4e8c3ec8e673976821c08 new file mode 100644 index 0000000000000000000000000000000000000000..ba6cfbad6f8c1f126ac73ae05fca5053271761db GIT binary patch literal 344 zcmV-e0jK_W0hN$TZ-PJ+g}a_#F?&;;VL&ixo1#!4h4BFt!wyCm1r!nLgI{0Jbk|K! za+7nv4oJIIf`-f{TcYpNR- zg|2Ht*D|Q;`78a-0rWAod{mffuVpuy(>t3HTH3)^?qoi8yu4?8lsR@wKDEr`m+hg> zCh3y#RMAWh;A#C)In)UyZ*h$Vh}{!fC={rVJ`}c*p?BA;Zk>8Pn-SVZGhBq4g{h}Y z0QPjLYn4r;g>g!Pfhs$*twD<0Jhs~?3#!%munJUDR7&Z1sa4fa$*pXH+%j(SIX#Ex q^)&`?vz$+hQ8sEAVK6i>Ff%bxD99;I&&<=SOw7$;;K+Kv)nlP3cUi2Qm{nYe w{t51D2|#7$rltxdsl_FF#Tg8qtfmj#pHE$SGMMe0hRl49vzw!906R(?UD|Xgng9R* literal 0 HcmV?d00001 diff --git a/pkg/plugin/testdata/test_plugin.git/objects/d7/8abde66b1d35bdac65402f0e2cddf3a96cd377 b/pkg/plugin/testdata/test_plugin.git/objects/d7/8abde66b1d35bdac65402f0e2cddf3a96cd377 new file mode 100644 index 0000000000000000000000000000000000000000..78b94e8996dd4a8bb2cdcb6c052a4c599e3a7813 GIT binary patch literal 380 zcmV-?0fYW{0hN$TZ<|0Ag}a_#G5e_YJb27VZB=8aO>L&Nfi`6~49pk|h7h8}{`G-G z-F4F=UFqCUI#*Xmb!}UX(8lb;IFu!nh!LgT96Ow|oM~jS(k)1?89{ErZDKo&Nv=UJ zhw?H)PIzv)m=!taxnoJna;s#DmWq{*OS$VXf)!#wK8>etfalA@RTGE8Pk-H4)5@w@ zsIL72v5Rd+DP|PjAqyce(doUh{O_RY&0}TY$5~#m2?dPfEsVpBSS9H(c;f)3ScEBR zwyRl(Y5Zt*dqqXJ|NZM6)hUjW^UTb$j`laY^Xsj7K3yJbt`Ev+=5Gyv!~M61NJN>P z{CtsjNWCTej5KJDzAL-QO)y;xr`1blwx;4H*SN|25Q`+R0i5aKyC~wsJId24wf_`- z!lWguq3HLPcR!APt$neJ&25DXS(c2h} acGpAn8NAmK2>)MY0JIAVK6i>Ff%bxD99;I&&<=SOw7$;;Ckq2XTknJ<@WJ=j+z&> x@2snRl?YU3ZfdGfl3HA%SDeA%$!hw*{rS|TCxh9}X~@jyIJ-Hz1^}iS9{935F311? literal 0 HcmV?d00001 diff --git a/pkg/plugin/testdata/test_plugin.git/packed-refs b/pkg/plugin/testdata/test_plugin.git/packed-refs new file mode 100644 index 000000000000..00a4f957a23f --- /dev/null +++ b/pkg/plugin/testdata/test_plugin.git/packed-refs @@ -0,0 +1,4 @@ +# pack-refs with: peeled fully-peeled sorted +d78abde66b1d35bdac65402f0e2cddf3a96cd377 refs/heads/main +929b4718db99b64a38b4e8c3ec8e673976821c08 refs/tags/v0.1.0 +d78abde66b1d35bdac65402f0e2cddf3a96cd377 refs/tags/v0.2.0 diff --git a/pkg/plugin/testdata/test_plugin.git/refs/heads/.gitkeep b/pkg/plugin/testdata/test_plugin.git/refs/heads/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pkg/plugin/testdata/test_plugin.git/refs/tags/.gitkeep b/pkg/plugin/testdata/test_plugin.git/refs/tags/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pkg/plugin/testdata/test_plugin/plugin.yaml b/pkg/plugin/testdata/test_plugin/plugin.yaml index 272c8d5760a7..086aefb548a1 100644 --- a/pkg/plugin/testdata/test_plugin/plugin.yaml +++ b/pkg/plugin/testdata/test_plugin/plugin.yaml @@ -1,6 +1,6 @@ name: "test_plugin" -repository: github.com/aquasecurity/trivy-plugin-test -version: "0.1.0" +repository: testdata/test_plugin +version: "0.2.0" summary: test description: test platforms: @@ -9,6 +9,3 @@ platforms: arch: amd64 uri: ./test.sh bin: ./test.sh -# for testing -_goos: linux -_goarch: amd64 \ No newline at end of file