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

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

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

@ -54,17 +54,17 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
chartpath = filepath.Clean(args[0])
}
man := &downloader.Manager{
Out: out,
ChartPath: chartpath,
Keyring: client.Keyring,
Getters: getter.All(settings),
Out: out,
ChartPath: chartpath,
Keyring: client.Keyring,
Getters: getter.All(settings),
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
}
if client.Verify {
man.Verify = downloader.VerifyIfPossible
}
if settings.Debug {
man.Debug = true
}
return man.Build()
},
}

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

@ -58,18 +58,18 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
chartpath = filepath.Clean(args[0])
}
man := &downloader.Manager{
Out: out,
ChartPath: chartpath,
Keyring: client.Keyring,
SkipUpdate: client.SkipRefresh,
Getters: getter.All(settings),
Out: out,
ChartPath: chartpath,
Keyring: client.Keyring,
SkipUpdate: client.SkipRefresh,
Getters: getter.All(settings),
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
}
if client.Verify {
man.Verify = downloader.VerifyAlways
}
if settings.Debug {
man.Debug = true
}
return man.Update()
},
}

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

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

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

@ -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)
vals, err := valueOpts.MergeValues(settings)
p := getter.All(settings)
vals, err := valueOpts.MergeValues(p)
if err != nil {
return nil, err
}
@ -197,11 +198,13 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
if err := action.CheckDependencies(chartRequested, req); err != nil {
if client.DependencyUpdate {
man := &downloader.Manager{
Out: out,
ChartPath: cp,
Keyring: client.ChartPathOptions.Keyring,
SkipUpdate: false,
Getters: getter.All(settings),
Out: out,
ChartPath: cp,
Keyring: client.ChartPathOptions.Keyring,
SkipUpdate: false,
Getters: p,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
}
if err := man.Update(); err != nil {
return nil, err

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

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

@ -62,7 +62,10 @@ func newPackageCmd(out io.Writer) *cobra.Command {
return errors.New("--keyring is required for signing a package")
}
}
vals, err := valueOpts.MergeValues(settings)
client.RepositoryConfig = settings.RepositoryConfig
client.RepositoryCache = settings.RepositoryCache
p := getter.All(settings)
vals, err := valueOpts.MergeValues(p)
if err != nil {
return err
}
@ -75,11 +78,13 @@ func newPackageCmd(out io.Writer) *cobra.Command {
if client.DependencyUpdate {
downloadManager := &downloader.Manager{
Out: ioutil.Discard,
ChartPath: path,
Keyring: client.Keyring,
Getters: getter.All(settings),
Debug: settings.Debug,
Out: ioutil.Discard,
ChartPath: path,
Keyring: client.Keyring,
Getters: p,
Debug: settings.Debug,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
}
if err := downloadManager.Update(); err != nil {

@ -32,19 +32,14 @@ import (
"helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/chart/loader"
"helm.sh/helm/pkg/chartutil"
"helm.sh/helm/pkg/helmpath"
)
func TestPackage(t *testing.T) {
statExe := "stat"
statFileMsg := "no such file or directory"
if runtime.GOOS == "windows" {
statExe = "FindFirstFile"
statFileMsg = "The system cannot find the file specified."
}
defer resetEnv()()
tests := []struct {
name string
flags map[string]string
@ -100,13 +95,6 @@ func TestPackage(t *testing.T) {
expect: "",
hasfile: "toot/alpine-0.1.0.tgz",
},
{
name: "package --destination does-not-exist",
args: []string{"testdata/testcharts/alpine"},
flags: map[string]string{"destination": "does-not-exist"},
expect: fmt.Sprintf("failed to save: %s does-not-exist: %s", statExe, statFileMsg),
err: true,
},
{
name: "package --sign --key=KEY --keyring=KEYRING testdata/testcharts/alpine",
args: []string{"testdata/testcharts/alpine"},
@ -139,75 +127,70 @@ func TestPackage(t *testing.T) {
t.Fatal(err)
}
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
t.Logf("Running tests in %s", helmpath.CachePath())
defer testChdir(t, helmpath.CachePath())()
if err := os.Mkdir("toot", 0777); err != nil {
t.Fatal(err)
}
for _, tt := range tests {
buf := bytes.NewBuffer(nil)
c := newPackageCmd(buf)
t.Run(tt.name, func(t *testing.T) {
cachePath := ensure.TempDir(t)
defer testChdir(t, cachePath)()
// This is an unfortunate byproduct of the tmpdir
if v, ok := tt.flags["keyring"]; ok && len(v) > 0 {
tt.flags["keyring"] = filepath.Join(origDir, v)
}
if err := os.MkdirAll("toot", 0777); err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
c := newPackageCmd(&buf)
setFlags(c, tt.flags)
re := regexp.MustCompile(tt.expect)
// This is an unfortunate byproduct of the tmpdir
if v, ok := tt.flags["keyring"]; ok && len(v) > 0 {
tt.flags["keyring"] = filepath.Join(origDir, v)
}
adjustedArgs := make([]string, len(tt.args))
for i, f := range tt.args {
adjustedArgs[i] = filepath.Join(origDir, f)
}
setFlags(c, tt.flags)
re := regexp.MustCompile(tt.expect)
err := c.RunE(c, adjustedArgs)
if err != nil {
if tt.err && re.MatchString(err.Error()) {
continue
adjustedArgs := make([]string, len(tt.args))
for i, f := range tt.args {
adjustedArgs[i] = filepath.Join(origDir, f)
}
t.Errorf("%q: expected error %q, got %q", tt.name, tt.expect, err)
continue
}
if !re.Match(buf.Bytes()) {
t.Errorf("%q: expected output %q, got %q", tt.name, tt.expect, buf.String())
}
err := c.RunE(c, adjustedArgs)
if err != nil {
if tt.err && re.MatchString(err.Error()) {
return
}
t.Fatalf("%q: expected error %q, got %q", tt.name, tt.expect, err)
}
if len(tt.hasfile) > 0 {
if fi, err := os.Stat(tt.hasfile); err != nil {
t.Errorf("%q: expected file %q, got err %q", tt.name, tt.hasfile, err)
} else if fi.Size() == 0 {
t.Errorf("%q: file %q has zero bytes.", tt.name, tt.hasfile)
if !re.Match(buf.Bytes()) {
t.Errorf("%q: expected output %q, got %q", tt.name, tt.expect, buf.String())
}
}
if v, ok := tt.flags["sign"]; ok && v == "1" {
if fi, err := os.Stat(tt.hasfile + ".prov"); err != nil {
t.Errorf("%q: expected provenance file", tt.name)
} else if fi.Size() == 0 {
t.Errorf("%q: provenance file is empty", tt.name)
if len(tt.hasfile) > 0 {
if fi, err := os.Stat(tt.hasfile); err != nil {
t.Errorf("%q: expected file %q, got err %q", tt.name, tt.hasfile, err)
} else if fi.Size() == 0 {
t.Errorf("%q: file %q has zero bytes.", tt.name, tt.hasfile)
}
}
}
if v, ok := tt.flags["sign"]; ok && v == "1" {
if fi, err := os.Stat(tt.hasfile + ".prov"); err != nil {
t.Errorf("%q: expected provenance file", tt.name)
} else if fi.Size() == 0 {
t.Errorf("%q: provenance file is empty", tt.name)
}
}
})
}
}
func TestSetAppVersion(t *testing.T) {
defer resetEnv()()
var ch *chart.Chart
expectedAppVersion := "app-version-foo"
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
dir := ensure.TempDir(t)
c := newPackageCmd(&bytes.Buffer{})
flags := map[string]string{
"destination": helmpath.CachePath(),
"destination": dir,
"app-version": expectedAppVersion,
}
setFlags(c, flags)
@ -215,7 +198,7 @@ func TestSetAppVersion(t *testing.T) {
t.Errorf("unexpected error %q", err)
}
chartPath := filepath.Join(helmpath.CachePath(), "alpine-0.1.0.tgz")
chartPath := filepath.Join(dir, "alpine-0.1.0.tgz")
if fi, err := os.Stat(chartPath); err != nil {
t.Errorf("expected file %q, got err %q", chartPath, err)
} else if fi.Size() == 0 {
@ -223,7 +206,7 @@ func TestSetAppVersion(t *testing.T) {
}
ch, err := loader.Load(chartPath)
if err != nil {
t.Errorf("unexpected error loading packaged chart: %v", err)
t.Fatalf("unexpected error loading packaged chart: %v", err)
}
if ch.Metadata.AppVersion != expectedAppVersion {
t.Errorf("expected app-version %q, found %q", expectedAppVersion, ch.Metadata.AppVersion)
@ -233,6 +216,8 @@ func TestSetAppVersion(t *testing.T) {
func TestPackageValues(t *testing.T) {
defer resetEnv()()
repoFile := "testdata/helmhome/helm/repositories.yaml"
testCases := []struct {
desc string
args []string
@ -243,33 +228,32 @@ func TestPackageValues(t *testing.T) {
{
desc: "helm package, single values file",
args: []string{"testdata/testcharts/alpine"},
flags: map[string]string{"repository-config": repoFile},
valuefilesContents: []string{"Name: chart-name-foo"},
expected: []string{"Name: chart-name-foo"},
},
{
desc: "helm package, multiple values files",
args: []string{"testdata/testcharts/alpine"},
flags: map[string]string{"repository-config": repoFile},
valuefilesContents: []string{"Name: chart-name-foo", "foo: bar"},
expected: []string{"Name: chart-name-foo", "foo: bar"},
},
{
desc: "helm package, with set option",
args: []string{"testdata/testcharts/alpine"},
flags: map[string]string{"set": "Name=chart-name-foo"},
flags: map[string]string{"set": "Name=chart-name-foo", "repository-config": repoFile},
expected: []string{"Name: chart-name-foo"},
},
{
desc: "helm package, set takes precedence over value file",
args: []string{"testdata/testcharts/alpine"},
valuefilesContents: []string{"Name: chart-name-foo"},
flags: map[string]string{"set": "Name=chart-name-bar"},
flags: map[string]string{"set": "Name=chart-name-bar", "repository-config": repoFile},
expected: []string{"Name: chart-name-bar"},
},
}
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
for _, tc := range testCases {
var files []string
for _, contents := range tc.valuefilesContents {
@ -283,58 +267,50 @@ func TestPackageValues(t *testing.T) {
t.Errorf("unexpected error parsing values: %q", err)
}
runAndVerifyPackageCommandValues(t, tc.args, tc.flags, valueFiles, expected)
}
}
outputDir := ensure.TempDir(t)
func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[string]string, valueFiles string, expected chartutil.Values) {
t.Helper()
outputDir := ensure.TempDir(t)
if len(tc.flags) == 0 {
tc.flags = make(map[string]string)
}
tc.flags["destination"] = outputDir
if len(flags) == 0 {
flags = make(map[string]string)
}
flags["destination"] = outputDir
if len(valueFiles) > 0 {
tc.flags["values"] = valueFiles
}
if len(valueFiles) > 0 {
flags["values"] = valueFiles
}
cmd := newPackageCmd(&bytes.Buffer{})
setFlags(cmd, tc.flags)
if err := cmd.RunE(cmd, tc.args); err != nil {
t.Fatalf("unexpected error: %q", err)
}
cmd := newPackageCmd(&bytes.Buffer{})
setFlags(cmd, flags)
if err := cmd.RunE(cmd, args); err != nil {
t.Errorf("unexpected error: %q", err)
}
outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz")
verifyOutputChartExists(t, outputFile)
outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz")
verifyOutputChartExists(t, outputFile)
actual, err := getChartValues(outputFile)
if err != nil {
t.Fatalf("unexpected error extracting chart values: %q", err)
}
actual, err := getChartValues(outputFile)
if err != nil {
t.Errorf("unexpected error extracting chart values: %q", err)
verifyValues(t, actual, expected)
}
verifyValues(t, actual, expected)
}
func createValuesFile(t *testing.T, data string) string {
outputDir := ensure.TempDir(t)
outputFile := filepath.Join(outputDir, "values.yaml")
if err := ioutil.WriteFile(outputFile, []byte(data), 0755); err != nil {
if err := ioutil.WriteFile(outputFile, []byte(data), 0644); err != nil {
t.Fatalf("err: %s", err)
}
return outputFile
}
func getChartValues(chartPath string) (chartutil.Values, error) {
chart, err := loader.Load(chartPath)
if err != nil {
return nil, err
}
return chart.Values, nil
}

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

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

@ -18,16 +18,11 @@ package main
import (
"bytes"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/spf13/cobra"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/helmpath/xdg"
"helm.sh/helm/pkg/plugin"
)
func TestManuallyProcessArgs(t *testing.T) {
@ -63,25 +58,22 @@ func TestManuallyProcessArgs(t *testing.T) {
}
func TestLoadPlugins(t *testing.T) {
defer resetEnv()()
os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome")
os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome")
os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome")
settings.PluginsDirectory = "testdata/helmhome/helm/plugins"
settings.RepositoryConfig = "testdata/helmhome/helm/repositories.yaml"
settings.RepositoryCache = "testdata/helmhome/helm/repository"
out := bytes.NewBuffer(nil)
cmd := &cobra.Command{}
loadPlugins(cmd, out)
var (
out bytes.Buffer
cmd cobra.Command
)
loadPlugins(&cmd, &out)
envs := strings.Join([]string{
"fullenv",
helmpath.Plugins() + "/fullenv",
helmpath.Plugins(),
helmpath.CachePath(),
helmpath.ConfigPath(),
helmpath.DataPath(),
helmpath.RepositoryFile(),
helmpath.RepositoryCache(),
"testdata/helmhome/helm/plugins/fullenv",
"testdata/helmhome/helm/plugins",
"testdata/helmhome/helm/repositories.yaml",
"testdata/helmhome/helm/repository",
os.Args[0],
}, "\n")
@ -95,7 +87,7 @@ func TestLoadPlugins(t *testing.T) {
}{
{"args", "echo args", "This echos args", "-a -b -c\n", []string{"-a", "-b", "-c"}},
{"echo", "echo stuff", "This echos stuff", "hello\n", []string{}},
{"env", "env stuff", "show the env", helmpath.DataPath() + "\n", []string{}},
{"env", "env stuff", "show the env", "env\n", []string{}},
{"fullenv", "show env vars", "show all env vars", envs + "\n", []string{}},
}
@ -123,7 +115,7 @@ func TestLoadPlugins(t *testing.T) {
// tests until this is fixed
if runtime.GOOS != "windows" {
if err := pp.RunE(pp, tt.args); err != nil {
t.Errorf("Error running %s: %s", tt.use, err)
t.Errorf("Error running %s: %+v", tt.use, err)
}
if out.String() != tt.expect {
t.Errorf("Expected %s to output:\n%s\ngot\n%s", tt.use, tt.expect, out.String())
@ -133,9 +125,8 @@ func TestLoadPlugins(t *testing.T) {
}
func TestLoadPlugins_HelmNoPlugins(t *testing.T) {
defer resetEnv()()
os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome")
settings.PluginsDirectory = "testdata/helmhome/helm/plugins"
settings.RepositoryConfig = "testdata/helmhome/helm/repository"
os.Setenv("HELM_NO_PLUGINS", "1")
@ -148,34 +139,3 @@ func TestLoadPlugins_HelmNoPlugins(t *testing.T) {
t.Fatalf("Expected 0 plugins, got %d", len(plugins))
}
}
func TestSetupEnv(t *testing.T) {
defer resetEnv()()
name := "pequod"
os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome")
base := filepath.Join(helmpath.Plugins(), name)
settings.Debug = true
defer func() {
settings.Debug = false
}()
plugin.SetupPluginEnv(settings, name, base)
for _, tt := range []struct {
name string
expect string
}{
{"HELM_PLUGIN_NAME", name},
{"HELM_PLUGIN_DIR", base},
{"HELM_DEBUG", "1"},
{"HELM_PATH_REPOSITORY_FILE", helmpath.RepositoryFile()},
{"HELM_PATH_CACHE", helmpath.CachePath()},
{"HELM_PATH_CONFIG", helmpath.ConfigPath()},
{"HELM_PATH_DATA", helmpath.DataPath()},
{"HELM_PATH_STARTER", helmpath.Starters()},
{"HELM_PLUGIN", helmpath.Plugins()},
} {
if got := os.Getenv(tt.name); got != tt.expect {
t.Errorf("Expected $%s=%q, got %q", tt.name, tt.expect, got)
}
}
}

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

@ -21,19 +21,12 @@ import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo/repotest"
)
func TestPullCmd(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*")
if err != nil {
t.Fatal(err)
@ -47,7 +40,7 @@ func TestPullCmd(t *testing.T) {
// all flags will get "-d outdir" appended.
tests := []struct {
name string
args []string
args string
wantError bool
failExpect string
expectFile string
@ -56,47 +49,47 @@ func TestPullCmd(t *testing.T) {
}{
{
name: "Basic chart fetch",
args: []string{"test/signtest"},
args: "test/signtest",
expectFile: "./signtest-0.1.0.tgz",
},
{
name: "Chart fetch with version",
args: []string{"test/signtest --version=0.1.0"},
args: "test/signtest --version=0.1.0",
expectFile: "./signtest-0.1.0.tgz",
},
{
name: "Fail chart fetch with non-existent version",
args: []string{"test/signtest --version=99.1.0"},
args: "test/signtest --version=99.1.0",
wantError: true,
failExpect: "no such chart",
},
{
name: "Fail fetching non-existent chart",
args: []string{"test/nosuchthing"},
args: "test/nosuchthing",
failExpect: "Failed to fetch",
wantError: true,
},
{
name: "Fetch and verify",
args: []string{"test/signtest --verify --keyring testdata/helm-test-key.pub"},
args: "test/signtest --verify --keyring testdata/helm-test-key.pub",
expectFile: "./signtest-0.1.0.tgz",
expectVerify: true,
},
{
name: "Fetch and fail verify",
args: []string{"test/reqtest --verify --keyring testdata/helm-test-key.pub"},
args: "test/reqtest --verify --keyring testdata/helm-test-key.pub",
failExpect: "Failed to fetch provenance",
wantError: true,
},
{
name: "Fetch and untar",
args: []string{"test/signtest --untar --untardir signtest"},
args: "test/signtest --untar --untardir signtest",
expectFile: "./signtest",
expectDir: true,
},
{
name: "Fetch, verify, untar",
args: []string{"test/signtest --verify --keyring=testdata/helm-test-key.pub --untar --untardir signtest"},
args: "test/signtest --verify --keyring=testdata/helm-test-key.pub --untar --untardir signtest",
expectFile: "./signtest",
expectDir: true,
expectVerify: true,
@ -104,59 +97,62 @@ func TestPullCmd(t *testing.T) {
{
name: "Chart fetch using repo URL",
expectFile: "./signtest-0.1.0.tgz",
args: []string{"signtest --repo", srv.URL()},
args: "signtest --repo " + srv.URL(),
},
{
name: "Fail fetching non-existent chart on repo URL",
args: []string{"someChart --repo", srv.URL()},
args: "someChart --repo " + srv.URL(),
failExpect: "Failed to fetch chart",
wantError: true,
},
{
name: "Specific version chart fetch using repo URL",
expectFile: "./signtest-0.1.0.tgz",
args: []string{"signtest --version=0.1.0 --repo", srv.URL()},
args: "signtest --version=0.1.0 --repo " + srv.URL(),
},
{
name: "Specific version chart fetch using repo URL",
args: []string{"signtest --version=0.2.0 --repo", srv.URL()},
args: "signtest --version=0.2.0 --repo " + srv.URL(),
failExpect: "Failed to fetch chart version",
wantError: true,
},
}
for _, tt := range tests {
outdir := filepath.Join(helmpath.DataPath(), "testout")
os.RemoveAll(outdir)
os.Mkdir(outdir, 0755)
cmd := strings.Join(append(tt.args, "-d", "'"+outdir+"'"), " ")
_, out, err := executeActionCommand("fetch " + cmd)
if err != nil {
if tt.wantError {
continue
t.Run(tt.name, func(t *testing.T) {
outdir := srv.Root()
cmd := fmt.Sprintf("fetch %s -d '%s' --repository-config %s --repository-cache %s ",
tt.args,
outdir,
filepath.Join(outdir, "repositories.yaml"),
outdir,
)
_, out, err := executeActionCommand(cmd)
if err != nil {
if tt.wantError {
return
}
t.Fatalf("%q reported error: %s", tt.name, err)
}
t.Errorf("%q reported error: %s", tt.name, err)
continue
}
if tt.expectVerify {
pointerAddressPattern := "0[xX][A-Fa-f0-9]+"
sha256Pattern := "[A-Fa-f0-9]{64}"
verificationRegex := regexp.MustCompile(
fmt.Sprintf("Verification: &{%s sha256:%s signtest-0.1.0.tgz}\n", pointerAddressPattern, sha256Pattern))
if !verificationRegex.MatchString(out) {
t.Errorf("%q: expected match for regex %s, got %s", tt.name, verificationRegex, out)
if tt.expectVerify {
pointerAddressPattern := "0[xX][A-Fa-f0-9]+"
sha256Pattern := "[A-Fa-f0-9]{64}"
verificationRegex := regexp.MustCompile(
fmt.Sprintf("Verification: &{%s sha256:%s signtest-0.1.0.tgz}\n", pointerAddressPattern, sha256Pattern))
if !verificationRegex.MatchString(out) {
t.Errorf("%q: expected match for regex %s, got %s", tt.name, verificationRegex, out)
}
}
}
ef := filepath.Join(outdir, tt.expectFile)
fi, err := os.Stat(ef)
if err != nil {
t.Errorf("%q: expected a file at %s. %s", tt.name, ef, err)
}
if fi.IsDir() != tt.expectDir {
t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir)
}
ef := filepath.Join(outdir, tt.expectFile)
fi, err := os.Stat(ef)
if err != nil {
t.Errorf("%q: expected a file at %s. %s", tt.name, ef, err)
}
if fi.IsDir() != tt.expectDir {
t.Errorf("%q: expected directory=%t, but it's not.", tt.name, tt.expectDir)
}
})
}
}

@ -19,13 +19,15 @@ package main
import (
"fmt"
"io"
"io/ioutil"
"os"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
"helm.sh/helm/cmd/helm/require"
"helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo"
)
@ -34,11 +36,14 @@ type repoAddOptions struct {
url string
username string
password string
noupdate bool
noUpdate bool
certFile string
keyFile string
caFile string
repoFile string
repoCache string
}
func newRepoAddCmd(out io.Writer) *cobra.Command {
@ -51,6 +56,8 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
o.name = args[0]
o.url = args[1]
o.repoFile = settings.RepositoryConfig
o.repoCache = settings.RepositoryCache
return o.run(out)
},
@ -59,7 +66,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
f := cmd.Flags()
f.StringVar(&o.username, "username", "", "chart repository username")
f.StringVar(&o.password, "password", "", "chart repository password")
f.BoolVar(&o.noupdate, "no-update", false, "raise error if repo is already registered")
f.BoolVar(&o.noUpdate, "no-update", false, "raise error if repo is already registered")
f.StringVar(&o.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&o.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
@ -68,31 +75,28 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
}
func (o *repoAddOptions) run(out io.Writer) error {
if err := addRepository(o.name, o.url, o.username, o.password, o.certFile, o.keyFile, o.caFile, o.noupdate); err != nil {
b, err := ioutil.ReadFile(o.repoFile)
if err != nil && !os.IsNotExist(err) {
return err
}
fmt.Fprintf(out, "%q has been added to your repositories\n", o.name)
return nil
}
func addRepository(name, url, username, password string, certFile, keyFile, caFile string, noUpdate bool) error {
f, err := repo.LoadFile(helmpath.RepositoryFile())
if err != nil {
var f repo.File
if err := yaml.Unmarshal(b, &f); err != nil {
return err
}
if noUpdate && f.Has(name) {
return errors.Errorf("repository name (%s) already exists, please specify a different name", name)
if o.noUpdate && f.Has(o.name) {
return errors.Errorf("repository name (%s) already exists, please specify a different name", o.name)
}
c := repo.Entry{
Name: name,
URL: url,
Username: username,
Password: password,
CertFile: certFile,
KeyFile: keyFile,
CAFile: caFile,
Name: o.name,
URL: o.url,
Username: o.username,
Password: o.password,
CertFile: o.certFile,
KeyFile: o.keyFile,
CAFile: o.caFile,
}
r, err := repo.NewChartRepository(&c, getter.All(settings))
@ -100,11 +104,15 @@ func addRepository(name, url, username, password string, certFile, keyFile, caFi
return err
}
if err := r.DownloadIndexFile(); err != nil {
return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url)
if _, err := r.DownloadIndexFile(); err != nil {
return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", o.url)
}
f.Update(&c)
return f.WriteFile(helmpath.RepositoryFile(), 0644)
if err := f.WriteFile(o.repoFile, 0644); err != nil {
return err
}
fmt.Fprintf(out, "%q has been added to your repositories\n", o.name)
return nil
}

@ -18,47 +18,27 @@ package main
import (
"fmt"
"os"
"io/ioutil"
"path/filepath"
"testing"
"helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo"
"helm.sh/helm/pkg/repo/repotest"
)
func TestRepoAddCmd(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
srv, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil {
t.Fatal(err)
}
defer srv.Stop()
repoFile := helmpath.RepositoryFile()
if _, err := os.Stat(repoFile); err != nil {
rf := repo.NewFile()
rf.Add(&repo.Entry{
Name: "charts",
URL: "http://example.com/foo",
})
if err := rf.WriteFile(repoFile, 0644); err != nil {
t.Fatal(err)
}
}
if r, err := repo.LoadFile(repoFile); err == repo.ErrRepoOutOfDate {
if err := r.WriteFile(repoFile, 0644); err != nil {
t.Fatal(err)
}
}
repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml")
tests := []cmdTestCase{{
name: "add a repository",
cmd: fmt.Sprintf("repo add test-name %s", srv.URL()),
cmd: fmt.Sprintf("repo add test-name %s --repository-config %s", srv.URL(), repoFile),
golden: "output/repo-add.txt",
}}
@ -66,54 +46,43 @@ func TestRepoAddCmd(t *testing.T) {
}
func TestRepoAdd(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
ts, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil {
t.Fatal(err)
}
defer ts.Stop()
repoFile := helmpath.RepositoryFile()
if _, err := os.Stat(repoFile); err != nil {
rf := repo.NewFile()
rf.Add(&repo.Entry{
Name: "charts",
URL: "http://example.com/foo",
})
if err := rf.WriteFile(repoFile, 0644); err != nil {
t.Fatal(err)
}
}
if r, err := repo.LoadFile(repoFile); err == repo.ErrRepoOutOfDate {
if err := r.WriteFile(repoFile, 0644); err != nil {
t.Fatal(err)
}
}
repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml")
const testRepoName = "test-name"
if err := addRepository(testRepoName, ts.URL(), "", "", "", "", "", true); err != nil {
o := &repoAddOptions{
name: testRepoName,
url: ts.URL(),
noUpdate: true,
repoFile: repoFile,
}
if err := o.run(ioutil.Discard); err != nil {
t.Error(err)
}
f, err := repo.LoadFile(helmpath.RepositoryFile())
f, err := repo.LoadFile(repoFile)
if err != nil {
t.Error(err)
t.Fatal(err)
}
if !f.Has(testRepoName) {
t.Errorf("%s was not successfully inserted into %s", testRepoName, helmpath.RepositoryFile())
t.Errorf("%s was not successfully inserted into %s", testRepoName, repoFile)
}
if err := addRepository(testRepoName, ts.URL(), "", "", "", "", "", false); err != nil {
o.noUpdate = false
if err := o.run(ioutil.Discard); err != nil {
t.Errorf("Repository was not updated: %s", err)
}
if err := addRepository(testRepoName, ts.URL(), "", "", "", "", "", false); err != nil {
if err := o.run(ioutil.Discard); err != nil {
t.Errorf("Duplicate repository name was added")
}
}

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

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

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

@ -19,6 +19,7 @@ package main
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
@ -29,60 +30,57 @@ import (
)
func TestRepoRemove(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
ts, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil {
t.Fatal(err)
}
defer ts.Stop()
repoFile := helmpath.RepositoryFile()
if _, err := os.Stat(repoFile); err != nil {
rf := repo.NewFile()
rf.Add(&repo.Entry{
Name: "charts",
URL: "http://example.com/foo",
})
if err := rf.WriteFile(repoFile, 0644); err != nil {
t.Fatal(err)
}
}
if r, err := repo.LoadFile(repoFile); err == repo.ErrRepoOutOfDate {
if err := r.WriteFile(repoFile, 0644); err != nil {
t.Fatal(err)
}
}
rootDir := ensure.TempDir(t)
repoFile := filepath.Join(rootDir, "repositories.yaml")
const testRepoName = "test-name"
b := bytes.NewBuffer(nil)
if err := removeRepoLine(b, testRepoName); err == nil {
rmOpts := repoRemoveOptions{
name: testRepoName,
repoFile: repoFile,
repoCache: rootDir,
}
if err := rmOpts.run(os.Stderr); err == nil {
t.Errorf("Expected error removing %s, but did not get one.", testRepoName)
}
if err := addRepository(testRepoName, ts.URL(), "", "", "", "", "", true); err != nil {
o := &repoAddOptions{
name: testRepoName,
url: ts.URL(),
repoFile: repoFile,
}
if err := o.run(os.Stderr); err != nil {
t.Error(err)
}
mf, _ := os.Create(helmpath.CacheIndex(testRepoName))
idx := filepath.Join(rootDir, helmpath.CacheIndexFile(testRepoName))
mf, _ := os.Create(idx)
mf.Close()
b.Reset()
if err := removeRepoLine(b, testRepoName); err != nil {
if err := rmOpts.run(b); err != nil {
t.Errorf("Error removing %s from repositories", testRepoName)
}
if !strings.Contains(b.String(), "has been removed") {
t.Errorf("Unexpected output: %s", b.String())
}
if _, err := os.Stat(helmpath.CacheIndex(testRepoName)); err == nil {
if _, err := os.Stat(idx); err == nil {
t.Errorf("Error cache file was not removed for repository %s", testRepoName)
}
f, err := repo.LoadFile(helmpath.RepositoryFile())
f, err := repo.LoadFile(repoFile)
if err != nil {
t.Error(err)
}

@ -26,7 +26,6 @@ import (
"helm.sh/helm/cmd/helm/require"
"helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/repo"
)
@ -38,7 +37,8 @@ Information is cached locally, where it is used by commands like 'helm search'.
var errNoRepositories = errors.New("no repositories found. You must add one before updating")
type repoUpdateOptions struct {
update func([]*repo.ChartRepository, io.Writer)
update func([]*repo.ChartRepository, io.Writer)
repoFile string
}
func newRepoUpdateCmd(out io.Writer) *cobra.Command {
@ -58,7 +58,7 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command {
}
func (o *repoUpdateOptions) run(out io.Writer) error {
f, err := repo.LoadFile(helmpath.RepositoryFile())
f, err := repo.LoadFile(o.repoFile)
if err != nil {
return err
}
@ -86,7 +86,7 @@ func updateCharts(repos []*repo.ChartRepository, out io.Writer) {
wg.Add(1)
go func(re *repo.ChartRepository) {
defer wg.Done()
if err := re.DownloadIndexFile(); err != nil {
if _, err := re.DownloadIndexFile(); err != nil {
fmt.Fprintf(out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", re.Config.Name, re.Config.URL, err)
} else {
fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", re.Config.Name)

@ -19,12 +19,9 @@ import (
"bytes"
"fmt"
"io"
"os"
"strings"
"testing"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/repo"
@ -32,29 +29,7 @@ import (
)
func TestUpdateCmd(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
repoFile := helmpath.RepositoryFile()
if _, err := os.Stat(repoFile); err != nil {
rf := repo.NewFile()
rf.Add(&repo.Entry{
Name: "charts",
URL: "http://example.com/foo",
})
if err := rf.WriteFile(repoFile, 0644); err != nil {
t.Fatal(err)
}
}
if r, err := repo.LoadFile(repoFile); err == repo.ErrRepoOutOfDate {
if err := r.WriteFile(repoFile, 0644); err != nil {
t.Fatal(err)
}
}
out := bytes.NewBuffer(nil)
var out bytes.Buffer
// Instead of using the HTTP updater, we provide our own for this test.
// The TestUpdateCharts test verifies the HTTP behavior independently.
updater := func(repos []*repo.ChartRepository, out io.Writer) {
@ -63,9 +38,10 @@ func TestUpdateCmd(t *testing.T) {
}
}
o := &repoUpdateOptions{
update: updater,
update: updater,
repoFile: "testdata/repositories.yaml",
}
if err := o.run(out); err != nil {
if err := o.run(&out); err != nil {
t.Fatal(err)
}
@ -76,9 +52,7 @@ func TestUpdateCmd(t *testing.T) {
func TestUpdateCharts(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
defer ensure.HelmHome(t)()
ts, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil {

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

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

@ -19,6 +19,7 @@ package main
import (
"fmt"
"io"
"path/filepath"
"strings"
"github.com/Masterminds/semver"
@ -43,10 +44,12 @@ Repositories are managed with 'helm repo' commands.
const searchMaxScore = 25
type searchRepoOptions struct {
versions bool
regexp bool
version string
maxColWidth uint
versions bool
regexp bool
version string
maxColWidth uint
repoFile string
repoCacheDir string
}
func newSearchRepoCmd(out io.Writer) *cobra.Command {
@ -57,6 +60,8 @@ func newSearchRepoCmd(out io.Writer) *cobra.Command {
Short: "search repositories for a keyword in charts",
Long: searchRepoDesc,
RunE: func(cmd *cobra.Command, args []string) error {
o.repoFile = settings.RepositoryConfig
o.repoCacheDir = settings.RepositoryCache
return o.run(out, args)
},
}
@ -141,15 +146,15 @@ func (o *searchRepoOptions) formatSearchResults(res []*search.Result) string {
func (o *searchRepoOptions) buildIndex(out io.Writer) (*search.Index, error) {
// Load the repositories.yaml
rf, err := repo.LoadFile(helmpath.RepositoryFile())
rf, err := repo.LoadFile(o.repoFile)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "loading repository config")
}
i := search.NewIndex()
for _, re := range rf.Repositories {
n := re.Name
f := helmpath.CacheIndex(n)
f := filepath.Join(o.repoCacheDir, helmpath.CacheIndexFile(n))
ind, err := repo.LoadIndexFile(f)
if err != nil {
// TODO should print to stderr

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

@ -1,4 +1,4 @@
name: env
usage: "env stuff"
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_DIR
echo $HELM_PLUGIN
echo $HELM_PATH_CACHE
echo $HELM_PATH_CONFIG
echo $HELM_PATH_DATA
echo $HELM_PATH_REPOSITORY_FILE
echo $HELM_PATH_REPOSITORY_CACHE
echo $HELM_BIN

@ -28,6 +28,7 @@ import (
"helm.sh/helm/pkg/action"
"helm.sh/helm/pkg/chart/loader"
"helm.sh/helm/pkg/cli/values"
"helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/storage/driver"
)
@ -73,7 +74,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.Version = ">0.0.0-0"
}
vals, err := valueOpts.MergeValues(settings)
vals, err := valueOpts.MergeValues(getter.All(settings))
if err != nil {
return err
}

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

@ -35,12 +35,14 @@ import (
// Resolver resolves dependencies from semantic version ranges to a particular version.
type Resolver struct {
chartpath string
cachepath string
}
// New creates a new resolver for a given chart and a given helm home.
func New(chartpath string) *Resolver {
func New(chartpath, cachepath string) *Resolver {
return &Resolver{
chartpath: chartpath,
cachepath: cachepath,
}
}
@ -69,9 +71,11 @@ func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string
return nil, errors.Wrapf(err, "dependency %q has an invalid version/constraint format", d.Name)
}
repoIndex, err := repo.LoadIndexFile(helmpath.CacheIndex(repoNames[d.Name]))
idx := filepath.Join(r.cachepath, helmpath.CacheIndexFile(repoNames[d.Name]))
repoIndex, err := repo.LoadIndexFile(idx)
if err != nil {
return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
return nil, errors.Wrapf(err, "no cached repo found. (try 'helm repo update') %s", idx)
}
vs, ok := repoIndex.Entries[d.Name]

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

@ -21,55 +21,18 @@ import (
"os"
"testing"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/helmpath/xdg"
)
// HelmHome sets up a Helm Home in a temp dir.
func HelmHome(t *testing.T) {
func HelmHome(t *testing.T) func() {
t.Helper()
cachePath := TempDir(t)
configPath := TempDir(t)
dataPath := TempDir(t)
os.Setenv(xdg.CacheHomeEnvVar, cachePath)
os.Setenv(xdg.ConfigHomeEnvVar, configPath)
os.Setenv(xdg.DataHomeEnvVar, dataPath)
HomeDirs(t)
}
// HomeDirs creates a home directory like ensureHome, but without remote references.
func HomeDirs(t *testing.T) {
t.Helper()
for _, p := range []string{
helmpath.CachePath(),
helmpath.ConfigPath(),
helmpath.DataPath(),
helmpath.RepositoryCache(),
helmpath.Plugins(),
helmpath.PluginCache(),
helmpath.Starters(),
} {
if err := os.MkdirAll(p, 0755); err != nil {
t.Fatal(err)
}
}
}
// CleanHomeDirs removes the directories created by HomeDirs.
func CleanHomeDirs(t *testing.T) {
t.Helper()
for _, p := range []string{
helmpath.CachePath(),
helmpath.ConfigPath(),
helmpath.DataPath(),
helmpath.RepositoryCache(),
helmpath.Plugins(),
helmpath.PluginCache(),
helmpath.Starters(),
} {
if err := os.RemoveAll(p); err != nil {
t.Log(err)
}
base := TempDir(t)
os.Setenv(xdg.CacheHomeEnvVar, base)
os.Setenv(xdg.ConfigHomeEnvVar, base)
os.Setenv(xdg.DataHomeEnvVar, base)
return func() {
os.RemoveAll(base)
}
}

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

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

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

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

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

@ -26,6 +26,8 @@ import (
"os"
"github.com/spf13/pflag"
"helm.sh/helm/pkg/helmpath"
)
// EnvSettings describes all of the environment settings.
@ -38,6 +40,24 @@ type EnvSettings struct {
KubeContext string
// Debug indicates whether or not Helm is running in Debug mode.
Debug bool
// RegistryConfig is the path to the registry config file.
RegistryConfig string
// RepositoryConfig is the path to the repositories file.
RepositoryConfig string
// Repositoryache is the path to the repository cache directory.
RepositoryCache string
// PluginsDirectory is the path to the plugins directory.
PluginsDirectory string
}
func New() *EnvSettings {
return &EnvSettings{
PluginsDirectory: helmpath.DataPath("plugins"),
RegistryConfig: helmpath.ConfigPath("registry.json"),
RepositoryConfig: helmpath.ConfigPath("repositories.yaml"),
RepositoryCache: helmpath.CachePath("repository"),
}
}
// AddFlags binds flags to the given flagset.
@ -46,6 +66,10 @@ func (s *EnvSettings) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.KubeConfig, "kubeconfig", "", "path to the kubeconfig file")
fs.StringVar(&s.KubeContext, "kube-context", "", "name of the kubeconfig context to use")
fs.BoolVar(&s.Debug, "debug", false, "enable verbose output")
fs.StringVar(&s.RegistryConfig, "registry-config", s.RegistryConfig, "path to the registry config file")
fs.StringVar(&s.RepositoryConfig, "repository-config", s.RepositoryConfig, "path to the repositories config file")
fs.StringVar(&s.RepositoryCache, "repository-cache", s.RepositoryCache, "path to the repositories config file")
}
// Init sets values from the environment.
@ -57,8 +81,10 @@ func (s *EnvSettings) Init(fs *pflag.FlagSet) {
// envMap maps flag names to envvars
var envMap = map[string]string{
"debug": "HELM_DEBUG",
"namespace": "HELM_NAMESPACE",
"debug": "HELM_DEBUG",
"namespace": "HELM_NAMESPACE",
"registry-config": "HELM_REGISTRY_CONFIG",
"repository-config": "HELM_REPOSITORY_CONFIG",
}
func setFlagFromEnv(name, envar string, fs *pflag.FlagSet) {

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

@ -67,7 +67,9 @@ type ChartDownloader struct {
// Getter collection for the operation
Getters getter.Providers
// Options provide parameters to be passed along to the Getter being initialized.
Options []getter.Option
Options []getter.Option
RepositoryConfig string
RepositoryCache string
}
// DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file.
@ -87,17 +89,12 @@ func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *proven
return "", nil, err
}
constructor, err := c.Getters.ByScheme(u.Scheme)
g, err := c.Getters.ByScheme(u.Scheme)
if err != nil {
return "", nil, err
}
g, err := constructor(c.Options...)
if err != nil {
return "", nil, err
}
data, err := g.Get(u.String())
data, err := g.Get(u.String(), c.Options...)
if err != nil {
return "", nil, err
}
@ -157,7 +154,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
}
c.Options = append(c.Options, getter.WithURL(ref))
rf, err := repo.LoadFile(helmpath.RepositoryFile())
rf, err := repo.LoadFile(c.RepositoryConfig)
if err != nil {
return u, err
}
@ -218,7 +215,8 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
}
// Next, we need to load the index, and actually look up the chart.
i, err := repo.LoadIndexFile(helmpath.CacheIndex(r.Config.Name))
idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
i, err := repo.LoadIndexFile(idxFile)
if err != nil {
return u, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
}
@ -337,7 +335,8 @@ func (c *ChartDownloader) scanReposForURL(u string, rf *repo.File) (*repo.Entry,
return nil, err
}
i, err := repo.LoadIndexFile(helmpath.CacheIndex(r.Config.Name))
idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
i, err := repo.LoadIndexFile(idxFile)
if err != nil {
return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
}

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

@ -55,7 +55,9 @@ type Manager struct {
// SkipUpdate indicates that the repository should not be updated first.
SkipUpdate bool
// Getter collection for the operation
Getters []getter.Provider
Getters []getter.Provider
RepositoryConfig string
RepositoryCache string
}
// Build rebuilds a local charts directory from a lockfile.
@ -94,11 +96,7 @@ func (m *Manager) Build() error {
}
// Now we need to fetch every package here into charts/
if err := m.downloadAll(lock.Dependencies); err != nil {
return err
}
return nil
return m.downloadAll(lock.Dependencies)
}
// Update updates a local charts directory.
@ -168,7 +166,7 @@ func (m *Manager) loadChartDir() (*chart.Chart, error) {
//
// This returns a lock file, which has all of the dependencies normalized to a specific version.
func (m *Manager) resolve(req []*chart.Dependency, repoNames map[string]string) (*chart.Lock, error) {
res := resolver.New(m.ChartPath)
res := resolver.New(m.ChartPath, m.RepositoryCache)
return res.Resolve(req, repoNames)
}
@ -229,10 +227,12 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
}
dl := ChartDownloader{
Out: m.Out,
Verify: m.Verify,
Keyring: m.Keyring,
Getters: m.Getters,
Out: m.Out,
Verify: m.Verify,
Keyring: m.Keyring,
RepositoryConfig: m.RepositoryConfig,
RepositoryCache: m.RepositoryCache,
Getters: m.Getters,
Options: []getter.Option{
getter.WithBasicAuth(username, password),
},
@ -311,7 +311,7 @@ func (m *Manager) safeDeleteDep(name, dir string) error {
// hasAllRepos ensures that all of the referenced deps are in the local repo cache.
func (m *Manager) hasAllRepos(deps []*chart.Dependency) error {
rf, err := repo.LoadFile(helmpath.RepositoryFile())
rf, err := repo.LoadFile(m.RepositoryConfig)
if err != nil {
return err
}
@ -345,8 +345,11 @@ Loop:
// getRepoNames returns the repo names of the referenced deps which can be used to fetch the cahced index file.
func (m *Manager) getRepoNames(deps []*chart.Dependency) (map[string]string, error) {
rf, err := repo.LoadFile(helmpath.RepositoryFile())
rf, err := repo.LoadFile(m.RepositoryConfig)
if err != nil {
if os.IsNotExist(err) {
return make(map[string]string), nil
}
return nil, err
}
repos := rf.Repositories
@ -412,7 +415,7 @@ repository, use "https://charts.example.com/" or "@example" instead of
// UpdateRepositories updates all of the local repos to the latest.
func (m *Manager) UpdateRepositories() error {
rf, err := repo.LoadFile(helmpath.RepositoryFile())
rf, err := repo.LoadFile(m.RepositoryConfig)
if err != nil {
return err
}
@ -427,8 +430,7 @@ func (m *Manager) UpdateRepositories() error {
}
func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
out := m.Out
fmt.Fprintln(out, "Hang tight while we grab the latest from your chart repositories...")
fmt.Fprintln(m.Out, "Hang tight while we grab the latest from your chart repositories...")
var wg sync.WaitGroup
for _, c := range repos {
r, err := repo.NewChartRepository(c, m.Getters)
@ -437,16 +439,16 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
}
wg.Add(1)
go func(r *repo.ChartRepository) {
if err := r.DownloadIndexFile(); err != nil {
fmt.Fprintf(out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", r.Config.Name, r.Config.URL, err)
if _, err := r.DownloadIndexFile(); err != nil {
fmt.Fprintf(m.Out, "...Unable to get an update from the %q chart repository (%s):\n\t%s\n", r.Config.Name, r.Config.URL, err)
} else {
fmt.Fprintf(out, "...Successfully got an update from the %q chart repository\n", r.Config.Name)
fmt.Fprintf(m.Out, "...Successfully got an update from the %q chart repository\n", r.Config.Name)
}
wg.Done()
}(r)
}
wg.Wait()
fmt.Fprintln(out, "Update Complete. ⎈Happy Helming!⎈")
fmt.Fprintln(m.Out, "Update Complete. ⎈Happy Helming!⎈")
return nil
}
@ -549,17 +551,17 @@ func normalizeURL(baseURL, urlOrPath string) (string, error) {
// The key is the local name (which is only present in the repositories.yaml).
func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, error) {
indices := map[string]*repo.ChartRepository{}
repoyaml := helmpath.RepositoryFile()
// Load repositories.yaml file
rf, err := repo.LoadFile(repoyaml)
rf, err := repo.LoadFile(m.RepositoryConfig)
if err != nil {
return indices, errors.Wrapf(err, "failed to load %s", repoyaml)
return indices, errors.Wrapf(err, "failed to load %s", m.RepositoryConfig)
}
for _, re := range rf.Repositories {
lname := re.Name
index, err := repo.LoadIndexFile(helmpath.CacheIndex(lname))
idxFile := filepath.Join(m.RepositoryCache, helmpath.CacheIndexFile(lname))
index, err := repo.LoadIndexFile(idxFile)
if err != nil {
return indices, err
}

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

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

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

@ -34,7 +34,10 @@ type HTTPGetter struct {
}
//Get performs a Get from repo.Getter and returns the body.
func (g *HTTPGetter) Get(href string) (*bytes.Buffer, error) {
func (g *HTTPGetter) Get(href string, options ...Option) (*bytes.Buffer, error) {
for _, opt := range options {
opt(&g.opts)
}
return g.get(href)
}

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

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

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

@ -13,69 +13,22 @@
package helmpath
import (
"fmt"
"path/filepath"
)
// This helper builds paths to Helm's configuration, cache and data paths.
const lp = lazypath("helm")
// ConfigPath returns the path where Helm stores configuration.
func ConfigPath() string {
return lp.configPath("")
}
func ConfigPath(elem ...string) string { return lp.configPath(elem...) }
// CachePath returns the path where Helm stores cached objects.
func CachePath() string {
return lp.cachePath("")
}
func CachePath(elem ...string) string { return lp.cachePath(elem...) }
// DataPath returns the path where Helm stores data.
func DataPath() string {
return lp.dataPath("")
}
// Registry returns the path to the local registry cache.
func Registry() string {
return lp.cachePath("registry")
}
// RepositoryFile returns the path to the repositories.yaml file.
func RepositoryFile() string {
return lp.configPath("repositories.yaml")
}
// RepositoryCache returns the cache path for repository metadata.
func RepositoryCache() string {
return lp.cachePath("repository")
}
func DataPath(elem ...string) string { return lp.dataPath(elem...) }
// CacheIndex returns the path to an index for the given named repository.
func CacheIndex(name string) string {
target := fmt.Sprintf("%s-index.yaml", name)
if name == "" {
target = "index.yaml"
func CacheIndexFile(name string) string {
if name != "" {
name += "-"
}
return filepath.Join(RepositoryCache(), target)
}
// Starters returns the path to the Helm starter packs.
func Starters() string {
return lp.dataPath("starters")
}
// PluginCache returns the cache path for plugins.
func PluginCache() string {
return lp.cachePath("plugins")
}
// Plugins returns the path to the plugins directory.
func Plugins() string {
return lp.dataPath("plugins")
}
// Archive returns the path to download chart archives.
func Archive() string {
return lp.cachePath("archive")
return name + "index.yaml"
}

@ -38,12 +38,6 @@ func TestHelmHome(t *testing.T) {
isEq(t, CachePath(), "/cache/helm")
isEq(t, ConfigPath(), "/config/helm")
isEq(t, DataPath(), "/data/helm")
isEq(t, RepositoryFile(), "/config/helm/repositories.yaml")
isEq(t, RepositoryCache(), "/cache/helm/repository")
isEq(t, CacheIndex("t"), "/cache/helm/repository/t-index.yaml")
isEq(t, CacheIndex(""), "/cache/helm/repository/index.yaml")
isEq(t, Starters(), "/data/helm/starters")
isEq(t, Archive(), "/cache/helm/archive")
// test to see if lazy-loading environment variables at runtime works
os.Setenv(xdg.CacheHomeEnvVar, "/cache2")

@ -35,12 +35,6 @@ func TestHelmHome(t *testing.T) {
isEq(t, CachePath(), "c:\\helm")
isEq(t, ConfigPath(), "d:\\helm")
isEq(t, DataPath(), "e:\\helm")
isEq(t, RepositoryFile(), "d:\\helm\\repositories.yaml")
isEq(t, RepositoryCache(), "c:\\helm\\repository")
isEq(t, CacheIndex("t"), "c:\\helm\\repository\\t-index.yaml")
isEq(t, CacheIndex(""), "c:\\helm\\repository\\index.yaml")
isEq(t, Starters(), "e:\\helm\\starters")
isEq(t, Archive(), "c:\\helm\\archive")
// test to see if lazy-loading environment variables at runtime works
os.Setenv(xdg.CacheHomeEnvVar, "f:\\")

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

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

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

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

@ -19,12 +19,12 @@ import (
"bytes"
"encoding/base64"
"os"
"path/filepath"
"testing"
"github.com/pkg/errors"
"helm.sh/helm/internal/test/ensure"
"helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/helmpath"
)
@ -36,7 +36,9 @@ type TestHTTPGetter struct {
MockError error
}
func (t *TestHTTPGetter) Get(href string) (*bytes.Buffer, error) { return t.MockResponse, t.MockError }
func (t *TestHTTPGetter) Get(href string, _ ...getter.Option) (*bytes.Buffer, error) {
return t.MockResponse, t.MockError
}
// Fake plugin tarball data
var fakePluginB64 = "H4sIAKRj51kAA+3UX0vCUBgGcC9jn+Iwuk3Peza3GeyiUlJQkcogCOzgli7dJm4TvYk+a5+k479UqquUCJ/fLs549sLO2TnvWnJa9aXnjwujYdYLovxMhsPcfnHOLdNkOXthM/IVQQYjg2yyLLJ4kXGhLp5j0z3P41tZksqxmspL3B/O+j/XtZu1y8rdYzkOZRCxduKPk53ny6Wwz/GfIIf1As8lxzGJSmoHNLJZphKHG4YpTCE0wVk3DULfpSJ3DMMqkj3P5JfMYLdX1Vr9Ie/5E5cstcdC8K04iGLX5HaJuKpWL17F0TCIBi5pf/0pjtLhun5j3f9v6r7wfnI/H0eNp9d1/5P6Gez0vzo7wsoxfrAZbTny/o9k6J8z/VkO/LPlWdC1iVpbEEcq5nmeJ13LEtmbV0k2r2PrOs9PuuNglC5rL1Y5S/syXRQmutaNw1BGnnp8Wq3UG51WvX1da3bKtZtCN/R09DwAAAAAAAAAAAAAAAAAAADAb30AoMczDwAoAAA="
@ -57,12 +59,11 @@ func TestStripName(t *testing.T) {
}
func TestHTTPInstaller(t *testing.T) {
defer ensure.HelmHome(t)()
source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz"
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err)
if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
}
i, err := NewForSource(source, "0.0.1")
@ -90,7 +91,7 @@ func TestHTTPInstaller(t *testing.T) {
if err := Install(i); err != nil {
t.Error(err)
}
if i.Path() != filepath.Join(helmpath.Plugins(), "fake-plugin") {
if i.Path() != helmpath.DataPath("plugins", "fake-plugin") {
t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path())
}
@ -104,12 +105,11 @@ func TestHTTPInstaller(t *testing.T) {
}
func TestHTTPInstallerNonExistentVersion(t *testing.T) {
defer ensure.HelmHome(t)()
source := "https://repo.localdomain/plugins/fake-plugin-0.0.2.tar.gz"
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err)
if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
}
i, err := NewForSource(source, "0.0.2")
@ -137,11 +137,10 @@ func TestHTTPInstallerNonExistentVersion(t *testing.T) {
func TestHTTPInstallerUpdate(t *testing.T) {
source := "https://repo.localdomain/plugins/fake-plugin-0.0.1.tar.gz"
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
defer ensure.HelmHome(t)()
if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err)
if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
}
i, err := NewForSource(source, "0.0.1")
@ -169,7 +168,7 @@ func TestHTTPInstallerUpdate(t *testing.T) {
if err := Install(i); err != nil {
t.Error(err)
}
if i.Path() != filepath.Join(helmpath.Plugins(), "fake-plugin") {
if i.Path() != helmpath.DataPath("plugins", "fake-plugin") {
t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/fake-plugin', got %q", i.Path())
}

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

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

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

@ -49,11 +49,10 @@ func (r *testRepo) UpdateVersion(version string) error {
}
func TestVCSInstaller(t *testing.T) {
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
defer ensure.HelmHome(t)()
if err := os.MkdirAll(helmpath.Plugins(), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.Plugins(), err)
if err := os.MkdirAll(helmpath.DataPath("plugins"), 0755); err != nil {
t.Fatalf("Could not create %s: %s", helmpath.DataPath("plugins"), err)
}
source := "https://github.com/adamreese/helm-env"
@ -83,7 +82,7 @@ func TestVCSInstaller(t *testing.T) {
if repo.current != "0.1.1" {
t.Errorf("expected version '0.1.1', got %q", repo.current)
}
if i.Path() != filepath.Join(helmpath.Plugins(), "helm-env") {
if i.Path() != helmpath.DataPath("plugins", "helm-env") {
t.Errorf("expected path '$XDG_CONFIG_HOME/helm/plugins/helm-env', got %q", i.Path())
}
@ -94,7 +93,7 @@ func TestVCSInstaller(t *testing.T) {
t.Errorf("expected error for plugin exists, got (%v)", err)
}
//Testing FindSource method, expect error because plugin code is not a cloned repository
// Testing FindSource method, expect error because plugin code is not a cloned repository
if _, err := FindSource(i.Path()); err == nil {
t.Error("expected error for inability to find plugin source, got none")
} else if err.Error() != "cannot get information about plugin source" {
@ -103,8 +102,7 @@ func TestVCSInstaller(t *testing.T) {
}
func TestVCSInstallerNonExistentVersion(t *testing.T) {
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
defer ensure.HelmHome(t)()
source := "https://github.com/adamreese/helm-env"
version := "0.2.0"
@ -127,8 +125,7 @@ func TestVCSInstallerNonExistentVersion(t *testing.T) {
}
}
func TestVCSInstallerUpdate(t *testing.T) {
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
defer ensure.HelmHome(t)()
source := "https://github.com/adamreese/helm-env"

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

@ -16,9 +16,13 @@ limitations under the License.
package plugin // import "helm.sh/helm/pkg/plugin"
import (
"os"
"path/filepath"
"reflect"
"runtime"
"testing"
"helm.sh/helm/pkg/cli"
)
func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) {
@ -87,7 +91,6 @@ func TestPlatformPrepareCommand(t *testing.T) {
},
},
}
argv := []string{"--debug", "--foo", "bar"}
var osStrCmp string
os := runtime.GOOS
arch := runtime.GOARCH
@ -101,6 +104,7 @@ func TestPlatformPrepareCommand(t *testing.T) {
osStrCmp = "os-arch"
}
argv := []string{"--debug", "--foo", "bar"}
checkCommand(p, argv, osStrCmp, t)
}
@ -116,7 +120,6 @@ func TestPartialPlatformPrepareCommand(t *testing.T) {
},
},
}
argv := []string{"--debug", "--foo", "bar"}
var osStrCmp string
os := runtime.GOOS
arch := runtime.GOARCH
@ -128,6 +131,7 @@ func TestPartialPlatformPrepareCommand(t *testing.T) {
osStrCmp = "os-arch"
}
argv := []string{"--debug", "--foo", "bar"}
checkCommand(p, argv, osStrCmp, t)
}
@ -158,8 +162,7 @@ func TestNoMatchPrepareCommand(t *testing.T) {
}
argv := []string{"--debug", "--foo", "bar"}
_, _, err := p.PrepareCommand(argv)
if err == nil {
if _, _, err := p.PrepareCommand(argv); err == nil {
t.Errorf("Expected error to be returned")
}
}
@ -251,3 +254,24 @@ func TestLoadAll(t *testing.T) {
t.Errorf("Expected second plugin to be hello, got %q", plugs[1].Metadata.Name)
}
}
func TestSetupEnv(t *testing.T) {
name := "pequod"
base := filepath.Join("testdata/helmhome/helm/plugins", name)
s := &cli.EnvSettings{
PluginsDirectory: "testdata/helmhome/helm/plugins",
}
SetupPluginEnv(s, name, base)
for _, tt := range []struct {
name, expect string
}{
{"HELM_PLUGIN_NAME", name},
{"HELM_PLUGIN_DIR", base},
} {
if got := os.Getenv(tt.name); got != tt.expect {
t.Errorf("Expected $%s=%q, got %q", tt.name, tt.expect, got)
}
}
}

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

@ -52,6 +52,7 @@ type ChartRepository struct {
ChartPaths []string
IndexFile *IndexFile
Client getter.Getter
CachePath string
}
// NewChartRepository constructs ChartRepository
@ -61,23 +62,16 @@ func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository,
return nil, errors.Errorf("invalid chart URL format: %s", cfg.URL)
}
getterConstructor, err := getters.ByScheme(u.Scheme)
client, err := getters.ByScheme(u.Scheme)
if err != nil {
return nil, errors.Errorf("could not find protocol handler for: %s", u.Scheme)
}
client, err := getterConstructor(
getter.WithURL(cfg.URL),
getter.WithTLSClientConfig(cfg.CertFile, cfg.KeyFile, cfg.CAFile),
getter.WithBasicAuth(cfg.Username, cfg.Password),
)
if err != nil {
return nil, errors.Wrapf(err, "could not construct protocol handler for: %s", u.Scheme)
}
return &ChartRepository{
Config: cfg,
IndexFile: NewIndexFile(),
Client: client,
CachePath: helmpath.CachePath("repository"),
}, nil
}
@ -114,31 +108,37 @@ func (r *ChartRepository) Load() error {
}
// DownloadIndexFile fetches the index from a repository.
func (r *ChartRepository) DownloadIndexFile() error {
func (r *ChartRepository) DownloadIndexFile() (string, error) {
var indexURL string
parsedURL, err := url.Parse(r.Config.URL)
if err != nil {
return err
return "", err
}
parsedURL.Path = strings.TrimSuffix(parsedURL.Path, "/") + "/index.yaml"
indexURL = parsedURL.String()
// TODO add user-agent
resp, err := r.Client.Get(indexURL)
resp, err := r.Client.Get(indexURL,
getter.WithURL(r.Config.URL),
getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile),
getter.WithBasicAuth(r.Config.Username, r.Config.Password),
)
if err != nil {
return err
return "", err
}
index, err := ioutil.ReadAll(resp)
if err != nil {
return err
return "", err
}
if _, err := loadIndex(index); err != nil {
return err
return "", err
}
return ioutil.WriteFile(helmpath.CacheIndex(r.Config.Name), index, 0644)
fname := filepath.Join(r.CachePath, helmpath.CacheIndexFile(r.Config.Name))
os.MkdirAll(filepath.Dir(fname), 0755)
return fname, ioutil.WriteFile(fname, index, 0644)
}
// Index generates an index for the chart repository and writes an index.yaml file.
@ -208,12 +208,13 @@ func FindChartInAuthRepoURL(repoURL, username, password, chartName, chartVersion
if err != nil {
return "", err
}
if err := r.DownloadIndexFile(); err != nil {
idx, err := r.DownloadIndexFile()
if err != nil {
return "", errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", repoURL)
}
// Read the index file for the repository to get chart information and return chart URL
repoIndex, err := LoadIndexFile(helmpath.CacheIndex(name))
repoIndex, err := LoadIndexFile(idx)
if err != nil {
return "", err
}

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

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

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

@ -19,16 +19,13 @@ package repo // import "helm.sh/helm/pkg/repo"
import (
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/pkg/errors"
"sigs.k8s.io/yaml"
)
// ErrRepoOutOfDate indicates that the repository file is out of date, but
// is fixable.
var ErrRepoOutOfDate = errors.New("repository file is out of date")
// File represents the repositories.yaml file
type File struct {
APIVersion string `json:"apiVersion"`
@ -48,41 +45,18 @@ func NewFile() *File {
}
// LoadFile takes a file at the given path and returns a File object
//
// If this returns ErrRepoOutOfDate, it also returns a recovered File that
// can be saved as a replacement to the out of date file.
func LoadFile(path string) (*File, error) {
b, err := ioutil.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil, errors.Errorf("couldn't load repositories file (%s).\nYou might need to run `helm init`", path)
return nil, errors.Wrapf(err, "couldn't load repositories file (%s)", path)
}
return nil, err
}
r := &File{}
err = yaml.Unmarshal(b, r)
if err != nil {
return nil, err
}
// File is either corrupt, or is from before v2.0.0-Alpha.5
if r.APIVersion == "" {
m := map[string]string{}
if err = yaml.Unmarshal(b, &m); err != nil {
return nil, err
}
r := NewFile()
for k, v := range m {
r.Add(&Entry{
Name: k,
URL: v,
})
}
return r, ErrRepoOutOfDate
}
return r, nil
return r, err
}
// Add adds one or more repo entries to a repo file.
@ -94,18 +68,18 @@ func (r *File) Add(re ...*Entry) {
// entry with the same name doesn't exist in the repo file it will add it.
func (r *File) Update(re ...*Entry) {
for _, target := range re {
found := false
for j, repo := range r.Repositories {
if repo.Name == target.Name {
r.Repositories[j] = target
found = true
break
}
}
if !found {
r.Add(target)
r.update(target)
}
}
func (r *File) update(e *Entry) {
for j, repo := range r.Repositories {
if repo.Name == e.Name {
r.Repositories[j] = e
return
}
}
r.Add(e)
}
// Has returns true if the given name is already a repository name.
@ -139,5 +113,8 @@ func (r *File) WriteFile(path string, perm os.FileMode) error {
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
return ioutil.WriteFile(path, data, perm)
}

@ -16,10 +16,12 @@ limitations under the License.
package repo
import "testing"
import "io/ioutil"
import "os"
import "strings"
import (
"io/ioutil"
"os"
"strings"
"testing"
)
const testRepositoriesFile = "testdata/repositories.yaml"
@ -89,27 +91,6 @@ func TestNewFile(t *testing.T) {
}
}
func TestNewPreV1File(t *testing.T) {
r, err := LoadFile("testdata/old-repositories.yaml")
if err != nil && err != ErrRepoOutOfDate {
t.Fatal(err)
}
if len(r.Repositories) != 3 {
t.Fatalf("Expected 3 repos: %#v", r)
}
// Because they are parsed as a map, we lose ordering.
found := false
for _, rr := range r.Repositories {
if rr.Name == "best-charts-ever" {
found = true
}
}
if !found {
t.Errorf("expected the best charts ever. Got %#v", r.Repositories)
}
}
func TestRemoveRepository(t *testing.T) {
sampleRepository := NewFile()
sampleRepository.Add(
@ -184,7 +165,7 @@ func TestWriteFile(t *testing.T) {
t.Errorf("failed to create test-file (%v)", err)
}
defer os.Remove(file.Name())
if err := sampleRepository.WriteFile(file.Name(), 0744); err != nil {
if err := sampleRepository.WriteFile(file.Name(), 0644); err != nil {
t.Errorf("failed to write file (%v)", err)
}
@ -200,10 +181,9 @@ func TestWriteFile(t *testing.T) {
}
func TestRepoNotExists(t *testing.T) {
_, err := LoadFile("/this/path/does/not/exist.yaml")
if err == nil {
if _, err := LoadFile("/this/path/does/not/exist.yaml"); err == nil {
t.Errorf("expected err to be non-nil when path does not exist")
} else if !strings.Contains(err.Error(), "You might need to run `helm init`") {
t.Errorf("expected prompt to run `helm init` when repositories file does not exist")
} else if !strings.Contains(err.Error(), "couldn't load repositories file") {
t.Errorf("expected prompt `couldn't load repositories file`")
}
}

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

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

Loading…
Cancel
Save