diff --git a/pkg/nsx/services/firewall.go b/pkg/nsx/services/firewall.go index c528c5cf3..143f907ae 100644 --- a/pkg/nsx/services/firewall.go +++ b/pkg/nsx/services/firewall.go @@ -15,12 +15,21 @@ import ( "github.com/vmware/vsphere-automation-sdk-go/runtime/data" "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/domains" "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" logf "sigs.k8s.io/controller-runtime/pkg/log" ) +const ( + MaxGroupExpressions int = 5 + MaxMatchExpressionInOp int = 1 + MaxMatchExpressionIn int = 1 + MaxMatchExpressionInValues int = 5 + MaxMatchExpressionMixed int = 15 +) + type SecurityPolicyService struct { NSXClient *nsx.Client NSXConfig *config.NSXOperatorConfig @@ -125,9 +134,17 @@ func (service *SecurityPolicyService) buildPolicyGroup(obj *v1alpha1.SecurityPol return nil, "ANY", nil } + targetGroupCount := 0 + var err error = nil for i, target := range appliedTo { - service.updateTargetExpressions(obj, &target, &policyGroup, i) + targetGroupCount += service.updateTargetExpressions(obj, &target, &policyGroup, i) } + if targetGroupCount > MaxGroupExpressions { + errorMsg := fmt.Sprintf("Total number of target group expressions:%d exceed NSX limit of %d", targetGroupCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "buildPolicyGroup") + } + log.V(1).Info(fmt.Sprintf("targetGroupCount: %v in buildPolicyGroup", targetGroupCount)) policyGroupPath := service.buildPolicyGroupPath(obj) return &policyGroup, policyGroupPath, nil @@ -201,45 +218,77 @@ func (service *SecurityPolicyService) buildBasicTags(obj *v1alpha1.SecurityPolic return tags } -func (service *SecurityPolicyService) updateTargetExpressions(obj *v1alpha1.SecurityPolicy, target *v1alpha1.SecurityPolicyTarget, group *model.Group, idx int) { +func (service *SecurityPolicyService) updateTargetExpressions(obj *v1alpha1.SecurityPolicy, target *v1alpha1.SecurityPolicyTarget, group *model.Group, idx int) int { service.appendOperatorIfNeeded(&group.Expression, "OR") - expressions := data.NewListValue() - expressionFrame := data.NewStructValue( - "", - map[string]data.DataValue{ - "expressions": expressions, - "resource_type": data.NewStringValue("NestedExpression"), - }, - ) - group.Expression = append(group.Expression, expressionFrame) + expressions := service.buildGroupExpression(&group.Expression) clusterExpression := service.buildExpression( "Condition", "SegmentPort", fmt.Sprintf("%s|%s", util.TagScopeNCPCluster, service.getCluster()), - "Tag", "EQUALS", + "Tag", "EQUALS", "EQUALS", ) expressions.Add(clusterExpression) + var err error = nil + var podSelectorCount, vMSelectorCount = 0, 0 + if target.PodSelector != nil { service.addOperatorIfNeeded(expressions, "AND") // TODO: consider to use project_uid instead of project nsExpression := service.buildExpression( "Condition", "SegmentPort", fmt.Sprintf("%s|%s", util.TagScopeNCPProject, obj.ObjectMeta.Namespace), - "Tag", "EQUALS", + "Tag", "EQUALS", "EQUALS", ) expressions.Add(nsExpression) - service.updatePortExpressions(target.PodSelector.MatchLabels, expressions) + service.updateExpressionsMatchLables(target.PodSelector.MatchLabels, "SegmentPort", expressions) + if len(target.PodSelector.MatchLabels) > 0 { + podSelectorCount = 1 + } + + if target.PodSelector.MatchExpressions != nil { + mergedMatchExpressions := service.mergeSelectorMatchExpression(target.PodSelector.MatchExpressions) + podSelectorCount, err = service.validateSelectorExpressions(target.PodSelector.MatchLabels, *mergedMatchExpressions, "SegmentPort") + if err == nil { + service.updateExpressionsMatchExpression(*mergedMatchExpressions, target.PodSelector.MatchLabels, + &group.Expression, clusterExpression, nsExpression, "SegmentPort", expressions) + } + } } if target.VMSelector != nil { service.addOperatorIfNeeded(expressions, "AND") nsExpression := service.buildExpression( "Condition", "SegmentPort", fmt.Sprintf("%s|%s", util.TagScopeNCPVIFProject, obj.ObjectMeta.Namespace), - "Tag", "EQUALS", + "Tag", "EQUALS", "EQUALS", ) expressions.Add(nsExpression) - service.updatePortExpressions(target.VMSelector.MatchLabels, expressions) + service.updateExpressionsMatchLables(target.VMSelector.MatchLabels, "SegmentPort", expressions) + if len(target.VMSelector.MatchLabels) > 0 { + vMSelectorCount = 1 + } + + if target.VMSelector.MatchExpressions != nil { + mergedMatchExpressions := service.mergeSelectorMatchExpression(target.VMSelector.MatchExpressions) + vMSelectorCount, err = service.validateSelectorExpressions(target.VMSelector.MatchLabels, *mergedMatchExpressions, "SegmentPort") + if err == nil { + service.updateExpressionsMatchExpression(*mergedMatchExpressions, target.VMSelector.MatchLabels, + &group.Expression, clusterExpression, nsExpression, "SegmentPort", expressions) + } + } } + + // Check Selector target group limitation + if podSelectorCount > MaxGroupExpressions { + errorMsg := fmt.Sprintf("Total number of PodSelector target expressions:%d exceed NSX limit of %d", podSelectorCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "updateTargetExpressions") + } + if vMSelectorCount > MaxGroupExpressions { + errorMsg := fmt.Sprintf("Total number of VMSelector target expressions:%d exceed NSX limit of %d", vMSelectorCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "updateTargetExpressions") + } + return (podSelectorCount + vMSelectorCount) } func (service *SecurityPolicyService) appendOperatorIfNeeded(policyExpression *[]*data.StructValue, op string) { @@ -260,17 +309,50 @@ func (service *SecurityPolicyService) buildConjOperator(op string) *data.StructV return operator } -func (service *SecurityPolicyService) buildExpression(resource_type, member_type, value, key, operator string) *data.StructValue { - expression := data.NewStructValue( - "", - map[string]data.DataValue{ - "resource_type": data.NewStringValue(resource_type), - "member_type": data.NewStringValue(member_type), - "value": data.NewStringValue(value), - "key": data.NewStringValue(key), - "operator": data.NewStringValue(operator), - }, - ) +func (service *SecurityPolicyService) buildGroupExpression(policyExpression *[]*data.StructValue) *data.ListValue { + if policyExpression != nil { + expressions := data.NewListValue() + expressionFrame := data.NewStructValue( + "", + map[string]data.DataValue{ + "expressions": expressions, + "resource_type": data.NewStringValue("NestedExpression"), + }, + ) + *policyExpression = append(*policyExpression, expressionFrame) + return expressions + } else { + return nil + } +} + +func (service *SecurityPolicyService) buildExpression(resource_type, member_type, value, key, operator, scope_op string) *data.StructValue { + var expression *data.StructValue + if scope_op == "NOTEQUALS" { + // when scope_op is "NOTEQUALS", the tag operator and value field will not used + expression = data.NewStructValue( + "", + map[string]data.DataValue{ + "resource_type": data.NewStringValue(resource_type), + "member_type": data.NewStringValue(member_type), + "value": data.NewStringValue(value), + "key": data.NewStringValue(key), + "scope_operator": data.NewStringValue(scope_op), + }, + ) + } else { + expression = data.NewStructValue( + "", + map[string]data.DataValue{ + "resource_type": data.NewStringValue(resource_type), + "member_type": data.NewStringValue(member_type), + "value": data.NewStringValue(value), + "key": data.NewStringValue(key), + "operator": data.NewStringValue(operator), + "scope_operator": data.NewStringValue(scope_op), + }, + ) + } return expression } @@ -281,18 +363,200 @@ func (service *SecurityPolicyService) addOperatorIfNeeded(expressions *data.List } } -func (service *SecurityPolicyService) updatePortExpressions(matchLabels map[string]string, expressions *data.ListValue) { +func (service *SecurityPolicyService) updateExpressionsMatchLables(matchLabels map[string]string, memberType string, expressions *data.ListValue) { for k, v := range *util.NormalizeLabels(&matchLabels) { service.addOperatorIfNeeded(expressions, "AND") expression := service.buildExpression( - "Condition", "SegmentPort", + "Condition", memberType, fmt.Sprintf("%s|%s", k, v), - "Tag", "EQUALS", + "Tag", "EQUALS", "EQUALS", ) expressions.Add(expression) } } +// NSX understand the multiple values w.r.t a key in a joined string manner +// this function iterates over input matchExpressions LabelSelectorRequirement +// with same operator and Key, and merges them into one and values to a joined string +// e.g. {'NotIn': [{'k1': ['a1', 'a2', 'a3']}, {'k1': ['a2', 'a3', 'a4']}]} +// => {'NotIn': [{'k1': 'a1, a2, a3, a4'}] +func (service *SecurityPolicyService) mergeSelectorMatchExpression(matchExpressions []metav1.LabelSelectorRequirement) *[]metav1.LabelSelectorRequirement { + mergedMatchExpressions := make([]metav1.LabelSelectorRequirement, 0) + isMerged := make([]bool, len(matchExpressions)) + var meregedSelector metav1.LabelSelectorRequirement + var i, j = 0, 0 + var isFound = false + + for i = 0; i < len(matchExpressions); i++ { + if isMerged[i] { + continue + } + for j = i + 1; j < len(matchExpressions); j++ { + if matchExpressions[i].Operator == matchExpressions[j].Operator && + matchExpressions[i].Key == matchExpressions[j].Key { + isMerged[j] = true + if !isFound { + meregedSelector.Values = util.MergeStr(matchExpressions[i].Values, matchExpressions[j].Values) + meregedSelector.Operator = matchExpressions[i].Operator + meregedSelector.Key = matchExpressions[i].Key + isFound = true + } else { + meregedSelector.Values = util.MergeStr(meregedSelector.Values, matchExpressions[j].Values) + } + } + } + + if !isFound { + mergedMatchExpressions = append(mergedMatchExpressions, matchExpressions[i]) + } else { + mergedMatchExpressions = append(mergedMatchExpressions, meregedSelector) + isFound = false + } + } + return &mergedMatchExpressions +} + +// Todo: check limit for mixed namespaceSelector, podSelector, vmSelector +func (service *SecurityPolicyService) validateSelectorExpressions(matchLabels map[string]string, + matchExpressions []metav1.LabelSelectorRequirement, member_type string) (int, error) { + var exprInCount = len(matchLabels) + var mexprInOpcount = 0 + var mexprInValueCount = 0 + var err error = nil + var errorMsg string = "" + + for _, expr := range matchExpressions { + if expr.Operator == metav1.LabelSelectorOpIn { + mexprInOpcount++ + mexprInValueCount += len(expr.Values) + if mexprInOpcount > MaxMatchExpressionInOp { + errorMsg = fmt.Sprintf("Number of Opeator IN expressions: %d exceed limit of %d", + mexprInOpcount, MaxMatchExpressionIn) + break + + } else if mexprInValueCount > MaxMatchExpressionInValues { + errorMsg = fmt.Sprintf("Values of Opeator IN expressions:%d exceed limit of %d", + mexprInValueCount, MaxMatchExpressionInValues) + break + } + } + } + + if len(errorMsg) != 0 { + err = errors.New(errorMsg) + log.Error(err, "validateSelectorExpressions") + return 0, err + } + + // Check total expressions from LabelSelectors in one group + totalExprCount := exprInCount + len(matchExpressions) + if totalExprCount > MaxGroupExpressions { + errorMsg = fmt.Sprintf("Total number of LabelSelectors nested expressions: %d exceed NSX limit of %d in one Group", + totalExprCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "validateSelectorExpressions") + return 0, err + } + + if mexprInOpcount != 0 { + return mexprInValueCount, nil + } else { + // matchExpressions will be 'AND' with matchLabels(if present) to produce 1 expression. + return 1, nil + } +} + +func (service *SecurityPolicyService) matchExpressionOpInExist(matchExpressions []metav1.LabelSelectorRequirement) (bool, int) { + var opeartorInIndex = -1 + var isFound = false + for i := 0; i < len(matchExpressions); i++ { + // find Opetator IN + if matchExpressions[i].Operator == metav1.LabelSelectorOpIn { + opeartorInIndex = i + isFound = true + break + } + } + return isFound, opeartorInIndex +} + +func (service *SecurityPolicyService) updateExpressionsMatchExpression(matchExpressions []metav1.LabelSelectorRequirement, matchLabels map[string]string, + policyExpression *[]*data.StructValue, clusterExpression *data.StructValue, nsExpression *data.StructValue, + memberType string, expressions *data.ListValue) { + + var found, opInIdx = service.matchExpressionOpInExist(matchExpressions) + if !found { + service.buildExpressionsMatchExpression(matchExpressions, memberType, expressions) + } else { + var expr = matchExpressions[opInIdx] + for i := 0; i < len(expr.Values); i++ { + if i != 0 { + service.appendOperatorIfNeeded(policyExpression, "OR") + expressions = service.buildGroupExpression(policyExpression) + + if clusterExpression != nil { + expressions.Add(clusterExpression) + } + if nsExpression != nil { + if clusterExpression != nil { + service.addOperatorIfNeeded(expressions, "AND") + } + expressions.Add(nsExpression) + } + service.updateExpressionsMatchLables(matchLabels, memberType, expressions) + } + + service.addOperatorIfNeeded(expressions, "AND") + expression := service.buildExpression( + "Condition", memberType, + fmt.Sprintf("%s|%s", expr.Key, expr.Values[i]), + "Tag", "EQUALS", "EQUALS", + ) + expressions.Add(expression) + service.buildExpressionsMatchExpression(matchExpressions, memberType, expressions) + } + } +} + +func (service *SecurityPolicyService) buildExpressionsMatchExpression(matchExpressions []metav1.LabelSelectorRequirement, + memberType string, expressions *data.ListValue) { + for _, expr := range matchExpressions { + switch expr.Operator { + case metav1.LabelSelectorOpIn: + continue + + case metav1.LabelSelectorOpNotIn: + service.addOperatorIfNeeded(expressions, "AND") + joinValues := strings.Join(expr.Values[:], ",") + + expression := service.buildExpression( + "Condition", memberType, + fmt.Sprintf("%s|%s", expr.Key, joinValues), + "Tag", "NOTIN", "EQUALS", + ) + expressions.Add(expression) + + case metav1.LabelSelectorOpExists: + service.addOperatorIfNeeded(expressions, "AND") + expression := service.buildExpression( + "Condition", memberType, + fmt.Sprintf("%s|", expr.Key), + "Tag", "EQUALS", "EQUALS", + ) + expressions.Add(expression) + + case metav1.LabelSelectorOpDoesNotExist: + service.addOperatorIfNeeded(expressions, "AND") + expression := service.buildExpression( + "Condition", memberType, + fmt.Sprintf("%s|", expr.Key), + "Tag", "", "NOTEQUALS", + ) + expressions.Add(expression) + } + } +} + func (service *SecurityPolicyService) buildPolicyGroupID(obj *v1alpha1.SecurityPolicy) string { return fmt.Sprintf("sp_%s_scope", obj.UID) } @@ -409,10 +673,18 @@ func (service *SecurityPolicyService) buildRuleAppliedGroup(obj *v1alpha1.Securi Tags: targetTags, } + ruleGroupCount := 0 + var err error = nil for i, target := range appliedTo { - service.updateTargetExpressions(obj, &target, &ruleAppliedGroup, i) - + ruleGroupCount += service.updateTargetExpressions(obj, &target, &ruleAppliedGroup, i) } + if ruleGroupCount > MaxGroupExpressions { + errorMsg := fmt.Sprintf("Total number of rule group expressions:%d exceed NSX limit of %d", ruleGroupCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "buildRuleAppliedGroup") + } + log.V(1).Info(fmt.Sprintf("ruleGroupCount: %v in buildRuleAppliedGroup", ruleGroupCount)) + return &ruleAppliedGroup, ruleAppliedGroupPath, nil } @@ -432,60 +704,108 @@ func (service *SecurityPolicyService) buildRuleSrcGroup(obj *v1alpha1.SecurityPo DisplayName: &ruleSrcGroupName, Tags: peerTags, } + + ruleSrcGroupCount := 0 + var err error = nil for i, peer := range sources { - service.updatePeerExpressions(obj, &peer, &ruleSrcGroup, i) + ruleSrcGroupCount += service.updatePeerExpressions(obj, &peer, &ruleSrcGroup, i) } + if ruleSrcGroupCount > MaxGroupExpressions { + errorMsg := fmt.Sprintf("Total number of rule source group expressions:%d exceed NSX limit of %d", ruleSrcGroupCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "buildRuleSrcGroup") + } + log.V(1).Info(fmt.Sprintf("ruleSrcGroupCount: %v in buildRuleSrcGroup", ruleSrcGroupCount)) + return &ruleSrcGroup, ruleSrcGroupPath, nil } -func (service *SecurityPolicyService) updatePeerExpressions(obj *v1alpha1.SecurityPolicy, peer *v1alpha1.SecurityPolicyPeer, group *model.Group, idx int) { +func (service *SecurityPolicyService) updatePeerExpressions(obj *v1alpha1.SecurityPolicy, peer *v1alpha1.SecurityPolicyPeer, group *model.Group, idx int) int { service.appendOperatorIfNeeded(&group.Expression, "OR") - expressions := data.NewListValue() - expressionFrame := data.NewStructValue( - "", - map[string]data.DataValue{ - "expressions": expressions, - "resource_type": data.NewStringValue("NestedExpression"), - }, - ) - group.Expression = append(group.Expression, expressionFrame) + expressions := service.buildGroupExpression(&group.Expression) clusterExpression := service.buildExpression( "Condition", "SegmentPort", fmt.Sprintf("%s|%s", util.TagScopeNCPCluster, service.getCluster()), - "Tag", "EQUALS", + "Tag", "EQUALS", "EQUALS", ) expressions.Add(clusterExpression) + var err error = nil + var podSelectorCount, vMSelectorCount, nsSelectorCount = 0, 0, 0 if peer.PodSelector != nil { service.addOperatorIfNeeded(expressions, "AND") podExpression := service.buildExpression( - "Condition", "SegmentPort", util.TagScopeNCPPod, "Tag", "EQUALS") + "Condition", "SegmentPort", util.TagScopeNCPPod, "Tag", "EQUALS", "EQUALS") expressions.Add(podExpression) - service.updatePortExpressions(peer.PodSelector.MatchLabels, expressions) + + service.updateExpressionsMatchLables(peer.PodSelector.MatchLabels, "SegmentPort", expressions) + if len(peer.PodSelector.MatchLabels) > 0 { + podSelectorCount = 1 + } + + if peer.PodSelector.MatchExpressions != nil { + mergedMatchExpressions := service.mergeSelectorMatchExpression(peer.PodSelector.MatchExpressions) + podSelectorCount, err = service.validateSelectorExpressions(peer.PodSelector.MatchLabels, *mergedMatchExpressions, "SegmentPort") + if err == nil { + service.updateExpressionsMatchExpression(*mergedMatchExpressions, peer.PodSelector.MatchLabels, + &group.Expression, clusterExpression, podExpression, "SegmentPort", expressions) + } + } } if peer.VMSelector != nil { service.addOperatorIfNeeded(expressions, "AND") vmExpression := service.buildExpression( - "Condition", "SegmentPort", util.TagScopeNCPVNETInterface, "Tag", "EQUALS") + "Condition", "SegmentPort", util.TagScopeNCPVNETInterface, "Tag", "EQUALS", "EQUALS") expressions.Add(vmExpression) - service.updatePortExpressions(peer.VMSelector.MatchLabels, expressions) + + service.updateExpressionsMatchLables(peer.VMSelector.MatchLabels, "SegmentPort", expressions) + if len(peer.VMSelector.MatchLabels) > 0 { + vMSelectorCount = 1 + } + + if peer.VMSelector.MatchExpressions != nil { + mergedMatchExpressions := service.mergeSelectorMatchExpression(peer.VMSelector.MatchExpressions) + vMSelectorCount, err = service.validateSelectorExpressions(peer.VMSelector.MatchLabels, *mergedMatchExpressions, "SegmentPort") + if err == nil { + service.updateExpressionsMatchExpression(*mergedMatchExpressions, peer.VMSelector.MatchLabels, + &group.Expression, clusterExpression, vmExpression, "SegmentPort", expressions) + } + } } if peer.NamespaceSelector != nil { - service.updateSegmentSelectorExpressions(peer.NamespaceSelector.MatchLabels, expressions) + service.updateExpressionsMatchLables(peer.NamespaceSelector.MatchLabels, "Segment", expressions) + if len(peer.NamespaceSelector.MatchLabels) > 0 { + nsSelectorCount = 1 + } + + if peer.NamespaceSelector.MatchExpressions != nil { + mergedMatchExpressions := service.mergeSelectorMatchExpression(peer.NamespaceSelector.MatchExpressions) + nsSelectorCount, err = service.validateSelectorExpressions(peer.NamespaceSelector.MatchLabels, *mergedMatchExpressions, "Segment") + if err == nil { + service.updateExpressionsMatchExpression(*mergedMatchExpressions, peer.NamespaceSelector.MatchLabels, + &group.Expression, clusterExpression, nil, "Segment", expressions) + } + } } -} -func (service *SecurityPolicyService) updateSegmentSelectorExpressions(matchLabels map[string]string, expressions *data.ListValue) { - for k, v := range *util.NormalizeLabels(&matchLabels) { - service.addOperatorIfNeeded(expressions, "AND") - expression := service.buildExpression( - "Condition", "Segment", - fmt.Sprintf("%s|%s", k, v), - "Tag", "EQUALS", - ) - expressions.Add(expression) + // Check total Selector peer group limitation + if podSelectorCount > MaxGroupExpressions { + errorMsg := fmt.Sprintf("Total number of PodSelector peer expressions:%d exceed NSX limit of %d", podSelectorCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "updatePeerExpressions") + } + if vMSelectorCount > MaxGroupExpressions { + errorMsg := fmt.Sprintf("Total number of VMSelector peer expressions:%d exceed NSX limit of %d", vMSelectorCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "updatePeerExpressions") + } + if nsSelectorCount > MaxGroupExpressions { + errorMsg := fmt.Sprintf("Total number of NamespaceSelector peer expressions:%d exceed NSX limit of %d", nsSelectorCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "updatePeerExpressions") } + return (podSelectorCount + vMSelectorCount + nsSelectorCount) } // TODO: merge buildRuleSrcGroup and buildRuleDstGroup @@ -505,9 +825,19 @@ func (service *SecurityPolicyService) buildRuleDstGroup(obj *v1alpha1.SecurityPo DisplayName: &ruleDstGroupName, Tags: peerTags, } + + ruleDstGroupCount := 0 + var err error = nil for i, peer := range destinations { - service.updatePeerExpressions(obj, &peer, &ruleDstGroup, i) + ruleDstGroupCount += service.updatePeerExpressions(obj, &peer, &ruleDstGroup, i) + } + if ruleDstGroupCount > MaxGroupExpressions { + errorMsg := fmt.Sprintf("Total number of rule destination group expressions:%d exceed NSX limit of %d", ruleDstGroupCount, MaxGroupExpressions) + err = errors.New(errorMsg) + log.Error(err, "buildRuleDstGroup") } + log.V(1).Info(fmt.Sprintf("ruleDstGroupCount: %v in buildRuleDstGroup", ruleDstGroupCount)) + return &ruleDstGroup, ruleDstGroupPath, nil } @@ -560,7 +890,7 @@ func (service *SecurityPolicyService) CreateOrUpdateSecurityPolicy(obj *v1alpha1 if len(nsxSecurityPolicy.Scope) == 0 { // warning - log.Info("SecurityPolicy %s has empty policy-level appliedTo") + log.Info("SecurityPolicy has empty policy-level appliedTo") } indexResults, err := service.GroupStore.ByIndex(util.TagScopeSecurityPolicyCRUID, string(obj.UID)) diff --git a/pkg/util/utils.go b/pkg/util/utils.go index ff5b5f9b3..d32ffef35 100644 --- a/pkg/util/utils.go +++ b/pkg/util/utils.go @@ -87,3 +87,21 @@ func Sha1(data string) string { sum := h.Sum(nil) return fmt.Sprintf("%x", sum) } + +func RemoveDuplicateStr(strSlice []string) []string { + allKeys := make(map[string]bool) + list := []string{} + for _, item := range strSlice { + if _, value := allKeys[item]; !value { + allKeys[item] = true + list = append(list, item) + } + } + return list +} + +func MergeStr(strSlice1 []string, strSlice2 []string) []string { + list := append(strSlice1, strSlice2...) + meregedList := RemoveDuplicateStr(list) + return meregedList +}