ProcessAgentUpdate runs the agent version check: binary version + DNS latest, then create/resolve alerts. Called by the version-update-check queue job. The Go server reads the agent binary version by executing patchmon-agent (same as Agent Version tab in Settings).
(ctx context.Context, d *database.DB, agentsDir string, tenantHost string, emit *notifications.Emitter, log *slog.Logger)
| 22 | // Called by the version-update-check queue job. |
| 23 | // The Go server reads the agent binary version by executing patchmon-agent (same as Agent Version tab in Settings). |
| 24 | func ProcessAgentUpdate(ctx context.Context, d *database.DB, agentsDir string, tenantHost string, emit *notifications.Emitter, log *slog.Logger) error { |
| 25 | enabled, err := IsAlertsEnabled(ctx, d) |
| 26 | if err != nil || !enabled { |
| 27 | log.Debug("agent_update: alerts disabled") |
| 28 | return nil |
| 29 | } |
| 30 | |
| 31 | // Current version from binary (same logic as /agent/version handler) |
| 32 | currentVersion := util.GetCurrentAgentVersionFromBinary(ctx, agentsDir) |
| 33 | |
| 34 | // Latest version from DNS |
| 35 | latest, dnsErr := util.LookupVersionFromDNS(agentVersionDNS) |
| 36 | if dnsErr != nil { |
| 37 | log.Warn("agent_update: DNS lookup failed", "error", dnsErr) |
| 38 | } else if latest != "" { |
| 39 | latest = strings.TrimSpace(strings.Trim(latest, "\"'")) |
| 40 | if !agentSemverRe.MatchString(latest) { |
| 41 | latest = "" |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | // Create/resolve alerts (only when agent_update config is enabled) |
| 46 | cfg, _ := GetConfigForType(ctx, d, "agent_update") |
| 47 | if cfg == nil || !cfg.IsEnabled { |
| 48 | return nil |
| 49 | } |
| 50 | |
| 51 | alertsStore := store.NewAlertsStore(d) |
| 52 | severity := DefaultSeverity(cfg.DefaultSeverity, "informational") |
| 53 | |
| 54 | if currentVersion != "" && latest != "" && util.CompareVersions(latest, currentVersion) > 0 { |
| 55 | title := "Agent Files Update Available" |
| 56 | msg := fmt.Sprintf("A new agent version (%s) is available. Current version: %s", latest, currentVersion) |
| 57 | meta := map[string]interface{}{"current_version": currentVersion, "latest_version": latest} |
| 58 | |
| 59 | // Skip if an active alert for this version already exists. |
| 60 | active, _ := d.Queries.ListActiveAlertsByType(ctx, "agent_update") |
| 61 | hasMatching := false |
| 62 | for _, a := range active { |
| 63 | var m map[string]interface{} |
| 64 | if len(a.Metadata) > 0 && json.Unmarshal(a.Metadata, &m) == nil { |
| 65 | if lv, _ := m["latest_version"].(string); lv == latest { |
| 66 | hasMatching = true |
| 67 | break |
| 68 | } |
| 69 | } |
| 70 | } |
| 71 | if !hasMatching { |
| 72 | // Emit event — notification routing decides which destinations receive it |
| 73 | // (including internal alerts if that destination is enabled). |
| 74 | if emit != nil { |
| 75 | emit.EmitEvent(ctx, d, tenantHost, notifications.Event{ |
| 76 | Type: "agent_update", Severity: severity, Title: title, Message: msg, |
| 77 | ReferenceType: "host", ReferenceID: "", |
| 78 | Metadata: meta, |
| 79 | }) |
| 80 | } |
| 81 | } |
nothing calls this directly
no test coverage detected