Skip to content

Fix omitted fields persisting when applying Kubernetes resources - #536

Open
thokra-nav wants to merge 2 commits into
mainfrom
apply_create_update
Open

thokra-nav wants to merge 2 commits into
mainfrom
apply_create_update

Conversation

@thokra-nav

Copy link
Copy Markdown
Contributor

Replace server-side apply with create/update operations so submitted manifests define the entire desired object.

Server-side apply retains omitted fields when another field manager owns them. This caused removed fields such as  spec.resources.limits.cpu  to remain on existing resources.

Existing resources are now replaced using their current  resourceVersion  to detect concurrent updates. Strict field validation and before/after change reporting are preserved.

This intentionally introduces replacement semantics for all supported resource types, including omitted metadata and data fields. Kubernetes defaults and validation still apply, and writes require create/update permissions.

Adds regression coverage for field removal across ownership histories, full-object replacement, resource versions, error propagation, and idempotent reapplication.

Replace server-side apply with create/update operations so submitted
manifests define the entire desired object.

Server-side apply retains omitted fields when another field manager owns
them. This caused removed fields such as  spec.resources.limits.cpu  to
remain on existing resources.

Existing resources are now replaced using their current
 resourceVersion  to detect concurrent updates. Strict field validation
and before/after change reporting are preserved.

This intentionally introduces replacement semantics for all supported
resource types, including omitted metadata and data fields. Kubernetes
defaults and validation still apply, and writes require create/update
permissions.

Adds regression coverage for field removal across ownership histories,
full-object replacement, resource versions, error propagation, and
idempotent reapplication.
@thokra-nav
thokra-nav requested a review from a team as a code owner October 2, 2026 08:24
@thokra-nav
thokra-nav requested a balanced review from Copilot October 2, 2026 08:24

Copilot AI left a comment

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.

Copilot review overview

🟡 Changes recommended

Raw updates can reject resources with server-assigned immutable fields and erase controller-managed metadata.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Replaces server-side apply with create/update semantics so omitted manifest fields are removed.

Changes:

  • Uses strict CREATE/UPDATE operations with resource-version conflict detection.
  • Adds unit and integration coverage for replacement, removals, errors, and idempotency.
File Description
internal/​apply/​client.go Implements create-or-replace behavior.
internal/​apply/​client_test.go Tests replacement and concurrency behavior.
integration_tests/​apply_replace.lua Verifies field removal end to end.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/apply/client.go Outdated
Transfer Update ownership from kubectl-create, kubectl-client-side-apply, and deployd to nais-api so omitted fields can be removed while preserving other managers' ownership. Reject foreign-field conflicts and unsafe cross-version migrations, and retain caller resourceVersion preconditions across migration.

Make the Kubernetes fake ownership-aware and cover omission, migration, controller ownership, and concurrency regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

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.

Copilot review overview

🟡 Changes recommended

The implementation contradicts the promised replacement semantics and leaves concurrency and partial-write risks.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
Resolved since last review (1)

Comment thread internal/apply/client.go
Comment on lines +84 to +89
if patch != nil {
migrated, err := resourceClient.Patch(ctx, name, types.JSONPatchType, patch, metav1.PatchOptions{
FieldManager: fieldManager,
})
if err != nil {
return nil, fmt.Errorf("migrating field ownership for %s/%s: %w", namespace, name, err)
Comment thread internal/apply/client.go
Comment on lines +91 to +96
if submittedVersion != "" {
if migrated == nil || migrated.GetResourceVersion() == "" {
return nil, fmt.Errorf("ownership migration for %s/%s returned no resourceVersion", namespace, name)
}
desired.SetResourceVersion(migrated.GetResourceVersion())
}
Comment thread internal/apply/client.go
Comment on lines +104 to +108
after, err := resourceClient.Patch(ctx, name, types.ApplyPatchType, data, metav1.PatchOptions{
FieldManager: fieldManager,
Force: new(false),
FieldValidation: metav1.FieldValidationStrict,
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants