From 789362dfd4c520f3d5c67e1b3f786ba227898a67 Mon Sep 17 00:00:00 2001 From: Shane Starcher Date: Tue, 17 Apr 2018 11:10:46 -0400 Subject: [PATCH] support the same selector repeated --- state/release_filters.go | 21 ++++++++------ state/state_test.go | 61 ++++++++++++++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/state/release_filters.go b/state/release_filters.go index 0709c35b..a7a61992 100644 --- a/state/release_filters.go +++ b/state/release_filters.go @@ -15,14 +15,16 @@ type ReleaseFilter interface { // LabelFilter matches a release with the given positive lables. Negative labels // invert the match for cases such as tier!=backend type LabelFilter struct { - positiveLabels map[string]string - negativeLabels map[string]string + positiveLabels [][]string + negativeLabels [][]string } // Match will match a release that has the same labels as the filter func (l LabelFilter) Match(r ReleaseSpec) bool { if len(l.positiveLabels) > 0 { - for k, v := range l.positiveLabels { + for _, element := range l.positiveLabels { + k := element[0] + v := element[1] if rVal, ok := r.Labels[k]; !ok { return false } else if rVal != v { @@ -30,8 +32,11 @@ func (l LabelFilter) Match(r ReleaseSpec) bool { } } } + if len(l.negativeLabels) > 0 { - for k, v := range l.negativeLabels { + for _, element := range l.negativeLabels { + k := element[0] + v := element[1] if rVal, ok := r.Labels[k]; !ok { return true } else if rVal == v { @@ -45,17 +50,17 @@ func (l LabelFilter) Match(r ReleaseSpec) bool { // ParseLabels takes a label in the form foo=bar,baz!=bat and returns a LabelFilter that will match the labels func ParseLabels(l string) (LabelFilter, error) { lf := LabelFilter{} - lf.positiveLabels = map[string]string{} - lf.negativeLabels = map[string]string{} + lf.positiveLabels = [][]string{} + lf.negativeLabels = [][]string{} var err error labels := strings.Split(l, ",") for _, label := range labels { if match, _ := regexp.MatchString("^[a-zA-Z0-9_-]+!=[a-zA-Z0-9_-]+$", label); match == true { // k!=v case kv := strings.Split(label, "!=") - lf.negativeLabels[kv[0]] = kv[1] + lf.negativeLabels = append(lf.negativeLabels, kv) } else if match, _ := regexp.MatchString("^[a-zA-Z0-9_-]+=[a-zA-Z0-9_-]+$", label); match == true { // k=v case kv := strings.Split(label, "=") - lf.positiveLabels[kv[0]] = kv[1] + lf.positiveLabels = append(lf.positiveLabels, kv) } else { // malformed case err = fmt.Errorf("Malformed label: %s. Expected label in form k=v or k!=v", label) } diff --git a/state/state_test.go b/state/state_test.go index 11c91c61..871f0287 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -87,13 +87,13 @@ func TestReadFromYaml_FilterReleasesOnLabels(t *testing.T) { filter LabelFilter results []bool }{ - {LabelFilter{positiveLabels: map[string]string{"tier": "frontend"}}, + {LabelFilter{positiveLabels: [][]string{[]string{"tier", "frontend"}}}, []bool{true, true, false}}, - {LabelFilter{positiveLabels: map[string]string{"tier": "frontend", "foo": "bar"}}, + {LabelFilter{positiveLabels: [][]string{[]string{"tier", "frontend"}, []string{"foo", "bar"}}}, []bool{true, false, false}}, - {LabelFilter{negativeLabels: map[string]string{"tier": "frontend"}}, + {LabelFilter{negativeLabels: [][]string{[]string{"tier", "frontend"}}}, []bool{false, false, true}}, - {LabelFilter{positiveLabels: map[string]string{"tier": "frontend"}, negativeLabels: map[string]string{"foo": "bar"}}, + {LabelFilter{positiveLabels: [][]string{[]string{"tier", "frontend"}}, negativeLabels: [][]string{[]string{"foo", "bar"}}}, []bool{false, true, false}}, } state, err := readFromYaml(yamlContent, yamlFile) @@ -109,18 +109,57 @@ func TestReadFromYaml_FilterReleasesOnLabels(t *testing.T) { } } +func TestReadFromYaml_FilterNegatives(t *testing.T) { + yamlFile := "example/path/to/yaml/file" + yamlContent := []byte(`releases: +- name: myrelease1 + chart: mychart1 + labels: + stage: pre + foo: bar +- name: myrelease2 + chart: mychart2 + labels: + stage: post +- name: myrelease3 + chart: mychart3 +`) + cases := []struct { + filter LabelFilter + results []bool + }{ + {LabelFilter{positiveLabels: [][]string{[]string{"stage", "pre"}}}, + []bool{true, false, false}}, + {LabelFilter{positiveLabels: [][]string{[]string{"stage", "post"}}}, + []bool{false, true, false}}, + {LabelFilter{negativeLabels: [][]string{[]string{"stage", "pre"}, []string{"stage", "post"}}}, + []bool{false, false, true}}, + } + state, err := readFromYaml(yamlContent, yamlFile) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + for idx, c := range cases { + for idx2, expected := range c.results { + if f := c.filter.Match(state.Releases[idx2]); f != expected { + t.Errorf("[case: %d][outcome: %d] Unexpected outcome wanted %t, got %t", idx, idx2, expected, f) + } + } + } +} + func TestLabelParsing(t *testing.T) { cases := []struct { labelString string expectedFilter LabelFilter errorExected bool }{ - {"foo=bar", LabelFilter{positiveLabels: map[string]string{"foo": "bar"}, negativeLabels: map[string]string{}}, false}, - {"foo!=bar", LabelFilter{positiveLabels: map[string]string{}, negativeLabels: map[string]string{"foo": "bar"}}, false}, - {"foo!=bar,baz=bat", LabelFilter{positiveLabels: map[string]string{"baz": "bat"}, negativeLabels: map[string]string{"foo": "bar"}}, false}, - {"foo", LabelFilter{positiveLabels: map[string]string{}, negativeLabels: map[string]string{}}, true}, - {"foo!=bar=baz", LabelFilter{positiveLabels: map[string]string{}, negativeLabels: map[string]string{}}, true}, - {"=bar", LabelFilter{positiveLabels: map[string]string{}, negativeLabels: map[string]string{}}, true}, + {"foo=bar", LabelFilter{positiveLabels: [][]string{[]string{"foo", "bar"}}, negativeLabels: [][]string{}}, false}, + {"foo!=bar", LabelFilter{positiveLabels: [][]string{}, negativeLabels: [][]string{[]string{"foo", "bar"}}}, false}, + {"foo!=bar,baz=bat", LabelFilter{positiveLabels: [][]string{[]string{"baz", "bat"}}, negativeLabels: [][]string{[]string{"foo", "bar"}}}, false}, + {"foo", LabelFilter{positiveLabels: [][]string{}, negativeLabels: [][]string{}}, true}, + {"foo!=bar=baz", LabelFilter{positiveLabels: [][]string{}, negativeLabels: [][]string{}}, true}, + {"=bar", LabelFilter{positiveLabels: [][]string{}, negativeLabels: [][]string{}}, true}, } for idx, c := range cases { filter, err := ParseLabels(c.labelString) @@ -129,7 +168,7 @@ func TestLabelParsing(t *testing.T) { } else if err == nil && c.errorExected { t.Errorf("[%d] Expected %s to result in an error but got none", idx, c.labelString) } else if !reflect.DeepEqual(filter, c.expectedFilter) { - t.Errorf("[%d] parsed label did not result in expected filter: %v", idx, filter) + t.Errorf("[%d] parsed label did not result in expected filter: %v, expected: %v", idx, filter, c.expectedFilter) } } }