Skip to content

⭐️ use Server-Side Apply (SSA) for resource reconciliation#1381

Merged
chris-rock merged 2 commits intomainfrom
ssa-migration
Feb 13, 2026
Merged

⭐️ use Server-Side Apply (SSA) for resource reconciliation#1381
chris-rock merged 2 commits intomainfrom
ssa-migration

Conversation

@chris-rock
Copy link
Copy Markdown
Member

@chris-rock chris-rock commented Feb 6, 2026

Summary

This PR replaces the previous custom Server-Side Apply (SSA) implementation with controller-runtime's upstream CreateOrUpdate pattern.

  • Restore pkg/utils/k8s/create_update.go wrapper around ctrl.CreateOrUpdate with controller reference and logging
  • Add pkg/utils/k8s/update_fields.go with field-level update helpers (UpdateCronJobFields, UpdateDeploymentFields, UpdateDaemonSetFields) that copy only managed fields, preserving server-set defaults
  • Convert all handlers (container_image, k8s_scan, nodes, resource_watcher) to use CreateOrUpdate with field-level mutate functions
  • Replace DeleteAllOf with DeleteCompletedJobs to avoid killing running scan Jobs
  • Add TerminationMessagePath/TerminationMessagePolicy to all container specs to prevent unnecessary diffs against server defaults
  • Delete custom apply.go/apply_test.go (SSA approach)

Why the approach changed

The initial SSA implementation caused integration test failures because:

  1. Constant update loopobj.Spec = desired.Spec in mutate functions zeroed out server-set defaults (DNSPolicy, SchedulerName, TerminationGracePeriodSeconds, etc.), causing DeepEqual to find differences on every reconcile
  2. Aggressive job cleanupDeleteAllOf killed ALL Jobs (including running ones) on every detected "update", preventing scan pods from ever completing

The upstream CreateOrUpdate + field-level updates pattern avoids both issues by only mutating fields the operator manages, letting the API server retain its defaults.

Closes #687

Test plan

  • All unit tests pass (make test)
  • Linting passes (make lint, make lint/actions)
  • Integration tests pass on a real cluster
  • Verify resources are created/updated correctly without constant update loops

🤖 Generated with Claude Code

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 6, 2026

Test Results

  5 files  ± 0   42 suites  ±0   42m 43s ⏱️ + 4m 36s
293 tests  - 55  293 ✅  - 55  0 💤 ±0  0 ❌ ±0 
315 runs   - 55  313 ✅  - 55  2 💤 ±0  0 ❌ ±0 

Results for commit 983969f. ± Comparison against base commit 784a806.

This pull request removes 55 tests.
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual/should_be_equal_when_identical
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual/should_not_be_equal_when_Pod_volume_definition(s)_differ
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual/should_not_be_equal_when_concurrency_policy_differ
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual/should_not_be_equal_when_container_args_differ
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual/should_not_be_equal_when_container_commands_differ
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual/should_not_be_equal_when_container_count_differ
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual/should_not_be_equal_when_container_images_differ
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual/should_not_be_equal_when_container_resource_requirements_differ
go.mondoo.com/mondoo-operator/pkg/utils/k8s ‑ TestAreCronJobsEqual/should_not_be_equal_when_env_vars_differ
…

♻️ This comment has been updated with latest results.

@chris-rock chris-rock force-pushed the ssa-migration branch 2 times, most recently from 3e9a932 to 81c5ded Compare February 11, 2026 21:47
Copy link
Copy Markdown
Member

@imilchev imilchev left a comment

Choose a reason for hiding this comment

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

chris-rock and others added 2 commits February 12, 2026 20:49
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the custom Server-Side Apply implementation with controller-runtime's
upstream CreateOrUpdate pattern, which is simpler and avoids issues with
server-set defaults being zeroed out on every reconcile.

Key changes:
- Delete custom apply.go/apply_test.go
- Restore CreateOrUpdate wrapper from main branch
- Add field-level update helpers (UpdateCronJobFields, UpdateDeploymentFields,
  UpdateDaemonSetFields) to preserve server-set defaults like DNSPolicy,
  SchedulerName, TerminationGracePeriodSeconds, etc.
- Replace DeleteAllOf with DeleteCompletedJobs to avoid killing running scans
- Add TerminationMessagePath/TerminationMessagePolicy to all container specs
  to prevent unnecessary diffs against server defaults

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@chris-rock chris-rock changed the title ⭐️ migrate to Server-Side Apply (SSA) for resource reconciliation ⭐️ use upstream CreateOrUpdate for resource reconciliation Feb 12, 2026
@chris-rock chris-rock changed the title ⭐️ use upstream CreateOrUpdate for resource reconciliation ⭐️ use Server-Side Apply (SSA) for resource reconciliation Feb 12, 2026
Copy link
Copy Markdown
Member

@imilchev imilchev left a comment

Choose a reason for hiding this comment

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

LGTM! Lots of deletes!

@chris-rock chris-rock merged commit 99ec99d into main Feb 13, 2026
24 checks passed
@chris-rock chris-rock deleted the ssa-migration branch February 13, 2026 12:34
@github-actions github-actions Bot locked and limited conversation to collaborators Feb 13, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use server-side-apply for the operator

2 participants