From 50715f2a2221e738d8df5a45886d6f394389bbaf Mon Sep 17 00:00:00 2001 From: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Date: Tue, 4 Aug 2026 12:50:08 -0700 Subject: [PATCH] fix(tektonconfig): keep prune-per-resource=false in spec Motivation: setting spec.pruner.prune-per-resource to false on a TektonConfig CR caused the field to disappear from the stored spec after being applied (visible via kubectl get -o yaml). Approach: PrunePerResource was tagged `json:"prune-per-resource,omitempty"`. Go's zero value for bool is false, so omitempty drops the key whenever the value is false. The operator's Knative-based mutating webhook computes a "round trip patch" before running SetDefaults: it diffs the raw admission request bytes against json.Marshal(json.Unmarshal(bytes)), and any key that disappears in that round trip becomes a JSON Patch "remove" op applied to the admitted object (see vendor/knative.dev/pkg/webhook/resourcesemantics/defaulting/defaulting.go, roundTripPatch). An explicit false is therefore stripped from the persisted TektonConfig spec on every apply. Dropping omitempty fixes the round trip so the field survives. The existing +optional marker above the field keeps it optional in the generated CRD schema (confirmed: no diff to config/base/generated-crds or the Helm chart CRDs after regenerating with make generate-crds and hack/sync-helm-crds.sh). Note this only changes how the value is persisted: a missing key and an explicit false already decoded to the same Go zero value everywhere PrunePerResource is read (pkg/reconciler/common/prune.go), so there is no separate pruning behavior change beyond the field no longer vanishing from the spec. Validation: go build ./...; go test ./... (all packages pass, no failures); go vet ./pkg/apis/operator/v1alpha1/...; golangci-lint run ./pkg/apis/operator/v1alpha1/... --modules-download-mode=vendor (0 issues, run via a manually installed golangci-lint v2.12.2 since `make lint-go` could not download its pinned binary in this sandbox). Added TestPrune_PrunePerResourceJSONRoundTrip in tektonconfig_types_test.go; confirmed it fails with the old omitempty tag and passes with the fix. Report: https://github.com/tektoncd/operator/issues/3202 Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> --- .../operator/v1alpha1/tektonconfig_types.go | 2 +- .../v1alpha1/tektonconfig_types_test.go | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 pkg/apis/operator/v1alpha1/tektonconfig_types_test.go diff --git a/pkg/apis/operator/v1alpha1/tektonconfig_types.go b/pkg/apis/operator/v1alpha1/tektonconfig_types.go index a225e7132a..03f12ddc5a 100644 --- a/pkg/apis/operator/v1alpha1/tektonconfig_types.go +++ b/pkg/apis/operator/v1alpha1/tektonconfig_types.go @@ -59,7 +59,7 @@ type Prune struct { Disabled bool `json:"disabled"` // apply the prune job to the individual resources // +optional - PrunePerResource bool `json:"prune-per-resource,omitempty"` + PrunePerResource bool `json:"prune-per-resource"` // The resources which need to be pruned Resources []string `json:"resources,omitempty"` // The number of resource to keep diff --git a/pkg/apis/operator/v1alpha1/tektonconfig_types_test.go b/pkg/apis/operator/v1alpha1/tektonconfig_types_test.go new file mode 100644 index 0000000000..0043fc6161 --- /dev/null +++ b/pkg/apis/operator/v1alpha1/tektonconfig_types_test.go @@ -0,0 +1,49 @@ +/* +Copyright 2026 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "encoding/json" + "testing" + + "gotest.tools/v3/assert" +) + +// TestPrune_PrunePerResourceJSONRoundTrip guards against the "prune-per-resource" +// field being dropped when explicitly set to false. The admission webhook computes +// a round-trip patch by marshaling a freshly unmarshaled copy of the request object +// and diffing it against the original bytes; a `false` value on a field tagged +// `omitempty` is indistinguishable from an unset field once marshaled, so the +// webhook would emit a patch removing the key from the stored object. +func TestPrune_PrunePerResourceJSONRoundTrip(t *testing.T) { + raw := []byte(`{"disabled":false,"prune-per-resource":false}`) + + var p Prune + err := json.Unmarshal(raw, &p) + assert.NilError(t, err) + assert.Equal(t, p.PrunePerResource, false) + + out, err := json.Marshal(p) + assert.NilError(t, err) + + var roundTripped map[string]interface{} + err = json.Unmarshal(out, &roundTripped) + assert.NilError(t, err) + + _, present := roundTripped["prune-per-resource"] + assert.Assert(t, present, "prune-per-resource key was dropped from marshaled JSON when false: %s", string(out)) +}