Skip to content

Commit 77f021a

Browse files
committed
networkpolicy: introduce annotation to select port(s)
Signed-off-by: akbarkn <akbarkusumanegaralth@gmail.com>
1 parent 9636d72 commit 77f021a

File tree

3 files changed

+482
-6
lines changed

3 files changed

+482
-6
lines changed

pkg/controller/network_policy.go

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package controller
22

33
import (
4+
"errors"
45
"fmt"
56
"reflect"
67
"slices"
@@ -28,6 +29,7 @@ import (
2829
const (
2930
NetworkPolicyEnforcementStandard = "standard"
3031
NetworkPolicyEnforcementLax = "lax"
32+
policyForAnnotation = "ovn.kubernetes.io/policy-for"
3133
)
3234

3335
func (c *Controller) enqueueAddNp(obj any) {
@@ -121,6 +123,11 @@ func (c *Controller) handleUpdateNp(key string) error {
121123
}
122124
logRate := parseACLLogRate(np.Annotations)
123125

126+
providers, includeSvc, err := parsePolicyFor(np)
127+
if err != nil {
128+
return err
129+
}
130+
124131
npName := np.Name
125132
nameArray := []rune(np.Name)
126133
if !unicode.IsLetter(nameArray[0]) {
@@ -142,7 +149,7 @@ func (c *Controller) handleUpdateNp(key string) error {
142149
}
143150

144151
namedPortMap := c.namedPort.GetNamedPortByNs(np.Namespace)
145-
ports, subnetNames, err := c.fetchSelectedPorts(np.Namespace, &np.Spec.PodSelector)
152+
ports, subnetNames, err := c.fetchSelectedPorts(np.Namespace, &np.Spec.PodSelector, providers)
146153
if err != nil {
147154
klog.Errorf("fetch ports belongs to np %s: %v", key, err)
148155
return err
@@ -212,7 +219,7 @@ func (c *Controller) handleUpdateNp(key string) error {
212219
} else {
213220
var allow, except []string
214221
for _, npp := range npr.From {
215-
if allow, except, err = c.fetchPolicySelectedAddresses(np.Namespace, protocol, npp); err != nil {
222+
if allow, except, err = c.fetchPolicySelectedAddresses(np.Namespace, protocol, npp, providers, includeSvc); err != nil {
216223
klog.Errorf("failed to fetch policy selected addresses, %v", err)
217224
return err
218225
}
@@ -359,7 +366,7 @@ func (c *Controller) handleUpdateNp(key string) error {
359366
} else {
360367
var allow, except []string
361368
for _, npp := range npr.To {
362-
if allow, except, err = c.fetchPolicySelectedAddresses(np.Namespace, protocol, npp); err != nil {
369+
if allow, except, err = c.fetchPolicySelectedAddresses(np.Namespace, protocol, npp, providers, includeSvc); err != nil {
363370
klog.Errorf("failed to fetch policy selected addresses, %v", err)
364371
return err
365372
}
@@ -531,7 +538,54 @@ func (c *Controller) handleDeleteNp(key string) error {
531538
return nil
532539
}
533540

534-
func (c *Controller) fetchSelectedPorts(namespace string, selector *metav1.LabelSelector) ([]string, []string, error) {
541+
func parsePolicyFor(np *netv1.NetworkPolicy) (map[string]struct{}, bool, error) {
542+
raw := strings.TrimSpace(np.Annotations[policyForAnnotation])
543+
if raw == "" {
544+
return nil, true, nil
545+
}
546+
547+
tokens := make([]string, 0)
548+
for _, token := range strings.Split(raw, ",") {
549+
t := strings.TrimSpace(token)
550+
if t == "" {
551+
continue
552+
}
553+
tokens = append(tokens, t)
554+
}
555+
556+
providers := map[string]struct{}{}
557+
includeSvc := false
558+
559+
for _, t := range tokens {
560+
switch strings.ToLower(t) {
561+
case "primary":
562+
providers[util.OvnProvider] = struct{}{}
563+
includeSvc = true
564+
continue
565+
case "default":
566+
return nil, false, fmt.Errorf("invalid policy-for entry %q (use 'primary')", t)
567+
case "all":
568+
return nil, false, fmt.Errorf("invalid policy-for entry %q (omit annotation for all)", t)
569+
}
570+
if strings.Contains(t, "/") {
571+
parts := strings.SplitN(t, "/", 2)
572+
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
573+
return nil, false, fmt.Errorf("invalid policy-for entry %q", t)
574+
}
575+
provider := fmt.Sprintf("%s.%s.%s", parts[1], parts[0], util.OvnProvider)
576+
providers[provider] = struct{}{}
577+
continue
578+
}
579+
return nil, false, fmt.Errorf("invalid policy-for entry %q", t)
580+
}
581+
582+
if len(providers) == 0 {
583+
return nil, false, errors.New("policy-for annotation has no valid entries")
584+
}
585+
return providers, includeSvc, nil
586+
}
587+
588+
func (c *Controller) fetchSelectedPorts(namespace string, selector *metav1.LabelSelector, providers map[string]struct{}) ([]string, []string, error) {
535589
var subnets []string
536590
sel, err := metav1.LabelSelectorAsSelector(selector)
537591
if err != nil {
@@ -557,6 +611,12 @@ func (c *Controller) fetchSelectedPorts(namespace string, selector *metav1.Label
557611
if !isOvnSubnet(podNet.Subnet) {
558612
continue
559613
}
614+
provider := podNet.ProviderName
615+
if providers != nil {
616+
if _, ok := providers[provider]; !ok {
617+
continue
618+
}
619+
}
560620

561621
if pod.Annotations[fmt.Sprintf(util.AllocatedAnnotationTemplate, podNet.ProviderName)] == "true" {
562622
ports = append(ports, ovs.PodNameToPortName(podName, pod.Namespace, podNet.ProviderName))
@@ -587,7 +647,7 @@ func hasEgressRule(np *netv1.NetworkPolicy) bool {
587647
return np.Spec.Egress != nil
588648
}
589649

590-
func (c *Controller) fetchPolicySelectedAddresses(namespace, protocol string, npp netv1.NetworkPolicyPeer) ([]string, []string, error) {
650+
func (c *Controller) fetchPolicySelectedAddresses(namespace, protocol string, npp netv1.NetworkPolicyPeer, providers map[string]struct{}, includeSvc bool) ([]string, []string, error) {
591651
selectedAddresses := []string{}
592652
exceptAddresses := []string{}
593653

@@ -644,14 +704,21 @@ func (c *Controller) fetchPolicySelectedAddresses(namespace, protocol string, np
644704
return nil, nil, err
645705
}
646706
for _, podNet := range podNets {
707+
provider := podNet.ProviderName
708+
if providers != nil {
709+
if _, ok := providers[provider]; !ok {
710+
continue
711+
}
712+
}
713+
647714
podIPAnnotation := pod.Annotations[fmt.Sprintf(util.IPAddressAnnotationTemplate, podNet.ProviderName)]
648715
podIPs := strings.SplitSeq(podIPAnnotation, ",")
649716
for podIP := range podIPs {
650717
if podIP != "" && util.CheckProtocol(podIP) == protocol {
651718
selectedAddresses = append(selectedAddresses, podIP)
652719
}
653720
}
654-
if len(svcs) == 0 {
721+
if !includeSvc || len(svcs) == 0 {
655722
continue
656723
}
657724

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package controller
2+
3+
import (
4+
"testing"
5+
6+
netv1 "k8s.io/api/networking/v1"
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/kubeovn/kube-ovn/pkg/util"
12+
)
13+
14+
func TestParsePolicyFor(t *testing.T) {
15+
t.Parallel()
16+
17+
tests := []struct {
18+
name string
19+
annotation *string
20+
wantProviders map[string]struct{}
21+
wantIncludeSvc bool
22+
wantErr bool
23+
}{
24+
{
25+
name: "annotation omitted",
26+
annotation: nil,
27+
wantProviders: nil,
28+
wantIncludeSvc: true,
29+
wantErr: false,
30+
},
31+
{
32+
name: "primary only",
33+
annotation: ptrString("primary"),
34+
wantProviders: map[string]struct{}{
35+
util.OvnProvider: {},
36+
},
37+
wantIncludeSvc: true,
38+
wantErr: false,
39+
},
40+
{
41+
name: "secondary only",
42+
annotation: ptrString("ns1/net1"),
43+
wantProviders: map[string]struct{}{
44+
"net1.ns1." + util.OvnProvider: {},
45+
},
46+
wantIncludeSvc: false,
47+
wantErr: false,
48+
},
49+
{
50+
name: "primary and secondary",
51+
annotation: ptrString(" primary , ns1/net1 "),
52+
wantProviders: map[string]struct{}{
53+
util.OvnProvider: {},
54+
"net1.ns1." + util.OvnProvider: {},
55+
},
56+
wantIncludeSvc: true,
57+
wantErr: false,
58+
},
59+
{
60+
name: "invalid all",
61+
annotation: ptrString("all"),
62+
wantErr: true,
63+
},
64+
{
65+
name: "invalid default",
66+
annotation: ptrString("default"),
67+
wantErr: true,
68+
},
69+
{
70+
name: "invalid no entries",
71+
annotation: ptrString(","),
72+
wantErr: true,
73+
},
74+
{
75+
name: "invalid token",
76+
annotation: ptrString("foo"),
77+
wantErr: true,
78+
},
79+
}
80+
81+
for _, tt := range tests {
82+
t.Run(tt.name, func(t *testing.T) {
83+
np := &netv1.NetworkPolicy{
84+
ObjectMeta: metav1.ObjectMeta{
85+
Name: "np",
86+
Namespace: "default",
87+
},
88+
}
89+
if tt.annotation != nil {
90+
np.Annotations = map[string]string{
91+
policyForAnnotation: *tt.annotation,
92+
}
93+
}
94+
95+
providers, includeSvc, err := parsePolicyFor(np)
96+
if tt.wantErr {
97+
require.Error(t, err)
98+
return
99+
}
100+
101+
require.NoError(t, err)
102+
require.Equal(t, tt.wantIncludeSvc, includeSvc)
103+
if tt.wantProviders == nil {
104+
require.Nil(t, providers)
105+
return
106+
}
107+
require.Equal(t, tt.wantProviders, providers)
108+
})
109+
}
110+
}
111+
112+
func ptrString(s string) *string {
113+
return &s
114+
}

0 commit comments

Comments
 (0)