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/manager.go b/pkg/plugin/manager.go index 8f1cd28c80f9..741d245d6b62 100644 --- a/pkg/plugin/manager.go +++ b/pkg/plugin/manager.go @@ -31,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 } } @@ -90,11 +96,11 @@ func Search(ctx context.Context, keyword string) error { return defaultManager( // Install installs a plugin func (m *Manager) Install(ctx context.Context, arg string, opts Options) (Plugin, error) { - input := m.parseArg(arg) + 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, input.name); 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 } @@ -343,14 +349,14 @@ 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 } } @@ -371,12 +377,12 @@ func (i *Input) String() string { return i.name } -func (m *Manager) parseArg(arg string) Input { +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.Debug("Unable to identify the plugin version", log.String("name", arg), log.Err(err)) + 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 diff --git a/pkg/plugin/manager_test.go b/pkg/plugin/manager_test.go index c831f77ed6f8..134d1d160763 100644 --- a/pkg/plugin/manager_test.go +++ b/pkg/plugin/manager_test.go @@ -5,6 +5,7 @@ package plugin_test import ( "bytes" "context" + "fmt" "github.com/aquasecurity/trivy/pkg/clock" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" v1 "github.com/google/go-containerregistry/pkg/v1" @@ -22,6 +23,34 @@ 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 @@ -318,38 +347,24 @@ 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 diff --git a/pkg/plugin/manager_unix_test.go b/pkg/plugin/manager_unix_test.go index f9f8cfecd008..52f4b0d4e2c3 100644 --- a/pkg/plugin/manager_unix_test.go +++ b/pkg/plugin/manager_unix_test.go @@ -4,19 +4,24 @@ package plugin_test import ( "archive/zip" + "bytes" "context" + "fmt" "net/http" "net/http/httptest" "os" "path/filepath" "testing" + "time" - v1 "github.com/google/go-containerregistry/pkg/v1" + "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" ) @@ -37,13 +42,20 @@ func setupGitServer() (*httptest.Server, error) { } func TestManager_Install(t *testing.T) { - ts, err := setupGitServer() + gs, err := setupGitServer() require.NoError(t, err) - defer ts.Close() + 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: "github.com/aquasecurity/trivy-plugin-test", + Repository: "testdata/test_plugin", Version: "0.2.0", Summary: "test", Description: "test", @@ -67,41 +79,75 @@ func TestManager_Install(t *testing.T) { 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", - want: wantPlugin, - wantFile: ".trivy/plugins/test_plugin/test.sh", + 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::" + ts.URL + "/test_plugin.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::" + ts.URL + "/test_plugin.git@v0.1.0", + 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", + 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", @@ -124,17 +170,16 @@ func TestManager_Install(t *testing.T) { // 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" + if tt.installed != nil { + setupInstalledPlugin(t, dst, *tt.installed) } - got, err := plugin.NewManager().Install(context.Background(), tt.pluginName, plugin.Options{ + 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", @@ -149,7 +194,10 @@ func TestManager_Install(t *testing.T) { assert.NoError(t, err) assert.EqualExportedValues(t, tt.want, got) - assert.FileExists(t, filepath.Join(dst, tt.wantFile)) + 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/objects/08/6aefb548a1150b765d1e163a5e542fc80bd660 b/pkg/plugin/testdata/test_plugin.git/objects/08/6aefb548a1150b765d1e163a5e542fc80bd660 new file mode 100644 index 000000000000..e7d696256b86 Binary files /dev/null and b/pkg/plugin/testdata/test_plugin.git/objects/08/6aefb548a1150b765d1e163a5e542fc80bd660 differ 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*{,��A�8��'��o�M����hd���^�ݩ� �����[������Bi \ No newline at end of file diff --git a/pkg/plugin/testdata/test_plugin.git/objects/2e/cea228bf881042eb74d3b691c5c6abfb2f7ee4 b/pkg/plugin/testdata/test_plugin.git/objects/2e/cea228bf881042eb74d3b691c5c6abfb2f7ee4 deleted file mode 100644 index 49854ac328a7..000000000000 Binary files a/pkg/plugin/testdata/test_plugin.git/objects/2e/cea228bf881042eb74d3b691c5c6abfb2f7ee4 and /dev/null differ diff --git a/pkg/plugin/testdata/test_plugin.git/objects/3b/147ae5ffafba7c1d833b4f4bc8ac6a22a8c9bf b/pkg/plugin/testdata/test_plugin.git/objects/3b/147ae5ffafba7c1d833b4f4bc8ac6a22a8c9bf deleted file mode 100644 index 90ef65cdcbb0..000000000000 Binary files a/pkg/plugin/testdata/test_plugin.git/objects/3b/147ae5ffafba7c1d833b4f4bc8ac6a22a8c9bf and /dev/null differ 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 000000000000..ba6cfbad6f8c Binary files /dev/null and b/pkg/plugin/testdata/test_plugin.git/objects/92/9b4718db99b64a38b4e8c3ec8e673976821c08 differ diff --git a/pkg/plugin/testdata/test_plugin.git/objects/96/e190ae5bcaa3900e568608faf00f16c3ff2714 b/pkg/plugin/testdata/test_plugin.git/objects/96/e190ae5bcaa3900e568608faf00f16c3ff2714 deleted file mode 100644 index 2bc33b30b6af..000000000000 --- a/pkg/plugin/testdata/test_plugin.git/objects/96/e190ae5bcaa3900e568608faf00f16c3ff2714 +++ /dev/null @@ -1,2 +0,0 @@ -x��Ko�@���_1{��<���V�@��@� ��nf<31�!Ї}!Qw��HW:����Ց��=���އ�S -%'��u1���u�b�ai�%Z�(e�+�Թ�T`���֚k��.��Җ�.�pWzB>�ϗ��/�5YR���jZ~l���~����c[Q8CB`��{���ף�'��5��n�[g~Qn�7`�3V�&�'����I�K��xJ���ibfQ�ɍn�:6?�ϯ��si���n��fie�� ɷDX�M�wU7���kl\W����x��*d8+"���,�D ����}|����e�Iv����˅�6�-bV(�kPC����L�{Y�|�xm�mbz�y�i���-���ཎ( �U���d���B�� \ No newline at end of file diff --git a/pkg/plugin/testdata/test_plugin.git/objects/a0/82cf7b16998b8f048e7d2bf8207d9525688a9f b/pkg/plugin/testdata/test_plugin.git/objects/a0/82cf7b16998b8f048e7d2bf8207d9525688a9f new file mode 100644 index 000000000000..981abada0a9b Binary files /dev/null and b/pkg/plugin/testdata/test_plugin.git/objects/a0/82cf7b16998b8f048e7d2bf8207d9525688a9f differ diff --git a/pkg/plugin/testdata/test_plugin.git/objects/b0/b5089c438d32daafa5a10acb7836280f31fd1b b/pkg/plugin/testdata/test_plugin.git/objects/b0/b5089c438d32daafa5a10acb7836280f31fd1b deleted file mode 100644 index ff074f718b4f..000000000000 Binary files a/pkg/plugin/testdata/test_plugin.git/objects/b0/b5089c438d32daafa5a10acb7836280f31fd1b and /dev/null differ diff --git a/pkg/plugin/testdata/test_plugin.git/objects/b8/fad1dc57e9b544da159fd2da39f82d4dcce34a b/pkg/plugin/testdata/test_plugin.git/objects/b8/fad1dc57e9b544da159fd2da39f82d4dcce34a deleted file mode 100644 index 643551faa0c5..000000000000 Binary files a/pkg/plugin/testdata/test_plugin.git/objects/b8/fad1dc57e9b544da159fd2da39f82d4dcce34a and /dev/null differ 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 000000000000..78b94e8996dd Binary files /dev/null and b/pkg/plugin/testdata/test_plugin.git/objects/d7/8abde66b1d35bdac65402f0e2cddf3a96cd377 differ diff --git a/pkg/plugin/testdata/test_plugin.git/objects/dc/135ebfc7f680300c981029184a492bbdfa6db3 b/pkg/plugin/testdata/test_plugin.git/objects/dc/135ebfc7f680300c981029184a492bbdfa6db3 new file mode 100644 index 000000000000..4dbb20dc16b4 Binary files /dev/null and b/pkg/plugin/testdata/test_plugin.git/objects/dc/135ebfc7f680300c981029184a492bbdfa6db3 differ diff --git a/pkg/plugin/testdata/test_plugin.git/objects/dd/c882ca64495a0498b3303be6a4ae58213f0485 b/pkg/plugin/testdata/test_plugin.git/objects/dd/c882ca64495a0498b3303be6a4ae58213f0485 deleted file mode 100644 index 12c485b8eebc..000000000000 Binary files a/pkg/plugin/testdata/test_plugin.git/objects/dd/c882ca64495a0498b3303be6a4ae58213f0485 and /dev/null differ diff --git a/pkg/plugin/testdata/test_plugin.git/packed-refs b/pkg/plugin/testdata/test_plugin.git/packed-refs index 539634191094..00a4f957a23f 100644 --- a/pkg/plugin/testdata/test_plugin.git/packed-refs +++ b/pkg/plugin/testdata/test_plugin.git/packed-refs @@ -1,4 +1,4 @@ # pack-refs with: peeled fully-peeled sorted -96e190ae5bcaa3900e568608faf00f16c3ff2714 refs/heads/main -3b147ae5ffafba7c1d833b4f4bc8ac6a22a8c9bf refs/tags/v0.1.0 -96e190ae5bcaa3900e568608faf00f16c3ff2714 refs/tags/v0.2.0 +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 deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/pkg/plugin/testdata/test_plugin.git/refs/tags/.gitkeep b/pkg/plugin/testdata/test_plugin.git/refs/tags/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/pkg/plugin/testdata/test_plugin/plugin.yaml b/pkg/plugin/testdata/test_plugin/plugin.yaml index b0b5089c438d..086aefb548a1 100644 --- a/pkg/plugin/testdata/test_plugin/plugin.yaml +++ b/pkg/plugin/testdata/test_plugin/plugin.yaml @@ -1,5 +1,5 @@ name: "test_plugin" -repository: github.com/aquasecurity/trivy-plugin-test +repository: testdata/test_plugin version: "0.2.0" summary: test description: test