diff options
| author | Raj Singh <raj@tailscale.com> | 2026-03-31 13:00:41 -0500 |
|---|---|---|
| committer | Raj Singh <raj@tailscale.com> | 2026-03-31 13:00:48 -0500 |
| commit | 0843986a0dc90b233b353d956109fb3c369570ab (patch) | |
| tree | 09964c557bd43810cc9b6550237ddc9f320ebc45 | |
| parent | 4334dfa7d5ccbee1daf5acf30b33557bbca66525 (diff) | |
| download | tailscale-fix/connector-appconnector-empty-routes.tar.xz tailscale-fix/connector-appconnector-empty-routes.zip | |
k8s-operator: remove minItems=1 from appConnector routesfix/connector-appconnector-empty-routes
AppConnector.Routes is documented as optional (routes are discovered
dynamically when not set), but the shared Routes type enforces
MinItems=1 via kubebuilder validation. This makes appConnector: {}
fail CRD validation through server-side apply, breaking GitOps
deployments (Flux, ArgoCD with SSA).
Decouple AppConnector.Routes from the shared Routes type so the
MinItems constraint only applies to SubnetRouter.AdvertiseRoutes
where it's actually needed.
Fixes #19195
Signed-off-by: Raj Singh <raj@tailscale.com>
| -rw-r--r-- | cmd/k8s-operator/connector.go | 2 | ||||
| -rw-r--r-- | cmd/k8s-operator/deploy/crds/tailscale.com_connectors.yaml | 1 | ||||
| -rw-r--r-- | cmd/k8s-operator/deploy/manifests/operator.yaml | 1 | ||||
| -rw-r--r-- | k8s-operator/api.md | 4 | ||||
| -rw-r--r-- | k8s-operator/apis/v1alpha1/types_connector.go | 2 | ||||
| -rw-r--r-- | k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go | 2 |
6 files changed, 5 insertions, 7 deletions
diff --git a/cmd/k8s-operator/connector.go b/cmd/k8s-operator/connector.go index 0c2d32482..4c35b442b 100644 --- a/cmd/k8s-operator/connector.go +++ b/cmd/k8s-operator/connector.go @@ -218,7 +218,7 @@ func (a *ConnectorReconciler) maybeProvisionConnector(ctx context.Context, logge if cn.Spec.AppConnector != nil { sts.Connector.isAppConnector = true if len(cn.Spec.AppConnector.Routes) != 0 { - sts.Connector.routes = cn.Spec.AppConnector.Routes.Stringify() + sts.Connector.routes = tsapi.Routes(cn.Spec.AppConnector.Routes).Stringify() } } diff --git a/cmd/k8s-operator/deploy/crds/tailscale.com_connectors.yaml b/cmd/k8s-operator/deploy/crds/tailscale.com_connectors.yaml index 03c51c755..91e81ae71 100644 --- a/cmd/k8s-operator/deploy/crds/tailscale.com_connectors.yaml +++ b/cmd/k8s-operator/deploy/crds/tailscale.com_connectors.yaml @@ -99,7 +99,6 @@ spec: also dynamically discover other routes. https://tailscale.com/kb/1332/apps-best-practices#preconfiguration type: array - minItems: 1 items: type: string format: cidr diff --git a/cmd/k8s-operator/deploy/manifests/operator.yaml b/cmd/k8s-operator/deploy/manifests/operator.yaml index 597641bde..14df9fbb8 100644 --- a/cmd/k8s-operator/deploy/manifests/operator.yaml +++ b/cmd/k8s-operator/deploy/manifests/operator.yaml @@ -125,7 +125,6 @@ spec: items: format: cidr type: string - minItems: 1 type: array type: object exitNode: diff --git a/k8s-operator/api.md b/k8s-operator/api.md index 5a60f66e0..762c1b424 100644 --- a/k8s-operator/api.md +++ b/k8s-operator/api.md @@ -53,7 +53,7 @@ _Appears in:_ | Field | Description | Default | Validation | | --- | --- | --- | --- | -| `routes` _[Routes](#routes)_ | Routes are optional preconfigured routes for the domains routed via the app connector.<br />If not set, routes for the domains will be discovered dynamically.<br />If set, the app connector will immediately be able to route traffic using the preconfigured routes, but may<br />also dynamically discover other routes.<br />https://tailscale.com/kb/1332/apps-best-practices#preconfiguration | | Format: cidr <br />MinItems: 1 <br />Type: string <br /> | +| `routes` _[Route](#route) array_ | Routes are optional preconfigured routes for the domains routed via the app connector.<br />If not set, routes for the domains will be discovered dynamically.<br />If set, the app connector will immediately be able to route traffic using the preconfigured routes, but may<br />also dynamically discover other routes.<br />https://tailscale.com/kb/1332/apps-best-practices#preconfiguration | | Format: cidr <br />Type: string <br /> | @@ -1049,6 +1049,7 @@ _Validation:_ - Type: string _Appears in:_ +- [AppConnector](#appconnector) - [Routes](#routes) @@ -1065,7 +1066,6 @@ _Validation:_ - Type: string _Appears in:_ -- [AppConnector](#appconnector) - [SubnetRouter](#subnetrouter) diff --git a/k8s-operator/apis/v1alpha1/types_connector.go b/k8s-operator/apis/v1alpha1/types_connector.go index af2df58af..4b18fb7fc 100644 --- a/k8s-operator/apis/v1alpha1/types_connector.go +++ b/k8s-operator/apis/v1alpha1/types_connector.go @@ -159,7 +159,7 @@ type AppConnector struct { // also dynamically discover other routes. // https://tailscale.com/kb/1332/apps-best-practices#preconfiguration // +optional - Routes Routes `json:"routes"` + Routes []Route `json:"routes,omitempty"` } type Tags []Tag diff --git a/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go b/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go index 2528c89f3..a29aacae6 100644 --- a/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go +++ b/k8s-operator/apis/v1alpha1/zz_generated.deepcopy.go @@ -18,7 +18,7 @@ func (in *AppConnector) DeepCopyInto(out *AppConnector) { *out = *in if in.Routes != nil { in, out := &in.Routes, &out.Routes - *out = make(Routes, len(*in)) + *out = make([]Route, len(*in)) copy(*out, *in) } } |
