Skip to content

Commit c025436

Browse files
committed
better error handling
1 parent 3735fdf commit c025436

2 files changed

Lines changed: 51 additions & 24 deletions

File tree

internal/k8sinventory/checkpoint/checkpointer.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,32 +144,38 @@ func (c *Checkpointer) Flush(ctx context.Context) error {
144144

145145
// Load reads the persisted RV for each namespace into memory.
146146
// Must be called before processing initial-list events so AlreadySeen() has data to compare against.
147-
func (c *Checkpointer) Load(ctx context.Context, namespaces []string, objectType string) {
147+
func (c *Checkpointer) Load(ctx context.Context, namespaces []string, objectType string) error {
148148
c.persistedMu.Lock()
149149
defer c.persistedMu.Unlock()
150150
c.persistedRVs = make(map[string]int64)
151151
for _, ns := range namespaces {
152152
rv, err := c.GetCheckpoint(ctx, ns, objectType)
153-
if err != nil || rv == "" {
153+
if err != nil {
154+
return err
155+
}
156+
if rv == "" {
154157
continue
155158
}
156-
if parsed, err := strconv.ParseInt(rv, 10, 64); err == nil {
157-
c.persistedRVs[ns] = parsed
159+
parsed, err := strconv.ParseInt(rv, 10, 64)
160+
if err != nil {
161+
return err
158162
}
163+
c.persistedRVs[ns] = parsed
159164
}
165+
return nil
160166
}
161167

162168
// AlreadySeen reports whether the given resourceVersion is ≤ the persisted checkpoint
163169
// for the namespace, meaning the object was already processed before the last restart.
164-
func (c *Checkpointer) AlreadySeen(resourceVersion, namespace string) bool {
170+
func (c *Checkpointer) AlreadySeen(resourceVersion, namespace string) (bool, error) {
165171
objRV, err := strconv.ParseInt(resourceVersion, 10, 64)
166172
if err != nil {
167-
return false
173+
return false, err
168174
}
169175
c.persistedMu.RLock()
170176
persisted, exists := c.persistedRVs[namespace]
171177
c.persistedMu.RUnlock()
172-
return exists && objRV <= persisted
178+
return exists && objRV <= persisted, nil
173179
}
174180

175181
// DeleteCheckpoint deletes the persisted checkpoint for a given namespace and object type.

internal/k8sinventory/checkpoint/checkpointer_test.go

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,9 @@ func TestCheckpointerAlreadySeenBeforeLoad(t *testing.T) {
326326
cp := New(client, zap.NewNop())
327327

328328
// Without Load, no namespace is known — AlreadySeen must not panic or return true.
329-
assert.False(t, cp.AlreadySeen("100", "default"))
329+
seen, err := cp.AlreadySeen("100", "default")
330+
require.NoError(t, err)
331+
assert.False(t, seen)
330332
}
331333

332334
func TestCheckpointerLoadAndAlreadySeen(t *testing.T) {
@@ -338,14 +340,19 @@ func TestCheckpointerLoadAndAlreadySeen(t *testing.T) {
338340
require.NoError(t, cp.SetCheckpoint(ctx, "kube-system", "pods", "500"))
339341
require.NoError(t, cp.Flush(ctx))
340342

341-
cp.Load(ctx, []string{"default", "kube-system"}, "pods")
343+
require.NoError(t, cp.Load(ctx, []string{"default", "kube-system"}, "pods"))
342344

343-
assert.True(t, cp.AlreadySeen("199", "default"), "RV below persisted should be seen")
344-
assert.True(t, cp.AlreadySeen("200", "default"), "RV equal to persisted should be seen (≤)")
345-
assert.False(t, cp.AlreadySeen("201", "default"), "RV above persisted should not be seen")
345+
mustSeen := func(rv, ns string) bool {
346+
seen, err := cp.AlreadySeen(rv, ns)
347+
require.NoError(t, err)
348+
return seen
349+
}
346350

347-
assert.True(t, cp.AlreadySeen("500", "kube-system"))
348-
assert.False(t, cp.AlreadySeen("501", "kube-system"))
351+
assert.True(t, mustSeen("199", "default"), "RV below persisted should be seen")
352+
assert.True(t, mustSeen("200", "default"), "RV equal to persisted should be seen (≤)")
353+
assert.False(t, mustSeen("201", "default"), "RV above persisted should not be seen")
354+
assert.True(t, mustSeen("500", "kube-system"))
355+
assert.False(t, mustSeen("501", "kube-system"))
349356
}
350357

351358
func TestCheckpointerLoadSkipsMissingAndUnparseable(t *testing.T) {
@@ -359,11 +366,19 @@ func TestCheckpointerLoadSkipsMissingAndUnparseable(t *testing.T) {
359366
require.NoError(t, cp.Flush(ctx))
360367
require.NoError(t, client.Set(ctx, cp.checkpointKey("weird", "pods"), []byte("not-a-number")))
361368

362-
cp.Load(ctx, []string{"default", "missing", "weird"}, "pods")
369+
// Load returns valid namespaces cleanly; missing keys are not an error.
370+
require.NoError(t, cp.Load(ctx, []string{"default", "missing"}, "pods"))
371+
372+
seen, err := cp.AlreadySeen("50", "default")
373+
require.NoError(t, err)
374+
assert.True(t, seen, "valid RV namespace loaded")
375+
376+
seen, err = cp.AlreadySeen("50", "missing")
377+
require.NoError(t, err)
378+
assert.False(t, seen, "namespace with no checkpoint must not be marked seen")
363379

364-
assert.True(t, cp.AlreadySeen("50", "default"), "valid RV namespace loaded")
365-
assert.False(t, cp.AlreadySeen("50", "missing"), "namespace with no checkpoint must not be marked seen")
366-
assert.False(t, cp.AlreadySeen("50", "weird"), "namespace with unparseable RV must not be marked seen")
380+
// An unparseable persisted RV surfaces as a Load error rather than being silently skipped.
381+
require.Error(t, cp.Load(ctx, []string{"default", "weird"}, "pods"), "unparseable persisted RV should fail Load")
367382
}
368383

369384
func TestCheckpointerLoadResetsState(t *testing.T) {
@@ -373,13 +388,17 @@ func TestCheckpointerLoadResetsState(t *testing.T) {
373388

374389
require.NoError(t, cp.SetCheckpoint(ctx, "default", "pods", "100"))
375390
require.NoError(t, cp.Flush(ctx))
376-
cp.Load(ctx, []string{"default"}, "pods")
377-
assert.True(t, cp.AlreadySeen("100", "default"))
391+
require.NoError(t, cp.Load(ctx, []string{"default"}, "pods"))
392+
seen, err := cp.AlreadySeen("100", "default")
393+
require.NoError(t, err)
394+
assert.True(t, seen)
378395

379396
// Delete the persisted entry; Load should discard the previously cached value.
380397
require.NoError(t, cp.DeleteCheckpoint(ctx, "default", "pods"))
381-
cp.Load(ctx, []string{"default"}, "pods")
382-
assert.False(t, cp.AlreadySeen("100", "default"), "Load must reset previously loaded entries")
398+
require.NoError(t, cp.Load(ctx, []string{"default"}, "pods"))
399+
seen, err = cp.AlreadySeen("100", "default")
400+
require.NoError(t, err)
401+
assert.False(t, seen, "Load must reset previously loaded entries")
383402
}
384403

385404
func TestCheckpointerAlreadySeenUnparseableRV(t *testing.T) {
@@ -389,7 +408,9 @@ func TestCheckpointerAlreadySeenUnparseableRV(t *testing.T) {
389408

390409
require.NoError(t, cp.SetCheckpoint(ctx, "default", "pods", "100"))
391410
require.NoError(t, cp.Flush(ctx))
392-
cp.Load(ctx, []string{"default"}, "pods")
411+
require.NoError(t, cp.Load(ctx, []string{"default"}, "pods"))
393412

394-
assert.False(t, cp.AlreadySeen("not-a-number", "default"), "unparseable RV must not be marked seen")
413+
seen, err := cp.AlreadySeen("not-a-number", "default")
414+
require.Error(t, err, "unparseable RV must surface a parse error")
415+
assert.False(t, seen, "unparseable RV must not be marked seen")
395416
}

0 commit comments

Comments
 (0)