Skip to content

Commit

Permalink
Merge remote-tracking branch 'helm/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
liuming-dev committed Apr 16, 2020
2 parents bd08203 + fa5eb64 commit 376a5ca
Show file tree
Hide file tree
Showing 47 changed files with 1,025 additions and 210 deletions.
1 change: 1 addition & 0 deletions ADOPTERS.md
Expand Up @@ -10,6 +10,7 @@
- [Microsoft](https://microsoft.com)
- [Qovery](https://www.qovery.com/)
- [Samsung SDS](https://www.samsungsds.com/)
- [Softonic](https://hello.softonic.com/)
- [Ville de Montreal](https://montreal.ca)

_This file is part of the CNCF official documentation for projects._
2 changes: 1 addition & 1 deletion cmd/helm/completion.go
Expand Up @@ -52,7 +52,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command {

cmd := &cobra.Command{
Use: "completion SHELL",
Short: "Generate autocompletions script for the specified shell (bash or zsh)",
Short: "generate autocompletions script for the specified shell (bash or zsh)",
Long: completionDesc,
RunE: func(cmd *cobra.Command, args []string) error {
return runCompletion(out, cmd, args)
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/create.go
Expand Up @@ -71,7 +71,7 @@ func newCreateCmd(out io.Writer) *cobra.Command {
},
}

cmd.Flags().StringVarP(&o.starter, "starter", "p", "", "The name or absolute path to Helm starter scaffold")
cmd.Flags().StringVarP(&o.starter, "starter", "p", "", "the name or absolute path to Helm starter scaffold")
return cmd
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/env.go
Expand Up @@ -40,7 +40,7 @@ func newEnvCmd(out io.Writer) *cobra.Command {

cmd := &cobra.Command{
Use: "env",
Short: "Helm client environment information",
Short: "helm client environment information",
Long: envHelp,
Args: require.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
Expand Down
11 changes: 0 additions & 11 deletions cmd/helm/helm.go
Expand Up @@ -32,7 +32,6 @@ import (
// Import to initialize client auth plugins.
_ "k8s.io/client-go/plugin/pkg/client/auth"

"helm.sh/helm/v3/internal/completion"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/gates"
Expand Down Expand Up @@ -73,16 +72,6 @@ func main() {
actionConfig := new(action.Configuration)
cmd := newRootCmd(actionConfig, os.Stdout, os.Args[1:])

if calledCmd, _, err := cmd.Find(os.Args[1:]); err == nil && calledCmd.Name() == completion.CompRequestCmd {
// If completion is being called, we have to check if the completion is for the "--kube-context"
// value; if it is, we cannot call the action.Init() method with an incomplete kube-context value
// or else it will fail immediately. So, we simply unset the invalid kube-context value.
if args := os.Args[1:]; len(args) > 2 && args[len(args)-2] == "--kube-context" {
// We are completing the kube-context value! Reset it as the current value is not valid.
settings.KubeContext = ""
}
}

helmDriver := os.Getenv("HELM_DRIVER")
if err := actionConfig.Init(settings.RESTClientGetter(), settings.Namespace(), helmDriver, debug); err != nil {
log.Fatal(err)
Expand Down
20 changes: 18 additions & 2 deletions cmd/helm/helm_test.go
Expand Up @@ -97,10 +97,15 @@ func storageFixture() *storage.Storage {
}

func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command, string, error) {
return executeActionCommandStdinC(store, nil, cmd)
}

func executeActionCommandStdinC(store *storage.Storage, in *os.File, cmd string) (*cobra.Command, string, error) {
args, err := shellwords.Parse(cmd)
if err != nil {
return nil, "", err
}

buf := new(bytes.Buffer)

actionConfig := &action.Configuration{
Expand All @@ -111,15 +116,26 @@ func executeActionCommandC(store *storage.Storage, cmd string) (*cobra.Command,
}

root := newRootCmd(actionConfig, buf, args)
root.SetOutput(buf)
root.SetOut(buf)
root.SetErr(buf)
root.SetArgs(args)

oldStdin := os.Stdin
if in != nil {
root.SetIn(in)
os.Stdin = in
}

if mem, ok := store.Driver.(*driver.Memory); ok {
mem.SetNamespace(settings.Namespace())
}
c, err := root.ExecuteC()

return c, buf.String(), err
result := buf.String()

os.Stdin = oldStdin

return c, result, err
}

// cmdTestCase describes a test case that works with releases.
Expand Down
7 changes: 6 additions & 1 deletion cmd/helm/install.go
Expand Up @@ -148,7 +148,7 @@ func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "run helm dependency update before installing the chart")
f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema")
f.BoolVar(&client.Atomic, "atomic", false, "if set, installation process purges chart on fail. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
addValueOptionsFlags(f, valueOpts)
Expand Down Expand Up @@ -210,10 +210,15 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
Getters: p,
RepositoryConfig: settings.RepositoryConfig,
RepositoryCache: settings.RepositoryCache,
Debug: settings.Debug,
}
if err := man.Update(); err != nil {
return nil, err
}
// Reload the chart with the updated Chart.lock file.
if chartRequested, err = loader.Load(cp); err != nil {
return nil, errors.Wrap(err, "failed reloading chart after repo update")
}
} else {
return nil, err
}
Expand Down
6 changes: 6 additions & 0 deletions cmd/helm/install_test.go
Expand Up @@ -111,6 +111,12 @@ func TestInstall(t *testing.T) {
cmd: "install nodeps testdata/testcharts/chart-missing-deps",
wantError: true,
},
// Install chart with update-dependency
{
name: "install chart with missing dependencies",
cmd: "install --dependency-update updeps testdata/testcharts/chart-with-subchart-update",
golden: "output/chart-with-subchart-update.txt",
},
// Install, chart with bad dependencies in Chart.yaml in /charts
{
name: "install chart with bad dependencies in Chart.yaml",
Expand Down
2 changes: 1 addition & 1 deletion cmd/helm/release_testing.go
Expand Up @@ -82,7 +82,7 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command

f := cmd.Flags()
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&outputLogs, "logs", false, "Dump the logs from test pods (this runs after all tests are complete, but before any cleanup)")
f.BoolVar(&outputLogs, "logs", false, "dump the logs from test pods (this runs after all tests are complete, but before any cleanup)")

return cmd
}
29 changes: 27 additions & 2 deletions cmd/helm/repo_list.go
Expand Up @@ -97,13 +97,38 @@ func (r *repoListWriter) encodeByFormat(out io.Writer, format output.Format) err
return nil
}

// Returns all repos from repos, except those with names matching ignoredRepoNames
// Inspired by https://stackoverflow.com/a/28701031/893211
func filterRepos(repos []*repo.Entry, ignoredRepoNames []string) []*repo.Entry {
// if ignoredRepoNames is nil, just return repo
if ignoredRepoNames == nil {
return repos
}

filteredRepos := make([]*repo.Entry, 0)

ignored := make(map[string]bool, len(ignoredRepoNames))
for _, repoName := range ignoredRepoNames {
ignored[repoName] = true
}

for _, repo := range repos {
if _, removed := ignored[repo.Name]; !removed {
filteredRepos = append(filteredRepos, repo)
}
}

return filteredRepos
}

// Provide dynamic auto-completion for repo names
func compListRepos(prefix string) []string {
func compListRepos(prefix string, ignoredRepoNames []string) []string {
var rNames []string

f, err := repo.LoadFile(settings.RepositoryConfig)
if err == nil && len(f.Repositories) > 0 {
for _, repo := range f.Repositories {
filteredRepos := filterRepos(f.Repositories, ignoredRepoNames)
for _, repo := range filteredRepos {
if strings.HasPrefix(repo.Name, prefix) {
rNames = append(rNames, repo.Name)
}
Expand Down
35 changes: 17 additions & 18 deletions cmd/helm/repo_remove.go
Expand Up @@ -32,7 +32,7 @@ import (
)

type repoRemoveOptions struct {
name string
names []string
repoFile string
repoCache string
}
Expand All @@ -41,24 +41,21 @@ func newRepoRemoveCmd(out io.Writer) *cobra.Command {
o := &repoRemoveOptions{}

cmd := &cobra.Command{
Use: "remove [NAME]",
Use: "remove [REPO1 [REPO2 ...]]",
Aliases: []string{"rm"},
Short: "remove a chart repository",
Args: require.ExactArgs(1),
Short: "remove one or more chart repositories",
Args: require.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
o.repoFile = settings.RepositoryConfig
o.repoCache = settings.RepositoryCache
o.name = args[0]
o.names = args
return o.run(out)
},
}

// Function providing dynamic auto-completion
completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) {
if len(args) != 0 {
return nil, completion.BashCompDirectiveNoFileComp
}
return compListRepos(toComplete), completion.BashCompDirectiveNoFileComp
return compListRepos(toComplete, args), completion.BashCompDirectiveNoFileComp
})

return cmd
Expand All @@ -70,18 +67,20 @@ func (o *repoRemoveOptions) run(out io.Writer) error {
return errors.New("no repositories configured")
}

if !r.Remove(o.name) {
return errors.Errorf("no repo named %q found", o.name)
}
if err := r.WriteFile(o.repoFile, 0644); err != nil {
return err
}
for _, name := range o.names {
if !r.Remove(name) {
return errors.Errorf("no repo named %q found", name)
}
if err := r.WriteFile(o.repoFile, 0644); err != nil {
return err
}

if err := removeRepoCache(o.repoCache, o.name); err != nil {
return err
if err := removeRepoCache(o.repoCache, 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
}

Expand Down
94 changes: 79 additions & 15 deletions cmd/helm/repo_remove_test.go
Expand Up @@ -44,7 +44,7 @@ func TestRepoRemove(t *testing.T) {
b := bytes.NewBuffer(nil)

rmOpts := repoRemoveOptions{
name: testRepoName,
names: []string{testRepoName},
repoFile: repoFile,
repoCache: rootDir,
}
Expand All @@ -62,14 +62,9 @@ func TestRepoRemove(t *testing.T) {
t.Error(err)
}

idx := filepath.Join(rootDir, helmpath.CacheIndexFile(testRepoName))
mf, _ := os.Create(idx)
mf.Close()

idx2 := filepath.Join(rootDir, helmpath.CacheChartsFile(testRepoName))
mf, _ = os.Create(idx2)
mf.Close()
cacheIndexFile, cacheChartsFile := createCacheFiles(rootDir, testRepoName)

// Reset the buffer before running repo remove
b.Reset()

if err := rmOpts.run(b); err != nil {
Expand All @@ -79,13 +74,7 @@ func TestRepoRemove(t *testing.T) {
t.Errorf("Unexpected output: %s", b.String())
}

if _, err := os.Stat(idx); err == nil {
t.Errorf("Error cache index file was not removed for repository %s", testRepoName)
}

if _, err := os.Stat(idx2); err == nil {
t.Errorf("Error cache chart file was not removed for repository %s", testRepoName)
}
testCacheFiles(t, cacheIndexFile, cacheChartsFile, testRepoName)

f, err := repo.LoadFile(repoFile)
if err != nil {
Expand All @@ -95,4 +84,79 @@ func TestRepoRemove(t *testing.T) {
if f.Has(testRepoName) {
t.Errorf("%s was not successfully removed from repositories list", testRepoName)
}

// Test removal of multiple repos in one go
var testRepoNames = []string{"foo", "bar", "baz"}
cacheFiles := make(map[string][]string, len(testRepoNames))

// Add test repos
for _, repoName := range testRepoNames {
o := &repoAddOptions{
name: repoName,
url: ts.URL(),
repoFile: repoFile,
}

if err := o.run(os.Stderr); err != nil {
t.Error(err)
}

cacheIndex, cacheChart := createCacheFiles(rootDir, repoName)
cacheFiles[repoName] = []string{cacheIndex, cacheChart}

}

// Create repo remove command
multiRmOpts := repoRemoveOptions{
names: testRepoNames,
repoFile: repoFile,
repoCache: rootDir,
}

// Reset the buffer before running repo remove
b.Reset()

// Run repo remove command
if err := multiRmOpts.run(b); err != nil {
t.Errorf("Error removing list of repos from repositories: %q", testRepoNames)
}

// Check that stuff were removed
if !strings.Contains(b.String(), "has been removed") {
t.Errorf("Unexpected output: %s", b.String())
}

for _, repoName := range testRepoNames {
f, err := repo.LoadFile(repoFile)
if err != nil {
t.Error(err)
}
if f.Has(repoName) {
t.Errorf("%s was not successfully removed from repositories list", repoName)
}
cacheIndex := cacheFiles[repoName][0]
cacheChart := cacheFiles[repoName][1]
testCacheFiles(t, cacheIndex, cacheChart, repoName)
}
}

func createCacheFiles(rootDir string, repoName string) (cacheIndexFile string, cacheChartsFile string) {
cacheIndexFile = filepath.Join(rootDir, helmpath.CacheIndexFile(repoName))
mf, _ := os.Create(cacheIndexFile)
mf.Close()

cacheChartsFile = filepath.Join(rootDir, helmpath.CacheChartsFile(repoName))
mf, _ = os.Create(cacheChartsFile)
mf.Close()

return cacheIndexFile, cacheChartsFile
}

func testCacheFiles(t *testing.T, cacheIndexFile string, cacheChartsFile string, repoName string) {
if _, err := os.Stat(cacheIndexFile); err == nil {
t.Errorf("Error cache index file was not removed for repository %s", repoName)
}
if _, err := os.Stat(cacheChartsFile); err == nil {
t.Errorf("Error cache chart file was not removed for repository %s", repoName)
}
}
3 changes: 2 additions & 1 deletion cmd/helm/root.go
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main // import "helm.sh/helm/v3/cmd/helm"

import (
"context"
"fmt"
"io"
"strings"
Expand Down Expand Up @@ -92,7 +93,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
completion.CompDebugln(fmt.Sprintf("About to call kube client for namespaces with timeout of: %d", to))

nsNames := []string{}
if namespaces, err := client.CoreV1().Namespaces().List(metav1.ListOptions{TimeoutSeconds: &to}); err == nil {
if namespaces, err := client.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{TimeoutSeconds: &to}); err == nil {
for _, ns := range namespaces.Items {
if strings.HasPrefix(ns.Name, toComplete) {
nsNames = append(nsNames, ns.Name)
Expand Down

0 comments on commit 376a5ca

Please sign in to comment.