cmd/k8s-operator: use correct tailnet client for L7 & L3 ingresses (#18749)
* cmd/k8s-operator: use correct tailnet client for L7 & L3 ingresses This commit fixes a bug when using multi-tailnet within the operator to spin up L7 & L3 ingresses where the client used to create the tailscale services was not switching depending on the tailnet used by the proxygroup backing the service/ingress. Updates: https://github.com/tailscale/corp/issues/34561 Signed-off-by: David Bond <davidsbond93@gmail.com> * cmd/k8s-operator: adding server url to proxygroups when a custom tailnet has been specified Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk> (cherry picked from commit 3b21ac5504e713e32dfcd43d9ee21e7e712ac200) --------- Signed-off-by: David Bond <davidsbond93@gmail.com> Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk> Co-authored-by: chaosinthecrd <tom@tmlabs.co.uk>
This commit is contained in:
@@ -23,6 +23,7 @@ import (
|
||||
"k8s.io/client-go/tools/record"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
"tailscale.com/internal/client/tailscale"
|
||||
tsoperator "tailscale.com/k8s-operator"
|
||||
tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
|
||||
@@ -52,7 +53,6 @@ type KubeAPIServerTSServiceReconciler struct {
|
||||
logger *zap.SugaredLogger
|
||||
tsClient tsClient
|
||||
tsNamespace string
|
||||
lc localClient
|
||||
defaultTags []string
|
||||
operatorID string // stableID of the operator's Tailscale device
|
||||
|
||||
@@ -78,9 +78,14 @@ func (r *KubeAPIServerTSServiceReconciler) Reconcile(ctx context.Context, req re
|
||||
serviceName := serviceNameForAPIServerProxy(pg)
|
||||
logger = logger.With("Tailscale Service", serviceName)
|
||||
|
||||
tailscaleClient, err := r.getClient(ctx, pg.Spec.Tailnet)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("failed to get tailscale client: %w", err)
|
||||
}
|
||||
|
||||
if markedForDeletion(pg) {
|
||||
logger.Debugf("ProxyGroup is being deleted, ensuring any created resources are cleaned up")
|
||||
if err = r.maybeCleanup(ctx, serviceName, pg, logger); err != nil && strings.Contains(err.Error(), optimisticLockErrorMsg) {
|
||||
if err = r.maybeCleanup(ctx, serviceName, pg, logger, tailscaleClient); err != nil && strings.Contains(err.Error(), optimisticLockErrorMsg) {
|
||||
logger.Infof("optimistic lock error, retrying: %s", err)
|
||||
return res, nil
|
||||
}
|
||||
@@ -88,7 +93,7 @@ func (r *KubeAPIServerTSServiceReconciler) Reconcile(ctx context.Context, req re
|
||||
return res, err
|
||||
}
|
||||
|
||||
err = r.maybeProvision(ctx, serviceName, pg, logger)
|
||||
err = r.maybeProvision(ctx, serviceName, pg, logger, tailscaleClient)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), optimisticLockErrorMsg) {
|
||||
logger.Infof("optimistic lock error, retrying: %s", err)
|
||||
@@ -100,11 +105,27 @@ func (r *KubeAPIServerTSServiceReconciler) Reconcile(ctx context.Context, req re
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
// getClient returns the appropriate Tailscale client for the given tailnet.
|
||||
// If no tailnet is specified, returns the default client.
|
||||
func (r *KubeAPIServerTSServiceReconciler) getClient(ctx context.Context, tailnetName string) (tsClient,
|
||||
error) {
|
||||
if tailnetName == "" {
|
||||
return r.tsClient, nil
|
||||
}
|
||||
|
||||
tc, _, err := clientForTailnet(ctx, r.Client, r.tsNamespace, tailnetName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
// maybeProvision ensures that a Tailscale Service for this ProxyGroup exists
|
||||
// and is up to date.
|
||||
//
|
||||
// Returns true if the operation resulted in a Tailscale Service update.
|
||||
func (r *KubeAPIServerTSServiceReconciler) maybeProvision(ctx context.Context, serviceName tailcfg.ServiceName, pg *tsapi.ProxyGroup, logger *zap.SugaredLogger) (err error) {
|
||||
func (r *KubeAPIServerTSServiceReconciler) maybeProvision(ctx context.Context, serviceName tailcfg.ServiceName, pg *tsapi.ProxyGroup, logger *zap.SugaredLogger, tsClient tsClient) (err error) {
|
||||
var dnsName string
|
||||
oldPGStatus := pg.Status.DeepCopy()
|
||||
defer func() {
|
||||
@@ -156,7 +177,7 @@ func (r *KubeAPIServerTSServiceReconciler) maybeProvision(ctx context.Context, s
|
||||
|
||||
// 1. Check there isn't a Tailscale Service with the same hostname
|
||||
// already created and not owned by this ProxyGroup.
|
||||
existingTSSvc, err := r.tsClient.GetVIPService(ctx, serviceName)
|
||||
existingTSSvc, err := tsClient.GetVIPService(ctx, serviceName)
|
||||
if err != nil && !isErrorTailscaleServiceNotFound(err) {
|
||||
return fmt.Errorf("error getting Tailscale Service %q: %w", serviceName, err)
|
||||
}
|
||||
@@ -198,17 +219,17 @@ func (r *KubeAPIServerTSServiceReconciler) maybeProvision(ctx context.Context, s
|
||||
!ownersAreSetAndEqual(tsSvc, existingTSSvc) ||
|
||||
!slices.Equal(tsSvc.Ports, existingTSSvc.Ports) {
|
||||
logger.Infof("Ensuring Tailscale Service exists and is up to date")
|
||||
if err := r.tsClient.CreateOrUpdateVIPService(ctx, tsSvc); err != nil {
|
||||
if err = tsClient.CreateOrUpdateVIPService(ctx, tsSvc); err != nil {
|
||||
return fmt.Errorf("error creating Tailscale Service: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Ensure that TLS Secret and RBAC exists.
|
||||
tcd, err := tailnetCertDomain(ctx, r.lc)
|
||||
dnsName, err = dnsNameForService(ctx, r.Client, serviceName, pg, r.tsNamespace)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error determining DNS name base: %w", err)
|
||||
return fmt.Errorf("error determining service DNS name: %w", err)
|
||||
}
|
||||
dnsName = serviceName.WithoutPrefix() + "." + tcd
|
||||
|
||||
if err = r.ensureCertResources(ctx, pg, dnsName); err != nil {
|
||||
return fmt.Errorf("error ensuring cert resources: %w", err)
|
||||
}
|
||||
@@ -219,7 +240,7 @@ func (r *KubeAPIServerTSServiceReconciler) maybeProvision(ctx context.Context, s
|
||||
}
|
||||
|
||||
// 5. Clean up any stale Tailscale Services from previous resource versions.
|
||||
if err = r.maybeDeleteStaleServices(ctx, pg, logger); err != nil {
|
||||
if err = r.maybeDeleteStaleServices(ctx, pg, logger, tsClient); err != nil {
|
||||
return fmt.Errorf("failed to delete stale Tailscale Services: %w", err)
|
||||
}
|
||||
|
||||
@@ -230,7 +251,7 @@ func (r *KubeAPIServerTSServiceReconciler) maybeProvision(ctx context.Context, s
|
||||
// Service is being deleted or is unexposed. The cleanup is safe for a multi-cluster setup- the Tailscale Service is only
|
||||
// deleted if it does not contain any other owner references. If it does, the cleanup only removes the owner reference
|
||||
// corresponding to this Service.
|
||||
func (r *KubeAPIServerTSServiceReconciler) maybeCleanup(ctx context.Context, serviceName tailcfg.ServiceName, pg *tsapi.ProxyGroup, logger *zap.SugaredLogger) (err error) {
|
||||
func (r *KubeAPIServerTSServiceReconciler) maybeCleanup(ctx context.Context, serviceName tailcfg.ServiceName, pg *tsapi.ProxyGroup, logger *zap.SugaredLogger, tsClient tsClient) (err error) {
|
||||
ix := slices.Index(pg.Finalizers, proxyPGFinalizerName)
|
||||
if ix < 0 {
|
||||
logger.Debugf("no finalizer, nothing to do")
|
||||
@@ -244,11 +265,11 @@ func (r *KubeAPIServerTSServiceReconciler) maybeCleanup(ctx context.Context, ser
|
||||
}
|
||||
}()
|
||||
|
||||
if _, err = cleanupTailscaleService(ctx, r.tsClient, serviceName, r.operatorID, logger); err != nil {
|
||||
if _, err = cleanupTailscaleService(ctx, tsClient, serviceName, r.operatorID, logger); err != nil {
|
||||
return fmt.Errorf("error deleting Tailscale Service: %w", err)
|
||||
}
|
||||
|
||||
if err = cleanupCertResources(ctx, r.Client, r.lc, r.tsNamespace, pg.Name, serviceName); err != nil {
|
||||
if err = cleanupCertResources(ctx, r.Client, r.tsNamespace, serviceName, pg); err != nil {
|
||||
return fmt.Errorf("failed to clean up cert resources: %w", err)
|
||||
}
|
||||
|
||||
@@ -257,10 +278,10 @@ func (r *KubeAPIServerTSServiceReconciler) maybeCleanup(ctx context.Context, ser
|
||||
|
||||
// maybeDeleteStaleServices deletes Services that have previously been created for
|
||||
// this ProxyGroup but are no longer needed.
|
||||
func (r *KubeAPIServerTSServiceReconciler) maybeDeleteStaleServices(ctx context.Context, pg *tsapi.ProxyGroup, logger *zap.SugaredLogger) error {
|
||||
func (r *KubeAPIServerTSServiceReconciler) maybeDeleteStaleServices(ctx context.Context, pg *tsapi.ProxyGroup, logger *zap.SugaredLogger, tsClient tsClient) error {
|
||||
serviceName := serviceNameForAPIServerProxy(pg)
|
||||
|
||||
svcs, err := r.tsClient.ListVIPServices(ctx)
|
||||
svcs, err := tsClient.ListVIPServices(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing Tailscale Services: %w", err)
|
||||
}
|
||||
@@ -285,11 +306,11 @@ func (r *KubeAPIServerTSServiceReconciler) maybeDeleteStaleServices(ctx context.
|
||||
}
|
||||
|
||||
logger.Infof("Deleting Tailscale Service %s", svc.Name)
|
||||
if err := r.tsClient.DeleteVIPService(ctx, svc.Name); err != nil && !isErrorTailscaleServiceNotFound(err) {
|
||||
if err = tsClient.DeleteVIPService(ctx, svc.Name); err != nil && !isErrorTailscaleServiceNotFound(err) {
|
||||
return fmt.Errorf("error deleting Tailscale Service %s: %w", svc.Name, err)
|
||||
}
|
||||
|
||||
if err = cleanupCertResources(ctx, r.Client, r.lc, r.tsNamespace, pg.Name, svc.Name); err != nil {
|
||||
if err = cleanupCertResources(ctx, r.Client, r.tsNamespace, svc.Name, pg); err != nil {
|
||||
return fmt.Errorf("failed to clean up cert resources: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -343,7 +364,7 @@ func (r *KubeAPIServerTSServiceReconciler) maybeAdvertiseServices(ctx context.Co
|
||||
|
||||
// Only advertise a Tailscale Service once the TLS certs required for
|
||||
// serving it are available.
|
||||
shouldBeAdvertised, err := hasCerts(ctx, r.Client, r.lc, r.tsNamespace, serviceName)
|
||||
shouldBeAdvertised, err := hasCerts(ctx, r.Client, r.tsNamespace, serviceName, pg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error checking TLS credentials provisioned for Tailscale Service %q: %w", serviceName, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user