Status says when each machine last reported, beside when it was sent

"Not waiting" says the declaration is current, not that the machine
finished applying it: the sent digest is recorded at send. So a test
that pushed, saw waiting clear, and asked the machine what it was
running found containers that did not exist yet — the certificate fix
made compositions stable, and the settling that used to fail first had
been hiding the gap behind it.

The mesh already held the missing half: every machine's last report,
with its time. It just was not in the JSON. `reported` now sets each
machine's last word beside when the current declaration went to it, and
"has it caught up" becomes a comparison of two timestamps the mesh
recorded itself — a report newer than the send means the machine acted
on what was sent; older means it is still working, which waiting alone
cannot distinguish.
This commit is contained in:
2026-09-01 23:02:26 +02:00
parent ef7750f816
commit c0107b8572
6 changed files with 69 additions and 6 deletions
+37
View File
@@ -514,6 +514,43 @@ func (i *Inventory) DoingOf(ctx context.Context, name string) (Doing, bool, erro
return d, true, nil
}
// Reported is one machine's last word set beside what was last asked of it.
//
// The pair is what makes "has it caught up" answerable: a machine that reported *after* it was
// sent the current declaration has acted on it; one that reported before is still working, or
// has not started — and "not waiting" alone cannot tell those apart, because the sent digest is
// recorded at send, not at apply. The lab asserted on a machine the moment its declaration was
// current and found containers that did not exist yet.
type Reported struct {
Node string
Outcome string
// At is when it last reported; nil if it never has.
At *time.Time
// Sent is when the current declaration went to it; nil if nothing ever did.
Sent *time.Time
}
// LastReports is every machine's last report beside when it was last sent a declaration.
func (i *Inventory) LastReports(ctx context.Context) ([]Reported, error) {
rows, err := i.store.Pool().Query(ctx,
`select n.name, coalesce(r.outcome, ''), r.at, n.sent_at
from node n left join node_report r on r.node = n.id
order by n.name`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []Reported
for rows.Next() {
var r Reported
if err := rows.Scan(&r.Node, &r.Outcome, &r.At, &r.Sent); err != nil {
return nil, err
}
out = append(out, r)
}
return out, rows.Err()
}
// RecordSent keeps a digest of the declaration a machine was last sent.
//
// **A digest rather than the declaration.** The mesh can compute what a machine should be at any