Take ownership of kubectl-managed resources
This content is for the v0.1 version. Switch to the latest version for up-to-date documentation.
Use a one-time server-side apply takeover when an existing resource was
applied with kubectl and should now be managed by Kix.
This guide uses a Deployment as the example. Back up the live resource first, and perform the takeover during a normal change window.
Record the live resource
Section titled “Record the live resource”Save the resource before changing its ownership:
❱ kubectl get deployment legacy-web -n takeover -o yaml --show-managed-fields > legacy-web.before.yaml Keep any fields your workload relies on, especially immutable selectors,
Service selectors, volume claims, and identity-bearing names. The
managedFields section also shows which manager owns each part of the object.
Make the Kix definition match
Section titled “Make the Kix definition match”Configure the package instance with the live Deployment’s selector and the state you want Kix to manage:
instances.takeover.legacy-web = { package = webPackage;
# Keep the live Deployment's immutable selector. selectorLabels = { app = "legacy-web"; tier = "frontend"; };
config = { replicas = 2; environment = "production"; message = "This workload is managed by Kix"; }; };The example retains the existing app and tier selector. Its desired replica
count is two, while the kubectl-managed fixture has one replica.
Check the cluster before contacting Kubernetes:
❱ kix check how-to-adoption-takeover
TOOL RESULT DETAILS
eval pass 10 manifests evaluated
kubeconform pass skipped (this validation tool is not yet integrated with Kix)
pluto pass skipped (this validation tool is not yet integrated with Kix)
kyverno pass skipped (this validation tool is not yet integrated with Kix)
scorecard pass 0 errors, 6 warnings, 2 info Inspect the rendered Deployment:
❱ kix build how-to-adoption-takeover --output json
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "legacy-web",
"namespace": "takeover"
},
"spec": {
"replicas": 2,
"selector": {
"matchLabels": {
"app": "legacy-web",
"tier": "frontend"
}
},
"template": {
"metadata": {
"labels": {
"app": "legacy-web",
"tier": "frontend"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "docker.io/library/nginx:1.27-alpine"
}
]
}
}
}
} Compare it with legacy-web.before.yaml. Do not continue if the rendered
resource changes an immutable selector or omits a field the workload still
needs. Correct the package configuration first.
Confirm the ownership conflict
Section titled “Confirm the ownership conflict”Run an API-server dry run without taking ownership:
❱ kix deploy how-to-adoption-takeover --dry-run -y Show output
⚠ Deployment/legacy-web@takeover field-ownership conflict on Deployment/legacy-web@takeover: another field manager owns one or more fields kix is trying to set. Re-run with --force-conflicts to take ownership. Underlying error: ApiError: Apply failed with 1 conflict: conflict with "kubectl": .spec.replicas: Conflict (ErrorResponse { status: "Failure", message: "Apply failed with 1 conflict: conflict with \"kubectl\": .spec.replicas", reason: "Conflict", code: 409 })
✗ halt-on-first-failure: field-ownership conflict on Deployment/legacy-web@takeover: another field manager owns one or more fields kix is trying to set. Re-run with --force-conflicts to take ownership. Underlying error: ApiError: Apply failed with 1 conflict: conflict with "kubectl": .spec.replicas: Conflict (ErrorResponse { status: "Failure", message: "Apply failed with 1 conflict: conflict with \"kubectl\": .spec.replicas", reason: "Conflict", code: 409 })
Dry run complete: 3 created, 2 configured, 0 unchanged, 1 failed, 4 cancelled
(exit code: 1) Kix discovers live state through its management labels, so kix diff does not
compare a resource that Kix has not managed before. The deployment dry run
sends the intended manifests to the API server without persisting them.
The API server rejects a change when another field manager owns a field Kix is
trying to change. The error identifies the resource and points to
--force-conflicts.
By default, the run stops after the first conflict and ends with 1 failed, 4 cancelled. The cancelled resources have not been checked. Run the dry run
again with --on-error continue to find conflicts on the remaining resources:
❱ kix deploy how-to-adoption-takeover --dry-run -y --on-error continue This still skips resources that depend on a failed resource, but continues
checking independent branches of the dependency graph. Review every reported
field against the saved resource before using --force-conflicts.
Take ownership once
Section titled “Take ownership once”When the diff and conflicting fields are expected, deploy with
--force-conflicts:
❱ kix deploy how-to-adoption-takeover --force-conflicts -y
~ Deployment/legacy-web@takeover configured
✔ Deployment/legacy-web@takeover ready
• activation 'how-to-adoption-takeover-nphkg40wdk30' → Active
Deploy complete: 6 created, 4 configured, 0 unchanged, 0 failed The flag tells server-side apply to transfer conflicting fields in the Kix
manifest to the kix field manager. Fields that Kix does not specify remain
with their existing managers.
Check the result and save its field ownership:
❱ kix status how-to-adoption-takeover❱ kubectl get deployment legacy-web -n takeover -o yaml --show-managed-fields > legacy-web.after.yaml Use ordinary kix deploy how-to-adoption-takeover commands after the takeover.
Do not leave --force-conflicts enabled for routine deployments, because a
later conflict may represent another controller making an intentional change.