From 5f40c22210ce03ef38ec8fa7bfb4557c1449fae0 Mon Sep 17 00:00:00 2001 From: lut777 Date: Tue, 15 Nov 2022 21:56:09 +0800 Subject: [PATCH] fix: add unitest (#2030) --- pkg/util/address_family_test.go | 61 ++ pkg/util/k8s_test.go | 76 +++ pkg/util/net_test.go | 963 +++++++++++++++++++++++++++- pkg/util/network_attachment_test.go | 425 ++++++++++++ pkg/util/patch_test.go | 8 +- pkg/util/slice_test.go | 182 ++++++ pkg/util/validator_test.go | 820 +++++++++++++++++++++++ pkg/util/version_test.go | 37 ++ pkg/util/vlan_test.go | 28 + 9 files changed, 2565 insertions(+), 35 deletions(-) create mode 100644 pkg/util/address_family_test.go create mode 100644 pkg/util/k8s_test.go create mode 100644 pkg/util/network_attachment_test.go create mode 100644 pkg/util/slice_test.go create mode 100644 pkg/util/validator_test.go create mode 100644 pkg/util/version_test.go create mode 100644 pkg/util/vlan_test.go diff --git a/pkg/util/address_family_test.go b/pkg/util/address_family_test.go new file mode 100644 index 00000000000..ec9088c7827 --- /dev/null +++ b/pkg/util/address_family_test.go @@ -0,0 +1,61 @@ +package util + +import ( + kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" + "golang.org/x/sys/unix" + "strings" + "testing" +) + +func TestProtocolToFamily(t *testing.T) { + tests := []struct { + name string + prot string + want int + err string + }{ + { + name: "correct", + prot: kubeovnv1.ProtocolIPv4, + want: unix.AF_INET, + err: "", + }, + { + name: "v6", + prot: kubeovnv1.ProtocolIPv6, + want: unix.AF_INET6, + err: "", + }, + { + name: "dual", + prot: kubeovnv1.ProtocolDual, + want: unix.AF_UNSPEC, + err: "", + }, + { + name: "err", + prot: "damn", + want: -1, + err: "invalid protocol", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans, err := ProtocolToFamily(c.prot) + if ans != c.want || !ErrorContains(err, c.err) { + t.Errorf("%v expected %v, %v, but %v, %v got", + c.prot, c.want, c.err, ans, err) + } + }) + } +} + +func ErrorContains(out error, want string) bool { + if out == nil { + return want == "" + } + if want == "" { + return false + } + return strings.Contains(out.Error(), want) +} diff --git a/pkg/util/k8s_test.go b/pkg/util/k8s_test.go new file mode 100644 index 00000000000..247465779c5 --- /dev/null +++ b/pkg/util/k8s_test.go @@ -0,0 +1,76 @@ +package util + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "testing" +) + +func TestGetNodeInternalIP(t *testing.T) { + tests := []struct { + name string + node v1.Node + exp4 string + exp6 string + }{ + { + name: "correct", + node: v1.Node{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: v1.NodeSpec{}, + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + { + Type: "InternalIP", + Address: "192.168.0.2", + }, + { + Type: "ExternalIP", + Address: "192.188.0.4", + }, + { + Type: "InternalIP", + Address: "ffff:ffff:ffff:ffff:ffff::23", + }, + }, + }, + }, + exp4: "192.168.0.2", + exp6: "ffff:ffff:ffff:ffff:ffff::23", + }, + { + name: "correctWithDiff", + node: v1.Node{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{}, + Spec: v1.NodeSpec{}, + Status: v1.NodeStatus{ + Addresses: []v1.NodeAddress{ + { + Type: "InternalIP", + Address: "ffff:ffff:ffff:ffff:ffff::23", + }, + { + Type: "ExternalIP", + Address: "192.188.0.4", + }, + { + Type: "InternalIP", + Address: "192.188.0.43", + }, + }, + }, + }, + exp4: "192.188.0.43", + exp6: "ffff:ffff:ffff:ffff:ffff::23", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if ret4, ret6 := GetNodeInternalIP(tt.node); ret4 != tt.exp4 || ret6 != tt.exp6 { + t.Errorf("got %v, %v, want %v, %v", ret4, ret6, tt.exp4, tt.exp6) + } + }) + } +} diff --git a/pkg/util/net_test.go b/pkg/util/net_test.go index b1e8d45bc9d..c45cc4b45f3 100644 --- a/pkg/util/net_test.go +++ b/pkg/util/net_test.go @@ -1,45 +1,45 @@ package util import ( - "errors" + kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" + "math/big" + "net" + "reflect" "testing" ) -func TestCheckCIDRsAll(t *testing.T) { +func TestCheckSystemCIDR(t *testing.T) { cases := []struct { name string config []string - expect error + expect string }{ // for all check - {"1v4", []string{"10.16.0.0/16"}, nil}, - {"v4", []string{"10.16.0.0/16", "10.96.0.0/12", "100.64.0.0/16"}, nil}, - {"dual", []string{"10.16.0.0/16,fd00:10:16::/64", "10.96.0.0/12,fd00:10:96::/112", "100.64.0.0/16,fd00:100:64::/64"}, nil}, - {"v6", []string{"fd00:10:16::/64", "fd00:10:96::/112", "fd00:100:64::/64"}, nil}, - {"169254", []string{"10.16.0.0/16", "10.96.0.0/12", "169.254.0.0/16"}, errors.New("")}, - {"0000", []string{"10.16.0.0/16", "10.96.0.0/12", "0.0.0.0/16"}, errors.New("")}, - {"127", []string{"10.16.0.0/16", "10.96.0.0/12", "127.127.0.0/16"}, errors.New("")}, - {"255", []string{"10.16.0.0/16", "10.96.0.0/12", "255.255.0.0/16"}, errors.New("")}, - {"ff80", []string{"10.16.0.0/16,ff80::/64", "10.96.0.0/12,fd00:10:96::/112", "100.64.0.0/16,fd00:100:64::/64"}, errors.New("")}, + {"1v4", []string{"10.16.0.0/16"}, ""}, + {"v4", []string{"10.16.0.0/16", "10.96.0.0/12", "100.64.0.0/16"}, ""}, + {"dual", []string{"10.16.0.0/16,fd00:10:16::/64", "10.96.0.0/12,fd00:10:96::/112", "100.64.0.0/16,fd00:100:64::/64"}, ""}, + {"v6", []string{"fd00:10:16::/64", "fd00:10:96::/112", "fd00:100:64::/64"}, ""}, + {"169254", []string{"10.16.0.0/16", "10.96.0.0/12", "169.254.0.0/16"}, "169.254.0.0/16 conflict with v4 link local cidr 169.254.0.0/16"}, + {"0000", []string{"10.16.0.0/16", "10.96.0.0/12", "0.0.0.0/16"}, "0.0.0.0/16 conflict with v4 localnet cidr 0.0.0.0/32"}, + {"127", []string{"10.16.0.0/16", "10.96.0.0/12", "127.127.0.0/16"}, "127.127.0.0/16 conflict with v4 loopback cidr 127.0.0.1/8"}, + {"255", []string{"10.16.0.0/16", "10.96.0.0/12", "255.255.0.0/16"}, "255.255.0.0/16 conflict with v4 broadcast cidr 255.255.255.255/32"}, + {"ff80", []string{"10.16.0.0/16,ff80::/64", "10.96.0.0/12,fd00:10:96::/112", "100.64.0.0/16,fd00:100:64::/64"}, "10.16.0.0/16,ff80::/64 conflict with v6 multicast cidr ff00::/8"}, // overlap only - {"overlapped", []string{"10.16.0.0/16", "10.96.0.0/12", "10.96.0.2/16"}, errors.New("")}, + {"overlapped", []string{"10.16.0.0/16", "10.96.0.0/12", "10.96.0.2/16"}, "conflict with cidr"}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { ans := CheckSystemCIDR(c.config) - if c.expect == nil && ans != c.expect { - t.Fatalf("%v expected %v, but %v got", + if !ErrorContains(ans, c.expect) { + t.Errorf("%v expected error %v, but %v got", c.config, c.expect, ans) - } else if c.expect != nil && ans == nil { - t.Fatalf("%v expected error, but %v got", - c.config, ans) } }) } } -func TestCheckSupCIDROverlap(t *testing.T) { +func TestCIDROverlap(t *testing.T) { cases := []struct { name string subnet1 string @@ -53,42 +53,943 @@ func TestCheckSupCIDROverlap(t *testing.T) { {"overlap", "10.96.0.0/12", "10.96.0.2/16", true}, // overlap only, spec ignore {"spec", "10.16.0.0/16", "169.254.0.0/12", false}, + {"allerr", "10.16.0/16", "169.254.0/12", false}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { ans := CIDROverlap(c.subnet1, c.subnet2) if ans != c.expect { - t.Fatalf("%v and %v expected %v, but %v got", + t.Errorf("%v and %v expected %v, but %v got", c.subnet1, c.subnet2, c.expect, ans) } }) } } -func TestCheckCIDRSpec(t *testing.T) { +func TestCIDRGlobalUnicast(t *testing.T) { cases := []struct { name string subnet string - expect error + expect string }{ // for all check - {"1v4", "10.16.0.0/16", nil}, - {"dual", "10.16.0.0/16,fd00:10:16::/64", nil}, - {"v6", "fd00:10:16::/64", nil}, - {"169254", "169.254.0.0/16", errors.New("")}, + {"1v4", "10.16.0.0/16", ""}, + {"dual", "10.16.0.0/16,fd00:10:16::/64", ""}, + {"v6", "fd00:10:16::/64", ""}, + {"169254", "169.254.0.0/16", "169.254.0.0/16 conflict with v4 link local cidr 169.254.0.0/16"}, + {"v4mulcast", "224.0.0.0/16", "224.0.0.0/16 conflict with v4 multicast cidr 224.0.0.0/4"}, + {"v6unspeci", "::/128", "::/128 conflict with v6 unspecified cidr ::/128"}, + {"v6loopbak", "::1/128", "::1/128 conflict with v6 loopback cidr ::1/128"}, + {"v6linklocal", "FE80::/10", "FE80::/10 conflict with v6 link local cidr FE80::/10"}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { ans := CIDRGlobalUnicast(c.subnet) - if c.expect == nil && ans != c.expect { - t.Fatalf("%v expected %v, but %v got", - c.subnet, c.expect, ans) - } else if c.expect != nil && ans == nil { - t.Fatalf("%v expected error, but %v got", + if !ErrorContains(ans, c.expect) { + t.Errorf("%v expected error, but %v got", c.subnet, ans) } }) } } + +func TestGenerateMac(t *testing.T) { + tests := []struct { + name string + want bool + }{ + { + name: "correct", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := GenerateMac() + if _, err := net.ParseMAC(ans); err != nil { + t.Errorf("%v expected %v, but %v got", + c.name, c.want, ans) + } + }) + } +} + +func TestIp2BigInt(t *testing.T) { + tests := []struct { + expect *big.Int + ip string + name string + }{ + { + name: "correctv4", + ip: "192.168.1.1", + expect: big.NewInt(0).SetBytes(net.ParseIP("192.168.1.1").To4()), + }, + { + name: "v6", + ip: "1050:0:0:0:5:600:300c:326b", + expect: big.NewInt(0).SetBytes(net.ParseIP("1050:0:0:0:5:600:300c:326b").To16()), + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + if ans := Ip2BigInt(c.ip); !reflect.DeepEqual(ans, c.expect) { + t.Errorf("%v expected %v, but %v got", + c.ip, c.expect, ans) + } + }) + } +} + +func TestBigInt2Ip(t *testing.T) { + tests := []struct { + name string + ip *big.Int + expect string + }{ + { + name: "v4", + ip: big.NewInt(0).SetBytes(net.ParseIP("192.168.1.1").To4()), + expect: "192.168.1.1", + }, + { + name: "v6", + expect: "1050::5:600:300c:326b", + ip: big.NewInt(0).SetBytes(net.ParseIP("1050:0:0:0:5:600:300c:326b").To16()), + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + if ans := BigInt2Ip(c.ip); !reflect.DeepEqual(ans, c.expect) { + t.Errorf("%v expected %v, but %v got", + c.ip, c.expect, ans) + } + }) + } +} + +func TestSubnetNumber(t *testing.T) { + tests := []struct { + name string + expect string + subnet string + }{ + { + name: "correct", + subnet: "192.168.0.1/24", + expect: "192.168.0.0", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + if ans := SubnetNumber(c.subnet); !reflect.DeepEqual(ans, c.expect) { + t.Errorf("%v expected %v, but %v got", + c.subnet, c.expect, ans) + } + }) + } +} + +func TestSubnetBroadcast(t *testing.T) { + tests := []struct { + name string + subnet string + expect string + }{ + { + name: "v4", + subnet: "192.128.23.1/15", + expect: "192.129.255.255", + }, + { + name: "v6", + subnet: "ffff:ffff:ffff:ffff:ffff:0:ffff:ffff/96", + expect: "ffff:ffff:ffff:ffff:ffff:0:ffff:ffff", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + if ans := SubnetBroadcast(c.subnet); !reflect.DeepEqual(ans, c.expect) { + t.Errorf("%v expected %v, but %v got", + c.subnet, c.expect, ans) + } + }) + } +} + +func TestFirstIP(t *testing.T) { + tests := []struct { + name string + subnet string + expect string + err string + }{ + { + name: "base", + subnet: "192.168.0.23/24", + expect: "192.168.0.1", + err: "", + }, + { + name: "controversy", + subnet: "192.168.0.23/32", + expect: "192.168.0.24", + err: "", + }, + { + name: "subneterr", + subnet: "192.168.0.0", + expect: "", + err: "192.168.0.0 is not a valid cidr", + }, + { + name: "v6", + subnet: "ffff:ffff:ffff:ffff:ffff:0:ffff:0/96", + expect: "ffff:ffff:ffff:ffff:ffff::1", + err: "", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans, err := FirstIP(c.subnet) + if !reflect.DeepEqual(ans, c.expect) || !ErrorContains(err, c.err) { + t.Errorf("%v expected %v, %v, but %v, %v got", + c.subnet, c.expect, c.err, ans, err) + } + }) + } +} + +func TestLastIP(t *testing.T) { + tests := []struct { + name string + subnet string + expect string + err string + }{ + { + name: "base", + subnet: "192.168.0.23/24", + expect: "192.168.0.254", + err: "", + }, + { + name: "subneterr", + subnet: "192.168.0.0", + expect: "", + err: "192.168.0.0 is not a valid cidr", + }, + { + name: "v6", + subnet: "ffff:ffff:ffff:ffff:ffff:0:ffff:0/96", + expect: "ffff:ffff:ffff:ffff:ffff:0:ffff:fffe", + err: "", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans, err := LastIP(c.subnet) + if !reflect.DeepEqual(ans, c.expect) || !ErrorContains(err, c.err) { + t.Errorf("%v expected %v, %v, but %v, %v got", + c.subnet, c.expect, c.err, ans, err) + } + }) + } +} + +// unstable function +func TestCIDRContainIP(t *testing.T) { + tests := []struct { + name string + cidrStr string + ipStr string + want bool + }{ + { + name: "base", + cidrStr: "192.168.0.23/24", + ipStr: "192.168.0.23,192.168.0.254", + want: true, + }, + { + name: "baseNoMask", + cidrStr: "192.168.0.23", + ipStr: "192.168.0.23,192.168.0.254", + want: false, + }, + { + name: "v6", + cidrStr: "ffff:ffff:ffff:ffff:ffff:0:ffff:0/96", + ipStr: "ffff:ffff:ffff:ffff:ffff:0:ffff:fffe", + want: true, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + if ans := CIDRContainIP(c.cidrStr, c.ipStr); ans != c.want { + t.Errorf("%v, %v expected %v, but %v got", + c.cidrStr, c.ipStr, c.want, ans) + } + }) + } +} + +func TestCheckProtocol(t *testing.T) { + tests := []struct { + name string + address string + want string + }{ + { + name: "v4", + address: "192.168.0.23", + want: kubeovnv1.ProtocolIPv4, + }, + { + name: "v6", + address: "ffff:ffff:ffff:ffff:ffff:0:ffff:fffe", + want: kubeovnv1.ProtocolIPv6, + }, + { + name: "dual", + address: "192.168.0.23,ffff:ffff:ffff:ffff:ffff:0:ffff:fffe", + want: kubeovnv1.ProtocolDual, + }, + { + name: "error", + address: "192.168.0.23,192.168.0.24", + want: "", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + if ans := CheckProtocol(c.address); ans != c.want { + t.Errorf("%v expected %v, but %v got", + c.address, c.want, ans) + } + }) + } +} + +func TestAddressCount(t *testing.T) { + tests := []struct { + name string + network *net.IPNet + want float64 + }{ + { + name: "base", + network: &net.IPNet{ + IP: net.ParseIP("192.168.1.0"), + Mask: net.IPMask{255, 255, 255, 0}, + }, + want: 254, + }, + { + name: "baseNoIP", + network: &net.IPNet{ + IP: net.ParseIP("192.168.1.0"), + Mask: net.IPMask{255, 255, 255, 254}, + }, + want: 0, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + if ans := AddressCount(c.network); ans != c.want { + t.Errorf("%v expected %v, but %v got", + c.network, c.want, ans) + } + }) + } +} + +func TestGenerateRandomV4IP(t *testing.T) { + tests := []struct { + name string + cidr string + want string + }{ + { + name: "base", + cidr: "10.16.0.0/16", + want: "10.16.0.0/16", + }, + { + name: "wrongcidr", + cidr: "10.16.0.0", + want: "", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + _, IPNets, err := net.ParseCIDR(c.cidr) + if err != nil { + ans := GenerateRandomV4IP(c.cidr) + if c.want != ans { + t.Errorf("%v expected %v, but %v got", + c.cidr, c.want, ans) + } + + } else { + ans := GenerateRandomV4IP(c.cidr) + if IPNets.Contains(net.ParseIP(GenerateRandomV4IP(c.cidr))) { + t.Errorf("%v expected %v, but %v got", + c.cidr, c.want, ans) + } + } + }) + } +} + +func TestIPToString(t *testing.T) { + tests := []struct { + name string + ip string + want string + }{ + { + name: "cidr", + ip: "10.16.0.1/16", + want: "10.16.0.1", + }, + { + name: "ip", + ip: "10.16.0.1", + want: "10.16.0.1", + }, + { + name: "error", + ip: "10.16.0", + want: "", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + if ans := IPToString(c.ip); ans != c.want { + t.Errorf("%v expected %v, but %v got", + c.ip, c.want, ans) + } + }) + } +} + +func TestIsValidIP(t *testing.T) { + tests := []struct { + name string + ip string + want bool + }{ + { + name: "base", + ip: "10.16.0.1", + want: true, + }, + { + name: "v6", + ip: "::ffff:192.0.2.1", + want: true, + }, + { + name: "err", + ip: "10.16.1", + want: false, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + if ans := IsValidIP(c.ip); ans != c.want { + t.Errorf("%v expected %v, but %v got", + c.ip, c.want, ans) + } + }) + } +} + +func TestCheckCidrs(t *testing.T) { + tests := []struct { + name string + cidr string + want string + }{ + { + name: "v4", + cidr: "10.16.0.1/24", + want: "", + }, + { + name: "v6", + cidr: "::ffff:192.0.2.1/96", + want: "", + }, + { + name: "Morev6", + cidr: "10.16.0.1/24,::ffff:192.0.2.1/96", + want: "", + }, + { + name: "err", + cidr: "10.16.1", + want: "CIDRInvalid", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := CheckCidrs(c.cidr) + if !ErrorContains(ans, c.want) { + t.Errorf("%v expected %v, but %v got", + c.cidr, c.want, ans) + } + }) + } +} + +func TestGetGwByCidr(t *testing.T) { + tests := []struct { + name string + cidr string + want string + err string + }{ + { + name: "v4", + cidr: "10.16.0.112/24", + want: "10.16.0.1", + err: "", + }, + { + name: "dual", + cidr: "10.16.0.112/24,ffff:ffff:ffff:ffff:ffff:0:ffff:0/96", + want: "10.16.0.1,ffff:ffff:ffff:ffff:ffff::1", + err: "", + }, + { + name: "err", + cidr: "10.16.112/24", + want: "", + err: "10.16.112/24 is not a valid cidr", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans, err := GetGwByCidr(c.cidr) + if !ErrorContains(err, c.err) || c.want != ans { + t.Errorf("%v expected %v, %v, but %v, %v got", + c.cidr, c.want, c.err, ans, err) + } + }) + } +} + +func TestAppendGwByCidr(t *testing.T) { + tests := []struct { + name string + gw string + cidr string + want string + err string + }{ + { + name: "correct", + gw: "10.16.0.1", + cidr: "ffff:ffff:ffff:ffff:ffff:0:ffff:0/96", + want: "10.16.0.1,ffff:ffff:ffff:ffff:ffff::1", + err: "", + }, + { + name: "versa", + gw: "ffff:ffff:ffff:ffff:ffff::1", + cidr: "10.16.0.112/24", + want: "10.16.0.1,ffff:ffff:ffff:ffff:ffff::1", + err: "", + }, + { + name: "dual", + gw: "10.16.0.1", + cidr: "10.16.0.112/24,ffff:ffff:ffff:ffff:ffff:0:ffff:0/96", + want: "10.16.0.1,ffff:ffff:ffff:ffff:ffff::1", + err: "", + }, + { + name: "err", + gw: "ffff:ffff:ffff:ffff:ffff::1", + cidr: "10.16.112/24", + want: "", + err: "10.16.112/24 is not a valid cidr", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans, err := AppendGwByCidr(c.gw, c.cidr) + if !ErrorContains(err, c.err) || c.want != ans { + t.Errorf("%v expected %v, %v, but %v, %v got", + c.cidr, c.want, c.err, ans, err) + } + }) + } +} + +func TestSplitIpsByProtocol(t *testing.T) { + tests := []struct { + name string + excl []string + want4 []string + want6 []string + }{ + { + name: "v4", + excl: []string{"10.66.0.1..10.66.0.10", "10.66.0.101..10.66.0.151"}, + want4: []string{"10.66.0.1", "10.66.0.10", "10.66.0.101", "10.66.0.151"}, + want6: []string{}, + }, + { + name: "v6", + excl: []string{"10.66.0.1..10.66.0.10", "10.66.0.101..10.66.0.151", "ffff:ffff:ffff:ffff:ffff::2"}, + want4: []string{"10.66.0.1", "10.66.0.10", "10.66.0.101", "10.66.0.151"}, + want6: []string{"ffff:ffff:ffff:ffff:ffff::2"}, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans4, ans6 := SplitIpsByProtocol(c.excl) + if reflect.DeepEqual(ans4, c.want4) && reflect.DeepEqual(ans6, c.want6) { + t.Errorf("%v expected %v, %v, but %v, %v got", + c.excl, c.want4, c.want6, ans4, ans6) + } + }) + } +} + +func TestGetStringIP(t *testing.T) { + tests := []struct { + name string + v4 string + v6 string + want string + }{ + { + name: "base", + v4: "10.16.0.1", + v6: "ffff:ffff:ffff:ffff:ffff::1", + want: "10.16.0.1,ffff:ffff:ffff:ffff:ffff::1", + }, + { + name: "err", + v4: "10.16.1", + v6: "ffff:ffff:ffff:ffff:ffff::1", + want: "ffff:ffff:ffff:ffff:ffff::1", + }, + { + name: "v4Okv6Er", + v4: "10.16.0.1", + v6: ":ffff:ffff:ffff:ffff::1", + want: "10.16.0.1", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := GetStringIP(c.v4, c.v6) + if c.want != ans { + t.Errorf("%v, %v expected %v, but %v got", + c.v4, c.v6, c.want, ans) + } + }) + } +} + +func TestGetIpAddrWithMask(t *testing.T) { + tests := []struct { + name string + ip string + cidr string + want string + }{ + { + name: "base", + ip: "10.16.0.23", + cidr: "10.16.0.0/24", + want: "10.16.0.23/24", + }, + { + name: "v6", + ip: "ffff:ffff:ffff:ffff:ffff::23", + cidr: "ffff:ffff:ffff:ffff:ffff:0:ffff:0/96", + want: "ffff:ffff:ffff:ffff:ffff::23/96", + }, + { + name: "dual", + ip: "10.16.0.23,ffff:ffff:ffff:ffff:ffff::23", + cidr: "10.16.0.0/24,ffff:ffff:ffff:ffff:ffff:0:ffff:0/96", + want: "10.16.0.23/24,ffff:ffff:ffff:ffff:ffff::23/96", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := GetIpAddrWithMask(c.ip, c.cidr) + if c.want != ans { + t.Errorf("%v, %v expected %v, but %v got", + c.ip, c.cidr, c.want, ans) + } + }) + } +} + +func TestGetIpWithoutMask(t *testing.T) { + tests := []struct { + name string + cidr string + want string + }{ + { + name: "v4", + cidr: "10.16.0.23/24", + want: "10.16.0.23", + }, + { + name: "dual", + cidr: "10.16.0.23/24,ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + want: "10.16.0.23,ffff:ffff:ffff:ffff:ffff:0:ffff:23", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := GetIpWithoutMask(c.cidr) + if c.want != ans { + t.Errorf("%v expected %v, but %v got", + c.cidr, c.want, ans) + } + }) + } +} + +func TestSplitStringIP(t *testing.T) { + tests := []struct { + name string + cidr string + wv4 string + wv6 string + }{ + { + name: "v4", + cidr: "10.16.0.23/24", + wv4: "10.16.0.23/24", + wv6: "", + }, + { + name: "dual", + cidr: "10.16.0.23/24,ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + wv4: "10.16.0.23/24", + wv6: "ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + }, + { + name: "V6", + cidr: "ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + wv4: "", + wv6: "ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + }, + { + name: "err", + cidr: "10.16.23/24,ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + wv4: "", + wv6: "", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ansv4, ansv6 := SplitStringIP(c.cidr) + if c.wv4 != ansv4 || c.wv6 != ansv6 { + t.Errorf("%v expected %v, %v but %v, %v got", + c.cidr, c.wv4, c.wv6, ansv4, ansv6) + } + }) + } +} + +func TestExpandExcludeIPs(t *testing.T) { + tests := []struct { + name string + cidr string + excl []string + want []string + }{ + { + name: "base", + cidr: "10.16.0.23/24", + excl: []string{"10.16.0.0..10.16.0.20", "10.16.0.23", "10.16.0.255"}, + want: []string{"10.16.0.1..10.16.0.20", "10.16.0.23"}, + }, + { + name: "exIPOutCIDR", + cidr: "10.16.0.23/24", + excl: []string{"10.16.0.0..10.16.0.20,10.16.0.23"}, + want: []string{}, + }, + { + name: "exIPFormatErr", + cidr: "10.16.0.23/24,ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + excl: []string{"10.16.0.20..10.16.0.1", "10.16.0.23", "ffff:ffff:ffff:ffff:ffff:0:ffff:23..ffff:ffff:ffff:ffff:ffff:0:ffff:25"}, + want: []string{"10.16.0.23", "ffff:ffff:ffff:ffff:ffff:0:ffff:23..ffff:ffff:ffff:ffff:ffff:0:ffff:25"}, + }, + { + name: "CIDRErr", + cidr: "10.16.0.254/32,10.16.0.1/33,10.16.0.23/24,ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + excl: []string{"10.16.0.1..10.16.0.10", "10.16.0.23", "ffff:ffff:ffff:ffff:ffff:0:ffff:23..ffff:ffff:ffff:ffff:ffff:0:ffff:25"}, + want: []string{"10.16.0.1..10.16.0.10", "10.16.0.23", "ffff:ffff:ffff:ffff:ffff:0:ffff:23..ffff:ffff:ffff:ffff:ffff:0:ffff:25"}, + }, + { + name: "exIPOverflow", + cidr: "10.16.0.23/24,ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + excl: []string{"10.16.0.1..10.16.1.10", "10.16.0.23", "ffff:ffff:ffff:ffff:ffff:0:ffff:23..ffff:ffff:ffff:ffff:ffff:0:ffff:25"}, + want: []string{"10.16.0.1..10.16.0.254", "10.16.0.23", "ffff:ffff:ffff:ffff:ffff:0:ffff:23..ffff:ffff:ffff:ffff:ffff:0:ffff:25"}, + }, + { + name: "exIPformatErr", + cidr: "10.16.0.23/24,ffff:ffff:ffff:ffff:ffff:0:ffff:23/96", + excl: []string{"10.16.0.10..10.16.0.10", "10.16.0.23", "ffff:ffff:ffff:ffff:ffff:0:ffff:23..ffff:ffff:ffff:ffff:ffff:0:ffff:25"}, + want: []string{"10.16.0.10", "10.16.0.23", "ffff:ffff:ffff:ffff:ffff:0:ffff:23..ffff:ffff:ffff:ffff:ffff:0:ffff:25"}, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := ExpandExcludeIPs(c.excl, c.cidr) + if !reflect.DeepEqual(ans, c.want) { + t.Errorf("%v expected %v but %v got", + c.cidr, c.want, ans) + } + }) + } +} + +func TestContainsIPs(t *testing.T) { + tests := []struct { + name string + excl string + ip string + want bool + }{ + { + name: "base", + excl: "10.16.0.0..10.16.0.20", + ip: "10.16.0.10", + want: true, + }, + { + name: "diffFormat", + excl: "10.16.0.10", + ip: "10.16.0.10", + want: true, + }, + { + name: "notOverlap", + excl: "10.16.0.0..10.16.0.20", + ip: "10.16.0.30", + want: false, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := ContainsIPs(c.excl, c.ip) + if ans != c.want { + t.Errorf("%v %v expected %v but %v got", + c.excl, c.ip, c.want, ans) + } + }) + } +} + +func TestCountIpNums(t *testing.T) { + tests := []struct { + name string + excl []string + want float64 + }{ + { + name: "base", + excl: []string{"10.16.0.0..10.16.0.20", "10.16.0.30"}, + want: 22, + }, + { + name: "only1", + excl: []string{"10.16.0.255"}, + want: 1, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := CountIpNums(c.excl) + if ans != c.want { + t.Errorf("%v expected %v but %v got", + c.excl, c.want, ans) + } + }) + } +} + +func TestGatewayContains(t *testing.T) { + tests := []struct { + name string + gatewayNodeStr string + gateway string + want bool + }{ + { + name: "base", + gatewayNodeStr: "kube-ovn-worker:172.18.0.2, kube-ovn-control-plane:172.18.0.3", + gateway: "kube-ovn-worker", + want: true, + }, + { + name: "err", + gatewayNodeStr: "kube-ovn-worker:172.18.0.2, kube-ovn-control-plane:172.18.0.3", + gateway: "kube-ovn-worker1", + want: false, + }, + { + name: "formatDiff", + gatewayNodeStr: "kube-ovn-worker, kube-ovn-control-plane:172.18.0.3", + gateway: "kube-ovn-worker", + want: true, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := GatewayContains(c.gatewayNodeStr, c.gateway) + if ans != c.want { + t.Errorf("%v, %v expected %v but %v got", + c.gatewayNodeStr, c.gateway, c.want, ans) + } + }) + } +} + +func TestJoinHostPort(t *testing.T) { + tests := []struct { + name string + host string + port int32 + want string + }{ + { + name: "v4", + host: "10.16.0.23", + port: 80, + want: "10.16.0.23:80", + }, + { + name: "v6", + host: "ffff:ffff:ffff:ffff:ffff:0:ffff:23", + port: 80, + want: "[ffff:ffff:ffff:ffff:ffff:0:ffff:23]:80", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ans := JoinHostPort(c.host, c.port) + if ans != c.want { + t.Errorf("%v, %v expected %v but %v got", + c.host, c.port, c.want, ans) + } + }) + } +} diff --git a/pkg/util/network_attachment_test.go b/pkg/util/network_attachment_test.go new file mode 100644 index 00000000000..c644ab039bf --- /dev/null +++ b/pkg/util/network_attachment_test.go @@ -0,0 +1,425 @@ +package util + +import ( + "encoding/json" + cnitypes "github.com/containernetworking/cni/pkg/types" + types2 "github.com/containernetworking/cni/pkg/types" + "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types" + "reflect" + "testing" +) + +func TestParsePodNetworkObjectName(t *testing.T) { + tests := []struct { + name string + podNetwork string + netNsName string + networkName string + netIfName string + err string + }{ + { + name: "controversy", + podNetwork: "", + netNsName: "", + networkName: "", + netIfName: "", + err: "", + }, + { + name: "base", + podNetwork: "kube-system/lb-svc-attachment", + netNsName: "kube-system", + networkName: "lb-svc-attachment", + netIfName: "", + err: "", + }, + { + name: "baseWithoutNS", + podNetwork: "lb-svc-attachment", + netNsName: "", + networkName: "lb-svc-attachment", + netIfName: "", + err: "", + }, + { + name: "baseWithIF", + podNetwork: "kube-system/lb-svc-attachment@eth0", + netNsName: "kube-system", + networkName: "lb-svc-attachment", + netIfName: "eth0", + err: "", + }, + { + name: "errFormat", + podNetwork: "kube-system/lb-svc-attachment/1", + netNsName: "", + networkName: "lb-svc-attachment", + netIfName: "", + err: "Invalid network object", + }, + { + name: "errFormatNS", + podNetwork: "mellanox.com/cx5_sriov_switchdev", + netNsName: "", + networkName: "lb-svc-attachment", + netIfName: "", + err: "one or more items did not match comma-delimited format", + }, + { + name: "errFormatIF", + podNetwork: "kube-system/lb-svc-attachment@eth0@1", + netNsName: "kube-system", + networkName: "lb-svc-attachment", + netIfName: "eth0", + err: "Invalid network object", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + netNsName, networkName, netIfName, err := parsePodNetworkObjectName(c.podNetwork) + if !ErrorContains(err, c.err) && (netNsName != c.netNsName || networkName != c.networkName || netIfName != c.netIfName) { + t.Errorf("%v expected %v %v %v and err %v, but %v %v %v and err %v got", + c.podNetwork, c.netNsName, c.networkName, c.netIfName, c.err, netNsName, networkName, netIfName, err) + } + }) + } +} + +func TestParsePodNetworkAnnotation(t *testing.T) { + + correctJson0, _ := json.Marshal([]types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + MacRequest: "00:0c:29:9a:96:74", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.50.6"}, + }, + }) + correctJson0IP, _ := json.Marshal([]types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + MacRequest: "00:0c:29:9a:96:74", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.50.6/20"}, + }, + }) + errJson0, _ := json.Marshal(types.NetworkSelectionElement{ + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + MacRequest: "00:0c:29:9a:96:74", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.50.6"}, + }) + errJsonMac, _ := json.Marshal([]types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + MacRequest: "123", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.50.6"}, + }, + }) + errJson0IP1, _ := json.Marshal([]types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + MacRequest: "00:0c:29:9a:96:74", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.6"}, + }, + }) + errJson0IP2, _ := json.Marshal([]types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + MacRequest: "00:0c:29:9a:96:74", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.6/20"}, + }, + }) + correctJson0IfReq, _ := json.Marshal([]types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "", + MacRequest: "00:0c:29:9a:96:74", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.50.6"}, + }, + }) + + tests := []struct { + name string + podNetworks string + defaultNamespace string + exp []*types.NetworkSelectionElement + err string + }{ + { + name: "base", + podNetworks: "kube-system/lb-svc-attachment@eth0", + defaultNamespace: "kube-system", + exp: []*types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + }, + }, + err: "", + }, + { + name: "baseWithIF", + podNetworks: "kube-system/lb-svc-attachment", + defaultNamespace: "kube-system", + exp: []*types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "", + }, + }, + err: "", + }, + { + name: "baseWithoutNS", + podNetworks: "lb-svc-attachment", + defaultNamespace: "kube-system", + exp: []*types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "", + }, + }, + err: "", + }, + { + name: "baseWithoutNS", + podNetworks: "lb-svc-attachment", + defaultNamespace: "kube-system", + exp: []*types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "", + }, + }, + err: "", + }, + { + name: "baseWithIFandNS", + podNetworks: "kube-system/lb-svc-attachment@eth0", + defaultNamespace: "kube-system", + exp: []*types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + }, + }, + err: "", + }, + { + name: "correctJson", + podNetworks: string(correctJson0), + defaultNamespace: "kube-system", + exp: []*types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + MacRequest: "00:0c:29:9a:96:74", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.50.6"}, + }, + }, + err: "", + }, + { + name: "correctJsonIP", + podNetworks: string(correctJson0IP), + defaultNamespace: "kube-system", + exp: []*types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + MacRequest: "00:0c:29:9a:96:74", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.50.6/20"}, + }, + }, + err: "", + }, + { + name: "correctJsonIfReq", + podNetworks: string(correctJson0IfReq), + defaultNamespace: "kube-system", + exp: []*types.NetworkSelectionElement{ + { + Name: "lb-svc-attachment", + Namespace: "kube-system", + InterfaceRequest: "eth0", + MacRequest: "00:0c:29:9a:96:74", + DeprecatedInterfaceRequest: "eth0", + IPRequest: []string{"192.168.50.6"}, + }, + }, + err: "", + }, + { + name: "errJson", + podNetworks: string(errJson0), + defaultNamespace: "kube-system", + exp: nil, + err: "json: cannot unmarshal object into Go value", + }, + { + name: "errJsonMac", + podNetworks: string(errJsonMac), + defaultNamespace: "kube-system", + exp: nil, + err: "invalid MAC address", + }, + { + name: "errJsonIP1", + podNetworks: string(errJson0IP1), + defaultNamespace: "kube-system", + exp: nil, + err: "failed to parse IP address", + }, + { + name: "errJsonIP2", + podNetworks: string(errJson0IP2), + defaultNamespace: "kube-system", + exp: nil, + err: "invalid CIDR address", + }, + { + name: "errFormat", + podNetworks: "kube-system/lb-svc-attachment@eth0@1", + defaultNamespace: "kube-system", + exp: nil, + err: "Invalid network object", + }, + { + name: "errNull", + podNetworks: "", + defaultNamespace: "kube-system", + exp: nil, + err: "", + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + ele, err := ParsePodNetworkAnnotation(c.podNetworks, c.defaultNamespace) + if !ErrorContains(err, c.err) || !reflect.DeepEqual(ele, c.exp) { + t.Errorf("%v, %v expected %v and err %v, but %v and err %v got", + c.podNetworks, c.defaultNamespace, c.exp[0], c.err, ele[0], err) + } + }) + } +} + +func TestIsOvnNetwork(t *testing.T) { + tests := []struct { + name string + netCfg *types.DelegateNetConf + expt bool + }{ + { + name: "base", + netCfg: &types.DelegateNetConf{ + Conf: types2.NetConf{ + Type: CniTypeName, + }, + }, + expt: true, + }, + { + name: "basewithPlugins", + netCfg: &types.DelegateNetConf{ + ConfList: cnitypes.NetConfList{ + Plugins: []*cnitypes.NetConf{ + {Type: CniTypeName}, + }, + }, + }, + expt: true, + }, + { + name: "baseWithErr", + netCfg: &types.DelegateNetConf{ + Conf: types2.NetConf{ + Type: "err", + }, + }, + expt: false, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + rslt := IsOvnNetwork(c.netCfg) + if rslt != c.expt { + t.Errorf("%v expected %v, but %v got", + c.netCfg, c.expt, rslt) + } + }) + } +} + +func TestIsDefaultNet(t *testing.T) { + tests := []struct { + name string + defaultNetAnnotation string + attach *types.NetworkSelectionElement + expt bool + }{ + { + name: "base", + defaultNetAnnotation: "nm", + attach: &types.NetworkSelectionElement{ + Name: "nm", + Namespace: "ns", + }, + expt: true, + }, + { + name: "baseWithNS", + defaultNetAnnotation: "ns/nm", + attach: &types.NetworkSelectionElement{ + Name: "nm", + Namespace: "ns", + }, + expt: true, + }, + { + name: "errFormat", + defaultNetAnnotation: "err", + attach: &types.NetworkSelectionElement{ + Name: "nm", + Namespace: "ns", + }, + expt: false, + }, + } + for _, c := range tests { + t.Run(c.name, func(t *testing.T) { + rslt := IsDefaultNet(c.defaultNetAnnotation, c.attach) + if rslt != c.expt { + t.Errorf("%v %v expected %v, but %v got", + c.defaultNetAnnotation, c.attach, c.expt, rslt) + } + }) + } +} diff --git a/pkg/util/patch_test.go b/pkg/util/patch_test.go index 23121700ce0..7c4e2a0b56b 100644 --- a/pkg/util/patch_test.go +++ b/pkg/util/patch_test.go @@ -27,7 +27,7 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { wantErr bool }{ { - name: "1", + name: "base", args: args{ original: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, @@ -37,7 +37,7 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { wantErr: false, }, { - name: "2", + name: "baseWithRemote", args: args{ original: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"ovn1": "1", "ovn2": "2"}}}, @@ -47,7 +47,7 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { wantErr: false, }, { - name: "3", + name: "baseWithoutAll", args: args{ original: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, @@ -57,7 +57,7 @@ func TestGenerateStrategicMergePatchPayload(t *testing.T) { wantErr: false, }, { - name: "4", + name: "baseWithoutModified", args: args{ original: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"calico1": "1"}}}, modified: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}}, diff --git a/pkg/util/slice_test.go b/pkg/util/slice_test.go new file mode 100644 index 00000000000..d3e99d5b021 --- /dev/null +++ b/pkg/util/slice_test.go @@ -0,0 +1,182 @@ +package util + +import "testing" +import "reflect" + +func TestDiffStringSlice(t *testing.T) { + tests := []struct { + name string + slice1 []string + slice2 []string + want []string + }{ + { + name: "base", + slice1: []string{"a", "b", "c"}, + slice2: []string{"a", "b", "f"}, + want: []string{"c", "f"}, + }, + { + name: "baseWithBlank", + slice1: []string{"a ", " b", " c "}, + slice2: []string{"a", " b", "f"}, + want: []string{"a ", " c ", "a", "f"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if ret := DiffStringSlice(tt.slice1, tt.slice2); !reflect.DeepEqual(ret, tt.want) { + t.Errorf("got %v, want %v", ret, tt.want) + } + }) + } +} + +func TestUniqString(t *testing.T) { + tests := []struct { + name string + slice1 []string + want []string + }{ + { + name: "base", + slice1: []string{"a", "b", "c", "d", "a", "b", "c"}, + want: []string{"a", "b", "c", "d"}, + }, + { + name: "baseWithBlank", + slice1: []string{" a", "b", "c", "d", "a", "b", "c"}, + want: []string{" a", "b", "c", "d", "a"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if ret := UniqString(tt.slice1); !reflect.DeepEqual(ret, tt.want) { + t.Errorf("got %v, want %v", ret, tt.want) + } + }) + } +} + +func TestIsStringsOverlap(t *testing.T) { + tests := []struct { + name string + a []string + b []string + want bool + }{ + { + name: "base", + a: []string{"a", "b", "c"}, + b: []string{"a", "e", "f"}, + want: true, + }, + { + name: "baseWithBlank", + a: []string{"a", "b", "c"}, + b: []string{" a", "e", "f"}, + want: false, + }, + { + name: "baseWithDiffString", + a: []string{"a", "b", "c"}, + b: []string{"d", "e", "f"}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if ret := IsStringsOverlap(tt.a, tt.b); ret != tt.want { + t.Errorf("got %v, want %v", ret, tt.want) + } + }) + } +} + +func TestIsStringIn(t *testing.T) { + tests := []struct { + name string + a string + b []string + want bool + }{ + { + name: "base", + a: "a", + b: []string{"a", "b"}, + want: true, + }, + { + name: "baseWithDiff", + a: "c", + b: []string{"a", "b"}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if ret := IsStringIn(tt.a, tt.b); ret != tt.want { + t.Errorf("got %v, want %v", ret, tt.want) + } + }) + } +} + +func TestContainsString(t *testing.T) { + tests := []struct { + name string + a string + b []string + want bool + }{ + { + name: "base", + a: "a", + b: []string{"a", "b"}, + want: true, + }, + { + name: "baseWithDiff", + a: "c", + b: []string{"a", "b"}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if ret := ContainsString(tt.b, tt.a); ret != tt.want { + t.Errorf("got %v, want %v", ret, tt.want) + } + }) + } +} + +func TestRemoveString(t *testing.T) { + tests := []struct { + name string + a string + b []string + want []string + }{ + { + name: "base", + a: "a", + b: []string{"a", "b", "c"}, + want: []string{"b", "c"}, + }, + { + name: "baseWithDiff", + a: "c", + b: []string{"a", "b"}, + want: []string{"a", "b"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if ret := RemoveString(tt.b, tt.a); !reflect.DeepEqual(ret, tt.want) { + t.Errorf("got %v, want %v", ret, tt.want) + } + }) + } +} diff --git a/pkg/util/validator_test.go b/pkg/util/validator_test.go new file mode 100644 index 00000000000..348aad93c9f --- /dev/null +++ b/pkg/util/validator_test.go @@ -0,0 +1,820 @@ +package util + +import ( + kubeovnv1 "github.com/kubeovn/kube-ovn/pkg/apis/kubeovn/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "os" + "testing" +) + +func TestValidateSubnet(t *testing.T) { + + os.Setenv("KUBERNETES_SERVICE_HOST", "10.20.0.1") + tests := []struct { + name string + asubnet kubeovnv1.Subnet + err string + }{ + { + name: "correct", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest", + GenerateName: "", + Namespace: "", + SelfLink: "", + UID: "", + ResourceVersion: "", + Generation: 0, + CreationTimestamp: metav1.Time{}, + DeletionTimestamp: nil, + DeletionGracePeriodSeconds: nil, + Labels: nil, + Annotations: nil, + OwnerReferences: nil, + Finalizers: nil, + ZZZ_DeprecatedClusterName: "", + ManagedFields: nil, + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + GatewayNode: "", + NatOutgoing: false, + U2oRouting: false, + ExternalEgressGateway: "", + PolicyRoutingPriority: 0, + PolicyRoutingTableID: 0, + Private: false, + AllowSubnets: nil, + Vlan: "", + HtbQos: "", + Vips: nil, + LogicalGateway: false, + DisableGatewayCheck: false, + DisableInterConnection: false, + EnableDHCP: false, + DHCPv4Options: "", + DHCPv6Options: "", + EnableIPv6RA: false, + IPv6RAConfigs: "", + Acls: nil, + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "", + }, + { + name: "gatewayErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-gatewayerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.17.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "gateway 10.17.0.1 is not in cidr 10.16.0.0/16", + }, + { + name: "CIDRUnicastErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-unicasterr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "127.0.0.1/8", + Gateway: "127.0.0.1", + ExcludeIps: []string{"127.0.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "127.0.0.1/8 conflict with v4 loopback cidr 127.0.0.1/8", + }, + { + name: "CIDRNotIPErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-cidryerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "127.0.1/8", + Gateway: "127.0.0.1", + ExcludeIps: []string{"127.0.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "gateway 127.0.0.1 is not in cidr 127.0.1/8", + }, + { + name: "CIDRNotIPErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-cidrerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "127.0.1/8", + Gateway: "127.0.0.1", + ExcludeIps: []string{"127.0.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "gateway 127.0.0.1 is not in cidr 127.0.1/8", + }, + { + name: "ExcludeIPFormatErr1", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-excludeiperr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1..10.16.0.10..10.16.0.12"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "in excludeIps is not a valid ip range", + }, + { + name: "ExcludeIPFormatErr2", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-excludeiperr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1.."}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "exclude_ips is not a valid address", + }, + { + name: "ExcludeIPNotIPErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-excludeiperr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.1..10.16.10"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "in exclude_ips is not a valid address", + }, + { + name: "ExcludeIPRangeErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-excludecidrerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.2..10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "10.16.0.2..10.16.0.1 in excludeIps is not a valid ip range", + }, + { + name: "AllowCIDRErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-allowcidrerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1..10.16.0.10"}, + Provider: "ovn", + GatewayType: "distributed", + Private: true, + AllowSubnets: []string{"10.18.0/16"}, + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "10.18.0/16 in allowSubnets is not a valid address", + }, + { + name: "gatewaytypeErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-gatewaytypeerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1..10.16.0.10"}, + Provider: "ovn", + GatewayType: "damn", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "damn is not a valid gateway type", + }, + { + name: "apiserverSVCErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-apisvcerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.20.0.0/16", + Gateway: "10.20.0.1", + ExcludeIps: []string{"10.20.0.1..10.20.0.10"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "subnet utest-apisvcerr cidr 10.20.0.0/16 conflicts with k8s apiserver svc ip 10.20.0.1", + }, + { + name: "ExgressGWErr1", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-exgatewayerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1..10.16.0.10"}, + Provider: "ovn", + GatewayType: "distributed", + ExternalEgressGateway: "192.178.2.1", + NatOutgoing: true, + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "conflict configuration: natOutgoing and externalEgressGateway", + }, + { + name: "ExgressGWErr2", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-exgatewayerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.2..10.16.0.10"}, + Provider: "ovn", + GatewayType: "distributed", + ExternalEgressGateway: "192.178.2.1,192.178.2.2,192.178.2.3", + NatOutgoing: false, + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "invalid external egress gateway configuration", + }, + { + name: "ExgressGWErr3", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-exgatewayerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.2..10.16.0.10"}, + Provider: "ovn", + GatewayType: "distributed", + ExternalEgressGateway: "192.178.2.1,192.178..2", + NatOutgoing: false, + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "IP 192.178..2 in externalEgressGateway is not a valid address", + }, + { + name: "ExgressGWErr4", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-exgatewayerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + ExternalEgressGateway: "192.178.2.1,fd00:10:16::1", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "invalid external egress gateway configuration: address family is conflict with CIDR", + }, + { + name: "ExgressGWErr5", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest-exgatewayerr", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1..10.16.0.10"}, + Provider: "ovn", + GatewayType: "distributed", + Vips: []string{"10.17.2.1"}, + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "vip 10.17.2.1 conflicts with subnet utest-exgatewayerr cidr 10.16.0.0/16", + }, + { + name: "CIDRformErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "gateway 10.16.0.1 is not in cidr 10.16.0/16", + }, + { + name: "ExcludeIPErr", + asubnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.1"}, + Provider: "ovn", + GatewayType: "distributed", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + err: "ip 10.16.1 in exclude_ips is not a valid address", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ret := ValidateSubnet(tt.asubnet) + if !ErrorContains(ret, tt.err) { + t.Errorf("got %v, want a %v", ret, tt.err) + } + }) + } +} + +func TestValidatePodNetwork(t *testing.T) { + tests := []struct { + name string + annotations map[string]string + err string + }{ + { + name: "podIP", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.16.0.15", + "ovn.kubernetes.io/mac_address": "00:00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.0.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "3", + "ovn.kubernetes.io/egress_rate": "1", + "ovn.kubernetes.io/cidr": "10.16.0.0/16", + }, + err: "", + }, + { + name: "podIPDual", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.244.0.0/16,fd00:10:244:0:2::/80", + "ovn.kubernetes.io/mac_address": "00:00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.0.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "3", + "ovn.kubernetes.io/egress_rate": "1", + }, + err: "", + }, + { + name: "podIPErr1", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.244.000.0/16,fd00:10:244:0:2::/80", + "ovn.kubernetes.io/mac_address": "00:00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.0.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "3", + "ovn.kubernetes.io/egress_rate": "1", + }, + err: "10.244.000.0/16 is not a valid ovn.kubernetes.io/ip_address", + }, + { + name: "podIPNotCIDRErr", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.244.0.0/16,fd00:10:244:0:2::::", + "ovn.kubernetes.io/mac_address": "00:00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.0.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "3", + "ovn.kubernetes.io/egress_rate": "1", + }, + err: "fd00:10:244:0:2:::: is not a valid ovn.kubernetes.io/ip_address", + }, + { + name: "podIPCIDRErr", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.244.0.0/16,fd00:10:244:0:2::/80", + "ovn.kubernetes.io/mac_address": "00:00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.0.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "3", + "ovn.kubernetes.io/egress_rate": "1", + "ovn.kubernetes.io/cidr": "10.16.0/16", + }, + err: "invalid cidr 10.16.0/16", + }, + { + name: "podIPErr4", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.244.0.0/16,fd00:10:244:0:2::/80", + "ovn.kubernetes.io/mac_address": "00:00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.0.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "3", + "ovn.kubernetes.io/egress_rate": "1", + "ovn.kubernetes.io/cidr": "10.16.0.0/16", + }, + err: "[10.244.0.0/16 not in cidr 10.16.0.0/16, fd00:10:244:0:2::/80 not in cidr 10.16.0.0/16]", + }, + { + name: "podMacErr", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.16.0.15", + "ovn.kubernetes.io/mac_address": "00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.0.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "3", + "ovn.kubernetes.io/egress_rate": "1", + "ovn.kubernetes.io/cidr": "10.16.0.0/16", + }, + err: "00:00:54:17:2A is not a valid ovn.kubernetes.io/mac_address", + }, + { + name: "podIPPollErr", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.16.0.15", + "ovn.kubernetes.io/mac_address": "00:00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.1111.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "3", + "ovn.kubernetes.io/egress_rate": "1", + "ovn.kubernetes.io/cidr": "10.16.0.0/16", + }, + err: "10.16.1111.15 in ovn.kubernetes.io/ip_pool is not a valid address", + }, + { + name: "ingRaErr", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.16.0.15", + "ovn.kubernetes.io/mac_address": "00:00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.0.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "a3", + "ovn.kubernetes.io/egress_rate": "1", + "ovn.kubernetes.io/cidr": "10.16.0.0/16", + }, + err: "a3 is not a valid ovn.kubernetes.io/ingress_rate", + }, + { + name: "EgRatErr", + annotations: map[string]string{ + "ovn.kubernetes.io/ip_address": "10.16.0.15", + "ovn.kubernetes.io/mac_address": "00:00:00:54:17:2A", + "ovn.kubernetes.io/ip_pool": "10.16.0.15,10.16.0.16,10.16.0.17", + "ovn.kubernetes.io/ingress_rate": "3", + "ovn.kubernetes.io/egress_rate": "a1", + "ovn.kubernetes.io/cidr": "10.16.0.0/16", + }, + err: "a1 is not a valid ovn.kubernetes.io/egress_rate", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ret := ValidatePodNetwork(tt.annotations) + if !ErrorContains(ret, tt.err) { + t.Errorf("got %v, want a error %v", ret, tt.err) + } + }) + } +} + +func TestValidatePodCidr(t *testing.T) { + tests := []struct { + name string + cidr string + ip string + err string + }{ + { + name: "corretV4", + cidr: "10.16.0.0/16", + ip: "10.16.0.3", + err: "", + }, + { + name: "corretDual", + cidr: "10.244.0.0/16,fd00:10:244:0:2::/80", + ip: "10.244.0.6,fd00:10:244:0:2:2", + err: "", + }, + { + name: "boardV4", + cidr: "10.16.0.0/16", + ip: "10.16.255.255", + err: "10.16.255.255 is the broadcast ip in cidr 10.16.0.0/16", + }, + { + name: "boardV4", + cidr: "10.16.0.0/16", + ip: "10.16.0.0", + err: "10.16.0.0 is the network number ip in cidr 10.16.0.0/16", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ret := ValidatePodCidr(tt.cidr, tt.ip) + if !ErrorContains(ret, tt.err) { + t.Errorf("got %v, want a error %v", ret, tt.err) + } + }) + } +} + +func TestValidateCidrConflict(t *testing.T) { + tests := []struct { + name string + subnet kubeovnv1.Subnet + subnetList []kubeovnv1.Subnet + err string + }{ + { + name: "base", + subnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest0", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.17.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + Vlan: "123", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + subnetList: []kubeovnv1.Subnet{ + kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest0", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster11", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.17.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + Vlan: "1234", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + }, + err: "", + }, + { + name: "cidrOverlapErr", + subnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest0", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.17.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + Vlan: "123", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + subnetList: []kubeovnv1.Subnet{ + kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest1", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.17.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + Vlan: "123", + }, + Status: kubeovnv1.SubnetStatus{}, + }, + }, + err: "10.16.0.0/16 is conflict with subnet utest1 cidr 10.16.0.0/16", + }, + { + name: "cidrOverlapErr", + subnet: kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest0", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.16.0.0/16", + Gateway: "10.16.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + Vlan: "123", + ExternalEgressGateway: "12.12.123.12", + PolicyRoutingTableID: 111, + }, + Status: kubeovnv1.SubnetStatus{}, + }, + subnetList: []kubeovnv1.Subnet{ + kubeovnv1.Subnet{ + TypeMeta: metav1.TypeMeta{Kind: "Subnet", APIVersion: "kubeovn.io/v1"}, + ObjectMeta: metav1.ObjectMeta{ + Name: "utest1", + }, + Spec: kubeovnv1.SubnetSpec{ + Default: true, + Vpc: "ovn-cluster", + Protocol: "IPv4", + Namespaces: nil, + CIDRBlock: "10.17.0.0/16", + Gateway: "10.17.0.1", + ExcludeIps: []string{"10.16.0.1"}, + Provider: "ovn", + GatewayType: "distributed", + Vlan: "123", + ExternalEgressGateway: "12.12.123.12", + PolicyRoutingTableID: 111, + }, + Status: kubeovnv1.SubnetStatus{}, + }, + }, + err: "subnet utest0 policy routing table ID 111 is conflict with subnet utest1 policy routing table ID 111", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ret := ValidateCidrConflict(tt.subnet, tt.subnetList) + if !ErrorContains(ret, tt.err) { + t.Errorf("got %v, want a error", ret) + } + }) + } +} diff --git a/pkg/util/version_test.go b/pkg/util/version_test.go new file mode 100644 index 00000000000..426d7271701 --- /dev/null +++ b/pkg/util/version_test.go @@ -0,0 +1,37 @@ +package util + +import ( + "testing" +) + +func TestCompareVersion(t *testing.T) { + tests := []struct { + name string + version1 string + version2 string + want int + }{ + { + version1: "21.06.1", + version2: "20.09", + want: 1, + }, + { + version1: "1.6.2", + version2: "1.8.4", + want: -1, + }, + { + version1: "1.8.4", + version2: "1.8.4", + want: 0, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if ret := CompareVersion(tt.version1, tt.version2); ret != tt.want { + t.Errorf("got %v, want %v", ret, tt.want) + } + }) + } +} diff --git a/pkg/util/vlan_test.go b/pkg/util/vlan_test.go new file mode 100644 index 00000000000..0cc2979ed26 --- /dev/null +++ b/pkg/util/vlan_test.go @@ -0,0 +1,28 @@ +package util + +import ( + "testing" +) + +func TestExternalBridgeName(t *testing.T) { + tests := []struct { + arg string + want string + }{ + { + arg: "ovn", + want: "br-ovn", + }, + { + arg: "Macvlan", + want: "br-Macvlan", + }, + } + for _, tt := range tests { + t.Run(tt.arg, func(t *testing.T) { + if ret := ExternalBridgeName(tt.arg); ret != tt.want { + t.Errorf("got %v, want %v", ret, tt.want) + } + }) + } +}