package cluster import ( "errors" "strings" "time" ) func (s *Service) GetAlertRouting(selector string) (AlertRoutingPolicy, error) { c, err := s.Get(selector) if err != nil { return AlertRoutingPolicy{}, err } return ensureAlertRoutingPolicy(c.AlertRouting), nil } func (s *Service) SetAlertRouting(selector string, p AlertRoutingPolicy) (Cluster, error) { reg, err := s.store.Load() if err != nil { return Cluster{}, err } c, idx, err := findCluster(reg, selector) if err != nil { return Cluster{}, err } c.AlertRouting = ensureAlertRoutingPolicy(p) c.UpdatedAt = s.now().UTC() reg.Clusters[idx] = c if err := s.store.Save(reg); err != nil { return Cluster{}, err } _ = s.AppendChange("alert.routing", c.Name, "updated") return c, nil } func (s *Service) GetRunbookTrigger(selector string) (RunbookTrigger, error) { c, err := s.Get(selector) if err != nil { return RunbookTrigger{}, err } return ensureRunbookTrigger(c.RunbookTrigger), nil } func (s *Service) SetRunbookTrigger(selector string, p RunbookTrigger) (Cluster, error) { reg, err := s.store.Load() if err != nil { return Cluster{}, err } c, idx, err := findCluster(reg, selector) if err != nil { return Cluster{}, err } c.RunbookTrigger = ensureRunbookTrigger(p) c.UpdatedAt = s.now().UTC() reg.Clusters[idx] = c if err := s.store.Save(reg); err != nil { return Cluster{}, err } _ = s.AppendChange("runbook.trigger", c.Name, "updated") return c, nil } func (s *Service) TouchRunbookTrigger(selector string, when time.Time) error { reg, err := s.store.Load() if err != nil { return err } c, idx, err := findCluster(reg, selector) if err != nil { return err } tr := ensureRunbookTrigger(c.RunbookTrigger) tr.LastTriggered = when.UTC() c.RunbookTrigger = tr c.UpdatedAt = s.now().UTC() reg.Clusters[idx] = c return s.store.Save(reg) } func (s *Service) SetDriftBaseline(selector string) (Cluster, error) { reg, err := s.store.Load() if err != nil { return Cluster{}, err } c, idx, err := findCluster(reg, selector) if err != nil { return Cluster{}, err } d := normalizeDriftControl(c.Drift) d.Baseline = DriftBaseline{ Enabled: true, SetAt: s.now().UTC(), AgentVersion: strings.TrimSpace(c.Agent.Version), Software: strings.TrimSpace(c.Software.Summary()), } c.Drift = d c.UpdatedAt = s.now().UTC() reg.Clusters[idx] = c if err := s.store.Save(reg); err != nil { return Cluster{}, err } _ = s.AppendChange("drift.baseline", c.Name, "set") return c, nil } func (s *Service) GetDriftControl(selector string) (DriftControl, error) { c, err := s.Get(selector) if err != nil { return DriftControl{}, err } return normalizeDriftControl(c.Drift), nil } func (s *Service) AckDriftIssue(selector, issueKind string, until time.Time) (Cluster, error) { kind := strings.ToLower(strings.TrimSpace(issueKind)) if kind == "" { return Cluster{}, errors.New("issue kind is required") } reg, err := s.store.Load() if err != nil { return Cluster{}, err } c, idx, err := findCluster(reg, selector) if err != nil { return Cluster{}, err } d := normalizeDriftControl(c.Drift) if d.AckUntil == nil { d.AckUntil = map[string]time.Time{} } d.AckUntil[kind] = until.UTC() c.Drift = d c.UpdatedAt = s.now().UTC() reg.Clusters[idx] = c if err := s.store.Save(reg); err != nil { return Cluster{}, err } _ = s.AppendChange("drift.ack", c.Name, kind+" until="+until.UTC().Format(time.RFC3339)) return c, nil } func (s *Service) IsDriftIssueAcked(c Cluster, kind string, now time.Time) bool { d := normalizeDriftControl(c.Drift) if len(d.AckUntil) == 0 { return false } u, ok := d.AckUntil[strings.ToLower(strings.TrimSpace(kind))] if !ok { return false } return now.UTC().Before(u) }