Merge pull request #6272 from adamreese/feat/initless

feat(cmd/helm): remove need for helm init command
pull/6286/head v3.0.0-beta.1
Adam Reese 5 years ago committed by GitHub
commit f76b5f21ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -52,6 +52,7 @@ will be overwritten, but other files will be left alone.
type createOptions struct { type createOptions struct {
starter string // --starter starter string // --starter
name string name string
starterDir string
} }
func newCreateCmd(out io.Writer) *cobra.Command { func newCreateCmd(out io.Writer) *cobra.Command {
@ -64,6 +65,7 @@ func newCreateCmd(out io.Writer) *cobra.Command {
Args: require.ExactArgs(1), Args: require.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
o.name = args[0] o.name = args[0]
o.starterDir = helmpath.DataPath("starters")
return o.run(out) return o.run(out)
}, },
} }
@ -87,7 +89,7 @@ func (o *createOptions) run(out io.Writer) error {
if o.starter != "" { if o.starter != "" {
// Create from the 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 path is absolute, we dont want to prefix it with helm starters folder
if filepath.IsAbs(o.starter) { if filepath.IsAbs(o.starter) {
lstarter = o.starter lstarter = o.starter

@ -31,15 +31,14 @@ import (
) )
func TestCreateCmd(t *testing.T) { func TestCreateCmd(t *testing.T) {
defer ensure.HelmHome(t)()
cname := "testchart" cname := "testchart"
ensure.HelmHome(t) dir := ensure.TempDir(t)
defer ensure.CleanHomeDirs(t) defer testChdir(t, dir)()
defer testChdir(t, helmpath.CachePath())()
// Run a create // Run a create
if _, _, err := executeActionCommand("create " + cname); err != nil { if _, _, err := executeActionCommand("create " + cname); err != nil {
t.Errorf("Failed to run create: %s", err) t.Fatalf("Failed to run create: %s", err)
return
} }
// Test that the chart is there // Test that the chart is there
@ -63,22 +62,22 @@ func TestCreateCmd(t *testing.T) {
} }
func TestCreateStarterCmd(t *testing.T) { func TestCreateStarterCmd(t *testing.T) {
defer ensure.HelmHome(t)()
cname := "testchart" cname := "testchart"
defer resetEnv()() defer resetEnv()()
ensure.HelmHome(t) os.MkdirAll(helmpath.CachePath(), 0755)
defer ensure.CleanHomeDirs(t)
defer testChdir(t, helmpath.CachePath())() defer testChdir(t, helmpath.CachePath())()
// Create a starter. // Create a starter.
starterchart := helmpath.Starters() starterchart := helmpath.DataPath("starters")
os.Mkdir(starterchart, 0755) os.MkdirAll(starterchart, 0755)
if dest, err := chartutil.Create("starterchart", starterchart); err != nil { if dest, err := chartutil.Create("starterchart", starterchart); err != nil {
t.Fatalf("Could not create chart: %s", err) t.Fatalf("Could not create chart: %s", err)
} else { } else {
t.Logf("Created %s", dest) t.Logf("Created %s", dest)
} }
tplpath := filepath.Join(starterchart, "starterchart", "templates", "foo.tpl") 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) t.Fatalf("Could not write template: %s", err)
} }
@ -128,23 +127,23 @@ func TestCreateStarterCmd(t *testing.T) {
func TestCreateStarterAbsoluteCmd(t *testing.T) { func TestCreateStarterAbsoluteCmd(t *testing.T) {
defer resetEnv()() defer resetEnv()()
ensure.HelmHome(t) defer ensure.HelmHome(t)()
defer ensure.CleanHomeDirs(t)
cname := "testchart" cname := "testchart"
// Create a starter. // Create a starter.
starterchart := helmpath.Starters() starterchart := helmpath.DataPath("starters")
os.Mkdir(starterchart, 0755) os.MkdirAll(starterchart, 0755)
if dest, err := chartutil.Create("starterchart", starterchart); err != nil { if dest, err := chartutil.Create("starterchart", starterchart); err != nil {
t.Fatalf("Could not create chart: %s", err) t.Fatalf("Could not create chart: %s", err)
} else { } else {
t.Logf("Created %s", dest) t.Logf("Created %s", dest)
} }
tplpath := filepath.Join(starterchart, "starterchart", "templates", "foo.tpl") 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) t.Fatalf("Could not write template: %s", err)
} }
os.MkdirAll(helmpath.CachePath(), 0755)
defer testChdir(t, helmpath.CachePath())() defer testChdir(t, helmpath.CachePath())()
starterChartPath := filepath.Join(starterchart, "starterchart") starterChartPath := filepath.Join(starterchart, "starterchart")

@ -58,13 +58,13 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
ChartPath: chartpath, ChartPath: chartpath,
Keyring: client.Keyring, Keyring: client.Keyring,
Getters: getter.All(settings), Getters: getter.All(settings),
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
} }
if client.Verify { if client.Verify {
man.Verify = downloader.VerifyIfPossible man.Verify = downloader.VerifyIfPossible
} }
if settings.Debug {
man.Debug = true
}
return man.Build() return man.Build()
}, },
} }

@ -22,31 +22,26 @@ import (
"strings" "strings"
"testing" "testing"
"helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/provenance" "helm.sh/helm/pkg/provenance"
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
"helm.sh/helm/pkg/repo/repotest" "helm.sh/helm/pkg/repo/repotest"
) )
func TestDependencyBuildCmd(t *testing.T) { func TestDependencyBuildCmd(t *testing.T) {
defer resetEnv()() srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz")
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
srv := repotest.NewServer(helmpath.ConfigPath())
defer srv.Stop() defer srv.Stop()
if _, err := srv.CopyCharts("testdata/testcharts/*.tgz"); err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
rootDir := srv.Root()
srv.LinkIndices()
chartname := "depbuild" chartname := "depbuild"
if err := createTestingChart(helmpath.DataPath(), chartname, srv.URL()); err != nil { createTestingChart(t, rootDir, chartname, srv.URL())
t.Fatal(err) 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) _, out, err := executeActionCommand(cmd)
// In the first pass, we basically want the same results as an update. // 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. // 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 { if _, err := os.Stat(expect); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// In the second pass, we want to remove the chart's request dependency, // In the second pass, we want to remove the chart's request dependency,
// then see if it restores from the lock. // 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 { if _, err := os.Stat(lockfile); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -82,7 +77,6 @@ func TestDependencyBuildCmd(t *testing.T) {
} }
// Now repeat the test that the dependency exists. // 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 { if _, err := os.Stat(expect); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -93,7 +87,7 @@ func TestDependencyBuildCmd(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
i, err := repo.LoadIndexFile(helmpath.CacheIndex("test")) i, err := repo.LoadIndexFile(filepath.Join(rootDir, "index.yaml"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -63,13 +63,13 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
Keyring: client.Keyring, Keyring: client.Keyring,
SkipUpdate: client.SkipRefresh, SkipUpdate: client.SkipRefresh,
Getters: getter.All(settings), Getters: getter.All(settings),
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
} }
if client.Verify { if client.Verify {
man.Verify = downloader.VerifyAlways man.Verify = downloader.VerifyAlways
} }
if settings.Debug {
man.Debug = true
}
return man.Update() return man.Update()
}, },
} }

@ -33,28 +33,31 @@ import (
) )
func TestDependencyUpdateCmd(t *testing.T) { func TestDependencyUpdateCmd(t *testing.T) {
defer resetEnv()() srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz")
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
srv := repotest.NewServer(helmpath.ConfigPath())
defer srv.Stop()
copied, err := srv.CopyCharts("testdata/testcharts/*.tgz")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Logf("Copied charts:\n%s", strings.Join(copied, "\n")) defer srv.Stop()
t.Logf("Listening on directory %s", srv.Root()) 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" chartname := "depup"
ch := createTestingMetadata(chartname, srv.URL()) ch := createTestingMetadata(chartname, srv.URL())
md := ch.Metadata md := ch.Metadata
if err := chartutil.SaveDir(ch, helmpath.DataPath()); err != nil { if err := chartutil.SaveDir(ch, dir()); err != nil {
t.Fatal(err) 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 { if err != nil {
t.Logf("Output: %s", out) t.Logf("Output: %s", out)
t.Fatal(err) t.Fatal(err)
@ -66,7 +69,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
} }
// Make sure the actual file got downloaded. // 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 { if _, err := os.Stat(expect); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -76,7 +79,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
i, err := repo.LoadIndexFile(helmpath.CacheIndex("test")) i, err := repo.LoadIndexFile(dir(helmpath.CacheIndexFile("test")))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -92,12 +95,11 @@ func TestDependencyUpdateCmd(t *testing.T) {
{Name: "reqtest", Version: "0.1.0", Repository: srv.URL()}, {Name: "reqtest", Version: "0.1.0", Repository: srv.URL()},
{Name: "compressedchart", Version: "0.3.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(chartname, "Chart.yaml"), md); err != nil {
if err := chartutil.SaveChartfile(dir, md); err != nil {
t.Fatal(err) 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 { if err != nil {
t.Logf("Output: %s", out) t.Logf("Output: %s", out)
t.Fatal(err) 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 // In this second run, we should see compressedchart-0.3.0.tgz, and not
// the 0.1.0 version. // 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 { if _, err := os.Stat(expect); err != nil {
t.Fatalf("Expected %q: %s", expect, err) 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 { if _, err := os.Stat(dontExpect); err == nil {
t.Fatalf("Unexpected %q", dontExpect) t.Fatalf("Unexpected %q", dontExpect)
} }
@ -117,9 +119,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) { func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) {
defer resetEnv()() defer resetEnv()()
defer ensure.HelmHome(t)()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
srv := repotest.NewServer(helmpath.ConfigPath()) srv := repotest.NewServer(helmpath.ConfigPath())
defer srv.Stop() defer srv.Stop()
@ -131,11 +131,9 @@ func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) {
t.Logf("Listening on directory %s", srv.Root()) t.Logf("Listening on directory %s", srv.Root())
chartname := "depup" chartname := "depup"
if err := createTestingChart(helmpath.DataPath(), chartname, srv.URL()); err != nil { createTestingChart(t, helmpath.DataPath(), chartname, srv.URL())
t.Fatal(err)
}
_, 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 { if err == nil {
t.Fatal("Expected failure to find the repo with skipRefresh") 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) { func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
defer resetEnv()() defer resetEnv()()
defer ensure.HelmHome(t)()
ensure.HelmHome(t) srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz")
defer ensure.CleanHomeDirs(t)
srv := repotest.NewServer(helmpath.ConfigPath())
defer srv.Stop()
copied, err := srv.CopyCharts("testdata/testcharts/*.tgz")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
t.Logf("Copied charts:\n%s", strings.Join(copied, "\n")) defer srv.Stop()
t.Logf("Listening on directory %s", srv.Root()) t.Logf("Listening on directory %s", srv.Root())
chartname := "depupdelete" if err := srv.LinkIndices(); err != nil {
if err := createTestingChart(helmpath.DataPath(), chartname, srv.URL()); err != nil {
t.Fatal(err) 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 { if err != nil {
t.Logf("Output: %s", output) t.Logf("Output: %s", output)
t.Fatal(err) t.Fatal(err)
@ -175,14 +175,14 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
// Chart repo is down // Chart repo is down
srv.Stop() 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 { if err == nil {
t.Logf("Output: %s", output) t.Logf("Output: %s", output)
t.Fatal("Expected error, got nil") t.Fatal("Expected error, got nil")
} }
// Make sure charts dir still has dependencies // 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -198,7 +198,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
} }
// Make sure tmpcharts is deleted // 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") 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 // createTestingChart creates a basic chart that depends on reqtest-0.1.0
// //
// The baseURL can be used to point to a particular repository server. // 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) cfile := createTestingMetadata(name, baseURL)
return chartutil.SaveDir(cfile, dest) if err := chartutil.SaveDir(cfile, dest); err != nil {
t.Fatal(err)
}
} }

@ -44,7 +44,7 @@ import (
const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI") const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI")
var ( var (
settings cli.EnvSettings settings = cli.New()
config genericclioptions.RESTClientGetter config genericclioptions.RESTClientGetter
configOnce sync.Once configOnce sync.Once
) )

@ -42,11 +42,6 @@ func init() {
action.Timestamper = testTimestamper action.Timestamper = testTimestamper
} }
func TestMain(m *testing.M) {
exitCode := m.Run()
os.Exit(exitCode)
}
func runTestCmd(t *testing.T, tests []cmdTestCase) { func runTestCmd(t *testing.T, tests []cmdTestCase) {
t.Helper() t.Helper()
for _, tt := range tests { for _, tt := range tests {
@ -59,6 +54,7 @@ func runTestCmd(t *testing.T, tests []cmdTestCase) {
t.Fatal(err) t.Fatal(err)
} }
} }
t.Log("running cmd: ", tt.cmd)
_, out, err := executeActionCommandC(storage, tt.cmd) _, out, err := executeActionCommandC(storage, tt.cmd)
if (err != nil) != tt.wantError { if (err != nil) != tt.wantError {
t.Errorf("expected error, got '%v'", err) t.Errorf("expected error, got '%v'", err)

@ -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
}

@ -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")
}
}

@ -174,7 +174,8 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
debug("CHART PATH: %s\n", cp) debug("CHART PATH: %s\n", cp)
vals, err := valueOpts.MergeValues(settings) p := getter.All(settings)
vals, err := valueOpts.MergeValues(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -201,7 +202,9 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
ChartPath: cp, ChartPath: cp,
Keyring: client.ChartPathOptions.Keyring, Keyring: client.ChartPathOptions.Keyring,
SkipUpdate: false, SkipUpdate: false,
Getters: getter.All(settings), Getters: p,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
} }
if err := man.Update(); err != nil { if err := man.Update(); err != nil {
return nil, err return nil, err

@ -26,6 +26,7 @@ import (
"helm.sh/helm/pkg/action" "helm.sh/helm/pkg/action"
"helm.sh/helm/pkg/cli/values" "helm.sh/helm/pkg/cli/values"
"helm.sh/helm/pkg/getter"
) )
var longLintHelp = ` var longLintHelp = `
@ -51,7 +52,7 @@ func newLintCmd(out io.Writer) *cobra.Command {
paths = args paths = args
} }
client.Namespace = getNamespace() client.Namespace = getNamespace()
vals, err := valueOpts.MergeValues(settings) vals, err := valueOpts.MergeValues(getter.All(settings))
if err != nil { if err != nil {
return err return err
} }

@ -26,7 +26,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/plugin" "helm.sh/helm/pkg/plugin"
) )
@ -42,7 +41,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
return return
} }
found, err := findPlugins(helmpath.Plugins()) found, err := findPlugins(settings.PluginsDirectory)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err) fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err)
return return

@ -62,7 +62,10 @@ func newPackageCmd(out io.Writer) *cobra.Command {
return errors.New("--keyring is required for signing a package") 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 { if err != nil {
return err return err
} }
@ -78,8 +81,10 @@ func newPackageCmd(out io.Writer) *cobra.Command {
Out: ioutil.Discard, Out: ioutil.Discard,
ChartPath: path, ChartPath: path,
Keyring: client.Keyring, Keyring: client.Keyring,
Getters: getter.All(settings), Getters: p,
Debug: settings.Debug, Debug: settings.Debug,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
} }
if err := downloadManager.Update(); err != nil { if err := downloadManager.Update(); err != nil {

@ -32,19 +32,14 @@ import (
"helm.sh/helm/pkg/chart" "helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/chart/loader" "helm.sh/helm/pkg/chart/loader"
"helm.sh/helm/pkg/chartutil" "helm.sh/helm/pkg/chartutil"
"helm.sh/helm/pkg/helmpath"
) )
func TestPackage(t *testing.T) { func TestPackage(t *testing.T) {
statExe := "stat"
statFileMsg := "no such file or directory" statFileMsg := "no such file or directory"
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
statExe = "FindFirstFile"
statFileMsg = "The system cannot find the file specified." statFileMsg = "The system cannot find the file specified."
} }
defer resetEnv()()
tests := []struct { tests := []struct {
name string name string
flags map[string]string flags map[string]string
@ -100,13 +95,6 @@ func TestPackage(t *testing.T) {
expect: "", expect: "",
hasfile: "toot/alpine-0.1.0.tgz", 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", name: "package --sign --key=KEY --keyring=KEYRING testdata/testcharts/alpine",
args: []string{"testdata/testcharts/alpine"}, args: []string{"testdata/testcharts/alpine"},
@ -139,18 +127,16 @@ func TestPackage(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
ensure.HelmHome(t) for _, tt := range tests {
defer ensure.CleanHomeDirs(t) t.Run(tt.name, func(t *testing.T) {
t.Logf("Running tests in %s", helmpath.CachePath()) cachePath := ensure.TempDir(t)
defer testChdir(t, helmpath.CachePath())() defer testChdir(t, cachePath)()
if err := os.Mkdir("toot", 0777); err != nil { if err := os.MkdirAll("toot", 0777); err != nil {
t.Fatal(err) t.Fatal(err)
} }
var buf bytes.Buffer
for _, tt := range tests { c := newPackageCmd(&buf)
buf := bytes.NewBuffer(nil)
c := newPackageCmd(buf)
// This is an unfortunate byproduct of the tmpdir // This is an unfortunate byproduct of the tmpdir
if v, ok := tt.flags["keyring"]; ok && len(v) > 0 { if v, ok := tt.flags["keyring"]; ok && len(v) > 0 {
@ -168,10 +154,9 @@ func TestPackage(t *testing.T) {
err := c.RunE(c, adjustedArgs) err := c.RunE(c, adjustedArgs)
if err != nil { if err != nil {
if tt.err && re.MatchString(err.Error()) { if tt.err && re.MatchString(err.Error()) {
continue return
} }
t.Errorf("%q: expected error %q, got %q", tt.name, tt.expect, err) t.Fatalf("%q: expected error %q, got %q", tt.name, tt.expect, err)
continue
} }
if !re.Match(buf.Bytes()) { if !re.Match(buf.Bytes()) {
@ -193,21 +178,19 @@ func TestPackage(t *testing.T) {
t.Errorf("%q: provenance file is empty", tt.name) t.Errorf("%q: provenance file is empty", tt.name)
} }
} }
})
} }
} }
func TestSetAppVersion(t *testing.T) { func TestSetAppVersion(t *testing.T) {
defer resetEnv()()
var ch *chart.Chart var ch *chart.Chart
expectedAppVersion := "app-version-foo" expectedAppVersion := "app-version-foo"
ensure.HelmHome(t) dir := ensure.TempDir(t)
defer ensure.CleanHomeDirs(t)
c := newPackageCmd(&bytes.Buffer{}) c := newPackageCmd(&bytes.Buffer{})
flags := map[string]string{ flags := map[string]string{
"destination": helmpath.CachePath(), "destination": dir,
"app-version": expectedAppVersion, "app-version": expectedAppVersion,
} }
setFlags(c, flags) setFlags(c, flags)
@ -215,7 +198,7 @@ func TestSetAppVersion(t *testing.T) {
t.Errorf("unexpected error %q", err) 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 { if fi, err := os.Stat(chartPath); err != nil {
t.Errorf("expected file %q, got err %q", chartPath, err) t.Errorf("expected file %q, got err %q", chartPath, err)
} else if fi.Size() == 0 { } else if fi.Size() == 0 {
@ -223,7 +206,7 @@ func TestSetAppVersion(t *testing.T) {
} }
ch, err := loader.Load(chartPath) ch, err := loader.Load(chartPath)
if err != nil { 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 { if ch.Metadata.AppVersion != expectedAppVersion {
t.Errorf("expected app-version %q, found %q", expectedAppVersion, ch.Metadata.AppVersion) 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) { func TestPackageValues(t *testing.T) {
defer resetEnv()() defer resetEnv()()
repoFile := "testdata/helmhome/helm/repositories.yaml"
testCases := []struct { testCases := []struct {
desc string desc string
args []string args []string
@ -243,33 +228,32 @@ func TestPackageValues(t *testing.T) {
{ {
desc: "helm package, single values file", desc: "helm package, single values file",
args: []string{"testdata/testcharts/alpine"}, args: []string{"testdata/testcharts/alpine"},
flags: map[string]string{"repository-config": repoFile},
valuefilesContents: []string{"Name: chart-name-foo"}, valuefilesContents: []string{"Name: chart-name-foo"},
expected: []string{"Name: chart-name-foo"}, expected: []string{"Name: chart-name-foo"},
}, },
{ {
desc: "helm package, multiple values files", desc: "helm package, multiple values files",
args: []string{"testdata/testcharts/alpine"}, args: []string{"testdata/testcharts/alpine"},
flags: map[string]string{"repository-config": repoFile},
valuefilesContents: []string{"Name: chart-name-foo", "foo: bar"}, valuefilesContents: []string{"Name: chart-name-foo", "foo: bar"},
expected: []string{"Name: chart-name-foo", "foo: bar"}, expected: []string{"Name: chart-name-foo", "foo: bar"},
}, },
{ {
desc: "helm package, with set option", desc: "helm package, with set option",
args: []string{"testdata/testcharts/alpine"}, 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"}, expected: []string{"Name: chart-name-foo"},
}, },
{ {
desc: "helm package, set takes precedence over value file", desc: "helm package, set takes precedence over value file",
args: []string{"testdata/testcharts/alpine"}, args: []string{"testdata/testcharts/alpine"},
valuefilesContents: []string{"Name: chart-name-foo"}, 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"}, expected: []string{"Name: chart-name-bar"},
}, },
} }
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
for _, tc := range testCases { for _, tc := range testCases {
var files []string var files []string
for _, contents := range tc.valuefilesContents { for _, contents := range tc.valuefilesContents {
@ -283,27 +267,21 @@ func TestPackageValues(t *testing.T) {
t.Errorf("unexpected error parsing values: %q", err) t.Errorf("unexpected error parsing values: %q", err)
} }
runAndVerifyPackageCommandValues(t, tc.args, tc.flags, valueFiles, expected)
}
}
func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[string]string, valueFiles string, expected chartutil.Values) {
t.Helper()
outputDir := ensure.TempDir(t) outputDir := ensure.TempDir(t)
if len(flags) == 0 { if len(tc.flags) == 0 {
flags = make(map[string]string) tc.flags = make(map[string]string)
} }
flags["destination"] = outputDir tc.flags["destination"] = outputDir
if len(valueFiles) > 0 { if len(valueFiles) > 0 {
flags["values"] = valueFiles tc.flags["values"] = valueFiles
} }
cmd := newPackageCmd(&bytes.Buffer{}) cmd := newPackageCmd(&bytes.Buffer{})
setFlags(cmd, flags) setFlags(cmd, tc.flags)
if err := cmd.RunE(cmd, args); err != nil { if err := cmd.RunE(cmd, tc.args); err != nil {
t.Errorf("unexpected error: %q", err) t.Fatalf("unexpected error: %q", err)
} }
outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz") outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz")
@ -311,30 +289,28 @@ func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[str
actual, err := getChartValues(outputFile) actual, err := getChartValues(outputFile)
if err != nil { if err != nil {
t.Errorf("unexpected error extracting chart values: %q", err) t.Fatalf("unexpected error extracting chart values: %q", err)
} }
verifyValues(t, actual, expected) verifyValues(t, actual, expected)
}
} }
func createValuesFile(t *testing.T, data string) string { func createValuesFile(t *testing.T, data string) string {
outputDir := ensure.TempDir(t) outputDir := ensure.TempDir(t)
outputFile := filepath.Join(outputDir, "values.yaml") 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) t.Fatalf("err: %s", err)
} }
return outputFile return outputFile
} }
func getChartValues(chartPath string) (chartutil.Values, error) { func getChartValues(chartPath string) (chartutil.Values, error) {
chart, err := loader.Load(chartPath) chart, err := loader.Load(chartPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return chart.Values, nil return chart.Values, nil
} }

@ -21,8 +21,6 @@ import (
"github.com/gosuri/uitable" "github.com/gosuri/uitable"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"helm.sh/helm/pkg/helmpath"
) )
func newPluginListCmd(out io.Writer) *cobra.Command { func newPluginListCmd(out io.Writer) *cobra.Command {
@ -30,8 +28,8 @@ func newPluginListCmd(out io.Writer) *cobra.Command {
Use: "list", Use: "list",
Short: "list installed Helm plugins", Short: "list installed Helm plugins",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
debug("pluginDirs: %s", helmpath.Plugins()) debug("pluginDirs: %s", settings.PluginsDirectory)
plugins, err := findPlugins(helmpath.Plugins()) plugins, err := findPlugins(settings.PluginsDirectory)
if err != nil { if err != nil {
return err return err
} }

@ -24,7 +24,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/plugin" "helm.sh/helm/pkg/plugin"
) )
@ -56,8 +55,8 @@ func (o *pluginRemoveOptions) complete(args []string) error {
} }
func (o *pluginRemoveOptions) run(out io.Writer) error { func (o *pluginRemoveOptions) run(out io.Writer) error {
debug("loading installed plugins from %s", helmpath.Plugins()) debug("loading installed plugins from %s", settings.PluginsDirectory)
plugins, err := findPlugins(helmpath.Plugins()) plugins, err := findPlugins(settings.PluginsDirectory)
if err != nil { if err != nil {
return err return err
} }

@ -18,16 +18,11 @@ package main
import ( import (
"bytes" "bytes"
"os" "os"
"path/filepath"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
"github.com/spf13/cobra" "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) { func TestManuallyProcessArgs(t *testing.T) {
@ -63,25 +58,22 @@ func TestManuallyProcessArgs(t *testing.T) {
} }
func TestLoadPlugins(t *testing.T) { func TestLoadPlugins(t *testing.T) {
defer resetEnv()() settings.PluginsDirectory = "testdata/helmhome/helm/plugins"
settings.RepositoryConfig = "testdata/helmhome/helm/repositories.yaml"
os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome") settings.RepositoryCache = "testdata/helmhome/helm/repository"
os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome")
os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome")
out := bytes.NewBuffer(nil) var (
cmd := &cobra.Command{} out bytes.Buffer
loadPlugins(cmd, out) cmd cobra.Command
)
loadPlugins(&cmd, &out)
envs := strings.Join([]string{ envs := strings.Join([]string{
"fullenv", "fullenv",
helmpath.Plugins() + "/fullenv", "testdata/helmhome/helm/plugins/fullenv",
helmpath.Plugins(), "testdata/helmhome/helm/plugins",
helmpath.CachePath(), "testdata/helmhome/helm/repositories.yaml",
helmpath.ConfigPath(), "testdata/helmhome/helm/repository",
helmpath.DataPath(),
helmpath.RepositoryFile(),
helmpath.RepositoryCache(),
os.Args[0], os.Args[0],
}, "\n") }, "\n")
@ -95,7 +87,7 @@ func TestLoadPlugins(t *testing.T) {
}{ }{
{"args", "echo args", "This echos args", "-a -b -c\n", []string{"-a", "-b", "-c"}}, {"args", "echo args", "This echos args", "-a -b -c\n", []string{"-a", "-b", "-c"}},
{"echo", "echo stuff", "This echos stuff", "hello\n", []string{}}, {"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{}}, {"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 // tests until this is fixed
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
if err := pp.RunE(pp, tt.args); err != nil { 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 { if out.String() != tt.expect {
t.Errorf("Expected %s to output:\n%s\ngot\n%s", tt.use, tt.expect, out.String()) 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) { func TestLoadPlugins_HelmNoPlugins(t *testing.T) {
defer resetEnv()() settings.PluginsDirectory = "testdata/helmhome/helm/plugins"
settings.RepositoryConfig = "testdata/helmhome/helm/repository"
os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome")
os.Setenv("HELM_NO_PLUGINS", "1") 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)) 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)
}
}
}

@ -24,7 +24,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/plugin" "helm.sh/helm/pkg/plugin"
"helm.sh/helm/pkg/plugin/installer" "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 { func (o *pluginUpdateOptions) run(out io.Writer) error {
installer.Debug = settings.Debug installer.Debug = settings.Debug
debug("loading installed plugins from %s", helmpath.Plugins()) debug("loading installed plugins from %s", settings.PluginsDirectory)
plugins, err := findPlugins(helmpath.Plugins()) plugins, err := findPlugins(settings.PluginsDirectory)
if err != nil { if err != nil {
return err return err
} }

@ -21,19 +21,12 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings"
"testing" "testing"
"helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo/repotest" "helm.sh/helm/pkg/repo/repotest"
) )
func TestPullCmd(t *testing.T) { func TestPullCmd(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*") srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -47,7 +40,7 @@ func TestPullCmd(t *testing.T) {
// all flags will get "-d outdir" appended. // all flags will get "-d outdir" appended.
tests := []struct { tests := []struct {
name string name string
args []string args string
wantError bool wantError bool
failExpect string failExpect string
expectFile string expectFile string
@ -56,47 +49,47 @@ func TestPullCmd(t *testing.T) {
}{ }{
{ {
name: "Basic chart fetch", name: "Basic chart fetch",
args: []string{"test/signtest"}, args: "test/signtest",
expectFile: "./signtest-0.1.0.tgz", expectFile: "./signtest-0.1.0.tgz",
}, },
{ {
name: "Chart fetch with version", 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", expectFile: "./signtest-0.1.0.tgz",
}, },
{ {
name: "Fail chart fetch with non-existent version", 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, wantError: true,
failExpect: "no such chart", failExpect: "no such chart",
}, },
{ {
name: "Fail fetching non-existent chart", name: "Fail fetching non-existent chart",
args: []string{"test/nosuchthing"}, args: "test/nosuchthing",
failExpect: "Failed to fetch", failExpect: "Failed to fetch",
wantError: true, wantError: true,
}, },
{ {
name: "Fetch and verify", 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", expectFile: "./signtest-0.1.0.tgz",
expectVerify: true, expectVerify: true,
}, },
{ {
name: "Fetch and fail verify", 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", failExpect: "Failed to fetch provenance",
wantError: true, wantError: true,
}, },
{ {
name: "Fetch and untar", name: "Fetch and untar",
args: []string{"test/signtest --untar --untardir signtest"}, args: "test/signtest --untar --untardir signtest",
expectFile: "./signtest", expectFile: "./signtest",
expectDir: true, expectDir: true,
}, },
{ {
name: "Fetch, verify, untar", 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", expectFile: "./signtest",
expectDir: true, expectDir: true,
expectVerify: true, expectVerify: true,
@ -104,40 +97,42 @@ func TestPullCmd(t *testing.T) {
{ {
name: "Chart fetch using repo URL", name: "Chart fetch using repo URL",
expectFile: "./signtest-0.1.0.tgz", 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", 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", failExpect: "Failed to fetch chart",
wantError: true, wantError: true,
}, },
{ {
name: "Specific version chart fetch using repo URL", name: "Specific version chart fetch using repo URL",
expectFile: "./signtest-0.1.0.tgz", 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", 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", failExpect: "Failed to fetch chart version",
wantError: true, wantError: true,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
outdir := filepath.Join(helmpath.DataPath(), "testout") t.Run(tt.name, func(t *testing.T) {
os.RemoveAll(outdir) outdir := srv.Root()
os.Mkdir(outdir, 0755) cmd := fmt.Sprintf("fetch %s -d '%s' --repository-config %s --repository-cache %s ",
tt.args,
cmd := strings.Join(append(tt.args, "-d", "'"+outdir+"'"), " ") outdir,
_, out, err := executeActionCommand("fetch " + cmd) filepath.Join(outdir, "repositories.yaml"),
outdir,
)
_, out, err := executeActionCommand(cmd)
if err != nil { if err != nil {
if tt.wantError { if tt.wantError {
continue return
} }
t.Errorf("%q reported error: %s", tt.name, err) t.Fatalf("%q reported error: %s", tt.name, err)
continue
} }
if tt.expectVerify { if tt.expectVerify {
@ -158,5 +153,6 @@ func TestPullCmd(t *testing.T) {
if fi.IsDir() != tt.expectDir { if fi.IsDir() != tt.expectDir {
t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir) t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir)
} }
})
} }
} }

@ -19,13 +19,15 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v2"
"helm.sh/helm/cmd/helm/require" "helm.sh/helm/cmd/helm/require"
"helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
) )
@ -34,11 +36,14 @@ type repoAddOptions struct {
url string url string
username string username string
password string password string
noupdate bool noUpdate bool
certFile string certFile string
keyFile string keyFile string
caFile string caFile string
repoFile string
repoCache string
} }
func newRepoAddCmd(out io.Writer) *cobra.Command { 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 { RunE: func(cmd *cobra.Command, args []string) error {
o.name = args[0] o.name = args[0]
o.url = args[1] o.url = args[1]
o.repoFile = settings.RepositoryConfig
o.repoCache = settings.RepositoryCache
return o.run(out) return o.run(out)
}, },
@ -59,7 +66,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
f := cmd.Flags() f := cmd.Flags()
f.StringVar(&o.username, "username", "", "chart repository username") f.StringVar(&o.username, "username", "", "chart repository username")
f.StringVar(&o.password, "password", "", "chart repository password") 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.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.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") 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 { 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 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 { var f repo.File
f, err := repo.LoadFile(helmpath.RepositoryFile()) if err := yaml.Unmarshal(b, &f); err != nil {
if err != nil {
return err return err
} }
if noUpdate && f.Has(name) { if o.noUpdate && f.Has(o.name) {
return errors.Errorf("repository name (%s) already exists, please specify a different name", name) return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name)
} }
c := repo.Entry{ c := repo.Entry{
Name: name, Name: o.name,
URL: url, URL: o.url,
Username: username, Username: o.username,
Password: password, Password: o.password,
CertFile: certFile, CertFile: o.certFile,
KeyFile: keyFile, KeyFile: o.keyFile,
CAFile: caFile, CAFile: o.caFile,
} }
r, err := repo.NewChartRepository(&c, getter.All(settings)) r, err := repo.NewChartRepository(&c, getter.All(settings))
@ -100,11 +104,15 @@ func addRepository(name, url, username, password string, certFile, keyFile, caFi
return err return err
} }
if err := r.DownloadIndexFile(); err != nil { if _, err := r.DownloadIndexFile(); err != nil {
return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url) return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", o.url)
} }
f.Update(&c) 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
} }

@ -18,47 +18,27 @@ package main
import ( import (
"fmt" "fmt"
"os" "io/ioutil"
"path/filepath"
"testing" "testing"
"helm.sh/helm/internal/test/ensure" "helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
"helm.sh/helm/pkg/repo/repotest" "helm.sh/helm/pkg/repo/repotest"
) )
func TestRepoAddCmd(t *testing.T) { func TestRepoAddCmd(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
srv, err := repotest.NewTempServer("testdata/testserver/*.*") srv, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer srv.Stop() defer srv.Stop()
repoFile := helmpath.RepositoryFile() repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml")
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)
}
}
tests := []cmdTestCase{{ tests := []cmdTestCase{{
name: "add a repository", 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", golden: "output/repo-add.txt",
}} }}
@ -66,54 +46,43 @@ func TestRepoAddCmd(t *testing.T) {
} }
func TestRepoAdd(t *testing.T) { func TestRepoAdd(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
ts, err := repotest.NewTempServer("testdata/testserver/*.*") ts, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer ts.Stop() defer ts.Stop()
repoFile := helmpath.RepositoryFile() repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml")
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)
}
}
const testRepoName = "test-name" 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) t.Error(err)
} }
f, err := repo.LoadFile(helmpath.RepositoryFile()) f, err := repo.LoadFile(repoFile)
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
} }
if !f.Has(testRepoName) { 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) 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") t.Errorf("Duplicate repository name was added")
} }
} }

@ -87,7 +87,7 @@ func index(dir, url, mergeTo string) error {
var i2 *repo.IndexFile var i2 *repo.IndexFile
if _, err := os.Stat(mergeTo); os.IsNotExist(err) { if _, err := os.Stat(mergeTo); os.IsNotExist(err) {
i2 = repo.NewIndexFile() i2 = repo.NewIndexFile()
i2.WriteFile(mergeTo, 0755) i2.WriteFile(mergeTo, 0644)
} else { } else {
i2, err = repo.LoadIndexFile(mergeTo) i2, err = repo.LoadIndexFile(mergeTo)
if err != nil { if err != nil {
@ -97,5 +97,5 @@ func index(dir, url, mergeTo string) error {
i.Merge(i2) i.Merge(i2)
} }
i.SortEntries() i.SortEntries()
return i.WriteFile(out, 0755) return i.WriteFile(out, 0644)
} }

@ -25,7 +25,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"helm.sh/helm/cmd/helm/require" "helm.sh/helm/cmd/helm/require"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
) )
@ -35,7 +34,7 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
Short: "list chart repositories", Short: "list chart repositories",
Args: require.NoArgs, Args: require.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
f, err := repo.LoadFile(helmpath.RepositoryFile()) f, err := repo.LoadFile(settings.RepositoryConfig)
if err != nil { if err != nil {
return err return err
} }

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"path/filepath"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -29,49 +30,57 @@ import (
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
) )
type repoRemoveOptions struct {
name string
repoFile string
repoCache string
}
func newRepoRemoveCmd(out io.Writer) *cobra.Command { func newRepoRemoveCmd(out io.Writer) *cobra.Command {
o := &repoRemoveOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "remove [NAME]", Use: "remove [NAME]",
Aliases: []string{"rm"}, Aliases: []string{"rm"},
Short: "remove a chart repository", Short: "remove a chart repository",
Args: require.ExactArgs(1), Args: require.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { 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 return cmd
} }
func removeRepoLine(out io.Writer, name string) error { func (o *repoRemoveOptions) run(out io.Writer) error {
repoFile := helmpath.RepositoryFile() r, err := repo.LoadFile(o.repoFile)
r, err := repo.LoadFile(repoFile)
if err != nil { if err != nil {
return err return err
} }
if !r.Remove(name) { if !r.Remove(o.name) {
return errors.Errorf("no repo named %q found", 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 return err
} }
if err := removeRepoCache(name); err != nil { if err := removeRepoCache(o.repoCache, o.name); err != nil {
return err 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 return nil
} }
func removeRepoCache(name string) error { func removeRepoCache(root, name string) error {
if _, err := os.Stat(helmpath.CacheIndex(name)); err == nil { idx := filepath.Join(root, helmpath.CacheIndexFile(name))
err = os.Remove(helmpath.CacheIndex(name)) if _, err := os.Stat(idx); os.IsNotExist(err) {
if err != nil {
return err
}
}
return nil return nil
} else if err != nil {
return errors.Wrapf(err, "can't remove index file %s", idx)
}
return os.Remove(idx)
} }

@ -19,6 +19,7 @@ package main
import ( import (
"bytes" "bytes"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
@ -29,60 +30,57 @@ import (
) )
func TestRepoRemove(t *testing.T) { func TestRepoRemove(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
ts, err := repotest.NewTempServer("testdata/testserver/*.*") ts, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer ts.Stop() defer ts.Stop()
repoFile := helmpath.RepositoryFile() rootDir := ensure.TempDir(t)
if _, err := os.Stat(repoFile); err != nil { repoFile := filepath.Join(rootDir, "repositories.yaml")
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)
}
}
const testRepoName = "test-name" const testRepoName = "test-name"
b := bytes.NewBuffer(nil) 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) 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) t.Error(err)
} }
mf, _ := os.Create(helmpath.CacheIndex(testRepoName)) idx := filepath.Join(rootDir, helmpath.CacheIndexFile(testRepoName))
mf, _ := os.Create(idx)
mf.Close() mf.Close()
b.Reset() b.Reset()
if err := removeRepoLine(b, testRepoName); err != nil {
if err := rmOpts.run(b); err != nil {
t.Errorf("Error removing %s from repositories", testRepoName) t.Errorf("Error removing %s from repositories", testRepoName)
} }
if !strings.Contains(b.String(), "has been removed") { if !strings.Contains(b.String(), "has been removed") {
t.Errorf("Unexpected output: %s", b.String()) 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) 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 { if err != nil {
t.Error(err) t.Error(err)
} }

@ -26,7 +26,6 @@ import (
"helm.sh/helm/cmd/helm/require" "helm.sh/helm/cmd/helm/require"
"helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
) )
@ -39,6 +38,7 @@ var errNoRepositories = errors.New("no repositories found. You must add one befo
type repoUpdateOptions struct { type repoUpdateOptions struct {
update func([]*repo.ChartRepository, io.Writer) update func([]*repo.ChartRepository, io.Writer)
repoFile string
} }
func newRepoUpdateCmd(out io.Writer) *cobra.Command { 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 { func (o *repoUpdateOptions) run(out io.Writer) error {
f, err := repo.LoadFile(helmpath.RepositoryFile()) f, err := repo.LoadFile(o.repoFile)
if err != nil { if err != nil {
return err return err
} }
@ -86,7 +86,7 @@ func updateCharts(repos []*repo.ChartRepository, out io.Writer) {
wg.Add(1) wg.Add(1)
go func(re *repo.ChartRepository) { go func(re *repo.ChartRepository) {
defer wg.Done() 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) 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 { } else {
fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", re.Config.Name) fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", re.Config.Name)

@ -19,12 +19,9 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"os"
"strings" "strings"
"testing" "testing"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/internal/test/ensure" "helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
@ -32,29 +29,7 @@ import (
) )
func TestUpdateCmd(t *testing.T) { func TestUpdateCmd(t *testing.T) {
defer resetEnv()() var out bytes.Buffer
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)
// Instead of using the HTTP updater, we provide our own for this test. // Instead of using the HTTP updater, we provide our own for this test.
// The TestUpdateCharts test verifies the HTTP behavior independently. // The TestUpdateCharts test verifies the HTTP behavior independently.
updater := func(repos []*repo.ChartRepository, out io.Writer) { updater := func(repos []*repo.ChartRepository, out io.Writer) {
@ -64,8 +39,9 @@ func TestUpdateCmd(t *testing.T) {
} }
o := &repoUpdateOptions{ 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) t.Fatal(err)
} }
@ -76,9 +52,7 @@ func TestUpdateCmd(t *testing.T) {
func TestUpdateCharts(t *testing.T) { func TestUpdateCharts(t *testing.T) {
defer resetEnv()() defer resetEnv()()
defer ensure.HelmHome(t)()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
ts, err := repotest.NewTempServer("testdata/testserver/*.*") ts, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil { if err != nil {

@ -154,7 +154,6 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
newUpgradeCmd(actionConfig, out), newUpgradeCmd(actionConfig, out),
newCompletionCmd(out), newCompletionCmd(out),
newInitCmd(out),
newPluginCmd(out), newPluginCmd(out),
newVersionCmd(out), newVersionCmd(out),

@ -59,8 +59,7 @@ func TestRootCmd(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
ensure.HelmHome(t) defer ensure.HelmHome(t)()
defer ensure.CleanHomeDirs(t)
for k, v := range tt.envars { for k, v := range tt.envars {
os.Setenv(k, v) os.Setenv(k, v)

@ -19,6 +19,7 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"path/filepath"
"strings" "strings"
"github.com/Masterminds/semver" "github.com/Masterminds/semver"
@ -47,6 +48,8 @@ type searchRepoOptions struct {
regexp bool regexp bool
version string version string
maxColWidth uint maxColWidth uint
repoFile string
repoCacheDir string
} }
func newSearchRepoCmd(out io.Writer) *cobra.Command { 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", Short: "search repositories for a keyword in charts",
Long: searchRepoDesc, Long: searchRepoDesc,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
o.repoFile = settings.RepositoryConfig
o.repoCacheDir = settings.RepositoryCache
return o.run(out, args) 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) { func (o *searchRepoOptions) buildIndex(out io.Writer) (*search.Index, error) {
// Load the repositories.yaml // Load the repositories.yaml
rf, err := repo.LoadFile(helmpath.RepositoryFile()) rf, err := repo.LoadFile(o.repoFile)
if err != nil { if err != nil {
return nil, err return nil, errors.Wrap(err, "loading repository config")
} }
i := search.NewIndex() i := search.NewIndex()
for _, re := range rf.Repositories { for _, re := range rf.Repositories {
n := re.Name n := re.Name
f := helmpath.CacheIndex(n) f := filepath.Join(o.repoCacheDir, helmpath.CacheIndexFile(n))
ind, err := repo.LoadIndexFile(f) ind, err := repo.LoadIndexFile(f)
if err != nil { if err != nil {
// TODO should print to stderr // TODO should print to stderr

@ -17,23 +17,17 @@ limitations under the License.
package main package main
import ( import (
"os"
"testing" "testing"
"helm.sh/helm/pkg/helmpath/xdg"
) )
func TestSearchRepositoriesCmd(t *testing.T) { func TestSearchRepositoriesCmd(t *testing.T) {
defer resetEnv()() repoFile := "testdata/helmhome/helm/repositories.yaml"
repoCache := "testdata/helmhome/helm/repository"
os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome")
os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome")
os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome")
tests := []cmdTestCase{{ tests := []cmdTestCase{{
name: "search for 'maria', expect one match", name: "search for 'alpine', expect two matches",
cmd: "search repo maria", cmd: "search repo alpine",
golden: "output/search-single.txt", golden: "output/search-multiple.txt",
}, { }, {
name: "search for 'alpine', expect two matches", name: "search for 'alpine', expect two matches",
cmd: "search repo alpine", cmd: "search repo alpine",
@ -71,5 +65,13 @@ func TestSearchRepositoriesCmd(t *testing.T) {
cmd: "search repo alp[ --regexp", cmd: "search repo alp[ --regexp",
wantError: true, 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) runTestCmd(t, tests)
} }

@ -1,4 +1,4 @@
name: env name: env
usage: "env stuff" usage: "env stuff"
description: "show the env" description: "show the env"
command: "echo $HELM_PATH_CONFIG" command: "echo $HELM_PLUGIN_NAME"

@ -2,9 +2,6 @@
echo $HELM_PLUGIN_NAME echo $HELM_PLUGIN_NAME
echo $HELM_PLUGIN_DIR echo $HELM_PLUGIN_DIR
echo $HELM_PLUGIN echo $HELM_PLUGIN
echo $HELM_PATH_CACHE
echo $HELM_PATH_CONFIG
echo $HELM_PATH_DATA
echo $HELM_PATH_REPOSITORY_FILE echo $HELM_PATH_REPOSITORY_FILE
echo $HELM_PATH_REPOSITORY_CACHE echo $HELM_PATH_REPOSITORY_CACHE
echo $HELM_BIN echo $HELM_BIN

@ -28,6 +28,7 @@ import (
"helm.sh/helm/pkg/action" "helm.sh/helm/pkg/action"
"helm.sh/helm/pkg/chart/loader" "helm.sh/helm/pkg/chart/loader"
"helm.sh/helm/pkg/cli/values" "helm.sh/helm/pkg/cli/values"
"helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/storage/driver" "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" client.Version = ">0.0.0-0"
} }
vals, err := valueOpts.MergeValues(settings) vals, err := valueOpts.MergeValues(getter.All(settings))
if err != nil { if err != nil {
return err return err
} }

@ -21,7 +21,6 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"path/filepath"
"sort" "sort"
auth "github.com/deislabs/oras/pkg/auth/docker" auth "github.com/deislabs/oras/pkg/auth/docker"
@ -60,7 +59,7 @@ func NewClient(opts ...ClientOption) (*Client, error) {
} }
// set defaults if fields are missing // set defaults if fields are missing
if client.authorizer == nil { if client.authorizer == nil {
credentialsFile := filepath.Join(helmpath.Registry(), CredentialsFileBasename) credentialsFile := helmpath.CachePath("registry", CredentialsFileBasename)
authClient, err := auth.NewClient(credentialsFile) authClient, err := auth.NewClient(credentialsFile)
if err != nil { if err != nil {
return nil, err return nil, err
@ -82,7 +81,7 @@ func NewClient(opts ...ClientOption) (*Client, error) {
cache, err := NewCache( cache, err := NewCache(
CacheOptDebug(client.debug), CacheOptDebug(client.debug),
CacheOptWriter(client.out), CacheOptWriter(client.out),
CacheOptRoot(filepath.Join(helmpath.Registry(), CacheRootDir)), CacheOptRoot(helmpath.CachePath("registry", CacheRootDir)),
) )
if err != nil { if err != nil {
return nil, err return nil, err

@ -35,12 +35,14 @@ import (
// Resolver resolves dependencies from semantic version ranges to a particular version. // Resolver resolves dependencies from semantic version ranges to a particular version.
type Resolver struct { type Resolver struct {
chartpath string chartpath string
cachepath string
} }
// New creates a new resolver for a given chart and a given helm home. // 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{ return &Resolver{
chartpath: chartpath, 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) 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 { 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] vs, ok := repoIndex.Entries[d.Name]

@ -16,15 +16,12 @@ limitations under the License.
package resolver package resolver
import ( import (
"os"
"testing" "testing"
"helm.sh/helm/pkg/chart" "helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/helmpath/xdg"
) )
func TestResolve(t *testing.T) { func TestResolve(t *testing.T) {
os.Setenv(xdg.CacheHomeEnvVar, "testdata")
tests := []struct { tests := []struct {
name string name string
req []*chart.Dependency req []*chart.Dependency
@ -91,7 +88,7 @@ func TestResolve(t *testing.T) {
} }
repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"} repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"}
r := New("testdata/chartpath") r := New("testdata/chartpath", "testdata/repository")
for _, tt := range tests { for _, tt := range tests {
l, err := r.Resolve(tt.req, repoNames) l, err := r.Resolve(tt.req, repoNames)
if err != nil { if err != nil {

@ -21,55 +21,18 @@ import (
"os" "os"
"testing" "testing"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/helmpath/xdg" "helm.sh/helm/pkg/helmpath/xdg"
) )
// HelmHome sets up a Helm Home in a temp dir. // HelmHome sets up a Helm Home in a temp dir.
func HelmHome(t *testing.T) { func HelmHome(t *testing.T) func() {
t.Helper() t.Helper()
cachePath := TempDir(t) base := TempDir(t)
configPath := TempDir(t) os.Setenv(xdg.CacheHomeEnvVar, base)
dataPath := TempDir(t) os.Setenv(xdg.ConfigHomeEnvVar, base)
os.Setenv(xdg.CacheHomeEnvVar, cachePath) os.Setenv(xdg.DataHomeEnvVar, base)
os.Setenv(xdg.ConfigHomeEnvVar, configPath) return func() {
os.Setenv(xdg.DataHomeEnvVar, dataPath) os.RemoveAll(base)
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)
}
} }
} }

@ -37,7 +37,6 @@ import (
"helm.sh/helm/pkg/downloader" "helm.sh/helm/pkg/downloader"
"helm.sh/helm/pkg/engine" "helm.sh/helm/pkg/engine"
"helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/helmpath"
kubefake "helm.sh/helm/pkg/kube/fake" kubefake "helm.sh/helm/pkg/kube/fake"
"helm.sh/helm/pkg/release" "helm.sh/helm/pkg/release"
"helm.sh/helm/pkg/releaseutil" "helm.sh/helm/pkg/releaseutil"
@ -563,8 +562,8 @@ OUTER:
// - if path is absolute or begins with '.', error out here // - if path is absolute or begins with '.', error out here
// - URL // - URL
// //
// If 'verify' is true, this will attempt to also verify the chart. // 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) { func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (string, error) {
name = strings.TrimSpace(name) name = strings.TrimSpace(name)
version := strings.TrimSpace(c.Version) version := strings.TrimSpace(c.Version)
@ -604,11 +603,11 @@ func (c *ChartPathOptions) LocateChart(name string, settings cli.EnvSettings) (s
name = chartURL name = chartURL
} }
if _, err := os.Stat(helmpath.Archive()); os.IsNotExist(err) { if err := os.MkdirAll(settings.RepositoryCache, 0755); err != nil {
os.MkdirAll(helmpath.Archive(), 0744) return "", err
} }
filename, _, err := dl.DownloadTo(name, version, helmpath.Archive()) filename, _, err := dl.DownloadTo(name, version, settings.RepositoryCache)
if err == nil { if err == nil {
lname, err := filepath.Abs(filename) lname, err := filepath.Abs(filename)
if err != nil { if err != nil {

@ -43,6 +43,9 @@ type Package struct {
AppVersion string AppVersion string
Destination string Destination string
DependencyUpdate bool DependencyUpdate bool
RepositoryConfig string
RepositoryCache string
} }
// NewPackage creates a new Package object with the given configuration. // NewPackage creates a new Package object with the given configuration.
@ -131,7 +134,7 @@ func (p *Package) Clearsign(filename string) error {
return err return err
} }
return ioutil.WriteFile(filename+".prov", []byte(sig), 0755) return ioutil.WriteFile(filename+".prov", []byte(sig), 0644)
} }
// promptUser implements provenance.PassphraseFetcher // promptUser implements provenance.PassphraseFetcher

@ -38,7 +38,7 @@ import (
type Pull struct { type Pull struct {
ChartPathOptions ChartPathOptions
Settings cli.EnvSettings // TODO: refactor this out of pkg/action Settings *cli.EnvSettings // TODO: refactor this out of pkg/action
Devel bool Devel bool
Untar bool Untar bool
@ -64,6 +64,8 @@ func (p *Pull) Run(chartRef string) (string, error) {
Options: []getter.Option{ Options: []getter.Option{
getter.WithBasicAuth(p.Username, p.Password), getter.WithBasicAuth(p.Username, p.Password),
}, },
RepositoryConfig: p.Settings.RepositoryConfig,
RepositoryCache: p.Settings.RepositoryCache,
} }
if p.Verify { if p.Verify {

@ -42,17 +42,19 @@ const (
// IgnorefileName is the name of the Helm ignore file. // IgnorefileName is the name of the Helm ignore file.
IgnorefileName = ".helmignore" IgnorefileName = ".helmignore"
// IngressFileName is the name of the example ingress file. // 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 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 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 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 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 const defaultChartfile = `apiVersion: v2
name: %s name: %s
description: A Helm chart for Kubernetes description: A Helm chart for Kubernetes
@ -345,12 +347,12 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
schart.Templates = updatedTemplates schart.Templates = updatedTemplates
b, err := yaml.Marshal(schart.Values) b, err := yaml.Marshal(schart.Values)
if err != nil { if err != nil {
return err return errors.Wrap(err, "reading values file")
} }
var m map[string]interface{} var m map[string]interface{}
if err := yaml.Unmarshal(transform(string(b), schart.Name()), &m); err != nil { if err := yaml.Unmarshal(transform(string(b), schart.Name()), &m); err != nil {
return err return errors.Wrap(err, "transforming values file")
} }
schart.Values = m schart.Values = m
@ -386,15 +388,6 @@ func Create(name, dir string) (string, error) {
if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() { if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() {
return cdir, errors.Errorf("file %s already exists and is not a directory", cdir) 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 { files := []struct {
path string path string
@ -417,27 +410,27 @@ func Create(name, dir string) (string, error) {
}, },
{ {
// ingress.yaml // ingress.yaml
path: filepath.Join(cdir, TemplatesDir, IngressFileName), path: filepath.Join(cdir, IngressFileName),
content: transform(defaultIngress, name), content: transform(defaultIngress, name),
}, },
{ {
// deployment.yaml // deployment.yaml
path: filepath.Join(cdir, TemplatesDir, DeploymentName), path: filepath.Join(cdir, DeploymentName),
content: transform(defaultDeployment, name), content: transform(defaultDeployment, name),
}, },
{ {
// service.yaml // service.yaml
path: filepath.Join(cdir, TemplatesDir, ServiceName), path: filepath.Join(cdir, ServiceName),
content: transform(defaultService, name), content: transform(defaultService, name),
}, },
{ {
// NOTES.txt // NOTES.txt
path: filepath.Join(cdir, TemplatesDir, NotesName), path: filepath.Join(cdir, NotesName),
content: transform(defaultNotes, name), content: transform(defaultNotes, name),
}, },
{ {
// _helpers.tpl // _helpers.tpl
path: filepath.Join(cdir, TemplatesDir, HelpersName), path: filepath.Join(cdir, HelpersName),
content: transform(defaultHelpers, name), content: transform(defaultHelpers, name),
}, },
} }
@ -447,7 +440,7 @@ func Create(name, dir string) (string, error) {
// File exists and is okay. Skip it. // File exists and is okay. Skip it.
continue continue
} }
if err := ioutil.WriteFile(file.path, file.content, 0644); err != nil { if err := writeFile(file.path, file.content); err != nil {
return cdir, err return cdir, err
} }
} }
@ -459,3 +452,10 @@ func Create(name, dir string) (string, error) {
func transform(src, replacement string) []byte { func transform(src, replacement string) []byte {
return []byte(strings.ReplaceAll(src, "<CHARTNAME>", replacement)) return []byte(strings.ReplaceAll(src, "<CHARTNAME>", 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)
}

@ -49,30 +49,20 @@ func TestCreate(t *testing.T) {
t.Errorf("Expected name to be 'foo', got %q", mychart.Name()) t.Errorf("Expected name to be 'foo', got %q", mychart.Name())
} }
for _, d := range []string{TemplatesDir, ChartsDir} { for _, f := range []string{
if fi, err := os.Stat(filepath.Join(dir, d)); err != nil { ChartfileName,
t.Errorf("Expected %s dir: %s", d, err) DeploymentName,
} else if !fi.IsDir() { HelpersName,
t.Errorf("Expected %s to be a directory.", d) IgnorefileName,
} NotesName,
} ServiceName,
TemplatesDir,
for _, f := range []string{ChartfileName, ValuesfileName, IgnorefileName} { ValuesfileName,
if fi, err := os.Stat(filepath.Join(dir, f)); err != nil { } {
t.Errorf("Expected %s file: %s", f, err) if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
} 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 {
t.Errorf("Expected %s file: %s", f, err) 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) { func TestCreateFrom(t *testing.T) {
@ -94,7 +84,6 @@ func TestCreateFrom(t *testing.T) {
} }
dir := filepath.Join(tdir, "foo") dir := filepath.Join(tdir, "foo")
c := filepath.Join(tdir, cf.Name) c := filepath.Join(tdir, cf.Name)
mychart, err := loader.LoadDir(c) mychart, err := loader.LoadDir(c)
if err != nil { if err != nil {
@ -105,27 +94,13 @@ func TestCreateFrom(t *testing.T) {
t.Errorf("Expected name to be 'foo', got %q", mychart.Name()) t.Errorf("Expected name to be 'foo', got %q", mychart.Name())
} }
for _, d := range []string{TemplatesDir, ChartsDir} { for _, f := range []string{
if fi, err := os.Stat(filepath.Join(dir, d)); err != nil { ChartfileName,
t.Errorf("Expected %s dir: %s", d, err) ValuesfileName,
} else if !fi.IsDir() { filepath.Join(TemplatesDir, "placeholder.tpl"),
t.Errorf("Expected %s to be a directory.", d) } {
} if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
}
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 {
t.Errorf("Expected %s file: %s", f, err) t.Errorf("Expected %s file: %s", f, err)
} else if fi.IsDir() {
t.Errorf("Expected %s to be a file.", f)
} }
} }
} }

@ -20,7 +20,6 @@ import (
"archive/tar" "archive/tar"
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -52,13 +51,7 @@ func SaveDir(c *chart.Chart, dest string) error {
if c.Values != nil { if c.Values != nil {
vf := filepath.Join(outdir, ValuesfileName) vf := filepath.Join(outdir, ValuesfileName)
b, _ := yaml.Marshal(c.Values) b, _ := yaml.Marshal(c.Values)
if err := ioutil.WriteFile(vf, b, 0644); err != nil { if err := writeFile(vf, b); err != nil {
return err
}
}
for _, d := range []string{TemplatesDir, ChartsDir} {
if err := os.MkdirAll(filepath.Join(outdir, d), 0755); err != nil {
return err return err
} }
} }
@ -67,13 +60,7 @@ func SaveDir(c *chart.Chart, dest string) error {
for _, o := range [][]*chart.File{c.Templates, c.Files} { for _, o := range [][]*chart.File{c.Templates, c.Files} {
for _, f := range o { for _, f := range o {
n := filepath.Join(outdir, f.Name) n := filepath.Join(outdir, f.Name)
if err := writeFile(n, f.Data); err != nil {
d := filepath.Dir(n)
if err := os.MkdirAll(d, 0755); err != nil {
return err
}
if err := ioutil.WriteFile(n, f.Data, 0644); err != nil {
return err return err
} }
} }
@ -84,7 +71,7 @@ func SaveDir(c *chart.Chart, dest string) error {
for _, dep := range c.Dependencies() { for _, dep := range c.Dependencies() {
// Here, we write each dependency as a tar file. // Here, we write each dependency as a tar file.
if _, err := Save(dep, base); err != nil { if _, err := Save(dep, base); err != nil {
return err return errors.Wrapf(err, "saving %s", dep.ChartFullPath())
} }
} }
return nil return nil
@ -99,13 +86,6 @@ func SaveDir(c *chart.Chart, dest string) error {
// //
// This returns the absolute path to the chart archive file. // This returns the absolute path to the chart archive file.
func Save(c *chart.Chart, outDir string) (string, error) { 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 { if err := c.Validate(); err != nil {
return "", errors.Wrap(err, "chart validation") 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? // TODO: Do we need to create dummy parent directory names if none exist?
h := &tar.Header{ h := &tar.Header{
Name: name, Name: name,
Mode: 0755, Mode: 0644,
Size: int64(len(body)), Size: int64(len(body)),
} }
if err := out.WriteHeader(h); err != nil { if err := out.WriteHeader(h); err != nil {

@ -26,6 +26,8 @@ import (
"os" "os"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"helm.sh/helm/pkg/helmpath"
) )
// EnvSettings describes all of the environment settings. // EnvSettings describes all of the environment settings.
@ -38,6 +40,24 @@ type EnvSettings struct {
KubeContext string KubeContext string
// Debug indicates whether or not Helm is running in Debug mode. // Debug indicates whether or not Helm is running in Debug mode.
Debug bool 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. // 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.KubeConfig, "kubeconfig", "", "path to the kubeconfig file")
fs.StringVar(&s.KubeContext, "kube-context", "", "name of the kubeconfig context to use") fs.StringVar(&s.KubeContext, "kube-context", "", "name of the kubeconfig context to use")
fs.BoolVar(&s.Debug, "debug", false, "enable verbose output") 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. // Init sets values from the environment.
@ -59,6 +83,8 @@ func (s *EnvSettings) Init(fs *pflag.FlagSet) {
var envMap = map[string]string{ var envMap = map[string]string{
"debug": "HELM_DEBUG", "debug": "HELM_DEBUG",
"namespace": "HELM_NAMESPACE", "namespace": "HELM_NAMESPACE",
"registry-config": "HELM_REGISTRY_CONFIG",
"repository-config": "HELM_REPOSITORY_CONFIG",
} }
func setFlagFromEnv(name, envar string, fs *pflag.FlagSet) { func setFlagFromEnv(name, envar string, fs *pflag.FlagSet) {

@ -25,7 +25,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/strvals" "helm.sh/helm/pkg/strvals"
) )
@ -38,14 +37,14 @@ type Options struct {
// MergeValues merges values from files specified via -f/--values and // MergeValues merges values from files specified via -f/--values and
// directly via --set or --set-string, marshaling them to YAML // 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{}{} base := map[string]interface{}{}
// User specified a values files via -f/--values // User specified a values files via -f/--values
for _, filePath := range opts.ValueFiles { for _, filePath := range opts.ValueFiles {
currentMap := map[string]interface{}{} currentMap := map[string]interface{}{}
bytes, err := readFile(filePath, settings) bytes, err := readFile(filePath, p)
if err != nil { if err != nil {
return nil, err 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. // 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) == "-" { if strings.TrimSpace(filePath) == "-" {
return ioutil.ReadAll(os.Stdin) return ioutil.ReadAll(os.Stdin)
} }
u, _ := url.Parse(filePath) u, _ := url.Parse(filePath)
p := getter.All(settings)
// FIXME: maybe someone handle other protocols like ftp. // FIXME: maybe someone handle other protocols like ftp.
getterConstructor, err := p.ByScheme(u.Scheme) g, err := p.ByScheme(u.Scheme)
if err != nil { if err != nil {
return ioutil.ReadFile(filePath) return ioutil.ReadFile(filePath)
} }
data, err := g.Get(filePath, getter.WithURL(filePath))
getter, err := getterConstructor(getter.WithURL(filePath))
if err != nil {
return []byte{}, err
}
data, err := getter.Get(filePath)
return data.Bytes(), err return data.Bytes(), err
} }

@ -68,6 +68,8 @@ type ChartDownloader struct {
Getters getter.Providers Getters getter.Providers
// Options provide parameters to be passed along to the Getter being initialized. // 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. // 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 return "", nil, err
} }
constructor, err := c.Getters.ByScheme(u.Scheme) g, err := c.Getters.ByScheme(u.Scheme)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
g, err := constructor(c.Options...) data, err := g.Get(u.String(), c.Options...)
if err != nil {
return "", nil, err
}
data, err := g.Get(u.String())
if err != nil { if err != nil {
return "", nil, err 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)) c.Options = append(c.Options, getter.WithURL(ref))
rf, err := repo.LoadFile(helmpath.RepositoryFile()) rf, err := repo.LoadFile(c.RepositoryConfig)
if err != nil { if err != nil {
return u, err 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. // 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 { if err != nil {
return u, errors.Wrap(err, "no cached repo found. (try 'helm repo update')") 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 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 { if err != nil {
return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')") return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
} }

@ -24,16 +24,16 @@ import (
"helm.sh/helm/internal/test/ensure" "helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/getter" "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"
"helm.sh/helm/pkg/repo/repotest" "helm.sh/helm/pkg/repo/repotest"
) )
func TestResolveChartRef(t *testing.T) { const (
os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome") repoConfig = "testdata/repositories.yaml"
os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome") repoCache = "testdata/repository"
)
func TestResolveChartRef(t *testing.T) {
tests := []struct { tests := []struct {
name, ref, expect, version string name, ref, expect, version string
fail bool fail bool
@ -57,7 +57,12 @@ func TestResolveChartRef(t *testing.T) {
c := ChartDownloader{ c := ChartDownloader{
Out: os.Stderr, Out: os.Stderr,
Getters: getter.All(cli.EnvSettings{}), RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
} }
for _, tt := range tests { for _, tt := range tests {
@ -105,16 +110,11 @@ func TestIsTar(t *testing.T) {
} }
func TestDownloadTo(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 // Set up a fake repo with basic auth enabled
srv := repotest.NewServer(helmpath.CachePath()) srv, err := repotest.NewTempServer("testdata/*.tgz*")
srv.Stop() srv.Stop()
if _, err := srv.CopyCharts("testdata/*.tgz*"); err != nil { if err != nil {
t.Error(err) t.Fatal(err)
return
} }
srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth() username, password, ok := r.BasicAuth()
@ -136,12 +136,18 @@ func TestDownloadTo(t *testing.T) {
Out: os.Stderr, Out: os.Stderr,
Verify: VerifyAlways, Verify: VerifyAlways,
Keyring: "testdata/helm-test-key.pub", Keyring: "testdata/helm-test-key.pub",
Getters: getter.All(cli.EnvSettings{}), RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
Options: []getter.Option{ Options: []getter.Option{
getter.WithBasicAuth("username", "password"), getter.WithBasicAuth("username", "password"),
}, },
} }
cname := "/signtest-0.1.0.tgz" cname := "/signtest-0.1.0.tgz"
dest := srv.Root()
where, v, err := c.DownloadTo(srv.URL()+cname, "", dest) where, v, err := c.DownloadTo(srv.URL()+cname, "", dest)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -161,10 +167,9 @@ func TestDownloadTo(t *testing.T) {
} }
func TestDownloadTo_VerifyLater(t *testing.T) { func TestDownloadTo_VerifyLater(t *testing.T) {
ensure.HelmHome(t) defer ensure.HelmHome(t)()
defer ensure.CleanHomeDirs(t)
dest := helmpath.CachePath() dest := ensure.TempDir(t)
// Set up a fake repo // Set up a fake repo
srv, err := repotest.NewTempServer("testdata/*.tgz*") srv, err := repotest.NewTempServer("testdata/*.tgz*")
@ -179,7 +184,12 @@ func TestDownloadTo_VerifyLater(t *testing.T) {
c := ChartDownloader{ c := ChartDownloader{
Out: os.Stderr, Out: os.Stderr,
Verify: VerifyLater, Verify: VerifyLater,
Getters: getter.All(cli.EnvSettings{}), RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
} }
cname := "/signtest-0.1.0.tgz" cname := "/signtest-0.1.0.tgz"
where, _, err := c.DownloadTo(srv.URL()+cname, "", dest) where, _, err := c.DownloadTo(srv.URL()+cname, "", dest)
@ -200,17 +210,19 @@ func TestDownloadTo_VerifyLater(t *testing.T) {
} }
func TestScanReposForURL(t *testing.T) { func TestScanReposForURL(t *testing.T) {
os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome")
os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome")
c := ChartDownloader{ c := ChartDownloader{
Out: os.Stderr, Out: os.Stderr,
Verify: VerifyLater, Verify: VerifyLater,
Getters: getter.All(cli.EnvSettings{}), RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
} }
u := "http://example.com/alpine-0.2.0.tgz" u := "http://example.com/alpine-0.2.0.tgz"
rf, err := repo.LoadFile(helmpath.RepositoryFile()) rf, err := repo.LoadFile(repoConfig)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -56,6 +56,8 @@ type Manager struct {
SkipUpdate bool SkipUpdate bool
// Getter collection for the operation // Getter collection for the operation
Getters []getter.Provider Getters []getter.Provider
RepositoryConfig string
RepositoryCache string
} }
// Build rebuilds a local charts directory from a lockfile. // 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/ // Now we need to fetch every package here into charts/
if err := m.downloadAll(lock.Dependencies); err != nil { return m.downloadAll(lock.Dependencies)
return err
}
return nil
} }
// Update updates a local charts directory. // 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. // 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) { 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) return res.Resolve(req, repoNames)
} }
@ -232,6 +230,8 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
Out: m.Out, Out: m.Out,
Verify: m.Verify, Verify: m.Verify,
Keyring: m.Keyring, Keyring: m.Keyring,
RepositoryConfig: m.RepositoryConfig,
RepositoryCache: m.RepositoryCache,
Getters: m.Getters, Getters: m.Getters,
Options: []getter.Option{ Options: []getter.Option{
getter.WithBasicAuth(username, password), 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. // hasAllRepos ensures that all of the referenced deps are in the local repo cache.
func (m *Manager) hasAllRepos(deps []*chart.Dependency) error { func (m *Manager) hasAllRepos(deps []*chart.Dependency) error {
rf, err := repo.LoadFile(helmpath.RepositoryFile()) rf, err := repo.LoadFile(m.RepositoryConfig)
if err != nil { if err != nil {
return err 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. // 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) { 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 err != nil {
if os.IsNotExist(err) {
return make(map[string]string), nil
}
return nil, err return nil, err
} }
repos := rf.Repositories 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. // UpdateRepositories updates all of the local repos to the latest.
func (m *Manager) UpdateRepositories() error { func (m *Manager) UpdateRepositories() error {
rf, err := repo.LoadFile(helmpath.RepositoryFile()) rf, err := repo.LoadFile(m.RepositoryConfig)
if err != nil { if err != nil {
return err return err
} }
@ -427,8 +430,7 @@ func (m *Manager) UpdateRepositories() error {
} }
func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error { func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
out := m.Out fmt.Fprintln(m.Out, "Hang tight while we grab the latest from your chart repositories...")
fmt.Fprintln(out, "Hang tight while we grab the latest from your chart repositories...")
var wg sync.WaitGroup var wg sync.WaitGroup
for _, c := range repos { for _, c := range repos {
r, err := repo.NewChartRepository(c, m.Getters) r, err := repo.NewChartRepository(c, m.Getters)
@ -437,16 +439,16 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
} }
wg.Add(1) wg.Add(1)
go func(r *repo.ChartRepository) { go func(r *repo.ChartRepository) {
if err := r.DownloadIndexFile(); err != nil { 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) 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 { } 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() wg.Done()
}(r) }(r)
} }
wg.Wait() wg.Wait()
fmt.Fprintln(out, "Update Complete. ⎈Happy Helming!⎈") fmt.Fprintln(m.Out, "Update Complete. ⎈Happy Helming!⎈")
return nil 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). // The key is the local name (which is only present in the repositories.yaml).
func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, error) { func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, error) {
indices := map[string]*repo.ChartRepository{} indices := map[string]*repo.ChartRepository{}
repoyaml := helmpath.RepositoryFile()
// Load repositories.yaml file // Load repositories.yaml file
rf, err := repo.LoadFile(repoyaml) rf, err := repo.LoadFile(m.RepositoryConfig)
if err != nil { 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 { for _, re := range rf.Repositories {
lname := re.Name 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 { if err != nil {
return indices, err return indices, err
} }

@ -17,12 +17,10 @@ package downloader
import ( import (
"bytes" "bytes"
"os"
"reflect" "reflect"
"testing" "testing"
"helm.sh/helm/pkg/chart" "helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/helmpath/xdg"
) )
func TestVersionEquals(t *testing.T) { func TestVersionEquals(t *testing.T) {
@ -64,12 +62,11 @@ func TestNormalizeURL(t *testing.T) {
} }
func TestFindChartURL(t *testing.T) { func TestFindChartURL(t *testing.T) {
os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome") var b bytes.Buffer
os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome")
b := bytes.NewBuffer(nil)
m := &Manager{ m := &Manager{
Out: b, Out: &b,
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
} }
repos, err := m.loadChartRepositories() repos, err := m.loadChartRepositories()
if err != nil { if err != nil {
@ -96,12 +93,11 @@ func TestFindChartURL(t *testing.T) {
} }
func TestGetRepoNames(t *testing.T) { func TestGetRepoNames(t *testing.T) {
os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome")
os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome")
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
m := &Manager{ m := &Manager{
Out: b, Out: b,
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
} }
tests := []struct { tests := []struct {
name string name string

@ -75,8 +75,8 @@ func WithTLSClientConfig(certFile, keyFile, caFile string) Option {
// Getter is an interface to support GET to the specified URL. // Getter is an interface to support GET to the specified URL.
type Getter interface { type Getter interface {
//Get file content by url string // Get file content by url string
Get(url string) (*bytes.Buffer, error) Get(url string, options ...Option) (*bytes.Buffer, error)
} }
// Constructor is the function for every getter which creates a specific instance // 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. // ByScheme returns a Provider that handles the given scheme.
// //
// If no provider handles this scheme, this will return an error. // 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 { for _, pp := range p {
if pp.Provides(scheme) { if pp.Provides(scheme) {
return pp.New, nil return pp.New()
} }
} }
return nil, errors.Errorf("scheme %q not supported", scheme) 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. // All finds all of the registered getters as a list of Provider instances.
// Currently, the built-in getters and the discovered plugins with downloader // Currently, the built-in getters and the discovered plugins with downloader
// notations are collected. // notations are collected.
func All(settings cli.EnvSettings) Providers { func All(settings *cli.EnvSettings) Providers {
result := Providers{ result := Providers{httpProvider}
{
Schemes: []string{"http", "https"},
New: NewHTTPGetter,
},
}
pluginDownloaders, _ := collectPlugins(settings) pluginDownloaders, _ := collectPlugins(settings)
result = append(result, pluginDownloaders...) result = append(result, pluginDownloaders...)
return result 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)
}

@ -16,13 +16,13 @@ limitations under the License.
package getter package getter
import ( import (
"os"
"testing" "testing"
"helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/helmpath/xdg"
) )
const pluginDir = "testdata/plugins"
func TestProvider(t *testing.T) { func TestProvider(t *testing.T) {
p := Provider{ p := Provider{
[]string{"one", "three"}, []string{"one", "three"},
@ -53,9 +53,9 @@ func TestProviders(t *testing.T) {
} }
func TestAll(t *testing.T) { func TestAll(t *testing.T) {
os.Setenv(xdg.DataHomeEnvVar, "testdata") all := All(&cli.EnvSettings{
PluginsDirectory: pluginDir,
all := All(cli.EnvSettings{}) })
if len(all) != 3 { if len(all) != 3 {
t.Errorf("expected 3 providers (default plus two plugins), got %d", len(all)) 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) { func TestByScheme(t *testing.T) {
os.Setenv(xdg.DataHomeEnvVar, "testdata") g := All(&cli.EnvSettings{
PluginsDirectory: pluginDir,
if _, err := ByScheme("test", cli.EnvSettings{}); err != nil { })
if _, err := g.ByScheme("test"); err != nil {
t.Error(err) t.Error(err)
} }
if _, err := ByScheme("https", cli.EnvSettings{}); err != nil { if _, err := g.ByScheme("https"); err != nil {
t.Error(err) t.Error(err)
} }
} }

@ -34,7 +34,10 @@ type HTTPGetter struct {
} }
//Get performs a Get from repo.Getter and returns the body. //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) return g.get(href)
} }

@ -104,16 +104,11 @@ func TestDownload(t *testing.T) {
})) }))
defer srv.Close() defer srv.Close()
provider, err := ByScheme("http", cli.EnvSettings{}) g, err := All(new(cli.EnvSettings)).ByScheme("http")
if err != nil {
t.Fatal("No http provider found")
}
g, err := provider.New(WithURL(srv.URL))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
got, err := g.Get(srv.URL) got, err := g.Get(srv.URL, WithURL(srv.URL))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

@ -25,14 +25,13 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/plugin" "helm.sh/helm/pkg/plugin"
) )
// collectPlugins scans for getter plugins. // collectPlugins scans for getter plugins.
// This will load plugins according to the cli. // This will load plugins according to the cli.
func collectPlugins(settings cli.EnvSettings) (Providers, error) { func collectPlugins(settings *cli.EnvSettings) (Providers, error) {
plugins, err := plugin.FindPlugins(helmpath.Plugins()) plugins, err := plugin.FindPlugins(settings.PluginsDirectory)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -57,14 +56,17 @@ func collectPlugins(settings cli.EnvSettings) (Providers, error) {
// implemented in plugins. // implemented in plugins.
type pluginGetter struct { type pluginGetter struct {
command string command string
settings cli.EnvSettings settings *cli.EnvSettings
name string name string
base string base string
opts options opts options
} }
// Get runs downloader plugin command // 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, " ") commands := strings.Split(p.command, " ")
argv := append(commands[1:], p.opts.certFile, p.opts.keyFile, p.opts.caFile, href) argv := append(commands[1:], p.opts.certFile, p.opts.keyFile, p.opts.caFile, href)
prog := exec.Command(filepath.Join(p.base, commands[0]), argv...) 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 // 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) { return func(options ...Option) (Getter, error) {
result := &pluginGetter{ result := &pluginGetter{
command: command, command: command,

@ -16,19 +16,17 @@ limitations under the License.
package getter package getter
import ( import (
"os"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
"helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/helmpath/xdg"
) )
func TestCollectPlugins(t *testing.T) { func TestCollectPlugins(t *testing.T) {
os.Setenv(xdg.DataHomeEnvVar, "testdata") env := &cli.EnvSettings{
PluginsDirectory: pluginDir,
env := cli.EnvSettings{} }
p, err := collectPlugins(env) p, err := collectPlugins(env)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -56,9 +54,9 @@ func TestPluginGetter(t *testing.T) {
t.Skip("TODO: refactor this test to work on windows") t.Skip("TODO: refactor this test to work on windows")
} }
os.Setenv(xdg.DataHomeEnvVar, "testdata") env := &cli.EnvSettings{
PluginsDirectory: pluginDir,
env := cli.EnvSettings{} }
pg := NewPluginGetter("echo", env, "test", ".") pg := NewPluginGetter("echo", env, "test", ".")
g, err := pg() g, err := pg()
if err != nil { if err != nil {
@ -82,11 +80,9 @@ func TestPluginSubCommands(t *testing.T) {
t.Skip("TODO: refactor this test to work on windows") t.Skip("TODO: refactor this test to work on windows")
} }
oldhh := os.Getenv("HELM_HOME") env := &cli.EnvSettings{
defer os.Setenv("HELM_HOME", oldhh) PluginsDirectory: pluginDir,
os.Setenv("HELM_HOME", "") }
env := cli.EnvSettings{}
pg := NewPluginGetter("echo -n", env, "test", ".") pg := NewPluginGetter("echo -n", env, "test", ".")
g, err := pg() g, err := pg()
if err != nil { if err != nil {

@ -13,69 +13,22 @@
package helmpath package helmpath
import (
"fmt"
"path/filepath"
)
// This helper builds paths to Helm's configuration, cache and data paths. // This helper builds paths to Helm's configuration, cache and data paths.
const lp = lazypath("helm") const lp = lazypath("helm")
// ConfigPath returns the path where Helm stores configuration. // ConfigPath returns the path where Helm stores configuration.
func ConfigPath() string { func ConfigPath(elem ...string) string { return lp.configPath(elem...) }
return lp.configPath("")
}
// CachePath returns the path where Helm stores cached objects. // CachePath returns the path where Helm stores cached objects.
func CachePath() string { func CachePath(elem ...string) string { return lp.cachePath(elem...) }
return lp.cachePath("")
}
// DataPath returns the path where Helm stores data. // DataPath returns the path where Helm stores data.
func DataPath() string { func DataPath(elem ...string) string { return lp.dataPath(elem...) }
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")
}
// CacheIndex returns the path to an index for the given named repository. // CacheIndex returns the path to an index for the given named repository.
func CacheIndex(name string) string { func CacheIndexFile(name string) string {
target := fmt.Sprintf("%s-index.yaml", name) if name != "" {
if name == "" { name += "-"
target = "index.yaml"
} }
return filepath.Join(RepositoryCache(), target) return name + "index.yaml"
}
// 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")
} }

@ -38,12 +38,6 @@ func TestHelmHome(t *testing.T) {
isEq(t, CachePath(), "/cache/helm") isEq(t, CachePath(), "/cache/helm")
isEq(t, ConfigPath(), "/config/helm") isEq(t, ConfigPath(), "/config/helm")
isEq(t, DataPath(), "/data/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 // test to see if lazy-loading environment variables at runtime works
os.Setenv(xdg.CacheHomeEnvVar, "/cache2") os.Setenv(xdg.CacheHomeEnvVar, "/cache2")

@ -35,12 +35,6 @@ func TestHelmHome(t *testing.T) {
isEq(t, CachePath(), "c:\\helm") isEq(t, CachePath(), "c:\\helm")
isEq(t, ConfigPath(), "d:\\helm") isEq(t, ConfigPath(), "d:\\helm")
isEq(t, DataPath(), "e:\\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 // test to see if lazy-loading environment variables at runtime works
os.Setenv(xdg.CacheHomeEnvVar, "f:\\") os.Setenv(xdg.CacheHomeEnvVar, "f:\\")

@ -22,27 +22,27 @@ import (
// lazypath is an lazy-loaded path buffer for the XDG base directory specification. // lazypath is an lazy-loaded path buffer for the XDG base directory specification.
type lazypath string 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) base := os.Getenv(envVar)
if base == "" { if base == "" {
base = defaultFn() 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 // cachePath defines the base directory relative to which user specific non-essential data files
// should be stored. // should be stored.
func (l lazypath) cachePath(file string) string { func (l lazypath) cachePath(elem ...string) string {
return l.path(xdg.CacheHomeEnvVar, cacheHome, file) return l.path(xdg.CacheHomeEnvVar, cacheHome, filepath.Join(elem...))
} }
// configPath defines the base directory relative to which user specific configuration files should // configPath defines the base directory relative to which user specific configuration files should
// be stored. // be stored.
func (l lazypath) configPath(file string) string { func (l lazypath) configPath(elem ...string) string {
return l.path(xdg.ConfigHomeEnvVar, configHome, file) return l.path(xdg.ConfigHomeEnvVar, configHome, filepath.Join(elem...))
} }
// dataPath defines the base directory relative to which user specific data files should be stored. // dataPath defines the base directory relative to which user specific data files should be stored.
func (l lazypath) dataPath(file string) string { func (l lazypath) dataPath(elem ...string) string {
return l.path(xdg.DataHomeEnvVar, dataHome, file) return l.path(xdg.DataHomeEnvVar, dataHome, filepath.Join(elem...))
} }

@ -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 // Key generates a cache key based on a url or scp string. The key is file
// system safe. // system safe.
func Key(repo string) (string, error) { func Key(repo string) (string, error) {
var (
var u *url.URL u *url.URL
var err error err error
var strip bool )
if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil { if m := scpSyntaxRe.FindStringSubmatch(repo); m != nil {
// Match SCP-like syntax and convert it to a URL. // Match SCP-like syntax and convert it to a URL.
// Eg, "git@github.com:user/repo" becomes // Eg, "git@github.com:user/repo" becomes
// "ssh://git@github.com/user/repo". // "ssh://git@github.com/user/repo".
u = &url.URL{ u = &url.URL{
Scheme: "ssh",
User: url.User(m[1]), User: url.User(m[1]),
Host: m[2], Host: m[2],
Path: "/" + m[3], Path: "/" + m[3],
} }
strip = true
} else { } else {
u, err = url.Parse(repo) u, err = url.Parse(repo)
if err != nil { if err != nil {
@ -52,23 +50,18 @@ func Key(repo string) (string, error) {
} }
} }
if strip { var key strings.Builder
u.Scheme = ""
}
var key string
if u.Scheme != "" { if u.Scheme != "" {
key = u.Scheme + "-" key.WriteString(u.Scheme)
key.WriteString("-")
} }
if u.User != nil && u.User.Username() != "" { 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 != "" { if u.Path != "" {
key = key + strings.ReplaceAll(u.Path, "/", "-") key.WriteString(strings.ReplaceAll(u.Path, "/", "-"))
} }
return strings.ReplaceAll(key.String(), ":", "-"), nil
key = strings.ReplaceAll(key, ":", "-")
return key, nil
} }

@ -42,5 +42,5 @@ func (b *base) Path() string {
if b.Source == "" { if b.Source == "" {
return "" return ""
} }
return filepath.Join(helmpath.Plugins(), filepath.Base(b.Source)) return helmpath.DataPath("plugins", filepath.Base(b.Source))
} }

@ -79,18 +79,13 @@ func NewHTTPInstaller(source string) (*HTTPInstaller, error) {
return nil, err return nil, err
} }
getConstructor, err := getter.ByScheme("http", cli.EnvSettings{}) get, err := getter.All(new(cli.EnvSettings)).ByScheme("http")
if err != nil {
return nil, err
}
get, err := getConstructor.New(getter.WithURL(source))
if err != nil { if err != nil {
return nil, err return nil, err
} }
i := &HTTPInstaller{ i := &HTTPInstaller{
CacheDir: filepath.Join(helmpath.PluginCache(), key), CacheDir: helmpath.CachePath("plugins", key),
PluginName: stripPluginName(filepath.Base(source)), PluginName: stripPluginName(filepath.Base(source)),
base: newBase(source), base: newBase(source),
extractor: extractor, extractor: extractor,
@ -157,7 +152,7 @@ func (i HTTPInstaller) Path() string {
if i.base.Source == "" { if i.base.Source == "" {
return "" return ""
} }
return filepath.Join(helmpath.Plugins(), i.PluginName) return helmpath.DataPath("plugins", i.PluginName)
} }
// Extract extracts compressed archives // Extract extracts compressed archives

@ -19,12 +19,12 @@ import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"os" "os"
"path/filepath"
"testing" "testing"
"github.com/pkg/errors" "github.com/pkg/errors"
"helm.sh/helm/internal/test/ensure" "helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/helmpath"
) )
@ -36,7 +36,9 @@ type TestHTTPGetter struct {
MockError error 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 // 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=" 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) { func TestHTTPInstaller(t *testing.T) {
defer ensure.HelmHome(t)()
source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" 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 { if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err) t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
} }
i, err := NewForSource(source, "0.0.1") i, err := NewForSource(source, "0.0.1")
@ -90,7 +91,7 @@ func TestHTTPInstaller(t *testing.T) {
if err := Install(i); err != nil { if err := Install(i); err != nil {
t.Error(err) 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()) 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) { func TestHTTPInstallerNonExistentVersion(t *testing.T) {
defer ensure.HelmHome(t)()
source := "https://repo.localdomain/plugins/fake-plugin-0.0.2.tar.gz" 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 { if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err) t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
} }
i, err := NewForSource(source, "0.0.2") i, err := NewForSource(source, "0.0.2")
@ -137,11 +137,10 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) {
func TestHTTPInstallerUpdate(t *testing.T) { func TestHTTPInstallerUpdate(t *testing.T) {
source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz" source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz"
ensure.HelmHome(t) defer ensure.HelmHome(t)()
defer ensure.CleanHomeDirs(t)
if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil { if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err) t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
} }
i, err := NewForSource(source, "0.0.1") i, err := NewForSource(source, "0.0.1")
@ -169,7 +168,7 @@ func TestHTTPInstallerUpdate(t *testing.T) {
if err := Install(i); err != nil { if err := Install(i); err != nil {
t.Error(err) 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()) t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path())
} }

@ -18,7 +18,6 @@ package installer
import ( import (
"fmt" "fmt"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strings" "strings"
@ -43,14 +42,12 @@ type Installer interface {
// Install installs a plugin. // Install installs a plugin.
func Install(i Installer) error { func Install(i Installer) error {
if _, pathErr := os.Stat(path.Dir(i.Path())); os.IsNotExist(pathErr) { if err := os.MkdirAll(filepath.Dir(i.Path()), 0755); err != nil {
return errors.New(`plugin home "$XDG_CONFIG_HOME/helm/plugins" does not exist`) return err
} }
if _, pathErr := os.Stat(i.Path()); !os.IsNotExist(pathErr) { if _, pathErr := os.Stat(i.Path()); !os.IsNotExist(pathErr) {
return errors.New("plugin already exists") return errors.New("plugin already exists")
} }
return i.Install() return i.Install()
} }
@ -59,7 +56,6 @@ func Update(i Installer) error {
if _, pathErr := os.Stat(i.Path()); os.IsNotExist(pathErr) { if _, pathErr := os.Stat(i.Path()); os.IsNotExist(pathErr) {
return errors.New("plugin does not exist") return errors.New("plugin does not exist")
} }
return i.Update() return i.Update()
} }

@ -21,16 +21,12 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/helmpath" "helm.sh/helm/pkg/helmpath"
) )
var _ Installer = new(LocalInstaller) var _ Installer = new(LocalInstaller)
func TestLocalInstaller(t *testing.T) { func TestLocalInstaller(t *testing.T) {
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
// Make a temp dir // Make a temp dir
tdir, err := ioutil.TempDir("", "helm-installer-") tdir, err := ioutil.TempDir("", "helm-installer-")
if err != nil { if err != nil {
@ -48,10 +44,10 @@ func TestLocalInstaller(t *testing.T) {
} }
if err := Install(i); err != nil { 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()) t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path())
} }
} }

@ -17,7 +17,6 @@ package installer // import "helm.sh/helm/pkg/plugin/installer"
import ( import (
"os" "os"
"path/filepath"
"sort" "sort"
"github.com/Masterminds/semver" "github.com/Masterminds/semver"
@ -53,7 +52,7 @@ func NewVCSInstaller(source, version string) (*VCSInstaller, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
cachedpath := filepath.Join(helmpath.PluginCache(), key) cachedpath := helmpath.CachePath("plugins", key)
repo, err := vcs.NewRepo(source, cachedpath) repo, err := vcs.NewRepo(source, cachedpath)
if err != nil { if err != nil {
return nil, err return nil, err

@ -49,11 +49,10 @@ func (r *testRepo) UpdateVersion(version string) error {
} }
func TestVCSInstaller(t *testing.T) { func TestVCSInstaller(t *testing.T) {
ensure.HelmHome(t) defer ensure.HelmHome(t)()
defer ensure.CleanHomeDirs(t)
if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil { if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err) t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
} }
source := "https://github.com/adamreese/helm-env" source := "https://github.com/adamreese/helm-env"
@ -83,7 +82,7 @@ func TestVCSInstaller(t *testing.T) {
if repo.current != "0.1.1" { if repo.current != "0.1.1" {
t.Errorf("expected version '0.1.1', got %q", repo.current) 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()) 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) 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 { if _, err := FindSource(i.Path()); err == nil {
t.Error("expected error for inability to find plugin source, got none") t.Error("expected error for inability to find plugin source, got none")
} else if err.Error() != "cannot get information about plugin source" { } else if err.Error() != "cannot get information about plugin source" {
@ -103,8 +102,7 @@ func TestVCSInstaller(t *testing.T) {
} }
func TestVCSInstallerNonExistentVersion(t *testing.T) { func TestVCSInstallerNonExistentVersion(t *testing.T) {
ensure.HelmHome(t) defer ensure.HelmHome(t)()
defer ensure.CleanHomeDirs(t)
source := "https://github.com/adamreese/helm-env" source := "https://github.com/adamreese/helm-env"
version := "0.2.0" version := "0.2.0"
@ -127,8 +125,7 @@ func TestVCSInstallerNonExistentVersion(t *testing.T) {
} }
} }
func TestVCSInstallerUpdate(t *testing.T) { func TestVCSInstallerUpdate(t *testing.T) {
ensure.HelmHome(t) defer ensure.HelmHome(t)()
defer ensure.CleanHomeDirs(t)
source := "https://github.com/adamreese/helm-env" source := "https://github.com/adamreese/helm-env"

@ -25,7 +25,7 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
helm_env "helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/helmpath" "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 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 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 // - If no OS/Arch match is found, return nil
func getPlatformCommand(platformCommands []PlatformCommand) []string { func getPlatformCommand(cmds []PlatformCommand) []string {
var command []string var command []string
for _, platformCommand := range platformCommands { eq := strings.EqualFold
if strings.EqualFold(platformCommand.OperatingSystem, runtime.GOOS) { for _, c := range cmds {
command = strings.Split(os.ExpandEnv(platformCommand.Command), " ") 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) { if eq(c.OperatingSystem, runtime.GOOS) && eq(c.Architecture, runtime.GOARCH) {
return strings.Split(os.ExpandEnv(platformCommand.Command), " ") return strings.Split(os.ExpandEnv(c.Command), " ")
} }
} }
return 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 // SetupPluginEnv prepares os.Env for plugins. It operates on os.Env because
// the plugin subsystem itself needs access to the environment variables // the plugin subsystem itself needs access to the environment variables
// created here. // created here.
func SetupPluginEnv(settings helm_env.EnvSettings, func SetupPluginEnv(settings *cli.EnvSettings, name, base string) {
shortName, base string) {
for key, val := range map[string]string{ for key, val := range map[string]string{
"HELM_PLUGIN_NAME": shortName, "HELM_PLUGIN_NAME": name,
"HELM_PLUGIN_DIR": base, "HELM_PLUGIN_DIR": base,
"HELM_BIN": os.Args[0], "HELM_BIN": os.Args[0],
"HELM_PLUGIN": helmpath.Plugins(), "HELM_PLUGIN": settings.PluginsDirectory,
// Set vars that convey common information. // Set vars that convey common information.
"HELM_PATH_REPOSITORY_FILE": helmpath.RepositoryFile(), "HELM_PATH_REPOSITORY_FILE": settings.RepositoryConfig,
"HELM_PATH_REPOSITORY_CACHE": helmpath.RepositoryCache(), "HELM_PATH_REPOSITORY_CACHE": settings.RepositoryCache,
"HELM_PATH_STARTER": helmpath.Starters(), "HELM_PATH_STARTER": helmpath.DataPath("starters"),
"HELM_PATH_CACHE": helmpath.CachePath(),
"HELM_PATH_CONFIG": helmpath.ConfigPath(),
"HELM_PATH_DATA": helmpath.DataPath(),
"HELM_HOME": helmpath.DataPath(), // for backwards compatibility with Helm 2 plugins "HELM_HOME": helmpath.DataPath(), // for backwards compatibility with Helm 2 plugins
"HELM_DEBUG": fmt.Sprint(settings.Debug),
} { } {
os.Setenv(key, val) os.Setenv(key, val)
} }
if settings.Debug {
os.Setenv("HELM_DEBUG", "1")
}
} }

@ -16,9 +16,13 @@ limitations under the License.
package plugin // import "helm.sh/helm/pkg/plugin" package plugin // import "helm.sh/helm/pkg/plugin"
import ( import (
"os"
"path/filepath"
"reflect" "reflect"
"runtime" "runtime"
"testing" "testing"
"helm.sh/helm/pkg/cli"
) )
func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) { 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 var osStrCmp string
os := runtime.GOOS os := runtime.GOOS
arch := runtime.GOARCH arch := runtime.GOARCH
@ -101,6 +104,7 @@ func TestPlatformPrepareCommand(t *testing.T) {
osStrCmp = "os-arch" osStrCmp = "os-arch"
} }
argv := []string{"--debug", "--foo", "bar"}
checkCommand(p, argv, osStrCmp, t) checkCommand(p, argv, osStrCmp, t)
} }
@ -116,7 +120,6 @@ func TestPartialPlatformPrepareCommand(t *testing.T) {
}, },
}, },
} }
argv := []string{"--debug", "--foo", "bar"}
var osStrCmp string var osStrCmp string
os := runtime.GOOS os := runtime.GOOS
arch := runtime.GOARCH arch := runtime.GOARCH
@ -128,6 +131,7 @@ func TestPartialPlatformPrepareCommand(t *testing.T) {
osStrCmp = "os-arch" osStrCmp = "os-arch"
} }
argv := []string{"--debug", "--foo", "bar"}
checkCommand(p, argv, osStrCmp, t) checkCommand(p, argv, osStrCmp, t)
} }
@ -158,8 +162,7 @@ func TestNoMatchPrepareCommand(t *testing.T) {
} }
argv := []string{"--debug", "--foo", "bar"} argv := []string{"--debug", "--foo", "bar"}
_, _, err := p.PrepareCommand(argv) if _, _, err := p.PrepareCommand(argv); err == nil {
if err == nil {
t.Errorf("Expected error to be returned") 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) 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)
}
}
}

@ -5,9 +5,5 @@ echo "Hello from a Helm plugin"
echo "PARAMS" echo "PARAMS"
echo $* echo $*
echo "ENVIRONMENT" $HELM_BIN ls --all
echo $TILLER_HOST
echo $HELM_PATH_CONFIG
$HELM_BIN --host $TILLER_HOST ls --all

@ -52,6 +52,7 @@ type ChartRepository struct {
ChartPaths []string ChartPaths []string
IndexFile *IndexFile IndexFile *IndexFile
Client getter.Getter Client getter.Getter
CachePath string
} }
// NewChartRepository constructs ChartRepository // 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) 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 { if err != nil {
return nil, errors.Errorf("could not find protocol handler for: %s", u.Scheme) 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{ return &ChartRepository{
Config: cfg, Config: cfg,
IndexFile: NewIndexFile(), IndexFile: NewIndexFile(),
Client: client, Client: client,
CachePath: helmpath.CachePath("repository"),
}, nil }, nil
} }
@ -114,31 +108,37 @@ func (r *ChartRepository) Load() error {
} }
// DownloadIndexFile fetches the index from a repository. // DownloadIndexFile fetches the index from a repository.
func (r *ChartRepository) DownloadIndexFile() error { func (r *ChartRepository) DownloadIndexFile() (string, error) {
var indexURL string var indexURL string
parsedURL, err := url.Parse(r.Config.URL) parsedURL, err := url.Parse(r.Config.URL)
if err != nil { if err != nil {
return err return "", err
} }
parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml" parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml"
indexURL = parsedURL.String() indexURL = parsedURL.String()
// TODO add user-agent // 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 { if err != nil {
return err return "", err
} }
index, err := ioutil.ReadAll(resp) index, err := ioutil.ReadAll(resp)
if err != nil { if err != nil {
return err return "", err
} }
if _, err := loadIndex(index); err != nil { 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. // 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 { if err != nil {
return "", err 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) 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 // 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 { if err != nil {
return "", err return "", err
} }

@ -34,8 +34,6 @@ import (
"helm.sh/helm/pkg/chart" "helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/helmpath/xdg"
) )
const ( const (
@ -47,7 +45,7 @@ func TestLoadChartRepository(t *testing.T) {
r, err := NewChartRepository(&Entry{ r, err := NewChartRepository(&Entry{
Name: testRepository, Name: testRepository,
URL: testURL, URL: testURL,
}, getter.All(cli.EnvSettings{})) }, getter.All(&cli.EnvSettings{}))
if err != nil { if err != nil {
t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
} }
@ -80,7 +78,7 @@ func TestIndex(t *testing.T) {
r, err := NewChartRepository(&Entry{ r, err := NewChartRepository(&Entry{
Name: testRepository, Name: testRepository,
URL: testURL, URL: testURL,
}, getter.All(cli.EnvSettings{})) }, getter.All(&cli.EnvSettings{}))
if err != nil { if err != nil {
t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) t.Errorf("Problem creating chart repository from %s: %v", testRepository, err)
} }
@ -118,7 +116,7 @@ type CustomGetter struct {
repoUrls []string 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{ index := &IndexFile{
APIVersion: "v1", APIVersion: "v1",
Generated: time.Now(), Generated: time.Now(),
@ -132,21 +130,16 @@ func (g *CustomGetter) Get(href string) (*bytes.Buffer, error) {
} }
func TestIndexCustomSchemeDownload(t *testing.T) { func TestIndexCustomSchemeDownload(t *testing.T) {
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
repoName := "gcs-repo" repoName := "gcs-repo"
repoURL := "gs://some-gcs-bucket" repoURL := "gs://some-gcs-bucket"
myCustomGetter := &CustomGetter{} myCustomGetter := &CustomGetter{}
customGetterConstructor := func(options ...getter.Option) (getter.Getter, error) { customGetterConstructor := func(options ...getter.Option) (getter.Getter, error) {
return myCustomGetter, nil return myCustomGetter, nil
} }
providers := getter.Providers{ providers := getter.Providers{{
{
Schemes: []string{"gs"}, Schemes: []string{"gs"},
New: customGetterConstructor, New: customGetterConstructor,
}, }}
}
repo, err := NewChartRepository(&Entry{ repo, err := NewChartRepository(&Entry{
Name: repoName, Name: repoName,
URL: repoURL, URL: repoURL,
@ -154,6 +147,7 @@ func TestIndexCustomSchemeDownload(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Problem loading chart repository from %s: %v", repoURL, err) t.Fatalf("Problem loading chart repository from %s: %v", repoURL, err)
} }
repo.CachePath = ensure.TempDir(t)
tempIndexFile, err := ioutil.TempFile("", "test-repo") tempIndexFile, err := ioutil.TempFile("", "test-repo")
if err != nil { if err != nil {
@ -161,8 +155,9 @@ func TestIndexCustomSchemeDownload(t *testing.T) {
} }
defer os.Remove(tempIndexFile.Name()) defer os.Remove(tempIndexFile.Name())
if err := repo.DownloadIndexFile(); err != nil { idx, err := repo.DownloadIndexFile()
t.Fatalf("Failed to download index file: %v", err) if err != nil {
t.Fatalf("Failed to download index file to %s: %v", idx, err)
} }
if len(myCustomGetter.repoUrls) != 1 { if len(myCustomGetter.repoUrls) != 1 {
@ -282,23 +277,21 @@ func startLocalServerForTests(handler http.Handler) (*httptest.Server, error) {
} }
func TestFindChartInRepoURL(t *testing.T) { func TestFindChartInRepoURL(t *testing.T) {
setupCacheHome(t)
srv, err := startLocalServerForTests(nil) srv, err := startLocalServerForTests(nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer srv.Close() 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 { if err != nil {
t.Errorf("%s", err) t.Fatalf("%v", err)
} }
if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" { if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" {
t.Errorf("%s is not the valid URL", chartURL) 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 { if err != nil {
t.Errorf("%s", err) t.Errorf("%s", err)
} }
@ -308,13 +301,14 @@ func TestFindChartInRepoURL(t *testing.T) {
} }
func TestErrorFindChartInRepoURL(t *testing.T) { func TestErrorFindChartInRepoURL(t *testing.T) {
setupCacheHome(t)
_, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", getter.All(cli.EnvSettings{})) g := getter.All(&cli.EnvSettings{
if err == nil { 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") t.Errorf("Expected error for bad chart URL, but did not get any errors")
} } 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`) {
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`) {
t.Errorf("Expected error for bad chart URL, but got a different error (%v)", err) 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() defer srv.Close()
_, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", getter.All(cli.EnvSettings{})) if _, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", g); err == nil {
if err == nil {
t.Errorf("Expected error for chart not found, but did not get any errors") t.Errorf("Expected error for chart not found, but did not get any errors")
} } else if err.Error() != `chart "nginx1" not found in `+srv.URL+` repository` {
if err != nil && 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) 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 = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", g); err == nil {
if err == nil {
t.Errorf("Expected error for chart not found, but did not get any errors") t.Errorf("Expected error for chart not found, but did not get any errors")
} } else if err.Error() != `chart "nginx1" version "0.1.0" not found in `+srv.URL+` repository` {
if err != nil && 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) 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 = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", g); err == nil {
if err == nil {
t.Errorf("Expected error for no chart URLs available, but did not get any errors") t.Errorf("Expected error for no chart URLs available, but did not get any errors")
} } else if err.Error() != `chart "chartWithNoURL" has no downloadable URLs` {
if err != nil && err.Error() != `chart "chartWithNoURL" has no downloadable URLs` {
t.Errorf("Expected error for chart not found, but got a different error (%v)", err) 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) 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)
}
}

@ -158,7 +158,7 @@ func (i IndexFile) Get(name, version string) (*ChartVersion, error) {
} }
var constraint *semver.Constraints var constraint *semver.Constraints
if len(version) == 0 { if version == "" {
constraint, _ = semver.NewConstraint("*") constraint, _ = semver.NewConstraint("*")
} else { } else {
var err error var err error
@ -276,10 +276,7 @@ func loadIndex(data []byte) (*IndexFile, error) {
} }
i.SortEntries() i.SortEntries()
if i.APIVersion == "" { if i.APIVersion == "" {
// When we leave Beta, we should remove legacy support and just return i, ErrNoAPIVersion
// return this error:
//return i, ErrNoAPIVersion
return loadUnversionedIndex(data)
} }
return i, nil return i, nil
} }

@ -24,7 +24,6 @@ import (
"helm.sh/helm/pkg/chart" "helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/helmpath"
) )
const ( const (
@ -135,35 +134,32 @@ func TestDownloadIndexFile(t *testing.T) {
} }
defer srv.Close() defer srv.Close()
setupCacheHome(t)
r, err := NewChartRepository(&Entry{ r, err := NewChartRepository(&Entry{
Name: testRepo, Name: testRepo,
URL: srv.URL, URL: srv.URL,
}, getter.All(cli.EnvSettings{})) }, getter.All(&cli.EnvSettings{}))
if err != nil { if err != nil {
t.Errorf("Problem creating chart repository from %s: %v", testRepo, err) t.Errorf("Problem creating chart repository from %s: %v", testRepo, err)
} }
if err := r.DownloadIndexFile(); err != nil { idx, err := r.DownloadIndexFile()
t.Errorf("%#v", err) if err != nil {
t.Fatalf("Failed to download index file to %s: %#v", idx, err)
} }
if _, err := os.Stat(helmpath.CacheIndex(testRepo)); err != nil { if _, err := os.Stat(idx); err != nil {
t.Errorf("error finding created index file: %#v", err) t.Fatalf("error finding created index file: %#v", err)
} }
b, err := ioutil.ReadFile(helmpath.CacheIndex(testRepo)) b, err := ioutil.ReadFile(idx)
if err != nil { if err != nil {
t.Errorf("error reading index file: %#v", err) t.Fatalf("error reading index file: %#v", err)
} }
i, err := loadIndex(b) i, err := loadIndex(b)
if err != nil { if err != nil {
t.Errorf("Index %q failed to parse: %s", testfile, err) t.Fatalf("Index %q failed to parse: %s", testfile, err)
return
} }
verifyLocalIndex(t, i) verifyLocalIndex(t, i)
} }
@ -175,19 +171,16 @@ func verifyLocalIndex(t *testing.T, i *IndexFile) {
alpine, ok := i.Entries["alpine"] alpine, ok := i.Entries["alpine"]
if !ok { if !ok {
t.Errorf("'alpine' section not found.") t.Fatalf("'alpine' section not found.")
return
} }
if l := len(alpine); l != 1 { if l := len(alpine); l != 1 {
t.Errorf("'alpine' should have 1 chart, got %d", l) t.Fatalf("'alpine' should have 1 chart, got %d", l)
return
} }
nginx, ok := i.Entries["nginx"] nginx, ok := i.Entries["nginx"]
if !ok || len(nginx) != 2 { if !ok || len(nginx) != 2 {
t.Error("Expected 2 nginx entries") t.Fatalf("Expected 2 nginx entries")
return
} }
expects := []*ChartVersion{ expects := []*ChartVersion{
@ -292,7 +285,7 @@ func TestIndexDirectory(t *testing.T) {
} }
frob := frobs[0] frob := frobs[0]
if len(frob.Digest) == 0 { if frob.Digest == "" {
t.Errorf("Missing digest of file %s.", frob.Name) t.Errorf("Missing digest of file %s.", frob.Name)
} }
if frob.URLs[0] != test.downloadLink { if frob.URLs[0] != test.downloadLink {

@ -19,16 +19,13 @@ package repo // import "helm.sh/helm/pkg/repo"
import ( import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"sigs.k8s.io/yaml" "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 // File represents the repositories.yaml file
type File struct { type File struct {
APIVersion string `json:"apiVersion"` APIVersion string `json:"apiVersion"`
@ -48,41 +45,18 @@ func NewFile() *File {
} }
// LoadFile takes a file at the given path and returns a File object // 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) { func LoadFile(path string) (*File, error) {
b, err := ioutil.ReadFile(path) b, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
if os.IsNotExist(err) { 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 return nil, err
} }
r := &File{} r := &File{}
err = yaml.Unmarshal(b, r) err = yaml.Unmarshal(b, r)
if err != nil { return r, err
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
} }
// Add adds one or more repo entries to a repo file. // 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. // entry with the same name doesn't exist in the repo file it will add it.
func (r *File) Update(re ...*Entry) { func (r *File) Update(re ...*Entry) {
for _, target := range re { for _, target := range re {
found := false r.update(target)
for j, repo := range r.Repositories {
if repo.Name == target.Name {
r.Repositories[j] = target
found = true
break
}
} }
if !found { }
r.Add(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. // 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 { if err != nil {
return err return err
} }
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
return ioutil.WriteFile(path, data, perm) return ioutil.WriteFile(path, data, perm)
} }

@ -16,10 +16,12 @@ limitations under the License.
package repo package repo
import "testing" import (
import "io/ioutil" "io/ioutil"
import "os" "os"
import "strings" "strings"
"testing"
)
const testRepositoriesFile = "testdata/repositories.yaml" 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) { func TestRemoveRepository(t *testing.T) {
sampleRepository := NewFile() sampleRepository := NewFile()
sampleRepository.Add( sampleRepository.Add(
@ -184,7 +165,7 @@ func TestWriteFile(t *testing.T) {
t.Errorf("failed to create test-file (%v)", err) t.Errorf("failed to create test-file (%v)", err)
} }
defer os.Remove(file.Name()) 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) t.Errorf("failed to write file (%v)", err)
} }
@ -200,10 +181,9 @@ func TestWriteFile(t *testing.T) {
} }
func TestRepoNotExists(t *testing.T) { func TestRepoNotExists(t *testing.T) {
_, err := LoadFile("/this/path/does/not/exist.yaml") if _, err := LoadFile("/this/path/does/not/exist.yaml"); err == nil {
if err == nil {
t.Errorf("expected err to be non-nil when path does not exist") 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`") { } else if !strings.Contains(err.Error(), "couldn't load repositories file") {
t.Errorf("expected prompt to run `helm init` when repositories file does not exist") t.Errorf("expected prompt `couldn't load repositories file`")
} }
} }

@ -24,7 +24,6 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
) )
@ -70,7 +69,7 @@ func NewServer(docroot string) *Server {
} }
srv.Start() srv.Start()
// Add the testing repository as the only repo. // 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) panic(err)
} }
return srv return srv
@ -108,7 +107,7 @@ func (s *Server) CopyCharts(origin string) ([]string, error) {
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
if err := ioutil.WriteFile(newname, data, 0755); err != nil { if err := ioutil.WriteFile(newname, data, 0644); err != nil {
return []string{}, err return []string{}, err
} }
copied[i] = newname copied[i] = newname
@ -132,7 +131,7 @@ func (s *Server) CreateIndex() error {
} }
ifile := filepath.Join(s.docroot, "index.yaml") ifile := filepath.Join(s.docroot, "index.yaml")
return ioutil.WriteFile(ifile, d, 0755) return ioutil.WriteFile(ifile, d, 0644)
} }
func (s *Server) Start() { 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. // This makes it possible to simulate a local cache of a repository.
func (s *Server) LinkIndices() error { func (s *Server) LinkIndices() error {
lstart := filepath.Join(s.docroot, "index.yaml") lstart := filepath.Join(s.docroot, "index.yaml")
ldest := helmpath.CacheIndex("test") ldest := filepath.Join(s.docroot, "test-index.yaml")
return os.Symlink(lstart, ldest) return os.Symlink(lstart, ldest)
} }
// setTestingRepository sets up a testing repository.yaml with only the given URL. // 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 := repo.NewFile()
r.Add(&repo.Entry{ r.Add(&repo.Entry{
Name: "test", Name: "test",
URL: url, URL: url,
}) })
return r.WriteFile(helmpath.RepositoryFile(), 0644) return r.WriteFile(fname, 0644)
} }

@ -24,24 +24,23 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"helm.sh/helm/internal/test/ensure" "helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
) )
// Young'n, in these here parts, we test our tests. // Young'n, in these here parts, we test our tests.
func TestServer(t *testing.T) { func TestServer(t *testing.T) {
ensure.HelmHome(t) defer ensure.HelmHome(t)()
defer ensure.CleanHomeDirs(t)
srv := NewServer(helmpath.CachePath()) rootDir := ensure.TempDir(t)
srv := NewServer(rootDir)
defer srv.Stop() defer srv.Stop()
c, err := srv.CopyCharts("testdata/*.tgz") c, err := srv.CopyCharts("testdata/*.tgz")
if err != nil { if err != nil {
// Some versions of Go don't correctly fire defer on Fatal. // Some versions of Go don't correctly fire defer on Fatal.
t.Error(err) t.Fatal(err)
return
} }
if len(c) != 1 { 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, err := http.Get(srv.URL() + "/examplechart-0.1.0.tgz")
res.Body.Close()
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
return
} }
if res.ContentLength < 500 { if res.ContentLength < 500 {
@ -64,26 +63,22 @@ func TestServer(t *testing.T) {
res, err = http.Get(srv.URL() + "/index.yaml") res, err = http.Get(srv.URL() + "/index.yaml")
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
return
} }
data, err := ioutil.ReadAll(res.Body) data, err := ioutil.ReadAll(res.Body)
res.Body.Close() res.Body.Close()
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
return
} }
m := repo.NewIndexFile() m := repo.NewIndexFile()
if err := yaml.Unmarshal(data, m); err != nil { if err := yaml.Unmarshal(data, m); err != nil {
t.Error(err) t.Fatal(err)
return
} }
if l := len(m.Entries); l != 1 { if l := len(m.Entries); l != 1 {
t.Errorf("Expected 1 entry, got %d", l) t.Fatalf("Expected 1 entry, got %d", l)
return
} }
expect := "examplechart" expect := "examplechart"
@ -92,28 +87,26 @@ func TestServer(t *testing.T) {
} }
res, err = http.Get(srv.URL() + "/index.yaml-nosuchthing") res, err = http.Get(srv.URL() + "/index.yaml-nosuchthing")
res.Body.Close()
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
return
} }
if res.StatusCode != 404 { 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) { func TestNewTempServer(t *testing.T) {
ensure.HelmHome(t) defer ensure.HelmHome(t)()
defer ensure.CleanHomeDirs(t)
srv, err := NewTempServer("testdata/examplechart-0.1.0.tgz") srv, err := NewTempServer("testdata/examplechart-0.1.0.tgz")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer func() { defer srv.Stop()
srv.Stop()
}()
res, err := http.Head(srv.URL() + "/examplechart-0.1.0.tgz") res, err := http.Head(srv.URL() + "/examplechart-0.1.0.tgz")
res.Body.Close()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }

Loading…
Cancel
Save