From f1f9255be4ec81e9d6bb217afdd3905ece23b882 Mon Sep 17 00:00:00 2001 From: Gobinath Krishnamoorthy Date: Fri, 31 Jan 2020 15:32:43 -0800 Subject: [PATCH] Add support for global() selector in calico network policy Add support for rules and selector for global keyword and add unit test case --- .../syncersv1/updateprocessors/rules.go | 5 +++ .../syncersv1/updateprocessors/rules_test.go | 33 +++++++++++++++++++ lib/selector/parser/ast.go | 16 +++++++++ lib/selector/parser/parser.go | 3 ++ lib/selector/parser/parser_test.go | 3 ++ 5 files changed, 60 insertions(+) diff --git a/lib/backend/syncersv1/updateprocessors/rules.go b/lib/backend/syncersv1/updateprocessors/rules.go index 62185382e..309ed4f8e 100644 --- a/lib/backend/syncersv1/updateprocessors/rules.go +++ b/lib/backend/syncersv1/updateprocessors/rules.go @@ -70,6 +70,11 @@ func getEndpointSelector(namespaceSelector, endpointSelector, serviceAccountSele // all endpoints, translate this to an equivalent expression which means select any workload that // is in a namespace. nsSelector = strings.Replace(nsSelector, "all()", "has(projectcalico.org/namespace)", -1) + + // We treat "global()" as opposite to `all()`, selects only host endpoints or GlobalNetworksets + // Since in the v1 data model "all()" will select all endpoints, + // translate this to an equivalent expressions which means select no workload endpoints + nsSelector = strings.Replace(nsSelector, "global()", "!has(projectcalico.org/namespace)", -1) } else if ns != "" { // No namespace selector was given and this is a namespaced network policy, // so the rule applies only to its own namespace. diff --git a/lib/backend/syncersv1/updateprocessors/rules_test.go b/lib/backend/syncersv1/updateprocessors/rules_test.go index a7c9a3dae..72bb63f1e 100644 --- a/lib/backend/syncersv1/updateprocessors/rules_test.go +++ b/lib/backend/syncersv1/updateprocessors/rules_test.go @@ -472,4 +472,37 @@ var _ = Describe("Test the Rules Conversion Functions", func() { Expect(rulev1.DstSelector).To(Equal(dste)) }) }) + + It("should parse a set of rules and validates the namespaceselector with label and all()", func() { + rules := []apiv3.Rule{ + { + Action: apiv3.Allow, + Destination: apiv3.EntityRule{ + NamespaceSelector: "namespace == 'red'", + Selector: "has(label1)", + }, + }, + { + Action: apiv3.Allow, + Destination: apiv3.EntityRule{ + NamespaceSelector: "all()", + Selector: "has(label2)", + }, + }, + { + Action: apiv3.Allow, + Destination: apiv3.EntityRule{ + NamespaceSelector: "global()", + Selector: "has(label3)", + }, + }, + } + + outRules := updateprocessors.RulesAPIV2ToBackend(rules, "namespace") + // The first rule should select "namespace `red`, the second rule should have 'has(projectcalico.org/namespace)' + // and third rule should select '!has(projectcalico.org/namespace)' + Expect(outRules[0].DstSelector).To(Equal("(pcns.namespace == \"red\") && (has(label1))")) + Expect(outRules[1].DstSelector).To(Equal("(has(projectcalico.org/namespace)) && (has(label2))")) + Expect(outRules[2].DstSelector).To(Equal("(!has(projectcalico.org/namespace)) && (has(label3))")) + }) }) diff --git a/lib/selector/parser/ast.go b/lib/selector/parser/ast.go index f729e28bc..44a739daf 100644 --- a/lib/selector/parser/ast.go +++ b/lib/selector/parser/ast.go @@ -429,3 +429,19 @@ func appendLabelOpAndQuotedString(fragments []string, label, op, s string) []str } return append(fragments, label, op, quote, s, quote) } + +type GlobalNode struct { +} + +func (node *GlobalNode) Evaluate(labels Labels) bool { + + return true +} + +func (node *GlobalNode) AcceptVisitor(v Visitor) { + v.Visit(node) +} + +func (node *GlobalNode) collectFragments(fragments []string) []string { + return append(fragments, "global()") +} diff --git a/lib/selector/parser/parser.go b/lib/selector/parser/parser.go index b8cef5c1a..a1c3afed8 100644 --- a/lib/selector/parser/parser.go +++ b/lib/selector/parser/parser.go @@ -147,6 +147,9 @@ func parseOperation(tokens []tokenizer.Token) (sel node, remTokens []tokenizer.T case tokenizer.TokAll: sel = &AllNode{} remTokens = tokens[1:] + case tokenizer.TokGlobal: + sel = &GlobalNode{} + remTokens = tokens[1:] case tokenizer.TokLabel: // should have an operator and a literal. if len(tokens) < 3 { diff --git a/lib/selector/parser/parser_test.go b/lib/selector/parser/parser_test.go index 83f8973e8..6b8e021ad 100644 --- a/lib/selector/parser/parser_test.go +++ b/lib/selector/parser/parser_test.go @@ -128,6 +128,9 @@ var selectorTests = []selectorTest{ {`all()`, []map[string]string{{}}, []map[string]string{}}, {` all()`, []map[string]string{{}}, []map[string]string{}}, {` all()`, []map[string]string{{"a": "b"}}, []map[string]string{}}, + {`global()`, []map[string]string{{}}, []map[string]string{}}, + {` global()`, []map[string]string{{}}, []map[string]string{}}, + {` global()`, []map[string]string{{"a": "b"}}, []map[string]string{}}, {`a == 'a'`, []map[string]string{}, []map[string]string{{"a": "b"}}}, {`a == 'a'`, []map[string]string{}, []map[string]string{{}}},