mirror of
https://github.com/kubenetworks/kubevpn.git
synced 2025-09-27 03:36:09 +08:00
refactor: use informer to list&watch pod&service ip for adding to route table (#610)
This commit is contained in:
162
pkg/dns/dns.go
162
pkg/dns/dns.go
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user