feat: optimize get cidr logic

This commit is contained in:
fengcaiwen
2023-03-08 20:50:29 +08:00
committed by wencaiwulue
parent 9ab86c3baf
commit 1b7794aa92
19 changed files with 234 additions and 136 deletions

View File

@@ -16,7 +16,7 @@ ARG BASE=github.com/wencaiwulue/kubevpn
RUN sed -i s@/security.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list \
&& sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
RUN apt-get clean && apt-get update && apt-get install -y wget dnsutils vim curl \
net-tools iptables iputils-ping lsof iproute2 tcpdump
net-tools iptables iputils-ping lsof iproute2 tcpdump binutils
WORKDIR /app

View File

@@ -4,7 +4,7 @@ FROM ubuntu:latest
RUN sed -i s@/security.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list \
&& sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
RUN apt-get clean && apt-get update && apt-get install -y wget dnsutils vim curl \
net-tools iptables iputils-ping lsof iproute2 tcpdump
net-tools iptables iputils-ping lsof iproute2 tcpdump binutils
WORKDIR /app

View File

@@ -46,7 +46,7 @@ func CmdReset(factory cmdutil.Factory) *cobra.Command {
if err != nil {
log.Fatal(err)
}
log.Infoln("done")
log.Println("done")
},
}

View File

@@ -46,7 +46,7 @@ func GetDNSServiceIPFromPod(clientset *kubernetes.Clientset, restclient *rest.RE
}
// duplicate server
set := sets.NewString()
set := sets.New[string]()
for i := 0; i < len(resolvConf.Servers); i++ {
if set.Has(resolvConf.Servers[i]) {
resolvConf.Servers = append(resolvConf.Servers[:i], resolvConf.Servers[i+1:]...)
@@ -183,8 +183,8 @@ func generateHostsEntry(list []v12.Service) string {
if strings.EqualFold(item.Name, ServiceKubernetes) {
continue
}
ipList := sets.NewString(item.Spec.ClusterIPs...).Insert(item.Spec.ExternalIPs...).List()
domainList := sets.NewString(item.Name).Insert(item.Spec.ExternalName).List()
ipList := sets.New[string](item.Spec.ClusterIPs...).Insert(item.Spec.ExternalIPs...).UnsortedList()
domainList := sets.New[string](item.Name).Insert(item.Spec.ExternalName).UnsortedList()
for _, ip := range ipList {
for _, domain := range domainList {
if net.ParseIP(ip) == nil || domain == "" {

View File

@@ -69,7 +69,7 @@ func usingResolver(clientConfig *miekgdns.ClientConfig, ns []string) {
Ndots: clientConfig.Ndots,
Timeout: 2,
}
for _, s := range sets.NewString(strings.Split(clientConfig.Search[0], ".")...).Insert(ns...).List() {
for _, s := range sets.New[string](strings.Split(clientConfig.Search[0], ".")...).Insert(ns...).UnsortedList() {
filename = filepath.Join("/", "etc", "resolver", s)
_ = os.WriteFile(filename, []byte(toString(config)), 0644)
}
@@ -97,7 +97,7 @@ func usingNetworkSetup(ip string, namespace string) {
//}
case <-c:
if rc, err := miekgdns.ClientConfigFromFile(resolv); err == nil && rc.Timeout != 1 {
if !sets.NewString(rc.Servers...).Has(ip) {
if !sets.New[string](rc.Servers...).Has(ip) {
rc.Servers = append(rc.Servers, ip)
for _, s := range []string{namespace + ".svc.cluster.local", "svc.cluster.local", "cluster.local"} {
rc.Search = append(rc.Search, s)

View File

@@ -4,7 +4,6 @@
package dns
import (
"context"
"fmt"
"net/netip"
"os"
@@ -38,7 +37,11 @@ func SetupDNS(clientConfig *miekgdns.ClientConfig, _ []string) error {
servers = append(servers, addr)
}
err = luid.SetDNS(windows.AF_INET, servers, clientConfig.Search)
_ = exec.CommandContext(context.Background(), "ipconfig", "/flushdns").Run()
if err != nil {
log.Warningln(err)
return err
}
err = luid.FlushDNS(windows.AF_INET)
if err != nil {
log.Warningln(err)
return err

View File

@@ -48,7 +48,8 @@ func (c *ConnectOptions) addCleanUpResourceHandler(clientset *kubernetes.Clients
if err == nil {
// if ref-count is less than zero or equals to zero, means nobody is using this traffic pod, so clean it
if count <= 0 {
cleanup(clientset, namespace, config.ConfigMapPodTrafficManager)
log.Info("ref-count is zero, prepare to clean up resource")
cleanup(clientset, namespace, config.ConfigMapPodTrafficManager, true)
}
} else {
log.Error(err)
@@ -123,14 +124,20 @@ func updateRefCount(configMapInterface v12.ConfigMapInterface, name string, incr
return
}
func cleanup(clientset *kubernetes.Clientset, namespace, name string) {
log.Info("ref-count is zero, prepare to clean up resource")
// keep configmap
p := []byte(fmt.Sprintf(`[{"op": "remove", "path": "/data/%s"}]`, config.KeyDHCP))
_, _ = clientset.CoreV1().ConfigMaps(namespace).Patch(context.Background(), name, types.JSONPatchType, p, v1.PatchOptions{})
p = []byte(fmt.Sprintf(`{"data":{"%s":"%s"}}`, config.KeyRefCount, strconv.Itoa(0)))
_, _ = clientset.CoreV1().ConfigMaps(namespace).Patch(context.Background(), name, types.MergePatchType, p, v1.PatchOptions{})
func cleanup(clientset *kubernetes.Clientset, namespace, name string, keepCidr bool) {
options := v1.DeleteOptions{GracePeriodSeconds: pointer.Int64(0)}
if keepCidr {
// keep configmap
p := []byte(fmt.Sprintf(`[{"op": "remove", "path": "/data/%s"}]`, config.KeyDHCP))
_, _ = clientset.CoreV1().ConfigMaps(namespace).Patch(context.Background(), name, types.JSONPatchType, p, v1.PatchOptions{})
p = []byte(fmt.Sprintf(`{"data":{"%s":"%s"}}`, config.KeyRefCount, strconv.Itoa(0)))
_, _ = clientset.CoreV1().ConfigMaps(namespace).Patch(context.Background(), name, types.MergePatchType, p, v1.PatchOptions{})
} else {
_ = clientset.CoreV1().ConfigMaps(namespace).Delete(context.Background(), name, options)
}
_ = clientset.CoreV1().Pods(namespace).Delete(context.Background(), config.CniNetName, options)
_ = clientset.CoreV1().Secrets(namespace).Delete(context.Background(), name, options)
_ = clientset.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(context.Background(), name+"."+namespace, options)
_ = clientset.RbacV1().RoleBindings(namespace).Delete(context.Background(), name, options)

View File

@@ -247,13 +247,13 @@ func (c *ConnectOptions) startLocalTunServe(ctx context.Context, forwardAddress
if util.IsWindows() {
c.localTunIP.Mask = net.CIDRMask(0, 32)
}
var list = sets.NewString(config.CIDR.String())
var list = sets.New[string](config.CIDR.String())
for _, ipNet := range c.cidrs {
list.Insert(ipNet.String())
}
r := core.Route{
ServeNodes: []string{
fmt.Sprintf("tun:/127.0.0.1:8422?net=%s&route=%s", c.localTunIP.String(), strings.Join(list.List(), ",")),
fmt.Sprintf("tun:/127.0.0.1:8422?net=%s&route=%s", c.localTunIP.String(), strings.Join(list.UnsortedList(), ",")),
},
ChainNode: forwardAddress,
Retries: 5,
@@ -261,7 +261,7 @@ func (c *ConnectOptions) startLocalTunServe(ctx context.Context, forwardAddress
log.Debugf("your ip is %s", c.localTunIP.IP.String())
if err = Start(ctx, r); err != nil {
log.Errorf("error while create tunnel, err: %v", err)
log.Errorf("error while create tunnel, err: %v", errors.WithStack(err))
} else {
log.Info("tunnel connected")
}
@@ -417,7 +417,7 @@ func (c *ConnectOptions) setupDNS() error {
if relovConf.Port == "" {
relovConf.Port = strconv.Itoa(port)
}
ns := sets.NewString()
ns := sets.New[string]()
list, err := c.clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if err == nil {
for _, item := range list.Items {
@@ -430,7 +430,7 @@ func (c *ConnectOptions) setupDNS() error {
ns.Insert(item.Name)
}
}
if err = dns.SetupDNS(relovConf, ns.List()); err != nil {
if err = dns.SetupDNS(relovConf, ns.UnsortedList()); err != nil {
return err
}
// dump service in current namespace for support DNS resolve service:port
@@ -604,7 +604,7 @@ func (c *ConnectOptions) PreCheckResource() {
controller, err := util.GetTopOwnerReferenceBySelector(c.factory, c.Namespace, selector.String())
if err == nil {
if len(controller) > 0 {
c.Workloads[i] = controller.List()[0]
c.Workloads[i] = controller.UnsortedList()[0]
}
}
// only a single service, not support it yet
@@ -624,7 +624,7 @@ func (c *ConnectOptions) GetRunningPodList() ([]v1.Pod, error) {
return nil, err
}
for i := 0; i < len(list.Items); i++ {
if list.Items[i].GetDeletionTimestamp() != nil || list.Items[i].Status.Phase != v1.PodRunning {
if list.Items[i].GetDeletionTimestamp() != nil || !util.AllContainerIsRunning(&list.Items[i]) {
list.Items = append(list.Items[:i], list.Items[i+1:]...)
i--
}
@@ -645,12 +645,12 @@ func (c *ConnectOptions) GetRunningPodList() ([]v1.Pod, error) {
func (c *ConnectOptions) GetCIDR(ctx context.Context) (err error) {
// (1) get cidr from cache
var value string
value, err = c.dhcp.Get(config.KeyClusterIPv4POOLS)
value, err = c.dhcp.Get(ctx, config.KeyClusterIPv4POOLS)
if err == nil {
for _, s := range strings.Split(value, " ") {
_, cidr, _ := net.ParseCIDR(s)
if cidr != nil {
c.cidrs = append(c.cidrs, cidr)
c.cidrs = util.Deduplicate(append(c.cidrs, cidr))
}
}
}
@@ -662,7 +662,7 @@ func (c *ConnectOptions) GetCIDR(ctx context.Context) (err error) {
// (2) get cidr from cni
c.cidrs, err = util.GetCIDRElegant(c.clientset, c.restclient, c.config, c.Namespace)
if err == nil {
s := sets.NewString()
s := sets.New[string]()
for _, cidr := range c.cidrs {
s.Insert(cidr.String())
}
@@ -670,8 +670,8 @@ func (c *ConnectOptions) GetCIDR(ctx context.Context) (err error) {
for _, cidr := range cidrs {
s.Insert(cidr.String())
}
c.cidrs = append(c.cidrs, cidrs...)
_ = c.dhcp.Set(config.KeyClusterIPv4POOLS, strings.Join(s.List(), " "))
c.cidrs = util.Deduplicate(append(c.cidrs, cidrs...))
_ = c.dhcp.Set(config.KeyClusterIPv4POOLS, strings.Join(s.UnsortedList(), " "))
return
}

View File

@@ -159,8 +159,8 @@ func (d *DHCPManager) Set(key, value string) error {
return nil
}
func (d *DHCPManager) Get(key string) (string, error) {
cm, err := d.client.Get(context.Background(), config.ConfigMapPodTrafficManager, metav1.GetOptions{})
func (d *DHCPManager) Get(ctx2 context.Context, key string) (string, error) {
cm, err := d.client.Get(ctx2, config.ConfigMapPodTrafficManager, metav1.GetOptions{})
if err != nil {
return "", err
}

View File

@@ -60,7 +60,7 @@ func InjectVPNAndEnvoySidecar(ctx1 context.Context, factory cmdutil.Factory, cli
}
// already inject container vpn and envoy-proxy, do nothing
containerNames := sets.NewString()
containerNames := sets.New[string]()
for _, container := range templateSpec.Spec.Containers {
containerNames.Insert(container.Name)
}

View File

@@ -39,6 +39,6 @@ func (c *ConnectOptions) Reset(ctx2 context.Context) error {
}
}
}
cleanup(c.clientset, c.Namespace, config.ConfigMapPodTrafficManager)
cleanup(c.clientset, c.Namespace, config.ConfigMapPodTrafficManager, false)
return nil
}

View File

@@ -19,7 +19,7 @@ var envoyConfig []byte
func RemoveContainers(spec *v1.PodTemplateSpec) {
for i := 0; i < len(spec.Spec.Containers); i++ {
if sets.NewString(config.ContainerSidecarEnvoyProxy, config.ContainerSidecarVPN).Has(spec.Spec.Containers[i].Name) {
if sets.New[string](config.ContainerSidecarEnvoyProxy, config.ContainerSidecarVPN).Has(spec.Spec.Containers[i].Name) {
spec.Spec.Containers = append(spec.Spec.Containers[:i], spec.Spec.Containers[i+1:]...)
i--
}

View File

@@ -84,7 +84,8 @@ func addTunRoutes(luid string, routes ...types.Route) error {
if err != nil {
return err
}
if err = ifName.AddRoute(prefix, addr, 0); err != nil {
err = ifName.AddRoute(prefix, addr, 0)
if err != nil && err != windows.ERROR_OBJECT_ALREADY_EXISTS {
return err
}
}

View File

@@ -38,25 +38,28 @@ func GetCIDRElegant(clientset *kubernetes.Clientset, restclient *rest.RESTClient
result = append(result, cni...)
}
pod, err := getPodCIDRFromCNI(clientset, restclient, restconfig, namespace)
if err == nil {
result = append(result, pod...)
}
svc, err := getServiceCIDRByCreateSvc(clientset.CoreV1().Services(namespace))
if err == nil {
result = append(result, svc)
fromCNI, err := getPodCIDRFromCNI(clientset, restclient, restconfig, namespace)
if err == nil {
log.Infoln("get cidr from cni ok")
result = append(result, fromCNI...)
}
}
log.Infoln("get cidr from svc...")
pod, err := getPodCIDRFromPod(clientset, namespace, svc)
pod, err = getPodCIDRFromPod(clientset, namespace, svc)
if err == nil {
log.Infoln("get cidr from svc ok")
result = append(result, pod...)
}
result = Deduplicate(result)
if len(result) == 0 {
return nil, fmt.Errorf("can not get any cidr, please make sure you have prilivage")
err = fmt.Errorf("can not get any cidr, please make sure you have prilivage")
return
}
return
}
@@ -95,7 +98,7 @@ func GetCIDRFromResourceUgly(clientset *kubernetes.Clientset, namespace string)
serviceList, _ := clientset.CoreV1().Services(namespace).List(context.Background(), v1.ListOptions{})
for _, service := range serviceList.Items {
if ip := net.ParseIP(service.Spec.ClusterIP); ip != nil {
mask := net.CIDRMask(16, 32)
mask := net.CIDRMask(24, 32)
cidrs = append(cidrs, &net.IPNet{IP: ip.Mask(mask), Mask: mask})
}
}

View File

@@ -1,6 +0,0 @@
package util
const (
v4 = `(([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,})`
v6 = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/[0-9]{1,}`
)

View File

@@ -1,93 +1,68 @@
package util
import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
"net"
"regexp"
"strings"
"github.com/containernetworking/cni/libcni"
log "github.com/sirupsen/logrus"
"github.com/wencaiwulue/kubevpn/pkg/config"
v12 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/utils/pointer"
"sigs.k8s.io/yaml"
"github.com/wencaiwulue/kubevpn/pkg/config"
)
// root 22008 21846 14 Jan18 ? 6-22:53:35 kube-apiserver --advertise-address=10.56.95.185 --allow-privileged=true --anonymous-auth=True --apiserver-count=3 --authorization-mode=Node,RBAC --bind-address=0.0.0.0 --client-ca-file=/etc/kubernetes/ssl/ca.crt --default-not-ready-toleration-seconds=300 --default-unreachable-toleration-seconds=300 --enable-admission-plugins=NodeRestriction --enable-aggregator-routing=False --enable-bootstrap-token-auth=true --endpoint-reconciler-type=lease --etcd-cafile=/etc/ssl/etcd/ssl/ca.pem --etcd-certfile=/etc/ssl/etcd/ssl/node-kube-control-1.pem --etcd-keyfile=/etc/ssl/etcd/ssl/node-kube-control-1-key.pem --etcd-servers=https://10.56.95.185:2379,https://10.56.95.186:2379,https://10.56.95.187:2379 --etcd-servers-overrides=/events#https://10.56.95.185:2381;https://10.56.95.186:2381;https://10.56.95.187:2381 --event-ttl=1h0m0s --insecure-port=0 --kubelet-certificate-authority=/etc/kubernetes/ssl/kubelet/kubelet-ca.crt --kubelet-client-certificate=/etc/kubernetes/ssl/apiserver-kubelet-client.crt --kubelet-client-key=/etc/kubernetes/ssl/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalDNS,InternalIP,Hostname,ExternalDNS,ExternalIP --profiling=False --proxy-client-cert-file=/etc/kubernetes/ssl/front-proxy-client.crt --proxy-client-key-file=/etc/kubernetes/ssl/front-proxy-client.key --request-timeout=1m0s --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/etc/kubernetes/ssl/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=6443 --service-account-issuer=https://kubernetes.default.svc.cluster.local --service-account-key-file=/etc/kubernetes/ssl/sa.pub --service-account-signing-key-file=/etc/kubernetes/ssl/sa.key --service-cluster-ip-range=10.233.0.0/18 --service-node-port-range=30000-32767 --storage-backend=etcd3 --tls-cert-file=/etc/kubernetes/ssl/apiserver.crt --tls-private-key-file=/etc/kubernetes/ssl/apiserver.key
// ref: https://kubernetes.io/docs/concepts/services-networking/dual-stack/#configure-ipv4-ipv6-dual-stack
// get cidr by dump cluster info
func getCIDRByDumpClusterInfo(clientset *kubernetes.Clientset) (result []*net.IPNet, err error) {
p, err := clientset.CoreV1().Pods("kube-system").List(context.Background(), v1.ListOptions{
FieldSelector: fields.OneTermEqualSelector("status.phase", string(v12.PodRunning)).String(),
})
func getCIDRByDumpClusterInfo(clientset *kubernetes.Clientset) ([]*net.IPNet, error) {
podList, err := clientset.CoreV1().Pods(v1.NamespaceSystem).List(context.Background(), v1.ListOptions{})
if err != nil {
return nil, err
}
marshal, err := yaml.Marshal(p)
if err != nil {
return nil, err
}
svcCIDR := `service-cluster-ip-range`
podCIDR := `cluster-cidr`
reader := bufio.NewReader(bytes.NewBufferString(string(marshal)))
svc := sets.NewString()
v4P := regexp.MustCompile(v4)
v6P := regexp.MustCompile(v6)
for {
line, _, err := reader.ReadLine()
if err != nil {
break
}
if strings.Contains(string(line), svcCIDR) {
ipv4 := v4P.FindAllString(string(line), -1)
ipv6 := v6P.FindAllString(string(line), -1)
svc.Insert(ipv4...).Insert(ipv6...)
}
if strings.Contains(string(line), podCIDR) {
ipv4 := v4P.FindAllString(string(line), -1)
ipv6 := v6P.FindAllString(string(line), -1)
svc.Insert(ipv4...).Insert(ipv6...)
var list []string
for _, item := range podList.Items {
for _, container := range item.Spec.Containers {
list = append(list, container.Args...)
list = append(list, container.Command...)
}
}
for _, s := range svc.List() {
_, ipnet, err := net.ParseCIDR(s)
if err != nil {
result = append(result, ipnet)
}
var result []*net.IPNet
for _, s := range list {
result = append(result, parseCIDRFromString(s)...)
}
return result, nil
return Deduplicate(result), nil
}
// 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
func getCIDRFromCNI(clientset *kubernetes.Clientset, restclient *rest.RESTClient, restconfig *rest.Config, namespace string) ([]*net.IPNet, error) {
pod, err := createCIDRPod(clientset, namespace)
if err != nil {
return nil, err
}
var cmd = `grep -a -R "service-cluster-ip-range\|cluster-ip-range\|cluster-cidr" /etc/cni/proc/*/cmdline | grep -a -v grep`
var cmd = `grep -a -R "service-cluster-ip-range\|cluster-cidr" /etc/cni/proc/*/cmdline | grep -a -v grep | tr "\0" "\n"`
var result []*net.IPNet
content, err := Shell(clientset, restclient, restconfig, pod.Name, "", pod.Namespace, []string{"sh", "-c", cmd})
if err != nil {
return nil, err
}
result = parseCIDRFromString(content)
if len(result) == 0 {
return nil, fmt.Errorf("can not found any cidr")
for _, s := range strings.Split(content, "\n") {
result = Deduplicate(append(result, parseCIDRFromString(s)...))
}
return result, nil
@@ -113,30 +88,67 @@ func getServiceCIDRByCreateSvc(serviceInterface corev1.ServiceInterface) (*net.I
return nil, err
}
/*
*
{
"name": "cni0",
"cniVersion":"0.3.1",
"plugins":[
{
"datastore_type": "kubernetes",
"nodename": "10.56.95.185",
"type": "calico",
"log_level": "info",
"log_file_path": "/var/log/calico/cni/cni.log",
"ipam": {
"type": "calico-ipam",
"assign_ipv4": "true",
"ipv4_pools": ["10.233.64.0/18"]
},
"policy": {
"type": "k8s"
},
"kubernetes": {
"kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
}
},
{
"type":"portmap",
"capabilities": {
"portMappings": true
}
}
]
}
*/
func getPodCIDRFromCNI(clientset *kubernetes.Clientset, restclient *rest.RESTClient, restconfig *rest.Config, namespace string) ([]*net.IPNet, error) {
pod, err := createCIDRPod(clientset, namespace)
if err != nil {
return nil, err
}
//var cmd = "cat /etc/cni/net.d/*.conflist"
content, err := Shell(clientset, restclient, restconfig, pod.Name, "", pod.Namespace, []string{"cat", "/etc/cni/net.d/*.conflist"})
content, err := Shell(clientset, restclient, restconfig, config.CniNetName, "", namespace, []string{"cat", "/etc/cni/net.d/*.conflist"})
if err != nil {
return nil, err
}
conf, err := libcni.ConfListFromFile(content)
configList, err := libcni.ConfListFromBytes([]byte(content))
if err == nil {
log.Infoln("get cni config", conf.Name)
log.Infoln("get cni config", configList.Name)
}
var cidr []*net.IPNet
for _, plugin := range configList.Plugins {
switch plugin.Network.Type {
case "calico":
var m map[string]interface{}
_ = json.Unmarshal(plugin.Bytes, &m)
slice, _, _ := unstructured.NestedStringSlice(m, "ipam", "ipv4_pools")
for _, s := range slice {
if _, ipNet, _ := net.ParseCIDR(s); ipNet != nil {
cidr = append(cidr, ipNet)
}
}
}
}
result := parseCIDRFromString(content)
if len(result) == 0 {
return nil, fmt.Errorf("can not found any cidr")
}
return result, nil
return cidr, nil
}
func createCIDRPod(clientset *kubernetes.Clientset, namespace string) (*v12.Pod, error) {
@@ -240,6 +252,9 @@ func createCIDRPod(clientset *kubernetes.Clientset, namespace string) (*v12.Pod,
}
get, err := clientset.CoreV1().Pods(pod.Namespace).Get(context.Background(), pod.Name, v1.GetOptions{})
if errors.IsNotFound(err) || get.Status.Phase != v12.PodRunning {
if get.Status.Phase != v12.PodRunning {
_ = clientset.CoreV1().Pods(namespace).Delete(context.Background(), pod.Name, v1.DeleteOptions{GracePeriodSeconds: pointer.Int64(0)})
}
pod, err = clientset.CoreV1().Pods(namespace).Create(context.Background(), pod, v1.CreateOptions{})
if err != nil {
return nil, err
@@ -247,10 +262,19 @@ func createCIDRPod(clientset *kubernetes.Clientset, namespace string) (*v12.Pod,
err = WaitPod(clientset.CoreV1().Pods(namespace), v1.ListOptions{
FieldSelector: fields.OneTermEqualSelector("metadata.name", pod.Name).String(),
}, func(pod *v12.Pod) bool {
return pod.Status.Phase == v12.PodRunning
isRunning := pod.Status.Phase == v12.PodRunning
if !isRunning {
if message := PrintStatusInline(pod); message != "" {
fmt.Printf("%s\r", message)
}
}
return isRunning
})
if err != nil {
fmt.Printf("wait pod %s to be running timeout, reason %s, ignore\r\n", pod.Name, pod.Status.Reason)
return nil, err
} else {
fmt.Printf("\r")
}
}
return pod, nil
@@ -278,22 +302,29 @@ func getPodCIDRFromPod(clientset *kubernetes.Clientset, namespace string, svc *n
return nil, fmt.Errorf("can not found pod cidr from pod list")
}
/*
*
kube-apiserver:
--service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
kube-controller-manager:
--cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
--service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
--node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 defaults to /24 for IPv4 and /64 for IPv6
kube-proxy:
--cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
*/
func parseCIDRFromString(content string) (result []*net.IPNet) {
ipv4 := regexp.MustCompile(v4).FindAllString(content, -1)
ipv6 := regexp.MustCompile(v6).FindAllString(content, -1)
for _, s := range ipv4 {
_, ipNet, err := net.ParseCIDR(s)
if err == nil {
result = append(result, ipNet)
if strings.Contains(content, "cluster-cidr") || strings.Contains(content, "service-cluster-ip-range") {
split := strings.Split(content, "=")
if len(split) == 2 {
cidrList := split[1]
for _, cidr := range strings.Split(cidrList, ",") {
_, c, err := net.ParseCIDR(cidr)
if err == nil {
result = append(result, c)
}
}
}
}
for _, s := range ipv6 {
_, ipNet, err := net.ParseCIDR(s)
if err == nil {
result = append(result, ipNet)
}
}
return result
return
}

View File

@@ -1,6 +1,7 @@
package util
import (
"bytes"
"fmt"
"io"
"text/tabwriter"
@@ -30,6 +31,28 @@ func PrintStatus(pod *corev1.Pod, writer io.Writer) {
}
}
func PrintStatusInline(pod *corev1.Pod) string {
var sb = bytes.NewBuffer(nil)
w := tabwriter.NewWriter(sb, 1, 1, 1, ' ', 0)
show := func(v1, v2 any) {
_, _ = fmt.Fprintf(w, "%v\t\t%v", v1, v2)
}
for _, status := range pod.Status.ContainerStatuses {
if status.State.Waiting != nil {
show(status.State.Waiting.Reason, status.State.Waiting.Message)
}
if status.State.Running != nil {
show("ContainerRunning", "")
}
if status.State.Terminated != nil {
show(status.State.Terminated.Reason, status.State.Terminated.Message)
}
}
_ = w.Flush()
return sb.String()
}
func max[T constraints.Ordered](a T, b T) T {
if a > b {
return a

View File

@@ -45,6 +45,7 @@ import (
"k8s.io/kubectl/pkg/cmd/util"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/polymorphichelpers"
"k8s.io/kubectl/pkg/util/podutils"
"github.com/wencaiwulue/kubevpn/pkg/config"
)
@@ -76,7 +77,7 @@ func GetAvailableTCPPortOrDie() int {
}
func WaitPod(podInterface v12.PodInterface, list metav1.ListOptions, checker func(*v1.Pod) bool) error {
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*10)
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*60)
defer cancelFunc()
w, err := podInterface.Watch(ctx, list)
if err != nil {
@@ -150,12 +151,12 @@ func GetTopOwnerReference(factory cmdutil.Factory, namespace, workload string) (
}
// GetTopOwnerReferenceBySelector assume pods, controller has same labels
func GetTopOwnerReferenceBySelector(factory cmdutil.Factory, namespace, selector string) (sets.String, error) {
func GetTopOwnerReferenceBySelector(factory cmdutil.Factory, namespace, selector string) (sets.Set[string], error) {
object, err := GetUnstructuredObjectBySelector(factory, namespace, selector)
if err != nil {
return nil, err
}
set := sets.NewString()
set := sets.New[string]()
for _, info := range object {
reference, err := GetTopOwnerReference(factory, namespace, fmt.Sprintf("%s/%s", info.Mapping.Resource.GroupResource().String(), info.Name))
if err == nil && reference.Mapping.Resource.Resource != "services" {
@@ -594,3 +595,27 @@ func IsIPv4(packet []byte) bool {
func IsIPv6(packet []byte) bool {
return 6 == (packet[0] >> 4)
}
func Deduplicate(cidr []*net.IPNet) (result []*net.IPNet) {
var set = sets.New[string]()
for _, ipNet := range cidr {
if !set.Has(ipNet.String()) {
result = append(result, ipNet)
}
set.Insert(ipNet.String())
}
return
}
func AllContainerIsRunning(pod *v1.Pod) bool {
isReady := podutils.IsPodReady(pod)
if !isReady {
return false
}
for _, status := range pod.Status.ContainerStatuses {
if !status.Ready {
return false
}
}
return true
}

View File

@@ -1,9 +1,12 @@
package util
import (
"fmt"
"regexp"
"encoding/json"
"testing"
"github.com/containernetworking/cni/libcni"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
func TestName(t *testing.T) {
@@ -41,8 +44,16 @@ func TestName(t *testing.T) {
`
// IPv6 with CIDR
compile := regexp.MustCompile(`(([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,})`)
v6 := regexp.MustCompile(`(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/[0-9]{1,}`)
fmt.Println(compile.FindAllString(s, -1))
fmt.Println(v6.FindAllString(s, -1))
configList, err := libcni.ConfListFromBytes([]byte(s))
if err == nil {
log.Infoln("get cni config", configList.Name)
}
for _, plugin := range configList.Plugins {
var m map[string]interface{}
_ = json.Unmarshal(plugin.Bytes, &m)
slice, _, _ := unstructured.NestedStringSlice(m, "ipam", "ipv4_pools")
for _, i := range slice {
println(i)
}
}
}