diff --git a/CHANGELOG.md b/CHANGELOG.md index 22f05780b9..465adc87df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Allow nodes to use SSH agent forwarding [#2145](https://github.com/juanfont/headscale/pull/2145) - Fixed processing of fields in post request in MoveNode rpc [#2179](https://github.com/juanfont/headscale/pull/2179) - Added conversion of 'Hostname' to 'givenName' in a node with FQDN rules applied [#2198](https://github.com/juanfont/headscale/pull/2198) +- Fixed updating of hostname and givenName when it is updated in HostInfo [#2199](https://github.com/juanfont/headscale/pull/2199) ## 0.23.0 (2024-09-18) diff --git a/hscontrol/poll.go b/hscontrol/poll.go index 755265f3a4..a8ae01f44f 100644 --- a/hscontrol/poll.go +++ b/hscontrol/poll.go @@ -471,7 +471,7 @@ func (m *mapSession) handleEndpointUpdate() { // Check if the Hostinfo of the node has changed. // If it has changed, check if there has been a change to - // the routable IPs of the host and update update them in + // the routable IPs of the host and update them in // the database. Then send a Changed update // (containing the whole node object) to peers to inform about // the route change. @@ -510,6 +510,12 @@ func (m *mapSession) handleEndpointUpdate() { m.node.ID) } + // Check if there has been a change to Hostname and update them + // in the database. Then send a Changed update + // (containing the whole node object) to peers to inform about + // the hostname change. + m.node.ApplyHostnameFromHostInfo(m.req.Hostinfo) + if err := m.h.db.DB.Save(m.node).Error; err != nil { m.errf(err, "Failed to persist/update node in the database") http.Error(m.w, "", http.StatusInternalServerError) @@ -526,7 +532,8 @@ func (m *mapSession) handleEndpointUpdate() { ChangeNodes: []types.NodeID{m.node.ID}, Message: "called from handlePoll -> update", }, - m.node.ID) + m.node.ID, + ) m.w.WriteHeader(http.StatusOK) mapResponseEndpointUpdates.WithLabelValues("ok").Inc() diff --git a/hscontrol/types/node.go b/hscontrol/types/node.go index c702f23a2e..fc6e68da80 100644 --- a/hscontrol/types/node.go +++ b/hscontrol/types/node.go @@ -97,6 +97,11 @@ type ( Nodes []*Node ) +// IsAutomaticNameMode returns whether the `givenName` can be automatically changed based on the `Hostname` of the node. +func (node *Node) IsAutomaticNameMode() bool { + return node.GivenName == node.Hostname +} + // IsExpired returns whether the node registration has expired. func (node Node) IsExpired() bool { // If Expiry is not set, the client has not indicated that @@ -347,6 +352,22 @@ func (node *Node) RegisterMethodToV1Enum() v1.RegisterMethod { } } +// ApplyHostnameFromHostInfo takes a Hostinfo struct and updates the node. +func (node *Node) ApplyHostnameFromHostInfo(hostInfo *tailcfg.Hostinfo) { + if hostInfo == nil { + return + } + + if node.Hostname != hostInfo.Hostname { + if node.IsAutomaticNameMode() { + // TODO(hopleus): Add hostname conversion with FQDN rules applied + node.GivenName = hostInfo.Hostname + } + + node.Hostname = hostInfo.Hostname + } +} + // ApplyPeerChange takes a PeerChange struct and updates the node. func (node *Node) ApplyPeerChange(change *tailcfg.PeerChange) { if change.Key != nil { diff --git a/hscontrol/types/node_test.go b/hscontrol/types/node_test.go index 1d0e7939ee..21c500c162 100644 --- a/hscontrol/types/node_test.go +++ b/hscontrol/types/node_test.go @@ -337,6 +337,66 @@ func TestPeerChangeFromMapRequest(t *testing.T) { } } +func TestApplyHostnameFromHostInfo(t *testing.T) { + tests := []struct { + name string + nodeBefore Node + change *tailcfg.Hostinfo + want Node + }{ + { + name: "hostinfo-not-exists", + nodeBefore: Node{ + GivenName: "manual-test.local", + Hostname: "TestHost.Local", + }, + change: nil, + want: Node{ + GivenName: "manual-test.local", + Hostname: "TestHost.Local", + }, + }, + { + name: "hostinfo-exists-no-automatic-givenName", + nodeBefore: Node{ + GivenName: "manual-test.local", + Hostname: "TestHost.Local", + }, + change: &tailcfg.Hostinfo{ + Hostname: "NewHostName.Local", + }, + want: Node{ + GivenName: "manual-test.local", + Hostname: "NewHostName.Local", + }, + }, + { + name: "hostinfo-exists-automatic-givenName", + nodeBefore: Node{ + GivenName: "AutomaticName.Test", + Hostname: "AutomaticName.Test", + }, + change: &tailcfg.Hostinfo{ + Hostname: "NewHostName.Local", + }, + want: Node{ + GivenName: "NewHostName.Local", + Hostname: "NewHostName.Local", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.nodeBefore.ApplyHostnameFromHostInfo(tt.change) + + if diff := cmp.Diff(tt.want, tt.nodeBefore, util.Comparers...); diff != "" { + t.Errorf("Patch unexpected result (-want +got):\n%s", diff) + } + }) + } +} + func TestApplyPeerChange(t *testing.T) { tests := []struct { name string