126 lines
2.7 KiB
Go
126 lines
2.7 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()
|
|
|
|
issueFingerprintValue := issueFingerprint(issue)
|
|
for _, existing := range issues {
|
|
if issueFingerprint(existing) == issueFingerprintValue {
|
|
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)
|
|
}
|