Repository: helm/helm (go)

Issue:
Helm v3 generates invalid release name 
<!-- If you need help or think you have found a bug, please help us with your issue by entering the following information (otherwise you can delete this text): -->

Output of `helm version`:
version.BuildInfo{Version:"v3.0.0-beta.3", GitCommit:"5cb923eecbe80d1ad76399aee234717c11931d9a", GitTreeState:"clean", GoVersion:"go1.12.9"}

Output of `kubectl version`:
Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.6", GitCommit:"96fac5cd13a5dc064f7d9f4f23030a6aeface6cc", GitTreeState:"clean", BuildDate:"2019-08-19T11:13:49Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.6", GitCommit:"96fac5cd13a5dc064f7d9f4f23030a6aeface6cc", GitTreeState:"clean", BuildDate:"2019-08-19T11:05:16Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"}

Cloud Provider/Platform (AKS, GKE, Minikube etc.): 
Docker Desktop 

Running `helm install mongodb-7.3.0.tgz -g`
Error: Service "mongodb-7.3.0.tgz-1570513937" is invalid: metadata.name: Invalid value: "mongodb-7.3.0.tgz-1570513937": a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name',  or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')

I think the issue arise from 
https://github.com/helm/helm/blob/cc450563835088945aa79dbfa0bce0bc031fb390/pkg/action/install.go#L556-L562

As the base = "mongodb-7.3.0.tgz". 

Should we add validation like Helm v2
https://github.com/helm/helm/blob/fb2cbb001986b28dec5303f5acad65390a1a8118/cmd/helm/install.go#L260-L263



Pull request:
fix(action): strip file extensions from name
This PR fixes an issue where `helm install mychart.tgz --generate-name` generates an invalid name with the file extension present. This also adds a ton of unit test coverage around `NameAndChart` that wasn't there before.

closes #6600

Signed-off-by: Matthew Fisher <matt.fisher@microsoft.com>

Patch:
diff --git a/pkg/action/install.go b/pkg/action/install.go
index 606226500..8f1b5528b 100644
--- a/pkg/action/install.go
+++ b/pkg/action/install.go
@@ -564,6 +564,10 @@ func (i *Install) NameAndChart(args []string) (string, string, error) {
 	if base == "." || base == "" {
 		base = "chart"
 	}
+	// if present, strip out the file extension from the name
+	if idx := strings.Index(base, "."); idx != -1 {
+		base = base[0:idx]
+	}
 
 	return fmt.Sprintf("%s-%d", base, time.Now().Unix()), args[0], nil
 }
diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go
index c67cde04f..6f0b38c05 100644
--- a/pkg/chartutil/create.go
+++ b/pkg/chartutil/create.go
@@ -324,7 +324,7 @@ const defaultNotes = `1. Get the application URL by running these commands:
 {{- else if contains "ClusterIP" .Values.service.type }}
   export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "<CHARTNAME>.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
   echo "Visit http://127.0.0.1:8080 to use your application"
-  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80
+  kubectl port-forward $POD_NAME 8080:80
 {{- end }}
 `
 
diff --git a/pkg/kube/client.go b/pkg/kube/client.go
index 2a5661306..06df8490e 100644
--- a/pkg/kube/client.go
+++ b/pkg/kube/client.go
@@ -401,7 +401,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object,
 		// send patch to server
 		obj, err = helper.Patch(target.Namespace, target.Name, patchType, patch, nil)
 		if err != nil {
-			return errors.Wrapf(err, "cannot patch %q with kind %s", target.Name, kind)
+			log.Printf("Cannot patch %s: %q (%v)", kind, target.Name, err)
 		}
 	}
 


Test patch:
diff --git a/pkg/action/install_test.go b/pkg/action/install_test.go
index 406574995..bb36b843d 100644
--- a/pkg/action/install_test.go
+++ b/pkg/action/install_test.go
@@ -33,6 +33,7 @@ import (
 	kubefake "helm.sh/helm/v3/pkg/kube/fake"
 	"helm.sh/helm/v3/pkg/release"
 	"helm.sh/helm/v3/pkg/storage/driver"
+	"helm.sh/helm/v3/pkg/time"
 )
 
 type nameTemplateTestCase struct {
@@ -469,3 +470,99 @@ func TestInstallReleaseOutputDir(t *testing.T) {
 	_, err = os.Stat(filepath.Join(dir, "hello/templates/empty"))
 	is.True(os.IsNotExist(err))
 }
+
+func TestNameAndChart(t *testing.T) {
+	is := assert.New(t)
+	instAction := installAction(t)
+	chartName := "./foo"
+
+	name, chrt, err := instAction.NameAndChart([]string{chartName})
+	if err != nil {
+		t.Fatal(err)
+	}
+	is.Equal(instAction.ReleaseName, name)
+	is.Equal(chartName, chrt)
+
+	instAction.GenerateName = true
+	_, _, err = instAction.NameAndChart([]string{"foo", chartName})
+	if err == nil {
+		t.Fatal("expected an error")
+	}
+	is.Equal("cannot set --generate-name and also specify a name", err.Error())
+
+	instAction.GenerateName = false
+	instAction.NameTemplate = "{{ . }}"
+	_, _, err = instAction.NameAndChart([]string{"foo", chartName})
+	if err == nil {
+		t.Fatal("expected an error")
+	}
+	is.Equal("cannot set --name-template and also specify a name", err.Error())
+
+	instAction.NameTemplate = ""
+	instAction.ReleaseName = ""
+	_, _, err = instAction.NameAndChart([]string{chartName})
+	if err == nil {
+		t.Fatal("expected an error")
+	}
+	is.Equal("must either provide a name or specify --generate-name", err.Error())
+}
+
+func TestNameAndChartGenerateName(t *testing.T) {
+	is := assert.New(t)
+	instAction := installAction(t)
+
+	instAction.ReleaseName = ""
+	instAction.GenerateName = true
+
+	tests := []struct {
+		Name         string
+		Chart        string
+		ExpectedName string
+	}{
+		{
+			"local filepath",
+			"./chart",
+			fmt.Sprintf("chart-%d", time.Now().Unix()),
+		},
+		{
+			"dot filepath",
+			".",
+			fmt.Sprintf("chart-%d", time.Now().Unix()),
+		},
+		{
+			"empty filepath",
+			"",
+			fmt.Sprintf("chart-%d", time.Now().Unix()),
+		},
+		{
+			"packaged chart",
+			"chart.tgz",
+			fmt.Sprintf("chart-%d", time.Now().Unix()),
+		},
+		{
+			"packaged chart with .tar.gz extension",
+			"chart.tar.gz",
+			fmt.Sprintf("chart-%d", time.Now().Unix()),
+		},
+		{
+			"packaged chart with local extension",
+			"./chart.tgz",
+			fmt.Sprintf("chart-%d", time.Now().Unix()),
+		},
+	}
+
+	for _, tc := range tests {
+		tc := tc
+		t.Run(tc.Name, func(t *testing.T) {
+			t.Parallel()
+
+			name, chrt, err := instAction.NameAndChart([]string{tc.Chart})
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			is.Equal(tc.ExpectedName, name)
+			is.Equal(tc.Chart, chrt)
+		})
+	}
+}