cmd/k8s-operator: add replica support to nameserver (#17246)

This commit modifies the `DNSConfig` custom resource to allow specifying
a replica count when deploying a nameserver. This allows deploying
nameservers in a HA configuration.

Updates https://github.com/tailscale/corp/issues/32589

Signed-off-by: David Bond <davidsbond93@gmail.com>
This commit is contained in:
David Bond
2025-09-29 12:38:15 +01:00
committed by GitHub
parent a32102f741
commit e466488a2a
7 changed files with 33 additions and 2 deletions

View File

@@ -100,6 +100,11 @@ spec:
tag:
description: Tag defaults to unstable.
type: string
replicas:
description: Replicas specifies how many Pods to create. Defaults to 1.
type: integer
format: int32
minimum: 0
service:
description: Service configuration.
type: object

View File

@@ -431,6 +431,11 @@ spec:
description: Tag defaults to unstable.
type: string
type: object
replicas:
description: Replicas specifies how many Pods to create. Defaults to 1.
format: int32
minimum: 0
type: integer
service:
description: Service configuration.
properties:

View File

@@ -30,6 +30,7 @@ import (
tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
"tailscale.com/kube/kubetypes"
"tailscale.com/tstime"
"tailscale.com/types/ptr"
"tailscale.com/util/clientmetric"
"tailscale.com/util/set"
)
@@ -130,7 +131,7 @@ func (a *NameserverReconciler) Reconcile(ctx context.Context, req reconcile.Requ
return setStatus(&dnsCfg, metav1.ConditionFalse, reasonNameserverCreationFailed, msg)
}
}
if err := a.maybeProvision(ctx, &dnsCfg, logger); err != nil {
if err = a.maybeProvision(ctx, &dnsCfg); err != nil {
if strings.Contains(err.Error(), optimisticLockErrorMsg) {
logger.Infof("optimistic lock error, retrying: %s", err)
return reconcile.Result{}, nil
@@ -167,7 +168,7 @@ func nameserverResourceLabels(name, namespace string) map[string]string {
return labels
}
func (a *NameserverReconciler) maybeProvision(ctx context.Context, tsDNSCfg *tsapi.DNSConfig, logger *zap.SugaredLogger) error {
func (a *NameserverReconciler) maybeProvision(ctx context.Context, tsDNSCfg *tsapi.DNSConfig) error {
labels := nameserverResourceLabels(tsDNSCfg.Name, a.tsNamespace)
dCfg := &deployConfig{
ownerRefs: []metav1.OwnerReference{*metav1.NewControllerRef(tsDNSCfg, tsapi.SchemeGroupVersion.WithKind("DNSConfig"))},
@@ -175,6 +176,11 @@ func (a *NameserverReconciler) maybeProvision(ctx context.Context, tsDNSCfg *tsa
labels: labels,
imageRepo: defaultNameserverImageRepo,
imageTag: defaultNameserverImageTag,
replicas: 1,
}
if tsDNSCfg.Spec.Nameserver.Replicas != nil {
dCfg.replicas = *tsDNSCfg.Spec.Nameserver.Replicas
}
if tsDNSCfg.Spec.Nameserver.Image != nil && tsDNSCfg.Spec.Nameserver.Image.Repo != "" {
dCfg.imageRepo = tsDNSCfg.Spec.Nameserver.Image.Repo
@@ -211,6 +217,7 @@ type deployable struct {
}
type deployConfig struct {
replicas int32
imageRepo string
imageTag string
labels map[string]string
@@ -236,6 +243,7 @@ var (
if err := yaml.Unmarshal(deployYaml, &d); err != nil {
return fmt.Errorf("error unmarshalling Deployment yaml: %w", err)
}
d.Spec.Replicas = ptr.To(cfg.replicas)
d.Spec.Template.Spec.Containers[0].Image = fmt.Sprintf("%s:%s", cfg.imageRepo, cfg.imageTag)
d.ObjectMeta.Namespace = cfg.namespace
d.ObjectMeta.Labels = cfg.labels

View File

@@ -22,6 +22,7 @@ import (
operatorutils "tailscale.com/k8s-operator"
tsapi "tailscale.com/k8s-operator/apis/v1alpha1"
"tailscale.com/tstest"
"tailscale.com/types/ptr"
"tailscale.com/util/mak"
)
@@ -33,6 +34,7 @@ func TestNameserverReconciler(t *testing.T) {
},
Spec: tsapi.DNSConfigSpec{
Nameserver: &tsapi.Nameserver{
Replicas: ptr.To[int32](3),
Image: &tsapi.NameserverImage{
Repo: "test",
Tag: "v0.0.1",
@@ -74,6 +76,7 @@ func TestNameserverReconciler(t *testing.T) {
}
wantsDeploy.OwnerReferences = []metav1.OwnerReference{*ownerReference}
wantsDeploy.Spec.Template.Spec.Containers[0].Image = "test:v0.0.1"
wantsDeploy.Spec.Replicas = ptr.To[int32](3)
wantsDeploy.Namespace = tsNamespace
wantsDeploy.ObjectMeta.Labels = nameserverLabels
expectEqual(t, fc, wantsDeploy)

View File

@@ -443,6 +443,7 @@ _Appears in:_
| --- | --- | --- | --- |
| `image` _[NameserverImage](#nameserverimage)_ | Nameserver image. Defaults to tailscale/k8s-nameserver:unstable. | | |
| `service` _[NameserverService](#nameserverservice)_ | Service configuration. | | |
| `replicas` _integer_ | Replicas specifies how many Pods to create. Defaults to 1. | | Minimum: 0 <br /> |
#### NameserverImage

View File

@@ -84,6 +84,10 @@ type Nameserver struct {
// Service configuration.
// +optional
Service *NameserverService `json:"service,omitempty"`
// Replicas specifies how many Pods to create. Defaults to 1.
// +optional
// +kubebuilder:validation:Minimum=0
Replicas *int32 `json:"replicas,omitempty"`
}
type NameserverImage struct {

View File

@@ -422,6 +422,11 @@ func (in *Nameserver) DeepCopyInto(out *Nameserver) {
*out = new(NameserverService)
**out = **in
}
if in.Replicas != nil {
in, out := &in.Replicas, &out.Replicas
*out = new(int32)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Nameserver.