feat(cmd/helm): remove need for helm init command

* allow repository config via cli
* make `helm repo add` create repo config file if it does not exist
* squash a ton of bugs

Signed-off-by: Adam Reese <adam@reese.io>
pull/6272/head
Adam Reese 6 years ago
parent fba311ba23
commit b6fdd8783b
No known key found for this signature in database
GPG Key ID: 06F35E60A7A18DD6

@ -52,6 +52,7 @@ will be overwritten, but other files will be left alone.
type createOptions struct {
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)
os.MkdirAll(helmpath.CachePath(), 0755)
defer testChdir(t, helmpath.CachePath())()
// 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")

@ -58,13 +58,13 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
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,37 +22,31 @@ 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()
chartname := "depbuild"
if err := createTestingChart(helmpath.DataPath(), chartname, srv.URL()); err != nil {
t.Fatal(err)
}
createTestingChart(t, rootDir, chartname, srv.URL())
repoFile := filepath.Join(srv.Root(), "repositories.yaml")
cmd := fmt.Sprintf("dependency build '%s'", filepath.Join(helmpath.DataPath(), chartname))
cmd := fmt.Sprintf("dependency build '%s' --repository-config %s", filepath.Join(rootDir, chartname), repoFile)
_, out, err := executeActionCommand(cmd)
// In the first pass, we basically want the same results as an update.
if err != nil {
t.Logf("Output: %s", out)
t.Fatal(err)
t.Fatalf("%+v", err)
}
if !strings.Contains(out, `update from the "test" chart repository`) {
@ -60,14 +54,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(srv.Root(), 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(srv.Root(), chartname, "Chart.lock")
if _, err := os.Stat(lockfile); err != nil {
t.Fatal(err)
}
@ -82,7 +76,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 +86,7 @@ func TestDependencyBuildCmd(t *testing.T) {
t.Fatal(err)
}
i, err := repo.LoadIndexFile(helmpath.CacheIndex("test"))
i, err := repo.LoadIndexFile(filepath.Join(srv.Root(), "index.yaml"))
if err != nil {
t.Fatal(err)
}

@ -63,13 +63,13 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
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,29 @@ 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())
dir := srv.Root()
if err := srv.LinkIndices(); err != nil {
t.Fatal(err)
}
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", filepath.Join(dir, chartname), filepath.Join(dir, "repositories.yaml")),
)
if err != nil {
t.Logf("Output: %s", out)
t.Fatal(err)
@ -66,7 +67,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 := filepath.Join(dir, chartname, "charts/reqtest-0.1.0.tgz")
if _, err := os.Stat(expect); err != nil {
t.Fatal(err)
}
@ -76,7 +77,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
t.Fatal(err)
}
i, err := repo.LoadIndexFile(helmpath.CacheIndex("test"))
i, err := repo.LoadIndexFile(filepath.Join(srv.Root(), helmpath.CacheIndexFile("test")))
if err != nil {
t.Fatal(err)
}
@ -92,12 +93,12 @@ 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 {
fname := filepath.Join(dir, chartname, "Chart.yaml")
if err := chartutil.SaveChartfile(fname, 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", filepath.Join(dir, chartname), filepath.Join(dir, "repositories.yaml")))
if err != nil {
t.Logf("Output: %s", out)
t.Fatal(err)
@ -105,11 +106,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 = filepath.Join(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 := filepath.Join(dir, chartname, "charts/compressedchart-0.1.0.tgz")
if _, err := os.Stat(dontExpect); err == nil {
t.Fatalf("Unexpected %q", dontExpect)
}
@ -117,9 +118,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 +130,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,9 +145,7 @@ func TestDependencyUpdateCmd_SkipRefresh(t *testing.T) {
func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
defer resetEnv()()
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
defer ensure.HelmHome(t)()
srv := repotest.NewServer(helmpath.ConfigPath())
defer srv.Stop()
@ -162,11 +157,9 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
t.Logf("Listening on directory %s", srv.Root())
chartname := "depupdelete"
if err := createTestingChart(helmpath.DataPath(), chartname, srv.URL()); err != nil {
t.Fatal(err)
}
createTestingChart(t, helmpath.DataPath(), chartname, srv.URL())
_, output, err := executeActionCommand(fmt.Sprintf("dependency update %s", filepath.Join(helmpath.DataPath(), chartname)))
_, output, err := executeActionCommand(fmt.Sprintf("dependency update %s", helmpath.DataPath(chartname)))
if err != nil {
t.Logf("Output: %s", output)
t.Fatal(err)
@ -175,14 +168,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", helmpath.DataPath(chartname)))
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(helmpath.DataPath(chartname, "charts"))
if err != nil {
t.Fatal(err)
}
@ -198,7 +191,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(helmpath.DataPath(chartname, "tmpcharts")); !os.IsNotExist(err) {
t.Fatalf("tmpcharts dir still exists")
}
}
@ -223,7 +216,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
)

@ -59,9 +59,10 @@ 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)
t.Errorf("expected error, got '%+v'", err)
}
if tt.golden != "" {
test.AssertGoldenString(t, out, tt.golden)

@ -31,7 +31,6 @@ import (
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/plugin"
"helm.sh/helm/pkg/plugin/installer"
"helm.sh/helm/pkg/repo"
)
const initDesc = `
@ -93,12 +92,6 @@ 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
@ -120,11 +113,9 @@ func ensureDirectories(out io.Writer) error {
helmpath.CachePath(),
helmpath.ConfigPath(),
helmpath.DataPath(),
helmpath.RepositoryCache(),
helmpath.Plugins(),
helmpath.PluginCache(),
helmpath.Starters(),
helmpath.Archive(),
helmpath.CachePath("repository"),
helmpath.DataPath("plugins"),
helmpath.CachePath("plugins"),
}
for _, p := range directories {
if fi, err := os.Stat(p); err != nil {
@ -140,31 +131,6 @@ func ensureDirectories(out io.Writer) error {
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 {

@ -28,22 +28,12 @@ import (
const testPluginsFile = "testdata/plugins.yaml"
func TestEnsureHome(t *testing.T) {
ensure.HelmHome(t)
defer ensure.CleanHomeDirs(t)
defer ensure.HelmHome(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)
}
@ -57,13 +47,7 @@ func TestEnsureHome(t *testing.T) {
}
}
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 {
if plugins, err := findPlugins(helmpath.DataPath("plugins")); err != nil {
t.Error(err)
} else if len(plugins) != 1 {
t.Errorf("Expected 1 plugin, got %d", len(plugins))

@ -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
}
@ -201,7 +202,9 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
ChartPath: cp,
Keyring: client.ChartPathOptions.Keyring,
SkipUpdate: false,
Getters: getter.All(settings),
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
}

@ -42,7 +42,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
@ -77,7 +77,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
// Call setupEnv before PrepareCommand because
// PrepareCommand uses os.ExpandEnv and expects the
// setupEnv vars.
plugin.SetupPluginEnv(settings, md.Name, plug.Dir)
SetupPluginEnv(md.Name, plug.Dir)
main, argv, prepCmdErr := plug.PrepareCommand(u)
if prepCmdErr != nil {
os.Stderr.WriteString(prepCmdErr.Error())
@ -153,3 +153,27 @@ func findPlugins(plugdirs string) ([]*plugin.Plugin, error) {
}
return found, nil
}
// 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(shortName, base string) {
for key, val := range map[string]string{
"HELM_PLUGIN_NAME": shortName,
"HELM_PLUGIN_DIR": base,
"HELM_BIN": os.Args[0],
"HELM_PLUGIN": settings.PluginsDirectory,
// Set vars that convey common information.
"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
} {
os.Setenv(key, val)
}
if settings.Debug {
os.Setenv("HELM_DEBUG", "1")
}
}

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

@ -32,10 +32,10 @@ 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) {
t.Skip("TODO")
statExe := "stat"
statFileMsg := "no such file or directory"
if runtime.GOOS == "windows" {
@ -43,8 +43,6 @@ func TestPackage(t *testing.T) {
statFileMsg = "The system cannot find the file specified."
}
defer resetEnv()()
tests := []struct {
name string
flags map[string]string
@ -139,18 +137,18 @@ 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())()
cachePath := ensure.TempDir(t)
t.Logf("Running tests in %s", cachePath)
if err := os.Mkdir("toot", 0777); err != nil {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer testChdir(t, cachePath)()
if err := os.MkdirAll("toot", 0777); err != nil {
t.Fatal(err)
}
for _, tt := range tests {
buf := bytes.NewBuffer(nil)
c := newPackageCmd(buf)
var buf bytes.Buffer
c := newPackageCmd(&buf)
// This is an unfortunate byproduct of the tmpdir
if v, ok := tt.flags["keyring"]; ok && len(v) > 0 {
@ -168,10 +166,9 @@ func TestPackage(t *testing.T) {
err := c.RunE(c, adjustedArgs)
if err != nil {
if tt.err && re.MatchString(err.Error()) {
continue
return
}
t.Errorf("%q: expected error %q, got %q", tt.name, tt.expect, err)
continue
t.Fatalf("%q: expected error %q, got %q", tt.name, tt.expect, err)
}
if !re.Match(buf.Bytes()) {
@ -193,21 +190,19 @@ func TestPackage(t *testing.T) {
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 +210,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 +218,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 +228,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 +240,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,27 +279,21 @@ func TestPackageValues(t *testing.T) {
t.Errorf("unexpected error parsing values: %q", err)
}
runAndVerifyPackageCommandValues(t, tc.args, tc.flags, valueFiles, expected)
}
}
func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[string]string, valueFiles string, expected chartutil.Values) {
t.Helper()
outputDir := ensure.TempDir(t)
if len(flags) == 0 {
flags = make(map[string]string)
if len(tc.flags) == 0 {
tc.flags = make(map[string]string)
}
flags["destination"] = outputDir
tc.flags["destination"] = outputDir
if len(valueFiles) > 0 {
flags["values"] = valueFiles
tc.flags["values"] = valueFiles
}
cmd := newPackageCmd(&bytes.Buffer{})
setFlags(cmd, flags)
if err := cmd.RunE(cmd, args); err != nil {
t.Errorf("unexpected error: %q", err)
setFlags(cmd, tc.flags)
if err := cmd.RunE(cmd, tc.args); err != nil {
t.Fatalf("unexpected error: %q", err)
}
outputFile := filepath.Join(outputDir, "alpine-0.1.0.tgz")
@ -311,30 +301,28 @@ func runAndVerifyPackageCommandValues(t *testing.T, args []string, flags map[str
actual, err := getChartValues(outputFile)
if err != nil {
t.Errorf("unexpected error extracting chart values: %q", err)
t.Fatalf("unexpected error extracting chart values: %q", err)
}
verifyValues(t, actual, expected)
}
}
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
}

@ -59,7 +59,7 @@ func runHook(p *plugin.Plugin, event string) error {
debug("running %s hook: %s", event, prog)
plugin.SetupPluginEnv(settings, p.Metadata.Name, p.Dir)
SetupPluginEnv(p.Metadata.Name, p.Dir)
prog.Stdout, prog.Stderr = os.Stdout, os.Stderr
if err := prog.Run(); err != nil {
if eerr, ok := err.(*exec.ExitError); ok {

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

@ -26,8 +26,6 @@ import (
"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 +61,21 @@ func TestManuallyProcessArgs(t *testing.T) {
}
func TestLoadPlugins(t *testing.T) {
defer resetEnv()()
settings.PluginsDirectory = "testdata/helmhome/helm/plugins"
settings.RepositoryConfig = "testdata/helmhome/helm/repository"
os.Setenv(xdg.CacheHomeEnvVar, "testdata/helmhome")
os.Setenv(xdg.ConfigHomeEnvVar, "testdata/helmhome")
os.Setenv(xdg.DataHomeEnvVar, "testdata/helmhome")
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/repository",
helmpath.CachePath("repository"),
os.Args[0],
}, "\n")
@ -95,7 +89,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{}},
}
@ -133,9 +127,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")
@ -150,29 +143,16 @@ func TestLoadPlugins_HelmNoPlugins(t *testing.T) {
}
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)
base := filepath.Join("testdata/helmhome/helm/plugins", name)
SetupPluginEnv(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
}

@ -25,15 +25,11 @@ import (
"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)
t.Skip("TODO")
srv, err := repotest.NewTempServer("testdata/testcharts/*.tgz*")
if err != nil {
t.Fatal(err)
@ -125,19 +121,22 @@ func TestPullCmd(t *testing.T) {
},
}
for _, tt := range tests {
outdir := filepath.Join(helmpath.DataPath(), "testout")
os.RemoveAll(outdir)
os.Mkdir(outdir, 0755)
settings.RepositoryConfig = filepath.Join(srv.Root(), "repositories.yaml")
settings.RepositoryCache = srv.Root()
cmd := strings.Join(append(tt.args, "-d", "'"+outdir+"'"), " ")
_, out, err := executeActionCommand("fetch " + cmd)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
outdir := ensure.TempDir(t)
outdir = srv.Root()
cmd := "fetch " + strings.Join(tt.args, " ")
cmd += fmt.Sprintf(" -d '%s' --repository-config %s --repository-cache %s ",
outdir, filepath.Join(srv.Root(), "repositories.yaml"), outdir)
_, out, err := executeActionCommand(cmd)
if err != nil {
if tt.wantError {
continue
return
}
t.Errorf("%q reported error: %s", tt.name, err)
continue
t.Fatalf("%q reported error: %s", tt.name, err)
}
if tt.expectVerify {
@ -158,5 +157,6 @@ func TestPullCmd(t *testing.T) {
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,55 @@ 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); err != nil {
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"
)
@ -39,6 +38,7 @@ var errNoRepositories = errors.New("no repositories found. You must add one befo
type repoUpdateOptions struct {
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) {
@ -64,8 +39,9 @@ func TestUpdateCmd(t *testing.T) {
}
o := &repoUpdateOptions{
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 {

@ -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"
@ -47,6 +48,8 @@ type searchRepoOptions struct {
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]

@ -91,14 +91,14 @@ func TestResolve(t *testing.T) {
}
repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"}
r := New("testdata/chartpath")
r := New("testdata/chartpath", "testdata/helm/repository")
for _, tt := range tests {
l, err := r.Resolve(tt.req, repoNames)
if err != nil {
if tt.err {
continue
}
t.Fatal(err)
t.Fatalf("%+v", err)
}
if tt.err {

@ -26,7 +26,7 @@ import (
)
// 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)
@ -34,49 +34,39 @@ func HelmHome(t *testing.T) {
os.Setenv(xdg.CacheHomeEnvVar, cachePath)
os.Setenv(xdg.ConfigHomeEnvVar, configPath)
os.Setenv(xdg.DataHomeEnvVar, dataPath)
HomeDirs(t)
return HomeDirs(t)
}
// HomeDirs creates a home directory like ensureHome, but without remote references.
func HomeDirs(t *testing.T) {
t.Helper()
for _, p := range []string{
var dirs = [...]string{
helmpath.CachePath(),
helmpath.ConfigPath(),
helmpath.DataPath(),
helmpath.RepositoryCache(),
helmpath.Plugins(),
helmpath.PluginCache(),
helmpath.Starters(),
} {
helmpath.CachePath("repository"),
}
// HomeDirs creates a home directory like ensureHome, but without remote references.
func HomeDirs(t *testing.T) func() {
return func() {}
t.Helper()
for _, p := range dirs {
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(),
} {
cleanup := func() {
for _, p := range dirs {
if err := os.RemoveAll(p); err != nil {
t.Log(err)
}
}
}
return cleanup
}
// TempDir ensures a scratch test directory for unit testing purposes.
func TempDir(t *testing.T) string {
t.Helper()
d, err := ioutil.TempDir("", "helm")
d, err := ioutil.TempDir("helm", "")
if err != nil {
t.Fatal(err)
}

@ -564,7 +564,7 @@ OUTER:
// - URL
//
// If 'verify' is true, this will attempt to also verify the chart.
func (c *ChartPathOptions) LocateChart(name string, settings cli.EnvSettings) (string, error) {
func (c *ChartPathOptions) LocateChart(name string, settings *cli.EnvSettings) (string, error) {
name = strings.TrimSpace(name)
version := strings.TrimSpace(c.Version)
@ -604,11 +604,12 @@ 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)
archivePath := helmpath.CachePath("archive")
if err := os.MkdirAll(archivePath, 0744); err != nil {
return "", err
}
filename, _, err := dl.DownloadTo(name, version, helmpath.Archive())
filename, _, err := dl.DownloadTo(name, version, archivePath)
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

@ -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 {
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)
}
}
for _, f := range []string{NotesName, DeploymentName, ServiceName, HelpersName} {
if fi, err := os.Stat(filepath.Join(dir, TemplatesDir, f)); err != nil {
t.Errorf("Expected %s file: %s", f, err)
} else if fi.IsDir() {
t.Errorf("Expected %s to be a file.", f)
}
}
}
func TestCreateFrom(t *testing.T) {
@ -90,11 +80,10 @@ func TestCreateFrom(t *testing.T) {
srcdir := "./testdata/mariner"
if err := CreateFrom(cf, tdir, srcdir); err != nil {
t.Fatal(err)
t.Fatalf("%+v", err)
}
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,21 @@ 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 repositories cache directory.
RepositoryCache string
// PluginsDirectory is the path to the plugins directory.
PluginsDirectory string
}
func New() *EnvSettings {
return &EnvSettings{
PluginsDirectory: helmpath.DataPath("plugins"),
}
}
// AddFlags binds flags to the given flagset.
@ -46,6 +63,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", helmpath.ConfigPath("registry.json"), "path to the registry config file")
fs.StringVar(&s.RepositoryConfig, "repository-config", helmpath.ConfigPath("repositories.yaml"), "path to the repositories config file")
fs.StringVar(&s.RepositoryCache, "repository-cache", helmpath.CachePath("repository"), "path to the repositories config file")
}
// Init sets values from the environment.
@ -59,6 +80,8 @@ func (s *EnvSettings) Init(fs *pflag.FlagSet) {
var envMap = map[string]string{
"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
}

@ -68,6 +68,8 @@ type ChartDownloader struct {
Getters getter.Providers
// Options provide parameters to be passed along to the Getter being initialized.
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')")
}

@ -30,10 +30,12 @@ import (
"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/helmhome/helm/repositories.yaml"
repoCache = "testdata/helmhome/helm/repository"
)
func TestResolveChartRef(t *testing.T) {
tests := []struct {
name, ref, expect, version string
fail bool
@ -57,7 +59,12 @@ func TestResolveChartRef(t *testing.T) {
c := ChartDownloader{
Out: os.Stderr,
Getters: getter.All(cli.EnvSettings{}),
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
}
for _, tt := range tests {
@ -105,16 +112,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()
@ -136,12 +138,18 @@ func TestDownloadTo(t *testing.T) {
Out: os.Stderr,
Verify: VerifyAlways,
Keyring: "testdata/helm-test-key.pub",
Getters: getter.All(cli.EnvSettings{}),
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
Options: []getter.Option{
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 +169,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*")
@ -179,12 +186,17 @@ func TestDownloadTo_VerifyLater(t *testing.T) {
c := ChartDownloader{
Out: os.Stderr,
Verify: VerifyLater,
Getters: getter.All(cli.EnvSettings{}),
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
}
cname := "/signtest-0.1.0.tgz"
where, _, err := c.DownloadTo(srv.URL()+cname, "", dest)
if err != nil {
t.Fatal(err)
t.Fatalf("%+v", err)
}
if expect := filepath.Join(dest, cname); where != expect {
@ -206,11 +218,13 @@ func TestScanReposForURL(t *testing.T) {
c := ChartDownloader{
Out: os.Stderr,
Verify: VerifyLater,
Getters: getter.All(cli.EnvSettings{}),
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{}),
}
u := "http://example.com/alpine-0.2.0.tgz"
rf, err := repo.LoadFile(helmpath.RepositoryFile())
rf, err := repo.LoadFile(helmpath.ConfigPath("repositories.yaml"))
if err != nil {
t.Fatal(err)
}

@ -56,6 +56,8 @@ type Manager struct {
SkipUpdate bool
// Getter collection for the operation
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)
}
@ -232,6 +230,8 @@ func (m *Manager) downloadAll(deps []*chart.Dependency) error {
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,
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}
tests := []struct {
name string

@ -76,7 +76,7 @@ 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(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, options ...Option) (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)
}
// 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{
{
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{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,54 @@
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("")
func DataPath(elem ...string) string {
return lp.dataPath(elem...)
}
// Registry returns the path to the local registry cache.
func Registry() string {
return lp.cachePath("registry")
}
// func Registry() string { return CachePath("registry") }
// RepositoryFile returns the path to the repositories.yaml file.
func RepositoryFile() string {
return lp.configPath("repositories.yaml")
}
// func RepositoryFile() string { return ConfigPath("repositories.yaml") }
// RepositoryCache returns the cache path for repository metadata.
func RepositoryCache() string {
return lp.cachePath("repository")
}
// func RepositoryCache() string { return CachePath("repository") }
// CacheIndex returns the path to an index for the given named repository.
func CacheIndexFile(name string) string {
if name != "" {
name += "-"
}
return name + "index.yaml"
}
func CacheIndex(name string) string {
target := fmt.Sprintf("%s-index.yaml", name)
if name == "" {
target = "index.yaml"
if name != "" {
name += "-"
}
return filepath.Join(RepositoryCache(), target)
name += "index.yaml"
return CachePath("repository", name)
}
// Starters returns the path to the Helm starter packs.
func Starters() string {
return lp.dataPath("starters")
}
// func Starters() string { return DataPath("starters") }
// PluginCache returns the cache path for plugins.
func PluginCache() string {
return lp.cachePath("plugins")
}
// func PluginCache() string { return 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")
}
// func Plugins() string { return DataPath("plugins") }

@ -38,12 +38,8 @@ 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,8 @@ 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],
}
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.Fatalf("%+v", 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())
}
@ -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"
)
@ -215,21 +215,18 @@ 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,
func SetupPluginEnv(settings *cli.EnvSettings,
shortName, base string) {
for key, val := range map[string]string{
"HELM_PLUGIN_NAME": shortName,
"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": helmpath.CachePath("repository"),
"HELM_PATH_STARTER": helmpath.DataPath("starters"),
"HELM_HOME": helmpath.DataPath(), // for backwards compatibility with Helm 2 plugins
} {
os.Setenv(key, val)

@ -87,7 +87,6 @@ func TestPlatformPrepareCommand(t *testing.T) {
},
},
}
argv := []string{"--debug", "--foo", "bar"}
var osStrCmp string
os := runtime.GOOS
arch := runtime.GOARCH
@ -101,6 +100,7 @@ func TestPlatformPrepareCommand(t *testing.T) {
osStrCmp = "os-arch"
}
argv := []string{"--debug", "--foo", "bar"}
checkCommand(p, argv, osStrCmp, t)
}
@ -116,7 +116,6 @@ func TestPartialPlatformPrepareCommand(t *testing.T) {
},
},
}
argv := []string{"--debug", "--foo", "bar"}
var osStrCmp string
os := runtime.GOOS
arch := runtime.GOARCH
@ -128,6 +127,7 @@ func TestPartialPlatformPrepareCommand(t *testing.T) {
osStrCmp = "os-arch"
}
argv := []string{"--debug", "--foo", "bar"}
checkCommand(p, argv, osStrCmp, t)
}
@ -158,8 +158,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")
}
}

@ -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, r.Config.Name+"-index.yaml")
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
}

@ -47,7 +47,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 +80,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 +118,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 +132,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{
{
providers := getter.Providers{{
Schemes: []string{"gs"},
New: customGetterConstructor,
},
}
}}
repo, err := NewChartRepository(&Entry{
Name: repoName,
URL: repoURL,
@ -154,6 +149,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 +157,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 +279,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)
}
@ -310,7 +305,7 @@ func TestFindChartInRepoURL(t *testing.T) {
func TestErrorFindChartInRepoURL(t *testing.T) {
setupCacheHome(t)
_, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", getter.All(cli.EnvSettings{}))
_, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", getter.All(&cli.EnvSettings{}))
if err == nil {
t.Errorf("Expected error for bad chart URL, but did not get any errors")
}
@ -324,7 +319,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) {
}
defer srv.Close()
_, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", getter.All(cli.EnvSettings{}))
_, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", getter.All(&cli.EnvSettings{}))
if err == nil {
t.Errorf("Expected error for chart not found, but did not get any errors")
}
@ -332,7 +327,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) {
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{}))
_, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", getter.All(&cli.EnvSettings{}))
if err == nil {
t.Errorf("Expected error for chart not found, but did not get any errors")
}
@ -340,7 +335,7 @@ func TestErrorFindChartInRepoURL(t *testing.T) {
t.Errorf("Expected error for chart not found, but got a different error (%v)", err)
}
_, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", getter.All(cli.EnvSettings{}))
_, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", getter.All(&cli.EnvSettings{}))
if err == nil {
t.Errorf("Expected error for no chart URLs available, but did not get any errors")
}
@ -383,7 +378,7 @@ func setupCacheHome(t *testing.T) {
}
os.Setenv(xdg.CacheHomeEnvVar, d)
if err := os.MkdirAll(helmpath.RepositoryCache(), 0755); err != nil {
if err := os.MkdirAll(helmpath.CachePath("repository"), 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).\nYou might need to run `helm init`", 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
r.update(target)
}
}
if !found {
r.Add(target)
func (r *File) update(e *Entry) {
for j, repo := range r.Repositories {
if repo.Name == e.Name {
r.Repositories[j] = e
return
}
}
r.Add(e)
}
// Has returns true if the given name is already a repository name.
@ -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,8 +181,7 @@ 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")

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