mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/kube-vip/kube-vip.git
synced 2026-09-20 08:03:47 +08:00
Serialize per-Service state behind UID locks, order events per Service, and make readiness and watcher ownership generation-aware. Coordinate shared lease membership so cleanup cannot cancel a recreated Service, drain cluster workers before restart, and start the shutdown watcher before slow startup calls. Signed-off-by: Marcel Fest <marcel.fest@telekom.de>
118 lines
3.8 KiB
Go
118 lines
3.8 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
|
|
log "log/slog"
|
|
|
|
"github.com/kube-vip/kube-vip/pkg/debouncer"
|
|
"github.com/kube-vip/kube-vip/pkg/endpoints"
|
|
"github.com/kube-vip/kube-vip/pkg/endpoints/providers"
|
|
"github.com/kube-vip/kube-vip/pkg/instance"
|
|
"github.com/kube-vip/kube-vip/pkg/servicecontext"
|
|
"github.com/kube-vip/kube-vip/pkg/utils"
|
|
v1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/watch"
|
|
)
|
|
|
|
func (p *Processor) watchEndpoint(svcCtx *servicecontext.Context, id string, service *v1.Service,
|
|
provider providers.Provider, cancelWatcher context.CancelCauseFunc) error {
|
|
log.Info("watching", "provider", provider.GetLabel(), "service_name", service.Name, "namespace", service.Namespace)
|
|
// Use a restartable watcher, as this should help in the event of etcd or timeout issues
|
|
|
|
rw, err := provider.CreateRetryWatcher(svcCtx.Ctx, p.rwClientSet, service)
|
|
if err != nil {
|
|
if svcCtx.Ctx.Err() != nil {
|
|
return nil
|
|
}
|
|
return utils.WrapPanicError(err, "[%s] error watching endpoints", provider.GetLabel())
|
|
}
|
|
|
|
d, err := debouncer.New(rw.ResultChan(), p.config.DebounceTime)
|
|
if err != nil {
|
|
rw.Stop()
|
|
return utils.WrapPanicError(err, "failed to create debouncer for endpoints event")
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
stopChan := make(chan any)
|
|
|
|
defer func() {
|
|
close(stopChan)
|
|
if d != nil {
|
|
d.Stop()
|
|
}
|
|
wg.Wait()
|
|
}()
|
|
|
|
wg.Go(func() {
|
|
if d != nil {
|
|
if err := d.Start(svcCtx.Ctx); err != nil {
|
|
log.Error("[endpoint watcher] debouncer, cancelling context", "error", err.Error())
|
|
if svcCtx.Ctx.Err() == nil {
|
|
cancelWatcher(utils.WrapPanicError(err, "[%s] endpoint debouncer failed", provider.GetLabel()))
|
|
}
|
|
svcCtx.Cancel()
|
|
}
|
|
}
|
|
select {
|
|
case <-svcCtx.Ctx.Done():
|
|
log.Debug("[endpoint watcher] context cancelled", "provider", provider.GetLabel())
|
|
rw.Stop()
|
|
case <-stopChan:
|
|
svcCtx.Cancel()
|
|
log.Debug("[endpoint watcher] exiting endpoint watcher", "namespace", service.Namespace, "service", service.Name, "provider", provider.GetLabel())
|
|
}
|
|
})
|
|
|
|
epProcessor := endpoints.NewEndpointProcessor(p.config, provider, p.bgpServer, &p.ServiceInstances, &p.instancesMutex, p.leaseMgr, p.TunnelMgr, p.routeMgr, p.lockService)
|
|
|
|
ch := rw.ResultChan()
|
|
if d != nil {
|
|
ch = d.Output()
|
|
}
|
|
|
|
var lastKnownGoodEndpoint string
|
|
for event := range ch {
|
|
// We need to inspect the event and get ResourceVersion out of it
|
|
switch event.Type {
|
|
|
|
case watch.Added, watch.Modified, watch.Deleted:
|
|
if event.Type == watch.Deleted {
|
|
log.Info("[endpoint watcher] endpoint object deleted", "provider", provider.GetLabel(), "service name", service.Name, "namespace", service.Namespace)
|
|
}
|
|
|
|
restart, err := epProcessor.Reconcile(svcCtx, event, &lastKnownGoodEndpoint, service, id,
|
|
&wg, p.clientSet, func(ctx context.Context, service *v1.Service, inst *instance.Instance) error {
|
|
return p.updateEgressConfiguration(ctx, service, inst)
|
|
})
|
|
if restart {
|
|
continue
|
|
}
|
|
if err != nil {
|
|
eventErr := fmt.Errorf("[%s] error while processing %s event: %w", provider.GetLabel(), event.Type, err)
|
|
if utils.IsPanicError(err) {
|
|
return eventErr
|
|
}
|
|
log.Error("endpoint watcher event failed", "provider", provider.GetLabel(), "type", event.Type, "error", eventErr)
|
|
continue
|
|
}
|
|
|
|
case watch.Error:
|
|
if svcCtx.Ctx.Err() != nil {
|
|
return nil
|
|
}
|
|
watchErr := utils.WatchError(event.Object)
|
|
log.Error("watch error", "provider", provider.GetLabel(), "err", watchErr)
|
|
return utils.WrapPanicError(watchErr, "[%s] endpoint watch failed", provider.GetLabel())
|
|
}
|
|
}
|
|
if svcCtx.Ctx.Err() != nil {
|
|
return nil
|
|
}
|
|
log.Info("[endpoint watcher] stopping watching", "provider", provider.GetLabel(), "service name", service.Name, "namespace", service.Namespace)
|
|
return utils.NewPanicError("[%s] endpoint watch channel closed unexpectedly for service %s/%s", provider.GetLabel(), service.Namespace, service.Name)
|
|
}
|