summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRaj Singh <raj@tailscale.com>2026-01-20 13:27:55 -0500
committerRaj Singh <raj@tailscale.com>2026-01-20 13:28:54 -0500
commitb870902c149f8335bcedcac991fb7edf3c5ff872 (patch)
treedd16c95b99d0924b0239d85a706ba11442601a25
parent1a79abf5fb0358242e77e8dedfd699a4d7e4e6c5 (diff)
downloadtailscale-rajsingh/fix-apiserver-pg-ownership-reclaim.tar.xz
tailscale-rajsingh/fix-apiserver-pg-ownership-reclaim.zip
cmd/k8s-operator: allow VIP ownership reclaim after operator recreationrajsingh/fix-apiserver-pg-ownership-reclaim
Fixes #18466 Signed-off-by: Raj Singh <raj@tailscale.com>
-rw-r--r--cmd/k8s-operator/api-server-proxy-pg.go9
-rw-r--r--cmd/k8s-operator/api-server-proxy-pg_test.go13
-rw-r--r--tool/gocross/gocross-wrapper.ps17
3 files changed, 24 insertions, 5 deletions
diff --git a/cmd/k8s-operator/api-server-proxy-pg.go b/cmd/k8s-operator/api-server-proxy-pg.go
index 1a81e4967..27563c2e2 100644
--- a/cmd/k8s-operator/api-server-proxy-pg.go
+++ b/cmd/k8s-operator/api-server-proxy-pg.go
@@ -446,8 +446,8 @@ func exclusiveOwnerAnnotations(pg *tsapi.ProxyGroup, operatorID string, svc *tai
if o == nil || len(o.OwnerRefs) == 0 {
return nil, fmt.Errorf("Tailscale Service %s exists, but does not contain owner annotation with owner references; not proceeding as this is likely a resource created by something other than the Tailscale Kubernetes operator", svc.Name)
}
- if len(o.OwnerRefs) > 1 || o.OwnerRefs[0].OperatorID != operatorID {
- return nil, fmt.Errorf("Tailscale Service %s is already owned by other operator(s) and cannot be shared across multiple clusters; configure a difference Service name to continue", svc.Name)
+ if len(o.OwnerRefs) > 1 {
+ return nil, fmt.Errorf("Tailscale Service %s is already owned by multiple operators and cannot be shared across multiple clusters; configure a difference Service name to continue", svc.Name)
}
if o.OwnerRefs[0].Resource == nil {
return nil, fmt.Errorf("Tailscale Service %s exists, but does not reference an owning resource; not proceeding as this is likely a Service already owned by an Ingress", svc.Name)
@@ -455,6 +455,11 @@ func exclusiveOwnerAnnotations(pg *tsapi.ProxyGroup, operatorID string, svc *tai
if o.OwnerRefs[0].Resource.Kind != "ProxyGroup" || o.OwnerRefs[0].Resource.UID != string(pg.UID) {
return nil, fmt.Errorf("Tailscale Service %s is already owned by another resource: %#v; configure a difference Service name to continue", svc.Name, o.OwnerRefs[0].Resource)
}
+ // Allow operator pod recreation: if the ProxyGroup UID matches but operatorID differs,
+ // the operator was recreated and should reclaim ownership.
+ if o.OwnerRefs[0].OperatorID != operatorID {
+ o.OwnerRefs[0].OperatorID = operatorID
+ }
if o.OwnerRefs[0].Resource.Name != pg.Name {
// ProxyGroup name can be updated in place.
o.OwnerRefs[0].Resource.Name = pg.Name
diff --git a/cmd/k8s-operator/api-server-proxy-pg_test.go b/cmd/k8s-operator/api-server-proxy-pg_test.go
index dee505723..c00bc36e9 100644
--- a/cmd/k8s-operator/api-server-proxy-pg_test.go
+++ b/cmd/k8s-operator/api-server-proxy-pg_test.go
@@ -323,13 +323,22 @@ func TestExclusiveOwnerAnnotations(t *testing.T) {
},
},
},
- "owned_by_another_operator": {
+ "owned_by_another_operator_no_resource": {
svc: &tailscale.VIPService{
Annotations: map[string]string{
ownerAnnotation: `{"ownerRefs":[{"operatorID":"operator-2"}]}`,
},
},
- wantErr: "already owned by other operator(s)",
+ wantErr: "does not reference an owning resource",
+ },
+ "operator_recreated_same_pg": {
+ // Different operatorID but same ProxyGroup UID - operator pod was recreated.
+ // Should allow reclaiming ownership.
+ svc: &tailscale.VIPService{
+ Annotations: map[string]string{
+ ownerAnnotation: `{"ownerRefs":[{"operatorID":"old-operator-id","resource":{"kind":"ProxyGroup","name":"pg1","uid":"pg1-uid"}}]}`,
+ },
+ },
},
"owned_by_an_ingress": {
svc: &tailscale.VIPService{
diff --git a/tool/gocross/gocross-wrapper.ps1 b/tool/gocross/gocross-wrapper.ps1
index fe0b46996..324b220c8 100644
--- a/tool/gocross/gocross-wrapper.ps1
+++ b/tool/gocross/gocross-wrapper.ps1
@@ -114,7 +114,12 @@ $bootstrapScriptBlock = {
New-Item -Force -Path $toolchain -ItemType Directory | Out-Null
Start-ChildScope -ScriptBlock {
Set-Location -LiteralPath $toolchain
- tar --strip-components=1 -xf "$toolchain.tar.gz"
+
+ # Using an absolute path to the tar that ships with Windows
+ # to avoid conflicts with others (eg msys2).
+ $system32 = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::System)
+ $tar = Join-Path $system32 'tar.exe' -Resolve
+ & $tar --strip-components=1 -xf "$toolchain.tar.gz"
if ($LASTEXITCODE -ne 0) {
throw "tar failed with exit code $LASTEXITCODE"
}