Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .docs-lint-scope
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,8 @@ docs/spec/attribution.md
docs/components.md
docs/quickstart.md
docs/demo/commit-window.md
docs/facts/kubernetes-watch-options.md
docs/spec/watch-event-ordering-and-attribution-grace.md
# Temporary: the watch-rule step 2 plan. Its completion criteria says to remove
# its line here once that step ships.
docs/design/watches-labels-simplification.md
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ loadtest
.claude/*
.agents/*
/external-sources/
/docs/local-only/*
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ agent over MCP.

Custom resources configure it: `GitProvider` for repository and credentials, `GitTarget` for branch
and folder, `WatchRule` or `ClusterWatchRule` for what to capture, and `ClusterProvider` for the
source cluster, created as `default` on install. See [Configuration](docs/configuration.md).
source cluster, created as `default` on install. A rule selects whole resource collections, including
their deletions; the `GitTarget`'s prune mode decides whether a deletion removes the file from Git.
See [Configuration](docs/configuration.md).

By default it watches its own cluster. Add more of each as you need them: one operator can serve
several source clusters, repositories, branches, and folders.
Expand Down
8 changes: 0 additions & 8 deletions api/v1alpha3/clusterwatchrule_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,6 @@ type ClusterWatchRuleSpec struct {
// ClusterResourceRule defines which CLUSTER-SCOPED resources to watch. It deliberately has no
// sourceNamespace: cluster-scoped objects have no namespace, so there is nothing to select.
type ClusterResourceRule struct {
// Operations to watch. If empty, watches all operations (CREATE, UPDATE, DELETE).
// Supports: CREATE, UPDATE, DELETE, or * (wildcard for all operations).
// Examples:
// - ["CREATE", "UPDATE"] watches only creation and updates
// - ["*"] or [] watches all operations
// +optional
Operations []OperationType `json:"operations,omitempty"`

// APIGroups to match. Empty string ("") matches the core API group.
// If omitted, GitOps Reverser resolves the resource name across all served API groups.
// Wildcards supported: "*" matches all groups.
Expand Down
38 changes: 8 additions & 30 deletions api/v1alpha3/watchrule_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// OperationType specifies the type of operation that triggers a watch event.
// +kubebuilder:validation:Enum=CREATE;UPDATE;DELETE;*
type OperationType string

const (
// OperationCreate matches resource creation events.
OperationCreate OperationType = "CREATE"
// OperationUpdate matches resource update events.
OperationUpdate OperationType = "UPDATE"
// OperationDelete matches resource deletion events.
OperationDelete OperationType = "DELETE"
// OperationAll matches all operation types.
OperationAll OperationType = "*"
)

// WatchRuleSpec defines the desired state of WatchRule.
// WatchRule selects NAMESPACED resources on its GitTarget's source cluster. Each rules[] item
// carries its own source namespace: omitted for this WatchRule's own namespace, an explicit name,
Expand All @@ -36,7 +21,9 @@ type WatchRuleSpec struct {

// Rules define which resources to watch, and in which source namespaces.
// Multiple rules create a logical OR - a resource matching ANY rule is watched.
// Each rule can specify operations, API groups, versions, resource types, and a source namespace.
// Each rule can specify API groups, versions, resource types, and a source namespace.
// A selected resource collection is observed through its whole lifecycle; the GitTarget's
// spec.prune.mode decides whether an observed removal deletes the Git document.
// +required
// +kubebuilder:validation:MinItems=1
Rules []ResourceRule `json:"rules"`
Expand All @@ -46,14 +33,6 @@ type WatchRuleSpec struct {
// Omitted API groups and versions are resolved from the served Kubernetes API surface.
// All fields except Resources are optional.
type ResourceRule struct {
// Operations to watch. If empty, watches all operations (CREATE, UPDATE, DELETE).
// Supports: CREATE, UPDATE, DELETE, or * (wildcard for all operations).
// Examples:
// - ["CREATE", "UPDATE"] watches only creation and updates, ignoring deletions
// - ["*"] or [] watches all operations
// +optional
Operations []OperationType `json:"operations,omitempty"`

// APIGroups to match. Empty string ("") matches the core API group.
// If omitted, GitOps Reverser resolves the resource name across all served API groups.
// Wildcards supported: "*" matches all groups.
Expand Down Expand Up @@ -104,11 +83,10 @@ type ResourceRule struct {
// A denied explicit name refuses the WHOLE WatchRule rather than trimming that item: mirroring
// two of the three namespaces a rule asked for is worse than a loud failure.
//
// A cluster-wide cell is a PEER of a named-namespace cell on the same type, never a
// replacement: each rule carries its own operations filter, and collapsing the two once widened
// a named rule's stream while discarding that filter (see CellKey in internal/types/cell.go).
// A target carrying both therefore runs two streams over overlapping objects, and that is
// correct.
// A cluster-wide collection is a PEER of a named-namespace collection on the same type, never a
// replacement: collapsing the two once widened a named rule's stream to every namespace its
// credential could read (see CollectionKey in internal/types/collection.go). A target carrying
// both therefore runs two streams over overlapping objects, and that is correct.

// SourceNamespace is the namespace this item watches IN THE SOURCE CLUSTER its GitTarget
// mirrors from: omitted for this WatchRule's own namespace, an exact name for one other, or
Expand Down Expand Up @@ -140,7 +118,7 @@ const SourceNamespaceWildcard = "*"
// EffectiveSourceNamespace is the source-cluster namespace this ITEM names, given the namespace of
// the WatchRule that carries it: spec.rules[].sourceNamespace when set, and the rule's OWN
// namespace otherwise. For a wildcard item it returns "*", which is not a namespace name: the
// caller compiles it to the cluster-wide cell (the empty namespace) rather than watching it.
// caller compiles it to the cluster-wide collection (the empty namespace) rather than watching it.
//
// It is controller logic rather than an API-server default because an apiserver default cannot
// refer to metadata.namespace.
Expand Down
10 changes: 0 additions & 10 deletions api/v1alpha3/zz_generated.deepcopy.go

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

5 changes: 0 additions & 5 deletions charts/gitops-reverser/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -569,11 +569,6 @@
"type": "object",
"additionalProperties": false,
"properties": {
"operations": {
"type": "array",
"minItems": 1,
"items": { "enum": ["CREATE", "UPDATE", "DELETE"] }
},
"apiGroups": { "type": "array", "items": { "type": "string" } },
"apiVersions": { "type": "array", "items": { "type": "string" } },
"resources": { "type": "array", "items": { "type": "string" } }
Expand Down
6 changes: 1 addition & 5 deletions charts/gitops-reverser/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -455,11 +455,7 @@ quickstart:
watchRule:
name: example-watchrule
rules:
- operations:
- CREATE
- UPDATE
- DELETE
apiGroups:
- apiGroups:
- ""
apiVersions:
- v1
Expand Down
62 changes: 62 additions & 0 deletions cmd/main_chart_quickstart_rules_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: Apache-2.0

package main

import (
"os"
"os/exec"
"testing"

"github.com/stretchr/testify/require"
)

// quickstartChartInputs are read in-process so `go test` treats them as cache inputs; helm reads
// the chart in a subprocess the test log cannot see.
var quickstartChartInputs = []string{
"../charts/gitops-reverser/values.yaml",
"../charts/gitops-reverser/values.schema.json",
"../charts/gitops-reverser/templates/quickstart.yaml",
}

func helmTemplateQuickstart(t *testing.T, setValues ...string) ([]byte, error) {
t.Helper()
for _, path := range quickstartChartInputs {
_, err := os.ReadFile(path)
require.NoError(t, err)
}
args := []string{
"template", "gitops-reverser", "../charts/gitops-reverser",
"--show-only", "templates/quickstart.yaml",
"--set", "quickstart.enabled=true",
"--set", "quickstart.gitProvider.url=https://example.com/repo.git",
}
for _, sv := range setValues {
args = append(args, "--set", sv)
}
return exec.Command("helm", args...).CombinedOutput()
}

// The starter WatchRule renders without a rules[].operations key: a rule selects a resource
// collection, which is observed through its whole lifecycle.
func TestChartQuickstart_StarterWatchRuleRendersWithoutOperations(t *testing.T) {
out, err := helmTemplateQuickstart(t)
require.NoErrorf(t, err, "helm template failed: %s", out)

rendered := string(out)
require.Contains(t, rendered, "kind: WatchRule")
require.Contains(t, rendered, "- configmaps")
require.NotContains(t, rendered, "operations")
}

// rules[].operations is gone from the API. A values file that still sets it must fail the render
// rather than install a rule the user believes suppresses deletions: the removed key is not
// deletion protection, the GitTarget's prune mode is.
func TestChartQuickstart_RemovedOperationsKeyFailsTheRender(t *testing.T) {
out, err := helmTemplateQuickstart(t,
"quickstart.watchRule.rules[0].resources[0]=configmaps",
"quickstart.watchRule.rules[0].operations[0]=CREATE",
)
require.Errorf(t, err, "render should have failed, got:\n%s", out)
require.Contains(t, string(out),
"at '/quickstart/watchRule/rules/0': additional properties 'operations' not allowed")
}
17 changes: 0 additions & 17 deletions config/crd/bases/configbutler.ai_clusterwatchrules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,23 +131,6 @@ spec:
items:
type: string
type: array
operations:
description: |-
Operations to watch. If empty, watches all operations (CREATE, UPDATE, DELETE).
Supports: CREATE, UPDATE, DELETE, or * (wildcard for all operations).
Examples:
- ["CREATE", "UPDATE"] watches only creation and updates
- ["*"] or [] watches all operations
items:
description: OperationType specifies the type of operation
that triggers a watch event.
enum:
- CREATE
- UPDATE
- DELETE
- '*'
type: string
type: array
resources:
description: |-
Resources to match (plural names like "nodes", "clusterroles").
Expand Down
21 changes: 3 additions & 18 deletions config/crd/bases/configbutler.ai_watchrules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ spec:
description: |-
Rules define which resources to watch, and in which source namespaces.
Multiple rules create a logical OR - a resource matching ANY rule is watched.
Each rule can specify operations, API groups, versions, resource types, and a source namespace.
Each rule can specify API groups, versions, resource types, and a source namespace.
A selected resource collection is observed through its whole lifecycle; the GitTarget's
spec.prune.mode decides whether an observed removal deletes the Git document.
items:
description: |-
ResourceRule defines a set of namespaced resources to watch.
Expand Down Expand Up @@ -134,23 +136,6 @@ spec:
items:
type: string
type: array
operations:
description: |-
Operations to watch. If empty, watches all operations (CREATE, UPDATE, DELETE).
Supports: CREATE, UPDATE, DELETE, or * (wildcard for all operations).
Examples:
- ["CREATE", "UPDATE"] watches only creation and updates, ignoring deletions
- ["*"] or [] watches all operations
items:
description: OperationType specifies the type of operation
that triggers a watch event.
enum:
- CREATE
- UPDATE
- DELETE
- '*'
type: string
type: array
resources:
description: |-
Resources to match (plural names like "pods", "configmaps").
Expand Down
3 changes: 1 addition & 2 deletions config/samples/clusterwatchrule.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ spec:
name: example-target
namespace: default
rules:
- operations: [CREATE, UPDATE, DELETE]
apiGroups: ["apiextensions.k8s.io"]
- apiGroups: ["apiextensions.k8s.io"]
apiVersions: ["v1"]
resources: ["customresourcedefinitions"]
3 changes: 1 addition & 2 deletions config/samples/quickstart-watchrule.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ spec:
gitTargetRef:
name: example-target
rules:
- operations: [CREATE, UPDATE, DELETE]
apiGroups: [""]
- apiGroups: [""]
apiVersions: ["v1"]
resources: ["configmaps"]
Loading
Loading