Retry validator-agent/prober registration; show validator hostname
Both binaries registered with control-api exactly once at startup and
exited (os.Exit(1)) on any failure — including control-api simply not
being up yet (no ordering guarantee between the two at boot/redeploy) or
the admin not having added this validator_id/site_id to the config yet.
Run() now retries registration with capped exponential backoff (3s->30s)
until it succeeds or the process is asked to shut down, instead of
crashing; registerWithRetry is identical in agentcore and probercore
since their Run/register shape already was.
Separately, the admin dashboard's Validators page had no hostname column
even though the agent already reports one on register (mirroring the
prober) and control-api already persists it — only the admin-config read
DTO (validatorDTO in httpapi and dashboard) dropped it before it reached
the template. Added hostname + last_heartbeat_at to that DTO end-to-end
and a Хост/Heartbeat column to validators.html, matching sites.html.
Rebuilt bin/{control-api,admin-dashboard,prober,validator-agent} and
bin/SHA256SUMS per docs/SETUP.md's documented build recipe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
399c64e801
commit
95f8066eed
15 files changed
+320
-23
No files matched your search
@@ -33,9 +33,11 @@ type clearQueueResponse struct {
|
||||
}
|
||||
|
||||
type validatorDTO struct {
|
||||
ValidatorID string `json:"validator_id"`
|
||||
OSPortID string `json:"os_port_id"`
|
||||
State string `json:"state"`
|
||||
ValidatorID string `json:"validator_id"`
|
||||
Hostname string `json:"hostname"`
|
||||
OSPortID string `json:"os_port_id"`
|
||||
State string `json:"state"`
|
||||
LastHeartbeatAt *time.Time `json:"last_heartbeat_at"`
|
||||
}
|
||||
|
||||
type createValidatorRequest struct {
|
||||
|
||||
@@ -17,7 +17,10 @@ func (s *Server) handleConfigListValidators(w http.ResponseWriter, r *http.Reque
|
||||
}
|
||||
out := make([]validatorDTO, len(validators))
|
||||
for i, v := range validators {
|
||||
out[i] = validatorDTO{ValidatorID: v.ValidatorID, OSPortID: v.OSPortID, State: v.State}
|
||||
out[i] = validatorDTO{
|
||||
ValidatorID: v.ValidatorID, Hostname: v.Hostname, OSPortID: v.OSPortID,
|
||||
State: v.State, LastHeartbeatAt: v.LastHeartbeatAt,
|
||||
}
|
||||
}
|
||||
writeJSON(w, http.StatusOK, out)
|
||||
}
|
||||
|
||||
@@ -666,6 +666,35 @@ func TestProberRegisterSetsHostnameAndIdleState(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidatorRegisterSetsHostname proves the admin config listing surfaces
|
||||
// a validator's hostname (and heartbeat time) once its agent registers,
|
||||
// mirroring TestProberRegisterSetsHostnameAndIdleState for sites.
|
||||
func TestValidatorRegisterSetsHostname(t *testing.T) {
|
||||
fc, _, _, _ := newConfigTestHarness(t)
|
||||
|
||||
fc.do(http.MethodPost, "/api/v1/admin/config/validators", createValidatorRequest{ValidatorID: "val-1", OSPortID: "port-1"})
|
||||
|
||||
resp, body := fc.do(http.MethodPost, "/api/v1/agents/register", registerAgentRequest{ValidatorID: "val-1", Hostname: "validator-host-1", AgentVersion: "test"})
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("register agent: status=%d body=%s", resp.StatusCode, body)
|
||||
}
|
||||
|
||||
resp, body = fc.do(http.MethodGet, "/api/v1/admin/config/validators", nil)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("get validators: status=%d body=%s", resp.StatusCode, body)
|
||||
}
|
||||
var validators []validatorDTO
|
||||
if err := json.Unmarshal(body, &validators); err != nil {
|
||||
t.Fatalf("unmarshal validators: %v", err)
|
||||
}
|
||||
if len(validators) != 1 || validators[0].Hostname != "validator-host-1" {
|
||||
t.Fatalf("expected hostname validator-host-1, got %+v", validators)
|
||||
}
|
||||
if validators[0].LastHeartbeatAt != nil {
|
||||
t.Fatalf("expected no heartbeat yet (only register was called), got %+v", validators[0].LastHeartbeatAt)
|
||||
}
|
||||
}
|
||||
|
||||
// TestProberHeartbeat404UnknownSite proves the heartbeat endpoint rejects
|
||||
// an unconfigured site_id, mirroring the validator heartbeat's 404.
|
||||
func TestProberHeartbeat404UnknownSite(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user