Files
tailscale/k8s-operator/reconciler/peerrelay/device.go
T
David BondandGitHub be0e460a20 cmd/k8s-operator,k8s-operator: Kubernetes Peer Relays (#20495)
This commit contains the Kubernetes implementation of peer relays via the new `PeerRelay` CRD. It's a mega branch consisting of the commits of other PRs gone into this work:

1. https://github.com/tailscale/tailscale/pull/20211
2. https://github.com/tailscale/tailscale/pull/20329
3. https://github.com/tailscale/tailscale/pull/20423
4. https://github.com/tailscale/tailscale/pull/20503

An instance of the `PeerRelay` CRD deploys a `StatefulSet` of containerboot instances configured to advertise themselves as peer relays using the IP addresses configured via `LoadBalancer` services on each cloud provider (with some AWS specifics as it's less automatic than its competing cloud providers). 

Per replica, a `LoadBalancer` type `Service` resource is provisioned and its IP address is used to configure the respective relay.

This has been tested with success in AWS, GCP & Azure and provides additional modification to `Service` resources via the CRD for any other kinds of deployment environments. It also contains some work that may appear to be duplication of what already exists within `cmd/k8s-operator` so we can start building an appropriate migration path for `Connector`, `ProxyGroup` etc into respective `k8s-operator/reconciler/*` packages.

Closes https://github.com/tailscale/corp/issues/34524
2026-07-20 16:37:15 +01:00

67 lines
1.9 KiB
Go

// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
//go:build !plan9
package peerrelay
import (
"context"
"errors"
"fmt"
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
tailscaleclient "tailscale.com/client/tailscale/v2"
tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
"tailscale.com/k8s-operator/reconciler/tailscaled"
"tailscale.com/kube/kubetypes"
)
func (r *Reconciler) deleteDevicesFrom(ctx context.Context, logger *zap.SugaredLogger, pr *tsapi.PeerRelay, fromIdx int32) error {
if r.tsClients == nil {
return nil
}
tsc, err := r.tsClients.For(pr.Spec.Tailnet)
if err != nil {
return fmt.Errorf("failed to resolve Tailscale API client for tailnet %q: %w", pr.Spec.Tailnet, err)
}
labels := peerRelayLabels(pr.Name)
labels[kubetypes.LabelSecretType] = kubetypes.LabelSecretTypeState
var list corev1.SecretList
if err = r.List(ctx, &list, client.InNamespace(r.tailscaleNamespace), client.MatchingLabels(labels)); err != nil {
return fmt.Errorf("failed to list state Secrets: %w", err)
}
var errs []error
for i := range list.Items {
s := &list.Items[i]
idx, ok := replicaIndexFromLabels(s.Labels)
if !ok || idx < fromIdx {
continue
}
if deviceID := tailscaled.DeviceIDFromStateSecret(s); deviceID != "" {
logger.Debugf("deleting tailnet device %q", deviceID)
if err = tsc.Devices().Delete(ctx, deviceID); err != nil && !tailscaleclient.IsNotFound(err) {
errs = append(errs, fmt.Errorf("failed to delete tailnet device %q: %w", deviceID, err))
continue
}
}
logger.Debugf("deleting state Secret %q", s.Name)
if err = r.Delete(ctx, s); err != nil && !apierrors.IsNotFound(err) {
errs = append(errs, fmt.Errorf("failed to delete state Secret %q: %w", s.Name, err))
}
}
return errors.Join(errs...)
}