Add support for drift detection ignore rules#1627
Add support for drift detection ignore rules#1627dipti-pai wants to merge 1 commit intofluxcd:mainfrom
Conversation
029fa79 to
02e1e73
Compare
|
Any specific reason for the API to not match the API of the helm-controller? |
Signed-off-by: Dipti Pai <diptipai89@outlook.com>
02e1e73 to
e688fe8
Compare
We discussed this at the dev meetings. KC does drift detection and correction by design, so the envelop we use in HC would be confusing here, people would expect |
| #### Ignore a field on all resources | ||
|
|
||
| To ignore a specific field on all resources managed by the Kustomization: | ||
|
|
||
| ```yaml | ||
| --- | ||
| apiVersion: kustomize.toolkit.fluxcd.io/v1 | ||
| kind: Kustomization | ||
| metadata: | ||
| name: app | ||
| namespace: flux-system | ||
| spec: | ||
| # ...omitted for brevity | ||
| driftIgnoreRules: | ||
| - paths: | ||
| - "/spec/replicas" | ||
| ``` | ||
|
|
||
| #### Ignore fields on specific resources | ||
|
|
||
| To ignore fields only on resources that match a target selector: |
There was a problem hiding this comment.
| #### Ignore a field on all resources | |
| To ignore a specific field on all resources managed by the Kustomization: | |
| ```yaml | |
| --- | |
| apiVersion: kustomize.toolkit.fluxcd.io/v1 | |
| kind: Kustomization | |
| metadata: | |
| name: app | |
| namespace: flux-system | |
| spec: | |
| # ...omitted for brevity | |
| driftIgnoreRules: | |
| - paths: | |
| - "/spec/replicas" | |
| ``` | |
| #### Ignore fields on specific resources | |
| To ignore fields only on resources that match a target selector: | |
| Example: |
We have the warning about omitting the target, so I don't think we need a dedicated example for it.
Changes include :
DriftIgnoreRules[]tokustomizationspec.[]jsondiff.IgnoreRuleand set inApplyOptionsFix: #1138
Test summary with kustomize-controller
E2E Test Results — DriftIgnoreRules
Setup
Source manifests (in
ssaDriftIgnoreRulesTest/) deploy:test-config—managed-key: "v1",ignored-key: "original",another-ignored: "keep-me"test-nginx—nginx:1.25, 1 replica, with resource requests/limitstest-service— with annotationexternal-dns.alpha.kubernetes.io/hostname: "test.example.com"Reconciliation verification approach
Each test triggers reconciliation after introducing drift, checks the controller logs and confirms non-ignored fields are in-tact.
Test 1 — Ignore
/spec/replicason Deployments (HPA use case)Kustomization:
Initial state:
Simulate HPA scaling (server-side apply with
hpa-controllerfield manager):Trigger reconciliation:
After reconciliation — resource verification:
** controller log:**
{ "level": "info", "ts": "2026-03-31T17:44:58.182Z", "msg": "server-side apply completed", "Kustomization": {"name": "test-basic-ignore", "namespace": "drift-ignore-test"}, "reconcileID": "745d39ee-dca1-4654-b608-449d29b54a48", "output": { "ConfigMap/drift-ignore-test/test-config": "unchanged", "Deployment/drift-ignore-test/test-nginx": "configured", "Namespace/drift-ignore-test": "unchanged", "Service/drift-ignore-test/test-service": "unchanged" } }Test 2 — Targeted selectors (external-dns + ConfigMap field)
Kustomization:
Initial state:
Simulate external-dns and controller changes (server-side apply):
After reconciliation — resource verification:
controller log:
{ "level": "info", "ts": "2026-03-31T17:47:36.973Z", "msg": "server-side apply completed", "Kustomization": {"name": "test-targeted-ignore", "namespace": "drift-ignore-test"}, "reconcileID": "072c53f3-47b8-465d-8788-01fe2266e6aa", "output": { "ConfigMap/drift-ignore-test/test-config": "configured", "Deployment/drift-ignore-test/test-nginx": "unchanged", "Namespace/drift-ignore-test": "unchanged", "Service/drift-ignore-test/test-service": "configured" } }Test 3 — Multi-resource (Deployment replicas + Service annotations + ConfigMap fields)
Kustomization:
Out-of-band changes via server-side apply (3 different field managers):
Before reconciliation:
After reconciliation — resource verification:
** controller log:**
{ "level": "info", "ts": "2026-03-31T17:50:30.122Z", "msg": "server-side apply completed", "Kustomization": {"name": "test-multi-resource", "namespace": "drift-ignore-test"}, "reconcileID": "76976031-dc4a-4763-a6d1-d0b4adaa9d44", "output": { "ConfigMap/drift-ignore-test/test-config": "configured", "Deployment/drift-ignore-test/test-nginx": "configured", "Namespace/drift-ignore-test": "unchanged", "Service/drift-ignore-test/test-service": "unchanged" } }Test 4 — Nested paths (VPA container resources)
Kustomization:
Initial state:
Simulate VPA adjusting resources (server-side apply with
vpa-recommenderfield manager):After reconciliation — VPA resources preserved:
controller log (VPA change):
{ "level": "info", "ts": "2026-03-31T17:53:07.760Z", "msg": "server-side apply completed", "Kustomization": {"name": "test-nested-paths", "namespace": "drift-ignore-test"}, "reconcileID": "61099129-e120-4b06-8643-0e3fe4f2df51", "output": { "ConfigMap/drift-ignore-test/test-config": "unchanged", "Deployment/drift-ignore-test/test-nginx": "configured", "Namespace/drift-ignore-test": "unchanged", "Service/drift-ignore-test/test-service": "unchanged" } }Test 4b — Non-ignored field drift correction
Change image to nginx:1.24, then reconcile:
controller log (image correction):
{ "level": "info", "ts": "2026-03-31T17:54:34.124Z", "msg": "server-side apply completed", "Kustomization": {"name": "test-nested-paths", "namespace": "drift-ignore-test"}, "reconcileID": "271aa062-20eb-4146-aaf6-da883e243f19", "output": { "ConfigMap/drift-ignore-test/test-config": "unchanged", "Deployment/drift-ignore-test/test-nginx": "configured", "Namespace/drift-ignore-test": "unchanged", "Service/drift-ignore-test/test-service": "unchanged" } }Test 5 — Edge cases (non-existent paths + non-matching selector)
Kustomization:
Initial state — reconciles successfully despite non-existent paths:
Change
ignored-keyvia SSA (rule targetsdoes-not-existCM, so should NOT matchtest-config):controller log:
{ "level": "info", "ts": "2026-03-31T17:57:09.475Z", "msg": "server-side apply completed", "Kustomization": {"name": "test-edge-cases", "namespace": "drift-ignore-test"}, "reconcileID": "2daa7063-36f1-46af-9c43-6cb674e399c8", "output": { "ConfigMap/drift-ignore-test/test-config": "configured", "Deployment/drift-ignore-test/test-nginx": "unchanged", "Namespace/drift-ignore-test": "unchanged", "Service/drift-ignore-test/test-service": "unchanged" } }No errors in controller logs:
Test 6 — Ignore mandatory
/spec/selector, introduce drift, no other SSA ownerKustomization:
Initial creation succeeds:
Introduce image drift via non-SSA patch (no new manager claims selector):
After reconciliation — APPLY FAILS:
Controller error log:
{ "level": "error", "ts": "2026-03-31T22:06:05.184Z", "msg": "Reconciliation failed after 206.673654ms, next try in 2m0s", "Kustomization": {"name": "test-6c-selector-no-owner", "namespace": "drift-test-10"}, "error": "Deployment/drift-test-10/test-nginx apply failed: Deployment.apps \"test-nginx\" is invalid: [spec.selector: Required value, spec.template.metadata.labels: Invalid value: {\"app\":\"nginx\"}: `selector` does not match template `labels`, spec.selector: Invalid value: null: field is immutable]" }