From a443255b3e5e0b14f50de11a1476ff9cca98dc06 Mon Sep 17 00:00:00 2001 From: Kristoffer Dalby Date: Mon, 30 May 2022 12:39:41 +0200 Subject: [PATCH] Validate isOutdated against all namespaces This commit makes isOutdated validate a nodes necessity to update against all namespaces, and not just the nodes own namespace (which made more sense before). getLastStateChange is now uses the passed namespaces as a filter, meaning that not requesting any namespace will give you the total last updated state. In addition, the sync.Map is exchanged for a variant that uses generics which allows us to remove some casting logic. --- app.go | 24 ++++++++++++++++++------ flake.nix | 2 +- go.mod | 1 + go.sum | 2 ++ machine.go | 11 ++++++----- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app.go b/app.go index ebe8eb69..c0f18ded 100644 --- a/app.go +++ b/app.go @@ -25,6 +25,7 @@ import ( v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/patrickmn/go-cache" zerolog "github.com/philip-bui/grpc-zerolog" + "github.com/puzpuzpuz/xsync" zl "github.com/rs/zerolog" "github.com/rs/zerolog/log" ginprometheus "github.com/zsais/go-gin-prometheus" @@ -160,7 +161,7 @@ type Headscale struct { aclPolicy *ACLPolicy aclRules []tailcfg.FilterRule - lastStateChange sync.Map + lastStateChange *xsync.MapOf[time.Time] oidcProvider *oidc.Provider oauth2Config *oauth2.Config @@ -793,18 +794,29 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) { func (h *Headscale) setLastStateChangeToNow(namespace string) { now := time.Now().UTC() lastStateUpdate.WithLabelValues("", "headscale").Set(float64(now.Unix())) + if h.lastStateChange == nil { + h.lastStateChange = xsync.NewMapOf[time.Time]() + } h.lastStateChange.Store(namespace, now) } 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) + // getLastStateChange takes a list of namespaces as a "filter", if no namespaces + // are past, then use the entier list of namespaces and look for the last update + if len(namespaces) > 0 { + for _, namespace := range namespaces { + if lastChange, ok := h.lastStateChange.Load(namespace); ok { + times = append(times, lastChange) + } } + } else { + h.lastStateChange.Range(func(key string, value time.Time) bool { + times = append(times, value) + + return true + }) } sort.Slice(times, func(i, j int) bool { diff --git a/flake.nix b/flake.nix index cbdeaf7f..74aa665d 100644 --- a/flake.nix +++ b/flake.nix @@ -24,7 +24,7 @@ # When updating go.mod or go.sum, a new sha will need to be calculated, # update this if you have a mismatch after doing a change to thos files. - vendorSha256 = "sha256-e3s10NsadWTEk7H7SbEvePhihJ1x2dS9UfIqPNIbJqI="; + vendorSha256 = "sha256-b6qPOO/NmcXsAsSRWZlYXZKyRAF++DsL4TEZzRhQhME="; ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ]; }; diff --git a/go.mod b/go.mod index 69510a5b..2d224032 100644 --- a/go.mod +++ b/go.mod @@ -115,6 +115,7 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/puzpuzpuz/xsync v1.2.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 // indirect diff --git a/go.sum b/go.sum index 85c6034c..eaf15b8f 100644 --- a/go.sum +++ b/go.sum @@ -478,6 +478,8 @@ github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5b github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s= github.com/pterm/pterm v0.12.41 h1:e2BRfFo1H9nL8GY0S3ImbZqfZ/YimOk9XtkhoobKJVs= github.com/pterm/pterm v0.12.41/go.mod h1:LW/G4J2A42XlTaPTAGRPvbBfF4UXvHWhC6SN7ueU4jU= +github.com/puzpuzpuz/xsync v1.2.1 h1:faRb6HT9XN3IAhnE7IP0TnPpokPK42qFKXkhQVkWNwM= +github.com/puzpuzpuz/xsync v1.2.1/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= diff --git a/machine.go b/machine.go index d1ef246c..db2c63f5 100644 --- a/machine.go +++ b/machine.go @@ -9,7 +9,6 @@ import ( "strings" "time" - mapset "github.com/deckarep/golang-set/v2" v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/rs/zerolog/log" "google.golang.org/protobuf/types/known/timestamppb" @@ -469,10 +468,12 @@ func (h *Headscale) isOutdated(machine *Machine) bool { return true } - namespaceSet := mapset.NewSet[string]() - namespaceSet.Add(machine.Namespace.Name) - - lastChange := h.getLastStateChange(namespaceSet.ToSlice()...) + // Get the last update from all headscale namespaces to compare with our nodes + // last update. + // TODO(kradalby): Only request updates from namespaces where we can talk to nodes + // This would mostly be for a bit of performance, and can be calculated based on + // ACLs. + lastChange := h.getLastStateChange() lastUpdate := machine.CreatedAt if machine.LastSuccessfulUpdate != nil { lastUpdate = *machine.LastSuccessfulUpdate