[PR #2875] [MERGED] {state,db}: preserve node expiry on MapRequest updates #2910

Closed
opened 2025-12-29 04:19:37 +01:00 by adam · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/juanfont/headscale/pull/2875
Author: @kradalby
Created: 11/10/2025
Status: Merged
Merged: 11/11/2025
Merged by: @kradalby

Base: mainHead: kradalby/2862-expiry-oidc


📝 Commits (5)

  • 5901df3 integration: add test to replicate #2862
  • 1acf213 hscontrol/state,db: preserve node expiry on MapRequest updates
  • dde20ad hscontrol: use Updates() instead of Save() for partial updates
  • 36a8a93 hscontrol/db: fix RenameUser to use Updates()
  • 997accf fix: preserve node expiry when tailscaled restarts

📊 Changes

9 files changed (+295 additions, -17 deletions)

View changed files

📝 .github/workflows/test-integration.yaml (+1 -0)
📝 hscontrol/auth.go (+8 -0)
📝 hscontrol/db/ip.go (+5 -1)
📝 hscontrol/db/node.go (+0 -7)
📝 hscontrol/db/preauth_keys.go (+3 -2)
hscontrol/db/user_update_test.go (+134 -0)
📝 hscontrol/db/users.go (+2 -1)
📝 hscontrol/state/state.go (+14 -6)
📝 integration/auth_oidc_test.go (+128 -0)

📄 Description

Fixes a regression introduced in v0.27.0 where node expiry times were being reset to zero when tailscaled restarts and sends a MapRequest.

The issue was caused by using GORM's Save() method in persistNodeToDB(), which overwrites ALL fields including zero values. When a MapRequest updates a node (without including expiry information), Save() would overwrite the database expiry field with a zero value.

Changed to use Updates() which only updates non-zero values, preserving existing database values when struct pointer fields are nil.

In BackfillNodeIPs, we need to explicitly update IPv4/IPv6 fields even when nil (to remove IPs), so we use Select() to specify those fields.

Added regression test that validates expiry is preserved after MapRequest.

Fixes #2862

claude was used in this PR.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/juanfont/headscale/pull/2875 **Author:** [@kradalby](https://github.com/kradalby) **Created:** 11/10/2025 **Status:** ✅ Merged **Merged:** 11/11/2025 **Merged by:** [@kradalby](https://github.com/kradalby) **Base:** `main` ← **Head:** `kradalby/2862-expiry-oidc` --- ### 📝 Commits (5) - [`5901df3`](https://github.com/juanfont/headscale/commit/5901df3002f28067f2bf6c71809e86d4398ad57a) integration: add test to replicate #2862 - [`1acf213`](https://github.com/juanfont/headscale/commit/1acf2130057c1e129ee12d13df59356ddd46d928) hscontrol/state,db: preserve node expiry on MapRequest updates - [`dde20ad`](https://github.com/juanfont/headscale/commit/dde20ade71a3ad6d3e6e9ec4c46dec187e678d06) hscontrol: use Updates() instead of Save() for partial updates - [`36a8a93`](https://github.com/juanfont/headscale/commit/36a8a935794eb1ce0b24fdc8c4927aa83cf3c815) hscontrol/db: fix RenameUser to use Updates() - [`997accf`](https://github.com/juanfont/headscale/commit/997accf67c8bc42a296aad1d87be8e81c89a6397) fix: preserve node expiry when tailscaled restarts ### 📊 Changes **9 files changed** (+295 additions, -17 deletions) <details> <summary>View changed files</summary> 📝 `.github/workflows/test-integration.yaml` (+1 -0) 📝 `hscontrol/auth.go` (+8 -0) 📝 `hscontrol/db/ip.go` (+5 -1) 📝 `hscontrol/db/node.go` (+0 -7) 📝 `hscontrol/db/preauth_keys.go` (+3 -2) ➕ `hscontrol/db/user_update_test.go` (+134 -0) 📝 `hscontrol/db/users.go` (+2 -1) 📝 `hscontrol/state/state.go` (+14 -6) 📝 `integration/auth_oidc_test.go` (+128 -0) </details> ### 📄 Description Fixes a regression introduced in v0.27.0 where node expiry times were being reset to zero when tailscaled restarts and sends a MapRequest. The issue was caused by using GORM's Save() method in persistNodeToDB(), which overwrites ALL fields including zero values. When a MapRequest updates a node (without including expiry information), Save() would overwrite the database expiry field with a zero value. Changed to use Updates() which only updates non-zero values, preserving existing database values when struct pointer fields are nil. In BackfillNodeIPs, we need to explicitly update IPv4/IPv6 fields even when nil (to remove IPs), so we use Select() to specify those fields. Added regression test that validates expiry is preserved after MapRequest. Fixes #2862 claude was used in this PR. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
adam added the pull-request label 2025-12-29 04:19:37 +01:00
adam closed this issue 2025-12-29 04:19:37 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/headscale#2910