diff --git a/cmd/helm/create.go b/cmd/helm/create.go index 4a7dd676c..0034e4f52 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -50,8 +50,9 @@ will be overwritten, but other files will be left alone. ` type createOptions struct { - starter string // --starter - name string + starter string // --starter + name string + starterDir string } func newCreateCmd(out io.Writer) *cobra.Command { @@ -64,6 +65,7 @@ func newCreateCmd(out io.Writer) *cobra.Command { Args: require.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { o.name = args[0] + o.starterDir = helmpath.DataPath("starters") return o.run(out) }, } @@ -87,7 +89,7 @@ func (o *createOptions) run(out io.Writer) error { if o.starter != "" { // Create from the starter - lstarter := filepath.Join(helmpath.Starters(), o.starter) + lstarter := filepath.Join(o.starterDir, o.starter) // If path is absolute, we dont want to prefix it with helm starters folder if filepath.IsAbs(o.starter) { lstarter = o.starter diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go index 76a2c5938..962030aec 100644 --- a/cmd/helm/create_test.go +++ b/cmd/helm/create_test.go @@ -31,15 +31,14 @@ import ( ) func TestCreateCmd(t *testing.T) { + defer ensure.HelmHome(t)() cname := "testchart" - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - defer testChdir(t, helmpath.CachePath())() + dir := ensure.TempDir(t) + defer testChdir(t, dir)() // Run a create if _, _, err := executeActionCommand("create " + cname); err != nil { - t.Errorf("Failed to run create: %s", err) - return + t.Fatalf("Failed to run create: %s", err) } // Test that the chart is there @@ -63,22 +62,22 @@ func TestCreateCmd(t *testing.T) { } func TestCreateStarterCmd(t *testing.T) { + defer ensure.HelmHome(t)() cname := "testchart" defer resetEnv()() - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + os.MkdirAll(helmpath.CachePath(), 0755) defer testChdir(t, helmpath.CachePath())() // Create a starter. - starterchart := helmpath.Starters() - os.Mkdir(starterchart, 0755) + starterchart := helmpath.DataPath("starters") + os.MkdirAll(starterchart, 0755) if dest, err := chartutil.Create("starterchart", starterchart); err != nil { t.Fatalf("Could not create chart: %s", err) } else { t.Logf("Created %s", dest) } tplpath := filepath.Join(starterchart, "starterchart", "templates", "foo.tpl") - if err := ioutil.WriteFile(tplpath, []byte("test"), 0755); err != nil { + if err := ioutil.WriteFile(tplpath, []byte("test"), 0644); err != nil { t.Fatalf("Could not write template: %s", err) } @@ -128,23 +127,23 @@ func TestCreateStarterCmd(t *testing.T) { func TestCreateStarterAbsoluteCmd(t *testing.T) { defer resetEnv()() - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() cname := "testchart" // Create a starter. - starterchart := helmpath.Starters() - os.Mkdir(starterchart, 0755) + starterchart := helmpath.DataPath("starters") + os.MkdirAll(starterchart, 0755) if dest, err := chartutil.Create("starterchart", starterchart); err != nil { t.Fatalf("Could not create chart: %s", err) } else { t.Logf("Created %s", dest) } tplpath := filepath.Join(starterchart, "starterchart", "templates", "foo.tpl") - if err := ioutil.WriteFile(tplpath, []byte("test"), 0755); err != nil { + if err := ioutil.WriteFile(tplpath, []byte("test"), 0644); err != nil { t.Fatalf("Could not write template: %s", err) } + os.MkdirAll(helmpath.CachePath(), 0755) defer testChdir(t, helmpath.CachePath())() starterChartPath := filepath.Join(starterchart, "starterchart") diff --git a/cmd/helm/dependency_build.go b/cmd/helm/dependency_build.go index 0deb0f993..6f5e20613 100644 --- a/cmd/helm/dependency_build.go +++ b/cmd/helm/dependency_build.go @@ -54,17 +54,17 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command { chartpath = filepath.Clean(args[0]) } man := &downloader.Manager{ - Out: out, - ChartPath: chartpath, - Keyring: client.Keyring, - Getters: getter.All(settings), + Out: out, + ChartPath: chartpath, + Keyring: client.Keyring, + Getters: getter.All(settings), + RepositoryConfig: settings.RepositoryConfig, + RepositoryCache: settings.RepositoryCache, + Debug: settings.Debug, } if client.Verify { man.Verify = downloader.VerifyIfPossible } - if settings.Debug { - man.Debug = true - } return man.Build() }, } diff --git a/cmd/helm/dependency_build_test.go b/cmd/helm/dependency_build_test.go index d6ef0c340..0f131bdf3 100644 --- a/cmd/helm/dependency_build_test.go +++ b/cmd/helm/dependency_build_test.go @@ -22,31 +22,26 @@ import ( "strings" "testing" - "helm.sh/helm/internal/test/ensure" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/provenance" "helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo/repotest" ) func TestDependencyBuildCmd(t *testing.T) { - defer resetEnv()() - - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - - srv := repotest.NewServer(helmpath.ConfigPath()) + srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") defer srv.Stop() - if _, err := srv.CopyCharts("testdata/testcharts/*.tgz"); err != nil { + if err != nil { t.Fatal(err) } + rootDir := srv.Root() + srv.LinkIndices() + chartname := "depbuild" - if err := createTestingChart(helmpath.DataPath(), chartname, srv.URL()); err != nil { - t.Fatal(err) - } + createTestingChart(t, rootDir, chartname, srv.URL()) + repoFile := filepath.Join(rootDir, "repositories.yaml") - cmd := fmt.Sprintf("dependency build '%s'", filepath.Join(helmpath.DataPath(), chartname)) + cmd := fmt.Sprintf("dependency build '%s' --repository-config %s --repository-cache %s", filepath.Join(rootDir, chartname), repoFile, rootDir) _, out, err := executeActionCommand(cmd) // In the first pass, we basically want the same results as an update. @@ -60,14 +55,14 @@ func TestDependencyBuildCmd(t *testing.T) { } // Make sure the actual file got downloaded. - expect := filepath.Join(helmpath.DataPath(), chartname, "charts/reqtest-0.1.0.tgz") + expect := filepath.Join(rootDir, chartname, "charts/reqtest-0.1.0.tgz") if _, err := os.Stat(expect); err != nil { t.Fatal(err) } // In the second pass, we want to remove the chart's request dependency, // then see if it restores from the lock. - lockfile := filepath.Join(helmpath.DataPath(), chartname, "Chart.lock") + lockfile := filepath.Join(rootDir, chartname, "Chart.lock") if _, err := os.Stat(lockfile); err != nil { t.Fatal(err) } @@ -82,7 +77,6 @@ func TestDependencyBuildCmd(t *testing.T) { } // Now repeat the test that the dependency exists. - expect = filepath.Join(helmpath.DataPath(), chartname, "charts/reqtest-0.1.0.tgz") if _, err := os.Stat(expect); err != nil { t.Fatal(err) } @@ -93,7 +87,7 @@ func TestDependencyBuildCmd(t *testing.T) { t.Fatal(err) } - i, err := repo.LoadIndexFile(helmpath.CacheIndex("test")) + i, err := repo.LoadIndexFile(filepath.Join(rootDir, "index.yaml")) if err != nil { t.Fatal(err) } diff --git a/cmd/helm/dependency_update.go b/cmd/helm/dependency_update.go index b9b211058..c86c2f9e2 100644 --- a/cmd/helm/dependency_update.go +++ b/cmd/helm/dependency_update.go @@ -58,18 +58,18 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command { chartpath = filepath.Clean(args[0]) } man := &downloader.Manager{ - Out: out, - ChartPath: chartpath, - Keyring: client.Keyring, - SkipUpdate: client.SkipRefresh, - Getters: getter.All(settings), + Out: out, + ChartPath: chartpath, + Keyring: client.Keyring, + SkipUpdate: client.SkipRefresh, + Getters: getter.All(settings), + RepositoryConfig: settings.RepositoryConfig, + RepositoryCache: settings.RepositoryCache, + Debug: settings.Debug, } if client.Verify { man.Verify = downloader.VerifyAlways } - if settings.Debug { - man.Debug = true - } return man.Update() }, } diff --git a/cmd/helm/dependency_update_test.go b/cmd/helm/dependency_update_test.go index 0f1f5e5c7..9cbd4f029 100644 --- a/cmd/helm/dependency_update_test.go +++ b/cmd/helm/dependency_update_test.go @@ -33,28 +33,31 @@ import ( ) func TestDependencyUpdateCmd(t *testing.T) { - defer resetEnv()() - - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - - srv := repotest.NewServer(helmpath.ConfigPath()) - defer srv.Stop() - copied, err := srv.CopyCharts("testdata/testcharts/*.tgz") + srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") if err != nil { t.Fatal(err) } - t.Logf("Copied charts:\n%s", strings.Join(copied, "\n")) + defer srv.Stop() t.Logf("Listening on directory %s", srv.Root()) + if err := srv.LinkIndices(); err != nil { + t.Fatal(err) + } + + dir := func(p ...string) string { + return filepath.Join(append([]string{srv.Root()}, p...)...) + } + chartname := "depup" ch := createTestingMetadata(chartname, srv.URL()) md := ch.Metadata - if err := chartutil.SaveDir(ch, helmpath.DataPath()); err != nil { + if err := chartutil.SaveDir(ch, dir()); err != nil { t.Fatal(err) } - _, out, err := executeActionCommand(fmt.Sprintf("dependency update '%s'", filepath.Join(helmpath.DataPath(), chartname))) + _, out, err := executeActionCommand( + fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir()), + ) if err != nil { t.Logf("Output: %s", out) t.Fatal(err) @@ -66,7 +69,7 @@ func TestDependencyUpdateCmd(t *testing.T) { } // Make sure the actual file got downloaded. - expect := filepath.Join(helmpath.DataPath(), chartname, "charts/reqtest-0.1.0.tgz") + expect := dir(chartname, "charts/reqtest-0.1.0.tgz") if _, err := os.Stat(expect); err != nil { t.Fatal(err) } @@ -76,7 +79,7 @@ func TestDependencyUpdateCmd(t *testing.T) { t.Fatal(err) } - i, err := repo.LoadIndexFile(helmpath.CacheIndex("test")) + i, err := repo.LoadIndexFile(dir(helmpath.CacheIndexFile("test"))) if err != nil { t.Fatal(err) } @@ -92,12 +95,11 @@ func TestDependencyUpdateCmd(t *testing.T) { {Name: "reqtest", Version: "0.1.0", Repository: srv.URL()}, {Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()}, } - dir := filepath.Join(helmpath.DataPath(), chartname, "Chart.yaml") - if err := chartutil.SaveChartfile(dir, md); err != nil { + if err := chartutil.SaveChartfile(dir(chartname, "Chart.yaml"), md); err != nil { t.Fatal(err) } - _, out, err = executeActionCommand(fmt.Sprintf("dependency update '%s'", filepath.Join(helmpath.DataPath(), chartname))) + _, out, err = executeActionCommand(fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir())) if err != nil { t.Logf("Output: %s", out) t.Fatal(err) @@ -105,11 +107,11 @@ func TestDependencyUpdateCmd(t *testing.T) { // In this second run, we should see compressedchart-0.3.0.tgz, and not // the 0.1.0 version. - expect = filepath.Join(helmpath.DataPath(), chartname, "charts/compressedchart-0.3.0.tgz") + expect = dir(chartname, "charts/compressedchart-0.3.0.tgz") if _, err := os.Stat(expect); err != nil { t.Fatalf("Expected %q: %s", expect, err) } - dontExpect := filepath.Join(helmpath.DataPath(), chartname, "charts/compressedchart-0.1.0.tgz") + dontExpect := dir(chartname, "charts/compressedchart-0.1.0.tgz") if _, err := os.Stat(dontExpect); err == nil { t.Fatalf("Unexpected %q", dontExpect) } @@ -117,9 +119,7 @@ func TestDependencyUpdateCmd(t *testing.T) { func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) { defer resetEnv()() - - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() srv := repotest.NewServer(helmpath.ConfigPath()) defer srv.Stop() @@ -131,11 +131,9 @@ func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) { t.Logf("Listening on directory %s", srv.Root()) chartname := "depup" - if err := createTestingChart(helmpath.DataPath(), chartname, srv.URL()); err != nil { - t.Fatal(err) - } + createTestingChart(t, helmpath.DataPath(), chartname, srv.URL()) - _, out, err := executeActionCommand(fmt.Sprintf("dependency update --skip-refresh %s", filepath.Join(helmpath.DataPath(), chartname))) + _, out, err := executeActionCommand(fmt.Sprintf("dependency update --skip-refresh %s", helmpath.DataPath(chartname))) if err == nil { t.Fatal("Expected failure to find the repo with skipRefresh") } @@ -148,25 +146,27 @@ func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) { func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { defer resetEnv()() + defer ensure.HelmHome(t)() - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - - srv := repotest.NewServer(helmpath.ConfigPath()) - defer srv.Stop() - copied, err := srv.CopyCharts("testdata/testcharts/*.tgz") + srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz") if err != nil { t.Fatal(err) } - t.Logf("Copied charts:\n%s", strings.Join(copied, "\n")) + defer srv.Stop() t.Logf("Listening on directory %s", srv.Root()) - chartname := "depupdelete" - if err := createTestingChart(helmpath.DataPath(), chartname, srv.URL()); err != nil { + if err := srv.LinkIndices(); err != nil { t.Fatal(err) } - _, output, err := executeActionCommand(fmt.Sprintf("dependency update %s", filepath.Join(helmpath.DataPath(), chartname))) + chartname := "depupdelete" + + dir := func(p ...string) string { + return filepath.Join(append([]string{srv.Root()}, p...)...) + } + createTestingChart(t, dir(), chartname, srv.URL()) + + _, output, err := executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir())) if err != nil { t.Logf("Output: %s", output) t.Fatal(err) @@ -175,14 +175,14 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { // Chart repo is down srv.Stop() - _, output, err = executeActionCommand(fmt.Sprintf("dependency update %s", filepath.Join(helmpath.DataPath(), chartname))) + _, output, err = executeActionCommand(fmt.Sprintf("dependency update %s --repository-config %s --repository-cache %s", dir(chartname), dir("repositories.yaml"), dir())) if err == nil { t.Logf("Output: %s", output) t.Fatal("Expected error, got nil") } // Make sure charts dir still has dependencies - files, err := ioutil.ReadDir(filepath.Join(filepath.Join(helmpath.DataPath(), chartname), "charts")) + files, err := ioutil.ReadDir(filepath.Join(dir(chartname), "charts")) if err != nil { t.Fatal(err) } @@ -198,7 +198,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { } // Make sure tmpcharts is deleted - if _, err := os.Stat(filepath.Join(filepath.Join(helmpath.DataPath(), chartname), "tmpcharts")); !os.IsNotExist(err) { + if _, err := os.Stat(filepath.Join(dir(chartname), "tmpcharts")); !os.IsNotExist(err) { t.Fatalf("tmpcharts dir still exists") } } @@ -223,7 +223,10 @@ func createTestingMetadata(name, baseURL string) *chart.Chart { // createTestingChart creates a basic chart that depends on reqtest-0.1.0 // // The baseURL can be used to point to a particular repository server. -func createTestingChart(dest, name, baseURL string) error { +func createTestingChart(t *testing.T, dest, name, baseURL string) { + t.Helper() cfile := createTestingMetadata(name, baseURL) - return chartutil.SaveDir(cfile, dest) + if err := chartutil.SaveDir(cfile, dest); err != nil { + t.Fatal(err) + } } diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 398b38b64..cdacf0b0c 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -44,7 +44,7 @@ import ( const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI") var ( - settings cli.EnvSettings + settings = cli.New() config genericclioptions.RESTClientGetter configOnce sync.Once ) diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index 152e69b54..0abadacb7 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -42,11 +42,6 @@ func init() { action.Timestamper = testTimestamper } -func TestMain(m *testing.M) { - exitCode := m.Run() - os.Exit(exitCode) -} - func runTestCmd(t *testing.T, tests []cmdTestCase) { t.Helper() for _, tt := range tests { @@ -59,6 +54,7 @@ func runTestCmd(t *testing.T, tests []cmdTestCase) { t.Fatal(err) } } + t.Log("running cmd: ", tt.cmd) _, out, err := executeActionCommandC(storage, tt.cmd) if (err != nil) != tt.wantError { t.Errorf("expected error, got '%v'", err) diff --git a/cmd/helm/init.go b/cmd/helm/init.go deleted file mode 100644 index 90ffbb1b3..000000000 --- a/cmd/helm/init.go +++ /dev/null @@ -1,234 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "fmt" - "io" - "io/ioutil" - "os" - - "github.com/Masterminds/semver" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "sigs.k8s.io/yaml" - - "helm.sh/helm/cmd/helm/require" - "helm.sh/helm/pkg/helmpath" - "helm.sh/helm/pkg/plugin" - "helm.sh/helm/pkg/plugin/installer" - "helm.sh/helm/pkg/repo" -) - -const initDesc = ` -This command sets up local configuration. - -Helm stores configuration based on the XDG base directory specification, so - -- cached files are stored in $XDG_CACHE_HOME/helm -- configuration is stored in $XDG_CONFIG_HOME/helm -- data is stored in $XDG_DATA_HOME/helm - -By default, the default directories depend on the Operating System. The defaults are listed below: - -+------------------+---------------------------+--------------------------------+-------------------------+ -| Operating System | Cache Path | Configuration Path | Data Path | -+------------------+---------------------------+--------------------------------+-------------------------+ -| Linux | $HOME/.cache/helm | $HOME/.config/helm | $HOME/.local/share/helm | -| macOS | $HOME/Library/Caches/helm | $HOME/Library/Preferences/helm | $HOME/Library/helm | -| Windows | %TEMP%\helm | %APPDATA%\helm | %APPDATA%\helm | -+------------------+---------------------------+--------------------------------+-------------------------+ -` - -type initOptions struct { - skipRefresh bool // --skip-refresh - pluginsFilename string // --plugins -} - -type pluginsFileEntry struct { - URL string `json:"url"` - Version string `json:"version,omitempty"` -} - -type pluginsFile struct { - Plugins []*pluginsFileEntry `json:"plugins"` -} - -func newInitCmd(out io.Writer) *cobra.Command { - o := &initOptions{} - - cmd := &cobra.Command{ - Use: "init", - Short: "initialize Helm client", - Long: initDesc, - Args: require.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out) - }, - } - - f := cmd.Flags() - f.BoolVar(&o.skipRefresh, "skip-refresh", false, "do not refresh (download) the local repository cache") - f.StringVar(&o.pluginsFilename, "plugins", "", "a YAML file specifying plugins to install") - - return cmd -} - -// run initializes local config. -func (o *initOptions) run(out io.Writer) error { - if err := ensureDirectories(out); err != nil { - return err - } - if err := ensureReposFile(out, o.skipRefresh); err != nil { - return err - } - if err := ensureRepoFileFormat(helmpath.RepositoryFile(), out); err != nil { - return err - } - if o.pluginsFilename != "" { - if err := ensurePluginsInstalled(o.pluginsFilename, out); err != nil { - return err - } - } - fmt.Fprintln(out, "Helm is now configured to use the following directories:") - fmt.Fprintf(out, "Cache: %s\n", helmpath.CachePath()) - fmt.Fprintf(out, "Configuration: %s\n", helmpath.ConfigPath()) - fmt.Fprintf(out, "Data: %s\n", helmpath.DataPath()) - fmt.Fprintln(out, "Happy Helming!") - return nil -} - -// ensureDirectories checks to see if the directories Helm uses exists. -// -// If they do not exist, this function will create it. -func ensureDirectories(out io.Writer) error { - directories := []string{ - helmpath.CachePath(), - helmpath.ConfigPath(), - helmpath.DataPath(), - helmpath.RepositoryCache(), - helmpath.Plugins(), - helmpath.PluginCache(), - helmpath.Starters(), - helmpath.Archive(), - } - for _, p := range directories { - if fi, err := os.Stat(p); err != nil { - fmt.Fprintf(out, "Creating %s \n", p) - if err := os.MkdirAll(p, 0755); err != nil { - return errors.Wrapf(err, "could not create %s", p) - } - } else if !fi.IsDir() { - return errors.Errorf("%s must be a directory", p) - } - } - - return nil -} - -func ensureReposFile(out io.Writer, skipRefresh bool) error { - repoFile := helmpath.RepositoryFile() - if fi, err := os.Stat(repoFile); err != nil { - fmt.Fprintf(out, "Creating %s \n", repoFile) - f := repo.NewFile() - if err := f.WriteFile(repoFile, 0644); err != nil { - return err - } - } else if fi.IsDir() { - return errors.Errorf("%s must be a file, not a directory", repoFile) - } - return nil -} - -func ensureRepoFileFormat(file string, out io.Writer) error { - r, err := repo.LoadFile(file) - if err == repo.ErrRepoOutOfDate { - fmt.Fprintln(out, "Updating repository file format...") - if err := r.WriteFile(file, 0644); err != nil { - return err - } - } - return nil -} - -func ensurePluginsInstalled(pluginsFilename string, out io.Writer) error { - bytes, err := ioutil.ReadFile(pluginsFilename) - if err != nil { - return err - } - - pf := new(pluginsFile) - if err := yaml.Unmarshal(bytes, &pf); err != nil { - return errors.Wrapf(err, "failed to parse %s", pluginsFilename) - } - - for _, requiredPlugin := range pf.Plugins { - if err := ensurePluginInstalled(requiredPlugin, pluginsFilename, out); err != nil { - return errors.Wrapf(err, "failed to install plugin from %s", requiredPlugin.URL) - } - } - - return nil -} - -func ensurePluginInstalled(requiredPlugin *pluginsFileEntry, pluginsFilename string, out io.Writer) error { - i, err := installer.NewForSource(requiredPlugin.URL, requiredPlugin.Version) - if err != nil { - return err - } - - if _, pathErr := os.Stat(i.Path()); os.IsNotExist(pathErr) { - if err := installer.Install(i); err != nil { - return err - } - - p, err := plugin.LoadDir(i.Path()) - if err != nil { - return err - } - - if err := runHook(p, plugin.Install); err != nil { - return err - } - - fmt.Fprintf(out, "Installed plugin: %s\n", p.Metadata.Name) - } else if requiredPlugin.Version != "" { - p, err := plugin.LoadDir(i.Path()) - if err != nil { - return err - } - - if p.Metadata.Version != "" { - pluginVersion, err := semver.NewVersion(p.Metadata.Version) - if err != nil { - return err - } - - constraint, err := semver.NewConstraint(requiredPlugin.Version) - if err != nil { - return err - } - - if !constraint.Check(pluginVersion) { - fmt.Fprintf(out, "WARNING: Installed plugin '%s' is at version %s, while %s specifies %s\n", - p.Metadata.Name, p.Metadata.Version, pluginsFilename, requiredPlugin.Version) - } - } - } - - return nil -} diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go deleted file mode 100644 index 125e017b9..000000000 --- a/cmd/helm/init_test.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "bytes" - "os" - "testing" - - "helm.sh/helm/internal/test/ensure" - "helm.sh/helm/pkg/helmpath" -) - -const testPluginsFile = "testdata/plugins.yaml" - -func TestEnsureHome(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - - b := bytes.NewBuffer(nil) - if err := ensureDirectories(b); err != nil { - t.Error(err) - } - if err := ensureReposFile(b, false); err != nil { - t.Error(err) - } - if err := ensureReposFile(b, true); err != nil { - t.Error(err) - } - if err := ensureRepoFileFormat(helmpath.RepositoryFile(), b); err != nil { - t.Error(err) - } - if err := ensurePluginsInstalled(testPluginsFile, b); err != nil { - t.Error(err) - } - - expectedDirs := []string{helmpath.CachePath(), helmpath.ConfigPath(), helmpath.DataPath()} - for _, dir := range expectedDirs { - if fi, err := os.Stat(dir); err != nil { - t.Errorf("%s", err) - } else if !fi.IsDir() { - t.Errorf("%s is not a directory", fi) - } - } - - if fi, err := os.Stat(helmpath.RepositoryFile()); err != nil { - t.Error(err) - } else if fi.IsDir() { - t.Errorf("%s should not be a directory", fi) - } - - if plugins, err := findPlugins(helmpath.Plugins()); err != nil { - t.Error(err) - } else if len(plugins) != 1 { - t.Errorf("Expected 1 plugin, got %d", len(plugins)) - } else if plugins[0].Metadata.Name != "testplugin" { - t.Errorf("Expected %s to be installed", "testplugin") - } -} diff --git a/cmd/helm/install.go b/cmd/helm/install.go index ec5d76188..509ad2564 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -174,7 +174,8 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options debug("CHART PATH: %s\n", cp) - vals, err := valueOpts.MergeValues(settings) + p := getter.All(settings) + vals, err := valueOpts.MergeValues(p) if err != nil { return nil, err } @@ -197,11 +198,13 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options if err := action.CheckDependencies(chartRequested, req); err != nil { if client.DependencyUpdate { man := &downloader.Manager{ - Out: out, - ChartPath: cp, - Keyring: client.ChartPathOptions.Keyring, - SkipUpdate: false, - Getters: getter.All(settings), + Out: out, + ChartPath: cp, + Keyring: client.ChartPathOptions.Keyring, + SkipUpdate: false, + Getters: p, + RepositoryConfig: settings.RepositoryConfig, + RepositoryCache: settings.RepositoryCache, } if err := man.Update(); err != nil { return nil, err diff --git a/cmd/helm/lint.go b/cmd/helm/lint.go index bab1ebb7c..839416bf3 100644 --- a/cmd/helm/lint.go +++ b/cmd/helm/lint.go @@ -26,6 +26,7 @@ import ( "helm.sh/helm/pkg/action" "helm.sh/helm/pkg/cli/values" + "helm.sh/helm/pkg/getter" ) var longLintHelp = ` @@ -51,7 +52,7 @@ func newLintCmd(out io.Writer) *cobra.Command { paths = args } client.Namespace = getNamespace() - vals, err := valueOpts.MergeValues(settings) + vals, err := valueOpts.MergeValues(getter.All(settings)) if err != nil { return err } diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index e0d4012c8..96c587dd3 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -26,7 +26,6 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/plugin" ) @@ -42,7 +41,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { return } - found, err := findPlugins(helmpath.Plugins()) + found, err := findPlugins(settings.PluginsDirectory) if err != nil { fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err) return diff --git a/cmd/helm/package.go b/cmd/helm/package.go index a9dc254cf..808aabf77 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -62,7 +62,10 @@ func newPackageCmd(out io.Writer) *cobra.Command { return errors.New("--keyring is required for signing a package") } } - vals, err := valueOpts.MergeValues(settings) + client.RepositoryConfig = settings.RepositoryConfig + client.RepositoryCache = settings.RepositoryCache + p := getter.All(settings) + vals, err := valueOpts.MergeValues(p) if err != nil { return err } @@ -75,11 +78,13 @@ func newPackageCmd(out io.Writer) *cobra.Command { if client.DependencyUpdate { downloadManager := &downloader.Manager{ - Out: ioutil.Discard, - ChartPath: path, - Keyring: client.Keyring, - Getters: getter.All(settings), - Debug: settings.Debug, + Out: ioutil.Discard, + ChartPath: path, + Keyring: client.Keyring, + Getters: p, + Debug: settings.Debug, + RepositoryConfig: settings.RepositoryConfig, + RepositoryCache: settings.RepositoryCache, } if err := downloadManager.Update(); err != nil { diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go index 32192d9bb..e21d3defc 100644 --- a/cmd/helm/package_test.go +++ b/cmd/helm/package_test.go @@ -32,19 +32,14 @@ import ( "helm.sh/helm/pkg/chart" "helm.sh/helm/pkg/chart/loader" "helm.sh/helm/pkg/chartutil" - "helm.sh/helm/pkg/helmpath" ) func TestPackage(t *testing.T) { - statExe := "stat" statFileMsg := "no such file or directory" if runtime.GOOS == "windows" { - statExe = "FindFirstFile" statFileMsg = "The system cannot find the file specified." } - defer resetEnv()() - tests := []struct { name string flags map[string]string @@ -100,13 +95,6 @@ func TestPackage(t *testing.T) { expect: "", hasfile: "toot/alpine-0.1.0.tgz", }, - { - name: "package --destination does-not-exist", - args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"destination": "does-not-exist"}, - expect: fmt.Sprintf("failed to save: %s does-not-exist: %s", statExe, statFileMsg), - err: true, - }, { name: "package --sign --key=KEY --keyring=KEYRING testdata/testcharts/alpine", args: []string{"testdata/testcharts/alpine"}, @@ -139,75 +127,70 @@ func TestPackage(t *testing.T) { t.Fatal(err) } - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - t.Logf("Running tests in %s", helmpath.CachePath()) - defer testChdir(t, helmpath.CachePath())() - - if err := os.Mkdir("toot", 0777); err != nil { - t.Fatal(err) - } - for _, tt := range tests { - buf := bytes.NewBuffer(nil) - c := newPackageCmd(buf) + t.Run(tt.name, func(t *testing.T) { + cachePath := ensure.TempDir(t) + defer testChdir(t, cachePath)() - // This is an unfortunate byproduct of the tmpdir - if v, ok := tt.flags["keyring"]; ok && len(v) > 0 { - tt.flags["keyring"] = filepath.Join(origDir, v) - } + if err := os.MkdirAll("toot", 0777); err != nil { + t.Fatal(err) + } + var buf bytes.Buffer + c := newPackageCmd(&buf) - setFlags(c, tt.flags) - re := regexp.MustCompile(tt.expect) + // This is an unfortunate byproduct of the tmpdir + if v, ok := tt.flags["keyring"]; ok && len(v) > 0 { + tt.flags["keyring"] = filepath.Join(origDir, v) + } - adjustedArgs := make([]string, len(tt.args)) - for i, f := range tt.args { - adjustedArgs[i] = filepath.Join(origDir, f) - } + setFlags(c, tt.flags) + re := regexp.MustCompile(tt.expect) - err := c.RunE(c, adjustedArgs) - if err != nil { - if tt.err && re.MatchString(err.Error()) { - continue + adjustedArgs := make([]string, len(tt.args)) + for i, f := range tt.args { + adjustedArgs[i] = filepath.Join(origDir, f) } - t.Errorf("%q: expected error %q, got %q", tt.name, tt.expect, err) - continue - } - if !re.Match(buf.Bytes()) { - t.Errorf("%q: expected output %q, got %q", tt.name, tt.expect, buf.String()) - } + err := c.RunE(c, adjustedArgs) + if err != nil { + if tt.err && re.MatchString(err.Error()) { + return + } + t.Fatalf("%q: expected error %q, got %q", tt.name, tt.expect, err) + } - if len(tt.hasfile) > 0 { - if fi, err := os.Stat(tt.hasfile); err != nil { - t.Errorf("%q: expected file %q, got err %q", tt.name, tt.hasfile, err) - } else if fi.Size() == 0 { - t.Errorf("%q: file %q has zero bytes.", tt.name, tt.hasfile) + if !re.Match(buf.Bytes()) { + t.Errorf("%q: expected output %q, got %q", tt.name, tt.expect, buf.String()) } - } - if v, ok := tt.flags["sign"]; ok && v == "1" { - if fi, err := os.Stat(tt.hasfile + ".prov"); err != nil { - t.Errorf("%q: expected provenance file", tt.name) - } else if fi.Size() == 0 { - t.Errorf("%q: provenance file is empty", tt.name) + if len(tt.hasfile) > 0 { + if fi, err := os.Stat(tt.hasfile); err != nil { + t.Errorf("%q: expected file %q, got err %q", tt.name, tt.hasfile, err) + } else if fi.Size() == 0 { + t.Errorf("%q: file %q has zero bytes.", tt.name, tt.hasfile) + } } - } + + if v, ok := tt.flags["sign"]; ok && v == "1" { + if fi, err := os.Stat(tt.hasfile + ".prov"); err != nil { + t.Errorf("%q: expected provenance file", tt.name) + } else if fi.Size() == 0 { + t.Errorf("%q: provenance file is empty", tt.name) + } + } + }) } } func TestSetAppVersion(t *testing.T) { - defer resetEnv()() - var ch *chart.Chart expectedAppVersion := "app-version-foo" - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + dir := ensure.TempDir(t) c := newPackageCmd(&bytes.Buffer{}) flags := map[string]string{ - "destination": helmpath.CachePath(), + "destination": dir, "app-version": expectedAppVersion, } setFlags(c, flags) @@ -215,7 +198,7 @@ func TestSetAppVersion(t *testing.T) { t.Errorf("unexpected error %q", err) } - chartPath := filepath.Join(helmpath.CachePath(), "alpine-0.1.0.tgz") + chartPath := filepath.Join(dir, "alpine-0.1.0.tgz") if fi, err := os.Stat(chartPath); err != nil { t.Errorf("expected file %q, got err %q", chartPath, err) } else if fi.Size() == 0 { @@ -223,7 +206,7 @@ func TestSetAppVersion(t *testing.T) { } ch, err := loader.Load(chartPath) if err != nil { - t.Errorf("unexpected error loading packaged chart: %v", err) + t.Fatalf("unexpected error loading packaged chart: %v", err) } if ch.Metadata.AppVersion != expectedAppVersion { t.Errorf("expected app-version %q, found %q", expectedAppVersion, ch.Metadata.AppVersion) @@ -233,6 +216,8 @@ func TestSetAppVersion(t *testing.T) { func TestPackageValues(t *testing.T) { defer resetEnv()() + repoFile := "testdata/helmhome/helm/repositories.yaml" + testCases := []struct { desc string args []string @@ -243,33 +228,32 @@ func TestPackageValues(t *testing.T) { { desc: "helm package, single values file", args: []string{"testdata/testcharts/alpine"}, + flags: map[string]string{"repository-config": repoFile}, valuefilesContents: []string{"Name: chart-name-foo"}, expected: []string{"Name: chart-name-foo"}, }, { desc: "helm package, multiple values files", args: []string{"testdata/testcharts/alpine"}, + flags: map[string]string{"repository-config": repoFile}, valuefilesContents: []string{"Name: chart-name-foo", "foo: bar"}, expected: []string{"Name: chart-name-foo", "foo: bar"}, }, { desc: "helm package, with set option", args: []string{"testdata/testcharts/alpine"}, - flags: map[string]string{"set": "Name=chart-name-foo"}, + flags: map[string]string{"set": "Name=chart-name-foo", "repository-config": repoFile}, expected: []string{"Name: chart-name-foo"}, }, { desc: "helm package, set takes precedence over value file", args: []string{"testdata/testcharts/alpine"}, valuefilesContents: []string{"Name: chart-name-foo"}, - flags: map[string]string{"set": "Name=chart-name-bar"}, + flags: map[string]string{"set": "Name=chart-name-bar", "repository-config": repoFile}, expected: []string{"Name: chart-name-bar"}, }, } - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - for _, tc := range testCases { var files []string for _, contents := range tc.valuefilesContents { @@ -283,58 +267,50 @@ func TestPackageValues(t *testing.T) { t.Errorf("unexpected error parsing values: %q", err) } - runAndVerifyPackageCommandValues(t, tc.args, tc.flags, valueFiles, expected) - } -} + outputDir := ensure.TempDir(t) -func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[string]string, valueFiles string, expected chartutil.Values) { - t.Helper() - outputDir := ensure.TempDir(t) + if len(tc.flags) == 0 { + tc.flags = make(map[string]string) + } + tc.flags["destination"] = outputDir - if len(flags) == 0 { - flags = make(map[string]string) - } - flags["destination"] = outputDir + if len(valueFiles) > 0 { + tc.flags["values"] = valueFiles + } - if len(valueFiles) > 0 { - flags["values"] = valueFiles - } + cmd := newPackageCmd(&bytes.Buffer{}) + setFlags(cmd, tc.flags) + if err := cmd.RunE(cmd, tc.args); err != nil { + t.Fatalf("unexpected error: %q", err) + } - cmd := newPackageCmd(&bytes.Buffer{}) - setFlags(cmd, flags) - if err := cmd.RunE(cmd, args); err != nil { - t.Errorf("unexpected error: %q", err) - } + outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz") + verifyOutputChartExists(t, outputFile) - outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz") - verifyOutputChartExists(t, outputFile) + actual, err := getChartValues(outputFile) + if err != nil { + t.Fatalf("unexpected error extracting chart values: %q", err) + } - actual, err := getChartValues(outputFile) - if err != nil { - t.Errorf("unexpected error extracting chart values: %q", err) + verifyValues(t, actual, expected) } - - verifyValues(t, actual, expected) } func createValuesFile(t *testing.T, data string) string { outputDir := ensure.TempDir(t) outputFile := filepath.Join(outputDir, "values.yaml") - if err := ioutil.WriteFile(outputFile, []byte(data), 0755); err != nil { + if err := ioutil.WriteFile(outputFile, []byte(data), 0644); err != nil { t.Fatalf("err: %s", err) } - return outputFile } func getChartValues(chartPath string) (chartutil.Values, error) { - chart, err := loader.Load(chartPath) if err != nil { return nil, err } - return chart.Values, nil } diff --git a/cmd/helm/plugin_list.go b/cmd/helm/plugin_list.go index 929313e9e..eccc54e2d 100644 --- a/cmd/helm/plugin_list.go +++ b/cmd/helm/plugin_list.go @@ -21,8 +21,6 @@ import ( "github.com/gosuri/uitable" "github.com/spf13/cobra" - - "helm.sh/helm/pkg/helmpath" ) func newPluginListCmd(out io.Writer) *cobra.Command { @@ -30,8 +28,8 @@ func newPluginListCmd(out io.Writer) *cobra.Command { Use: "list", Short: "list installed Helm plugins", RunE: func(cmd *cobra.Command, args []string) error { - debug("pluginDirs: %s", helmpath.Plugins()) - plugins, err := findPlugins(helmpath.Plugins()) + debug("pluginDirs: %s", settings.PluginsDirectory) + plugins, err := findPlugins(settings.PluginsDirectory) if err != nil { return err } diff --git a/cmd/helm/plugin_remove.go b/cmd/helm/plugin_remove.go index 1d4ad67c2..a668c7115 100644 --- a/cmd/helm/plugin_remove.go +++ b/cmd/helm/plugin_remove.go @@ -24,7 +24,6 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/plugin" ) @@ -56,8 +55,8 @@ func (o *pluginRemoveOptions) complete(args []string) error { } func (o *pluginRemoveOptions) run(out io.Writer) error { - debug("loading installed plugins from %s", helmpath.Plugins()) - plugins, err := findPlugins(helmpath.Plugins()) + debug("loading installed plugins from %s", settings.PluginsDirectory) + plugins, err := findPlugins(settings.PluginsDirectory) if err != nil { return err } diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go index bde7b9c7f..17b6b75c8 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -18,16 +18,11 @@ package main import ( "bytes" "os" - "path/filepath" "runtime" "strings" "testing" "github.com/spf13/cobra" - - "helm.sh/helm/pkg/helmpath" - "helm.sh/helm/pkg/helmpath/xdg" - "helm.sh/helm/pkg/plugin" ) func TestManuallyProcessArgs(t *testing.T) { @@ -63,25 +58,22 @@ func TestManuallyProcessArgs(t *testing.T) { } func TestLoadPlugins(t *testing.T) { - defer resetEnv()() - - os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome") - os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome") - os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome") + settings.PluginsDirectory = "testdata/helmhome/helm/plugins" + settings.RepositoryConfig = "testdata/helmhome/helm/repositories.yaml" + settings.RepositoryCache = "testdata/helmhome/helm/repository" - out := bytes.NewBuffer(nil) - cmd := &cobra.Command{} - loadPlugins(cmd, out) + var ( + out bytes.Buffer + cmd cobra.Command + ) + loadPlugins(&cmd, &out) envs := strings.Join([]string{ "fullenv", - helmpath.Plugins() + "/fullenv", - helmpath.Plugins(), - helmpath.CachePath(), - helmpath.ConfigPath(), - helmpath.DataPath(), - helmpath.RepositoryFile(), - helmpath.RepositoryCache(), + "testdata/helmhome/helm/plugins/fullenv", + "testdata/helmhome/helm/plugins", + "testdata/helmhome/helm/repositories.yaml", + "testdata/helmhome/helm/repository", os.Args[0], }, "\n") @@ -95,7 +87,7 @@ func TestLoadPlugins(t *testing.T) { }{ {"args", "echo args", "This echos args", "-a -b -c\n", []string{"-a", "-b", "-c"}}, {"echo", "echo stuff", "This echos stuff", "hello\n", []string{}}, - {"env", "env stuff", "show the env", helmpath.DataPath() + "\n", []string{}}, + {"env", "env stuff", "show the env", "env\n", []string{}}, {"fullenv", "show env vars", "show all env vars", envs + "\n", []string{}}, } @@ -123,7 +115,7 @@ func TestLoadPlugins(t *testing.T) { // tests until this is fixed if runtime.GOOS != "windows" { if err := pp.RunE(pp, tt.args); err != nil { - t.Errorf("Error running %s: %s", tt.use, err) + t.Errorf("Error running %s: %+v", tt.use, err) } if out.String() != tt.expect { t.Errorf("Expected %s to output:\n%s\ngot\n%s", tt.use, tt.expect, out.String()) @@ -133,9 +125,8 @@ func TestLoadPlugins(t *testing.T) { } func TestLoadPlugins_HelmNoPlugins(t *testing.T) { - defer resetEnv()() - - os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome") + settings.PluginsDirectory = "testdata/helmhome/helm/plugins" + settings.RepositoryConfig = "testdata/helmhome/helm/repository" os.Setenv("HELM_NO_PLUGINS", "1") @@ -148,34 +139,3 @@ func TestLoadPlugins_HelmNoPlugins(t *testing.T) { t.Fatalf("Expected 0 plugins, got %d", len(plugins)) } } - -func TestSetupEnv(t *testing.T) { - defer resetEnv()() - name := "pequod" - os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome") - base := filepath.Join(helmpath.Plugins(), name) - settings.Debug = true - defer func() { - settings.Debug = false - }() - - plugin.SetupPluginEnv(settings, name, base) - for _, tt := range []struct { - name string - expect string - }{ - {"HELM_PLUGIN_NAME", name}, - {"HELM_PLUGIN_DIR", base}, - {"HELM_DEBUG", "1"}, - {"HELM_PATH_REPOSITORY_FILE", helmpath.RepositoryFile()}, - {"HELM_PATH_CACHE", helmpath.CachePath()}, - {"HELM_PATH_CONFIG", helmpath.ConfigPath()}, - {"HELM_PATH_DATA", helmpath.DataPath()}, - {"HELM_PATH_STARTER", helmpath.Starters()}, - {"HELM_PLUGIN", helmpath.Plugins()}, - } { - if got := os.Getenv(tt.name); got != tt.expect { - t.Errorf("Expected $%s=%q, got %q", tt.name, tt.expect, got) - } - } -} diff --git a/cmd/helm/plugin_update.go b/cmd/helm/plugin_update.go index d7be8bb0e..4cf2cbf7b 100644 --- a/cmd/helm/plugin_update.go +++ b/cmd/helm/plugin_update.go @@ -24,7 +24,6 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/plugin" "helm.sh/helm/pkg/plugin/installer" ) @@ -58,8 +57,8 @@ func (o *pluginUpdateOptions) complete(args []string) error { func (o *pluginUpdateOptions) run(out io.Writer) error { installer.Debug = settings.Debug - debug("loading installed plugins from %s", helmpath.Plugins()) - plugins, err := findPlugins(helmpath.Plugins()) + debug("loading installed plugins from %s", settings.PluginsDirectory) + plugins, err := findPlugins(settings.PluginsDirectory) if err != nil { return err } diff --git a/cmd/helm/pull_test.go b/cmd/helm/pull_test.go index 311f6ffca..6fb66cbc6 100644 --- a/cmd/helm/pull_test.go +++ b/cmd/helm/pull_test.go @@ -21,19 +21,12 @@ import ( "os" "path/filepath" "regexp" - "strings" "testing" - "helm.sh/helm/internal/test/ensure" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/repo/repotest" ) func TestPullCmd(t *testing.T) { - defer resetEnv()() - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*") if err != nil { t.Fatal(err) @@ -47,7 +40,7 @@ func TestPullCmd(t *testing.T) { // all flags will get "-d outdir" appended. tests := []struct { name string - args []string + args string wantError bool failExpect string expectFile string @@ -56,47 +49,47 @@ func TestPullCmd(t *testing.T) { }{ { name: "Basic chart fetch", - args: []string{"test/signtest"}, + args: "test/signtest", expectFile: "./signtest-0.1.0.tgz", }, { name: "Chart fetch with version", - args: []string{"test/signtest --version=0.1.0"}, + args: "test/signtest --version=0.1.0", expectFile: "./signtest-0.1.0.tgz", }, { name: "Fail chart fetch with non-existent version", - args: []string{"test/signtest --version=99.1.0"}, + args: "test/signtest --version=99.1.0", wantError: true, failExpect: "no such chart", }, { name: "Fail fetching non-existent chart", - args: []string{"test/nosuchthing"}, + args: "test/nosuchthing", failExpect: "Failed to fetch", wantError: true, }, { name: "Fetch and verify", - args: []string{"test/signtest --verify --keyring testdata/helm-test-key.pub"}, + args: "test/signtest --verify --keyring testdata/helm-test-key.pub", expectFile: "./signtest-0.1.0.tgz", expectVerify: true, }, { name: "Fetch and fail verify", - args: []string{"test/reqtest --verify --keyring testdata/helm-test-key.pub"}, + args: "test/reqtest --verify --keyring testdata/helm-test-key.pub", failExpect: "Failed to fetch provenance", wantError: true, }, { name: "Fetch and untar", - args: []string{"test/signtest --untar --untardir signtest"}, + args: "test/signtest --untar --untardir signtest", expectFile: "./signtest", expectDir: true, }, { name: "Fetch, verify, untar", - args: []string{"test/signtest --verify --keyring=testdata/helm-test-key.pub --untar --untardir signtest"}, + args: "test/signtest --verify --keyring=testdata/helm-test-key.pub --untar --untardir signtest", expectFile: "./signtest", expectDir: true, expectVerify: true, @@ -104,59 +97,62 @@ func TestPullCmd(t *testing.T) { { name: "Chart fetch using repo URL", expectFile: "./signtest-0.1.0.tgz", - args: []string{"signtest --repo", srv.URL()}, + args: "signtest --repo " + srv.URL(), }, { name: "Fail fetching non-existent chart on repo URL", - args: []string{"someChart --repo", srv.URL()}, + args: "someChart --repo " + srv.URL(), failExpect: "Failed to fetch chart", wantError: true, }, { name: "Specific version chart fetch using repo URL", expectFile: "./signtest-0.1.0.tgz", - args: []string{"signtest --version=0.1.0 --repo", srv.URL()}, + args: "signtest --version=0.1.0 --repo " + srv.URL(), }, { name: "Specific version chart fetch using repo URL", - args: []string{"signtest --version=0.2.0 --repo", srv.URL()}, + args: "signtest --version=0.2.0 --repo " + srv.URL(), failExpect: "Failed to fetch chart version", wantError: true, }, } for _, tt := range tests { - outdir := filepath.Join(helmpath.DataPath(), "testout") - os.RemoveAll(outdir) - os.Mkdir(outdir, 0755) - - cmd := strings.Join(append(tt.args, "-d", "'"+outdir+"'"), " ") - _, out, err := executeActionCommand("fetch " + cmd) - if err != nil { - if tt.wantError { - continue + t.Run(tt.name, func(t *testing.T) { + outdir := srv.Root() + cmd := fmt.Sprintf("fetch %s -d '%s' --repository-config %s --repository-cache %s ", + tt.args, + outdir, + filepath.Join(outdir, "repositories.yaml"), + outdir, + ) + _, out, err := executeActionCommand(cmd) + if err != nil { + if tt.wantError { + return + } + t.Fatalf("%q reported error: %s", tt.name, err) } - t.Errorf("%q reported error: %s", tt.name, err) - continue - } - if tt.expectVerify { - pointerAddressPattern := "0[xX][A-Fa-f0-9]+" - sha256Pattern := "[A-Fa-f0-9]{64}" - verificationRegex := regexp.MustCompile( - fmt.Sprintf("Verification: &{%s sha256:%s signtest-0.1.0.tgz}\n", pointerAddressPattern, sha256Pattern)) - if !verificationRegex.MatchString(out) { - t.Errorf("%q: expected match for regex %s, got %s", tt.name, verificationRegex, out) + if tt.expectVerify { + pointerAddressPattern := "0[xX][A-Fa-f0-9]+" + sha256Pattern := "[A-Fa-f0-9]{64}" + verificationRegex := regexp.MustCompile( + fmt.Sprintf("Verification: &{%s sha256:%s signtest-0.1.0.tgz}\n", pointerAddressPattern, sha256Pattern)) + if !verificationRegex.MatchString(out) { + t.Errorf("%q: expected match for regex %s, got %s", tt.name, verificationRegex, out) + } } - } - ef := filepath.Join(outdir, tt.expectFile) - fi, err := os.Stat(ef) - if err != nil { - t.Errorf("%q: expected a file at %s. %s", tt.name, ef, err) - } - if fi.IsDir() != tt.expectDir { - t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir) - } + ef := filepath.Join(outdir, tt.expectFile) + fi, err := os.Stat(ef) + if err != nil { + t.Errorf("%q: expected a file at %s. %s", tt.name, ef, err) + } + if fi.IsDir() != tt.expectDir { + t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir) + } + }) } } diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 5168c9039..53432264f 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -19,13 +19,15 @@ package main import ( "fmt" "io" + "io/ioutil" + "os" "github.com/pkg/errors" "github.com/spf13/cobra" + "gopkg.in/yaml.v2" "helm.sh/helm/cmd/helm/require" "helm.sh/helm/pkg/getter" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/repo" ) @@ -34,11 +36,14 @@ type repoAddOptions struct { url string username string password string - noupdate bool + noUpdate bool certFile string keyFile string caFile string + + repoFile string + repoCache string } func newRepoAddCmd(out io.Writer) *cobra.Command { @@ -51,6 +56,8 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { o.name = args[0] o.url = args[1] + o.repoFile = settings.RepositoryConfig + o.repoCache = settings.RepositoryCache return o.run(out) }, @@ -59,7 +66,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { f := cmd.Flags() f.StringVar(&o.username, "username", "", "chart repository username") f.StringVar(&o.password, "password", "", "chart repository password") - f.BoolVar(&o.noupdate, "no-update", false, "raise error if repo is already registered") + f.BoolVar(&o.noUpdate, "no-update", false, "raise error if repo is already registered") f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file") f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file") f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle") @@ -68,31 +75,28 @@ func newRepoAddCmd(out io.Writer) *cobra.Command { } func (o *repoAddOptions) run(out io.Writer) error { - if err := addRepository(o.name, o.url, o.username, o.password, o.certFile, o.keyFile, o.caFile, o.noupdate); err != nil { + b, err := ioutil.ReadFile(o.repoFile) + if err != nil && !os.IsNotExist(err) { return err } - fmt.Fprintf(out, "%q has been added to your repositories\n", o.name) - return nil -} -func addRepository(name, url, username, password string, certFile, keyFile, caFile string, noUpdate bool) error { - f, err := repo.LoadFile(helmpath.RepositoryFile()) - if err != nil { + var f repo.File + if err := yaml.Unmarshal(b, &f); err != nil { return err } - if noUpdate && f.Has(name) { - return errors.Errorf("repository name (%s) already exists, please specify a different name", name) + if o.noUpdate && f.Has(o.name) { + return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name) } c := repo.Entry{ - Name: name, - URL: url, - Username: username, - Password: password, - CertFile: certFile, - KeyFile: keyFile, - CAFile: caFile, + Name: o.name, + URL: o.url, + Username: o.username, + Password: o.password, + CertFile: o.certFile, + KeyFile: o.keyFile, + CAFile: o.caFile, } r, err := repo.NewChartRepository(&c, getter.All(settings)) @@ -100,11 +104,15 @@ func addRepository(name, url, username, password string, certFile, keyFile, caFi return err } - if err := r.DownloadIndexFile(); err != nil { - return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url) + if _, err := r.DownloadIndexFile(); err != nil { + return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", o.url) } f.Update(&c) - return f.WriteFile(helmpath.RepositoryFile(), 0644) + if err := f.WriteFile(o.repoFile, 0644); err != nil { + return err + } + fmt.Fprintf(out, "%q has been added to your repositories\n", o.name) + return nil } diff --git a/cmd/helm/repo_add_test.go b/cmd/helm/repo_add_test.go index 32ad7e1bc..217a8124c 100644 --- a/cmd/helm/repo_add_test.go +++ b/cmd/helm/repo_add_test.go @@ -18,47 +18,27 @@ package main import ( "fmt" - "os" + "io/ioutil" + "path/filepath" "testing" "helm.sh/helm/internal/test/ensure" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo/repotest" ) func TestRepoAddCmd(t *testing.T) { - defer resetEnv()() - - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - srv, err := repotest.NewTempServer("testdata/testserver/*.*") if err != nil { t.Fatal(err) } defer srv.Stop() - repoFile := helmpath.RepositoryFile() - if _, err := os.Stat(repoFile); err != nil { - rf := repo.NewFile() - rf.Add(&repo.Entry{ - Name: "charts", - URL: "http://example.com/foo", - }) - if err := rf.WriteFile(repoFile, 0644); err != nil { - t.Fatal(err) - } - } - if r, err := repo.LoadFile(repoFile); err == repo.ErrRepoOutOfDate { - if err := r.WriteFile(repoFile, 0644); err != nil { - t.Fatal(err) - } - } + repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml") tests := []cmdTestCase{{ name: "add a repository", - cmd: fmt.Sprintf("repo add test-name %s", srv.URL()), + cmd: fmt.Sprintf("repo add test-name %s --repository-config %s", srv.URL(), repoFile), golden: "output/repo-add.txt", }} @@ -66,54 +46,43 @@ func TestRepoAddCmd(t *testing.T) { } func TestRepoAdd(t *testing.T) { - defer resetEnv()() - - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - ts, err := repotest.NewTempServer("testdata/testserver/*.*") if err != nil { t.Fatal(err) } defer ts.Stop() - repoFile := helmpath.RepositoryFile() - if _, err := os.Stat(repoFile); err != nil { - rf := repo.NewFile() - rf.Add(&repo.Entry{ - Name: "charts", - URL: "http://example.com/foo", - }) - if err := rf.WriteFile(repoFile, 0644); err != nil { - t.Fatal(err) - } - } - if r, err := repo.LoadFile(repoFile); err == repo.ErrRepoOutOfDate { - if err := r.WriteFile(repoFile, 0644); err != nil { - t.Fatal(err) - } - } + repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml") const testRepoName = "test-name" - if err := addRepository(testRepoName, ts.URL(), "", "", "", "", "", true); err != nil { + o := &repoAddOptions{ + name: testRepoName, + url: ts.URL(), + noUpdate: true, + repoFile: repoFile, + } + + if err := o.run(ioutil.Discard); err != nil { t.Error(err) } - f, err := repo.LoadFile(helmpath.RepositoryFile()) + f, err := repo.LoadFile(repoFile) if err != nil { - t.Error(err) + t.Fatal(err) } if !f.Has(testRepoName) { - t.Errorf("%s was not successfully inserted into %s", testRepoName, helmpath.RepositoryFile()) + t.Errorf("%s was not successfully inserted into %s", testRepoName, repoFile) } - if err := addRepository(testRepoName, ts.URL(), "", "", "", "", "", false); err != nil { + o.noUpdate = false + + if err := o.run(ioutil.Discard); err != nil { t.Errorf("Repository was not updated: %s", err) } - if err := addRepository(testRepoName, ts.URL(), "", "", "", "", "", false); err != nil { + if err := o.run(ioutil.Discard); err != nil { t.Errorf("Duplicate repository name was added") } } diff --git a/cmd/helm/repo_index.go b/cmd/helm/repo_index.go index 677f532b1..03cf002e9 100644 --- a/cmd/helm/repo_index.go +++ b/cmd/helm/repo_index.go @@ -87,7 +87,7 @@ func index(dir, url, mergeTo string) error { var i2 *repo.IndexFile if _, err := os.Stat(mergeTo); os.IsNotExist(err) { i2 = repo.NewIndexFile() - i2.WriteFile(mergeTo, 0755) + i2.WriteFile(mergeTo, 0644) } else { i2, err = repo.LoadIndexFile(mergeTo) if err != nil { @@ -97,5 +97,5 @@ func index(dir, url, mergeTo string) error { i.Merge(i2) } i.SortEntries() - return i.WriteFile(out, 0755) + return i.WriteFile(out, 0644) } diff --git a/cmd/helm/repo_list.go b/cmd/helm/repo_list.go index cea1733d3..3228dbb1e 100644 --- a/cmd/helm/repo_list.go +++ b/cmd/helm/repo_list.go @@ -25,7 +25,6 @@ import ( "github.com/spf13/cobra" "helm.sh/helm/cmd/helm/require" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/repo" ) @@ -35,7 +34,7 @@ func newRepoListCmd(out io.Writer) *cobra.Command { Short: "list chart repositories", Args: require.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - f, err := repo.LoadFile(helmpath.RepositoryFile()) + f, err := repo.LoadFile(settings.RepositoryConfig) if err != nil { return err } diff --git a/cmd/helm/repo_remove.go b/cmd/helm/repo_remove.go index 5600ab565..006908cd0 100644 --- a/cmd/helm/repo_remove.go +++ b/cmd/helm/repo_remove.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "os" + "path/filepath" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -29,49 +30,57 @@ import ( "helm.sh/helm/pkg/repo" ) +type repoRemoveOptions struct { + name string + repoFile string + repoCache string +} + func newRepoRemoveCmd(out io.Writer) *cobra.Command { + o := &repoRemoveOptions{} cmd := &cobra.Command{ Use: "remove [NAME]", Aliases: []string{"rm"}, Short: "remove a chart repository", Args: require.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return removeRepoLine(out, args[0]) + o.repoFile = settings.RepositoryConfig + o.repoCache = settings.RepositoryCache + o.name = args[0] + return o.run(out) }, } return cmd } -func removeRepoLine(out io.Writer, name string) error { - repoFile := helmpath.RepositoryFile() - r, err := repo.LoadFile(repoFile) +func (o *repoRemoveOptions) run(out io.Writer) error { + r, err := repo.LoadFile(o.repoFile) if err != nil { return err } - if !r.Remove(name) { - return errors.Errorf("no repo named %q found", name) + if !r.Remove(o.name) { + return errors.Errorf("no repo named %q found", o.name) } - if err := r.WriteFile(repoFile, 0644); err != nil { + if err := r.WriteFile(o.repoFile, 0644); err != nil { return err } - if err := removeRepoCache(name); err != nil { + if err := removeRepoCache(o.repoCache, o.name); err != nil { return err } - fmt.Fprintf(out, "%q has been removed from your repositories\n", name) - + fmt.Fprintf(out, "%q has been removed from your repositories\n", o.name) return nil } -func removeRepoCache(name string) error { - if _, err := os.Stat(helmpath.CacheIndex(name)); err == nil { - err = os.Remove(helmpath.CacheIndex(name)) - if err != nil { - return err - } +func removeRepoCache(root, name string) error { + idx := filepath.Join(root, helmpath.CacheIndexFile(name)) + if _, err := os.Stat(idx); os.IsNotExist(err) { + return nil + } else if err != nil { + return errors.Wrapf(err, "can't remove index file %s", idx) } - return nil + return os.Remove(idx) } diff --git a/cmd/helm/repo_remove_test.go b/cmd/helm/repo_remove_test.go index 451ef326c..5e8ddd209 100644 --- a/cmd/helm/repo_remove_test.go +++ b/cmd/helm/repo_remove_test.go @@ -19,6 +19,7 @@ package main import ( "bytes" "os" + "path/filepath" "strings" "testing" @@ -29,60 +30,57 @@ import ( ) func TestRepoRemove(t *testing.T) { - defer resetEnv()() - - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - ts, err := repotest.NewTempServer("testdata/testserver/*.*") if err != nil { t.Fatal(err) } defer ts.Stop() - repoFile := helmpath.RepositoryFile() - if _, err := os.Stat(repoFile); err != nil { - rf := repo.NewFile() - rf.Add(&repo.Entry{ - Name: "charts", - URL: "http://example.com/foo", - }) - if err := rf.WriteFile(repoFile, 0644); err != nil { - t.Fatal(err) - } - } - if r, err := repo.LoadFile(repoFile); err == repo.ErrRepoOutOfDate { - if err := r.WriteFile(repoFile, 0644); err != nil { - t.Fatal(err) - } - } + rootDir := ensure.TempDir(t) + repoFile := filepath.Join(rootDir, "repositories.yaml") const testRepoName = "test-name" b := bytes.NewBuffer(nil) - if err := removeRepoLine(b, testRepoName); err == nil { + + rmOpts := repoRemoveOptions{ + name: testRepoName, + repoFile: repoFile, + repoCache: rootDir, + } + + if err := rmOpts.run(os.Stderr); err == nil { t.Errorf("Expected error removing %s, but did not get one.", testRepoName) } - if err := addRepository(testRepoName, ts.URL(), "", "", "", "", "", true); err != nil { + o := &repoAddOptions{ + name: testRepoName, + url: ts.URL(), + repoFile: repoFile, + } + + if err := o.run(os.Stderr); err != nil { t.Error(err) } - mf, _ := os.Create(helmpath.CacheIndex(testRepoName)) + idx := filepath.Join(rootDir, helmpath.CacheIndexFile(testRepoName)) + + mf, _ := os.Create(idx) mf.Close() b.Reset() - if err := removeRepoLine(b, testRepoName); err != nil { + + if err := rmOpts.run(b); err != nil { t.Errorf("Error removing %s from repositories", testRepoName) } if !strings.Contains(b.String(), "has been removed") { t.Errorf("Unexpected output: %s", b.String()) } - if _, err := os.Stat(helmpath.CacheIndex(testRepoName)); err == nil { + if _, err := os.Stat(idx); err == nil { t.Errorf("Error cache file was not removed for repository %s", testRepoName) } - f, err := repo.LoadFile(helmpath.RepositoryFile()) + f, err := repo.LoadFile(repoFile) if err != nil { t.Error(err) } diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go index e3d69ffce..b36681e93 100644 --- a/cmd/helm/repo_update.go +++ b/cmd/helm/repo_update.go @@ -26,7 +26,6 @@ import ( "helm.sh/helm/cmd/helm/require" "helm.sh/helm/pkg/getter" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/repo" ) @@ -38,7 +37,8 @@ Information is cached locally, where it is used by commands like 'helm search'. var errNoRepositories = errors.New("no repositories found. You must add one before updating") type repoUpdateOptions struct { - update func([]*repo.ChartRepository, io.Writer) + update func([]*repo.ChartRepository, io.Writer) + repoFile string } func newRepoUpdateCmd(out io.Writer) *cobra.Command { @@ -58,7 +58,7 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command { } func (o *repoUpdateOptions) run(out io.Writer) error { - f, err := repo.LoadFile(helmpath.RepositoryFile()) + f, err := repo.LoadFile(o.repoFile) if err != nil { return err } @@ -86,7 +86,7 @@ func updateCharts(repos []*repo.ChartRepository, out io.Writer) { wg.Add(1) go func(re *repo.ChartRepository) { defer wg.Done() - if err := re.DownloadIndexFile(); err != nil { + if _, err := re.DownloadIndexFile(); err != nil { fmt.Fprintf(out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", re.Config.Name, re.Config.URL, err) } else { fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", re.Config.Name) diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index bfe490305..da147a15c 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -19,12 +19,9 @@ import ( "bytes" "fmt" "io" - "os" "strings" "testing" - "helm.sh/helm/pkg/helmpath" - "helm.sh/helm/internal/test/ensure" "helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/repo" @@ -32,29 +29,7 @@ import ( ) func TestUpdateCmd(t *testing.T) { - defer resetEnv()() - - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - - repoFile := helmpath.RepositoryFile() - if _, err := os.Stat(repoFile); err != nil { - rf := repo.NewFile() - rf.Add(&repo.Entry{ - Name: "charts", - URL: "http://example.com/foo", - }) - if err := rf.WriteFile(repoFile, 0644); err != nil { - t.Fatal(err) - } - } - if r, err := repo.LoadFile(repoFile); err == repo.ErrRepoOutOfDate { - if err := r.WriteFile(repoFile, 0644); err != nil { - t.Fatal(err) - } - } - - out := bytes.NewBuffer(nil) + var out bytes.Buffer // Instead of using the HTTP updater, we provide our own for this test. // The TestUpdateCharts test verifies the HTTP behavior independently. updater := func(repos []*repo.ChartRepository, out io.Writer) { @@ -63,9 +38,10 @@ func TestUpdateCmd(t *testing.T) { } } o := &repoUpdateOptions{ - update: updater, + update: updater, + repoFile: "testdata/repositories.yaml", } - if err := o.run(out); err != nil { + if err := o.run(&out); err != nil { t.Fatal(err) } @@ -76,9 +52,7 @@ func TestUpdateCmd(t *testing.T) { func TestUpdateCharts(t *testing.T) { defer resetEnv()() - - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() ts, err := repotest.NewTempServer("testdata/testserver/*.*") if err != nil { diff --git a/cmd/helm/root.go b/cmd/helm/root.go index 83117b87d..76162ed1d 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -154,7 +154,6 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string newUpgradeCmd(actionConfig, out), newCompletionCmd(out), - newInitCmd(out), newPluginCmd(out), newVersionCmd(out), diff --git a/cmd/helm/root_test.go b/cmd/helm/root_test.go index 5daca1eac..95802cf5d 100644 --- a/cmd/helm/root_test.go +++ b/cmd/helm/root_test.go @@ -59,8 +59,7 @@ func TestRootCmd(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() for k, v := range tt.envars { os.Setenv(k, v) diff --git a/cmd/helm/search_repo.go b/cmd/helm/search_repo.go index 432803e69..0399490aa 100644 --- a/cmd/helm/search_repo.go +++ b/cmd/helm/search_repo.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "path/filepath" "strings" "github.com/Masterminds/semver" @@ -43,10 +44,12 @@ Repositories are managed with 'helm repo' commands. const searchMaxScore = 25 type searchRepoOptions struct { - versions bool - regexp bool - version string - maxColWidth uint + versions bool + regexp bool + version string + maxColWidth uint + repoFile string + repoCacheDir string } func newSearchRepoCmd(out io.Writer) *cobra.Command { @@ -57,6 +60,8 @@ func newSearchRepoCmd(out io.Writer) *cobra.Command { Short: "search repositories for a keyword in charts", Long: searchRepoDesc, RunE: func(cmd *cobra.Command, args []string) error { + o.repoFile = settings.RepositoryConfig + o.repoCacheDir = settings.RepositoryCache return o.run(out, args) }, } @@ -141,15 +146,15 @@ func (o *searchRepoOptions) formatSearchResults(res []*search.Result) string { func (o *searchRepoOptions) buildIndex(out io.Writer) (*search.Index, error) { // Load the repositories.yaml - rf, err := repo.LoadFile(helmpath.RepositoryFile()) + rf, err := repo.LoadFile(o.repoFile) if err != nil { - return nil, err + return nil, errors.Wrap(err, "loading repository config") } i := search.NewIndex() for _, re := range rf.Repositories { n := re.Name - f := helmpath.CacheIndex(n) + f := filepath.Join(o.repoCacheDir, helmpath.CacheIndexFile(n)) ind, err := repo.LoadIndexFile(f) if err != nil { // TODO should print to stderr diff --git a/cmd/helm/search_repo_test.go b/cmd/helm/search_repo_test.go index c6b9f8074..babab73e6 100644 --- a/cmd/helm/search_repo_test.go +++ b/cmd/helm/search_repo_test.go @@ -17,23 +17,17 @@ limitations under the License. package main import ( - "os" "testing" - - "helm.sh/helm/pkg/helmpath/xdg" ) func TestSearchRepositoriesCmd(t *testing.T) { - defer resetEnv()() - - os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome") - os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome") - os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome") + repoFile := "testdata/helmhome/helm/repositories.yaml" + repoCache := "testdata/helmhome/helm/repository" tests := []cmdTestCase{{ - name: "search for 'maria', expect one match", - cmd: "search repo maria", - golden: "output/search-single.txt", + name: "search for 'alpine', expect two matches", + cmd: "search repo alpine", + golden: "output/search-multiple.txt", }, { name: "search for 'alpine', expect two matches", cmd: "search repo alpine", @@ -71,5 +65,13 @@ func TestSearchRepositoriesCmd(t *testing.T) { cmd: "search repo alp[ --regexp", wantError: true, }} + + settings.Debug = true + defer func() { settings.Debug = false }() + + for i := range tests { + tests[i].cmd += " --repository-config " + repoFile + tests[i].cmd += " --repository-cache " + repoCache + } runTestCmd(t, tests) } diff --git a/cmd/helm/testdata/helmhome/helm/plugins/env/plugin.yaml b/cmd/helm/testdata/helmhome/helm/plugins/env/plugin.yaml index 8b78a91c7..52cb7a848 100644 --- a/cmd/helm/testdata/helmhome/helm/plugins/env/plugin.yaml +++ b/cmd/helm/testdata/helmhome/helm/plugins/env/plugin.yaml @@ -1,4 +1,4 @@ name: env usage: "env stuff" description: "show the env" -command: "echo $HELM_PATH_CONFIG" +command: "echo $HELM_PLUGIN_NAME" diff --git a/cmd/helm/testdata/helmhome/helm/plugins/fullenv/fullenv.sh b/cmd/helm/testdata/helmhome/helm/plugins/fullenv/fullenv.sh index b53cb4a41..501bac82c 100755 --- a/cmd/helm/testdata/helmhome/helm/plugins/fullenv/fullenv.sh +++ b/cmd/helm/testdata/helmhome/helm/plugins/fullenv/fullenv.sh @@ -2,9 +2,6 @@ echo $HELM_PLUGIN_NAME echo $HELM_PLUGIN_DIR echo $HELM_PLUGIN -echo $HELM_PATH_CACHE -echo $HELM_PATH_CONFIG -echo $HELM_PATH_DATA echo $HELM_PATH_REPOSITORY_FILE echo $HELM_PATH_REPOSITORY_CACHE echo $HELM_BIN diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 03de21f4d..d679c7c79 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -28,6 +28,7 @@ import ( "helm.sh/helm/pkg/action" "helm.sh/helm/pkg/chart/loader" "helm.sh/helm/pkg/cli/values" + "helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/storage/driver" ) @@ -73,7 +74,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { client.Version = ">0.0.0-0" } - vals, err := valueOpts.MergeValues(settings) + vals, err := valueOpts.MergeValues(getter.All(settings)) if err != nil { return err } diff --git a/internal/experimental/registry/client.go b/internal/experimental/registry/client.go index 8a668739a..8ec5ccd7b 100644 --- a/internal/experimental/registry/client.go +++ b/internal/experimental/registry/client.go @@ -21,7 +21,6 @@ import ( "fmt" "io" "io/ioutil" - "path/filepath" "sort" auth "github.com/deislabs/oras/pkg/auth/docker" @@ -60,7 +59,7 @@ func NewClient(opts ...ClientOption) (*Client, error) { } // set defaults if fields are missing if client.authorizer == nil { - credentialsFile := filepath.Join(helmpath.Registry(), CredentialsFileBasename) + credentialsFile := helmpath.CachePath("registry", CredentialsFileBasename) authClient, err := auth.NewClient(credentialsFile) if err != nil { return nil, err @@ -82,7 +81,7 @@ func NewClient(opts ...ClientOption) (*Client, error) { cache, err := NewCache( CacheOptDebug(client.debug), CacheOptWriter(client.out), - CacheOptRoot(filepath.Join(helmpath.Registry(), CacheRootDir)), + CacheOptRoot(helmpath.CachePath("registry", CacheRootDir)), ) if err != nil { return nil, err diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 679c5046e..622af4bfb 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -35,12 +35,14 @@ import ( // Resolver resolves dependencies from semantic version ranges to a particular version. type Resolver struct { chartpath string + cachepath string } // New creates a new resolver for a given chart and a given helm home. -func New(chartpath string) *Resolver { +func New(chartpath, cachepath string) *Resolver { return &Resolver{ chartpath: chartpath, + cachepath: cachepath, } } @@ -69,9 +71,11 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string return nil, errors.Wrapf(err, "dependency %q has an invalid version/constraint format", d.Name) } - repoIndex, err := repo.LoadIndexFile(helmpath.CacheIndex(repoNames[d.Name])) + idx := filepath.Join(r.cachepath, helmpath.CacheIndexFile(repoNames[d.Name])) + + repoIndex, err := repo.LoadIndexFile(idx) if err != nil { - return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')") + return nil, errors.Wrapf(err, "no cached repo found. (try 'helm repo update') %s", idx) } vs, ok := repoIndex.Entries[d.Name] diff --git a/internal/resolver/resolver_test.go b/internal/resolver/resolver_test.go index a31ac311a..38fa2a099 100644 --- a/internal/resolver/resolver_test.go +++ b/internal/resolver/resolver_test.go @@ -16,15 +16,12 @@ limitations under the License. package resolver import ( - "os" "testing" "helm.sh/helm/pkg/chart" - "helm.sh/helm/pkg/helmpath/xdg" ) func TestResolve(t *testing.T) { - os.Setenv(xdg.CacheHomeEnvVar, "testdata") tests := []struct { name string req []*chart.Dependency @@ -91,7 +88,7 @@ func TestResolve(t *testing.T) { } repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"} - r := New("testdata/chartpath") + r := New("testdata/chartpath", "testdata/repository") for _, tt := range tests { l, err := r.Resolve(tt.req, repoNames) if err != nil { diff --git a/internal/resolver/testdata/helm/repository/kubernetes-charts-index.yaml b/internal/resolver/testdata/repository/kubernetes-charts-index.yaml similarity index 100% rename from internal/resolver/testdata/helm/repository/kubernetes-charts-index.yaml rename to internal/resolver/testdata/repository/kubernetes-charts-index.yaml diff --git a/internal/test/ensure/ensure.go b/internal/test/ensure/ensure.go index 8abc6c406..ded2f1cca 100644 --- a/internal/test/ensure/ensure.go +++ b/internal/test/ensure/ensure.go @@ -21,55 +21,18 @@ import ( "os" "testing" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/helmpath/xdg" ) // HelmHome sets up a Helm Home in a temp dir. -func HelmHome(t *testing.T) { +func HelmHome(t *testing.T) func() { t.Helper() - cachePath := TempDir(t) - configPath := TempDir(t) - dataPath := TempDir(t) - os.Setenv(xdg.CacheHomeEnvVar, cachePath) - os.Setenv(xdg.ConfigHomeEnvVar, configPath) - os.Setenv(xdg.DataHomeEnvVar, dataPath) - HomeDirs(t) -} - -// HomeDirs creates a home directory like ensureHome, but without remote references. -func HomeDirs(t *testing.T) { - t.Helper() - for _, p := range []string{ - helmpath.CachePath(), - helmpath.ConfigPath(), - helmpath.DataPath(), - helmpath.RepositoryCache(), - helmpath.Plugins(), - helmpath.PluginCache(), - helmpath.Starters(), - } { - if err := os.MkdirAll(p, 0755); err != nil { - t.Fatal(err) - } - } -} - -// CleanHomeDirs removes the directories created by HomeDirs. -func CleanHomeDirs(t *testing.T) { - t.Helper() - for _, p := range []string{ - helmpath.CachePath(), - helmpath.ConfigPath(), - helmpath.DataPath(), - helmpath.RepositoryCache(), - helmpath.Plugins(), - helmpath.PluginCache(), - helmpath.Starters(), - } { - if err := os.RemoveAll(p); err != nil { - t.Log(err) - } + base := TempDir(t) + os.Setenv(xdg.CacheHomeEnvVar, base) + os.Setenv(xdg.ConfigHomeEnvVar, base) + os.Setenv(xdg.DataHomeEnvVar, base) + return func() { + os.RemoveAll(base) } } diff --git a/pkg/action/install.go b/pkg/action/install.go index e1ced1411..a185667de 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -37,7 +37,6 @@ import ( "helm.sh/helm/pkg/downloader" "helm.sh/helm/pkg/engine" "helm.sh/helm/pkg/getter" - "helm.sh/helm/pkg/helmpath" kubefake "helm.sh/helm/pkg/kube/fake" "helm.sh/helm/pkg/release" "helm.sh/helm/pkg/releaseutil" @@ -563,8 +562,8 @@ OUTER: // - if path is absolute or begins with '.', error out here // - URL // -// If 'verify' is true, this will attempt to also verify the chart. -func (c *ChartPathOptions) LocateChart(name string, settings cli.EnvSettings) (string, error) { +// If 'verify' was set on ChartPathOptions, this will attempt to also verify the chart. +func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (string, error) { name = strings.TrimSpace(name) version := strings.TrimSpace(c.Version) @@ -604,11 +603,11 @@ func (c *ChartPathOptions) LocateChart(name string, settings cli.EnvSettings) (s name = chartURL } - if _, err := os.Stat(helmpath.Archive()); os.IsNotExist(err) { - os.MkdirAll(helmpath.Archive(), 0744) + if err := os.MkdirAll(settings.RepositoryCache, 0755); err != nil { + return "", err } - filename, _, err := dl.DownloadTo(name, version, helmpath.Archive()) + filename, _, err := dl.DownloadTo(name, version, settings.RepositoryCache) if err == nil { lname, err := filepath.Abs(filename) if err != nil { diff --git a/pkg/action/package.go b/pkg/action/package.go index 5deb15a5f..63971ba8c 100644 --- a/pkg/action/package.go +++ b/pkg/action/package.go @@ -43,6 +43,9 @@ type Package struct { AppVersion string Destination string DependencyUpdate bool + + RepositoryConfig string + RepositoryCache string } // NewPackage creates a new Package object with the given configuration. @@ -131,7 +134,7 @@ func (p *Package) Clearsign(filename string) error { return err } - return ioutil.WriteFile(filename+".prov", []byte(sig), 0755) + return ioutil.WriteFile(filename+".prov", []byte(sig), 0644) } // promptUser implements provenance.PassphraseFetcher diff --git a/pkg/action/pull.go b/pkg/action/pull.go index 7304e994d..8a1dc535e 100644 --- a/pkg/action/pull.go +++ b/pkg/action/pull.go @@ -38,7 +38,7 @@ import ( type Pull struct { ChartPathOptions - Settings cli.EnvSettings // TODO: refactor this out of pkg/action + Settings *cli.EnvSettings // TODO: refactor this out of pkg/action Devel bool Untar bool @@ -64,6 +64,8 @@ func (p *Pull) Run(chartRef string) (string, error) { Options: []getter.Option{ getter.WithBasicAuth(p.Username, p.Password), }, + RepositoryConfig: p.Settings.RepositoryConfig, + RepositoryCache: p.Settings.RepositoryCache, } if p.Verify { diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index a534a67a1..ed4da83d4 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -42,17 +42,19 @@ const ( // IgnorefileName is the name of the Helm ignore file. IgnorefileName = ".helmignore" // IngressFileName is the name of the example ingress file. - IngressFileName = "ingress.yaml" + IngressFileName = TemplatesDir + sep + "ingress.yaml" // DeploymentName is the name of the example deployment file. - DeploymentName = "deployment.yaml" + DeploymentName = TemplatesDir + sep + "deployment.yaml" // ServiceName is the name of the example service file. - ServiceName = "service.yaml" + ServiceName = TemplatesDir + sep + "service.yaml" // NotesName is the name of the example NOTES.txt file. - NotesName = "NOTES.txt" + NotesName = TemplatesDir + sep + "NOTES.txt" // HelpersName is the name of the example NOTES.txt file. - HelpersName = "_helpers.tpl" + HelpersName = TemplatesDir + sep + "_helpers.tpl" ) +const sep = string(filepath.Separator) + const defaultChartfile = `apiVersion: v2 name: %s description: A Helm chart for Kubernetes @@ -345,12 +347,12 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error { schart.Templates = updatedTemplates b, err := yaml.Marshal(schart.Values) if err != nil { - return err + return errors.Wrap(err, "reading values file") } var m map[string]interface{} if err := yaml.Unmarshal(transform(string(b), schart.Name()), &m); err != nil { - return err + return errors.Wrap(err, "transforming values file") } schart.Values = m @@ -386,15 +388,6 @@ func Create(name, dir string) (string, error) { if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() { return cdir, errors.Errorf("file %s already exists and is not a directory", cdir) } - if err := os.MkdirAll(cdir, 0755); err != nil { - return cdir, err - } - - for _, d := range []string{TemplatesDir, ChartsDir} { - if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil { - return cdir, err - } - } files := []struct { path string @@ -417,27 +410,27 @@ func Create(name, dir string) (string, error) { }, { // ingress.yaml - path: filepath.Join(cdir, TemplatesDir, IngressFileName), + path: filepath.Join(cdir, IngressFileName), content: transform(defaultIngress, name), }, { // deployment.yaml - path: filepath.Join(cdir, TemplatesDir, DeploymentName), + path: filepath.Join(cdir, DeploymentName), content: transform(defaultDeployment, name), }, { // service.yaml - path: filepath.Join(cdir, TemplatesDir, ServiceName), + path: filepath.Join(cdir, ServiceName), content: transform(defaultService, name), }, { // NOTES.txt - path: filepath.Join(cdir, TemplatesDir, NotesName), + path: filepath.Join(cdir, NotesName), content: transform(defaultNotes, name), }, { // _helpers.tpl - path: filepath.Join(cdir, TemplatesDir, HelpersName), + path: filepath.Join(cdir, HelpersName), content: transform(defaultHelpers, name), }, } @@ -447,7 +440,7 @@ func Create(name, dir string) (string, error) { // File exists and is okay. Skip it. continue } - if err := ioutil.WriteFile(file.path, file.content, 0644); err != nil { + if err := writeFile(file.path, file.content); err != nil { return cdir, err } } @@ -459,3 +452,10 @@ func Create(name, dir string) (string, error) { func transform(src, replacement string) []byte { return []byte(strings.ReplaceAll(src, "", replacement)) } + +func writeFile(name string, content []byte) error { + if err := os.MkdirAll(filepath.Dir(name), 0755); err != nil { + return err + } + return ioutil.WriteFile(name, content, 0644) +} diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index 09ec7e663..d6e6a498f 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -49,30 +49,20 @@ func TestCreate(t *testing.T) { t.Errorf("Expected name to be 'foo', got %q", mychart.Name()) } - for _, d := range []string{TemplatesDir, ChartsDir} { - if fi, err := os.Stat(filepath.Join(dir, d)); err != nil { - t.Errorf("Expected %s dir: %s", d, err) - } else if !fi.IsDir() { - t.Errorf("Expected %s to be a directory.", d) - } - } - - for _, f := range []string{ChartfileName, ValuesfileName, IgnorefileName} { - if fi, err := os.Stat(filepath.Join(dir, f)); err != nil { - t.Errorf("Expected %s file: %s", f, err) - } else if fi.IsDir() { - t.Errorf("Expected %s to be a file.", f) - } - } - - for _, f := range []string{NotesName, DeploymentName, ServiceName, HelpersName} { - if fi, err := os.Stat(filepath.Join(dir, TemplatesDir, f)); err != nil { + for _, f := range []string{ + ChartfileName, + DeploymentName, + HelpersName, + IgnorefileName, + NotesName, + ServiceName, + TemplatesDir, + ValuesfileName, + } { + if _, err := os.Stat(filepath.Join(dir, f)); err != nil { t.Errorf("Expected %s file: %s", f, err) - } else if fi.IsDir() { - t.Errorf("Expected %s to be a file.", f) } } - } func TestCreateFrom(t *testing.T) { @@ -94,7 +84,6 @@ func TestCreateFrom(t *testing.T) { } dir := filepath.Join(tdir, "foo") - c := filepath.Join(tdir, cf.Name) mychart, err := loader.LoadDir(c) if err != nil { @@ -105,27 +94,13 @@ func TestCreateFrom(t *testing.T) { t.Errorf("Expected name to be 'foo', got %q", mychart.Name()) } - for _, d := range []string{TemplatesDir, ChartsDir} { - if fi, err := os.Stat(filepath.Join(dir, d)); err != nil { - t.Errorf("Expected %s dir: %s", d, err) - } else if !fi.IsDir() { - t.Errorf("Expected %s to be a directory.", d) - } - } - - for _, f := range []string{ChartfileName, ValuesfileName} { - if fi, err := os.Stat(filepath.Join(dir, f)); err != nil { - t.Errorf("Expected %s file: %s", f, err) - } else if fi.IsDir() { - t.Errorf("Expected %s to be a file.", f) - } - } - - for _, f := range []string{"placeholder.tpl"} { - if fi, err := os.Stat(filepath.Join(dir, TemplatesDir, f)); err != nil { + for _, f := range []string{ + ChartfileName, + ValuesfileName, + filepath.Join(TemplatesDir, "placeholder.tpl"), + } { + if _, err := os.Stat(filepath.Join(dir, f)); err != nil { t.Errorf("Expected %s file: %s", f, err) - } else if fi.IsDir() { - t.Errorf("Expected %s to be a file.", f) } } } diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index 4fbba511b..1ea99bbd7 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -20,7 +20,6 @@ import ( "archive/tar" "compress/gzip" "fmt" - "io/ioutil" "os" "path/filepath" @@ -52,13 +51,7 @@ func SaveDir(c *chart.Chart, dest string) error { if c.Values != nil { vf := filepath.Join(outdir, ValuesfileName) b, _ := yaml.Marshal(c.Values) - if err := ioutil.WriteFile(vf, b, 0644); err != nil { - return err - } - } - - for _, d := range []string{TemplatesDir, ChartsDir} { - if err := os.MkdirAll(filepath.Join(outdir, d), 0755); err != nil { + if err := writeFile(vf, b); err != nil { return err } } @@ -67,13 +60,7 @@ func SaveDir(c *chart.Chart, dest string) error { for _, o := range [][]*chart.File{c.Templates, c.Files} { for _, f := range o { n := filepath.Join(outdir, f.Name) - - d := filepath.Dir(n) - if err := os.MkdirAll(d, 0755); err != nil { - return err - } - - if err := ioutil.WriteFile(n, f.Data, 0644); err != nil { + if err := writeFile(n, f.Data); err != nil { return err } } @@ -84,7 +71,7 @@ func SaveDir(c *chart.Chart, dest string) error { for _, dep := range c.Dependencies() { // Here, we write each dependency as a tar file. if _, err := Save(dep, base); err != nil { - return err + return errors.Wrapf(err, "saving %s", dep.ChartFullPath()) } } return nil @@ -99,13 +86,6 @@ func SaveDir(c *chart.Chart, dest string) error { // // This returns the absolute path to the chart archive file. func Save(c *chart.Chart, outDir string) (string, error) { - // Create archive - if fi, err := os.Stat(outDir); err != nil { - return "", err - } else if !fi.IsDir() { - return "", errors.Errorf("location %s is not a directory", outDir) - } - if err := c.Validate(); err != nil { return "", errors.Wrap(err, "chart validation") } @@ -199,7 +179,7 @@ func writeToTar(out *tar.Writer, name string, body []byte) error { // TODO: Do we need to create dummy parent directory names if none exist? h := &tar.Header{ Name: name, - Mode: 0755, + Mode: 0644, Size: int64(len(body)), } if err := out.WriteHeader(h); err != nil { diff --git a/pkg/cli/environment.go b/pkg/cli/environment.go index 194ea3135..b6b0b114b 100644 --- a/pkg/cli/environment.go +++ b/pkg/cli/environment.go @@ -26,6 +26,8 @@ import ( "os" "github.com/spf13/pflag" + + "helm.sh/helm/pkg/helmpath" ) // EnvSettings describes all of the environment settings. @@ -38,6 +40,24 @@ type EnvSettings struct { KubeContext string // Debug indicates whether or not Helm is running in Debug mode. Debug bool + + // RegistryConfig is the path to the registry config file. + RegistryConfig string + // RepositoryConfig is the path to the repositories file. + RepositoryConfig string + // Repositoryache is the path to the repository cache directory. + RepositoryCache string + // PluginsDirectory is the path to the plugins directory. + PluginsDirectory string +} + +func New() *EnvSettings { + return &EnvSettings{ + PluginsDirectory: helmpath.DataPath("plugins"), + RegistryConfig: helmpath.ConfigPath("registry.json"), + RepositoryConfig: helmpath.ConfigPath("repositories.yaml"), + RepositoryCache: helmpath.CachePath("repository"), + } } // AddFlags binds flags to the given flagset. @@ -46,6 +66,10 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.KubeConfig, "kubeconfig", "", "path to the kubeconfig file") fs.StringVar(&s.KubeContext, "kube-context", "", "name of the kubeconfig context to use") fs.BoolVar(&s.Debug, "debug", false, "enable verbose output") + + fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file") + fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the repositories config file") + fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the repositories config file") } // Init sets values from the environment. @@ -57,8 +81,10 @@ func (s *EnvSettings) Init(fs *pflag.FlagSet) { // envMap maps flag names to envvars var envMap = map[string]string{ - "debug": "HELM_DEBUG", - "namespace": "HELM_NAMESPACE", + "debug": "HELM_DEBUG", + "namespace": "HELM_NAMESPACE", + "registry-config": "HELM_REGISTRY_CONFIG", + "repository-config": "HELM_REPOSITORY_CONFIG", } func setFlagFromEnv(name, envar string, fs *pflag.FlagSet) { diff --git a/pkg/cli/values/options.go b/pkg/cli/values/options.go index 86e83ab76..e9f7a3ca3 100644 --- a/pkg/cli/values/options.go +++ b/pkg/cli/values/options.go @@ -25,7 +25,6 @@ import ( "github.com/pkg/errors" "sigs.k8s.io/yaml" - "helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/strvals" ) @@ -38,14 +37,14 @@ type Options struct { // MergeValues merges values from files specified via -f/--values and // directly via --set or --set-string, marshaling them to YAML -func (opts *Options) MergeValues(settings cli.EnvSettings) (map[string]interface{}, error) { +func (opts *Options) MergeValues(p getter.Providers) (map[string]interface{}, error) { base := map[string]interface{}{} // User specified a values files via -f/--values for _, filePath := range opts.ValueFiles { currentMap := map[string]interface{}{} - bytes, err := readFile(filePath, settings) + bytes, err := readFile(filePath, p) if err != nil { return nil, err } @@ -94,24 +93,17 @@ func mergeMaps(a, b map[string]interface{}) map[string]interface{} { } // readFile load a file from stdin, the local directory, or a remote file with a url. -func readFile(filePath string, settings cli.EnvSettings) ([]byte, error) { +func readFile(filePath string, p getter.Providers) ([]byte, error) { if strings.TrimSpace(filePath) == "-" { return ioutil.ReadAll(os.Stdin) } u, _ := url.Parse(filePath) - p := getter.All(settings) // FIXME: maybe someone handle other protocols like ftp. - getterConstructor, err := p.ByScheme(u.Scheme) - + g, err := p.ByScheme(u.Scheme) if err != nil { return ioutil.ReadFile(filePath) } - - getter, err := getterConstructor(getter.WithURL(filePath)) - if err != nil { - return []byte{}, err - } - data, err := getter.Get(filePath) + data, err := g.Get(filePath, getter.WithURL(filePath)) return data.Bytes(), err } diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 0135cb608..cdb146e79 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -67,7 +67,9 @@ type ChartDownloader struct { // Getter collection for the operation Getters getter.Providers // Options provide parameters to be passed along to the Getter being initialized. - Options []getter.Option + Options []getter.Option + RepositoryConfig string + RepositoryCache string } // DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file. @@ -87,17 +89,12 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven return "", nil, err } - constructor, err := c.Getters.ByScheme(u.Scheme) + g, err := c.Getters.ByScheme(u.Scheme) if err != nil { return "", nil, err } - g, err := constructor(c.Options...) - if err != nil { - return "", nil, err - } - - data, err := g.Get(u.String()) + data, err := g.Get(u.String(), c.Options...) if err != nil { return "", nil, err } @@ -157,7 +154,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er } c.Options = append(c.Options, getter.WithURL(ref)) - rf, err := repo.LoadFile(helmpath.RepositoryFile()) + rf, err := repo.LoadFile(c.RepositoryConfig) if err != nil { return u, err } @@ -218,7 +215,8 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er } // Next, we need to load the index, and actually look up the chart. - i, err := repo.LoadIndexFile(helmpath.CacheIndex(r.Config.Name)) + idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name)) + i, err := repo.LoadIndexFile(idxFile) if err != nil { return u, errors.Wrap(err, "no cached repo found. (try 'helm repo update')") } @@ -337,7 +335,8 @@ func (c *ChartDownloader) scanReposForURL(u string, rf *repo.File) (*repo.Entry, return nil, err } - i, err := repo.LoadIndexFile(helmpath.CacheIndex(r.Config.Name)) + idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name)) + i, err := repo.LoadIndexFile(idxFile) if err != nil { return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')") } diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index e0e737b3a..26d19343f 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -24,16 +24,16 @@ import ( "helm.sh/helm/internal/test/ensure" "helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/getter" - "helm.sh/helm/pkg/helmpath" - "helm.sh/helm/pkg/helmpath/xdg" "helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo/repotest" ) -func TestResolveChartRef(t *testing.T) { - os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome") - os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome") +const ( + repoConfig = "testdata/repositories.yaml" + repoCache = "testdata/repository" +) +func TestResolveChartRef(t *testing.T) { tests := []struct { name, ref, expect, version string fail bool @@ -56,8 +56,13 @@ func TestResolveChartRef(t *testing.T) { } c := ChartDownloader{ - Out: os.Stderr, - Getters: getter.All(cli.EnvSettings{}), + Out: os.Stderr, + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + Getters: getter.All(&cli.EnvSettings{ + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + }), } for _, tt := range tests { @@ -105,16 +110,11 @@ func TestIsTar(t *testing.T) { } func TestDownloadTo(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - dest := helmpath.CachePath() - // Set up a fake repo with basic auth enabled - srv := repotest.NewServer(helmpath.CachePath()) + srv, err := repotest.NewTempServer("testdata/*.tgz*") srv.Stop() - if _, err := srv.CopyCharts("testdata/*.tgz*"); err != nil { - t.Error(err) - return + if err != nil { + t.Fatal(err) } srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { username, password, ok := r.BasicAuth() @@ -133,15 +133,21 @@ func TestDownloadTo(t *testing.T) { } c := ChartDownloader{ - Out: os.Stderr, - Verify: VerifyAlways, - Keyring: "testdata/helm-test-key.pub", - Getters: getter.All(cli.EnvSettings{}), + Out: os.Stderr, + Verify: VerifyAlways, + Keyring: "testdata/helm-test-key.pub", + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + Getters: getter.All(&cli.EnvSettings{ + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + }), Options: []getter.Option{ getter.WithBasicAuth("username", "password"), }, } cname := "/signtest-0.1.0.tgz" + dest := srv.Root() where, v, err := c.DownloadTo(srv.URL()+cname, "", dest) if err != nil { t.Fatal(err) @@ -161,10 +167,9 @@ func TestDownloadTo(t *testing.T) { } func TestDownloadTo_VerifyLater(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() - dest := helmpath.CachePath() + dest := ensure.TempDir(t) // Set up a fake repo srv, err := repotest.NewTempServer("testdata/*.tgz*") @@ -177,9 +182,14 @@ func TestDownloadTo_VerifyLater(t *testing.T) { } c := ChartDownloader{ - Out: os.Stderr, - Verify: VerifyLater, - Getters: getter.All(cli.EnvSettings{}), + Out: os.Stderr, + Verify: VerifyLater, + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + Getters: getter.All(&cli.EnvSettings{ + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + }), } cname := "/signtest-0.1.0.tgz" where, _, err := c.DownloadTo(srv.URL()+cname, "", dest) @@ -200,17 +210,19 @@ func TestDownloadTo_VerifyLater(t *testing.T) { } func TestScanReposForURL(t *testing.T) { - os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome") - os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome") - c := ChartDownloader{ - Out: os.Stderr, - Verify: VerifyLater, - Getters: getter.All(cli.EnvSettings{}), + Out: os.Stderr, + Verify: VerifyLater, + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + Getters: getter.All(&cli.EnvSettings{ + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, + }), } u := "http://example.com/alpine-0.2.0.tgz" - rf, err := repo.LoadFile(helmpath.RepositoryFile()) + rf, err := repo.LoadFile(repoConfig) if err != nil { t.Fatal(err) } diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 13fdc4932..4bfa1e670 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -55,7 +55,9 @@ type Manager struct { // SkipUpdate indicates that the repository should not be updated first. SkipUpdate bool // Getter collection for the operation - Getters []getter.Provider + Getters []getter.Provider + RepositoryConfig string + RepositoryCache string } // Build rebuilds a local charts directory from a lockfile. @@ -94,11 +96,7 @@ func (m *Manager) Build() error { } // Now we need to fetch every package here into charts/ - if err := m.downloadAll(lock.Dependencies); err != nil { - return err - } - - return nil + return m.downloadAll(lock.Dependencies) } // Update updates a local charts directory. @@ -168,7 +166,7 @@ func (m *Manager) loadChartDir() (*chart.Chart, error) { // // This returns a lock file, which has all of the dependencies normalized to a specific version. func (m *Manager) resolve(req []*chart.Dependency, repoNames map[string]string) (*chart.Lock, error) { - res := resolver.New(m.ChartPath) + res := resolver.New(m.ChartPath, m.RepositoryCache) return res.Resolve(req, repoNames) } @@ -229,10 +227,12 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error { } dl := ChartDownloader{ - Out: m.Out, - Verify: m.Verify, - Keyring: m.Keyring, - Getters: m.Getters, + Out: m.Out, + Verify: m.Verify, + Keyring: m.Keyring, + RepositoryConfig: m.RepositoryConfig, + RepositoryCache: m.RepositoryCache, + Getters: m.Getters, Options: []getter.Option{ getter.WithBasicAuth(username, password), }, @@ -311,7 +311,7 @@ func (m *Manager) safeDeleteDep(name, dir string) error { // hasAllRepos ensures that all of the referenced deps are in the local repo cache. func (m *Manager) hasAllRepos(deps []*chart.Dependency) error { - rf, err := repo.LoadFile(helmpath.RepositoryFile()) + rf, err := repo.LoadFile(m.RepositoryConfig) if err != nil { return err } @@ -345,8 +345,11 @@ Loop: // getRepoNames returns the repo names of the referenced deps which can be used to fetch the cahced index file. func (m *Manager) getRepoNames(deps []*chart.Dependency) (map[string]string, error) { - rf, err := repo.LoadFile(helmpath.RepositoryFile()) + rf, err := repo.LoadFile(m.RepositoryConfig) if err != nil { + if os.IsNotExist(err) { + return make(map[string]string), nil + } return nil, err } repos := rf.Repositories @@ -412,7 +415,7 @@ repository, use "https://charts.example.com/" or "@example" instead of // UpdateRepositories updates all of the local repos to the latest. func (m *Manager) UpdateRepositories() error { - rf, err := repo.LoadFile(helmpath.RepositoryFile()) + rf, err := repo.LoadFile(m.RepositoryConfig) if err != nil { return err } @@ -427,8 +430,7 @@ func (m *Manager) UpdateRepositories() error { } func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { - out := m.Out - fmt.Fprintln(out, "Hang tight while we grab the latest from your chart repositories...") + fmt.Fprintln(m.Out, "Hang tight while we grab the latest from your chart repositories...") var wg sync.WaitGroup for _, c := range repos { r, err := repo.NewChartRepository(c, m.Getters) @@ -437,16 +439,16 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { } wg.Add(1) go func(r *repo.ChartRepository) { - if err := r.DownloadIndexFile(); err != nil { - fmt.Fprintf(out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", r.Config.Name, r.Config.URL, err) + if _, err := r.DownloadIndexFile(); err != nil { + fmt.Fprintf(m.Out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", r.Config.Name, r.Config.URL, err) } else { - fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", r.Config.Name) + fmt.Fprintf(m.Out, "...Successfully got an update from the %q chart repository\n", r.Config.Name) } wg.Done() }(r) } wg.Wait() - fmt.Fprintln(out, "Update Complete. ⎈Happy Helming!⎈") + fmt.Fprintln(m.Out, "Update Complete. ⎈Happy Helming!⎈") return nil } @@ -549,17 +551,17 @@ func normalizeURL(baseURL, urlOrPath string) (string, error) { // The key is the local name (which is only present in the repositories.yaml). func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, error) { indices := map[string]*repo.ChartRepository{} - repoyaml := helmpath.RepositoryFile() // Load repositories.yaml file - rf, err := repo.LoadFile(repoyaml) + rf, err := repo.LoadFile(m.RepositoryConfig) if err != nil { - return indices, errors.Wrapf(err, "failed to load %s", repoyaml) + return indices, errors.Wrapf(err, "failed to load %s", m.RepositoryConfig) } for _, re := range rf.Repositories { lname := re.Name - index, err := repo.LoadIndexFile(helmpath.CacheIndex(lname)) + idxFile := filepath.Join(m.RepositoryCache, helmpath.CacheIndexFile(lname)) + index, err := repo.LoadIndexFile(idxFile) if err != nil { return indices, err } diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index d015e5b3e..ae1f2325a 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -17,12 +17,10 @@ package downloader import ( "bytes" - "os" "reflect" "testing" "helm.sh/helm/pkg/chart" - "helm.sh/helm/pkg/helmpath/xdg" ) func TestVersionEquals(t *testing.T) { @@ -64,12 +62,11 @@ func TestNormalizeURL(t *testing.T) { } func TestFindChartURL(t *testing.T) { - os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome") - os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome") - - b := bytes.NewBuffer(nil) + var b bytes.Buffer m := &Manager{ - Out: b, + Out: &b, + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, } repos, err := m.loadChartRepositories() if err != nil { @@ -96,12 +93,11 @@ func TestFindChartURL(t *testing.T) { } func TestGetRepoNames(t *testing.T) { - os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome") - os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome") - b := bytes.NewBuffer(nil) m := &Manager{ - Out: b, + Out: b, + RepositoryConfig: repoConfig, + RepositoryCache: repoCache, } tests := []struct { name string diff --git a/pkg/downloader/testdata/helmhome/helm/repositories.yaml b/pkg/downloader/testdata/repositories.yaml similarity index 100% rename from pkg/downloader/testdata/helmhome/helm/repositories.yaml rename to pkg/downloader/testdata/repositories.yaml diff --git a/pkg/downloader/testdata/helmhome/helm/repository/kubernetes-charts-index.yaml b/pkg/downloader/testdata/repository/kubernetes-charts-index.yaml similarity index 100% rename from pkg/downloader/testdata/helmhome/helm/repository/kubernetes-charts-index.yaml rename to pkg/downloader/testdata/repository/kubernetes-charts-index.yaml diff --git a/pkg/downloader/testdata/helmhome/helm/repository/malformed-index.yaml b/pkg/downloader/testdata/repository/malformed-index.yaml similarity index 100% rename from pkg/downloader/testdata/helmhome/helm/repository/malformed-index.yaml rename to pkg/downloader/testdata/repository/malformed-index.yaml diff --git a/pkg/downloader/testdata/helmhome/helm/repository/testing-basicauth-index.yaml b/pkg/downloader/testdata/repository/testing-basicauth-index.yaml similarity index 100% rename from pkg/downloader/testdata/helmhome/helm/repository/testing-basicauth-index.yaml rename to pkg/downloader/testdata/repository/testing-basicauth-index.yaml diff --git a/pkg/downloader/testdata/helmhome/helm/repository/testing-https-index.yaml b/pkg/downloader/testdata/repository/testing-https-index.yaml similarity index 100% rename from pkg/downloader/testdata/helmhome/helm/repository/testing-https-index.yaml rename to pkg/downloader/testdata/repository/testing-https-index.yaml diff --git a/pkg/downloader/testdata/helmhome/helm/repository/testing-index.yaml b/pkg/downloader/testdata/repository/testing-index.yaml similarity index 100% rename from pkg/downloader/testdata/helmhome/helm/repository/testing-index.yaml rename to pkg/downloader/testdata/repository/testing-index.yaml diff --git a/pkg/downloader/testdata/helmhome/helm/repository/testing-querystring-index.yaml b/pkg/downloader/testdata/repository/testing-querystring-index.yaml similarity index 100% rename from pkg/downloader/testdata/helmhome/helm/repository/testing-querystring-index.yaml rename to pkg/downloader/testdata/repository/testing-querystring-index.yaml diff --git a/pkg/downloader/testdata/helmhome/helm/repository/testing-relative-index.yaml b/pkg/downloader/testdata/repository/testing-relative-index.yaml similarity index 100% rename from pkg/downloader/testdata/helmhome/helm/repository/testing-relative-index.yaml rename to pkg/downloader/testdata/repository/testing-relative-index.yaml diff --git a/pkg/downloader/testdata/helmhome/helm/repository/testing-relative-trailing-slash-index.yaml b/pkg/downloader/testdata/repository/testing-relative-trailing-slash-index.yaml similarity index 100% rename from pkg/downloader/testdata/helmhome/helm/repository/testing-relative-trailing-slash-index.yaml rename to pkg/downloader/testdata/repository/testing-relative-trailing-slash-index.yaml diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index 24059e00c..fcc90fd3a 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -75,8 +75,8 @@ func WithTLSClientConfig(certFile, keyFile, caFile string) Option { // Getter is an interface to support GET to the specified URL. type Getter interface { - //Get file content by url string - Get(url string) (*bytes.Buffer, error) + // Get file content by url string + Get(url string, options ...Option) (*bytes.Buffer, error) } // Constructor is the function for every getter which creates a specific instance @@ -108,41 +108,26 @@ type Providers []Provider // ByScheme returns a Provider that handles the given scheme. // // If no provider handles this scheme, this will return an error. -func (p Providers) ByScheme(scheme string) (Constructor, error) { +func (p Providers) ByScheme(scheme string) (Getter, error) { for _, pp := range p { if pp.Provides(scheme) { - return pp.New, nil + return pp.New() } } return nil, errors.Errorf("scheme %q not supported", scheme) } +var httpProvider = Provider{ + Schemes: []string{"http", "https"}, + New: NewHTTPGetter, +} + // All finds all of the registered getters as a list of Provider instances. // Currently, the built-in getters and the discovered plugins with downloader // notations are collected. -func All(settings cli.EnvSettings) Providers { - result := Providers{ - { - Schemes: []string{"http", "https"}, - New: NewHTTPGetter, - }, - } +func All(settings *cli.EnvSettings) Providers { + result := Providers{httpProvider} pluginDownloaders, _ := collectPlugins(settings) result = append(result, pluginDownloaders...) return result } - -// ByScheme returns a getter for the given scheme. -// -// If the scheme is not supported, this will return an error. -func ByScheme(scheme string, settings cli.EnvSettings) (Provider, error) { - // Q: What do you call a scheme string who's the boss? - // A: Bruce Schemestring, of course. - a := All(settings) - for _, p := range a { - if p.Provides(scheme) { - return p, nil - } - } - return Provider{}, errors.Errorf("scheme %q not supported", scheme) -} diff --git a/pkg/getter/getter_test.go b/pkg/getter/getter_test.go index 627d8d2ec..9b55f86ce 100644 --- a/pkg/getter/getter_test.go +++ b/pkg/getter/getter_test.go @@ -16,13 +16,13 @@ limitations under the License. package getter import ( - "os" "testing" "helm.sh/helm/pkg/cli" - "helm.sh/helm/pkg/helmpath/xdg" ) +const pluginDir = "testdata/plugins" + func TestProvider(t *testing.T) { p := Provider{ []string{"one", "three"}, @@ -53,9 +53,9 @@ func TestProviders(t *testing.T) { } func TestAll(t *testing.T) { - os.Setenv(xdg.DataHomeEnvVar, "testdata") - - all := All(cli.EnvSettings{}) + all := All(&cli.EnvSettings{ + PluginsDirectory: pluginDir, + }) if len(all) != 3 { t.Errorf("expected 3 providers (default plus two plugins), got %d", len(all)) } @@ -66,12 +66,13 @@ func TestAll(t *testing.T) { } func TestByScheme(t *testing.T) { - os.Setenv(xdg.DataHomeEnvVar, "testdata") - - if _, err := ByScheme("test", cli.EnvSettings{}); err != nil { + g := All(&cli.EnvSettings{ + PluginsDirectory: pluginDir, + }) + if _, err := g.ByScheme("test"); err != nil { t.Error(err) } - if _, err := ByScheme("https", cli.EnvSettings{}); err != nil { + if _, err := g.ByScheme("https"); err != nil { t.Error(err) } } diff --git a/pkg/getter/httpgetter.go b/pkg/getter/httpgetter.go index 5e99951c3..2e409c104 100644 --- a/pkg/getter/httpgetter.go +++ b/pkg/getter/httpgetter.go @@ -34,7 +34,10 @@ type HTTPGetter struct { } //Get performs a Get from repo.Getter and returns the body. -func (g *HTTPGetter) Get(href string) (*bytes.Buffer, error) { +func (g *HTTPGetter) Get(href string, options ...Option) (*bytes.Buffer, error) { + for _, opt := range options { + opt(&g.opts) + } return g.get(href) } diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go index c813b8494..3801aa89f 100644 --- a/pkg/getter/httpgetter_test.go +++ b/pkg/getter/httpgetter_test.go @@ -104,16 +104,11 @@ func TestDownload(t *testing.T) { })) defer srv.Close() - provider, err := ByScheme("http", cli.EnvSettings{}) - if err != nil { - t.Fatal("No http provider found") - } - - g, err := provider.New(WithURL(srv.URL)) + g, err := All(new(cli.EnvSettings)).ByScheme("http") if err != nil { t.Fatal(err) } - got, err := g.Get(srv.URL) + got, err := g.Get(srv.URL, WithURL(srv.URL)) if err != nil { t.Fatal(err) } diff --git a/pkg/getter/plugingetter.go b/pkg/getter/plugingetter.go index 78e44ea8d..a56ba8bbc 100644 --- a/pkg/getter/plugingetter.go +++ b/pkg/getter/plugingetter.go @@ -25,14 +25,13 @@ import ( "github.com/pkg/errors" "helm.sh/helm/pkg/cli" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/plugin" ) // collectPlugins scans for getter plugins. // This will load plugins according to the cli. -func collectPlugins(settings cli.EnvSettings) (Providers, error) { - plugins, err := plugin.FindPlugins(helmpath.Plugins()) +func collectPlugins(settings *cli.EnvSettings) (Providers, error) { + plugins, err := plugin.FindPlugins(settings.PluginsDirectory) if err != nil { return nil, err } @@ -57,14 +56,17 @@ func collectPlugins(settings cli.EnvSettings) (Providers, error) { // implemented in plugins. type pluginGetter struct { command string - settings cli.EnvSettings + settings *cli.EnvSettings name string base string opts options } // Get runs downloader plugin command -func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) { +func (p *pluginGetter) Get(href string, options ...Option) (*bytes.Buffer, error) { + for _, opt := range options { + opt(&p.opts) + } commands := strings.Split(p.command, " ") argv := append(commands[1:], p.opts.certFile, p.opts.keyFile, p.opts.caFile, href) prog := exec.Command(filepath.Join(p.base, commands[0]), argv...) @@ -84,7 +86,7 @@ func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) { } // NewPluginGetter constructs a valid plugin getter -func NewPluginGetter(command string, settings cli.EnvSettings, name, base string) Constructor { +func NewPluginGetter(command string, settings *cli.EnvSettings, name, base string) Constructor { return func(options ...Option) (Getter, error) { result := &pluginGetter{ command: command, diff --git a/pkg/getter/plugingetter_test.go b/pkg/getter/plugingetter_test.go index e5fdb88a8..ed09babb0 100644 --- a/pkg/getter/plugingetter_test.go +++ b/pkg/getter/plugingetter_test.go @@ -16,19 +16,17 @@ limitations under the License. package getter import ( - "os" "runtime" "strings" "testing" "helm.sh/helm/pkg/cli" - "helm.sh/helm/pkg/helmpath/xdg" ) func TestCollectPlugins(t *testing.T) { - os.Setenv(xdg.DataHomeEnvVar, "testdata") - - env := cli.EnvSettings{} + env := &cli.EnvSettings{ + PluginsDirectory: pluginDir, + } p, err := collectPlugins(env) if err != nil { t.Fatal(err) @@ -56,9 +54,9 @@ func TestPluginGetter(t *testing.T) { t.Skip("TODO: refactor this test to work on windows") } - os.Setenv(xdg.DataHomeEnvVar, "testdata") - - env := cli.EnvSettings{} + env := &cli.EnvSettings{ + PluginsDirectory: pluginDir, + } pg := NewPluginGetter("echo", env, "test", ".") g, err := pg() if err != nil { @@ -82,11 +80,9 @@ func TestPluginSubCommands(t *testing.T) { t.Skip("TODO: refactor this test to work on windows") } - oldhh := os.Getenv("HELM_HOME") - defer os.Setenv("HELM_HOME", oldhh) - os.Setenv("HELM_HOME", "") - - env := cli.EnvSettings{} + env := &cli.EnvSettings{ + PluginsDirectory: pluginDir, + } pg := NewPluginGetter("echo -n", env, "test", ".") g, err := pg() if err != nil { diff --git a/pkg/getter/testdata/helm/plugins/testgetter/get.sh b/pkg/getter/testdata/plugins/testgetter/get.sh similarity index 100% rename from pkg/getter/testdata/helm/plugins/testgetter/get.sh rename to pkg/getter/testdata/plugins/testgetter/get.sh diff --git a/pkg/getter/testdata/helm/plugins/testgetter/plugin.yaml b/pkg/getter/testdata/plugins/testgetter/plugin.yaml similarity index 100% rename from pkg/getter/testdata/helm/plugins/testgetter/plugin.yaml rename to pkg/getter/testdata/plugins/testgetter/plugin.yaml diff --git a/pkg/getter/testdata/helm/plugins/testgetter2/get.sh b/pkg/getter/testdata/plugins/testgetter2/get.sh similarity index 100% rename from pkg/getter/testdata/helm/plugins/testgetter2/get.sh rename to pkg/getter/testdata/plugins/testgetter2/get.sh diff --git a/pkg/getter/testdata/helm/plugins/testgetter2/plugin.yaml b/pkg/getter/testdata/plugins/testgetter2/plugin.yaml similarity index 100% rename from pkg/getter/testdata/helm/plugins/testgetter2/plugin.yaml rename to pkg/getter/testdata/plugins/testgetter2/plugin.yaml diff --git a/pkg/getter/testdata/helm/repository/local/index.yaml b/pkg/getter/testdata/repository/local/index.yaml similarity index 100% rename from pkg/getter/testdata/helm/repository/local/index.yaml rename to pkg/getter/testdata/repository/local/index.yaml diff --git a/pkg/getter/testdata/helm/repository/repositories.yaml b/pkg/getter/testdata/repository/repositories.yaml similarity index 100% rename from pkg/getter/testdata/helm/repository/repositories.yaml rename to pkg/getter/testdata/repository/repositories.yaml diff --git a/pkg/helmpath/home.go b/pkg/helmpath/home.go index 9c9fe9379..0b0f110a5 100644 --- a/pkg/helmpath/home.go +++ b/pkg/helmpath/home.go @@ -13,69 +13,22 @@ package helmpath -import ( - "fmt" - "path/filepath" -) - // This helper builds paths to Helm's configuration, cache and data paths. const lp = lazypath("helm") // ConfigPath returns the path where Helm stores configuration. -func ConfigPath() string { - return lp.configPath("") -} +func ConfigPath(elem ...string) string { return lp.configPath(elem...) } // CachePath returns the path where Helm stores cached objects. -func CachePath() string { - return lp.cachePath("") -} +func CachePath(elem ...string) string { return lp.cachePath(elem...) } // DataPath returns the path where Helm stores data. -func DataPath() string { - return lp.dataPath("") -} - -// Registry returns the path to the local registry cache. -func Registry() string { - return lp.cachePath("registry") -} - -// RepositoryFile returns the path to the repositories.yaml file. -func RepositoryFile() string { - return lp.configPath("repositories.yaml") -} - -// RepositoryCache returns the cache path for repository metadata. -func RepositoryCache() string { - return lp.cachePath("repository") -} +func DataPath(elem ...string) string { return lp.dataPath(elem...) } // CacheIndex returns the path to an index for the given named repository. -func CacheIndex(name string) string { - target := fmt.Sprintf("%s-index.yaml", name) - if name == "" { - target = "index.yaml" +func CacheIndexFile(name string) string { + if name != "" { + name += "-" } - return filepath.Join(RepositoryCache(), target) -} - -// Starters returns the path to the Helm starter packs. -func Starters() string { - return lp.dataPath("starters") -} - -// PluginCache returns the cache path for plugins. -func PluginCache() string { - return lp.cachePath("plugins") -} - -// Plugins returns the path to the plugins directory. -func Plugins() string { - return lp.dataPath("plugins") -} - -// Archive returns the path to download chart archives. -func Archive() string { - return lp.cachePath("archive") + return name + "index.yaml" } diff --git a/pkg/helmpath/home_unix_test.go b/pkg/helmpath/home_unix_test.go index 0c933a862..1b813f4c4 100644 --- a/pkg/helmpath/home_unix_test.go +++ b/pkg/helmpath/home_unix_test.go @@ -38,12 +38,6 @@ func TestHelmHome(t *testing.T) { isEq(t, CachePath(), "/cache/helm") isEq(t, ConfigPath(), "/config/helm") isEq(t, DataPath(), "/data/helm") - isEq(t, RepositoryFile(), "/config/helm/repositories.yaml") - isEq(t, RepositoryCache(), "/cache/helm/repository") - isEq(t, CacheIndex("t"), "/cache/helm/repository/t-index.yaml") - isEq(t, CacheIndex(""), "/cache/helm/repository/index.yaml") - isEq(t, Starters(), "/data/helm/starters") - isEq(t, Archive(), "/cache/helm/archive") // test to see if lazy-loading environment variables at runtime works os.Setenv(xdg.CacheHomeEnvVar, "/cache2") diff --git a/pkg/helmpath/home_windows_test.go b/pkg/helmpath/home_windows_test.go index d0258094e..ff1dfd6e7 100644 --- a/pkg/helmpath/home_windows_test.go +++ b/pkg/helmpath/home_windows_test.go @@ -35,12 +35,6 @@ func TestHelmHome(t *testing.T) { isEq(t, CachePath(), "c:\\helm") isEq(t, ConfigPath(), "d:\\helm") isEq(t, DataPath(), "e:\\helm") - isEq(t, RepositoryFile(), "d:\\helm\\repositories.yaml") - isEq(t, RepositoryCache(), "c:\\helm\\repository") - isEq(t, CacheIndex("t"), "c:\\helm\\repository\\t-index.yaml") - isEq(t, CacheIndex(""), "c:\\helm\\repository\\index.yaml") - isEq(t, Starters(), "e:\\helm\\starters") - isEq(t, Archive(), "c:\\helm\\archive") // test to see if lazy-loading environment variables at runtime works os.Setenv(xdg.CacheHomeEnvVar, "f:\\") diff --git a/pkg/helmpath/lazypath.go b/pkg/helmpath/lazypath.go index 39d349552..fea018b1e 100644 --- a/pkg/helmpath/lazypath.go +++ b/pkg/helmpath/lazypath.go @@ -22,27 +22,27 @@ import ( // lazypath is an lazy-loaded path buffer for the XDG base directory specification. type lazypath string -func (l lazypath) path(envVar string, defaultFn func() string, file string) string { +func (l lazypath) path(envVar string, defaultFn func() string, elem ...string) string { base := os.Getenv(envVar) if base == "" { base = defaultFn() } - return filepath.Join(base, string(l), file) + return filepath.Join(base, string(l), filepath.Join(elem...)) } // cachePath defines the base directory relative to which user specific non-essential data files // should be stored. -func (l lazypath) cachePath(file string) string { - return l.path(xdg.CacheHomeEnvVar, cacheHome, file) +func (l lazypath) cachePath(elem ...string) string { + return l.path(xdg.CacheHomeEnvVar, cacheHome, filepath.Join(elem...)) } // configPath defines the base directory relative to which user specific configuration files should // be stored. -func (l lazypath) configPath(file string) string { - return l.path(xdg.ConfigHomeEnvVar, configHome, file) +func (l lazypath) configPath(elem ...string) string { + return l.path(xdg.ConfigHomeEnvVar, configHome, filepath.Join(elem...)) } // dataPath defines the base directory relative to which user specific data files should be stored. -func (l lazypath) dataPath(file string) string { - return l.path(xdg.DataHomeEnvVar, dataHome, file) +func (l lazypath) dataPath(elem ...string) string { + return l.path(xdg.DataHomeEnvVar, dataHome, filepath.Join(elem...)) } diff --git a/pkg/plugin/cache/cache.go b/pkg/plugin/cache/cache.go index 72c0b7061..e5119439e 100644 --- a/pkg/plugin/cache/cache.go +++ b/pkg/plugin/cache/cache.go @@ -30,21 +30,19 @@ var scpSyntaxRe = regexp.MustCompile(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) // Key generates a cache key based on a url or scp string. The key is file // system safe. func Key(repo string) (string, error) { - - var u *url.URL - var err error - var strip bool + var ( + u *url.URL + err error + ) if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil { // Match SCP-like syntax and convert it to a URL. // Eg, "git@github.com:user/repo" becomes // "ssh://git@github.com/user/repo". u = &url.URL{ - Scheme: "ssh", - User: url.User(m[1]), - Host: m[2], - Path: "/" + m[3], + User: url.User(m[1]), + Host: m[2], + Path: "/" + m[3], } - strip = true } else { u, err = url.Parse(repo) if err != nil { @@ -52,23 +50,18 @@ func Key(repo string) (string, error) { } } - if strip { - u.Scheme = "" - } - - var key string + var key strings.Builder if u.Scheme != "" { - key = u.Scheme + "-" + key.WriteString(u.Scheme) + key.WriteString("-") } if u.User != nil && u.User.Username() != "" { - key = key + u.User.Username() + "-" + key.WriteString(u.User.Username()) + key.WriteString("-") } - key = key + u.Host + key.WriteString(u.Host) if u.Path != "" { - key = key + strings.ReplaceAll(u.Path, "/", "-") + key.WriteString(strings.ReplaceAll(u.Path, "/", "-")) } - - key = strings.ReplaceAll(key, ":", "-") - - return key, nil + return strings.ReplaceAll(key.String(), ":", "-"), nil } diff --git a/pkg/plugin/installer/base.go b/pkg/plugin/installer/base.go index e9840b5b9..12ae8c58c 100644 --- a/pkg/plugin/installer/base.go +++ b/pkg/plugin/installer/base.go @@ -42,5 +42,5 @@ func (b *base) Path() string { if b.Source == "" { return "" } - return filepath.Join(helmpath.Plugins(), filepath.Base(b.Source)) + return helmpath.DataPath("plugins", filepath.Base(b.Source)) } diff --git a/pkg/plugin/installer/http_installer.go b/pkg/plugin/installer/http_installer.go index d3294b3b3..7bb0f34aa 100644 --- a/pkg/plugin/installer/http_installer.go +++ b/pkg/plugin/installer/http_installer.go @@ -79,18 +79,13 @@ func NewHTTPInstaller(source string) (*HTTPInstaller, error) { return nil, err } - getConstructor, err := getter.ByScheme("http", cli.EnvSettings{}) - if err != nil { - return nil, err - } - - get, err := getConstructor.New(getter.WithURL(source)) + get, err := getter.All(new(cli.EnvSettings)).ByScheme("http") if err != nil { return nil, err } i := &HTTPInstaller{ - CacheDir: filepath.Join(helmpath.PluginCache(), key), + CacheDir: helmpath.CachePath("plugins", key), PluginName: stripPluginName(filepath.Base(source)), base: newBase(source), extractor: extractor, @@ -157,7 +152,7 @@ func (i HTTPInstaller) Path() string { if i.base.Source == "" { return "" } - return filepath.Join(helmpath.Plugins(), i.PluginName) + return helmpath.DataPath("plugins", i.PluginName) } // Extract extracts compressed archives diff --git a/pkg/plugin/installer/http_installer_test.go b/pkg/plugin/installer/http_installer_test.go index 55e915b4e..10929a514 100644 --- a/pkg/plugin/installer/http_installer_test.go +++ b/pkg/plugin/installer/http_installer_test.go @@ -19,12 +19,12 @@ import ( "bytes" "encoding/base64" "os" - "path/filepath" "testing" "github.com/pkg/errors" "helm.sh/helm/internal/test/ensure" + "helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/helmpath" ) @@ -36,7 +36,9 @@ type TestHTTPGetter struct { MockError error } -func (t *TestHTTPGetter) Get(href string) (*bytes.Buffer, error) { return t.MockResponse, t.MockError } +func (t *TestHTTPGetter) Get(href string, _ ...getter.Option) (*bytes.Buffer, error) { + return t.MockResponse, t.MockError +} // Fake plugin tarball data var fakePluginB64 = "H4sIAKRj51kAA+3UX0vCUBgGcC9jn+Iwuk3Peza3GeyiUlJQkcogCOzgli7dJm4TvYk+a5+k479UqquUCJ/fLs549sLO2TnvWnJa9aXnjwujYdYLovxMhsPcfnHOLdNkOXthM/IVQQYjg2yyLLJ4kXGhLp5j0z3P41tZksqxmspL3B/O+j/XtZu1y8rdYzkOZRCxduKPk53ny6Wwz/GfIIf1As8lxzGJSmoHNLJZphKHG4YpTCE0wVk3DULfpSJ3DMMqkj3P5JfMYLdX1Vr9Ie/5E5cstcdC8K04iGLX5HaJuKpWL17F0TCIBi5pf/0pjtLhun5j3f9v6r7wfnI/H0eNp9d1/5P6Gez0vzo7wsoxfrAZbTny/o9k6J8z/VkO/LPlWdC1iVpbEEcq5nmeJ13LEtmbV0k2r2PrOs9PuuNglC5rL1Y5S/syXRQmutaNw1BGnnp8Wq3UG51WvX1da3bKtZtCN/R09DwAAAAAAAAAAAAAAAAAAADAb30AoMczDwAoAAA=" @@ -57,12 +59,11 @@ func TestStripName(t *testing.T) { } func TestHTTPInstaller(t *testing.T) { + defer ensure.HelmHome(t)() source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil { - t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err) + if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { + t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) } i, err := NewForSource(source, "0.0.1") @@ -90,7 +91,7 @@ func TestHTTPInstaller(t *testing.T) { if err := Install(i); err != nil { t.Error(err) } - if i.Path() != filepath.Join(helmpath.Plugins(), "fake-plugin") { + if i.Path() != helmpath.DataPath("plugins", "fake-plugin") { t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path()) } @@ -104,12 +105,11 @@ func TestHTTPInstaller(t *testing.T) { } func TestHTTPInstallerNonExistentVersion(t *testing.T) { + defer ensure.HelmHome(t)() source := "https://repo.localdomain/plugins/fake-plugin-0.0.2.tar.gz" - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil { - t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err) + if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { + t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) } i, err := NewForSource(source, "0.0.2") @@ -137,11 +137,10 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) { func TestHTTPInstallerUpdate(t *testing.T) { source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() - if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil { - t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err) + if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { + t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) } i, err := NewForSource(source, "0.0.1") @@ -169,7 +168,7 @@ func TestHTTPInstallerUpdate(t *testing.T) { if err := Install(i); err != nil { t.Error(err) } - if i.Path() != filepath.Join(helmpath.Plugins(), "fake-plugin") { + if i.Path() != helmpath.DataPath("plugins", "fake-plugin") { t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path()) } diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go index 9303d7e19..14a02a87e 100644 --- a/pkg/plugin/installer/installer.go +++ b/pkg/plugin/installer/installer.go @@ -18,7 +18,6 @@ package installer import ( "fmt" "os" - "path" "path/filepath" "strings" @@ -43,14 +42,12 @@ type Installer interface { // Install installs a plugin. func Install(i Installer) error { - if _, pathErr := os.Stat(path.Dir(i.Path())); os.IsNotExist(pathErr) { - return errors.New(`plugin home "$XDG_CONFIG_HOME/helm/plugins" does not exist`) + if err := os.MkdirAll(filepath.Dir(i.Path()), 0755); err != nil { + return err } - if _, pathErr := os.Stat(i.Path()); !os.IsNotExist(pathErr) { return errors.New("plugin already exists") } - return i.Install() } @@ -59,7 +56,6 @@ func Update(i Installer) error { if _, pathErr := os.Stat(i.Path()); os.IsNotExist(pathErr) { return errors.New("plugin does not exist") } - return i.Update() } diff --git a/pkg/plugin/installer/local_installer_test.go b/pkg/plugin/installer/local_installer_test.go index 828b5e3ce..c21e6afac 100644 --- a/pkg/plugin/installer/local_installer_test.go +++ b/pkg/plugin/installer/local_installer_test.go @@ -21,16 +21,12 @@ import ( "path/filepath" "testing" - "helm.sh/helm/internal/test/ensure" "helm.sh/helm/pkg/helmpath" ) var _ Installer = new(LocalInstaller) func TestLocalInstaller(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - // Make a temp dir tdir, err := ioutil.TempDir("", "helm-installer-") if err != nil { @@ -48,10 +44,10 @@ func TestLocalInstaller(t *testing.T) { } if err := Install(i); err != nil { - t.Error(err) + t.Fatal(err) } - if i.Path() != filepath.Join(helmpath.Plugins(), "echo") { + if i.Path() != helmpath.DataPath("plugins", "echo") { t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path()) } } diff --git a/pkg/plugin/installer/vcs_installer.go b/pkg/plugin/installer/vcs_installer.go index b56881210..e4ee27261 100644 --- a/pkg/plugin/installer/vcs_installer.go +++ b/pkg/plugin/installer/vcs_installer.go @@ -17,7 +17,6 @@ package installer // import "helm.sh/helm/pkg/plugin/installer" import ( "os" - "path/filepath" "sort" "github.com/Masterminds/semver" @@ -53,7 +52,7 @@ func NewVCSInstaller(source, version string) (*VCSInstaller, error) { if err != nil { return nil, err } - cachedpath := filepath.Join(helmpath.PluginCache(), key) + cachedpath := helmpath.CachePath("plugins", key) repo, err := vcs.NewRepo(source, cachedpath) if err != nil { return nil, err diff --git a/pkg/plugin/installer/vcs_installer_test.go b/pkg/plugin/installer/vcs_installer_test.go index 47fdecd63..f9ffa8ace 100644 --- a/pkg/plugin/installer/vcs_installer_test.go +++ b/pkg/plugin/installer/vcs_installer_test.go @@ -49,11 +49,10 @@ func (r *testRepo) UpdateVersion(version string) error { } func TestVCSInstaller(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() - if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil { - t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err) + if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil { + t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err) } source := "https://github.com/adamreese/helm-env" @@ -83,7 +82,7 @@ func TestVCSInstaller(t *testing.T) { if repo.current != "0.1.1" { t.Errorf("expected version '0.1.1', got %q", repo.current) } - if i.Path() != filepath.Join(helmpath.Plugins(), "helm-env") { + if i.Path() != helmpath.DataPath("plugins", "helm-env") { t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path()) } @@ -94,7 +93,7 @@ func TestVCSInstaller(t *testing.T) { t.Errorf("expected error for plugin exists, got (%v)", err) } - //Testing FindSource method, expect error because plugin code is not a cloned repository + // Testing FindSource method, expect error because plugin code is not a cloned repository if _, err := FindSource(i.Path()); err == nil { t.Error("expected error for inability to find plugin source, got none") } else if err.Error() != "cannot get information about plugin source" { @@ -103,8 +102,7 @@ func TestVCSInstaller(t *testing.T) { } func TestVCSInstallerNonExistentVersion(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() source := "https://github.com/adamreese/helm-env" version := "0.2.0" @@ -127,8 +125,7 @@ func TestVCSInstallerNonExistentVersion(t *testing.T) { } } func TestVCSInstallerUpdate(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() source := "https://github.com/adamreese/helm-env" diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 76f9f461e..dc2cc02d2 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -25,7 +25,7 @@ import ( "sigs.k8s.io/yaml" - helm_env "helm.sh/helm/pkg/cli" + "helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/helmpath" ) @@ -109,14 +109,15 @@ type Plugin struct { // - If both OS and Arch match the current platform, search will stop and the command will be prepared for execution // - If OS matches and there is no more specific match, the command will be prepared for execution // - If no OS/Arch match is found, return nil -func getPlatformCommand(platformCommands []PlatformCommand) []string { +func getPlatformCommand(cmds []PlatformCommand) []string { var command []string - for _, platformCommand := range platformCommands { - if strings.EqualFold(platformCommand.OperatingSystem, runtime.GOOS) { - command = strings.Split(os.ExpandEnv(platformCommand.Command), " ") + eq := strings.EqualFold + for _, c := range cmds { + if eq(c.OperatingSystem, runtime.GOOS) { + command = strings.Split(os.ExpandEnv(c.Command), " ") } - if strings.EqualFold(platformCommand.OperatingSystem, runtime.GOOS) && strings.EqualFold(platformCommand.Architecture, runtime.GOARCH) { - return strings.Split(os.ExpandEnv(platformCommand.Command), " ") + if eq(c.OperatingSystem, runtime.GOOS) && eq(c.Architecture, runtime.GOARCH) { + return strings.Split(os.ExpandEnv(c.Command), " ") } } return command @@ -215,27 +216,20 @@ func FindPlugins(plugdirs string) ([]*Plugin, error) { // SetupPluginEnv prepares os.Env for plugins. It operates on os.Env because // the plugin subsystem itself needs access to the environment variables // created here. -func SetupPluginEnv(settings helm_env.EnvSettings, - shortName, base string) { +func SetupPluginEnv(settings *cli.EnvSettings, name, base string) { for key, val := range map[string]string{ - "HELM_PLUGIN_NAME": shortName, + "HELM_PLUGIN_NAME": name, "HELM_PLUGIN_DIR": base, "HELM_BIN": os.Args[0], - "HELM_PLUGIN": helmpath.Plugins(), + "HELM_PLUGIN": settings.PluginsDirectory, // Set vars that convey common information. - "HELM_PATH_REPOSITORY_FILE": helmpath.RepositoryFile(), - "HELM_PATH_REPOSITORY_CACHE": helmpath.RepositoryCache(), - "HELM_PATH_STARTER": helmpath.Starters(), - "HELM_PATH_CACHE": helmpath.CachePath(), - "HELM_PATH_CONFIG": helmpath.ConfigPath(), - "HELM_PATH_DATA": helmpath.DataPath(), + "HELM_PATH_REPOSITORY_FILE": settings.RepositoryConfig, + "HELM_PATH_REPOSITORY_CACHE": settings.RepositoryCache, + "HELM_PATH_STARTER": helmpath.DataPath("starters"), "HELM_HOME": helmpath.DataPath(), // for backwards compatibility with Helm 2 plugins + "HELM_DEBUG": fmt.Sprint(settings.Debug), } { os.Setenv(key, val) } - - if settings.Debug { - os.Setenv("HELM_DEBUG", "1") - } } diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index be6553489..dfe607cd6 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -16,9 +16,13 @@ limitations under the License. package plugin // import "helm.sh/helm/pkg/plugin" import ( + "os" + "path/filepath" "reflect" "runtime" "testing" + + "helm.sh/helm/pkg/cli" ) func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) { @@ -87,7 +91,6 @@ func TestPlatformPrepareCommand(t *testing.T) { }, }, } - argv := []string{"--debug", "--foo", "bar"} var osStrCmp string os := runtime.GOOS arch := runtime.GOARCH @@ -101,6 +104,7 @@ func TestPlatformPrepareCommand(t *testing.T) { osStrCmp = "os-arch" } + argv := []string{"--debug", "--foo", "bar"} checkCommand(p, argv, osStrCmp, t) } @@ -116,7 +120,6 @@ func TestPartialPlatformPrepareCommand(t *testing.T) { }, }, } - argv := []string{"--debug", "--foo", "bar"} var osStrCmp string os := runtime.GOOS arch := runtime.GOARCH @@ -128,6 +131,7 @@ func TestPartialPlatformPrepareCommand(t *testing.T) { osStrCmp = "os-arch" } + argv := []string{"--debug", "--foo", "bar"} checkCommand(p, argv, osStrCmp, t) } @@ -158,8 +162,7 @@ func TestNoMatchPrepareCommand(t *testing.T) { } argv := []string{"--debug", "--foo", "bar"} - _, _, err := p.PrepareCommand(argv) - if err == nil { + if _, _, err := p.PrepareCommand(argv); err == nil { t.Errorf("Expected error to be returned") } } @@ -251,3 +254,24 @@ func TestLoadAll(t *testing.T) { t.Errorf("Expected second plugin to be hello, got %q", plugs[1].Metadata.Name) } } + +func TestSetupEnv(t *testing.T) { + name := "pequod" + base := filepath.Join("testdata/helmhome/helm/plugins", name) + + s := &cli.EnvSettings{ + PluginsDirectory: "testdata/helmhome/helm/plugins", + } + + SetupPluginEnv(s, name, base) + for _, tt := range []struct { + name, expect string + }{ + {"HELM_PLUGIN_NAME", name}, + {"HELM_PLUGIN_DIR", base}, + } { + if got := os.Getenv(tt.name); got != tt.expect { + t.Errorf("Expected $%s=%q, got %q", tt.name, tt.expect, got) + } + } +} diff --git a/pkg/plugin/testdata/plugdir/hello/hello.sh b/pkg/plugin/testdata/plugdir/hello/hello.sh index 8778e4431..dcfd58876 100755 --- a/pkg/plugin/testdata/plugdir/hello/hello.sh +++ b/pkg/plugin/testdata/plugdir/hello/hello.sh @@ -5,9 +5,5 @@ echo "Hello from a Helm plugin" echo "PARAMS" echo $* -echo "ENVIRONMENT" -echo $TILLER_HOST -echo $HELM_PATH_CONFIG - -$HELM_BIN --host $TILLER_HOST ls --all +$HELM_BIN ls --all diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index 183dd4157..1ce869580 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -52,6 +52,7 @@ type ChartRepository struct { ChartPaths []string IndexFile *IndexFile Client getter.Getter + CachePath string } // NewChartRepository constructs ChartRepository @@ -61,23 +62,16 @@ func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository, return nil, errors.Errorf("invalid chart URL format: %s", cfg.URL) } - getterConstructor, err := getters.ByScheme(u.Scheme) + client, err := getters.ByScheme(u.Scheme) if err != nil { return nil, errors.Errorf("could not find protocol handler for: %s", u.Scheme) } - client, err := getterConstructor( - getter.WithURL(cfg.URL), - getter.WithTLSClientConfig(cfg.CertFile, cfg.KeyFile, cfg.CAFile), - getter.WithBasicAuth(cfg.Username, cfg.Password), - ) - if err != nil { - return nil, errors.Wrapf(err, "could not construct protocol handler for: %s", u.Scheme) - } return &ChartRepository{ Config: cfg, IndexFile: NewIndexFile(), Client: client, + CachePath: helmpath.CachePath("repository"), }, nil } @@ -114,31 +108,37 @@ func (r *ChartRepository) Load() error { } // DownloadIndexFile fetches the index from a repository. -func (r *ChartRepository) DownloadIndexFile() error { +func (r *ChartRepository) DownloadIndexFile() (string, error) { var indexURL string parsedURL, err := url.Parse(r.Config.URL) if err != nil { - return err + return "", err } parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml" indexURL = parsedURL.String() // TODO add user-agent - resp, err := r.Client.Get(indexURL) + resp, err := r.Client.Get(indexURL, + getter.WithURL(r.Config.URL), + getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile), + getter.WithBasicAuth(r.Config.Username, r.Config.Password), + ) if err != nil { - return err + return "", err } index, err := ioutil.ReadAll(resp) if err != nil { - return err + return "", err } if _, err := loadIndex(index); err != nil { - return err + return "", err } - return ioutil.WriteFile(helmpath.CacheIndex(r.Config.Name), index, 0644) + fname := filepath.Join(r.CachePath, helmpath.CacheIndexFile(r.Config.Name)) + os.MkdirAll(filepath.Dir(fname), 0755) + return fname, ioutil.WriteFile(fname, index, 0644) } // Index generates an index for the chart repository and writes an index.yaml file. @@ -208,12 +208,13 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion if err != nil { return "", err } - if err := r.DownloadIndexFile(); err != nil { + idx, err := r.DownloadIndexFile() + if err != nil { return "", errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", repoURL) } // Read the index file for the repository to get chart information and return chart URL - repoIndex, err := LoadIndexFile(helmpath.CacheIndex(name)) + repoIndex, err := LoadIndexFile(idx) if err != nil { return "", err } diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index d3c18a64b..d840f963f 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -34,8 +34,6 @@ import ( "helm.sh/helm/pkg/chart" "helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/getter" - "helm.sh/helm/pkg/helmpath" - "helm.sh/helm/pkg/helmpath/xdg" ) const ( @@ -47,7 +45,7 @@ func TestLoadChartRepository(t *testing.T) { r, err := NewChartRepository(&Entry{ Name: testRepository, URL: testURL, - }, getter.All(cli.EnvSettings{})) + }, getter.All(&cli.EnvSettings{})) if err != nil { t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) } @@ -80,7 +78,7 @@ func TestIndex(t *testing.T) { r, err := NewChartRepository(&Entry{ Name: testRepository, URL: testURL, - }, getter.All(cli.EnvSettings{})) + }, getter.All(&cli.EnvSettings{})) if err != nil { t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) } @@ -118,7 +116,7 @@ type CustomGetter struct { repoUrls []string } -func (g *CustomGetter) Get(href string) (*bytes.Buffer, error) { +func (g *CustomGetter) Get(href string, options ...getter.Option) (*bytes.Buffer, error) { index := &IndexFile{ APIVersion: "v1", Generated: time.Now(), @@ -132,21 +130,16 @@ func (g *CustomGetter) Get(href string) (*bytes.Buffer, error) { } func TestIndexCustomSchemeDownload(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) - repoName := "gcs-repo" repoURL := "gs://some-gcs-bucket" myCustomGetter := &CustomGetter{} customGetterConstructor := func(options ...getter.Option) (getter.Getter, error) { return myCustomGetter, nil } - providers := getter.Providers{ - { - Schemes: []string{"gs"}, - New: customGetterConstructor, - }, - } + providers := getter.Providers{{ + Schemes: []string{"gs"}, + New: customGetterConstructor, + }} repo, err := NewChartRepository(&Entry{ Name: repoName, URL: repoURL, @@ -154,6 +147,7 @@ func TestIndexCustomSchemeDownload(t *testing.T) { if err != nil { t.Fatalf("Problem loading chart repository from %s: %v", repoURL, err) } + repo.CachePath = ensure.TempDir(t) tempIndexFile, err := ioutil.TempFile("", "test-repo") if err != nil { @@ -161,8 +155,9 @@ func TestIndexCustomSchemeDownload(t *testing.T) { } defer os.Remove(tempIndexFile.Name()) - if err := repo.DownloadIndexFile(); err != nil { - t.Fatalf("Failed to download index file: %v", err) + idx, err := repo.DownloadIndexFile() + if err != nil { + t.Fatalf("Failed to download index file to %s: %v", idx, err) } if len(myCustomGetter.repoUrls) != 1 { @@ -282,23 +277,21 @@ func startLocalServerForTests(handler http.Handler) (*httptest.Server, error) { } func TestFindChartInRepoURL(t *testing.T) { - setupCacheHome(t) - srv, err := startLocalServerForTests(nil) if err != nil { t.Fatal(err) } defer srv.Close() - chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(cli.EnvSettings{})) + chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(&cli.EnvSettings{})) if err != nil { - t.Errorf("%s", err) + t.Fatalf("%v", err) } if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" { t.Errorf("%s is not the valid URL", chartURL) } - chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(cli.EnvSettings{})) + chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(&cli.EnvSettings{})) if err != nil { t.Errorf("%s", err) } @@ -308,13 +301,14 @@ func TestFindChartInRepoURL(t *testing.T) { } func TestErrorFindChartInRepoURL(t *testing.T) { - setupCacheHome(t) - _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", getter.All(cli.EnvSettings{})) - if err == nil { + g := getter.All(&cli.EnvSettings{ + RepositoryCache: ensure.TempDir(t), + }) + + if _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", g); err == nil { t.Errorf("Expected error for bad chart URL, but did not get any errors") - } - if err != nil && !strings.Contains(err.Error(), `looks like "http://someserver/something" is not a valid chart repository or cannot be reached: Get http://someserver/something/index.yaml`) { + } else if !strings.Contains(err.Error(), `looks like "http://someserver/something" is not a valid chart repository or cannot be reached: Get http://someserver/something/index.yaml`) { t.Errorf("Expected error for bad chart URL, but got a different error (%v)", err) } @@ -324,27 +318,21 @@ func TestErrorFindChartInRepoURL(t *testing.T) { } defer srv.Close() - _, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", getter.All(cli.EnvSettings{})) - if err == nil { + if _, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", g); err == nil { t.Errorf("Expected error for chart not found, but did not get any errors") - } - if err != nil && err.Error() != `chart "nginx1" not found in `+srv.URL+` repository` { + } else if err.Error() != `chart "nginx1" not found in `+srv.URL+` repository` { t.Errorf("Expected error for chart not found, but got a different error (%v)", err) } - _, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", getter.All(cli.EnvSettings{})) - if err == nil { + if _, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", g); err == nil { t.Errorf("Expected error for chart not found, but did not get any errors") - } - if err != nil && err.Error() != `chart "nginx1" version "0.1.0" not found in `+srv.URL+` repository` { + } else if err.Error() != `chart "nginx1" version "0.1.0" not found in `+srv.URL+` repository` { t.Errorf("Expected error for chart not found, but got a different error (%v)", err) } - _, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", getter.All(cli.EnvSettings{})) - if err == nil { + if _, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", g); err == nil { t.Errorf("Expected error for no chart URLs available, but did not get any errors") - } - if err != nil && err.Error() != `chart "chartWithNoURL" has no downloadable URLs` { + } else if err.Error() != `chart "chartWithNoURL" has no downloadable URLs` { t.Errorf("Expected error for chart not found, but got a different error (%v)", err) } } @@ -374,16 +362,3 @@ func TestResolveReferenceURL(t *testing.T) { t.Errorf("%s", chartURL) } } - -func setupCacheHome(t *testing.T) { - t.Helper() - d, err := ioutil.TempDir("", "helm") - if err != nil { - t.Fatal(err) - } - os.Setenv(xdg.CacheHomeEnvVar, d) - - if err := os.MkdirAll(helmpath.RepositoryCache(), 0755); err != nil { - t.Fatal(err) - } -} diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 164c2a511..92d898fd5 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -158,7 +158,7 @@ func (i IndexFile) Get(name, version string) (*ChartVersion, error) { } var constraint *semver.Constraints - if len(version) == 0 { + if version == "" { constraint, _ = semver.NewConstraint("*") } else { var err error @@ -276,10 +276,7 @@ func loadIndex(data []byte) (*IndexFile, error) { } i.SortEntries() if i.APIVersion == "" { - // When we leave Beta, we should remove legacy support and just - // return this error: - //return i, ErrNoAPIVersion - return loadUnversionedIndex(data) + return i, ErrNoAPIVersion } return i, nil } diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index 408b9773d..c33f257ea 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -24,7 +24,6 @@ import ( "helm.sh/helm/pkg/chart" "helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/getter" - "helm.sh/helm/pkg/helmpath" ) const ( @@ -135,35 +134,32 @@ func TestDownloadIndexFile(t *testing.T) { } defer srv.Close() - setupCacheHome(t) - r, err := NewChartRepository(&Entry{ Name: testRepo, URL: srv.URL, - }, getter.All(cli.EnvSettings{})) + }, getter.All(&cli.EnvSettings{})) if err != nil { t.Errorf("Problem creating chart repository from %s: %v", testRepo, err) } - if err := r.DownloadIndexFile(); err != nil { - t.Errorf("%#v", err) + idx, err := r.DownloadIndexFile() + if err != nil { + t.Fatalf("Failed to download index file to %s: %#v", idx, err) } - if _, err := os.Stat(helmpath.CacheIndex(testRepo)); err != nil { - t.Errorf("error finding created index file: %#v", err) + if _, err := os.Stat(idx); err != nil { + t.Fatalf("error finding created index file: %#v", err) } - b, err := ioutil.ReadFile(helmpath.CacheIndex(testRepo)) + b, err := ioutil.ReadFile(idx) if err != nil { - t.Errorf("error reading index file: %#v", err) + t.Fatalf("error reading index file: %#v", err) } i, err := loadIndex(b) if err != nil { - t.Errorf("Index %q failed to parse: %s", testfile, err) - return + t.Fatalf("Index %q failed to parse: %s", testfile, err) } - verifyLocalIndex(t, i) } @@ -175,19 +171,16 @@ func verifyLocalIndex(t *testing.T, i *IndexFile) { alpine, ok := i.Entries["alpine"] if !ok { - t.Errorf("'alpine' section not found.") - return + t.Fatalf("'alpine' section not found.") } if l := len(alpine); l != 1 { - t.Errorf("'alpine' should have 1 chart, got %d", l) - return + t.Fatalf("'alpine' should have 1 chart, got %d", l) } nginx, ok := i.Entries["nginx"] if !ok || len(nginx) != 2 { - t.Error("Expected 2 nginx entries") - return + t.Fatalf("Expected 2 nginx entries") } expects := []*ChartVersion{ @@ -292,7 +285,7 @@ func TestIndexDirectory(t *testing.T) { } frob := frobs[0] - if len(frob.Digest) == 0 { + if frob.Digest == "" { t.Errorf("Missing digest of file %s.", frob.Name) } if frob.URLs[0] != test.downloadLink { diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go index 54152a54e..70f026dcf 100644 --- a/pkg/repo/repo.go +++ b/pkg/repo/repo.go @@ -19,16 +19,13 @@ package repo // import "helm.sh/helm/pkg/repo" import ( "io/ioutil" "os" + "path/filepath" "time" "github.com/pkg/errors" "sigs.k8s.io/yaml" ) -// ErrRepoOutOfDate indicates that the repository file is out of date, but -// is fixable. -var ErrRepoOutOfDate = errors.New("repository file is out of date") - // File represents the repositories.yaml file type File struct { APIVersion string `json:"apiVersion"` @@ -48,41 +45,18 @@ func NewFile() *File { } // LoadFile takes a file at the given path and returns a File object -// -// If this returns ErrRepoOutOfDate, it also returns a recovered File that -// can be saved as a replacement to the out of date file. func LoadFile(path string) (*File, error) { b, err := ioutil.ReadFile(path) if err != nil { if os.IsNotExist(err) { - return nil, errors.Errorf("couldn't load repositories file (%s).\nYou might need to run `helm init`", path) + return nil, errors.Wrapf(err, "couldn't load repositories file (%s)", path) } return nil, err } r := &File{} err = yaml.Unmarshal(b, r) - if err != nil { - return nil, err - } - - // File is either corrupt, or is from before v2.0.0-Alpha.5 - if r.APIVersion == "" { - m := map[string]string{} - if err = yaml.Unmarshal(b, &m); err != nil { - return nil, err - } - r := NewFile() - for k, v := range m { - r.Add(&Entry{ - Name: k, - URL: v, - }) - } - return r, ErrRepoOutOfDate - } - - return r, nil + return r, err } // Add adds one or more repo entries to a repo file. @@ -94,18 +68,18 @@ func (r *File) Add(re ...*Entry) { // entry with the same name doesn't exist in the repo file it will add it. func (r *File) Update(re ...*Entry) { for _, target := range re { - found := false - for j, repo := range r.Repositories { - if repo.Name == target.Name { - r.Repositories[j] = target - found = true - break - } - } - if !found { - r.Add(target) + r.update(target) + } +} + +func (r *File) update(e *Entry) { + for j, repo := range r.Repositories { + if repo.Name == e.Name { + r.Repositories[j] = e + return } } + r.Add(e) } // Has returns true if the given name is already a repository name. @@ -139,5 +113,8 @@ func (r *File) WriteFile(path string, perm os.FileMode) error { if err != nil { return err } + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + return err + } return ioutil.WriteFile(path, data, perm) } diff --git a/pkg/repo/repo_test.go b/pkg/repo/repo_test.go index 2d6e46bbd..1ed3fb298 100644 --- a/pkg/repo/repo_test.go +++ b/pkg/repo/repo_test.go @@ -16,10 +16,12 @@ limitations under the License. package repo -import "testing" -import "io/ioutil" -import "os" -import "strings" +import ( + "io/ioutil" + "os" + "strings" + "testing" +) const testRepositoriesFile = "testdata/repositories.yaml" @@ -89,27 +91,6 @@ func TestNewFile(t *testing.T) { } } -func TestNewPreV1File(t *testing.T) { - r, err := LoadFile("testdata/old-repositories.yaml") - if err != nil && err != ErrRepoOutOfDate { - t.Fatal(err) - } - if len(r.Repositories) != 3 { - t.Fatalf("Expected 3 repos: %#v", r) - } - - // Because they are parsed as a map, we lose ordering. - found := false - for _, rr := range r.Repositories { - if rr.Name == "best-charts-ever" { - found = true - } - } - if !found { - t.Errorf("expected the best charts ever. Got %#v", r.Repositories) - } -} - func TestRemoveRepository(t *testing.T) { sampleRepository := NewFile() sampleRepository.Add( @@ -184,7 +165,7 @@ func TestWriteFile(t *testing.T) { t.Errorf("failed to create test-file (%v)", err) } defer os.Remove(file.Name()) - if err := sampleRepository.WriteFile(file.Name(), 0744); err != nil { + if err := sampleRepository.WriteFile(file.Name(), 0644); err != nil { t.Errorf("failed to write file (%v)", err) } @@ -200,10 +181,9 @@ func TestWriteFile(t *testing.T) { } func TestRepoNotExists(t *testing.T) { - _, err := LoadFile("/this/path/does/not/exist.yaml") - if err == nil { + if _, err := LoadFile("/this/path/does/not/exist.yaml"); err == nil { t.Errorf("expected err to be non-nil when path does not exist") - } else if !strings.Contains(err.Error(), "You might need to run `helm init`") { - t.Errorf("expected prompt to run `helm init` when repositories file does not exist") + } else if !strings.Contains(err.Error(), "couldn't load repositories file") { + t.Errorf("expected prompt `couldn't load repositories file`") } } diff --git a/pkg/repo/repotest/server.go b/pkg/repo/repotest/server.go index e8dab3b36..b344b336e 100644 --- a/pkg/repo/repotest/server.go +++ b/pkg/repo/repotest/server.go @@ -24,7 +24,6 @@ import ( "sigs.k8s.io/yaml" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/repo" ) @@ -70,7 +69,7 @@ func NewServer(docroot string) *Server { } srv.Start() // Add the testing repository as the only repo. - if err := setTestingRepository(srv.URL()); err != nil { + if err := setTestingRepository(srv.URL(), filepath.Join(root, "repositories.yaml")); err != nil { panic(err) } return srv @@ -108,7 +107,7 @@ func (s *Server) CopyCharts(origin string) ([]string, error) { if err != nil { return []string{}, err } - if err := ioutil.WriteFile(newname, data, 0755); err != nil { + if err := ioutil.WriteFile(newname, data, 0644); err != nil { return []string{}, err } copied[i] = newname @@ -132,7 +131,7 @@ func (s *Server) CreateIndex() error { } ifile := filepath.Join(s.docroot, "index.yaml") - return ioutil.WriteFile(ifile, d, 0755) + return ioutil.WriteFile(ifile, d, 0644) } func (s *Server) Start() { @@ -164,16 +163,16 @@ func (s *Server) URL() string { // This makes it possible to simulate a local cache of a repository. func (s *Server) LinkIndices() error { lstart := filepath.Join(s.docroot, "index.yaml") - ldest := helmpath.CacheIndex("test") + ldest := filepath.Join(s.docroot, "test-index.yaml") return os.Symlink(lstart, ldest) } // setTestingRepository sets up a testing repository.yaml with only the given URL. -func setTestingRepository(url string) error { +func setTestingRepository(url, fname string) error { r := repo.NewFile() r.Add(&repo.Entry{ Name: "test", URL: url, }) - return r.WriteFile(helmpath.RepositoryFile(), 0644) + return r.WriteFile(fname, 0644) } diff --git a/pkg/repo/repotest/server_test.go b/pkg/repo/repotest/server_test.go index 1d11b8ec9..77a7855b9 100644 --- a/pkg/repo/repotest/server_test.go +++ b/pkg/repo/repotest/server_test.go @@ -24,24 +24,23 @@ import ( "sigs.k8s.io/yaml" "helm.sh/helm/internal/test/ensure" - "helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/repo" ) // Young'n, in these here parts, we test our tests. func TestServer(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() - srv := NewServer(helmpath.CachePath()) + rootDir := ensure.TempDir(t) + + srv := NewServer(rootDir) defer srv.Stop() c, err := srv.CopyCharts("testdata/*.tgz") if err != nil { // Some versions of Go don't correctly fire defer on Fatal. - t.Error(err) - return + t.Fatal(err) } if len(c) != 1 { @@ -53,9 +52,9 @@ func TestServer(t *testing.T) { } res, err := http.Get(srv.URL() + "/examplechart-0.1.0.tgz") + res.Body.Close() if err != nil { - t.Error(err) - return + t.Fatal(err) } if res.ContentLength < 500 { @@ -64,26 +63,22 @@ func TestServer(t *testing.T) { res, err = http.Get(srv.URL() + "/index.yaml") if err != nil { - t.Error(err) - return + t.Fatal(err) } data, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { - t.Error(err) - return + t.Fatal(err) } m := repo.NewIndexFile() if err := yaml.Unmarshal(data, m); err != nil { - t.Error(err) - return + t.Fatal(err) } if l := len(m.Entries); l != 1 { - t.Errorf("Expected 1 entry, got %d", l) - return + t.Fatalf("Expected 1 entry, got %d", l) } expect := "examplechart" @@ -92,28 +87,26 @@ func TestServer(t *testing.T) { } res, err = http.Get(srv.URL() + "/index.yaml-nosuchthing") + res.Body.Close() if err != nil { - t.Error(err) - return + t.Fatal(err) } if res.StatusCode != 404 { - t.Errorf("Expected 404, got %d", res.StatusCode) + t.Fatalf("Expected 404, got %d", res.StatusCode) } } func TestNewTempServer(t *testing.T) { - ensure.HelmHome(t) - defer ensure.CleanHomeDirs(t) + defer ensure.HelmHome(t)() srv, err := NewTempServer("testdata/examplechart-0.1.0.tgz") if err != nil { t.Fatal(err) } - defer func() { - srv.Stop() - }() + defer srv.Stop() res, err := http.Head(srv.URL() + "/examplechart-0.1.0.tgz") + res.Body.Close() if err != nil { t.Error(err) }