Skip to content
Open
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
b487bed
chore: add optional validatingAdmissionWebhook, and prepare for a sep…
sunib Jun 24, 2026
ec49609
chore: creating plan and code to capture all the mechanisms in a more…
sunib Jun 24, 2026
876ff60
chore: here is M2!
sunib Jun 24, 2026
e0b6369
chore: Interesting findings on the shallow body problem
sunib Jun 25, 2026
758a629
chore: small fixes
sunib Jun 25, 2026
2ccf07a
chore: more improvements in the tests
sunib Jun 25, 2026
7c41668
chore: first draft on architecture update
sunib Jun 25, 2026
8dc3aaa
chore: finishing for now
sunib Jun 25, 2026
e39ca06
test(mutationlab): capture rows 10 (owner-ref cascade) and 13 (conflict)
sunib Jun 25, 2026
77e349f
feat(watch): parallel watch-state stream behind --watch-state-stream
sunib Jun 25, 2026
6d87865
feat(watch): diff watch-derived vs audit-derived desired sets (Phase …
sunib Jun 25, 2026
2535109
test(mutationlab): capture rows 16+17 (watch resync + bookmark)
sunib Jun 25, 2026
d41cfab
chore: finishing the design
sunib Jun 25, 2026
38f59f8
feat: let's get all testing to Kubernetes 1.36
sunib Jun 25, 2026
6dec610
chore: reran all mutatons on k8s v1.36.1
sunib Jun 25, 2026
e612fc6
chore: improving watch-ingestion document.
sunib Jun 25, 2026
d0e25a2
chore: getting the design docs better
sunib Jun 25, 2026
b2d5bc7
feat: watch-first ingestion
sunib Jun 25, 2026
5122032
chore: next steps
sunib Jun 26, 2026
79a9fe6
docs: moving architecture along with the rewrite
sunib Jun 26, 2026
b524d83
chore: relisten to a watch when possible
sunib Jun 26, 2026
7e011db
chore: details on how Redis is needed
sunib Jun 26, 2026
d7bdb16
docs: created new plan, and hopefully found why the tests are so flaky
sunib Jun 26, 2026
acf73d5
chore: easier status and streamsready
sunib Jun 26, 2026
acaea33
chore: e2e flake preventions
sunib Jun 26, 2026
04aa391
chore: overall improvements, fixing things and cleaning docs
sunib Jun 26, 2026
915b524
feat: reworking metrics to new architecture
sunib Jun 26, 2026
261c440
feat(manifestanalyzer,git): refuse unsupported GitTarget folder conte…
sunib Jun 26, 2026
cb8d4b0
feat(watch): surface a refused GitTarget folder as a Blocked stream
sunib Jun 26, 2026
d09ab73
test(e2e): prove unsupported-folder refusal end to end (Test D) + docs
sunib Jun 26, 2026
893e17f
test(e2e): apply sops-age-key in unsupported-folder test so Ready can…
sunib Jun 26, 2026
f2773a8
docs: adding skills and working on status design
sunib Jun 27, 2026
92fa490
chore: improve status, support kstatus
sunib Jun 27, 2026
12f3aa2
chore: refining names and more explicit e2e test for status behaviour
sunib Jun 27, 2026
419ab33
docs: designing gittargetignore
sunib Jun 27, 2026
1c61666
feat: refuse weird files in GitTarget path, but do allow .gittargetig…
sunib Jun 27, 2026
b895ef3
chore: removing settings and preparing merge
sunib Jun 28, 2026
a555d9d
chore: Support CommitRequest with clearer status and non-attribution …
sunib Jun 28, 2026
52a14e8
fix: Allow GitTarget to respond quickly to changes in the tracked Git…
sunib Jun 29, 2026
73376dd
feat: --author-attribution={true|false} is now allowing you to enable…
sunib Jun 29, 2026
bc887e0
chore: resolving pr feedback
sunib Jun 29, 2026
40a06e0
chore: refine the redis queue names / behaviour
sunib Jun 29, 2026
8877db3
feat: CommitRequest can be attributed by validating webhook handler s…
sunib Jun 29, 2026
161e89c
feat!: validating webhook is required (even is audit has been configu…
sunib Jun 29, 2026
97f053d
chore: cleaning up
sunib Jun 29, 2026
2b31a1d
chore: reworking wording and comments
sunib Jun 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .coverage-baseline
Original file line number Diff line number Diff line change
@@ -1 +1 @@
75.0
73.6
14 changes: 14 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,20 @@ RUN printf '%s\n' \
'fi' \
>> /etc/bash.bashrc

# Install Node.js (provides npm + npx, e.g. for installing Claude Code skills).
# Dev stage only; CI does not need a JS runtime.
# NODE_MAJOR -> https://github.com/nodejs/node/releases (track a current LTS line)
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
| gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& chmod a+r /etc/apt/keyrings/nodesource.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" \
> /etc/apt/sources.list.d/nodesource.list \
&& apt-get update \
&& apt-get -y install --no-install-recommends nodejs \
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*

# Install Tilt CLI (local dev loop orchestration; dev stage only)
RUN arch="$(dpkg --print-architecture)" && \
case "${arch}" in \
Expand Down
15 changes: 14 additions & 1 deletion api/v1alpha2/clusterwatchrule_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,32 @@ type ClusterResourceRule struct {

// ClusterWatchRuleStatus defines the observed state of ClusterWatchRule.
type ClusterWatchRuleStatus struct {
// ObservedGeneration is the latest generation observed by the controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// Conditions represent the latest available observations of the ClusterWatchRule's state.
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`

// Streams is the bounded stream-readiness roll-up for the types this rule resolves.
// +optional
Streams *WatchRuleStreamsStatus `json:"streams,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:printcolumn:name="Target",type=string,JSONPath=`.spec.targetRef.name`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].message`
// +kubebuilder:printcolumn:name="Reconciling",type=string,JSONPath=`.status.conditions[?(@.type=="Reconciling")].status`
// +kubebuilder:printcolumn:name="Stalled",type=string,JSONPath=`.status.conditions[?(@.type=="Stalled")].status`
// +kubebuilder:printcolumn:name="GitTargetReady",type=string,JSONPath=`.status.conditions[?(@.type=="GitTargetReady")].status`
// +kubebuilder:printcolumn:name="StreamsRunning",type=string,JSONPath=`.status.conditions[?(@.type=="StreamsRunning")].status`
// +kubebuilder:printcolumn:name="Streams",type=string,JSONPath=`.status.streams.summary`
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

// ClusterWatchRule watches resources across the entire cluster.
Expand Down
58 changes: 23 additions & 35 deletions api/v1alpha2/gittarget_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,6 @@ type GitTargetStatus struct {
// +patchStrategy=merge
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`

// Phase is a small, derived, human-facing summary of the GitTarget's state —
// purely a projection of the conditions (Pending / Initializing / Synced /
// Degraded), informational only. Automation must gate on the conditions, never
// on phase. See docs/design/status-design-git-target.md §3.3.
// +optional
Phase string `json:"phase,omitempty"`

// LastReconcileTime is the timestamp of the most recent reconcile attempt.
// +optional
LastReconcileTime metav1.Time `json:"lastReconcileTime,omitempty"`
Expand All @@ -119,39 +112,30 @@ type GitTargetStatus struct {
// +optional
LastPushTime *metav1.Time `json:"lastPushTime,omitempty"`

// Materialization is a bounded roll-up of the demand-driven checkpoint state for the
// resource types this GitTarget claims.
// Streams is the bounded data-plane roll-up over this GitTarget's tracked types.
// Counts, never a per-type list, so it stays bounded however many types are watched.
// +optional
Materialization *GitTargetMaterializationStatus `json:"materialization,omitempty"`
Streams *GitTargetStreamsStatus `json:"streams,omitempty"`
}

// GitTargetMaterializationStatus is a bounded roll-up of the demand-driven materialization
// state for the types this GitTarget claims: how many it demands and where they sit in the
// per-type checkpoint lifecycle. It is a summary (counts), not a per-type list, so it stays
// bounded regardless of how many types are watched.
type GitTargetMaterializationStatus struct {
// ClaimedTypes is how many resource types this GitTarget currently claims (demands).
// GitTargetStreamsStatus is a bounded roll-up of the stream readiness state for the
// types this GitTarget tracks.
type GitTargetStreamsStatus struct {
// Summary is the display-only ready/total ratio.
// +optional
ClaimedTypes int32 `json:"claimedTypes,omitempty"`
Summary string `json:"summary,omitempty"`

// SyncedTypes is how many claimed types have a current checkpoint and are serviceable.
// +optional
SyncedTypes int32 `json:"syncedTypes,omitempty"`
// Total is how many types this target tracks.
Total int32 `json:"total"`

// PendingTypes is how many claimed types are still building their FIRST checkpoint and so are
// not yet serviceable (Requested/Syncing). A type refreshing an existing checkpoint (Resyncing)
// is NOT pending — it still serves the prior checkpoint and counts as Synced.
// +optional
PendingTypes int32 `json:"pendingTypes,omitempty"`
// Ready is how many tracked types are Streaming.
Ready int32 `json:"ready"`

// FailingTypes is how many claimed types last failed their checkpoint sync.
// +optional
FailingTypes int32 `json:"failingTypes,omitempty"`
// Replaying is how many tracked types are still replaying their initial events.
Replaying int32 `json:"replaying"`

// NotFollowableTypes is how many claimed types are not currently followable — the
// claim-vs-refused mismatch an operator should notice.
// +optional
NotFollowableTypes int32 `json:"notFollowableTypes,omitempty"`
// Blocked is how many tracked types cannot currently be watched.
Blocked int32 `json:"blocked"`

// ObservedTime is when this roll-up was last computed.
// +optional
Expand All @@ -162,10 +146,14 @@ type GitTargetMaterializationStatus struct {
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Provider",type=string,JSONPath=`.spec.providerRef.name`
// +kubebuilder:printcolumn:name="Branch",type=string,JSONPath=`.spec.branch`
// +kubebuilder:printcolumn:name="Path",type=string,JSONPath=`.spec.path`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].message`
// +kubebuilder:printcolumn:name="Reconciling",type=string,JSONPath=`.status.conditions[?(@.type=="Reconciling")].status`
// +kubebuilder:printcolumn:name="Stalled",type=string,JSONPath=`.status.conditions[?(@.type=="Stalled")].status`
// +kubebuilder:printcolumn:name="GitPathAccepted",type=string,JSONPath=`.status.conditions[?(@.type=="GitPathAccepted")].status`
// +kubebuilder:printcolumn:name="StreamsRunning",type=string,JSONPath=`.status.conditions[?(@.type=="StreamsRunning")].status`
// +kubebuilder:printcolumn:name="Streams",type=string,JSONPath=`.status.streams.summary`
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=="Stalled")].reason`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Stalled")].message`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

// GitTarget is the Schema for the gittargets API.
Expand Down
44 changes: 43 additions & 1 deletion api/v1alpha2/watchrule_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,61 @@ type ResourceRule struct {

// WatchRuleStatus defines the observed state of WatchRule.
type WatchRuleStatus struct {
// ObservedGeneration is the latest generation observed by the controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`

// Conditions represent the latest available observations of an object's state
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`

// Streams is the bounded stream-readiness roll-up for the types this rule resolves.
// +optional
Streams *WatchRuleStreamsStatus `json:"streams,omitempty"`
}

// WatchRuleStreamsStatus is a bounded roll-up of the stream-readiness state for the
// types a WatchRule or ClusterWatchRule resolves.
type WatchRuleStreamsStatus struct {
// Summary is the display-only ready/total ratio.
// +optional
Summary string `json:"summary,omitempty"`

// Total is how many types this rule resolves.
Total int32 `json:"total"`

// Ready is how many resolved types are Streaming.
Ready int32 `json:"ready"`

// Replaying is how many resolved types are still replaying their initial events.
Replaying int32 `json:"replaying"`

// Blocked is how many resolved types cannot currently be watched.
Blocked int32 `json:"blocked"`

// PendingSample is a bounded sample of types not yet ready.
// +optional
// +kubebuilder:validation:MaxItems=5
PendingSample []string `json:"pendingSample,omitempty"`

// ObservedTime is when this roll-up was last computed.
// +optional
ObservedTime *metav1.Time `json:"observedTime,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Namespaced
// +kubebuilder:printcolumn:name="Target",type=string,JSONPath=`.spec.targetRef.name`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].message`
// +kubebuilder:printcolumn:name="Reconciling",type=string,JSONPath=`.status.conditions[?(@.type=="Reconciling")].status`
// +kubebuilder:printcolumn:name="Stalled",type=string,JSONPath=`.status.conditions[?(@.type=="Stalled")].status`
// +kubebuilder:printcolumn:name="GitTargetReady",type=string,JSONPath=`.status.conditions[?(@.type=="GitTargetReady")].status`
// +kubebuilder:printcolumn:name="StreamsRunning",type=string,JSONPath=`.status.conditions[?(@.type=="StreamsRunning")].status`
// +kubebuilder:printcolumn:name="Streams",type=string,JSONPath=`.status.streams.summary`
// +kubebuilder:printcolumn:name="Reason",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

// WatchRule watches namespaced resources within its own namespace.
Expand Down
78 changes: 56 additions & 22 deletions api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading