Skip to content

feat: add FieldIgnore functionality#1708

Open
dgn wants to merge 2 commits into
istio-ecosystem:mainfrom
dgn:fieldignorerules
Open

feat: add FieldIgnore functionality#1708
dgn wants to merge 2 commits into
istio-ecosystem:mainfrom
dgn:fieldignorerules

Conversation

@dgn
Copy link
Copy Markdown
Collaborator

@dgn dgn commented Mar 17, 2026

this is a cleaner API around ignoring certain fields in watches and when installing charts, through a PostRender func. The goal is to eventually expose this API to the user, but for now it'll just help to keep track of our default ignores that we have implemented to fix specific bugs.

Pushing this here so we can discuss in this week's call.

Update: I added a bunch of tests and improved the API a bit. Removed the Regexes, but now you can select where the rules should apply: reconcile, reconcile + upgrade, or reconcile + upgrade + install

Fixes #430, #1148

@dgn
Copy link
Copy Markdown
Collaborator Author

dgn commented Mar 17, 2026

/retest

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 17, 2026

Codecov Report

❌ Patch coverage is 86.77249% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.81%. Comparing base (37f2998) to head (bb9df06).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
pkg/fieldignore/fieldignore.go 89.50% 9 Missing and 8 partials ⚠️
pkg/helm/chartmanager.go 66.66% 3 Missing and 3 partials ⚠️
cmd/main.go 0.00% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #1708       +/-   ##
===========================================
+ Coverage   69.52%   80.81%   +11.29%     
===========================================
  Files          46       52        +6     
  Lines        2838     2732      -106     
===========================================
+ Hits         1973     2208      +235     
+ Misses        697      394      -303     
+ Partials      168      130       -38     
Flag Coverage Δ
integration-tests 71.59% <79.14%> (+2.07%) ⬆️
unit-tests 54.35% <77.24%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a generalized “field ignore” mechanism used both by the Helm post-renderer and controller predicates to avoid reconcile/install/upgrade loops caused by external controllers mutating specific fields.

Changes:

  • Introduces pkg/fieldignore with rules, manifest field stripping, and an update predicate.
  • Replaces bespoke webhook ignore logic in the IstioRevision controller with fieldignore.NewPredicate and centralizes default rules.
  • Wires Helm chart install/upgrade post-rendering to apply ignore rules via ChartManager + updated NewHelmPostRenderer API.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pkg/helm/postrenderer_test.go Updates Helm post-renderer tests to pass field ignore rules and adds new coverage for caBundle stripping.
pkg/helm/postrenderer.go Replaces hard-coded webhook field stripping with rule-driven fieldignore.RemoveFieldsFromManifest.
pkg/helm/chartmanager.go Adds WithFieldIgnoreRules option and passes rules into Helm post-renderer for install/upgrade.
pkg/fieldignore/fieldignore.go New rule engine: rule compilation, manifest matching, field-path removal, and controller-runtime predicate.
pkg/fieldignore/fieldignore_test.go New unit tests for rule compilation/matching, field removal, and predicates.
controllers/istiorevision/istiorevision_controller.go Introduces DefaultFieldIgnoreRules and swaps webhook predicate implementation to fieldignore predicate.
cmd/main.go Configures ChartManager with the controller’s default ignore rules.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +27 to +48
// defaultTestRules replicates the built-in field ignore rules used in production.
var defaultTestRules = []fieldignore.FieldIgnoreRule{
{
Group: "admissionregistration.k8s.io",
Version: "v1",
Kind: "ValidatingWebhookConfiguration",
Fields: []string{"webhooks[*].failurePolicy"},
OnlyOnUpdate: true,
},
{
Group: "admissionregistration.k8s.io",
Version: "v1",
Kind: "ValidatingWebhookConfiguration",
Fields: []string{"webhooks[*].clientConfig.caBundle"},
},
{
Group: "admissionregistration.k8s.io",
Version: "v1",
Kind: "MutatingWebhookConfiguration",
Fields: []string{"webhooks[*].clientConfig.caBundle"},
},
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says these replicate production defaults, but they currently diverge from controllers/istiorevision.DefaultFieldIgnoreRules (notably missing NameRegex on the failurePolicy rule and not being pre-compiled). This can let tests pass even if production name-filtering or regex compilation behavior is broken. Consider either (a) reusing a shared default-rules definition from a package-level location (so tests can import it without depending on controllers/...), or (b) updating defaultTestRules to include the same NameRegex and using fieldignore.MustCompileRules(...) so the test exercises the same matching path as production.

Copilot uses AI. Check for mistakes.
Comment thread pkg/helm/postrenderer.go
return nil, fmt.Errorf("error removing ValidatingWebhookConfiguration failurePolicy: %v", err)
}
}
fieldignore.RemoveFieldsFromManifest(manifest, pr.fieldIgnoreRules, pr.isUpdate)
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RemoveFieldsFromManifest is “best-effort” and silently no-ops on unexpected manifest shapes (e.g., webhooks not being an array, non-map elements, etc.). In this post-renderer context that can reintroduce the exact upgrade/install drift this feature is meant to prevent, without any signal. Recommend changing the fieldignore API to return an error (or at least a boolean “removed something” + error) when a rule matches a manifest but one of the configured paths can’t be traversed due to a type mismatch, and then propagate/log that here so failures are visible.

Copilot uses AI. Check for mistakes.
Comment thread pkg/fieldignore/fieldignore.go Outdated
Comment on lines +200 to +209
func gvkForObject(scheme *runtime.Scheme, obj client.Object) (schema.GroupVersionKind, error) {
gvks, _, err := scheme.ObjectKinds(obj)
if err != nil {
return schema.GroupVersionKind{}, err
}
if len(gvks) == 0 {
return schema.GroupVersionKind{}, fmt.Errorf("no GVK found for object %T", obj)
}
return gvks[0], nil
}
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning gvks[0] can be incorrect when a type is registered under multiple GVKs/versions; picking the first can cause rules not to match and the predicate to incorrectly treat ignored-field-only updates as “changed” (leading to reconcile loops). Prefer a deterministic GVK resolution that matches the object’s actual GVK (e.g., controller-runtime’s apiutil.GVKForObject), or select the GVK that matches the object’s apiVersion/kind when available.

Copilot uses AI. Check for mistakes.
Comment thread pkg/helm/chartmanager.go Outdated
Comment on lines +57 to +59
func WithFieldIgnoreRules(rules []fieldignore.FieldIgnoreRule) ChartManagerOption {
return func(cm *ChartManager) {
cm.fieldIgnoreRules = rules
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This accepts rules in either compiled or uncompiled form, but uncompiled rules with NameRegex will fall back to regexp.MatchString(...) in Matches, incurring regex compilation overhead repeatedly during rendering/predicate evaluation. To make the option safer and more predictable, consider compiling once here (or documenting/enforcing that callers must pass fieldignore.MustCompileRules(...)), and failing fast on invalid regex rather than deferring to runtime behavior.

Suggested change
func WithFieldIgnoreRules(rules []fieldignore.FieldIgnoreRule) ChartManagerOption {
return func(cm *ChartManager) {
cm.fieldIgnoreRules = rules
// Rules are compiled when the option is applied so invalid regexes fail fast and
// matching does not repeatedly recompile patterns at render time.
func WithFieldIgnoreRules(rules []fieldignore.FieldIgnoreRule) ChartManagerOption {
return func(cm *ChartManager) {
cm.fieldIgnoreRules = fieldignore.MustCompileRules(rules)

Copilot uses AI. Check for mistakes.
Comment thread pkg/fieldignore/fieldignore.go Outdated
Comment on lines +50 to +51
// NameRegex is an optional regex to match resource names. Empty matches all names.
NameRegex string `json:"nameRegex,omitempty"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need regex support? This seems like it would make the API harder to use and could lead to false positives. iirc we removed the regex from the current hardcoded rules. Using either Name and (optionally) Namespace or a label selector to match resources would be more reliable. If these aren't specified then matching all GVKs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed it, now we match on exact name or match all objects of a kind

Comment thread pkg/fieldignore/fieldignore.go Outdated
Comment on lines +142 to +147
// NewPredicate returns a predicate that ignores changes to fields specified by
// the given rules. On update events it converts both old and new objects to
// unstructured maps, removes the ignored fields (plus standard metadata noise
// like resourceVersion, generation, managedFields), and only triggers
// reconciliation when the remaining content differs.
func NewPredicate(scheme *runtime.Scheme, rules []FieldIgnoreRule) predicate.Funcs {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it allowed to specify multiple FieldIgnoreRule for the same GVK? e.g.

podGVK := 
NewPredicate(s, []FieldIgnoreRule{
  {Group: "core", Version: "v1", Kind: "Pod", Fields: [".spec.imagePullSecrets"]},
  {Group: "core", Version: "v1", Kind: "Pod", Fields: [".spec.containers[*]"]},
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Predicates should have a type parameter: https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.23.3/pkg/predicate#Predicate

Maybe FieldIgnoreRule should have one as well so that the GVK matching can be enforced by the compiler? I think we only want to pass ignore rules that match the type anyways and not all DefaultIgnoreRules?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they are now typed!

@dgn dgn force-pushed the fieldignorerules branch from e24ecb7 to 3bac33d Compare April 24, 2026 14:47
@dgn dgn linked an issue Apr 24, 2026 that may be closed by this pull request
1 task
@dgn dgn changed the title [PoC] feat: add FieldIgnore functionality feat: add FieldIgnore functionality Apr 24, 2026
@dgn dgn force-pushed the fieldignorerules branch from 3bac33d to 325f26f Compare April 24, 2026 14:53
this is a cleaner API around ignoring certain fields in watches and when
installing charts, through a PostRender func. The goal is to eventually
expose this API to the user, but for now it'll just help to keep track
of our default ignores that we have implemented to fix specific bugs.

Signed-off-by: Daniel Grimm <dgrimm@redhat.com>
@dgn dgn force-pushed the fieldignorerules branch from 325f26f to bb9df06 Compare April 25, 2026 12:28
@dgn
Copy link
Copy Markdown
Collaborator Author

dgn commented Apr 28, 2026

/retest

Removes the "Untyped" variants of FieldIgnoreRules in favor of a passing `Unstructured` as the specific type for generic rules coming from either helm manifest or user input.

Signed-off-by: Nick Fox <nfox@redhat.com>
@nrfox nrfox mentioned this pull request May 5, 2026
6 tasks
@istio-testing
Copy link
Copy Markdown
Collaborator

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants