Repository: helm/helm (go)

Issue:
Failed upgrade with --cleanup-on-fail errors out with "unable to cleanup resources: object not found"
Output of `helm version`:
```
$ helm-3.0.2 version
version.BuildInfo{Version:"v3.0.2", GitCommit:"19e47ee3283ae98139d98460de796c1be1e3975f", GitTreeState:"clean", GoVersion:"go1.13.5"}
```

Output of `kubectl version`:
```
Client Version: version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.0", GitCommit:"2bd9643cee5b3b3a5ecbd3af49d09018f0773c77", GitTreeState:"clean", BuildDate:"2019-09-18T14:36:53Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.6", GitCommit:"7015f71e75f670eb9e7ebd4b5749639d42e20079", GitTreeState:"clean", BuildDate:"2019-11-13T11:11:50Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/amd64"}
```

Cloud Provider/Platform (AKS, GKE, Minikube etc.): AWS, via Kops.

I'm running an `upgrade` over a successfully deployed release. The `upgrade` sets image tag to a non-existent one, causing deployment to never succeed and timeout.

Expectation: release is rolled back to the previous version
Actual: `helm` fails with `Error: UPGRADE FAILED: an error occurred while cleaning up resources. original upgrade error: timed out waiting for the condition: unable to cleanup resources: object not found, skipping delete` error.

Full log:
```
$ helm-3.0.2 upgrade --install myapp charts/some-chart --version 0.6.0 --values values.yaml --set ingress.host=myapp.dot.com --set-string name=myapp,image.name=registry/myapp,image.tag=WRONGTAG --wait --atomic --cleanup-on-fail --namespace helm3-test-1 --timeout 1m --debug -v 5
history.go:52: [debug] getting history for release myapp
upgrade.go:79: [debug] preparing upgrade for myapp
I0128 19:46:56.330953   72366 cached_discovery.go:78] skipped caching discovery info due to Unauthorized
action.go:120: [debug] WARNING: The Kubernetes server has an orphaned API service. Server reports: unable to retrieve the complete list of server APIs: node.k8s.io/v1beta1: Unauthorized
action.go:121: [debug] WARNING: To fix this, kubectl delete apiservice <service-name>
upgrade.go:87: [debug] performing update for myapp
upgrade.go:225: [debug] creating upgraded release for myapp
client.go:151: [debug] checking 5 resources for changes
client.go:386: [debug] Looks like there are no changes for PodDisruptionBudget "myapp"
client.go:386: [debug] Looks like there are no changes for Service "myapp"
client.go:386: [debug] Looks like there are no changes for Ingress "myapp"
client.go:386: [debug] Looks like there are no changes for ServiceMonitor "myapp-monitor"
wait.go:51: [debug] beginning wait for 5 resources with timeout of 1m0s
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
wait.go:199: [debug] Deployment is not ready: mynamespace/myapp. 0 out of 1 expected pods are ready
upgrade.go:280: [debug] warning: Upgrade "myapp" failed: timed out waiting for the condition
upgrade.go:286: [debug] Cleanup on fail set, cleaning up 0 resources
Error: UPGRADE FAILED: an error occurred while cleaning up resources. original upgrade error: timed out waiting for the condition: unable to cleanup resources: object not found, skipping delete
helm.go:76: [debug] unable to cleanup resources: object not found, skipping delete
an error occurred while cleaning up resources. original upgrade error: timed out waiting for the condition
helm.sh/helm/v3/pkg/action.(*Upgrade).failRelease
        /home/circleci/helm.sh/helm/pkg/action/upgrade.go:293
helm.sh/helm/v3/pkg/action.(*Upgrade).performUpgrade
        /home/circleci/helm.sh/helm/pkg/action/upgrade.go:258
helm.sh/helm/v3/pkg/action.(*Upgrade).Run
        /home/circleci/helm.sh/helm/pkg/action/upgrade.go:88
main.newUpgradeCmd.func1
        /home/circleci/helm.sh/helm/cmd/helm/upgrade.go:134
github.com/spf13/cobra.(*Command).execute
        /go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:826
github.com/spf13/cobra.(*Command).ExecuteC
        /go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:914
github.com/spf13/cobra.(*Command).Execute
        /go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:864
main.main
        /home/circleci/helm.sh/helm/cmd/helm/helm.go:75
runtime.main
        /usr/local/go/src/runtime/proc.go:203
runtime.goexit
        /usr/local/go/src/runtime/asm_amd64.s:1357
UPGRADE FAILED
main.newUpgradeCmd.func1
        /home/circleci/helm.sh/helm/cmd/helm/upgrade.go:136
github.com/spf13/cobra.(*Command).execute
        /go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:826
github.com/spf13/cobra.(*Command).ExecuteC
        /go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:914
github.com/spf13/cobra.(*Command).Execute
        /go/pkg/mod/github.com/spf13/cobra@v0.0.5/command.go:864
main.main
        /home/circleci/helm.sh/helm/cmd/helm/helm.go:75
runtime.main
        /usr/local/go/src/runtime/proc.go:203
runtime.goexit
        /usr/local/go/src/runtime/asm_amd64.s:1357
```

Unsure why the `Unauthorized` error occurs. `helm` is clearly able to connect to the cluster, as can `kubectl` (auth is via `aws-iam-authenticator`).

Additional info: I ran this with a debugger (with the same settings and results), and noticed that `results.Created` https://github.com/helm/helm/blob/19e47ee3283ae98139d98460de796c1be1e3975f/pkg/action/upgrade.go#L265 is `nil`. `results.Updated`, however, contained objects. Perhaps here https://github.com/helm/helm/blob/19e47ee3283ae98139d98460de796c1be1e3975f/pkg/action/upgrade.go#L285 an additional check for `&& len(created) > 0` is missing?

Pull request description:
When no resources were created, do not try to clean them up
Fixes #7481

Test will fail if the change is undone.

Signed-off-by: Ilya Shaisultanov <ilya.shaisultanov@gmail.com>

Patch:
diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go
index cdc40eaaa..1db4184ff 100644
--- a/pkg/action/upgrade.go
+++ b/pkg/action/upgrade.go
@@ -291,7 +291,7 @@ func (u *Upgrade) failRelease(rel *release.Release, created kube.ResourceList, e
 	rel.Info.Status = release.StatusFailed
 	rel.Info.Description = msg
 	u.cfg.recordRelease(rel)
-	if u.CleanupOnFail {
+	if u.CleanupOnFail && len(created) > 0 {
 		u.cfg.Log("Cleanup on fail set, cleaning up %d resources", len(created))
 		_, errs := u.cfg.KubeClient.Delete(created)
 		if errs != nil {


Test patch:
diff --git a/pkg/action/upgrade_test.go b/pkg/action/upgrade_test.go
index e7bfeefc5..f25d115c4 100644
--- a/pkg/action/upgrade_test.go
+++ b/pkg/action/upgrade_test.go
@@ -60,6 +60,31 @@ func TestUpgradeRelease_Wait(t *testing.T) {
 	is.Equal(res.Info.Status, release.StatusFailed)
 }
 
+func TestUpgradeRelease_CleanupOnFail(t *testing.T) {
+	is := assert.New(t)
+	req := require.New(t)
+
+	upAction := upgradeAction(t)
+	rel := releaseStub()
+	rel.Name = "come-fail-away"
+	rel.Info.Status = release.StatusDeployed
+	upAction.cfg.Releases.Create(rel)
+
+	failer := upAction.cfg.KubeClient.(*kubefake.FailingKubeClient)
+	failer.WaitError = fmt.Errorf("I timed out")
+	failer.DeleteError = fmt.Errorf("I tried to delete nil")
+	upAction.cfg.KubeClient = failer
+	upAction.Wait = true
+	upAction.CleanupOnFail = true
+	vals := map[string]interface{}{}
+
+	res, err := upAction.Run(rel.Name, buildChart(), vals)
+	req.Error(err)
+	is.NotContains(err.Error(), "unable to cleanup resources")
+	is.Contains(res.Info.Description, "I timed out")
+	is.Equal(res.Info.Status, release.StatusFailed)
+}
+
 func TestUpgradeRelease_Atomic(t *testing.T) {
 	is := assert.New(t)
 	req := require.New(t)