Repository: roboll/helmfile (go)

Issue:
feat: Allow more characters in release labels
I have a release label

```
version: x.y.z
```

and helmfile cmd w/ `--selector whatever=dog,version=x.y.z`

```
Malformed label: version=x.y.z. Expected label in form k=v or k!=v in ...[mytemplate.yaml]...
```

Looking at:

https://github.com/roboll/helmfile/blob/c68fc5bc50ea80f5782381f1f4a1ce975ad98e06/pkg/state/release_filters.go#L51

Why are there any character restrictions on helmfile release selectors/labels anyway? They just are used to control what releases helmfile processes, and are not necessarily labels that end up in k8s so I don't see the need to comply with a specific character restriction format?


Pull request description:
Allow more characters in label selectors
label selectors now also allows . and / in filter.

Not sure if we should allow more characters? Maybe everything except whitespace?


Fixes #970

Patch:
diff --git a/USERS.md b/USERS.md
index 5fdbde9..a65470a 100644
--- a/USERS.md
+++ b/USERS.md
@@ -31,4 +31,5 @@ information to this file.
 | [William Hill](https://www.williamhill.com) | production | We have standardised on Helmfile for managing both our application services and our Kubernetes platform components. | International |  March 2021 |
 | [The Hyve](https://www.thehyve.nl) | production | We've been using Helmfile since 2019 to deploy [Radar-base applications](https://github.com/RADAR-base/RADAR-Kubernetes). [Blog Post](https://www.thehyve.nl/articles/kubernetes-added-to-radar-base) | Utrecht, Netherlands |  April 2021 |
 | [IKEA US](https://www.ikea.com) | production | We started using Helmfile this year to shrink the boilerplate involved in deploying or microservices to cloud Kubernetes clusters. | Utrecht, Netherlands |  April 2021 |
+| [ShareGate](https://www.sharegate.com) | production | Used to deploy and sync our kubernetes workloads in Azure, using Azure Pipelines and custom extensions to [install](https://marketplace.visualstudio.com/items?itemName=GSoft.HelmfileInstaller) and [execute](https://marketplace.visualstudio.com/items?itemName=GSoft.HelmfileRunner) the cli. | Montreal, Canada |  June 2021 |
 <!-- TABLE_END -->
diff --git a/pkg/state/release_filters.go b/pkg/state/release_filters.go
index 801e466..dcdd68d 100644
--- a/pkg/state/release_filters.go
+++ b/pkg/state/release_filters.go
@@ -54,8 +54,8 @@ func ParseLabels(l string) (LabelFilter, error) {
 	lf.negativeLabels = [][]string{}
 	var err error
 	labels := strings.Split(l, ",")
-	reMissmatch := regexp.MustCompile("^[a-zA-Z0-9_-]+!=[a-zA-Z0-9_-]+$")
-	reMatch := regexp.MustCompile("^[a-zA-Z0-9_-]+=[a-zA-Z0-9_-]+$")
+	reMissmatch := regexp.MustCompile(`^[a-zA-Z0-9_\.\/\+-]+!=[a-zA-Z0-9_\.\/\+-]+$`)
+	reMatch := regexp.MustCompile(`^[a-zA-Z0-9_\.\/\+-]+=[a-zA-Z0-9_\.\/\+-]+$`)
 	for _, label := range labels {
 		if match := reMissmatch.MatchString(label); match { // k!=v case
 			kv := strings.Split(label, "!=")


Test patch:
diff --git a/pkg/state/selector_test.go b/pkg/state/selector_test.go
index 80cd4af..82efbee 100644
--- a/pkg/state/selector_test.go
+++ b/pkg/state/selector_test.go
@@ -22,22 +22,22 @@ func TestSelectReleasesWithOverrides(t *testing.T) {
 		{
 			subject:  "multiple OR selectors (non-nillable label first)",
 			selector: []string{"name=foo", "type!=bar"},
-			want:     []string{"nolabel1", "nolabel2", "foo"},
+			want:     []string{"nolabel1", "nolabel2", "foo", "bar", "foobar"},
 		},
 		{
 			subject:  "multiple AND conditions (nillable label first)",
 			selector: []string{"type!=bar,name!=nolabel2"},
-			want:     []string{"nolabel1"},
+			want:     []string{"nolabel1", "bar", "foobar"},
 		},
 		{
 			subject:  "multiple AND conditions (non-nillable label first)",
 			selector: []string{"name!=nolabel2,type!=bar"},
-			want:     []string{"nolabel1"},
+			want:     []string{"nolabel1", "bar", "foobar"},
 		},
 		{
 			subject:  "inequality on nillable label",
 			selector: []string{"type!=bar"},
-			want:     []string{"nolabel1", "nolabel2"},
+			want:     []string{"nolabel1", "nolabel2", "bar", "foobar"},
 		},
 		{
 			subject:  "equality on nillable label",
@@ -47,13 +47,33 @@ func TestSelectReleasesWithOverrides(t *testing.T) {
 		{
 			subject:  "inequality on non-nillable label",
 			selector: []string{"name!=nolabel1"},
-			want:     []string{"nolabel2", "foo"},
+			want:     []string{"nolabel2", "foo", "bar", "foobar"},
 		},
 		{
 			subject:  "equality on non-nillable label",
 			selector: []string{"name=nolabel1"},
 			want:     []string{"nolabel1"},
 		},
+		{
+			subject:  "equality on non-alphabetic character label",
+			selector: []string{"version=1.2/3"},
+			want:     []string{"bar"},
+		},
+		{
+			subject:  "equality on non-alphabetic character label",
+			selector: []string{"version=1.2.3+123"},
+			want:     []string{"foobar"},
+		},
+		{
+			subject:  "inequality on non-alphatetic character label",
+			selector: []string{"version!=1.2/3"},
+			want:     []string{"nolabel1", "nolabel2", "foo", "foobar"},
+		},
+		{
+			subject:  "inequality on non-alphatetic character label",
+			selector: []string{"version!=1.2.3+123"},
+			want:     []string{"nolabel1", "nolabel2", "foo", "bar"},
+		},
 	}
 
 	example := []byte(`releases:
@@ -68,6 +88,16 @@ func TestSelectReleasesWithOverrides(t *testing.T) {
   chart: stable/foo
   labels:
     type: bar
+- name: bar
+  namespace: kube-system
+  chart: stable/bar
+  labels:
+    version: 1.2/3
+- name: foobar
+  namespace: kube-system
+  chart: stable/bar
+  labels:
+    version: 1.2.3+123
 `)
 
 	state := stateTestEnv{