Skip to content

Stop the reconciler wedging private networks as failed - #12

Merged
vitramir merged 2 commits into
mainfrom
ziti-reconciler-fixes
Aug 10, 2026
Merged

Stop the reconciler wedging private networks as failed#12
vitramir merged 2 commits into
mainfrom
ziti-reconciler-fixes

Conversation

@vitramir

Copy link
Copy Markdown
Contributor

A private network sat at failed in the console for an hour while OpenZiti was perfectly healthy — the bind policy existed, the tunnel was enrolled and online. Three faults compounded.

@ resolves by id, not name. createResourceAccessDialPolicyRequest selected its service with @private-<resourceId> — the service name. OpenZiti answers no services found with the given ids; probing the controller with both forms against the same live service gives 400 for @name and 201 for @id. Every other @ reference in the platform already uses an id.

Rather than switch to the id, services now carry a private-resource-<id> role attribute and the dial policy selects #private-resource-<id>. An id goes stale the moment a service is recreated, and the access reconciler only checks that its policy exists — it would never notice the policy pointed at a dead service. A role attribute is derived from the resource id, so it survives recreation.

A failed re-provision erased the id it already had. UpdateNetworkProvisioning(ctx, id, failed, "") wrote an empty openziti_bind_policy_id over a good one, stranding the live OpenZiti object. Every later reconcile then conflicted on the unique name, permanently. keepID now preserves the recorded id when provisioning fails.

Nothing was logged. Provisioning errors were discarded, so both services looked idle. They are logged now — that is how the config-level conflict behind this was found at all.

Creates set return_existing, so a reconcile after a lost response adopts the existing object instead of conflicting forever. The private-resource guard also re-provisions a service missing its role attribute, so one predating this change converges instead of being skipped as "exists".

Needs ziti-management with the matching tag-filter and adopt fixes — without those the managed-object listing comes back empty and orphan cleanup never runs.

Verified end to end on the bundle VM: network, resource and access all reach active, the dial policy resolves to exactly 1 service and 1 identity, and two consecutive reconcile ticks write nothing.

Also adds devspace.yaml (the repo had none) and gitignores .devspace/ and vendored subcharts.

A sandbox workload matches no existing principal type. It carries no agent-<id>
attribute -- there is no agent class behind it -- and it cannot be a group
member, because groups collect users, agents, and apps. Nothing in the access
model could name the engineer at a sandbox shell.

An environment principal resolves to every workload running that environment,
agent workloads and sandboxes alike. Its dial policy names environment-<id>,
which the Orchestrator already stamps on every workload identity it creates and
which egress rule attachments already target -- so no new OpenZiti mechanism is
involved and nothing about the topology changes.

The two per-principal steps that assume an identity now branch. An environment
is a configuration resource and is not in the Identity registry, so existence
and organization resolve through Agents.GetEnvironment; the call forwards the
caller for the same reason the group lookup does. AGENTS_GRPC_TARGET joins the
dependency set, and WithAgentsClient keeps the existing constructors' shapes
since every other principal resolves without it.

Authorization is can_edit_config on the environment, matching what an egress
rule attachment to an environment already requires and what the agent principal
uses on its agent. Not organization owner: a grant here reaches anyone holding
can_use, because they can start a sandbox and a sandbox is a shell -- it adds a
destination to what a shell there already reaches rather than widening who
stands in front of it.

The enum gains its value by ALTER TYPE. The runner applies pending migrations in
one transaction and PostgreSQL forbids using an enum value in the transaction
that added it, so the file says as much for whoever adds the next one.
Three faults compounded into a network that reported failed forever while
OpenZiti was healthy.

OpenZiti resolves a "@" service role by id only, never by name, so the dial
policy for a resource access could never match its service. Services now carry
a private-resource-<id> role attribute and the policy selects on that, which
also survives the service being recreated as an id would not.

A reconcile that failed wrote an empty id over the one already recorded,
stranding the live OpenZiti object and guaranteeing every later attempt hit the
unique-name constraint. Provisioning failures were also swallowed unlogged,
so both services looked idle while this ran.

Creates now set return_existing so a reconcile after a lost response adopts
what is already there.

Adds a devspace.yaml so the service can run from sources against the VM.
@vitramir
vitramir merged commit 352d544 into main Aug 10, 2026
1 check passed
@vitramir
vitramir deleted the ziti-reconciler-fixes branch August 10, 2026 05:01
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.

1 participant