Skip to main content Skip to content

K8s manifests: view and edit

View any Kubernetes resource on a registered cluster as YAML, diff and dry-run your changes, and apply them live with optimistic concurrency, RBAC checks, and an audit trail.

9 min read

ZopDay has a live manifest view and editor for every Kubernetes resource discovered on a registered cluster. You can read the running YAML, diff it against your edited copy, dry-run it, and apply the change. Kubernetes RBAC and ZopDay’s own permission system both gate every change, and every change is audited.

Before you start

  • A registered cluster. See Cluster registration.
  • The resource:update policy in ZopDay to edit. Without it, the Edit button isn’t shown. Editing a Secret requires organisation:update (admin).
  • Kubernetes RBAC on the cluster that lets ZopDay’s identity update the kind in that namespace. See RBAC on the cluster.

How it works

Open the manifest

Open a resource’s detail drawer and click Edit. The manifest drawer opens with the live YAML, read-only. Click Edit in the drawer to change it in the editor, then Apply: ZopDay dry-runs the change on the cluster and shows a side-by-side diff of the live manifest and your copy. Nothing is written until you click Confirm & Apply.

The Edit button on the detail drawer appears only when your role can update the resource, so without that permission there is no YAML view from this drawer.

The editor loads the first time you open it, so the first open takes a moment; later opens are faster.

Secret values

Secret values are admin-only. Admins get a Secret’s data and stringData. For anyone with read-only access, ZopDay strips the values: they can confirm the Secret exists and see its keys, but not what is in it. A request with no identity at all is treated as untrusted rather than as a system call. Its values are stripped and it gets no namespace access.

Stripping also removes kubectl.kubernetes.io/last-applied-configuration and any other annotation whose value carries a copy of the secret data. Those annotations echo the whole manifest, so removing only data would still expose the credentials.

Server-managed fields are visible but greyed out in the editor: status, metadata.managedFields, metadata.resourceVersion, and similar. You can edit them, but the server strips them and ignores the change on apply.

Why optimistic concurrency

ZopDay applies your edited manifest to the live cluster and checks resourceVersion first. If a colleague, or a controller, changed the same resource after you opened it, you get a 409 instead of overwriting their change. Refresh to load the live version and re-apply your edit.

If a GitOps controller (such as Argo CD or Flux) manages the resource, it may revert a hand edit on its next sync. Make lasting changes in the source it syncs from.

RBAC gating

A change must pass two permission layers:

  1. ZopDay RBAC: the user’s role must include the resource:update policy. The UI shows the Edit button only when it does.
  2. Kubernetes RBAC: on apply, the Kubernetes API server authorises the request. If the identity ZopDay uses on the cluster can’t update this kind in this namespace, the server returns 403.

Both must succeed. ZopDay checks its own policy first. The cluster checks its RBAC on apply, which costs a network round-trip.

Edit a manifest

  1. Open the YAML

    Open the resource’s detail drawer and click Edit.

  2. Edit

    Click Edit in the manifest drawer and change the manifest.

  3. Apply and review the diff

    Click Apply. ZopDay dry-runs the change on the cluster (the button reads Validating…), then shows the diff between the live manifest and your copy.

  4. Confirm

    Click Confirm & Apply. If the live resource changed since you opened it, you get a 409; refresh and re-apply.

Dry-run from the UI

Every Apply in the UI runs a dry run first. It calls the edit endpoint with dryRun: true, which the backend sends to the cluster as a server-side dry run (dryRun=All): the cluster validates the change and returns the object it would have written, without applying anything. If the cluster rejects the change, the error gives the reason (RBAC, validation, or conflict) and nothing is applied.

The diff you confirm is shown only after the dry run passes.

Use the API

Read a manifest

Terminal window
GET /resources/{resID}/live/manifest

Returns the Kubernetes object as JSON; the UI converts it to YAML for display. Secret values are stripped for non-admins, as above.

Edit a manifest

Terminal window
PUT /resources/{clusterResID}/children/{kind}/{namespace}/{name}/manifest

The request body is your full edited manifest as JSON. The endpoint does five things:

  1. Validates identity: name, namespace, and kind in the body must match the path. Mismatch returns 400.
  2. Strips server-managed fields: removes status, metadata.managedFields, and similar fields before apply.
  3. Blocks dangerous kinds: Node, CustomResourceDefinition, ClusterRole, ClusterRoleBinding, Role, RoleBinding, ServiceAccount, Pod, ReplicaSet, EndpointSlice. You can’t edit these here. Use the cloud console or kubectl instead. Secrets and Namespaces are editable here but cannot be deleted. See Dangerous-kind blocklist.
  4. Supports server-side dry-run: pass ?dryRun=All to validate without applying. The response is the mutated object as the cluster would have written it.
  5. Enforces optimistic concurrency: detects conflicts with resourceVersion. If the live resource changed since you read it, the endpoint returns 409 Conflict with the current resourceVersion. Refresh and re-apply.

Create a resource

Terminal window
POST /resources/{clusterResID}/children

Creates a new Kubernetes resource. Returns 409 if a resource already exists at that (kind, namespace, name). Use it for one-off resources outside the Helm / ZopDay deploy flow, such as a CR for a controller that ZopDay’s catalogue doesn’t know about yet.

Delete a resource

Terminal window
DELETE /resources/{clusterResID}/children/{kind}/{namespace}/{name}

Delete is idempotent. It succeeds whether the resource was deleted or was already gone. It returns an error only if the delete itself fails, for example on an RBAC denial or a finalizer hang.

CLI access

For automation, call the same endpoints with curl or an SDK:

Terminal window
# $ZOPDAY_API is your ZopDay API base URL; see the API reference
curl -X PUT "$ZOPDAY_API/resources/$RES_ID/children/Deployment/payments-prod/api-server/manifest" \
-H "Authorization: Bearer $ZOPDAY_PAT" \
-H "Content-Type: application/json" \
-d @manifest.json

The PAT acts as you, within the organisations and write access you gave it, so RBAC works as it does in the UI.

Dangerous-kind blocklist

These Kubernetes kinds cannot be created or edited through ZopDay’s manifest editor:

KindWhy
NodeEditing a node spec can break the cluster autoscaler in non-obvious ways. Use the cloud console.
CustomResourceDefinitionEditing a CRD can break every custom resource in the cluster. Use kubectl deliberately.
ClusterRole / ClusterRoleBindingRBAC changes are sensitive. Use a GitOps workflow or kubectl with peer review.
Role / RoleBindingSame.
ServiceAccountIdentity, and therefore RBAC, by another route.
PodEdit the thing that owns it (the Deployment or StatefulSet), not the pod it created.
ReplicaSetSame: owned by a Deployment, which is what you should be editing.
EndpointSliceMaintained by the cluster from your Services. A hand edit is overwritten.

The UI does not disable the Edit button for these kinds. The server rejects the change on Apply with a 403 saying editing that kind is not allowed.

Some kinds cannot be deleted through this surface: Node, CustomResourceDefinition, Secret, Namespace, PersistentVolume, StorageClass, ClusterRole, ClusterRoleBinding and IngressClass. Secrets and Namespaces stay editable, because a bad edit is recoverable. Deleting a Namespace deletes everything in it, and deleting a Secret breaks whatever mounts it.

For every other kind, the two permission layers govern delete. Deleting a sensitive kind succeeds only if both ZopDay RBAC and Kubernetes RBAC allow it. Take the same care with it, or use kubectl and a GitOps workflow.

Reference

Audit trail

Every successful create, update, or delete via the manifest endpoints:

  • Writes an audit log entry (actor, action, resource, full request and response bodies, timestamp)
  • Appears in Activity → Audit Logs
  • Is forwardable to your SIEM with the rest of the audit feed

Because the entry holds the full request and response bodies, compliance reviewers can see who changed what. Bodies over the audit size cap are truncated with a marker, as in Audit logs, so a very large manifest may not be captured in full. Keep this in mind when you edit a Secret: treat its audit entries as sensitive, and restrict audit-log:view accordingly.

Limitations

The manifest editor can’t yet do the following:

  • Edit across clusters in one operation: every edit targets one resource on one cluster. For multi-cluster changes, use a GitOps tool.
  • Edit by selector: you can’t say “edit every Deployment with label app=payments”. One resource at a time.
  • Diff against an arbitrary historical version: the diff compares the live manifest with your edits, not with earlier revisions. For deploy revisions, use the deployment history.

Troubleshooting

Apply returns 409 Conflict

Someone, or a controller, changed the resource after you opened it. Refresh to load the live version and re-apply your edit.

Apply returns 403

Kubernetes RBAC denied the change: the identity ZopDay uses on the cluster can’t update this kind in this namespace. Widen its ClusterRole binding, or make the change with kubectl.

My edit was reverted

A GitOps controller (such as Argo CD or Flux) manages the resource and restored it on its next sync. Make the change in the source it syncs from.

Apply says the kind is not allowed

The kind is on the dangerous-kind blocklist, and the server refused the edit with a 403. If there is no Edit button at all, your role lacks resource:update (or, for a Secret, organisation:update).

A delete times out

The resource has a stuck finalizer. Remove the finalizer by hand or with kubectl, then delete again.

Next steps

Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·