refactor: use informer to list&watch pod&service ip for adding to route table (#610)

This commit is contained in:
naison
2025-05-23 10:09:06 +08:00
committed by GitHub
parent 6d545dc5c9
commit 75c609211b
5 changed files with 280 additions and 332 deletions

View File

@@ -17,12 +17,8 @@ import (
miekgdns "github.com/miekg/dns" miekgdns "github.com/miekg/dns"
"github.com/pkg/errors" "github.com/pkg/errors"
v12 "k8s.io/api/core/v1" v12 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/tools/cache"
v13 "k8s.io/client-go/kubernetes/typed/core/v1"
"tailscale.com/net/dns" "tailscale.com/net/dns"
"github.com/wencaiwulue/kubevpn/v2/pkg/config" "github.com/wencaiwulue/kubevpn/v2/pkg/config"
@@ -31,10 +27,11 @@ import (
) )
type Config struct { type Config struct {
Config *miekgdns.ClientConfig Config *miekgdns.ClientConfig
Ns []string Ns []string
Services []v12.Service Services []v12.Service
TunName string SvcInformer cache.SharedIndexInformer
TunName string
Hosts []Entry Hosts []Entry
Lock *sync.Mutex Lock *sync.Mutex
@@ -45,22 +42,8 @@ type Config struct {
OSConfigurator dns.OSConfigurator OSConfigurator dns.OSConfigurator
} }
func (c *Config) AddServiceNameToHosts(ctx context.Context, serviceInterface v13.ServiceInterface, hosts ...Entry) error { func (c *Config) AddServiceNameToHosts(ctx context.Context, hosts ...Entry) error {
var serviceList []v12.Service var serviceList []v12.Service
//listOptions := v1.ListOptions{Limit: 100}
//for {
// services, err := serviceInterface.List(ctx, listOptions)
// if err != nil {
// break
// }
// serviceList = append(serviceList, services.Items...)
// if services.Continue != "" {
// listOptions.Continue = services.Continue
// } else {
// break
// }
//}
c.Lock.Lock() c.Lock.Lock()
defer c.Lock.Unlock() defer c.Lock.Unlock()
@@ -71,100 +54,65 @@ func (c *Config) AddServiceNameToHosts(ctx context.Context, serviceInterface v13
return err return err
} }
go c.watchServiceToAddHosts(ctx, serviceInterface, hosts) go c.watchServiceToAddHosts(ctx, hosts)
return nil return nil
} }
func (c *Config) watchServiceToAddHosts(ctx context.Context, serviceInterface v13.ServiceInterface, hosts []Entry) { func (c *Config) watchServiceToAddHosts(ctx context.Context, hosts []Entry) {
defer util.HandleCrash() defer util.HandleCrash()
ticker := time.NewTicker(time.Second * 15) ticker := time.NewTicker(time.Second * 15)
defer ticker.Stop() defer ticker.Stop()
immediate := make(chan struct{}, 1) _, err := c.SvcInformer.AddEventHandler(cache.FilteringResourceEventHandler{
immediate <- struct{}{} FilterFunc: func(obj interface{}) bool {
var ErrChanDone = errors.New("watch service chan done") if svc, ok := obj.(*v12.Service); ok && svc.Namespace == c.Ns[0] {
for ctx.Err() == nil { return true
err := func() error { } else {
w, err := serviceInterface.Watch(ctx, v1.ListOptions{Watch: true}) return false
if err != nil {
return err
} }
defer w.Stop() },
for { Handler: cache.ResourceEventHandlerFuncs{
select { AddFunc: func(obj interface{}) {
case <-ctx.Done(): ticker.Reset(time.Second * 3)
return ctx.Err() },
case event, ok := <-w.ResultChan(): UpdateFunc: func(oldObj, newObj interface{}) {
if !ok { ticker.Reset(time.Second * 3)
return ErrChanDone },
} DeleteFunc: func(obj interface{}) {
svc, ok := event.Object.(*v12.Service) ticker.Reset(time.Second * 3)
if !ok { },
continue },
} })
if ctx.Err() != nil { if err != nil {
return ctx.Err() plog.G(ctx).Errorf("Failed to add service event handler: %v", err)
} return
if event.Type == watch.Deleted { }
if net.ParseIP(svc.Spec.ClusterIP) == nil { for ; ctx.Err() == nil; <-ticker.C {
continue ticker.Reset(time.Second * 15)
} serviceList, err := c.SvcInformer.GetIndexer().ByIndex(cache.NamespaceIndex, c.Ns[0])
var list = []Entry{{ if err != nil {
IP: svc.Spec.ClusterIP, plog.G(ctx).Errorf("Failed to list service by namespace %s: %v", c.Ns[0], err)
Domain: svc.Name, continue
}} }
err = c.removeHosts(list) var services []v12.Service
if err != nil { for _, service := range serviceList {
plog.G(ctx).Errorf("Failed to remove hosts(%s) to hosts: %v", entryList2String(list), err) svc, ok := service.(*v12.Service)
} if !ok {
} continue
if event.Type == watch.Added {
c.Lock.Lock()
appendHosts := c.generateAppendHosts([]v12.Service{*svc}, hosts)
err = c.appendHosts(appendHosts)
c.Lock.Unlock()
if err != nil {
plog.G(ctx).Errorf("Failed to add hosts(%s) to hosts: %v", entryList2String(appendHosts), err)
}
}
case <-ticker.C:
var list *v12.ServiceList
list, err = serviceInterface.List(ctx, v1.ListOptions{})
if err != nil {
continue
}
c.Lock.Lock()
appendHosts := c.generateAppendHosts(list.Items, hosts)
err = c.appendHosts(appendHosts)
c.Lock.Unlock()
if err != nil {
plog.G(ctx).Errorf("Failed to add hosts(%s) to hosts: %v", entryList2String(appendHosts), err)
}
case <-immediate:
var list *v12.ServiceList
list, err = serviceInterface.List(ctx, v1.ListOptions{})
if err != nil {
continue
}
c.Lock.Lock()
appendHosts := c.generateAppendHosts(list.Items, hosts)
err = c.appendHosts(appendHosts)
c.Lock.Unlock()
if err != nil {
plog.G(ctx).Errorf("Failed to add hosts(%s) to hosts: %v", entryList2String(appendHosts), err)
}
}
} }
}() services = append(services, *svc)
}
if len(services) == 0 {
continue
}
if ctx.Err() != nil { if ctx.Err() != nil {
return return
} }
if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, ErrChanDone) { c.Lock.Lock()
plog.G(ctx).Debugf("Failed to watch service to add route table: %v", err) appendHosts := c.generateAppendHosts(services, hosts)
} err = c.appendHosts(appendHosts)
if utilnet.IsConnectionRefused(err) || apierrors.IsTooManyRequests(err) || apierrors.IsForbidden(err) { c.Lock.Unlock()
time.Sleep(time.Second * 1) if err != nil && !errors.Is(err, context.Canceled) {
} else { plog.G(ctx).Errorf("Failed to add hosts(%s) to hosts: %v", entryList2String(appendHosts), err)
time.Sleep(time.Millisecond * 200)
} }
} }
} }

View File

@@ -6,10 +6,10 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"slices"
"strings" "strings"
"time" "time"
@@ -17,6 +17,10 @@ import (
miekgdns "github.com/miekg/dns" miekgdns "github.com/miekg/dns"
v12 "k8s.io/api/core/v1" v12 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/cache"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
"github.com/wencaiwulue/kubevpn/v2/pkg/util"
) )
// https://github.com/golang/go/issues/12524 // https://github.com/golang/go/issues/12524
@@ -32,6 +36,59 @@ var resolv = "/etc/resolv.conf"
// service.namespace.svc.cluster:port // service.namespace.svc.cluster:port
// service.namespace.svc.cluster.local:port // service.namespace.svc.cluster.local:port
func (c *Config) SetupDNS(ctx context.Context) error { func (c *Config) SetupDNS(ctx context.Context) error {
defer util.HandleCrash()
ticker := time.NewTicker(time.Second * 15)
_, err := c.SvcInformer.AddEventHandler(cache.FilteringResourceEventHandler{
FilterFunc: func(obj interface{}) bool {
if svc, ok := obj.(*v12.Service); ok && svc.Namespace == c.Ns[0] {
return true
} else {
return false
}
},
Handler: cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
ticker.Reset(time.Second * 3)
},
UpdateFunc: func(oldObj, newObj interface{}) {
ticker.Reset(time.Second * 3)
},
DeleteFunc: func(obj interface{}) {
ticker.Reset(time.Second * 3)
},
},
})
if err != nil {
plog.G(ctx).Errorf("Failed to add service event handler: %v", err)
return err
}
go func() {
defer ticker.Stop()
for ; ctx.Err() == nil; <-ticker.C {
ticker.Reset(time.Second * 15)
serviceList, err := c.SvcInformer.GetIndexer().ByIndex(cache.NamespaceIndex, c.Ns[0])
if err != nil {
plog.G(ctx).Errorf("Failed to list service by namespace %s: %v", c.Ns[0], err)
continue
}
var services []v12.Service
for _, service := range serviceList {
svc, ok := service.(*v12.Service)
if !ok {
continue
}
services = append(services, *svc)
}
if len(services) == 0 {
continue
}
if ctx.Err() != nil {
return
}
c.Services = services
c.usingResolver(ctx)
}
}()
c.usingResolver(ctx) c.usingResolver(ctx)
return nil return nil
} }
@@ -72,6 +129,9 @@ func (c *Config) usingResolver(ctx context.Context) {
plog.G(ctx).Errorf("Parse resolver %s error: %v", filename, err) plog.G(ctx).Errorf("Parse resolver %s error: %v", filename, err)
continue continue
} }
if slices.Contains(conf.Servers, clientConfig.Servers[0]) {
continue
}
// insert current name server to first location // insert current name server to first location
conf.Servers = append([]string{clientConfig.Servers[0]}, conf.Servers...) conf.Servers = append([]string{clientConfig.Servers[0]}, conf.Servers...)
err = os.WriteFile(filename, []byte(toString(*conf)), 0644) err = os.WriteFile(filename, []byte(toString(*conf)), 0644)

View File

@@ -32,15 +32,16 @@ import (
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
pkgruntime "k8s.io/apimachinery/pkg/runtime" pkgruntime "k8s.io/apimachinery/pkg/runtime"
pkgtypes "k8s.io/apimachinery/pkg/types" pkgtypes "k8s.io/apimachinery/pkg/types"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/cli-runtime/pkg/resource" "k8s.io/cli-runtime/pkg/resource"
runtimeresource "k8s.io/cli-runtime/pkg/resource" runtimeresource "k8s.io/cli-runtime/pkg/resource"
informerv1 "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
v2 "k8s.io/client-go/kubernetes/typed/networking/v1" v2 "k8s.io/client-go/kubernetes/typed/networking/v1"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/retry" "k8s.io/client-go/util/retry"
"k8s.io/kubectl/pkg/cmd/set" "k8s.io/kubectl/pkg/cmd/set"
cmdutil "k8s.io/kubectl/pkg/cmd/util" cmdutil "k8s.io/kubectl/pkg/cmd/util"
@@ -280,13 +281,14 @@ func (c *ConnectOptions) DoConnect(ctx context.Context, isLite bool, stopChan <-
return return
} }
plog.G(ctx).Infof("Adding Pod IP and Service IP to route table...") plog.G(ctx).Infof("Adding Pod IP and Service IP to route table...")
if err = c.addRouteDynamic(c.ctx); err != nil { var svcInformer cache.SharedIndexInformer
if svcInformer, _, err = c.addRouteDynamic(c.ctx); err != nil {
plog.G(ctx).Errorf("Add route dynamic failed: %v", err) plog.G(ctx).Errorf("Add route dynamic failed: %v", err)
return return
} }
go c.deleteFirewallRule(c.ctx) go c.deleteFirewallRule(c.ctx)
plog.G(ctx).Infof("Configuring DNS service...") plog.G(ctx).Infof("Configuring DNS service...")
if err = c.setupDNS(c.ctx); err != nil { if err = c.setupDNS(c.ctx, svcInformer); err != nil {
plog.G(ctx).Errorf("Configure DNS failed: %v", err) plog.G(ctx).Errorf("Configure DNS failed: %v", err)
return return
} }
@@ -490,57 +492,116 @@ func (c *ConnectOptions) startLocalTunServer(ctx context.Context, forwardAddress
} }
// Listen all pod, add route if needed // Listen all pod, add route if needed
func (c *ConnectOptions) addRouteDynamic(ctx context.Context) error { func (c *ConnectOptions) addRouteDynamic(ctx context.Context) (cache.SharedIndexInformer, cache.SharedIndexInformer, error) {
podNs, svcNs, err1 := util.GetNsForListPodAndSvc(ctx, c.clientset, []string{v1.NamespaceAll, c.OriginNamespace}) podNs, svcNs, err := util.GetNsForListPodAndSvc(ctx, c.clientset, []string{v1.NamespaceAll, c.OriginNamespace})
if err1 != nil { if err != nil {
return err1 return nil, nil, err
} }
conf := rest.CopyConfig(c.config)
conf.QPS = 1
conf.Burst = 2
clientSet, err := kubernetes.NewForConfig(conf)
if err != nil {
plog.G(ctx).Errorf("Failed to create clientset: %v", err)
return nil, nil, err
}
svcIndexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
svcInformer := informerv1.NewServiceInformer(clientSet, svcNs, 0, svcIndexers)
svcTicker := time.NewTicker(time.Second * 15)
_, err = svcInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
svcTicker.Reset(time.Second * 3)
},
UpdateFunc: func(oldObj, newObj interface{}) {
svcTicker.Reset(time.Second * 3)
},
DeleteFunc: func(obj interface{}) {
svcTicker.Reset(time.Second * 3)
},
})
if err != nil {
plog.G(ctx).Errorf("Failed to add service event handler: %v", err)
return nil, nil, err
}
go svcInformer.Run(ctx.Done())
go func() { go func() {
var listDone bool defer svcTicker.Stop()
for ctx.Err() == nil { for ; ctx.Err() == nil; <-svcTicker.C {
err := func() error { svcTicker.Reset(time.Second * 15)
if !listDone { serviceList := svcInformer.GetIndexer().List()
err := util.ListService(ctx, c.clientset.CoreV1().Services(svcNs), c.addRoute) var ips = sets.New[string]()
if err != nil { for _, service := range serviceList {
return err svc, ok := service.(*v1.Service)
} if !ok {
listDone = true continue
} }
err := util.WatchServiceToAddRoute(ctx, c.clientset.CoreV1().Services(svcNs), c.addRoute) ips.Insert(svc.Spec.ClusterIP)
return err ips.Insert(svc.Spec.ClusterIPs...)
}() }
if utilnet.IsConnectionRefused(err) || apierrors.IsTooManyRequests(err) || apierrors.IsForbidden(err) { if ctx.Err() != nil {
time.Sleep(time.Second * 10) return
} else { }
time.Sleep(time.Second * 2) if ips.Len() == 0 {
continue
}
err := c.addRoute(ips.UnsortedList()...)
if err != nil {
plog.G(ctx).Debugf("Add service IP to route table failed: %v", err)
} }
} }
}() }()
podIndexers := cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}
podInformer := informerv1.NewPodInformer(clientSet, podNs, 0, podIndexers)
podTicker := time.NewTicker(time.Second * 15)
_, err = podInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
podTicker.Reset(time.Second * 3)
},
UpdateFunc: func(oldObj, newObj interface{}) {
podTicker.Reset(time.Second * 3)
},
DeleteFunc: func(obj interface{}) {
podTicker.Reset(time.Second * 3)
},
})
if err != nil {
plog.G(ctx).Errorf("Failed to add service event handler: %v", err)
return nil, nil, err
}
go podInformer.Run(ctx.Done())
go func() { go func() {
var listDone bool defer podTicker.Stop()
for ctx.Err() == nil { for ; ctx.Err() == nil; <-podTicker.C {
err := func() error { podTicker.Reset(time.Second * 15)
if !listDone { podList := podInformer.GetIndexer().List()
err := util.ListPod(ctx, c.clientset.CoreV1().Pods(podNs), c.addRoute) var ips = sets.New[string]()
if err != nil { for _, pod := range podList {
return err p, ok := pod.(*v1.Pod)
} if !ok {
listDone = true continue
} }
err := util.WatchPodToAddRoute(ctx, c.clientset.CoreV1().Pods(podNs), c.addRoute) if p.Spec.HostNetwork {
return err continue
}() }
if utilnet.IsConnectionRefused(err) || apierrors.IsTooManyRequests(err) || apierrors.IsForbidden(err) { ips.Insert(util.GetPodIP(*p)...)
time.Sleep(time.Second * 10) }
} else { if ctx.Err() != nil {
time.Sleep(time.Second * 2) return
}
if ips.Len() == 0 {
continue
}
err := c.addRoute(ips.UnsortedList()...)
if err != nil {
plog.G(ctx).Debugf("Add pod IP to route table failed: %v", err)
} }
} }
}() }()
return nil return svcInformer, podInformer, nil
} }
func (c *ConnectOptions) addRoute(ipStrList ...string) error { func (c *ConnectOptions) addRoute(ipStrList ...string) error {
@@ -548,6 +609,7 @@ func (c *ConnectOptions) addRoute(ipStrList ...string) error {
return nil return nil
} }
var routes []types.Route var routes []types.Route
r, _ := netroute.New()
for _, ipStr := range ipStrList { for _, ipStr := range ipStrList {
ip := net.ParseIP(ipStr) ip := net.ParseIP(ipStr)
if ip == nil { if ip == nil {
@@ -570,7 +632,7 @@ func (c *ConnectOptions) addRoute(ipStrList ...string) error {
} else { } else {
mask = net.CIDRMask(128, 128) mask = net.CIDRMask(128, 128)
} }
if r, err := netroute.New(); err == nil { if r != nil {
ifi, _, _, err := r.Route(ip) ifi, _, _, err := r.Route(ip)
if err == nil && ifi.Name == c.tunName { if err == nil && ifi.Name == c.tunName {
continue continue
@@ -578,6 +640,9 @@ func (c *ConnectOptions) addRoute(ipStrList ...string) error {
} }
routes = append(routes, types.Route{Dst: net.IPNet{IP: ip, Mask: mask}}) routes = append(routes, types.Route{Dst: net.IPNet{IP: ip, Mask: mask}})
} }
if len(routes) == 0 {
return nil
}
err := tun.AddRoutes(c.tunName, routes...) err := tun.AddRoutes(c.tunName, routes...)
return err return err
} }
@@ -600,7 +665,7 @@ func (c *ConnectOptions) deleteFirewallRule(ctx context.Context) {
util.DeleteBlockFirewallRule(ctx) util.DeleteBlockFirewallRule(ctx)
} }
func (c *ConnectOptions) setupDNS(ctx context.Context) error { func (c *ConnectOptions) setupDNS(ctx context.Context, svcInformer cache.SharedIndexInformer) error {
const portTCP = 10800 const portTCP = 10800
podList, err := c.GetRunningPodList(ctx) podList, err := c.GetRunningPodList(ctx)
if err != nil { if err != nil {
@@ -652,19 +717,14 @@ func (c *ConnectOptions) setupDNS(ctx context.Context) error {
} }
plog.G(ctx).Infof("Listing namespace %s services...", c.OriginNamespace) plog.G(ctx).Infof("Listing namespace %s services...", c.OriginNamespace)
var serviceList []v1.Service
services, err := c.clientset.CoreV1().Services(c.OriginNamespace).List(ctx, metav1.ListOptions{})
if err == nil {
serviceList = append(serviceList, services.Items...)
}
c.dnsConfig = &dns.Config{ c.dnsConfig = &dns.Config{
Config: relovConf, Config: relovConf,
Ns: ns, Ns: ns,
Services: serviceList, Services: []v1.Service{},
TunName: c.tunName, SvcInformer: svcInformer,
Hosts: c.extraHost, TunName: c.tunName,
Lock: c.Lock, Hosts: c.extraHost,
Lock: c.Lock,
HowToGetExternalName: func(domain string) (string, error) { HowToGetExternalName: func(domain string) (string, error) {
podList, err := c.GetRunningPodList(ctx) podList, err := c.GetRunningPodList(ctx)
if err != nil { if err != nil {
@@ -688,7 +748,7 @@ func (c *ConnectOptions) setupDNS(ctx context.Context) error {
} }
plog.G(ctx).Infof("Dump service in namespace %s into hosts...", c.OriginNamespace) plog.G(ctx).Infof("Dump service in namespace %s into hosts...", c.OriginNamespace)
// dump service in current namespace for support DNS resolve service:port // dump service in current namespace for support DNS resolve service:port
err = c.dnsConfig.AddServiceNameToHosts(ctx, c.clientset.CoreV1().Services(c.OriginNamespace), c.extraHost...) err = c.dnsConfig.AddServiceNameToHosts(ctx, c.extraHost...)
return err return err
} }

View File

@@ -11,12 +11,15 @@ import (
"strings" "strings"
"unsafe" "unsafe"
errors2 "github.com/pkg/errors"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/kubernetes"
v12 "k8s.io/client-go/kubernetes/typed/core/v1" v12 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
@@ -26,6 +29,7 @@ import (
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
"github.com/wencaiwulue/kubevpn/v2/pkg/config" "github.com/wencaiwulue/kubevpn/v2/pkg/config"
"github.com/wencaiwulue/kubevpn/v2/pkg/log"
) )
func GetClusterID(ctx context.Context, client v12.ConfigMapInterface) (types.UID, error) { func GetClusterID(ctx context.Context, client v12.ConfigMapInterface) (types.UID, error) {
@@ -259,3 +263,48 @@ func GetKubeconfigPath(factory cmdutil.Factory) (string, error) {
} }
return file, nil return file, nil
} }
func GetNsForListPodAndSvc(ctx context.Context, clientset *kubernetes.Clientset, nsList []string) (podNs string, svcNs string, err error) {
for _, ns := range nsList {
_, err = clientset.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{Limit: 1})
if errors.IsForbidden(err) {
continue
}
if err != nil {
return
}
podNs = ns
break
}
if err != nil {
err = errors2.Wrap(err, "can not list pod to add it to route table")
return
}
if podNs == "" {
log.G(ctx).Debugf("List all namepsace pods")
} else {
log.G(ctx).Debugf("List namepsace %s pods", podNs)
}
for _, ns := range nsList {
_, err = clientset.CoreV1().Services(ns).List(ctx, metav1.ListOptions{Limit: 1})
if errors.IsForbidden(err) {
continue
}
if err != nil {
return
}
svcNs = ns
break
}
if err != nil {
err = errors2.Wrap(err, "can not list service to add it to route table")
return
}
if svcNs == "" {
log.G(ctx).Debugf("List all namepsace services")
} else {
log.G(ctx).Debugf("List namepsace %s services", svcNs)
}
return
}

View File

@@ -1,169 +0,0 @@
package util
import (
"context"
"fmt"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
v12 "k8s.io/client-go/kubernetes/typed/core/v1"
plog "github.com/wencaiwulue/kubevpn/v2/pkg/log"
)
func GetNsForListPodAndSvc(ctx context.Context, clientset *kubernetes.Clientset, nsList []string) (podNs string, svcNs string, err error) {
for _, ns := range nsList {
_, err = clientset.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{Limit: 1})
if apierrors.IsForbidden(err) {
continue
}
if err != nil {
return
}
podNs = ns
break
}
if err != nil {
err = errors.Wrap(err, "can not list pod to add it to route table")
return
}
if podNs == "" {
plog.G(ctx).Debugf("List all namepsace pods")
} else {
plog.G(ctx).Debugf("List namepsace %s pods", podNs)
}
for _, ns := range nsList {
_, err = clientset.CoreV1().Services(ns).List(ctx, metav1.ListOptions{Limit: 1})
if apierrors.IsForbidden(err) {
continue
}
if err != nil {
return
}
svcNs = ns
break
}
if err != nil {
err = errors.Wrap(err, "can not list service to add it to route table")
return
}
if svcNs == "" {
plog.G(ctx).Debugf("List all namepsace services")
} else {
plog.G(ctx).Debugf("List namepsace %s services", svcNs)
}
return
}
func ListService(ctx context.Context, lister v12.ServiceInterface, addRouteFunc func(ipStr ...string) error) error {
opts := metav1.ListOptions{Limit: 100, Continue: ""}
for {
serviceList, err := lister.List(ctx, opts)
if err != nil {
return err
}
var ips []string
for _, service := range serviceList.Items {
ips = append(ips, service.Spec.ClusterIP)
}
err = addRouteFunc(ips...)
if err != nil {
plog.G(ctx).Errorf("Failed to add service IP to route table: %v", err)
}
if serviceList.Continue == "" {
return nil
}
opts.Continue = serviceList.Continue
}
}
func WatchServiceToAddRoute(ctx context.Context, watcher v12.ServiceInterface, routeFunc func(ipStr ...string) error) error {
defer func() {
if er := recover(); er != nil {
plog.G(ctx).Error(er)
}
}()
w, err := watcher.Watch(ctx, metav1.ListOptions{Watch: true})
if err != nil {
return err
}
defer w.Stop()
for {
select {
case <-ctx.Done():
return nil
case e, ok := <-w.ResultChan():
if !ok {
return errors.New("watch service chan done")
}
var svc *v1.Service
svc, ok = e.Object.(*v1.Service)
if !ok {
continue
}
_ = routeFunc(svc.Spec.ClusterIP)
}
}
}
func ListPod(ctx context.Context, lister v12.PodInterface, addRouteFunc func(ipStr ...string) error) error {
opts := metav1.ListOptions{Limit: 100, Continue: ""}
for {
podList, err := lister.List(ctx, opts)
if err != nil {
return err
}
var ips []string
for _, pod := range podList.Items {
if pod.Spec.HostNetwork {
continue
}
ips = append(ips, pod.Status.PodIP)
}
err = addRouteFunc(ips...)
if err != nil {
plog.G(ctx).Errorf("Failed to add Pod IP to route table: %v", err)
}
if podList.Continue == "" {
return nil
}
opts.Continue = podList.Continue
}
}
func WatchPodToAddRoute(ctx context.Context, watcher v12.PodInterface, addRouteFunc func(ipStrList ...string) error) error {
defer func() {
if er := recover(); er != nil {
plog.G(ctx).Errorln(er)
}
}()
w, err := watcher.Watch(ctx, metav1.ListOptions{Watch: true})
if err != nil {
return err
}
defer w.Stop()
for {
select {
case <-ctx.Done():
return nil
case e, ok := <-w.ResultChan():
if !ok {
return fmt.Errorf("watch pod chan done")
}
var pod *v1.Pod
pod, ok = e.Object.(*v1.Pod)
if !ok {
continue
}
if pod.Spec.HostNetwork {
continue
}
ip := pod.Status.PodIP
_ = addRouteFunc(ip)
}
}
}