refactor: optimize get pod/service netowrk cidr logic (#585)

This commit is contained in:
naison
2025-05-09 13:06:32 +08:00
committed by GitHub
parent e21fc8cda9
commit ca6a2be70f
10 changed files with 330 additions and 354 deletions

View File

@@ -216,12 +216,7 @@ func (m *Manager) Get(ctx context.Context, key string) (string, error) {
if err != nil {
return "", err
}
if cm != nil && cm.Data != nil {
if v, ok := cm.Data[key]; ok {
return v, nil
}
}
return "", fmt.Errorf("can not get data")
return cm.Data[key], nil
}
func (m *Manager) ForEach(ctx context.Context, fnv4 func(net.IP), fnv6 func(net.IP)) error {

View File

@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"net"
"net/url"
"reflect"
"slices"
"sort"
@@ -411,17 +410,19 @@ func (c *ConnectOptions) startLocalTunServer(ctx context.Context, forwardAddress
}
// add extra-cidr
for _, s := range c.ExtraRouteInfo.ExtraCIDR {
var ipnet *net.IPNet
_, ipnet, err = net.ParseCIDR(s)
var ipNet *net.IPNet
_, ipNet, err = net.ParseCIDR(s)
if err != nil {
return fmt.Errorf("invalid extra-cidr %s, err: %v", s, err)
}
cidrList = append(cidrList, ipnet)
cidrList = append(cidrList, ipNet)
}
var routes []types.Route
for _, ipNet := range util.RemoveLargerOverlappingCIDRs(cidrList) {
routes = append(routes, types.Route{Dst: *ipNet})
if ipNet != nil {
routes = append(routes, types.Route{Dst: *ipNet})
}
}
tunConfig := tun.Config{
@@ -761,78 +762,45 @@ func (c *ConnectOptions) GetRunningPodList(ctx context.Context) ([]v1.Pod, error
// https://stackoverflow.com/questions/45903123/kubernetes-set-service-cidr-and-pod-cidr-the-same
// https://stackoverflow.com/questions/44190607/how-do-you-find-the-cluster-service-cidr-of-a-kubernetes-cluster/54183373#54183373
// https://stackoverflow.com/questions/44190607/how-do-you-find-the-cluster-service-cidr-of-a-kubernetes-cluster
func (c *ConnectOptions) getCIDR(ctx context.Context, m *dhcp.Manager) (err error) {
defer func() {
if err == nil {
u, err2 := url.Parse(c.config.Host)
if err2 != nil {
return
}
host, _, err3 := net.SplitHostPort(u.Host)
if err3 != nil {
return
}
var ipList []net.IP
if ip := net.ParseIP(host); ip != nil {
ipList = append(ipList, ip)
}
ips, _ := net.LookupIP(host)
if ips != nil {
ipList = append(ipList, ips...)
}
c.apiServerIPs = ipList
c.removeCIDRsContainingIPs(ipList)
}
}()
func (c *ConnectOptions) getCIDR(ctx context.Context, m *dhcp.Manager) error {
var err error
c.apiServerIPs, err = util.GetAPIServerIP(c.config.Host)
if err != nil {
return err
}
// (1) get CIDR from cache
var value string
value, err = m.Get(ctx, config.KeyClusterIPv4POOLS)
if err == nil {
for _, s := range strings.Split(value, " ") {
var ipPoolStr string
ipPoolStr, err = m.Get(ctx, config.KeyClusterIPv4POOLS)
if err != nil {
return err
}
if ipPoolStr != "" {
for _, s := range strings.Split(ipPoolStr, " ") {
_, cidr, _ := net.ParseCIDR(s)
if cidr != nil {
c.cidrs = util.RemoveLargerOverlappingCIDRs(append(c.cidrs, cidr))
c.cidrs = util.RemoveCIDRsContainingIPs(util.RemoveLargerOverlappingCIDRs(append(c.cidrs, cidr)), c.apiServerIPs)
}
}
if len(c.cidrs) != 0 {
plog.G(ctx).Infoln("Get network CIDR from cache")
return nil
}
plog.G(ctx).Infoln("Get network CIDR from cache")
return nil
}
// (2) get CIDR from cni
c.cidrs, err = util.GetCIDRElegant(ctx, c.clientset, c.config, c.Namespace)
c.cidrs, err = util.GetCIDR(ctx, c.clientset, c.config, c.Namespace)
c.cidrs = util.RemoveCIDRsContainingIPs(util.RemoveLargerOverlappingCIDRs(c.cidrs), c.apiServerIPs)
if err == nil {
s := sets.New[string]()
for _, cidr := range c.cidrs {
s.Insert(cidr.String())
}
cidrs := util.GetCIDRFromResourceUgly(ctx, c.clientset, c.Namespace)
for _, cidr := range cidrs {
s.Insert(cidr.String())
}
c.cidrs = util.RemoveLargerOverlappingCIDRs(append(c.cidrs, cidrs...))
_ = m.Set(ctx, config.KeyClusterIPv4POOLS, strings.Join(s.UnsortedList(), " "))
return nil
return m.Set(ctx, config.KeyClusterIPv4POOLS, strings.Join(s.UnsortedList(), " "))
}
// (3) fallback to get cidr from node/pod/service
c.cidrs = util.GetCIDRFromResourceUgly(ctx, c.clientset, c.Namespace)
// ignore error
return nil
}
func (c *ConnectOptions) removeCIDRsContainingIPs(ipList []net.IP) {
for i := len(c.cidrs) - 1; i >= 0; i-- {
for _, ip := range ipList {
if c.cidrs[i].Contains(ip) {
c.cidrs = append(c.cidrs[:i], c.cidrs[i+1:]...)
break
}
}
}
}
func (c *ConnectOptions) addExtraRoute(ctx context.Context, name string) error {
if len(c.ExtraRouteInfo.ExtraDomain) == 0 {
return nil

View File

@@ -27,134 +27,6 @@ func TestRoute(t *testing.T) {
t.Logf("iface: %s, gateway: %s, src: %s", iface.Name, gateway, src)
}
func TestRemoveCIDRsContainingIPs(t *testing.T) {
tests := []struct {
name string
cidrStrings []string
ipStrings []string
expectedCIDRs []string
expectPanic bool
}{
{
name: "Normal case - some overlaps",
cidrStrings: []string{
"10.140.45.0/24", "10.140.44.0/24", "10.31.0.0/24", "10.31.1.0/24", "10.31.2.0/24", "10.31.3.0/24", "10.140.47.0/24", "10.140.46.0/24",
},
ipStrings: []string{
"10.140.45.1", "10.140.46.220", "10.140.45.180", "10.140.45.152",
"10.140.46.183", "10.140.45.52", "10.140.47.148", "10.140.46.214",
},
expectedCIDRs: []string{
"10.140.44.0/24", "10.31.0.0/24", "10.31.1.0/24", "10.31.2.0/24", "10.31.3.0/24",
},
expectPanic: false,
},
{
name: "Empty CIDR list",
cidrStrings: []string{},
ipStrings: []string{
"10.140.45.1",
},
expectedCIDRs: []string{},
expectPanic: false,
},
{
name: "Empty IP list",
cidrStrings: []string{
"10.140.45.0/24", "10.140.44.0/24",
},
ipStrings: []string{},
expectedCIDRs: []string{
"10.140.45.0/24", "10.140.44.0/24",
},
expectPanic: false,
},
{
name: "All CIDRs removed",
cidrStrings: []string{
"10.140.45.0/24", "10.140.46.0/24",
},
ipStrings: []string{
"10.140.45.1", "10.140.46.220",
},
expectedCIDRs: []string{},
expectPanic: false,
},
{
name: "Overlapping CIDRs",
cidrStrings: []string{
"10.140.45.0/24", "10.140.45.0/25", "10.140.45.128/25",
},
ipStrings: []string{
"10.140.45.1", "10.140.45.129",
},
expectedCIDRs: []string{},
expectPanic: false,
},
{
name: "Invalid CIDR format",
cidrStrings: []string{
"10.140.45.0/24", "invalid-cidr",
},
ipStrings: []string{
"10.140.45.1",
},
expectedCIDRs: nil, // Panic expected
expectPanic: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
if !test.expectPanic {
t.Errorf("unexpected panic: %v", r)
}
} else if test.expectPanic {
t.Errorf("expected panic but got none")
}
}()
var cidrs []*net.IPNet
for _, cidr := range test.cidrStrings {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
if test.expectPanic {
panic(err)
}
t.Fatalf("failed to parse CIDR %s: %v", cidr, err)
}
cidrs = append(cidrs, ipNet)
}
var ipList []net.IP
for _, ip := range test.ipStrings {
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
t.Fatalf("failed to parse IP %s", ip)
}
ipList = append(ipList, parsedIP)
}
c := &ConnectOptions{
cidrs: cidrs,
}
c.removeCIDRsContainingIPs(ipList)
if !test.expectPanic {
if len(c.cidrs) != len(test.expectedCIDRs) {
t.Fatalf("unexpected number of remaining CIDRs: got %d, want %d", len(c.cidrs), len(test.expectedCIDRs))
}
for i, cidr := range c.cidrs {
if cidr.String() != test.expectedCIDRs[i] {
t.Errorf("unexpected CIDR at index %d: got %s, want %s", i, cidr.String(), test.expectedCIDRs[i])
}
}
}
})
}
}
func TestSort(t *testing.T) {
list := v1.PodList{
Items: []v1.Pod{

View File

@@ -101,7 +101,7 @@ func addTunRoutes(ifName string, routes ...types.Route) error {
var prefixList []netip.Prefix
for _, r := range routes {
if r.Dst.String() == "" {
if net.ParseIP(r.Dst.IP.String()) == nil {
continue
}
var prefix netip.Prefix
@@ -116,7 +116,7 @@ func addTunRoutes(ifName string, routes ...types.Route) error {
}
err = addRoute(gw, prefixList...)
if err != nil {
return fmt.Errorf("failed to add route: %v", err)
return fmt.Errorf("failed to add dst %v via %s to route table: %v", prefixList, ifName, err)
}
return nil
}

View File

@@ -107,13 +107,13 @@ func createTun(cfg Config) (conn net.Conn, itf *net.Interface, err error) {
func addTunRoutes(ifName string, routes ...types.Route) error {
for _, route := range routes {
if route.Dst.String() == "" {
if net.ParseIP(route.Dst.IP.String()) == nil {
continue
}
// ip route add 192.168.1.123/32 dev utun0
err := netlink.AddRoute(route.Dst.String(), "", "", ifName)
if err != nil && !errors.Is(err, syscall.EEXIST) {
return fmt.Errorf("failed to add route: %v", err)
return fmt.Errorf("failed to add dst %v via %s to route table: %v", route.Dst.String(), ifName, err)
}
}
return nil

View File

@@ -135,7 +135,7 @@ func addTunRoutes(tunName string, routes ...types.Route) error {
return err
}
for _, route := range routes {
if route.Dst.String() == "" {
if net.ParseIP(route.Dst.IP.String()) == nil {
continue
}
if route.GW == nil {

View File

@@ -5,6 +5,8 @@ import (
"encoding/json"
"fmt"
"net"
"net/url"
"sort"
"strings"
"time"
@@ -26,12 +28,12 @@ import (
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
// GetCIDRElegant
// GetCIDR
// 1) dump cluster info
// 2) grep cmdline
// 3) create svc + cat *.conflist
// 4) create svc + get pod ip with svc mask
func GetCIDRElegant(ctx context.Context, clientset *kubernetes.Clientset, restconfig *rest.Config, namespace string) ([]*net.IPNet, error) {
func GetCIDR(ctx context.Context, clientset *kubernetes.Clientset, restconfig *rest.Config, namespace string) ([]*net.IPNet, error) {
defer func() {
_ = clientset.CoreV1().Pods(namespace).Delete(context.Background(), config.CniNetName, v1.DeleteOptions{GracePeriodSeconds: pointer.Int64(0)})
}()
@@ -51,107 +53,26 @@ func GetCIDRElegant(ctx context.Context, clientset *kubernetes.Clientset, restco
result = append(result, cni...)
}
pod, err := GetPodCIDRFromCNI(ctx, clientset, restconfig, namespace)
podCIDR, err := GetPodCIDRFromCNI(ctx, clientset, restconfig, namespace)
if err == nil {
result = append(result, pod...)
}
svc, err := GetServiceCIDRByCreateService(ctx, clientset.CoreV1().Services(namespace))
if err == nil {
result = append(result, svc)
result = append(result, podCIDR...)
}
plog.G(ctx).Infoln("Getting network CIDR from services...")
pod, err = GetPodCIDRFromPod(ctx, clientset, namespace, svc)
if err == nil {
svcCIDR, _ := GetServiceCIDRByCreateService(ctx, clientset.CoreV1().Services(namespace))
if svcCIDR != nil {
plog.G(ctx).Debugf("Getting network CIDR from services successfully")
result = append(result, pod...)
result = append(result, svcCIDR)
podCIDR, err = GetPodCIDRFromPod(ctx, clientset, namespace, svcCIDR)
if err == nil {
result = append(result, podCIDR...)
}
}
result = RemoveLargerOverlappingCIDRs(result)
if len(result) == 0 {
err = fmt.Errorf("failed to get any network CIDR, please verify that you have the necessary permissions")
return nil, err
}
return result, nil
}
// GetCIDRFromResourceUgly
// use podIP/24 and serviceIP/24 as cidr
func GetCIDRFromResourceUgly(ctx context.Context, clientset *kubernetes.Clientset, namespace string) []*net.IPNet {
var cidrs []*net.IPNet
// (2) get pod CIDR from pod ip, why doing this: notice that node's pod cidr is not correct in minikube
// ➜ ~ kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}'
//10.244.0.0/24%
// ➜ ~ kubectl get pods -o=custom-columns=podIP:.status.podIP
//podIP
//172.17.0.5
//172.17.0.4
//172.17.0.4
//172.17.0.3
//172.17.0.3
//172.17.0.6
//172.17.0.8
//172.17.0.3
//172.17.0.7
//172.17.0.2
for _, n := range []string{v1.NamespaceAll, namespace} {
podList, err := clientset.CoreV1().Pods(n).List(ctx, v1.ListOptions{})
if err != nil {
continue
}
for _, pod := range podList.Items {
if pod.Spec.HostNetwork {
continue
}
s := sets.Set[string]{}.Insert(pod.Status.PodIP)
for _, p := range pod.Status.PodIPs {
s.Insert(p.IP)
}
for _, t := range s.UnsortedList() {
if ip := net.ParseIP(t); ip != nil {
var mask net.IPMask
if ip.To4() != nil {
mask = net.CIDRMask(24, 32)
} else {
mask = net.CIDRMask(64, 128)
}
cidrs = append(cidrs, &net.IPNet{IP: ip.Mask(mask), Mask: mask})
}
}
}
break
}
// (2) get service CIDR
for _, n := range []string{v1.NamespaceAll, namespace} {
serviceList, err := clientset.CoreV1().Services(n).List(ctx, v1.ListOptions{})
if err != nil {
continue
}
for _, service := range serviceList.Items {
s := sets.Set[string]{}.Insert(service.Spec.ClusterIP)
for _, p := range service.Spec.ClusterIPs {
s.Insert(p)
}
for _, t := range s.UnsortedList() {
if ip := net.ParseIP(t); ip != nil {
var mask net.IPMask
if ip.To4() != nil {
mask = net.CIDRMask(24, 32)
} else {
mask = net.CIDRMask(64, 128)
}
cidrs = append(cidrs, &net.IPNet{IP: ip.Mask(mask), Mask: mask})
}
}
}
break
}
return cidrs
}
// ParseCIDRFromString
/*
*
@@ -170,8 +91,8 @@ func ParseCIDRFromString(content string) (result []*net.IPNet) {
if len(split) == 2 {
cidrList := split[1]
for _, cidr := range strings.Split(cidrList, ",") {
_, c, err := net.ParseCIDR(cidr)
if err == nil {
_, c, _ := net.ParseCIDR(cidr)
if c != nil {
result = append(result, c)
}
}
@@ -185,7 +106,7 @@ func ParseCIDRFromString(content string) (result []*net.IPNet) {
// ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#configure-ipv4-ipv6-dual-stack
// get cidr by dump cluster info
func GetCIDRByDumpClusterInfo(ctx context.Context, clientset *kubernetes.Clientset) ([]*net.IPNet, error) {
podList, err := clientset.CoreV1().Pods(v1.NamespaceSystem).List(ctx, v1.ListOptions{})
podList, err := clientset.CoreV1().Pods(v1.NamespaceSystem).List(ctx, v1.ListOptions{Limit: 100})
if err != nil {
return nil, err
}
@@ -201,7 +122,7 @@ func GetCIDRByDumpClusterInfo(ctx context.Context, clientset *kubernetes.Clients
for _, s := range list {
result = append(result, ParseCIDRFromString(s)...)
}
return RemoveLargerOverlappingCIDRs(result), nil
return result, nil
}
// GetCIDRFromCNI kube-controller-manager--allocate-node-cidrs=true--authentication-kubeconfig=/etc/kubernetes/controller-manager.conf--authorization-kubeconfig=/etc/kubernetes/controller-manager.conf--bind-address=0.0.0.0--client-ca-file=/etc/kubernetes/ssl/ca.crt--cluster-cidr=10.233.64.0/18--cluster-name=cluster.local--cluster-signing-cert-file=/etc/kubernetes/ssl/ca.crt--cluster-signing-key-file=/etc/kubernetes/ssl/ca.key--configure-cloud-routes=false--controllers=*,bootstrapsigner,tokencleaner--kubeconfig=/etc/kubernetes/controller-manager.conf--leader-elect=true--leader-elect-lease-duration=15s--leader-elect-renew-deadline=10s--node-cidr-mask-size=24--node-monitor-grace-period=40s--node-monitor-period=5s--port=0--profiling=False--requestheader-client-ca-file=/etc/kubernetes/ssl/front-proxy-ca.crt--root-ca-file=/etc/kubernetes/ssl/ca.crt--service-account-private-key-file=/etc/kubernetes/ssl/sa.key--service-cluster-ip-range=10.233.0.0/18--terminated-pod-gc-threshold=12500--use-service-account-credentials=true
@@ -221,7 +142,7 @@ func GetCIDRFromCNI(ctx context.Context, clientset *kubernetes.Clientset, restco
var result []*net.IPNet
for _, s := range strings.Split(content, "\n") {
result = RemoveLargerOverlappingCIDRs(append(result, ParseCIDRFromString(s)...))
result = append(result, ParseCIDRFromString(s)...)
}
return result, nil
@@ -237,15 +158,12 @@ func GetServiceCIDRByCreateService(ctx context.Context, serviceInterface v12.Ser
if err != nil {
idx := strings.LastIndex(err.Error(), defaultCIDRIndex)
if idx != -1 {
_, cidr, err := net.ParseCIDR(strings.TrimSpace(err.Error()[idx+len(defaultCIDRIndex):]))
if err != nil {
return nil, err
}
return cidr, nil
_, cidr, err1 := net.ParseCIDR(strings.TrimSpace(err.Error()[idx+len(defaultCIDRIndex):]))
return cidr, err1
}
return nil, fmt.Errorf("can not found any keyword of service network CIDR info, err: %s", err.Error())
return nil, fmt.Errorf("can not found any keyword of service network CIDR info: %s", err.Error())
}
return nil, err
return nil, fmt.Errorf("can not found any keyword of service network CIDR info")
}
// GetPodCIDRFromCNI
@@ -295,7 +213,7 @@ func GetPodCIDRFromCNI(ctx context.Context, clientset *kubernetes.Clientset, res
return nil, err
}
plog.G(ctx).Infoln("Get CNI config", configList.Name)
var cidr []*net.IPNet
var cidrList []*net.IPNet
for _, plugin := range configList.Plugins {
switch plugin.Network.Type {
case "calico":
@@ -305,13 +223,13 @@ func GetPodCIDRFromCNI(ctx context.Context, clientset *kubernetes.Clientset, res
slice6, _, _ := unstructured.NestedStringSlice(m, "ipam", "ipv6_pools")
for _, s := range sets.New[string]().Insert(slice...).Insert(slice6...).UnsortedList() {
if _, ipNet, _ := net.ParseCIDR(s); ipNet != nil {
cidr = append(cidr, ipNet)
cidrList = append(cidrList, ipNet)
}
}
}
}
return cidr, nil
return cidrList, nil
}
func CreateCIDRPod(ctx context.Context, clientset *kubernetes.Clientset, namespace string) (*v13.Pod, error) {
@@ -439,37 +357,110 @@ func CreateCIDRPod(ctx context.Context, clientset *kubernetes.Clientset, namespa
}
func GetPodCIDRFromPod(ctx context.Context, clientset *kubernetes.Clientset, namespace string, svc *net.IPNet) ([]*net.IPNet, error) {
podList, err := clientset.CoreV1().Pods(namespace).List(ctx, v1.ListOptions{})
podList, err := clientset.CoreV1().Pods(namespace).List(ctx, v1.ListOptions{Limit: 100})
if err != nil {
return nil, err
}
for i := 0; i < len(podList.Items); i++ {
if podList.Items[i].Spec.HostNetwork {
podList.Items = append(podList.Items[:i], podList.Items[i+1:]...)
i--
}
}
var result []*net.IPNet
for _, item := range podList.Items {
if item.Spec.HostNetwork {
continue
}
s := sets.New[string]().Insert(item.Status.PodIP)
for _, p := range item.Status.PodIPs {
s.Insert(p.IP)
}
for _, t := range s.UnsortedList() {
if ip := net.ParseIP(t); ip != nil {
var mask net.IPMask
if ip.To4() != nil {
mask = net.CIDRMask(24, 32)
} else {
mask = net.CIDRMask(64, 128)
_, ipNet, _ := net.ParseCIDR((&net.IPNet{IP: ip, Mask: svc.Mask}).String())
if ipNet != nil {
result = append(result, ipNet)
}
result = append(result, &net.IPNet{IP: ip, Mask: /*svc.Mask*/ mask})
}
}
}
if len(result) == 0 {
return nil, fmt.Errorf("can not found pod network CIDR from pod list")
}
return result, nil
}
func RemoveCIDRsContainingIPs(cidrs []*net.IPNet, ipList []net.IP) []*net.IPNet {
for i := len(cidrs) - 1; i >= 0; i-- {
for _, ip := range ipList {
if cidrs[i].Contains(ip) {
cidrs = append(cidrs[:i], cidrs[i+1:]...)
break
}
}
}
return cidrs
}
func GetAPIServerIP(apiServerHost string) ([]net.IP, error) {
u, err := url.Parse(apiServerHost)
if err != nil {
return nil, err
}
var host string
if strings.IndexByte(u.Host, ':') < 0 {
host = u.Host
} else {
host, _, err = net.SplitHostPort(u.Host)
if err != nil {
return nil, err
}
}
var ipList []net.IP
var set = sets.New[string]()
if ip := net.ParseIP(host); ip != nil {
if !set.Has(ip.String()) {
ipList = append(ipList, ip)
set.Insert(ip.String())
}
}
addrs, _ := net.LookupHost(host)
for _, addr := range addrs {
ip := net.ParseIP(addr)
if ip == nil {
continue
}
if !set.Has(ip.String()) {
ipList = append(ipList, ip)
set.Insert(ip.String())
}
}
return ipList, nil
}
func RemoveLargerOverlappingCIDRs(cidrNets []*net.IPNet) []*net.IPNet {
sort.Slice(cidrNets, func(i, j int) bool {
onesI, _ := cidrNets[i].Mask.Size()
onesJ, _ := cidrNets[j].Mask.Size()
// mask number is smaller, means the mask is larger
return onesI < onesJ
})
var cidrsOverlap = func(cidr1, cidr2 *net.IPNet) bool {
return cidr1.Contains(cidr2.IP) || cidr2.Contains(cidr1.IP)
}
var result []*net.IPNet
skipped := make(map[int]bool)
for i := range cidrNets {
if skipped[i] {
continue
}
for j := i + 1; j < len(cidrNets); j++ {
if cidrsOverlap(cidrNets[i], cidrNets[j]) {
skipped[j] = true
}
}
result = append(result, cidrNets[i])
}
return result
}

View File

@@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"net"
"reflect"
"testing"
"time"
@@ -69,7 +71,7 @@ func TestByCreateSvc(t *testing.T) {
func TestElegant(t *testing.T) {
before()
elegant, err := GetCIDRElegant(context.Background(), clientset, restconfig, namespace)
elegant, err := GetCIDR(context.Background(), clientset, restconfig, namespace)
if err != nil {
t.Error(err)
}
@@ -127,3 +129,181 @@ func TestPatch(t *testing.T) {
}
fmt.Println(string(bytes))
}
func TestRemoveCIDRsContainingIPs(t *testing.T) {
tests := []struct {
name string
cidrStrings []string
ipStrings []string
expectedCIDRs []string
expectPanic bool
}{
{
name: "Normal case - some overlaps",
cidrStrings: []string{
"10.140.45.0/24", "10.140.44.0/24", "10.31.0.0/24", "10.31.1.0/24", "10.31.2.0/24", "10.31.3.0/24", "10.140.47.0/24", "10.140.46.0/24",
},
ipStrings: []string{
"10.140.45.1", "10.140.46.220", "10.140.45.180", "10.140.45.152",
"10.140.46.183", "10.140.45.52", "10.140.47.148", "10.140.46.214",
},
expectedCIDRs: []string{
"10.140.44.0/24", "10.31.0.0/24", "10.31.1.0/24", "10.31.2.0/24", "10.31.3.0/24",
},
expectPanic: false,
},
{
name: "Empty CIDR list",
cidrStrings: []string{},
ipStrings: []string{
"10.140.45.1",
},
expectedCIDRs: []string{},
expectPanic: false,
},
{
name: "Empty IP list",
cidrStrings: []string{
"10.140.45.0/24", "10.140.44.0/24",
},
ipStrings: []string{},
expectedCIDRs: []string{
"10.140.45.0/24", "10.140.44.0/24",
},
expectPanic: false,
},
{
name: "All CIDRs removed",
cidrStrings: []string{
"10.140.45.0/24", "10.140.46.0/24",
},
ipStrings: []string{
"10.140.45.1", "10.140.46.220",
},
expectedCIDRs: []string{},
expectPanic: false,
},
{
name: "Overlapping CIDRs",
cidrStrings: []string{
"10.140.45.0/24", "10.140.45.0/25", "10.140.45.128/25",
},
ipStrings: []string{
"10.140.45.1", "10.140.45.129",
},
expectedCIDRs: []string{},
expectPanic: false,
},
{
name: "Invalid CIDR format",
cidrStrings: []string{
"10.140.45.0/24", "invalid-cidr",
},
ipStrings: []string{
"10.140.45.1",
},
expectedCIDRs: nil, // Panic expected
expectPanic: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
if !test.expectPanic {
t.Errorf("unexpected panic: %v", r)
}
} else if test.expectPanic {
t.Errorf("expected panic but got none")
}
}()
var cidrs []*net.IPNet
for _, cidr := range test.cidrStrings {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
if test.expectPanic {
panic(err)
}
t.Fatalf("failed to parse CIDR %s: %v", cidr, err)
}
cidrs = append(cidrs, ipNet)
}
var ipList []net.IP
for _, ip := range test.ipStrings {
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
t.Fatalf("failed to parse IP %s", ip)
}
ipList = append(ipList, parsedIP)
}
cidrs = RemoveCIDRsContainingIPs(cidrs, ipList)
if !test.expectPanic {
if len(cidrs) != len(test.expectedCIDRs) {
t.Fatalf("unexpected number of remaining CIDRs: got %d, want %d", len(cidrs), len(test.expectedCIDRs))
}
for i, cidr := range cidrs {
if cidr.String() != test.expectedCIDRs[i] {
t.Errorf("unexpected CIDR at index %d: got %s, want %s", i, cidr.String(), test.expectedCIDRs[i])
}
}
}
})
}
}
func TestRemoveLargerOverlappingCIDRs(t *testing.T) {
type args struct {
cidrNets []*net.IPNet
}
tests := []struct {
name string
args args
want []*net.IPNet
}{
{
name: "equal",
args: args{
cidrNets: []*net.IPNet{
{IP: net.ParseIP("192.168.1.0"), Mask: net.CIDRMask(24, 32)},
{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(24, 32)},
}},
want: []*net.IPNet{
{IP: net.ParseIP("192.168.1.0"), Mask: net.CIDRMask(24, 32)},
{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(24, 32)},
},
},
{
name: "larger",
args: args{
cidrNets: []*net.IPNet{
{IP: net.ParseIP("192.168.1.0"), Mask: net.CIDRMask(24, 32)},
{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(16, 32)},
}},
want: []*net.IPNet{
{IP: net.ParseIP("192.168.2.0"), Mask: net.CIDRMask(16, 32)},
},
},
{
name: "deduplicated",
args: args{
cidrNets: []*net.IPNet{
{IP: net.ParseIP("192.168.1.0"), Mask: net.CIDRMask(24, 32)},
{IP: net.ParseIP("192.168.1.0"), Mask: net.CIDRMask(24, 32)},
}},
want: []*net.IPNet{
{IP: net.ParseIP("192.168.1.0"), Mask: net.CIDRMask(24, 32)},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := RemoveLargerOverlappingCIDRs(tt.args.cidrNets); !reflect.DeepEqual(got, tt.want) {
t.Errorf("RemoveLargerOverlappingCIDRs() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -24,7 +24,7 @@ var (
)
const (
addr = "https://github.com/wencaiwulue/kubevpn/releases/latest"
downloadAddr = "https://github.com/wencaiwulue/kubevpn/releases/latest"
)
func GetManifest(httpCli *http.Client, os string, arch string) (version string, url string, err error) {
@@ -70,7 +70,7 @@ func GetManifest(httpCli *http.Client, os string, arch string) (version string,
}
}
err = fmt.Errorf("%s: try download it from: %s", If(m.Message != "", m.Message, string(content)), addr)
err = fmt.Errorf("%s: try download it from: %s", If(m.Message != "", m.Message, string(content)), downloadAddr)
return
}

View File

@@ -7,13 +7,11 @@ import (
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
osexec "os/exec"
"path/filepath"
"runtime"
"sort"
"strings"
"syscall"
"time"
@@ -234,34 +232,6 @@ func CanI(clientset *kubernetes.Clientset, sa, ns string, resource *rbacv1.Polic
return false, nil
}
func RemoveLargerOverlappingCIDRs(cidrNets []*net.IPNet) []*net.IPNet {
sort.Slice(cidrNets, func(i, j int) bool {
onesI, _ := cidrNets[i].Mask.Size()
onesJ, _ := cidrNets[j].Mask.Size()
return onesI > onesJ
})
var cidrsOverlap = func(cidr1, cidr2 *net.IPNet) bool {
return cidr1.Contains(cidr2.IP) || cidr2.Contains(cidr1.IP)
}
var result []*net.IPNet
skipped := make(map[int]bool)
for i := range cidrNets {
if skipped[i] {
continue
}
for j := i + 1; j < len(cidrNets); j++ {
if cidrsOverlap(cidrNets[i], cidrNets[j]) {
skipped[j] = true
}
}
result = append(result, cidrNets[i])
}
return result
}
func CleanExtensionLib() {
if !IsWindows() {
return