olivetin/service/internal/configissues/store.go

126 lines
2.6 KiB
Go

package configissues
import (
"sync"
log "github.com/sirupsen/logrus"
)
var (
storeMu sync.RWMutex
issues []Issue
stickyMu sync.Mutex
sticky []Issue
)
// BeginConfigLoad clears sticky issues captured during config decode/include load.
func BeginConfigLoad() {
stickyMu.Lock()
defer stickyMu.Unlock()
sticky = nil
}
// ReportSticky records an issue that cannot be re-derived after config decode
// (for example unset environment variables that were already expanded).
func ReportSticky(issue Issue) {
stickyMu.Lock()
defer stickyMu.Unlock()
sticky = append(sticky, issue)
}
func copySticky() []Issue {
stickyMu.Lock()
defer stickyMu.Unlock()
out := make([]Issue, len(sticky))
copy(out, sticky)
return out
}
// CopySticky returns a copy of sticky load-time issues.
func CopySticky() []Issue {
return copySticky()
}
// Replace sets the current issue list and logs only newly appeared issues.
func Replace(next []Issue) {
storeMu.Lock()
defer storeMu.Unlock()
previous := make(map[string]struct{}, len(issues))
for _, issue := range issues {
previous[issueFingerprint(issue)] = struct{}{}
}
issues = make([]Issue, len(next))
copy(issues, next)
for _, issue := range issues {
if _, exists := previous[issueFingerprint(issue)]; exists {
continue
}
logNewIssue(issue)
}
}
func issueFingerprint(issue Issue) string {
return issue.Severity + "\x00" +
issue.Code + "\x00" +
issue.ActionID + "\x00" +
issue.ActionTitle + "\x00" +
issue.ArgumentName + "\x00" +
issue.Source + "\x00" +
issue.ConfigFile + "\x00" +
issue.Message
}
func logNewIssue(issue Issue) {
fields := log.Fields{
"code": issue.Code,
"actionTitle": issue.ActionTitle,
"argumentName": issue.ArgumentName,
"configFile": issue.ConfigFile,
"source": issue.Source,
}
if issue.Severity == SeverityError {
log.WithFields(fields).Error(issue.Message)
return
}
log.WithFields(fields).Warn(issue.Message)
}
// Report adds a live issue if it is not already present and logs it when new.
// Use this for runtime discoveries that happen outside Rebuild (for example
// filesystem watcher setup failures).
func Report(issue Issue) {
storeMu.Lock()
defer storeMu.Unlock()
fp := issueFingerprint(issue)
for _, existing := range issues {
if issueFingerprint(existing) == fp {
return
}
}
issues = append(issues, issue)
logNewIssue(issue)
}
// List returns a copy of the current issues.
func List() []Issue {
storeMu.RLock()
defer storeMu.RUnlock()
out := make([]Issue, len(issues))
copy(out, issues)
return out
}
// Count returns the number of current issues.
func Count() int {
storeMu.RLock()
defer storeMu.RUnlock()
return len(issues)
}