Skip to content

feat(render): label docker resources with a session ID - #431

Merged
jcogilvie merged 1 commit into
crossplane:mainfrom
jcogilvie:session-label
Oct 15, 2026
Merged

jcogilvie merged 1 commit into
crossplane:mainfrom
jcogilvie:session-label

Conversation

@jcogilvie

@jcogilvie jcogilvie commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Description of your changes

Adds the render.crossplane.io/session label to every Docker container and network crossplane render creates, completing the label set proposed in #401 (managed-by and cleanup landed in #407).

Callers can supply the session ID, so many renders in one process (or concurrent runs) can each own a distinct session and clean up only their own resources:

  • CLI / library: EngineFlags.SessionID (--session-id). The engine applies it to the network and engine container it creates, and annotates Functions in Setup (both the Docker and local-binary engines) so Function containers carry it too.
  • Per Function: the new render.crossplane.io/runtime-docker-session annotation (RuntimeDocker.Session). An annotation already on a Function is never overwritten.
  • If no ID is supplied, a random ID is generated and used as the fallback. It is shared by everything in the process that was not given an ID, so callers running independent renders should supply their own.

Not included: the optional created-by-pid label and a render cleanup subcommand.

Part of #401

I have:

🤖 Generated with Claude Code

@jcogilvie
jcogilvie force-pushed the session-label branch 4 times, most recently from 5ea578b to 305e286 Compare October 15, 2026 18:29
@jcogilvie
jcogilvie marked this pull request as ready for review October 15, 2026 18:30
@jcogilvie
jcogilvie requested review from a team and tampakrap as code owners October 15, 2026 18:30
@jcogilvie
jcogilvie requested review from jbw976 and removed request for a team October 15, 2026 18:30
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

@haarchri haarchri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nice!

@coderabbitai

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: crossplane/cli/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0f48db54-1abd-48fa-98bd-74c36913754d
📥 Commits

Reviewing files that changed from the base of the PR and between c6b004c and c61fed7.

📒 Files selected for processing: 10
  • cmd/crossplane/render/engine.go
  • cmd/crossplane/render/engine_docker.go
  • cmd/crossplane/render/engine_docker_test.go
  • cmd/crossplane/render/engine_local.go
  • cmd/crossplane/render/network.go
  • cmd/crossplane/render/network_test.go
  • cmd/crossplane/render/render.go
  • cmd/crossplane/render/runtime_docker.go
  • cmd/crossplane/render/runtime_docker_test.go
  • cmd/crossplane/render/session_test.go

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

Render engines now carry session IDs into Function annotations and Docker resource labels. If no session ID is supplied, Docker labeling uses a stable process-wide ID. Both Docker and local engine setup annotate Functions.

Changes: Render session labels

Layer / File(s) Summary
Session ID and runtime labels
cmd/crossplane/render/runtime_docker.go, cmd/crossplane/render/runtime_docker_test.go, cmd/crossplane/render/session_test.go
Runtime Docker configuration reads the session annotation and applies session labels. An empty session uses a stable process-wide ID. Tests cover supplied and default IDs.
Engine session propagation
cmd/crossplane/render/engine.go, cmd/crossplane/render/engine_docker.go, cmd/crossplane/render/engine_local.go, cmd/crossplane/render/render.go, cmd/crossplane/render/session_test.go
Engine flags pass the session ID to both engine types. Engine setup adds it to Functions that do not already have a session annotation.
Docker resource labels
cmd/crossplane/render/network.go, cmd/crossplane/render/engine_docker.go, cmd/crossplane/render/network_test.go, cmd/crossplane/render/engine_docker_test.go
Render networks and Function containers use labels derived from the session ID. Function container labels retain the cleanup-policy label.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant EngineFlags
  participant NewEngineFromFlags
  participant dockerRenderEngine
  participant Function
  participant GetRuntimeDocker
  participant Docker
  EngineFlags->>NewEngineFromFlags: pass SessionID
  NewEngineFromFlags->>dockerRenderEngine: construct with session
  dockerRenderEngine->>Function: inject session annotation during Setup
  Function->>GetRuntimeDocker: provide runtime Docker annotation
  GetRuntimeDocker->>Docker: provide session for container labels
Loading
Merge Risk: ⚪ Minimal · up to c61fe

This change adds a session label to Docker resources created by render. It is optional and falls back to a stable per-process ID, so no merge-blocking risk is evident.

Pre-merge checks: Passed 6
Check name Status Explanation
Title check Passed The title is 54 characters, stays under the 72-character limit, and clearly describes adding session IDs to Docker resources during rendering.
Description check Passed The description directly explains the session label, caller-supplied session IDs, annotation behavior, fallback behavior, scope, and test coverage.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Breaking Changes Passed No breaking change condition is introduced. The PR changes only cmd/**; it removes no public field or flag. EngineFlags.SessionID and RuntimeDocker.Session are optional string fields, and SessionID ha…
Feature Gate Requirement Passed The change is not an experimental command or API feature. The authoritative diff modifies only cmd/crossplane/render; it does not touch apis/**. The render command is registered as GA, and the new…
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jcogilvie
jcogilvie merged commit 9703fb5 into crossplane:main Oct 15, 2026
10 checks passed
jcogilvie added a commit to crossplane-contrib/crossplane-diff that referenced this pull request Oct 15, 2026
Bump github.com/crossplane/cli/v2 to 9703fb5 (crossplane/cli#431, not yet
released), which lets the caller set the render session ID that the engine
labels its containers and network with. The run's ID is now that session ID:

- Pass it as EngineFlags.SessionID to the docker and local engines, and set
  the runtime-docker-session annotation on a copy of each Function, so a
  session annotation the Function already carries cannot hide its container
  from the run.
- Stop naming function containers for the run and stop creating our own
  render network: the engine creates and labels its own.
- Reap containers, then networks, by the session and managed-by labels.
  Close removes the run's containers before the engine's own cleanup removes
  its network, then sweeps any of the run's networks left over.
- Keep the engine's cleanup when starting functions fails, rather than
  running it straight away while orphaned containers still hold the network.

Upstream moved to the moby/moby client, so dockerrun's Docker interface now
uses its types and gains NetworkList.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
jcogilvie added a commit to crossplane-contrib/crossplane-diff that referenced this pull request Oct 16, 2026
… died (#525) (#545)

* fix(diff): reclaim function containers and networks left by runs that died (#525)

A run that dies without cleaning up (SIGKILL, OOM, a second Ctrl+C, a CI
runner tearing down the job) leaves its function containers and the
crossplane-render-* network behind. Nothing tied them to the run that
made them, so no later run reclaimed them, and leaked networks
eventually exhaust Docker's address pools.

The render engine now ties what it creates to its run, through a new
cmd/diff/dockerrun package:

- Every function container is named <fn>-<version>-diff-<run ID>. The
  upstream Docker runtime can't label a function container, but it does
  honour the runtime-docker-name annotation. The engine takes over this
  naming from CachedFunctionProvider, so `xr` containers are named too,
  and the provider is now only a cache.
- The docker engine renders on a crossplane-diff-<run ID> network that
  the run creates and labels, instead of the unattributable network
  upstream would create. A network named by
  CROSSPLANE_DIFF_DOCKER_NETWORK is used but never created or removed.
- A run holds a flock on <user cache dir>/crossplane-diff/runs/<run
  ID>.lock while it has resources. Before starting any container, it
  removes the containers (by name, or by network membership for the
  unnamed render containers), then the network, of every run whose lease
  it can lock, i.e. whose process is dead. With no dead run this costs
  no Docker call at all.
- On exit the run sweeps its own resources the same way, which also
  catches containers upstream cleanup leaves behind. It keeps its lease
  if anything is left, so the next run retries.

Reaping is best effort: a failure is a warning and never fails the
diff. Live runs and resources crossplane-diff did not name are never
touched.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* refactor(diff): keep EngineRenderFn.Render under the cognitive-complexity limit

Move preparing the run and naming the functions' containers into
claimForRun, and drop a nolint directive golangci-lint reports unused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* docs(design): note that a lease does not record its run's Docker daemon

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* refactor(dockerrun): reap each dead run as its lease is taken

Drop the shared container listing and the dead-run bookkeeping it needed.
Each dead run is reaped right after its lease is locked, with one
ContainerList per dead run. Dead runs are rare and few, so the extra
listing costs nothing that matters, and reap no longer has a second mode
where the caller supplies the listing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* refactor(dockerrun): drop the lease identity re-check in tryLease

The re-check guarded against a second reaper locking a lease file that
the first had already reclaimed and unlinked. That reaper can only take
the lock after the first has finished and closed it, so its own reap
lists no containers for the run and finds the network gone: reaping
twice is harmless, and the check bought nothing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* refactor(diff): fold claimFunction into claimForRun

claimFunction had one caller and existed only to copy and set two
annotations. Do that in claimForRun's loop and keep its rationale in
claimForRun's doc comment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* fix(dockerrun): reclaim Docker resources by render session label

Bump github.com/crossplane/cli/v2 to 9703fb5 (crossplane/cli#431, not yet
released), which lets the caller set the render session ID that the engine
labels its containers and network with. The run's ID is now that session ID:

- Pass it as EngineFlags.SessionID to the docker and local engines, and set
  the runtime-docker-session annotation on a copy of each Function, so a
  session annotation the Function already carries cannot hide its container
  from the run.
- Stop naming function containers for the run and stop creating our own
  render network: the engine creates and labels its own.
- Reap containers, then networks, by the session and managed-by labels.
  Close removes the run's containers before the engine's own cleanup removes
  its network, then sweeps any of the run's networks left over.
- Keep the engine's cleanup when starting functions fails, rather than
  running it straight away while orphaned containers still hold the network.

Upstream moved to the moby/moby client, so dockerrun's Docker interface now
uses its types and gains NetworkList.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* fix(diff): drop a function's container name from the run's copy

The Docker runtime reuses an existing container of the runtime-docker-name
name without relabelling it, so concurrent runs, or concurrent engines in one
test binary, shared one container that the first to finish removed. The
integration fixtures' -it names made the suite fail exactly that way.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* test(it): drop Docker runtime annotations the render engine overrides

The engine now sets the session and cleanup annotations itself and drops any
container name, so the fixtures' annotations did nothing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* docs: describe run ownership by render session labels

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* docs: reconcile run-ownership docs with main's env-injecting provider

Main added EnvInjectingFunctionProvider, whose design-doc entry relied on
CachedFunctionProvider's per-run container names. The run now starts its own
unnamed containers instead, so say that. Regenerate diff-call-sequence.svg
from the merged mermaid source, and describe function container reuse in
CLAUDE.md as the engine does it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

* test(dockerrun): assert what runs leave behind, not the Docker calls they make

The dockerrun and render engine tests asserted the sequence and count of
Docker calls. Replace them with end-state assertions over a shared fake
Docker host (testutils.FakeDocker) that behaves like the daemon where it
matters: label filters apply as MatchKVList does, stopped containers are
listed only with All, running ones are removed only with Force, and a
network with an attached container cannot be removed. A wrong filter,
flag or removal order now leaves something behind and fails on outcome.

Fold Start's cases into one table and Close's into another, each
comparing the containers, networks and lease files left plus warnings
with one cmp.Diff. Drop the call-recording fake and the separate
no-Docker-call and liveness-only tests, which mirrored the
implementation; the liveness signal is covered end to end by
TestRunReclaimsARunThatDied.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>

---------

Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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