Ignore status reports older than the stored generation
This commit is contained in:
@@ -281,13 +281,17 @@ func (s *Store) UpsertDevice(ctx context.Context, d model.Device) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RecordDeviceStatus stores an agent's apply report. reported_generation only
|
// RecordDeviceStatus stores an agent's apply report. reported_generation only
|
||||||
// advances on applied; every report updates the last-status columns.
|
// advances on applied; the last-status columns only take reports at or above
|
||||||
|
// the stored status generation, so late or replayed reports never clobber newer state.
|
||||||
func (s *Store) RecordDeviceStatus(ctx context.Context, name string, r model.StatusReport) error {
|
func (s *Store) RecordDeviceStatus(ctx context.Context, name string, r model.StatusReport) error {
|
||||||
tag, err := s.pool.Exec(ctx, `
|
tag, err := s.pool.Exec(ctx, `
|
||||||
UPDATE devices SET
|
UPDATE devices SET
|
||||||
reported_generation = CASE WHEN $2 = 'applied' THEN $3 ELSE reported_generation END,
|
reported_generation = CASE WHEN $2 = 'applied' THEN GREATEST(reported_generation, $3) ELSE reported_generation END,
|
||||||
status = $2, status_generation = $3, status_error = $4,
|
status = CASE WHEN $3 >= status_generation THEN $2 ELSE status END,
|
||||||
status_at = now(), last_seen = now()
|
status_error = CASE WHEN $3 >= status_generation THEN $4 ELSE status_error END,
|
||||||
|
status_at = CASE WHEN $3 >= status_generation THEN now() ELSE status_at END,
|
||||||
|
status_generation = GREATEST(status_generation, $3),
|
||||||
|
last_seen = now()
|
||||||
WHERE name = $1`,
|
WHERE name = $1`,
|
||||||
name, string(r.Status), r.Generation, r.Error)
|
name, string(r.Status), r.Generation, r.Error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -196,7 +196,27 @@ func TestRecordDeviceStatus(t *testing.T) {
|
|||||||
t.Errorf("failed must not advance applied generation: %+v", got)
|
t.Errorf("failed must not advance applied generation: %+v", got)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.RecordDeviceStatus(ctx, "fw1", model.StatusReport{Status: "bogus", Generation: 9}); err == nil {
|
got = report(model.StatusReport{Status: model.StatusApplied, Generation: 9})
|
||||||
|
if got.AppliedGeneration != 9 || got.Status != model.StatusApplied || got.Generation != 9 {
|
||||||
|
t.Errorf("applied 9: %+v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
got = report(model.StatusReport{Status: model.StatusApplied, Generation: 4})
|
||||||
|
if got.AppliedGeneration != 9 || got.Status != model.StatusApplied || got.Generation != 9 {
|
||||||
|
t.Errorf("older applied must not regress: %+v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
got = report(model.StatusReport{Status: model.StatusReverted, Generation: 8, Error: "stale"})
|
||||||
|
if got.AppliedGeneration != 9 || got.Status != model.StatusApplied || got.Generation != 9 || got.Error != "" {
|
||||||
|
t.Errorf("older reverted must not overwrite newer status: %+v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
got = report(model.StatusReport{Status: model.StatusReverted, Generation: 9, Error: "lost api"})
|
||||||
|
if got.AppliedGeneration != 9 || got.Status != model.StatusReverted || got.Generation != 9 || got.Error != "lost api" {
|
||||||
|
t.Errorf("same-generation reverted after applied must update: %+v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.RecordDeviceStatus(ctx, "fw1", model.StatusReport{Status: "bogus", Generation: 10}); err == nil {
|
||||||
t.Error("db should reject unknown status")
|
t.Error("db should reject unknown status")
|
||||||
}
|
}
|
||||||
if err := s.RecordDeviceStatus(ctx, "nope", model.StatusReport{Status: model.StatusApplied}); err != store.ErrNotFound {
|
if err := s.RecordDeviceStatus(ctx, "nope", model.StatusReport{Status: model.StatusApplied}); err != store.ErrNotFound {
|
||||||
|
|||||||
Reference in New Issue
Block a user