From fc20b38851c0d27589fcf9d55f28b47678bca0db Mon Sep 17 00:00:00 2001 From: Vishal Dalwadi <51291657+VishalDalwadi@users.noreply.github.com> Date: Thu, 6 Nov 2025 15:25:52 +0530 Subject: [PATCH] Merge pull request #3718 from gravitl/fix/v1.2.0 Fixes v1.2.0 --- controllers/node.go | 11 ++++++ controllers/server.go | 4 +++ logic/metrics.go | 64 +++++++++++++++++++++++++++++++++++ models/metrics.go | 1 + pro/auth/headless_callback.go | 2 +- pro/auth/register_callback.go | 2 +- pro/email/invite.go | 2 +- pro/initialize.go | 2 ++ pro/logic/metrics.go | 1 + 9 files changed, 86 insertions(+), 3 deletions(-) diff --git a/controllers/node.go b/controllers/node.go index b6828d17..cd2a0d98 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -704,6 +704,17 @@ func updateNode(w http.ResponseWriter, r *http.Request) { if servercfg.IsDNSMode() { logic.SetDNS() } + if !newNode.Connected { + metrics, err := logic.GetMetrics(newNode.ID.String()) + if err == nil { + for peer, connectivity := range metrics.Connectivity { + connectivity.Connected = false + metrics.Connectivity[peer] = connectivity + } + + _ = logic.UpdateMetrics(newNode.ID.String(), metrics) + } + } }(aclUpdate, relayUpdate, newNode) } diff --git a/controllers/server.go b/controllers/server.go index eafa1fb3..17e45290 100644 --- a/controllers/server.go +++ b/controllers/server.go @@ -282,6 +282,10 @@ func reInit(curr, new models.ServerSettings, force bool) { logic.EmailInit() logic.SetVerbosity(int(logic.GetServerSettings().Verbosity)) logic.ResetIDPSyncHook() + if curr.MetricInterval != new.MetricInterval { + logic.GetMetricsMonitor().Stop() + logic.GetMetricsMonitor().Start() + } // check if auto update is changed if force { if curr.NetclientAutoUpdate != new.NetclientAutoUpdate { diff --git a/logic/metrics.go b/logic/metrics.go index 2ac11552..c82e1d4b 100644 --- a/logic/metrics.go +++ b/logic/metrics.go @@ -1,9 +1,73 @@ package logic import ( + "context" + "math" + "strconv" + "time" + "github.com/gravitl/netmaker/models" ) +type MetricsMonitor struct { + cancel context.CancelFunc +} + +var metricsMonitor MetricsMonitor + +func GetMetricsMonitor() *MetricsMonitor { + return &metricsMonitor +} + +func (m *MetricsMonitor) Start() { + if m.cancel != nil { + m.cancel() + m.cancel = nil + } + + var ctx context.Context + ctx, m.cancel = context.WithCancel(context.Background()) + + go func(ctx context.Context) { + metricsInterval, _ := strconv.Atoi(GetServerSettings().MetricInterval) + if metricsInterval == 0 { + return + } + + checkInterval := time.Duration(2*metricsInterval) * time.Minute + for { + select { + case <-time.After(checkInterval): + nodes, _ := GetAllNodes() + for _, node := range nodes { + if node.Connected || node.PendingDelete { + continue + } + + nodeMetrics, err := GetMetrics(node.ID.String()) + if err == nil { + inc := math.Round(float64(time.Since(nodeMetrics.UpdatedAt)) / float64(time.Minute)) + for peer, peerMetrics := range nodeMetrics.Connectivity { + peerMetrics.TotalTime += int64(inc) + peerMetrics.PercentUp = 100.0 * (float64(peerMetrics.Uptime) / float64(peerMetrics.TotalTime)) + nodeMetrics.Connectivity[peer] = peerMetrics + } + + _ = UpdateMetrics(node.ID.String(), nodeMetrics) + } + } + case <-ctx.Done(): + return + } + } + }(ctx) +} + +func (m *MetricsMonitor) Stop() { + m.cancel() + m.cancel = nil +} + var DeleteMetrics = func(string) error { return nil } diff --git a/models/metrics.go b/models/metrics.go index c364c18e..5f0c5660 100644 --- a/models/metrics.go +++ b/models/metrics.go @@ -10,6 +10,7 @@ type Metrics struct { NodeID string `json:"node_id" bson:"node_id" yaml:"node_id"` NodeName string `json:"node_name" bson:"node_name" yaml:"node_name"` Connectivity map[string]Metric `json:"connectivity" bson:"connectivity" yaml:"connectivity"` + UpdatedAt time.Time `json:"updated_at" bson:"updated_at" yaml:"updated_at"` } // Metric - holds a metric for data between nodes diff --git a/pro/auth/headless_callback.go b/pro/auth/headless_callback.go index bf1fcfff..ddaab255 100644 --- a/pro/auth/headless_callback.go +++ b/pro/auth/headless_callback.go @@ -98,7 +98,7 @@ func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) { var response bytes.Buffer if err := ssoCallbackTemplate.Execute(&response, ssoCallbackTemplateConfig{ User: userClaims.getUserName(), - Verb: "Authenticated", + Verb: "authenticated", }); err != nil { logger.Log(0, "Could not render SSO callback template ", err.Error()) response := returnErrTemplate(userClaims.getUserName(), "Could not render SSO callback template", state, reqKeyIf) diff --git a/pro/auth/register_callback.go b/pro/auth/register_callback.go index a2085c40..1c655a7d 100644 --- a/pro/auth/register_callback.go +++ b/pro/auth/register_callback.go @@ -86,7 +86,7 @@ func HandleHostSSOCallback(w http.ResponseWriter, r *http.Request) { var response bytes.Buffer if err := ssoCallbackTemplate.Execute(&response, ssoCallbackTemplateConfig{ User: userClaims.getUserName(), - Verb: "Authenticated", + Verb: "authenticated", }); err != nil { logger.Log(0, "Could not render SSO callback template ", err.Error()) response := returnErrTemplate(reqKeyIf.User, "Could not render SSO callback template", state, reqKeyIf) diff --git a/pro/email/invite.go b/pro/email/invite.go index dd4f4767..3637cec9 100644 --- a/pro/email/invite.go +++ b/pro/email/invite.go @@ -53,7 +53,7 @@ func (invite UserInvitedMail) GetBody(info Notification) string { WithHtml(""). WithParagraph("Important Information:"). WithHtml("