package engine import ( "context" "fmt" "os" "strings" "sync" "time" "jarl/internal/model" "jarl/internal/render" "jarl/internal/validate" ) // namedCheckTTL is how long the result of checking named's running // configuration is kept when nothing changed. const namedCheckTTL = 10 * time.Minute // namedCheck caches the warnings of named-checkconf on the configuration // named runs, keyed by what it depends on. type namedCheck struct { mu sync.Mutex key string at time.Time problems []validate.Problem } // namedProblems runs named-checkconf on the configuration named runs // (without loading zones, which is quick) and returns what it reports as // problems. An apply shows these too, but they are named's own checks, // which Jarl doesn't repeat ("both recursion no and allow-recursion // active"): the dashboard lists them with Jarl's so they don't go unseen // after the apply. The result is reused while the deployed files and // named's main file are unchanged. func (e *Engine) namedProblems(ctx context.Context, st *model.State, deployed map[string]*model.DeployedFile) []validate.Problem { if e.tools == nil || e.cfg.SkipCheck || e.cfg.MainConfig == "" || len(deployed) == 0 { return nil } key := "" if fi, err := os.Stat(e.cfg.MainConfig); err == nil { key = fi.ModTime().String() } if f := deployed[e.cfg.Paths.MainConfig()]; f != nil { key += "|" + f.SHA256 } c := &e.namedCheck c.mu.Lock() defer c.mu.Unlock() if c.key == key && e.Now().Sub(c.at) < namedCheckTTL { return c.problems } res, err := e.tools.CheckConfig(ctx, e.cfg.MainConfig, false) if err != nil { return nil // named-checkconf couldn't run; an apply reports that } out := render.Render(st, e.cfg.Paths, e.extras(ctx)) var ps []validate.Problem for _, m := range res.Messages { p := validate.Problem{Severity: validate.Warning, Code: "named_warning", View: m.View, Zone: m.Zone} if m.Error { p.Severity, p.Code = validate.Error, "named_error" } where := "" if m.File != "" { where = fmt.Sprintf(" (%s:%d)", m.File, m.Line) } if span, ok := out.Lookup(m.File, m.Line); ok { p.ObjectType, p.ObjectID = span.ObjectType, span.ObjectID where = " (" + span.Label + ", " + where[2:] } p.Message = "named-checkconf: " + strings.TrimSpace(m.Text) + where ps = append(ps, p) } c.key, c.at, c.problems = key, e.Now(), ps return ps }