mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-04 19:17:20 +09:00
## What this PR does While preparing for my CozySummit talk of last week (creating commits from CozySummit edits with [gitops-reverser](https://github.com/ConfigButler/gitops-reverser)) I noticed warnings on my console: ``` Warning: event bookmark expired err="reflector.go:343: hasn't received required bookmark event marking the end of initial events stream, received last event 59.998946102s ago" The warning fires on every reflector restart and continuously while the cache is unsynced from the WatchList perspective. ``` I've been searching a bit better and it turns out that `TenantSecret`, `TenantModule`, and `TenantNamespace` (group `core.cozystack.io`) all ignore `ListOptions.SendInitialEvents`. Interestingly enough `apps.cozystack.io/Application` already does implement this in the right way. Under the [WatchList / streaming-list protocol](https://kubernetes.io/docs/reference/using-api/api-concepts/#streaming-lists) (client-go's `WatchListClient`, default-on since v1.35), clients expect the initial `ADDED` events to be terminated by a [`Bookmark`](https://kubernetes.io/docs/reference/using-api/api-concepts/#watch-bookmarks) annotated with `k8s.io/initial-events-end: "true"`. These three resources only forwarded the backing watcher's bookmarks (which lack the annotation), so the marker was never sent and client-go reflectors never reached `HasSynced` — looping on `reflector.go:343: hasn't received required bookmark event marking the end of initial events stream`. **Fix:** extracted the contract into a shared `registry.InitialEventsBookmarker` helper (`pkg/registry/utils.go`) and wired it into all four `Watch` implementations. It annotates the first backing bookmark as the terminating marker, with fallbacks to emit it before the first live event and on watcher close. The annotation reuses the upstream `metav1.InitialEventsAnnotationKey` constant rather than a string literal. The full bookmark machinery in `k8s.io/apiserver/pkg/storage/cacher` isn't reusable here: it's internal to the watch-cache layer and only fires for cacher-backed storage, whereas these resources translate a controller-runtime watch in custom REST storage that bypasses the cacher. `Application` is refactored onto the helper with its `WorkloadMonitor` event-draining preserved. Adds unit tests for the helper and watch tests for each core resource (`ADDED` → annotated `Bookmark` → live event). ### Screenshots N/A — no UI changes. ### Release note ```release-note fix(api): core.cozystack.io resources (TenantSecret, TenantModule, TenantNamespace) now emit the k8s.io/initial-events-end bookmark for SendInitialEvents=true (WatchList) watches, so client-go informers reach HasSynced instead of looping on the missing-bookmark warning. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Watch endpoints reliably emit the "initial-events-end" bookmark after initial events, improving watch/list contract compliance and client reflector synchronization. * **Refactor** * Watch implementations consolidated around a centralized initial-events bookmarker and stop-aware event forwarding for consistent bookmark behavior. * **New Features** * Introduced a reusable initial-events bookmarker to coordinate bookmark emission and resourceVersion observation across translated watch streams. * **Tests** * Added unit and integration tests validating bookmark emission, ordering, resourceVersion seeding/observation, and close-time behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->