Files
flock/pkg/agent/netpol/translator_fuzz_test.go
T

148 lines
4.7 KiB
Go
Raw Normal View History

package netpol
import (
"net"
"strings"
"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"
)
// FuzzTranslate_AndRender stitches the Translator and Renderer together
// against synthetic NetworkPolicies built from fuzzed bytes. We are not
// trying to produce *valid* policies — the goal is to confirm that:
//
// 1. Neither stage panics on weird input.
// 2. Render output is balanced (every "{" has a matching "}").
// 3. Rendering twice is byte-stable.
// 4. The Pods set in Output is consistent with Isolated (every isolated
// PodKey has a matching entry in Pods).
//
// The translator's warn callback is captured to ensure it never panics
// with unexpected message types either.
func FuzzTranslate_AndRender(f *testing.F) {
type seed struct {
policyNS, policyName string
podSelectorKey, podSelValue string
peerSelectorKey, peerSelV string
peerNS, peerName, peerIP string
port uint16
ipBlockCIDR, ipBlockExcept string
}
for _, s := range []seed{
{policyNS: "ns1", policyName: "p1", podSelectorKey: "app", podSelValue: "web", port: 80},
{policyNS: "ns1", policyName: "p1", peerSelectorKey: "app", peerSelV: "client", peerNS: "ns1", peerName: "c1", peerIP: "2001:db8::aa", port: 443},
{policyNS: "ns1", policyName: "p1", ipBlockCIDR: "10.0.0.0/8", ipBlockExcept: "10.99.0.0/16", port: 0},
{policyNS: "", policyName: ""}, // pathological
{policyNS: "ns1", policyName: "p1", podSelectorKey: "app\x00", podSelValue: "web\nnewline"},
{policyNS: "ns1", policyName: "p1", port: 65535},
{policyNS: "ns1", policyName: "p1", port: 1},
} {
f.Add(s.policyNS, s.policyName, s.podSelectorKey, s.podSelValue,
s.peerSelectorKey, s.peerSelV, s.peerNS, s.peerName, s.peerIP,
s.port, s.ipBlockCIDR, s.ipBlockExcept)
}
f.Fuzz(func(t *testing.T,
policyNS, policyName,
podSelectorKey, podSelValue,
peerSelectorKey, peerSelV,
peerNS, peerName, peerIP string,
port uint16,
ipBlockCIDR, ipBlockExcept string,
) {
// Build a synthetic policy.
policy := netv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{Namespace: policyNS, Name: policyName},
Spec: netv1.NetworkPolicySpec{
PolicyTypes: []netv1.PolicyType{netv1.PolicyTypeIngress},
},
}
if podSelectorKey != "" {
policy.Spec.PodSelector = metav1.LabelSelector{
MatchLabels: map[string]string{podSelectorKey: podSelValue},
}
} else {
policy.Spec.PodSelector = metav1.LabelSelector{}
}
ingress := netv1.NetworkPolicyIngressRule{}
if peerSelectorKey != "" {
ingress.From = append(ingress.From, netv1.NetworkPolicyPeer{
PodSelector: &metav1.LabelSelector{
MatchLabels: map[string]string{peerSelectorKey: peerSelV},
},
})
}
if ipBlockCIDR != "" {
peer := netv1.NetworkPolicyPeer{
IPBlock: &netv1.IPBlock{CIDR: ipBlockCIDR},
}
if ipBlockExcept != "" {
peer.IPBlock.Except = []string{ipBlockExcept}
}
ingress.From = append(ingress.From, peer)
}
if port != 0 {
tcp := corev1.ProtocolTCP
p := intstr.FromInt32(int32(port))
ingress.Ports = append(ingress.Ports, netv1.NetworkPolicyPort{
Protocol: &tcp, Port: &p,
})
}
policy.Spec.Ingress = append(policy.Spec.Ingress, ingress)
// Local pod, possibly matching the policy.
pod := Pod{
Namespace: "ns1", Name: "web",
Labels: map[string]string{podSelectorKey: podSelValue, "app": "web"},
HostIface: "flock00000001",
IPs: []net.IP{mustIP("2001:db8::1")},
}
// Peer pod, possibly matching the peer selector.
var peers []PeerPod
if peerName != "" {
peerIPParsed := net.ParseIP(peerIP)
if peerIPParsed != nil {
peers = append(peers, PeerPod{
Namespace: peerNS, Name: peerName,
Labels: map[string]string{peerSelectorKey: peerSelV},
IPs: []net.IP{peerIPParsed},
})
}
}
out, err := Translate(Inputs{
LocalPods: []Pod{pod},
PeerPods: peers,
Namespaces: []Namespace{
{Name: "ns1", Labels: map[string]string{"kubernetes.io/metadata.name": "ns1"}},
},
Policies: []netv1.NetworkPolicy{policy},
}, func(string) {})
if err != nil {
return // any error is acceptable
}
// Property: every isolated PodKey appears in Output.Pods.
for iso := range out.Isolated {
if _, ok := out.Pods[iso.PodKey]; !ok {
t.Fatalf("isolated %s has no Pods entry", iso.PodKey)
}
}
script := Render(out)
// Property: balanced braces.
if got := strings.Count(script, "{") - strings.Count(script, "}"); got != 0 {
t.Fatalf("unbalanced braces (%d):\n%s", got, script)
}
// Property: deterministic (run again, compare).
script2 := Render(out)
if script != script2 {
t.Fatalf("Render not deterministic")
}
})
}