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
67 lines
1.9 KiB
Go
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...)
|
|
}
|