From 8abc7575cd7083060d19fb007dbf7117b2319738 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 5 Oct 2021 16:17:18 +0000 Subject: [PATCH 1/8] Tear out all the complicated update logic There is some weird behaviour that seem to storm the update channel. And our solution with a central map of update channels isnt particularly elegant. For now, replace all the complicated stuff with a simple channel that checks roughly every 10s if the node is up to date. Only generate and update if there has been changes. --- poll.go | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/poll.go b/poll.go index a33d341b..df3112c4 100644 --- a/poll.go +++ b/poll.go @@ -140,10 +140,9 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { Str("id", c.Param("id")). Str("machine", m.Name). Msg("Loading or creating update channel") - updateChan := h.getOrOpenUpdateChannel(m) + updateChan := make(chan struct{}) pollDataChan := make(chan []byte) - // defer close(pollData) keepAliveChan := make(chan []byte) @@ -160,7 +159,7 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { // It sounds like we should update the nodes when we have received a endpoint update // even tho the comments in the tailscale code dont explicitly say so. updateRequestsFromNode.WithLabelValues("endpoint-update").Inc() - go h.notifyChangesToPeers(m) + go func() { updateChan <- struct{}{} }() return } else if req.OmitPeers && req.Stream { log.Warn(). @@ -186,7 +185,7 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { Str("machine", m.Name). Msg("Notifying peers") updateRequestsFromNode.WithLabelValues("full-update").Inc() - go h.notifyChangesToPeers(m) + go func() { updateChan <- struct{}{} }() h.PollNetMapStream(c, m, req, mKey, pollDataChan, keepAliveChan, updateChan, cancelKeepAlive) log.Trace(). @@ -206,10 +205,10 @@ func (h *Headscale) PollNetMapStream( mKey wgkey.Key, pollDataChan chan []byte, keepAliveChan chan []byte, - updateChan <-chan struct{}, + updateChan chan struct{}, cancelKeepAlive chan struct{}, ) { - go h.scheduledPollWorker(cancelKeepAlive, keepAliveChan, mKey, req, m) + go h.scheduledPollWorker(cancelKeepAlive, updateChan, keepAliveChan, mKey, req, m) c.Stream(func(w io.Writer) bool { log.Trace(). @@ -423,7 +422,8 @@ func (h *Headscale) PollNetMapStream( Str("machine", m.Name). Str("channel", "Done"). Msg("Closing update channel") - h.closeUpdateChannel(m) + //h.closeUpdateChannel(m) + close(updateChan) log.Trace(). Str("handler", "PollNetMapStream"). @@ -446,13 +446,14 @@ func (h *Headscale) PollNetMapStream( func (h *Headscale) scheduledPollWorker( cancelChan <-chan struct{}, + updateChan chan<- struct{}, keepAliveChan chan<- []byte, mKey wgkey.Key, req tailcfg.MapRequest, m *Machine, ) { keepAliveTicker := time.NewTicker(60 * time.Second) - updateCheckerTicker := time.NewTicker(30 * time.Second) + updateCheckerTicker := time.NewTicker(10 * time.Second) for { select { @@ -476,16 +477,12 @@ func (h *Headscale) scheduledPollWorker( keepAliveChan <- data case <-updateCheckerTicker.C: - // Send an update request regardless of outdated or not, if data is sent - // to the node is determined in the updateChan consumer block - err := h.sendRequestOnUpdateChannel(m) - if err != nil { - log.Error(). - Str("func", "keepAlive"). - Str("machine", m.Name). - Err(err). - Msgf("Failed to send update request to %s", m.Name) - } + log.Debug(). + Str("func", "scheduledPollWorker"). + Str("machine", m.Name). + Msg("Sending update request") + updateRequestsFromNode.WithLabelValues("scheduled-update").Inc() + updateChan <- struct{}{} } } } From a01a0d103924348fc2f76cd6d2cea8b20f6d71bd Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 5 Oct 2021 16:24:46 +0000 Subject: [PATCH 2/8] Remove unstable update channel, replace with state updates --- app.go | 6 +-- machine.go | 106 -------------------------------------------------- namespaces.go | 19 +++------ 3 files changed, 7 insertions(+), 124 deletions(-) diff --git a/app.go b/app.go index 27b9672d..a6e547f5 100644 --- a/app.go +++ b/app.go @@ -65,9 +65,6 @@ type Headscale struct { aclPolicy *ACLPolicy aclRules *[]tailcfg.FilterRule - clientsUpdateChannels sync.Map - clientsUpdateChannelMutex sync.Mutex - lastStateChange sync.Map } @@ -145,10 +142,9 @@ func (h *Headscale) expireEphemeralNodesWorker() { if err != nil { log.Error().Err(err).Str("machine", m.Name).Msg("🤮 Cannot delete ephemeral machine from the database") } - updateRequestsFromNode.WithLabelValues("ephemeral-node-update").Inc() - h.notifyChangesToPeers(&m) } } + h.setLastStateChangeToNow(ns.Name) } } diff --git a/machine.go b/machine.go index 7a9df2e8..fcc8255c 100644 --- a/machine.go +++ b/machine.go @@ -2,7 +2,6 @@ package headscale import ( "encoding/json" - "errors" "fmt" "sort" "strconv" @@ -214,111 +213,6 @@ func (m *Machine) GetHostInfo() (*tailcfg.Hostinfo, error) { return &hostinfo, nil } -func (h *Headscale) notifyChangesToPeers(m *Machine) { - peers, err := h.getPeers(m) - if err != nil { - log.Error(). - Str("func", "notifyChangesToPeers"). - Str("machine", m.Name). - Msgf("Error getting peers: %s", err) - return - } - for _, peer := range peers { - log.Info(). - Str("func", "notifyChangesToPeers"). - Str("machine", m.Name). - Str("peer", peer.Name). - Str("address", peer.IPAddress). - Msgf("Notifying peer %s (%s)", peer.Name, peer.IPAddress) - err := h.sendRequestOnUpdateChannel(&peer) - if err != nil { - log.Info(). - Str("func", "notifyChangesToPeers"). - Str("machine", m.Name). - Str("peer", peer.Name). - Msgf("Peer %s does not have an open update client, skipping.", peer.Name) - continue - } - log.Trace(). - Str("func", "notifyChangesToPeers"). - Str("machine", m.Name). - Str("peer", peer.Name). - Str("address", peer.IPAddress). - Msgf("Notified peer %s (%s)", peer.Name, peer.IPAddress) - } -} - -func (h *Headscale) getOrOpenUpdateChannel(m *Machine) <-chan struct{} { - var updateChan chan struct{} - if storedChan, ok := h.clientsUpdateChannels.Load(m.ID); ok { - if unwrapped, ok := storedChan.(chan struct{}); ok { - updateChan = unwrapped - } else { - log.Error(). - Str("handler", "openUpdateChannel"). - Str("machine", m.Name). - Msg("Failed to convert update channel to struct{}") - } - } else { - log.Debug(). - Str("handler", "openUpdateChannel"). - Str("machine", m.Name). - Msg("Update channel not found, creating") - - updateChan = make(chan struct{}) - h.clientsUpdateChannels.Store(m.ID, updateChan) - } - return updateChan -} - -func (h *Headscale) closeUpdateChannel(m *Machine) { - h.clientsUpdateChannelMutex.Lock() - defer h.clientsUpdateChannelMutex.Unlock() - - if storedChan, ok := h.clientsUpdateChannels.Load(m.ID); ok { - if unwrapped, ok := storedChan.(chan struct{}); ok { - close(unwrapped) - } - } - h.clientsUpdateChannels.Delete(m.ID) -} - -func (h *Headscale) sendRequestOnUpdateChannel(m *Machine) error { - h.clientsUpdateChannelMutex.Lock() - defer h.clientsUpdateChannelMutex.Unlock() - - pUp, ok := h.clientsUpdateChannels.Load(uint64(m.ID)) - if ok { - log.Info(). - Str("func", "requestUpdate"). - Str("machine", m.Name). - Msgf("Notifying peer %s", m.Name) - - if update, ok := pUp.(chan struct{}); ok { - log.Trace(). - Str("func", "requestUpdate"). - Str("machine", m.Name). - Msgf("Update channel is %#v", update) - - updateRequestsToNode.Inc() - update <- struct{}{} - - log.Trace(). - Str("func", "requestUpdate"). - Str("machine", m.Name). - Msgf("Notified machine %s", m.Name) - } - } else { - err := errors.New("machine does not have an open update channel") - log.Info(). - Str("func", "requestUpdate"). - Str("machine", m.Name). - Msgf("Machine %s does not have an open update channel", m.Name) - return err - } - return nil -} - func (h *Headscale) isOutdated(m *Machine) bool { err := h.UpdateMachine(m) if err != nil { diff --git a/namespaces.go b/namespaces.go index 75b6eab5..2bf62bb3 100644 --- a/namespaces.go +++ b/namespaces.go @@ -176,24 +176,17 @@ func (h *Headscale) checkForNamespacesPendingUpdates() { return } - names := []string{} - err = json.Unmarshal([]byte(v), &names) + namespaces := []string{} + err = json.Unmarshal([]byte(v), &namespaces) if err != nil { return } - for _, name := range names { + for _, namespace := range namespaces { log.Trace(). Str("func", "RequestMapUpdates"). - Str("machine", name). - Msg("Sending updates to nodes in namespace") - machines, err := h.ListMachinesInNamespace(name) - if err != nil { - continue - } - for _, m := range *machines { - updateRequestsFromNode.WithLabelValues("namespace-update").Inc() - h.notifyChangesToPeers(&m) - } + Str("machine", namespace). + Msg("Sending updates to nodes in namespacespace") + h.setLastStateChangeToNow(namespace) } newV, err := h.getValue("namespaces_pending_updates") if err != nil { From 722084fbd36399b0dbc8d358d141b92b8f2e7fee Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 5 Oct 2021 16:51:42 +0000 Subject: [PATCH 3/8] Comment out aggressive logging --- api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.go b/api.go index 17306b32..ce3b242e 100644 --- a/api.go +++ b/api.go @@ -277,7 +277,7 @@ func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Ma log.Trace(). Str("func", "getMapResponse"). Str("machine", req.Hostinfo.Hostname). - Interface("payload", resp). + // Interface("payload", resp). Msgf("Generated map response: %s", tailMapResponseToString(resp)) var respBody []byte From c582c8d206fb3212f9aa71fbd4f1d74f7a76b1ec Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Tue, 5 Oct 2021 21:59:15 +0000 Subject: [PATCH 4/8] Update metrics for new code --- metrics.go | 10 +++++----- poll.go | 24 +++++++++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/metrics.go b/metrics.go index d516fade..0d3dca34 100644 --- a/metrics.go +++ b/metrics.go @@ -26,16 +26,16 @@ var ( Namespace: prometheusNamespace, Name: "update_request_from_node_total", Help: "The number of updates requested by a node/update function", - }, []string{"state"}) - updateRequestsToNode = promauto.NewCounter(prometheus.CounterOpts{ + }, []string{"namespace", "machine", "state"}) + updateRequestsSentToNode = promauto.NewCounterVec(prometheus.CounterOpts{ Namespace: prometheusNamespace, - Name: "update_request_to_node_total", + Name: "update_request_sent_to_node_total", Help: "The number of calls/messages issued on a specific nodes update channel", - }) + }, []string{"namespace", "machine", "status"}) //TODO(kradalby): This is very debugging, we might want to remove it. updateRequestsReceivedOnChannel = promauto.NewCounterVec(prometheus.CounterOpts{ Namespace: prometheusNamespace, Name: "update_request_received_on_channel_total", Help: "The number of update requests received on an update channel", - }, []string{"machine"}) + }, []string{"namespace", "machine"}) ) diff --git a/poll.go b/poll.go index df3112c4..6a652805 100644 --- a/poll.go +++ b/poll.go @@ -158,7 +158,7 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { // It sounds like we should update the nodes when we have received a endpoint update // even tho the comments in the tailscale code dont explicitly say so. - updateRequestsFromNode.WithLabelValues("endpoint-update").Inc() + updateRequestsFromNode.WithLabelValues(m.Name, m.Namespace.Name, "endpoint-update").Inc() go func() { updateChan <- struct{}{} }() return } else if req.OmitPeers && req.Stream { @@ -184,7 +184,7 @@ func (h *Headscale) PollNetMapHandler(c *gin.Context) { Str("handler", "PollNetMap"). Str("machine", m.Name). Msg("Notifying peers") - updateRequestsFromNode.WithLabelValues("full-update").Inc() + updateRequestsFromNode.WithLabelValues(m.Name, m.Namespace.Name, "full-update").Inc() go func() { updateChan <- struct{}{} }() h.PollNetMapStream(c, m, req, mKey, pollDataChan, keepAliveChan, updateChan, cancelKeepAlive) @@ -324,7 +324,7 @@ func (h *Headscale) PollNetMapStream( Str("machine", m.Name). Str("channel", "update"). Msg("Received a request for update") - updateRequestsReceivedOnChannel.WithLabelValues(m.Name).Inc() + updateRequestsReceivedOnChannel.WithLabelValues(m.Name, m.Namespace.Name).Inc() if h.isOutdated(m) { log.Debug(). Str("handler", "PollNetMapStream"). @@ -349,6 +349,7 @@ func (h *Headscale) PollNetMapStream( Str("channel", "update"). Err(err). Msg("Could not write the map response") + updateRequestsSentToNode.WithLabelValues(m.Name, m.Namespace.Name, "failed").Inc() return false } log.Trace(). @@ -356,14 +357,15 @@ func (h *Headscale) PollNetMapStream( Str("machine", m.Name). Str("channel", "update"). Msg("Updated Map has been sent") + updateRequestsSentToNode.WithLabelValues(m.Name, m.Namespace.Name, "success").Inc() - // Keep track of the last successful update, - // we sometimes end in a state were the update - // is not picked up by a client and we use this - // to determine if we should "force" an update. - // TODO(kradalby): Abstract away all the database calls, this can cause race conditions - // when an outdated machine object is kept alive, e.g. db is update from - // command line, but then overwritten. + // Keep track of the last successful update, + // we sometimes end in a state were the update + // is not picked up by a client and we use this + // to determine if we should "force" an update. + // TODO(kradalby): Abstract away all the database calls, this can cause race conditions + // when an outdated machine object is kept alive, e.g. db is update from + // command line, but then overwritten. err = h.UpdateMachine(m) if err != nil { log.Error(). @@ -481,7 +483,7 @@ func (h *Headscale) scheduledPollWorker( Str("func", "scheduledPollWorker"). Str("machine", m.Name). Msg("Sending update request") - updateRequestsFromNode.WithLabelValues("scheduled-update").Inc() + updateRequestsFromNode.WithLabelValues(m.Name, m.Namespace.Name, "scheduled-update").Inc() updateChan <- struct{}{} } } From ba391bc2eda93cedf749f2242b0caa42a036ae71 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 6 Oct 2021 19:32:15 +0000 Subject: [PATCH 5/8] Account for updates in shared namespaces --- app.go | 2 +- machine.go | 23 +++++++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/app.go b/app.go index a6e547f5..78412a24 100644 --- a/app.go +++ b/app.go @@ -250,8 +250,8 @@ func (h *Headscale) setLastStateChangeToNow(namespace string) { func (h *Headscale) getLastStateChange(namespace string) time.Time { if wrapped, ok := h.lastStateChange.Load(namespace); ok { lastChange, _ := wrapped.(time.Time) - return lastChange + return lastChange } now := time.Now().UTC() diff --git a/machine.go b/machine.go index fcc8255c..acc9e0e2 100644 --- a/machine.go +++ b/machine.go @@ -65,7 +65,7 @@ func (h *Headscale) getDirectPeers(m *Machine) (Machines, error) { if err := h.db.Where("namespace_id = ? AND machine_key <> ? AND registered", m.NamespaceID, m.MachineKey).Find(&machines).Error; err != nil { log.Error().Err(err).Msg("Error accessing db") - return nil, err + return Machines{}, err } sort.Slice(machines, func(i, j int) bool { return machines[i].ID < machines[j].ID }) @@ -87,7 +87,7 @@ func (h *Headscale) getShared(m *Machine) (Machines, error) { sharedMachines := []SharedMachine{} if err := h.db.Preload("Namespace").Preload("Machine").Where("namespace_id = ?", m.NamespaceID).Find(&sharedMachines).Error; err != nil { - return nil, err + return Machines{}, err } peers := make(Machines, 0) @@ -111,7 +111,7 @@ func (h *Headscale) getPeers(m *Machine) (Machines, error) { Str("func", "getPeers"). Err(err). Msg("Cannot fetch peers") - return nil, err + return Machines{}, err } shared, err := h.getShared(m) @@ -120,7 +120,7 @@ func (h *Headscale) getPeers(m *Machine) (Machines, error) { Str("func", "getDirectPeers"). Err(err). Msg("Cannot fetch peers") - return nil, err + return Machines{}, err } peers := append(direct, shared...) @@ -219,6 +219,21 @@ func (h *Headscale) isOutdated(m *Machine) bool { return true } + sharedMachines, _ := h.getShared(m) + + // Check if any of our shared namespaces has updates that we have + // not propagated. + for _, sharedMachine := range sharedMachines { + lastChange := h.getLastStateChange(sharedMachine.Namespace.Name) + log.Trace(). + Str("func", "keepAlive"). + Str("machine", m.Name). + Time("last_successful_update", *m.LastSuccessfulUpdate). + Time("last_state_change", lastChange). + Msgf("Checking if %s is missing updates", m.Name) + return m.LastSuccessfulUpdate.Before(lastChange) + } + lastChange := h.getLastStateChange(m.Namespace.Name) log.Trace(). Str("func", "keepAlive"). From 95f726fb31eed5d2430c8359a514796365a077d9 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 6 Oct 2021 19:56:14 +0000 Subject: [PATCH 6/8] Fix logic --- machine.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/machine.go b/machine.go index acc9e0e2..ebe6f84b 100644 --- a/machine.go +++ b/machine.go @@ -231,7 +231,10 @@ func (h *Headscale) isOutdated(m *Machine) bool { Time("last_successful_update", *m.LastSuccessfulUpdate). Time("last_state_change", lastChange). Msgf("Checking if %s is missing updates", m.Name) - return m.LastSuccessfulUpdate.Before(lastChange) + // Only return if we have a shared node with a newer update. + if m.LastSuccessfulUpdate.Before(lastChange) { + return true + } } lastChange := h.getLastStateChange(m.Namespace.Name) From f0c54490ed9f8deec835cf59a59c16780a4374fe Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Wed, 6 Oct 2021 22:06:07 +0000 Subject: [PATCH 7/8] Allow multiple namespaces to be checked for state at the same time --- app.go | 29 ++++++++++++++++++++++------- go.mod | 1 + go.sum | 2 ++ machine.go | 23 +++++++++++------------ 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/app.go b/app.go index 78412a24..acaa7f1b 100644 --- a/app.go +++ b/app.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "os" + "sort" "strings" "sync" "time" @@ -247,14 +248,28 @@ func (h *Headscale) setLastStateChangeToNow(namespace string) { h.lastStateChange.Store(namespace, now) } -func (h *Headscale) getLastStateChange(namespace string) time.Time { - if wrapped, ok := h.lastStateChange.Load(namespace); ok { - lastChange, _ := wrapped.(time.Time) +func (h *Headscale) getLastStateChange(namespaces ...string) time.Time { + times := []time.Time{} + + for _, namespace := range namespaces { + if wrapped, ok := h.lastStateChange.Load(namespace); ok { + lastChange, _ := wrapped.(time.Time) + + times = append(times, lastChange) + } - return lastChange } - now := time.Now().UTC() - h.lastStateChange.Store(namespace, now) - return now + sort.Slice(times, func(i, j int) bool { + return times[i].After(times[j]) + }) + + log.Trace().Msgf("Latest times %#v", times) + + if len(times) == 0 { + return time.Now().UTC() + + } else { + return times[0] + } } diff --git a/go.mod b/go.mod index 70428315..1fadd6b7 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/docker/cli v20.10.8+incompatible // indirect github.com/docker/docker v20.10.8+incompatible // indirect github.com/efekarakus/termcolor v1.0.1 + github.com/fatih/set v0.2.1 // indirect github.com/gin-gonic/gin v1.7.4 github.com/gofrs/uuid v4.0.0+incompatible github.com/google/go-github v17.0.0+incompatible // indirect diff --git a/go.sum b/go.sum index 6dc92b6d..b429ca95 100644 --- a/go.sum +++ b/go.sum @@ -204,6 +204,8 @@ github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:Pjfxu github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= +github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= diff --git a/machine.go b/machine.go index ebe6f84b..326c2fca 100644 --- a/machine.go +++ b/machine.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/fatih/set" "github.com/rs/zerolog/log" "gorm.io/datatypes" @@ -221,23 +222,21 @@ func (h *Headscale) isOutdated(m *Machine) bool { sharedMachines, _ := h.getShared(m) + namespaceSet := set.New(set.ThreadSafe) + namespaceSet.Add(m.Namespace.Name) + // Check if any of our shared namespaces has updates that we have // not propagated. for _, sharedMachine := range sharedMachines { - lastChange := h.getLastStateChange(sharedMachine.Namespace.Name) - log.Trace(). - Str("func", "keepAlive"). - Str("machine", m.Name). - Time("last_successful_update", *m.LastSuccessfulUpdate). - Time("last_state_change", lastChange). - Msgf("Checking if %s is missing updates", m.Name) - // Only return if we have a shared node with a newer update. - if m.LastSuccessfulUpdate.Before(lastChange) { - return true - } + namespaceSet.Add(sharedMachine.Namespace.Name) } - lastChange := h.getLastStateChange(m.Namespace.Name) + namespaces := make([]string, namespaceSet.Size()) + for index, namespace := range namespaceSet.List() { + namespaces[index] = namespace.(string) + } + + lastChange := h.getLastStateChange(namespaces...) log.Trace(). Str("func", "keepAlive"). Str("machine", m.Name). From 06f56411dd040d26269273c8377880d86321ba14 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Thu, 7 Oct 2021 15:45:45 +0100 Subject: [PATCH 8/8] Update machine.go --- machine.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/machine.go b/machine.go index 326c2fca..21d774b4 100644 --- a/machine.go +++ b/machine.go @@ -217,6 +217,8 @@ func (m *Machine) GetHostInfo() (*tailcfg.Hostinfo, error) { func (h *Headscale) isOutdated(m *Machine) bool { err := h.UpdateMachine(m) if err != nil { + // It does not seem meaningful to propagate this error as the end result + // will have to be that the machine has to be considered outdated. return true }