453 lines
15 KiB
Go
453 lines
15 KiB
Go
|
|
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)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|