Skip to content

Commit 6835b86

Browse files
authored
refactor: [NPM] parsing label selector for general translation logic (#1077)
* Add comments for package and functions * Handle namespaceSelector in simple way and remove unnecessary code for ipBlock field * Translate podSelector in a simple way and update its UTs. * Use parsedSelectors struct to reduce duplicated codes and update comments * Address lint errors and add missing comments * Correct comments * Addressed comments * Use correct settype for all-namespaces * Address comments * Addressed comments
1 parent 76e7531 commit 6835b86

File tree

4 files changed

+313
-326
lines changed

4 files changed

+313
-326
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Package translation converts NetworkPolicy object to policies.NPMNetworkPolicy object
2+
// which contains necessary information to program dataplanes.
3+
// The basic rule of conversion is to start from simple single rule (e.g., allow all traffic, only port, only IPBlock, etc)
4+
// to composite rules (e.g., port with IPBlock or port rule with peers rule (e.g., podSelector, namespaceSelector, or both podSelector and namespaceSelector)).
5+
package translation

npm/pkg/controlplane/translation/parseSelector.go

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package translation
22

33
import (
44
"github.com/Azure/azure-container-networking/log"
5+
"github.com/Azure/azure-container-networking/npm/pkg/dataplane/ipsets"
56
"github.com/Azure/azure-container-networking/npm/util"
67
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
78
)
@@ -45,7 +46,6 @@ func GetOperatorsAndLabels(labelsWithOps []string) (ops, labelsWithoutOps []stri
4546
ops[i] = op
4647
labelsWithoutOps[i] = labelWithoutOp
4748
}
48-
4949
return ops, labelsWithoutOps
5050
}
5151

@@ -59,7 +59,7 @@ func getSetNameForMultiValueSelector(key string, vals []string) string {
5959
return newIPSet
6060
}
6161

62-
// FlattenNameSpaceSelector will help flatten multiple NameSpace selector match Expressions values
62+
// FlattenNameSpaceSelector will help flatten multiple nameSpace selector match Expressions values
6363
// into multiple label selectors helping with the OR condition.
6464
func FlattenNameSpaceSelector(nsSelector *metav1.LabelSelector) []metav1.LabelSelector {
6565
/*
@@ -238,3 +238,148 @@ func parseSelector(selector *metav1.LabelSelector) (labels []string, vals map[st
238238

239239
return labels, vals
240240
}
241+
242+
// labelSelector has parsed matchLabels and MatchExpressions information.
243+
type labelSelector struct {
244+
// include is a flag to indicate whether Op exists or not.
245+
include bool
246+
setType ipsets.SetType
247+
// setName is among
248+
// 1. matchKey + ":" + matchVal (can be empty string) case
249+
// 2. "matchKey" case
250+
// or 3. "matchKey + : + multiple matchVals" case.
251+
setName string
252+
// members slice exists only if setType is only NestedLabelOfPod.
253+
members []string
254+
}
255+
256+
// parsedSelectors maintains slice of unique labelSelector.
257+
type parsedSelectors struct {
258+
labelSelectors []labelSelector
259+
// Use set data structure to avoid the duplicate setName among matchLabels and MatchExpression.
260+
// The key of labelSet includes "!" if operator is "OpNotIn" or "OpDoesNotExist"
261+
// to make difference when it has the same key (and value), but different operator
262+
// while this is weird since it is not always matched, but K8s accepts this spec.
263+
labelSet map[string]struct{}
264+
}
265+
266+
func newParsedSelectors() parsedSelectors {
267+
return parsedSelectors{
268+
labelSelectors: []labelSelector{},
269+
labelSet: map[string]struct{}{},
270+
}
271+
}
272+
273+
// addSelector only adds non-duplicated labelSelector.
274+
// Only nested labels from podSelector has members fields.
275+
func (ps *parsedSelectors) addSelector(include bool, setType ipsets.SetType, setName string, members ...string) {
276+
setNameWithOp := setName
277+
if !include {
278+
setNameWithOp = "!" + setName
279+
}
280+
281+
// in case setNameWithOp exists in a set, do not need to add it.
282+
if _, exist := ps.labelSet[setNameWithOp]; exist {
283+
return
284+
}
285+
286+
ls := labelSelector{
287+
include: include,
288+
setType: setType,
289+
setName: setName,
290+
members: members,
291+
}
292+
293+
ps.labelSelectors = append(ps.labelSelectors, ls)
294+
ps.labelSet[setNameWithOp] = struct{}{}
295+
}
296+
297+
// parseNSSelector parses namespaceSelector and returns slice of labelSelector object
298+
// which includes operator, setType, ipset name and always nil members slice.
299+
// Member slices is always nil since parseNSSelector function is called
300+
// after FlattenNameSpaceSelector function is called, which guarantees
301+
// there is no matchExpression with multiple values.
302+
// TODO: good to remove this dependency later if possible.
303+
func parseNSSelector(selector *metav1.LabelSelector) []labelSelector {
304+
parsedSelectors := newParsedSelectors()
305+
306+
// #1. All namespaces case
307+
if len(selector.MatchLabels) == 0 && len(selector.MatchExpressions) == 0 {
308+
parsedSelectors.addSelector(true, ipsets.KeyLabelOfNamespace, util.KubeAllNamespacesFlag)
309+
return parsedSelectors.labelSelectors
310+
}
311+
312+
// #2. MatchLabels
313+
for matchKey, matchVal := range selector.MatchLabels {
314+
// matchKey + ":" + matchVal (can be empty string) case
315+
setName := util.GetIpSetFromLabelKV(matchKey, matchVal)
316+
parsedSelectors.addSelector(true, ipsets.KeyValueLabelOfNamespace, setName)
317+
}
318+
319+
// #3. MatchExpressions
320+
for _, req := range selector.MatchExpressions {
321+
var setName string
322+
var setType ipsets.SetType
323+
switch op := req.Operator; op {
324+
case metav1.LabelSelectorOpIn, metav1.LabelSelectorOpNotIn:
325+
// "(!) + matchKey + : + matchVal" case
326+
setName = util.GetIpSetFromLabelKV(req.Key, req.Values[0])
327+
setType = ipsets.KeyValueLabelOfNamespace
328+
case metav1.LabelSelectorOpExists, metav1.LabelSelectorOpDoesNotExist:
329+
// "(!) + matchKey" case
330+
setName = req.Key
331+
setType = ipsets.KeyLabelOfNamespace
332+
}
333+
334+
noNegativeOp := (req.Operator == metav1.LabelSelectorOpIn) || (req.Operator == metav1.LabelSelectorOpExists)
335+
parsedSelectors.addSelector(noNegativeOp, setType, setName)
336+
}
337+
338+
return parsedSelectors.labelSelectors
339+
}
340+
341+
// parsePodSelector parses podSelector and returns slice of labelSelector object
342+
// which includes operator, setType, ipset name and its members slice.
343+
// Members slice exists only if setType is only NestedLabelOfPod.
344+
func parsePodSelector(selector *metav1.LabelSelector) []labelSelector {
345+
parsedSelectors := newParsedSelectors()
346+
347+
// #1. MatchLabels
348+
for matchKey, matchVal := range selector.MatchLabels {
349+
// matchKey + ":" + matchVal (can be empty string) case
350+
setName := util.GetIpSetFromLabelKV(matchKey, matchVal)
351+
parsedSelectors.addSelector(true, ipsets.KeyValueLabelOfPod, setName)
352+
}
353+
354+
// #2. MatchExpressions
355+
for _, req := range selector.MatchExpressions {
356+
var setName string
357+
var setType ipsets.SetType
358+
var members []string
359+
switch op := req.Operator; op {
360+
case metav1.LabelSelectorOpIn, metav1.LabelSelectorOpNotIn:
361+
// "(!) + matchKey + : + matchVal" case
362+
if len(req.Values) == 1 {
363+
setName = util.GetIpSetFromLabelKV(req.Key, req.Values[0])
364+
setType = ipsets.KeyValueLabelOfPod
365+
} else {
366+
// "(!) + matchKey + : + multiple matchVals" case
367+
setName = req.Key
368+
for _, val := range req.Values {
369+
setName = util.GetIpSetFromLabelKV(setName, val)
370+
members = append(members, util.GetIpSetFromLabelKV(req.Key, val))
371+
}
372+
setType = ipsets.NestedLabelOfPod
373+
}
374+
case metav1.LabelSelectorOpExists, metav1.LabelSelectorOpDoesNotExist:
375+
// "(!) + matchKey" case
376+
setName = req.Key
377+
setType = ipsets.KeyLabelOfPod
378+
}
379+
380+
noNegativeOp := (req.Operator == metav1.LabelSelectorOpIn) || (req.Operator == metav1.LabelSelectorOpExists)
381+
parsedSelectors.addSelector(noNegativeOp, setType, setName, members...)
382+
}
383+
384+
return parsedSelectors.labelSelectors
385+
}

0 commit comments

Comments
 (0)