mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-24 17:47:30 +00:00
cmd/k8s-operator: allow specifying replicas for connectors (#16721)
This commit adds a `replicas` field to the `Connector` custom resource that allows users to specify the number of desired replicas deployed for their connectors. This allows users to deploy exit nodes, subnet routers and app connectors in a highly available fashion. Fixes #14020 Signed-off-by: David Bond <davidsbond93@gmail.com>
This commit is contained in:
@@ -25,7 +25,6 @@ import (
|
||||
"k8s.io/client-go/tools/record"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
tsoperator "tailscale.com/k8s-operator"
|
||||
tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
|
||||
"tailscale.com/kube/kubetypes"
|
||||
@@ -176,6 +175,7 @@ func (a *ConnectorReconciler) maybeProvisionConnector(ctx context.Context, logge
|
||||
if cn.Spec.Hostname != "" {
|
||||
hostname = string(cn.Spec.Hostname)
|
||||
}
|
||||
|
||||
crl := childResourceLabels(cn.Name, a.tsnamespace, "connector")
|
||||
|
||||
proxyClass := cn.Spec.ProxyClass
|
||||
@@ -188,10 +188,17 @@ func (a *ConnectorReconciler) maybeProvisionConnector(ctx context.Context, logge
|
||||
}
|
||||
}
|
||||
|
||||
var replicas int32 = 1
|
||||
if cn.Spec.Replicas != nil {
|
||||
replicas = *cn.Spec.Replicas
|
||||
}
|
||||
|
||||
sts := &tailscaleSTSConfig{
|
||||
Replicas: replicas,
|
||||
ParentResourceName: cn.Name,
|
||||
ParentResourceUID: string(cn.UID),
|
||||
Hostname: hostname,
|
||||
HostnamePrefix: string(cn.Spec.HostnamePrefix),
|
||||
ChildResourceLabels: crl,
|
||||
Tags: cn.Spec.Tags.Stringify(),
|
||||
Connector: &connector{
|
||||
@@ -219,16 +226,19 @@ func (a *ConnectorReconciler) maybeProvisionConnector(ctx context.Context, logge
|
||||
} else {
|
||||
a.exitNodes.Remove(cn.UID)
|
||||
}
|
||||
|
||||
if cn.Spec.SubnetRouter != nil {
|
||||
a.subnetRouters.Add(cn.GetUID())
|
||||
} else {
|
||||
a.subnetRouters.Remove(cn.GetUID())
|
||||
}
|
||||
|
||||
if cn.Spec.AppConnector != nil {
|
||||
a.appConnectors.Add(cn.GetUID())
|
||||
} else {
|
||||
a.appConnectors.Remove(cn.GetUID())
|
||||
}
|
||||
|
||||
a.mu.Unlock()
|
||||
gaugeConnectorSubnetRouterResources.Set(int64(a.subnetRouters.Len()))
|
||||
gaugeConnectorExitNodeResources.Set(int64(a.exitNodes.Len()))
|
||||
@@ -244,21 +254,23 @@ func (a *ConnectorReconciler) maybeProvisionConnector(ctx context.Context, logge
|
||||
return err
|
||||
}
|
||||
|
||||
dev, err := a.ssr.DeviceInfo(ctx, crl, logger)
|
||||
devices, err := a.ssr.DeviceInfo(ctx, crl, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dev == nil || dev.hostname == "" {
|
||||
logger.Debugf("no Tailscale hostname known yet, waiting for Connector Pod to finish auth")
|
||||
// No hostname yet. Wait for the connector pod to auth.
|
||||
cn.Status.TailnetIPs = nil
|
||||
cn.Status.Hostname = ""
|
||||
return nil
|
||||
cn.Status.Devices = make([]tsapi.ConnectorDevice, len(devices))
|
||||
for i, dev := range devices {
|
||||
cn.Status.Devices[i] = tsapi.ConnectorDevice{
|
||||
Hostname: dev.hostname,
|
||||
TailnetIPs: dev.ips,
|
||||
}
|
||||
}
|
||||
|
||||
cn.Status.TailnetIPs = dev.ips
|
||||
cn.Status.Hostname = dev.hostname
|
||||
if len(cn.Status.Devices) > 0 {
|
||||
cn.Status.Hostname = cn.Status.Devices[0].Hostname
|
||||
cn.Status.TailnetIPs = cn.Status.Devices[0].TailnetIPs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -302,6 +314,15 @@ func (a *ConnectorReconciler) validate(cn *tsapi.Connector) error {
|
||||
if (cn.Spec.SubnetRouter != nil || cn.Spec.ExitNode) && cn.Spec.AppConnector != nil {
|
||||
return errors.New("invalid spec: a Connector that is configured as an app connector must not be also configured as a subnet router or exit node")
|
||||
}
|
||||
|
||||
// These two checks should be caught by the Connector schema validation.
|
||||
if cn.Spec.Replicas != nil && *cn.Spec.Replicas > 1 && cn.Spec.Hostname != "" {
|
||||
return errors.New("invalid spec: a Connector that is configured with multiple replicas cannot specify a hostname. Instead, use a hostnamePrefix")
|
||||
}
|
||||
if cn.Spec.HostnamePrefix != "" && cn.Spec.Hostname != "" {
|
||||
return errors.New("invalid spec: a Connect cannot use both a hostname and hostname prefix")
|
||||
}
|
||||
|
||||
if cn.Spec.AppConnector != nil {
|
||||
return validateAppConnector(cn.Spec.AppConnector)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user