Repository: helm/helm (go)

Issue:
helm3 - --timeout flag ignored
Output of `helm version`:
```
version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:"go1.12.9"}
```

Output of `kubectl version`:
```
Client Version: version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.9", GitCommit:"3e4f6a92de5f259ef313ad876bb008897f6a98f0", GitTreeState:"archive", BuildDate:"1970-01-01T00:00:01Z", GoVersion:"go1.11.6", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.2", GitCommit:"c97fe5036ef3df2967d086711e6c0c405941e14b", GitTreeState:"clean", BuildDate:"2019-10-15T19:09:08Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
```

Cloud Provider/Platform (AKS, GKE, Minikube etc.): 
Minikube, self-hosted Kubernetes

I have such job:
```
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migrate
  annotations:
    "helm.sh/hook": pre-install,pre-upgrade
    "helm.sh/hook-delete-policy": hook-succeeded
spec:
  template:
    metadata:
      name: db-migrate
    spec:
      restartPolicy: Never
      containers:
        - name: migrade-exmple
          image: alpine:latest
          command: ["sleep", "300"]

```

I install chart with:
```
helm upgrade --install myname . --timeout 3000s
```

After 60s no matter what in the timeout flag I get:
```
Release "myname" does not exist. Installing it now.
Error: failed pre-install: watch closed before UntilWithoutRetry timeout
```


Pull request:
fix(kube): Port use of watcher with retries to wait for resources
Port of Helm 2 PR #6014 and PR #6907 with modifications to integrate with Helm 3.

Fixes #7210 

Patch:
diff --git a/cmd/helm/root.go b/cmd/helm/root.go
index f0a034d45..3a15966bb 100644
--- a/cmd/helm/root.go
+++ b/cmd/helm/root.go
@@ -64,12 +64,12 @@ __helm_override_flags_to_kubectl_flags()
     # --kubeconfig, -n, --namespace stay the same for kubectl
     # --kube-context becomes --context for kubectl
     __helm_debug "${FUNCNAME[0]}: flags to convert: $1"
-    echo "$1" | sed s/kube-context/context/
+    echo "$1" | \sed s/kube-context/context/
 }
 
 __helm_get_repos()
 {
-    eval $(__helm_binary_name) repo list 2>/dev/null | tail +2 | cut -f1
+    eval $(__helm_binary_name) repo list 2>/dev/null | \tail +2 | \cut -f1
 }
 
 __helm_get_contexts()
@@ -125,7 +125,7 @@ __helm_zsh_comp_nospace() {
     # We only do this if there is a single choice left for completion
     # to reduce the times the user could be presented with the fake completion choice.
 
-    out=($(echo ${in[*]} | tr " " "\n" | \grep "^${cur}"))
+    out=($(echo ${in[*]} | \tr " " "\n" | \grep "^${cur}"))
     __helm_debug "${FUNCNAME[0]}: out is ${out[*]}"
 
     [ ${#out[*]} -eq 1 ] && out+=("${out}.")
@@ -145,7 +145,7 @@ __helm_list_charts()
     for repo in $(__helm_get_repos); do
         if [[ "${cur}" =~ ^${repo}/.* ]]; then
             # We are doing completion from within a repo
-            out=$(eval $(__helm_binary_name) search repo ${cur} 2>/dev/null | cut -f1 | \grep ^${cur})
+            out=$(eval $(__helm_binary_name) search repo ${cur} 2>/dev/null | \cut -f1 | \grep ^${cur})
             nospace=0
         elif [[ ${repo} =~ ^${cur}.* ]]; then
             # We are completing a repo name
@@ -236,7 +236,7 @@ __helm_list_plugins()
     __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
     local out
     # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h3)
-    if out=$(eval $(__helm_binary_name) plugin list 2>/dev/null | tail +2 | cut -f1); then
+    if out=$(eval $(__helm_binary_name) plugin list 2>/dev/null | \tail +2 | \cut -f1); then
         COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
     fi
 }
diff --git a/cmd/helm/status.go b/cmd/helm/status.go
index 92e12dbe0..92a947c26 100644
--- a/cmd/helm/status.go
+++ b/cmd/helm/status.go
@@ -31,12 +31,13 @@ import (
 	"helm.sh/helm/v3/pkg/release"
 )
 
+// NOTE: Keep the list of statuses up-to-date with pkg/release/status.go.
 var statusHelp = `
 This command shows the status of a named release.
 The status consists of:
 - last deployment time
 - k8s namespace in which the release lives
-- state of the release (can be: unknown, deployed, deleted, superseded, failed or deleting)
+- state of the release (can be: unknown, deployed, uninstalled, superseded, failed, uninstalling, pending-install, pending-upgrade or pending-rollback)
 - list of resources that this release consists of, sorted by kind
 - details on last test suite run, if applicable
 - additional notes provided by the chart
diff --git a/go.mod b/go.mod
index 57b342ed2..8e972fdfe 100644
--- a/go.mod
+++ b/go.mod
@@ -5,8 +5,8 @@ go 1.13
 require (
 	github.com/BurntSushi/toml v0.3.1
 	github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect
-	github.com/Masterminds/semver/v3 v3.0.1
-	github.com/Masterminds/sprig/v3 v3.0.0
+	github.com/Masterminds/semver/v3 v3.0.3
+	github.com/Masterminds/sprig/v3 v3.0.2
 	github.com/Masterminds/vcs v1.13.0
 	github.com/Microsoft/go-winio v0.4.12 // indirect
 	github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
diff --git a/go.sum b/go.sum
index 5659b06f8..fb11fdbc4 100644
--- a/go.sum
+++ b/go.sum
@@ -27,8 +27,12 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP
 github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
 github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk=
 github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14=
+github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
 github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g=
 github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U=
+github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8=
+github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU=
 github.com/Masterminds/vcs v1.13.0 h1:USF5TvZGYgIpcbNAEMLfFhHqP08tFZVlUVrmTSpqnyA=
 github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
 github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
diff --git a/pkg/action/install.go b/pkg/action/install.go
index 89a343a6f..dc5941810 100644
--- a/pkg/action/install.go
+++ b/pkg/action/install.go
@@ -555,6 +555,10 @@ func (i *Install) NameAndChart(args []string) (string, string, error) {
 		return nil
 	}
 
+	if len(args) > 2 {
+		return args[0], args[1], errors.Errorf("expected at most two arguments, unexpected arguments: %v", strings.Join(args[2:], ", "))
+	}
+
 	if len(args) == 2 {
 		return args[0], args[1], flagsNotSet()
 	}
diff --git a/pkg/kube/client.go b/pkg/kube/client.go
index ab1f600ff..96179d19d 100644
--- a/pkg/kube/client.go
+++ b/pkg/kube/client.go
@@ -34,6 +34,7 @@ import (
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/fields"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/strategicpatch"
@@ -41,6 +42,7 @@ import (
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	"k8s.io/cli-runtime/pkg/resource"
 	"k8s.io/client-go/kubernetes/scheme"
+	cachetools "k8s.io/client-go/tools/cache"
 	watchtools "k8s.io/client-go/tools/watch"
 	cmdutil "k8s.io/kubectl/pkg/cmd/util"
 )
@@ -419,10 +421,13 @@ func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) err
 
 	c.Log("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout)
 
-	w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion)
+	// Use a selector on the name of the resource. This should be unique for the
+	// given version and kind
+	selector, err := fields.ParseSelector(fmt.Sprintf("metadata.name=%s", info.Name))
 	if err != nil {
 		return err
 	}
+	lw := cachetools.NewListWatchFromClient(info.Client, info.Mapping.Resource.Resource, info.Namespace, selector)
 
 	// What we watch for depends on the Kind.
 	// - For a Job, we watch for completion.
@@ -432,7 +437,7 @@ func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) err
 
 	ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout)
 	defer cancel()
-	_, err = watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
+	_, err = watchtools.ListWatchUntil(ctx, lw, func(e watch.Event) (bool, error) {
 		// Make sure the incoming object is versioned as we use unstructured
 		// objects when we build manifests
 		obj := convertWithMapper(e.Object, info.Mapping)
diff --git a/pkg/release/status.go b/pkg/release/status.go
index cd025e279..0e535f9a4 100644
--- a/pkg/release/status.go
+++ b/pkg/release/status.go
@@ -19,6 +19,7 @@ package release
 type Status string
 
 // Describe the status of a release
+// NOTE: Make sure to update cmd/helm/status.go when adding or modifying any of these statuses.
 const (
 	// StatusUnknown indicates that a release is in an uncertain state.
 	StatusUnknown Status = "unknown"


Test patch:
diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go
index 4637a4b10..d6f1c88cd 100644
--- a/pkg/action/install_test.go
+++ b/pkg/action/install_test.go
@@ -505,6 +505,14 @@ func TestNameAndChart(t *testing.T) {
 		t.Fatal("expected an error")
 	}
 	is.Equal("must either provide a name or specify --generate-name", err.Error())
+
+	instAction.NameTemplate = ""
+	instAction.ReleaseName = ""
+	_, _, err = instAction.NameAndChart([]string{"foo", chartName, "bar"})
+	if err == nil {
+		t.Fatal("expected an error")
+	}
+	is.Equal("expected at most two arguments, unexpected arguments: bar", err.Error())
 }
 
 func TestNameAndChartGenerateName(t *testing.T) {