netpol: NetworkPolicy v1 enforcement via nftables
Build flock Image / build (push) Has been cancelled
Build flock Image / build (push) Has been cancelled
New pkg/agent/netpol implementing standard networking.k8s.io/v1 NetworkPolicy. Pipeline: pods + policies + namespaces → Translate → Render → Apply Supports ingress + egress, all three peer types (podSelector, namespaceSelector, ipBlock with except), numeric ports + port ranges, default-deny semantics derived from PolicyTypes (or inferred from non-empty Spec.Egress when unset). Apply path is `nft -f -` shell-out — single transaction, atomic, kernel guarantees partial-failure rollback. Idempotent dedup via last-applied script. Reconcile triggers: informer events, 30s self-heal tick, every CNI ADD/DEL. Verified against the three live cluster NetPols (calico-apiserver, remote-proxies/lodge-home-assistant, storage/garage-admin-restrict). Fuzz target stitches Translate + Render with random selector and peer inputs; 21 unit tests cover the policy semantics. Named ports skip with a warn — deferred until kubelet exposes them in a form that doesn't require shadowing pod state. Dockerfile: + nftables. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,452 @@
|
||||
package netpol
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
netv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func mustIP(s string) net.IP {
|
||||
ip := net.ParseIP(s)
|
||||
if ip == nil {
|
||||
panic("bad IP: " + s)
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
func newPolicy(ns, name string, mods ...func(*netv1.NetworkPolicy)) netv1.NetworkPolicy {
|
||||
p := netv1.NetworkPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: ns, Name: name},
|
||||
Spec: netv1.NetworkPolicySpec{},
|
||||
}
|
||||
for _, m := range mods {
|
||||
m(&p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func tcpPort(port int) netv1.NetworkPolicyPort {
|
||||
proto := corev1.ProtocolTCP
|
||||
p := intstr.FromInt32(int32(port))
|
||||
return netv1.NetworkPolicyPort{Protocol: &proto, Port: &p}
|
||||
}
|
||||
|
||||
// Pod-only selector that matches everything (`{}`).
|
||||
func emptySelector() *metav1.LabelSelector {
|
||||
return &metav1.LabelSelector{}
|
||||
}
|
||||
|
||||
func selectorMatching(kv map[string]string) *metav1.LabelSelector {
|
||||
return &metav1.LabelSelector{MatchLabels: kv}
|
||||
}
|
||||
|
||||
// Helper: collect Isolated keys for the given pod into a string list.
|
||||
func isolationFor(out Output, podKey string) (in, eg bool) {
|
||||
if _, ok := out.Isolated[Isolation{PodKey: podKey, Direction: DirIngress}]; ok {
|
||||
in = true
|
||||
}
|
||||
if _, ok := out.Isolated[Isolation{PodKey: podKey, Direction: DirEgress}]; ok {
|
||||
eg = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TestTranslate_NoPolicies — pod with no matching policy is unrestricted.
|
||||
func TestTranslate_NoPolicies(t *testing.T) {
|
||||
pod := Pod{
|
||||
Namespace: "ns1", Name: "p1",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
HostIface: "flock00000001",
|
||||
IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
out, err := Translate(Inputs{LocalPods: []Pod{pod}}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(out.Rules) != 0 {
|
||||
t.Fatalf("expected no rules, got %d", len(out.Rules))
|
||||
}
|
||||
in, eg := isolationFor(out, "ns1/p1")
|
||||
if in || eg {
|
||||
t.Fatalf("pod should not be isolated: in=%v eg=%v", in, eg)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_DefaultDeny — a policy with empty Ingress + PolicyTypes
|
||||
// = [Ingress] selects the pod and isolates it; no allow rules emitted.
|
||||
func TestTranslate_DefaultDenyIngress(t *testing.T) {
|
||||
pod := Pod{
|
||||
Namespace: "ns1", Name: "web",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
HostIface: "flock00000001",
|
||||
IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
policy := newPolicy("ns1", "default-deny", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
})
|
||||
out, err := Translate(Inputs{
|
||||
LocalPods: []Pod{pod},
|
||||
Policies: []netv1.NetworkPolicy{policy},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(out.Rules) != 0 {
|
||||
t.Fatalf("expected no rules from a deny-all, got %d", len(out.Rules))
|
||||
}
|
||||
in, eg := isolationFor(out, "ns1/web")
|
||||
if !in {
|
||||
t.Fatalf("ingress should be isolated")
|
||||
}
|
||||
if eg {
|
||||
t.Fatalf("egress should NOT be isolated (policy only set ingress)")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_DefaultDenyEgress_InferredFromEgressList — when
|
||||
// PolicyTypes is omitted but Spec.Egress is non-empty, egress should
|
||||
// also be isolated by inference.
|
||||
func TestTranslate_DefaultDenyEgress_InferredFromEgressList(t *testing.T) {
|
||||
pod := Pod{
|
||||
Namespace: "ns1", Name: "web",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
HostIface: "f1", IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
policy := newPolicy("ns1", "egress-rule", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.Egress = []netv1.NetworkPolicyEgressRule{{}}
|
||||
})
|
||||
out, _ := Translate(Inputs{LocalPods: []Pod{pod}, Policies: []netv1.NetworkPolicy{policy}}, nil)
|
||||
in, eg := isolationFor(out, "ns1/web")
|
||||
if !in || !eg {
|
||||
t.Fatalf("both directions should be isolated: in=%v eg=%v", in, eg)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_PodSelectorPeer_SameNamespace — peer is a single pod in
|
||||
// the same namespace, identified by label.
|
||||
func TestTranslate_PodSelectorPeer(t *testing.T) {
|
||||
web := Pod{
|
||||
Namespace: "ns1", Name: "web",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
HostIface: "f1", IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
clientIP := mustIP("2001:db8::2")
|
||||
peer := PeerPod{
|
||||
Namespace: "ns1", Name: "client",
|
||||
Labels: map[string]string{"app": "client"},
|
||||
IPs: []net.IP{clientIP},
|
||||
}
|
||||
policy := newPolicy("ns1", "allow-from-client", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *selectorMatching(map[string]string{"app": "web"})
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
p.Spec.Ingress = []netv1.NetworkPolicyIngressRule{{
|
||||
From: []netv1.NetworkPolicyPeer{{
|
||||
PodSelector: selectorMatching(map[string]string{"app": "client"}),
|
||||
}},
|
||||
Ports: []netv1.NetworkPolicyPort{tcpPort(80)},
|
||||
}}
|
||||
})
|
||||
|
||||
out, err := Translate(Inputs{
|
||||
LocalPods: []Pod{web},
|
||||
PeerPods: []PeerPod{peer},
|
||||
Policies: []netv1.NetworkPolicy{policy},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(out.Rules) != 1 {
|
||||
t.Fatalf("expected 1 rule, got %d: %+v", len(out.Rules), out.Rules)
|
||||
}
|
||||
r := out.Rules[0]
|
||||
if r.PodKey != "ns1/web" || r.Direction != DirIngress {
|
||||
t.Fatalf("rule has wrong subject: %+v", r)
|
||||
}
|
||||
if len(r.PeerCIDRs) != 1 || !r.PeerCIDRs[0].IP.Equal(clientIP) {
|
||||
t.Fatalf("peer CIDR wrong: %+v", r.PeerCIDRs)
|
||||
}
|
||||
if len(r.Ports) != 1 || r.Ports[0].Protocol != "tcp" || r.Ports[0].Port != 80 {
|
||||
t.Fatalf("port wrong: %+v", r.Ports)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_NamespaceSelector — peer is "every pod in any namespace
|
||||
// with label tier=trusted".
|
||||
func TestTranslate_NamespaceSelector(t *testing.T) {
|
||||
web := Pod{
|
||||
Namespace: "ns1", Name: "web",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
HostIface: "f1", IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
out, err := Translate(Inputs{
|
||||
LocalPods: []Pod{web},
|
||||
Namespaces: []Namespace{
|
||||
{Name: "ns1", Labels: map[string]string{}},
|
||||
{Name: "trusted-1", Labels: map[string]string{"tier": "trusted"}},
|
||||
{Name: "trusted-2", Labels: map[string]string{"tier": "trusted"}},
|
||||
{Name: "untrusted", Labels: map[string]string{"tier": "wild"}},
|
||||
},
|
||||
PeerPods: []PeerPod{
|
||||
{Namespace: "trusted-1", Name: "a", IPs: []net.IP{mustIP("2001:db8::a")}},
|
||||
{Namespace: "trusted-2", Name: "b", IPs: []net.IP{mustIP("2001:db8::b")}},
|
||||
{Namespace: "untrusted", Name: "x", IPs: []net.IP{mustIP("2001:db8::ff")}},
|
||||
},
|
||||
Policies: []netv1.NetworkPolicy{newPolicy("ns1", "allow-trusted", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
p.Spec.Ingress = []netv1.NetworkPolicyIngressRule{{
|
||||
From: []netv1.NetworkPolicyPeer{{
|
||||
NamespaceSelector: selectorMatching(map[string]string{"tier": "trusted"}),
|
||||
}},
|
||||
}}
|
||||
})},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(out.Rules) != 1 {
|
||||
t.Fatalf("expected 1 rule, got %d", len(out.Rules))
|
||||
}
|
||||
got := map[string]bool{}
|
||||
for _, c := range out.Rules[0].PeerCIDRs {
|
||||
got[c.IP.String()] = true
|
||||
}
|
||||
if !got["2001:db8::a"] || !got["2001:db8::b"] {
|
||||
t.Fatalf("trusted pod IPs missing: %v", got)
|
||||
}
|
||||
if got["2001:db8::ff"] {
|
||||
t.Fatalf("untrusted pod IP leaked into rule")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_IPBlockWithExcept — ipBlock with an except range.
|
||||
func TestTranslate_IPBlockWithExcept(t *testing.T) {
|
||||
pod := Pod{
|
||||
Namespace: "ns1", Name: "web", HostIface: "f1",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
IPs: []net.IP{mustIP("10.0.0.1")},
|
||||
}
|
||||
policy := newPolicy("ns1", "ipblock", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
p.Spec.Ingress = []netv1.NetworkPolicyIngressRule{{
|
||||
From: []netv1.NetworkPolicyPeer{{
|
||||
IPBlock: &netv1.IPBlock{
|
||||
CIDR: "10.0.0.0/8",
|
||||
Except: []string{"10.99.0.0/16", "10.42.42.0/24"},
|
||||
},
|
||||
}},
|
||||
}}
|
||||
})
|
||||
out, err := Translate(Inputs{
|
||||
LocalPods: []Pod{pod},
|
||||
Policies: []netv1.NetworkPolicy{policy},
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(out.Rules) != 1 {
|
||||
t.Fatalf("expected 1 rule, got %d", len(out.Rules))
|
||||
}
|
||||
r := out.Rules[0]
|
||||
if len(r.PeerCIDRs) != 1 || r.PeerCIDRs[0].String() != "10.0.0.0/8" {
|
||||
t.Fatalf("peer CIDR wrong: %v", r.PeerCIDRs)
|
||||
}
|
||||
if len(r.PeerExcept) != 2 {
|
||||
t.Fatalf("expected 2 except, got %d", len(r.PeerExcept))
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_AllowAllPeers — empty From list means "from anywhere".
|
||||
func TestTranslate_AllowAllPeers(t *testing.T) {
|
||||
pod := Pod{
|
||||
Namespace: "ns1", Name: "web", HostIface: "f1",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
policy := newPolicy("ns1", "allow-all-on-port", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
p.Spec.Ingress = []netv1.NetworkPolicyIngressRule{{
|
||||
Ports: []netv1.NetworkPolicyPort{tcpPort(443)},
|
||||
}}
|
||||
})
|
||||
out, _ := Translate(Inputs{LocalPods: []Pod{pod}, Policies: []netv1.NetworkPolicy{policy}}, nil)
|
||||
if len(out.Rules) != 1 {
|
||||
t.Fatalf("expected 1 rule, got %d", len(out.Rules))
|
||||
}
|
||||
r := out.Rules[0]
|
||||
if len(r.PeerCIDRs) != 0 || len(r.PeerExcept) != 0 {
|
||||
t.Fatalf("expected allow-all peers, got CIDRs=%v Except=%v", r.PeerCIDRs, r.PeerExcept)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_AllowAllPorts — empty Ports list means "all ports".
|
||||
func TestTranslate_AllowAllPorts(t *testing.T) {
|
||||
pod := Pod{
|
||||
Namespace: "ns1", Name: "web", HostIface: "f1",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
policy := newPolicy("ns1", "allow-from-all", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
p.Spec.Ingress = []netv1.NetworkPolicyIngressRule{{
|
||||
From: []netv1.NetworkPolicyPeer{{
|
||||
PodSelector: emptySelector(),
|
||||
}},
|
||||
}}
|
||||
})
|
||||
peer := PeerPod{
|
||||
Namespace: "ns1", Name: "x",
|
||||
IPs: []net.IP{mustIP("2001:db8::aa")},
|
||||
}
|
||||
out, _ := Translate(Inputs{
|
||||
LocalPods: []Pod{pod}, PeerPods: []PeerPod{peer},
|
||||
Policies: []netv1.NetworkPolicy{policy},
|
||||
}, nil)
|
||||
if len(out.Rules) != 1 {
|
||||
t.Fatalf("expected 1 rule, got %d", len(out.Rules))
|
||||
}
|
||||
r := out.Rules[0]
|
||||
if len(r.Ports) != 1 || r.Ports[0] != (PortMatch{}) {
|
||||
t.Fatalf("expected single any-port match, got %+v", r.Ports)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_PortRange — endPort field.
|
||||
func TestTranslate_PortRange(t *testing.T) {
|
||||
pod := Pod{
|
||||
Namespace: "ns1", Name: "web", HostIface: "f1",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
policy := newPolicy("ns1", "range", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
proto := corev1.ProtocolTCP
|
||||
port := intstr.FromInt32(8000)
|
||||
end := int32(8999)
|
||||
p.Spec.Ingress = []netv1.NetworkPolicyIngressRule{{
|
||||
Ports: []netv1.NetworkPolicyPort{{Protocol: &proto, Port: &port, EndPort: &end}},
|
||||
}}
|
||||
})
|
||||
out, _ := Translate(Inputs{LocalPods: []Pod{pod}, Policies: []netv1.NetworkPolicy{policy}}, nil)
|
||||
if len(out.Rules) != 1 || out.Rules[0].Ports[0].Port != 8000 || out.Rules[0].Ports[0].EndPort != 8999 {
|
||||
t.Fatalf("range not preserved: %+v", out.Rules)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_NamedPortRejected — named ports aren't supported yet;
|
||||
// translator must skip the rule and warn.
|
||||
func TestTranslate_NamedPortRejected(t *testing.T) {
|
||||
pod := Pod{
|
||||
Namespace: "ns1", Name: "web", HostIface: "f1",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
proto := corev1.ProtocolTCP
|
||||
named := intstr.FromString("http")
|
||||
policy := newPolicy("ns1", "named", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
p.Spec.Ingress = []netv1.NetworkPolicyIngressRule{{
|
||||
Ports: []netv1.NetworkPolicyPort{{Protocol: &proto, Port: &named}},
|
||||
}}
|
||||
})
|
||||
var warns []string
|
||||
out, _ := Translate(Inputs{LocalPods: []Pod{pod}, Policies: []netv1.NetworkPolicy{policy}}, func(s string) {
|
||||
warns = append(warns, s)
|
||||
})
|
||||
if len(out.Rules) != 0 {
|
||||
t.Fatalf("expected named-port rule to be skipped")
|
||||
}
|
||||
if len(warns) == 0 {
|
||||
t.Fatalf("expected a warning about named ports")
|
||||
}
|
||||
// The pod should still be isolated since the policy selected it.
|
||||
in, _ := isolationFor(out, "ns1/web")
|
||||
if !in {
|
||||
t.Fatalf("pod should be isolated even when its rule is dropped")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_PolicyOnlyAppliesToOwnNamespace — a policy in nsA does
|
||||
// NOT select pods in nsB even if their labels match.
|
||||
func TestTranslate_PolicyScopedToNamespace(t *testing.T) {
|
||||
a := Pod{Namespace: "nsA", Name: "p", HostIface: "f1",
|
||||
Labels: map[string]string{"app": "web"}, IPs: []net.IP{mustIP("2001:db8::1")}}
|
||||
b := Pod{Namespace: "nsB", Name: "p", HostIface: "f2",
|
||||
Labels: map[string]string{"app": "web"}, IPs: []net.IP{mustIP("2001:db8::2")}}
|
||||
policy := newPolicy("nsA", "deny", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *selectorMatching(map[string]string{"app": "web"})
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
})
|
||||
out, _ := Translate(Inputs{LocalPods: []Pod{a, b}, Policies: []netv1.NetworkPolicy{policy}}, nil)
|
||||
inA, _ := isolationFor(out, "nsA/p")
|
||||
inB, _ := isolationFor(out, "nsB/p")
|
||||
if !inA {
|
||||
t.Fatalf("nsA/p should be isolated")
|
||||
}
|
||||
if inB {
|
||||
t.Fatalf("nsB/p must NOT be isolated by a policy in nsA")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_PodWithoutAllocationSkipped — pod with no IPs is silently
|
||||
// skipped (its rule could not match any traffic anyway).
|
||||
func TestTranslate_PodWithoutAllocationSkipped(t *testing.T) {
|
||||
pod := Pod{Namespace: "ns1", Name: "p", HostIface: "f1",
|
||||
Labels: map[string]string{"app": "web"}}
|
||||
policy := newPolicy("ns1", "deny", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
})
|
||||
out, _ := Translate(Inputs{LocalPods: []Pod{pod}, Policies: []netv1.NetworkPolicy{policy}}, nil)
|
||||
in, _ := isolationFor(out, "ns1/p")
|
||||
if in {
|
||||
t.Fatalf("pod without IP should not appear in output")
|
||||
}
|
||||
}
|
||||
|
||||
// TestTranslate_Determinism — translating the same Inputs twice produces
|
||||
// equal outputs (Rules in equal order, Isolated equal).
|
||||
func TestTranslate_Determinism(t *testing.T) {
|
||||
pod := Pod{
|
||||
Namespace: "ns1", Name: "web", HostIface: "f1",
|
||||
Labels: map[string]string{"app": "web"},
|
||||
IPs: []net.IP{mustIP("2001:db8::1")},
|
||||
}
|
||||
peers := []PeerPod{
|
||||
{Namespace: "ns1", Name: "z", Labels: map[string]string{"app": "client"}, IPs: []net.IP{mustIP("2001:db8::2")}},
|
||||
{Namespace: "ns1", Name: "a", Labels: map[string]string{"app": "client"}, IPs: []net.IP{mustIP("2001:db8::3")}},
|
||||
}
|
||||
policies := []netv1.NetworkPolicy{
|
||||
newPolicy("ns1", "z-second", func(p *netv1.NetworkPolicy) {
|
||||
p.Spec.PodSelector = *emptySelector()
|
||||
p.Spec.PolicyTypes = []netv1.PolicyType{netv1.PolicyTypeIngress}
|
||||
p.Spec.Ingress = []netv1.NetworkPolicyIngressRule{{
|
||||
From: []netv1.NetworkPolicyPeer{{
|
||||
PodSelector: selectorMatching(map[string]string{"app": "client"}),
|
||||
}},
|
||||
}}
|
||||
}),
|
||||
}
|
||||
in := Inputs{LocalPods: []Pod{pod}, PeerPods: peers, Policies: policies}
|
||||
a, _ := Translate(in, nil)
|
||||
b, _ := Translate(in, nil)
|
||||
if len(a.Rules) != len(b.Rules) {
|
||||
t.Fatalf("rule count differs: %d vs %d", len(a.Rules), len(b.Rules))
|
||||
}
|
||||
for i := range a.Rules {
|
||||
if a.Rules[i].PodKey != b.Rules[i].PodKey || len(a.Rules[i].PeerCIDRs) != len(b.Rules[i].PeerCIDRs) {
|
||||
t.Fatalf("rule[%d] differs", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user