mirror of
https://github.com/juanfont/headscale.git
synced 2025-11-20 17:56:02 -05:00
hscontrol/state,db: preserve node expiry on MapRequest updates
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
This commit is contained in:
committed by
Kristoffer Dalby
parent
773a46a968
commit
4a8dc2d445
@@ -325,7 +325,11 @@ func (db *HSDatabase) BackfillNodeIPs(i *IPAllocator) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
err := tx.Save(node).Error
|
// Use Updates() with Select() to only update IP fields, avoiding overwriting
|
||||||
|
// other fields like Expiry. We need Select() because Updates() alone skips
|
||||||
|
// zero values, but we DO want to update IPv4/IPv6 to nil when removing them.
|
||||||
|
// See issue #2862.
|
||||||
|
err := tx.Model(node).Select("ipv4", "ipv6").Updates(node).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("saving node(%d) after adding IPs: %w", node.ID, err)
|
return fmt.Errorf("saving node(%d) after adding IPs: %w", node.ID, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -386,7 +386,11 @@ func (s *State) persistNodeToDB(node types.NodeView) (types.NodeView, change.Cha
|
|||||||
|
|
||||||
nodePtr := node.AsStruct()
|
nodePtr := node.AsStruct()
|
||||||
|
|
||||||
if err := s.db.DB.Save(nodePtr).Error; err != nil {
|
// Use Omit("expiry") to prevent overwriting expiry during MapRequest updates.
|
||||||
|
// Expiry should only be updated through explicit SetNodeExpiry calls or re-registration.
|
||||||
|
// See: https://github.com/juanfont/headscale/issues/2862
|
||||||
|
err := s.db.DB.Omit("expiry").Updates(nodePtr).Error
|
||||||
|
if err != nil {
|
||||||
return types.NodeView{}, change.EmptySet, fmt.Errorf("saving node: %w", err)
|
return types.NodeView{}, change.EmptySet, fmt.Errorf("saving node: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user