Files
nixos-router/pkgs/router-webui/check.go
T
nilsandClaude Sonnet 5.5 98b0ebc130 Type the REST API's responses
Every tool now returns a Go struct (apitypes.go) instead of a map, and
typeschema.go derives the OpenAPI response schemas from those types by
reflection (json, doc and enum tags), so the document cannot drift from
what is sent. Text results are documented as text/plain, settings in
answers use the typed settings schema, and edits answer with EditResponse.

TestResponsesMatchTheirSchemas checks real responses against the
generated schemas.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
2026-11-05 08:10:59 +01:00

78 lines
2.2 KiB
Go

package main
import (
"encoding/json"
"os"
"path/filepath"
"time"
)
// A check evaluates the saved settings like an apply would (option types and
// the router's assertions) without building anything: router-check.service
// runs `nix eval` on the configuration and leaves its verdict in
// <state>/check/result.json.
type CheckResult struct {
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
Etag string `json:"etag"` // of the settings that were evaluated
Time time.Time `json:"time"`
}
func (s System) checkResultPath() string { return filepath.Join(s.StateDir, "check", "result.json") }
func (s System) readCheck() (CheckResult, bool) {
var r CheckResult
data, err := os.ReadFile(s.checkResultPath())
if err != nil || json.Unmarshal(data, &r) != nil {
return r, false
}
return r, true
}
func (s System) checkRunning() bool {
return unitProperty("router-check.service", "ActiveState") == "activating"
}
// checkSettings starts a check unless one runs or the last result is for the
// current settings, and waits a while for it.
func (a *app) checkSettings() (any, error) {
data, err := os.ReadFile(a.sys.settingsPath())
if err != nil {
return nil, err
}
current := etag(data)
if r, ok := a.sys.readCheck(); ok && r.Etag == current && !a.sys.checkRunning() {
return a.checkReport(r, true), nil
}
if !a.sys.checkRunning() {
if _, err := command("systemctl", "start", "--no-block", "router-check.service"); err != nil {
return nil, err
}
}
for range 50 {
time.Sleep(time.Second)
if a.sys.checkRunning() {
continue
}
if r, ok := a.sys.readCheck(); ok {
return a.checkReport(r, r.Etag == current), nil
}
break
}
return CheckResponse{Status: "running", Next: "Still evaluating; call check_settings again in a few seconds."}, nil
}
func (a *app) checkReport(r CheckResult, current bool) CheckResponse {
out := CheckResponse{Status: "done", OK: &r.OK, ForTheSavedSettings: &current, CheckedAt: &r.Time}
if r.OK {
out.Message = "The settings evaluate: the router's assertions pass."
} else {
out.Error = r.Error
}
if !current {
out.Note = "The settings changed during or after this check; call check_settings again."
}
return out
}