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
96 lines
3.4 KiB
Go
96 lines
3.4 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build !plan9
|
|
|
|
// Package reconciler provides utilities for working with Kubernetes resources within controller reconciliation
|
|
// loops.
|
|
package reconciler
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
|
|
"tailscale.com/kube/kubetypes"
|
|
)
|
|
|
|
const (
|
|
// FinalizerName is the common finalizer used across all Tailscale Kubernetes resources.
|
|
FinalizerName = "tailscale.com/finalizer"
|
|
|
|
// LabelParentType identifies which Tailscale CRD kind owns a managed resource. Every resource that a Tailscale
|
|
// CRD reconciler creates should carry this label alongside LabelParentName.
|
|
LabelParentType = "tailscale.com/parent-resource-type"
|
|
|
|
// LabelParentName identifies the name of the Tailscale CRD that owns a managed resource. Combined with
|
|
// LabelParentType, this uniquely identifies the parent within its scope.
|
|
LabelParentName = "tailscale.com/parent-resource"
|
|
|
|
// LabelParentNamespace identifies the namespace of the owning Tailscale CRD. It is only stamped when the parent
|
|
// CRD is namespaced; cluster-scoped parents omit it.
|
|
LabelParentNamespace = "tailscale.com/parent-resource-ns"
|
|
)
|
|
|
|
// SetFinalizer adds the finalizer to the resource if not already present.
|
|
func SetFinalizer(obj client.Object) {
|
|
if idx := slices.Index(obj.GetFinalizers(), FinalizerName); idx >= 0 {
|
|
return
|
|
}
|
|
|
|
obj.SetFinalizers(append(obj.GetFinalizers(), FinalizerName))
|
|
}
|
|
|
|
// RemoveFinalizer removes the finalizer from the resource if present.
|
|
func RemoveFinalizer(obj client.Object) {
|
|
idx := slices.Index(obj.GetFinalizers(), FinalizerName)
|
|
if idx < 0 {
|
|
return
|
|
}
|
|
|
|
finalizers := obj.GetFinalizers()
|
|
obj.SetFinalizers(append(finalizers[:idx], finalizers[idx+1:]...))
|
|
}
|
|
|
|
// Labels returns the standard ownership labels stamped on every resource a Tailscale CRD reconciler creates:
|
|
// tailscale.com/managed=true, plus LabelParentType and LabelParentName. If parentNamespace is non-empty (i.e. the
|
|
// parent CRD is namespaced) it is stamped as LabelParentNamespace; cluster-scoped parents pass "".
|
|
func Labels(parentType, parentName, parentNamespace string) map[string]string {
|
|
labels := map[string]string{
|
|
kubetypes.LabelManaged: "true",
|
|
LabelParentType: parentType,
|
|
LabelParentName: parentName,
|
|
}
|
|
if parentNamespace != "" {
|
|
labels[LabelParentNamespace] = parentNamespace
|
|
}
|
|
return labels
|
|
}
|
|
|
|
// EnqueueForChild returns a handler.MapFunc that enqueues a reconcile.Request for the parent CRD of a managed child
|
|
// resource. It filters by tailscale.com/managed=true and LabelParentType, and derives the request's NamespacedName
|
|
// from LabelParentName plus LabelParentNamespace (blank namespace for cluster-scoped parents). Use it on Watches of
|
|
// child resources so drift or cloud-controller updates propagate to the owning CRD's reconciler.
|
|
func EnqueueForChild(parentType string) handler.MapFunc {
|
|
return func(_ context.Context, o client.Object) []reconcile.Request {
|
|
labels := o.GetLabels()
|
|
if labels[kubetypes.LabelManaged] != "true" || labels[LabelParentType] != parentType {
|
|
return nil
|
|
}
|
|
|
|
name := labels[LabelParentName]
|
|
if name == "" {
|
|
return nil
|
|
}
|
|
|
|
return []reconcile.Request{{NamespacedName: types.NamespacedName{
|
|
Name: name,
|
|
Namespace: labels[LabelParentNamespace],
|
|
}}}
|
|
}
|
|
}
|