|
|
@ -10,13 +10,17 @@ import (
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"net/netip"
|
|
|
|
"net/netip"
|
|
|
|
"strings"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"golang.org/x/exp/slices"
|
|
|
|
"golang.org/x/exp/slices"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/types"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
|
|
|
|
|
|
"tailscale.com/util/clientmetric"
|
|
|
|
|
|
|
|
"tailscale.com/util/set"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type ServiceReconciler struct {
|
|
|
|
type ServiceReconciler struct {
|
|
|
@ -24,8 +28,26 @@ type ServiceReconciler struct {
|
|
|
|
ssr *tailscaleSTSReconciler
|
|
|
|
ssr *tailscaleSTSReconciler
|
|
|
|
logger *zap.SugaredLogger
|
|
|
|
logger *zap.SugaredLogger
|
|
|
|
isDefaultLoadBalancer bool
|
|
|
|
isDefaultLoadBalancer bool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mu sync.Mutex // protects following
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// managedIngressProxies is a set of all ingress proxies that we're
|
|
|
|
|
|
|
|
// currently managing. This is only used for metrics.
|
|
|
|
|
|
|
|
managedIngressProxies set.Slice[types.UID]
|
|
|
|
|
|
|
|
// managedEgressProxies is a set of all egress proxies that we're currently
|
|
|
|
|
|
|
|
// managing. This is only used for metrics.
|
|
|
|
|
|
|
|
managedEgressProxies set.Slice[types.UID]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
|
|
|
// gaugeEgressProxies tracks the number of egress proxies that we're
|
|
|
|
|
|
|
|
// currently managing.
|
|
|
|
|
|
|
|
gaugeEgressProxies = clientmetric.NewGauge("k8s_egress_proxies")
|
|
|
|
|
|
|
|
// gaugeIngressProxies tracks the number of ingress proxies that we're
|
|
|
|
|
|
|
|
// currently managing.
|
|
|
|
|
|
|
|
gaugeIngressProxies = clientmetric.NewGauge("k8s_ingress_proxies")
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
func childResourceLabels(name, ns, typ string) map[string]string {
|
|
|
|
func childResourceLabels(name, ns, typ string) map[string]string {
|
|
|
|
// You might wonder why we're using owner references, since they seem to be
|
|
|
|
// You might wonder why we're using owner references, since they seem to be
|
|
|
|
// built for exactly this. Unfortunately, Kubernetes does not support
|
|
|
|
// built for exactly this. Unfortunately, Kubernetes does not support
|
|
|
@ -71,6 +93,12 @@ func (a *ServiceReconciler) maybeCleanup(ctx context.Context, logger *zap.Sugare
|
|
|
|
ix := slices.Index(svc.Finalizers, FinalizerName)
|
|
|
|
ix := slices.Index(svc.Finalizers, FinalizerName)
|
|
|
|
if ix < 0 {
|
|
|
|
if ix < 0 {
|
|
|
|
logger.Debugf("no finalizer, nothing to do")
|
|
|
|
logger.Debugf("no finalizer, nothing to do")
|
|
|
|
|
|
|
|
a.mu.Lock()
|
|
|
|
|
|
|
|
defer a.mu.Unlock()
|
|
|
|
|
|
|
|
a.managedIngressProxies.Remove(svc.UID)
|
|
|
|
|
|
|
|
a.managedEgressProxies.Remove(svc.UID)
|
|
|
|
|
|
|
|
gaugeIngressProxies.Set(int64(a.managedIngressProxies.Len()))
|
|
|
|
|
|
|
|
gaugeEgressProxies.Set(int64(a.managedEgressProxies.Len()))
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -91,6 +119,13 @@ func (a *ServiceReconciler) maybeCleanup(ctx context.Context, logger *zap.Sugare
|
|
|
|
// cleanup removes the tailscale finalizer, which will make all future
|
|
|
|
// cleanup removes the tailscale finalizer, which will make all future
|
|
|
|
// reconciles exit early.
|
|
|
|
// reconciles exit early.
|
|
|
|
logger.Infof("unexposed service from tailnet")
|
|
|
|
logger.Infof("unexposed service from tailnet")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a.mu.Lock()
|
|
|
|
|
|
|
|
defer a.mu.Unlock()
|
|
|
|
|
|
|
|
a.managedIngressProxies.Remove(svc.UID)
|
|
|
|
|
|
|
|
a.managedEgressProxies.Remove(svc.UID)
|
|
|
|
|
|
|
|
gaugeIngressProxies.Set(int64(a.managedIngressProxies.Len()))
|
|
|
|
|
|
|
|
gaugeEgressProxies.Set(int64(a.managedEgressProxies.Len()))
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -130,11 +165,17 @@ func (a *ServiceReconciler) maybeProvision(ctx context.Context, logger *zap.Suga
|
|
|
|
ChildResourceLabels: crl,
|
|
|
|
ChildResourceLabels: crl,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a.mu.Lock()
|
|
|
|
if a.shouldExpose(svc) {
|
|
|
|
if a.shouldExpose(svc) {
|
|
|
|
sts.ClusterTargetIP = svc.Spec.ClusterIP
|
|
|
|
sts.ClusterTargetIP = svc.Spec.ClusterIP
|
|
|
|
|
|
|
|
a.managedIngressProxies.Add(svc.UID)
|
|
|
|
|
|
|
|
gaugeIngressProxies.Set(int64(a.managedIngressProxies.Len()))
|
|
|
|
} else if a.hasTailnetTargetAnnotation(svc) {
|
|
|
|
} else if a.hasTailnetTargetAnnotation(svc) {
|
|
|
|
sts.TailnetTargetIP = svc.Annotations[AnnotationTailnetTargetIP]
|
|
|
|
sts.TailnetTargetIP = svc.Annotations[AnnotationTailnetTargetIP]
|
|
|
|
|
|
|
|
a.managedEgressProxies.Add(svc.UID)
|
|
|
|
|
|
|
|
gaugeEgressProxies.Set(int64(a.managedEgressProxies.Len()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
var hsvc *corev1.Service
|
|
|
|
var hsvc *corev1.Service
|
|
|
|
if hsvc, err = a.ssr.Provision(ctx, logger, sts); err != nil {
|
|
|
|
if hsvc, err = a.ssr.Provision(ctx, logger, sts); err != nil {
|
|
|
|