diff --git a/internal/plugin/installer/base_test.go b/internal/plugin/installer/base_test.go index 8ef7ff588..406d6715b 100644 --- a/internal/plugin/installer/base_test.go +++ b/internal/plugin/installer/base_test.go @@ -14,32 +14,54 @@ limitations under the License. package installer import ( + "path/filepath" "testing" "github.com/stretchr/testify/assert" + + "helm.sh/helm/v4/pkg/helmpath" ) func TestPath(t *testing.T) { + customPluginsDir := "/foo/bar" + tests := []struct { + name string source string helmPluginsDir string expectPath string }{ { + name: "empty source default helm plugins dir", + source: "", + helmPluginsDir: "", + expectPath: "", + }, { + name: "default helm plugins dir", + source: "https://github.com/adamreese/helm-env", + helmPluginsDir: "", + expectPath: helmpath.DataPath("plugins", "helm-env"), + }, { + name: "empty source custom helm plugins dir", source: "", - helmPluginsDir: "/helm/data/plugins", + helmPluginsDir: customPluginsDir, expectPath: "", }, { - source: "https://github.com/jkroepke/helm-secrets", - helmPluginsDir: "/helm/data/plugins", - expectPath: "/helm/data/plugins/helm-secrets", + name: "custom helm plugins dir", + source: "https://github.com/adamreese/helm-env", + helmPluginsDir: customPluginsDir, + expectPath: filepath.Join(customPluginsDir, "helm-env"), }, } for _, tt := range tests { - t.Setenv("HELM_PLUGINS", tt.helmPluginsDir) - baseIns := newBase(tt.source) - baseInsPath := baseIns.Path() - assert.Equal(t, tt.expectPath, baseInsPath, "expected name %s, got %s", tt.expectPath, baseInsPath) + t.Run(tt.name, func(t *testing.T) { + if tt.helmPluginsDir != "" { + t.Setenv("HELM_PLUGINS", tt.helmPluginsDir) + } + installer := newBase(tt.source) + path := installer.Path() + assert.Equal(t, tt.expectPath, path) + }) } } diff --git a/internal/plugin/installer/http_installer.go b/internal/plugin/installer/http_installer.go index 7be326de2..f052d8862 100644 --- a/internal/plugin/installer/http_installer.go +++ b/internal/plugin/installer/http_installer.go @@ -37,6 +37,7 @@ type HTTPInstaller struct { CacheDir string PluginName string base + settings *cli.EnvSettings extractor Extractor getter getter.Getter // Cached data to avoid duplicate downloads @@ -51,6 +52,8 @@ func NewHTTPInstaller(source string) (*HTTPInstaller, error) { return nil, err } + settings := cli.New() + extractor, err := NewExtractor(source) if err != nil { return nil, err @@ -65,6 +68,7 @@ func NewHTTPInstaller(source string) (*HTTPInstaller, error) { CacheDir: helmpath.CachePath("plugins", key), PluginName: stripPluginName(filepath.Base(source)), base: newBase(source), + settings: settings, extractor: extractor, getter: get, } @@ -152,7 +156,7 @@ func (i HTTPInstaller) Path() string { if i.Source == "" { return "" } - return helmpath.DataPath("plugins", i.PluginName) + return filepath.Join(i.settings.PluginsDirectory, i.PluginName) } // SupportsVerification returns true if the HTTP installer can verify plugins diff --git a/internal/plugin/installer/http_installer_test.go b/internal/plugin/installer/http_installer_test.go index 3ae813b14..de6176db2 100644 --- a/internal/plugin/installer/http_installer_test.go +++ b/internal/plugin/installer/http_installer_test.go @@ -36,6 +36,7 @@ import ( "github.com/stretchr/testify/require" "helm.sh/helm/v4/internal/test/ensure" + "helm.sh/helm/v4/pkg/cli" "helm.sh/helm/v4/pkg/getter" "helm.sh/helm/v4/pkg/helmpath" ) @@ -464,6 +465,7 @@ func TestExtractPluginInSubdirectory(t *testing.T) { CacheDir: tempDir, PluginName: "subdir-plugin", base: newBase(source), + settings: cli.New(), extractor: &TarGzExtractor{}, } @@ -488,3 +490,38 @@ func TestExtractPluginInSubdirectory(t *testing.T) { expectedRoot := filepath.Join(tempDir, "my-plugin") assert.Equal(t, expectedRoot, pluginRoot, "Expected plugin root to be %s but got %s", expectedRoot, pluginRoot) } + +func TestHTTPInstaller_Path(t *testing.T) { + customPluginsDir := "/foo/bar" + + tests := []struct { + name string + source string + helmPluginsDir string + expectPath string + }{ + { + name: "default helm plugins dir", + source: "https://example.com/fake-plugin-0.0.1.tar.gz", + helmPluginsDir: "", + expectPath: helmpath.DataPath("plugins", "fake-plugin"), + }, { + name: "custom helm plugins dir", + source: "https://example.com/fake-plugin-0.0.1.tar.gz", + helmPluginsDir: customPluginsDir, + expectPath: filepath.Join(customPluginsDir, "fake-plugin"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.helmPluginsDir != "" { + t.Setenv("HELM_PLUGINS", tt.helmPluginsDir) + } + installer, err := NewHTTPInstaller(tt.source) + require.NoError(t, err) + path := installer.Path() + assert.Equal(t, tt.expectPath, path) + }) + } +} diff --git a/internal/plugin/installer/local_installer.go b/internal/plugin/installer/local_installer.go index 59918401b..a4169680d 100644 --- a/internal/plugin/installer/local_installer.go +++ b/internal/plugin/installer/local_installer.go @@ -26,6 +26,7 @@ import ( "helm.sh/helm/v4/internal/plugin" "helm.sh/helm/v4/internal/third_party/dep/fs" + "helm.sh/helm/v4/pkg/cli" "helm.sh/helm/v4/pkg/helmpath" ) @@ -35,6 +36,7 @@ var ErrPluginNotADirectory = errors.New("expected plugin to be a directory (cont // LocalInstaller installs plugins from the filesystem. type LocalInstaller struct { base + settings *cli.EnvSettings isArchive bool extractor Extractor pluginData []byte // Cached plugin data @@ -47,8 +49,12 @@ func NewLocalInstaller(source string) (*LocalInstaller, error) { if err != nil { return nil, fmt.Errorf("unable to get absolute path to plugin: %w", err) } + + settings := cli.New() + i := &LocalInstaller{ - base: newBase(src), + base: newBase(src), + settings: settings, } // Check if source is an archive @@ -176,7 +182,7 @@ func (i *LocalInstaller) Path() string { pluginName = stripPluginName(pluginName) } - return helmpath.DataPath("plugins", pluginName) + return filepath.Join(i.settings.PluginsDirectory, pluginName) } // SupportsVerification returns true if the local installer can verify plugins diff --git a/internal/plugin/installer/local_installer_test.go b/internal/plugin/installer/local_installer_test.go index 94632756c..9619525d2 100644 --- a/internal/plugin/installer/local_installer_test.go +++ b/internal/plugin/installer/local_installer_test.go @@ -23,6 +23,7 @@ import ( "path/filepath" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "helm.sh/helm/v4/internal/test/ensure" @@ -109,3 +110,48 @@ func TestLocalInstallerTarball(t *testing.T) { _, err = os.Stat(i.Path()) require.NoErrorf(t, err, "plugin not found at %s", i.Path()) } + +func TestLocalInstaller_Path(t *testing.T) { + customPluginsDir := "/foo/bar" + + tests := []struct { + name string + source string + helmPluginsDir string + expectPath string + }{ + { + name: "default helm plugins dir", + source: "../testdata/plugdir/good/echo-v1", + helmPluginsDir: "", + expectPath: helmpath.DataPath("plugins", "echo-v1"), + }, { + name: "archive default helm plugins dir", + source: "../testdata/plugdir/good/archive-1.2.3.tar.gz", + helmPluginsDir: "", + expectPath: helmpath.DataPath("plugins", "archive"), + }, { + name: "custom helm plugins dir", + source: "../testdata/plugdir/good/echo-v1", + helmPluginsDir: customPluginsDir, + expectPath: filepath.Join(customPluginsDir, "echo-v1"), + }, { + name: "archive custom helm plugins dir", + source: "../testdata/plugdir/good/archive-1.2.3.tar.gz", + helmPluginsDir: customPluginsDir, + expectPath: filepath.Join(customPluginsDir, "archive"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.helmPluginsDir != "" { + t.Setenv("HELM_PLUGINS", tt.helmPluginsDir) + } + installer, err := NewLocalInstaller(tt.source) + require.NoError(t, err) + path := installer.Path() + assert.Equal(t, tt.expectPath, path) + }) + } +} diff --git a/internal/plugin/installer/oci_installer_test.go b/internal/plugin/installer/oci_installer_test.go index ce381d12c..e59d7eda5 100644 --- a/internal/plugin/installer/oci_installer_test.go +++ b/internal/plugin/installer/oci_installer_test.go @@ -37,7 +37,6 @@ import ( "github.com/stretchr/testify/require" "helm.sh/helm/v4/internal/test/ensure" - "helm.sh/helm/v4/pkg/cli" "helm.sh/helm/v4/pkg/getter" "helm.sh/helm/v4/pkg/helmpath" ) @@ -248,34 +247,34 @@ func TestNewOCIInstaller(t *testing.T) { } func TestOCIInstaller_Path(t *testing.T) { + customPluginsDir := "/foo/bar" + tests := []struct { - name string - source string - pluginName string - expectPath string + name string + source string + helmPluginsDir string + expectPath string }{ { - name: "valid plugin name", - source: "oci://ghcr.io/user/plugin-name:v1.0.0", - pluginName: "plugin-name", - expectPath: helmpath.DataPath("plugins", "plugin-name"), - }, - { - name: "empty source", - source: "", - pluginName: "", - expectPath: "", + name: "default helm plugins dir", + source: "oci://ghcr.io/user/plugin-name:v1.0.0", + helmPluginsDir: "", + expectPath: helmpath.DataPath("plugins", "plugin-name"), + }, { + name: "custom helm plugins dir", + source: "oci://ghcr.io/user/plugin-name:v1.0.0", + helmPluginsDir: customPluginsDir, + expectPath: filepath.Join(customPluginsDir, "plugin-name"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - installer := &OCIInstaller{ - PluginName: tt.pluginName, - base: newBase(tt.source), - settings: cli.New(), + if tt.helmPluginsDir != "" { + t.Setenv("HELM_PLUGINS", tt.helmPluginsDir) } - + installer, err := NewOCIInstaller(tt.source) + require.NoError(t, err) path := installer.Path() assert.Equal(t, tt.expectPath, path, "expected path %s, got %s", tt.expectPath, path) }) diff --git a/internal/plugin/installer/vcs_installer_test.go b/internal/plugin/installer/vcs_installer_test.go index 0fee301a4..44732c679 100644 --- a/internal/plugin/installer/vcs_installer_test.go +++ b/internal/plugin/installer/vcs_installer_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/Masterminds/vcs" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "helm.sh/helm/v4/internal/test/ensure" @@ -143,3 +144,41 @@ func TestVCSInstallerUpdate(t *testing.T) { // Testing update for error require.EqualErrorf(t, Update(vcsInstaller), "plugin repo was modified", "expected error for plugin modified") } + +func TestVCSInstaller_Path(t *testing.T) { + customPluginsDir := "/foo/bar" + + tests := []struct { + name string + source string + version string + helmPluginsDir string + expectPath string + }{ + { + name: "default helm plugins dir", + source: "https://github.com/adamreese/helm-env", + version: "0.2.0", + helmPluginsDir: "", + expectPath: helmpath.DataPath("plugins", "helm-env"), + }, { + name: "custom helm plugins dir", + source: "https://github.com/adamreese/helm-env", + version: "0.2.0", + helmPluginsDir: customPluginsDir, + expectPath: filepath.Join(customPluginsDir, "helm-env"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.helmPluginsDir != "" { + t.Setenv("HELM_PLUGINS", tt.helmPluginsDir) + } + installer, err := NewVCSInstaller(tt.source, tt.version) + require.NoError(t, err) + path := installer.Path() + assert.Equal(t, tt.expectPath, path) + }) + } +} diff --git a/internal/plugin/testdata/plugdir/good/archive-1.2.3.tar.gz b/internal/plugin/testdata/plugdir/good/archive-1.2.3.tar.gz new file mode 100644 index 000000000..3082f4b06 Binary files /dev/null and b/internal/plugin/testdata/plugdir/good/archive-1.2.3.tar.gz differ