Skip to content

feat(python): expose additionalPodMetadata in sandbox client#979

Merged
kubernetes-prow[bot] merged 2 commits into
kubernetes-sigs:mainfrom
mvanhorn:feat/914-python-client-pod-metadata
Jun 23, 2026
Merged

feat(python): expose additionalPodMetadata in sandbox client#979
kubernetes-prow[bot] merged 2 commits into
kubernetes-sigs:mainfrom
mvanhorn:feat/914-python-client-pod-metadata

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

The SandboxClaim CRD already supports spec.additionalPodMetadata, and the
claim controller propagates its labels and annotations onto the running
Sandbox Pod. Until now the Python client had no way to set it: the existing
labels argument on create_sandbox only wires the claim object's own
metadata.labels, which lands on the SandboxClaim resource and never reaches
the Pod.

This PR adds two new optional, keyword-only parameters to both the sync
(SandboxClient) and async (AsyncSandboxClient) create_sandbox:

  • pod_labels -> spec.additionalPodMetadata.labels
  • pod_annotations -> spec.additionalPodMetadata.annotations

They flow from the public client through _create_claim into the
create_sandbox_claim manifest builders (k8s_helper / async_k8s_helper),
mirroring the existing labels / lifecycle plumbing rather than introducing a
new pattern. With this, a caller can stamp a tenant / client identifier onto the
Pod and the running sandbox can read its own identity via the Downward API.

Details:

  • Additive and keyword-only, so existing callers are unaffected.
  • pod_labels reuse the existing _validate_labels RFC-1123 validation so bad
    keys/values fail fast client-side (the controller validates server-side too).
  • Empty sections are omitted; no empty additionalPodMetadata is written when
    neither pod_labels nor pod_annotations is provided.
  • README documents the distinction between claim-level labels and Pod-level
    pod_labels / pod_annotations.

Which issue(s) this PR is related to:

Fixes #914
Ref #914

Testing

  • Added unit tests for the sync and async k8s helpers (manifest emits
    spec.additionalPodMetadata; key omitted when unset).
  • Added unit tests for the sync and async clients (parameters thread through to
    _create_claim / create_sandbox_claim as the expected pod_metadata dict;
    invalid pod-label keys raise the same validation error as claim labels).
  • Updated existing client tests that assert the exact _create_claim /
    create_sandbox_claim call signature.
  • python -m pytest k8s_agent_sandbox/test/unit passes (223 passed).

AI was used for assistance.

Release Note

The Python `SandboxClient` and `AsyncSandboxClient` now accept `pod_labels` and `pod_annotations` on `create_sandbox`, which are propagated to the Sandbox Pod via `spec.additionalPodMetadata`.

Summary by CodeRabbit

  • New Features

    • Added pod_labels and pod_annotations parameters to the Python sandbox clients to apply metadata directly to the running Sandbox Pod.
    • Supported in both synchronous and asynchronous clients, with client-side validation for pod label formatting.
  • Documentation

    • Expanded the client README with a “Labels and Pod Metadata” section and new usage snippets, including Kubernetes validation expectations.
    • Fixed the “Run in Production Mode” code example formatting.
Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
@netlify

netlify Bot commented Jun 14, 2026

Copy link
Copy Markdown

Deploy Preview for agent-sandbox ready!

Name Link
🔨 Latest commit 127dfbb
🔍 Latest deploy log https://app.netlify.com/projects/agent-sandbox/deploys/6a39e0ed3e19890008207288
😎 Deploy Preview https://deploy-preview-979--agent-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@k8s-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: mvanhorn
Once this PR has been reviewed and has the lgtm label, please assign janetkuo for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Jun 14, 2026
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

Hi @mvanhorn. Thanks for your PR.

I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 78888f33-6d4a-40ba-b4d0-90ace88aa37d

📥 Commits

Reviewing files that changed from the base of the PR and between 7589d35 and 127dfbb.

📒 Files selected for processing (5)
  • clients/python/agentic-sandbox-client/README.md
  • clients/python/agentic-sandbox-client/k8s_agent_sandbox/async_sandbox_client.py
  • clients/python/agentic-sandbox-client/k8s_agent_sandbox/pod_metadata.py
  • clients/python/agentic-sandbox-client/k8s_agent_sandbox/sandbox_client.py
  • clients/python/agentic-sandbox-client/k8s_agent_sandbox/test/unit/test_sandboxclient.py
✅ Files skipped from review due to trivial changes (2)
  • clients/python/agentic-sandbox-client/k8s_agent_sandbox/pod_metadata.py
  • clients/python/agentic-sandbox-client/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • clients/python/agentic-sandbox-client/k8s_agent_sandbox/sandbox_client.py
  • clients/python/agentic-sandbox-client/k8s_agent_sandbox/test/unit/test_sandboxclient.py

📝 Walkthrough

Walkthrough

Both SandboxClient and AsyncSandboxClient gain optional pod_labels and pod_annotations keyword parameters on create_sandbox. A new pod_metadata.py module validates Kubernetes label syntax and assembles the metadata payload. The clients validate pod labels, construct pod_metadata, and flow it through _create_claim to K8sHelper/AsyncK8sHelper.create_sandbox_claim, where it is written into spec.additionalPodMetadata in the SandboxClaim manifest. Tests and README documentation are updated accordingly.

Changes

Pod labels/annotations propagation

Layer / File(s) Summary
Pod metadata validation and construction module
k8s_agent_sandbox/pod_metadata.py
New module with validate_label_name, validate_labels, and build_pod_metadata functions. Validates Kubernetes label keys (including optional prefix/name form) and values against regex-based segment constraints, enforces length limits, and assembles additionalPodMetadata from provided labels and annotations, returning None when both are absent.
K8s helpers extended to accept pod_metadata parameter
k8s_agent_sandbox/k8s_helper.py, k8s_agent_sandbox/async_k8s_helper.py, test/unit/test_k8s_helper.py, test/unit/test_async_k8s_helper.py
Both K8sHelper and AsyncK8sHelper.create_sandbox_claim add an optional pod_metadata parameter; when provided, the manifest spec is extended with additionalPodMetadata. Tests verify the key is included when set and omitted otherwise.
SandboxClient and AsyncSandboxClient: public API and client-side wiring
k8s_agent_sandbox/sandbox_client.py, k8s_agent_sandbox/async_sandbox_client.py, test/unit/test_sandboxclient.py, test/unit/test_async_sandboxclient.py
create_sandbox gains pod_labels and pod_annotations keyword-only parameters. Clients validate pod labels via the new validation module, assemble pod_metadata using build_pod_metadata, and forward it through _create_claim to the K8s helper. Tests cover pass-through, None defaults, and invalid label key rejection. Label-validation logic moved from class methods to the shared pod_metadata module.
README: Labels and Pod Metadata
README.md
New section 7 documents labels, pod_labels, and pod_annotations parameters, their target Kubernetes resources (SandboxClaim vs. running Pod), Kubernetes label validation rules enforced client-side, async client parity, controller rejection behavior for conflicting pod labels/annotations, and differences between client-side validation and server-side enforcement. Fixed unclosed code fence in production mode example.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

lgtm, size/M, approved

Suggested reviewers

  • janetkuo

Poem

🐇 Hop hop! A label on every Pod,
Annotations flow through the claim's facade,
pod_labels validated, spec neatly set,
The sandbox knows just who it has met.
Now tenants rejoice with their IDs in place —
A rabbit tidied the metadata space! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(python): expose additionalPodMetadata in sandbox client' accurately summarizes the main change: exposing the ability to set additionalPodMetadata through the Python sandbox client.
Description check ✅ Passed The PR description is comprehensive and follows the template with clear sections: what the PR does, related issues, testing details, and a release note. It explains the motivation, implementation details, and validation approach.
Linked Issues check ✅ Passed The PR directly addresses issue #914 by implementing support for caller-supplied labels and annotations on Sandbox Pods via new pod_labels and pod_annotations parameters that flow through to spec.additionalPodMetadata.
Out of Scope Changes check ✅ Passed All changes are within scope: the core client parameter additions, helper function refactoring into pod_metadata.py, comprehensive unit test coverage, and README documentation are all necessary to implement the requested feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Jun 14, 2026
@janetkuo janetkuo added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Jun 18, 2026
@janetkuo janetkuo requested a review from Copilot June 19, 2026 20:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds first-class Python SDK support for setting SandboxClaim.spec.additionalPodMetadata so callers can stamp labels/annotations onto the Sandbox Pod (not just the claim object). This aligns the Python client with existing CRD/controller capabilities and enables in-sandbox identity/tenant signaling via the Kubernetes Downward API.

Changes:

  • Add pod_labels and pod_annotations keyword-only parameters to SandboxClient.create_sandbox and AsyncSandboxClient.create_sandbox, threading them through to claim creation.
  • Extend sync/async Kubernetes manifest builders to emit spec.additionalPodMetadata only when provided.
  • Add/adjust unit tests and update Python client README to document the claim-labels vs pod-metadata distinction.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
clients/python/agentic-sandbox-client/README.md Documents claim-level labels vs pod-level pod_labels / pod_annotations with an example.
clients/python/agentic-sandbox-client/k8s_agent_sandbox/sandbox_client.py Adds new create_sandbox kwargs, builds additionalPodMetadata payload, validates pod_labels, and passes through to claim creation.
clients/python/agentic-sandbox-client/k8s_agent_sandbox/async_sandbox_client.py Async parity for new kwargs and pod-metadata threading/validation behavior.
clients/python/agentic-sandbox-client/k8s_agent_sandbox/k8s_helper.py Adds optional pod_metadata to create_sandbox_claim and emits spec.additionalPodMetadata when set.
clients/python/agentic-sandbox-client/k8s_agent_sandbox/async_k8s_helper.py Async parity for manifest emission of spec.additionalPodMetadata.
clients/python/agentic-sandbox-client/k8s_agent_sandbox/test/unit/test_sandboxclient.py Updates signature expectations and adds coverage for pod metadata plumbing + invalid pod-label validation.
clients/python/agentic-sandbox-client/k8s_agent_sandbox/test/unit/test_async_sandboxclient.py Async client tests for pod metadata plumbing + invalid pod-label validation.
clients/python/agentic-sandbox-client/k8s_agent_sandbox/test/unit/test_k8s_helper.py Tests that additionalPodMetadata is included/omitted appropriately in sync manifests.
clients/python/agentic-sandbox-client/k8s_agent_sandbox/test/unit/test_async_k8s_helper.py Tests that additionalPodMetadata is included/omitted appropriately in async manifests.

@igooch igooch 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.

Overall looks good to me, a few non-blocking comments in-line.

SandboxClient._validate_label_name(value, f"value '{value}' for key '{key}'")

@classmethod
def _build_pod_metadata(cls, pod_labels: dict[str, str] | None, pod_annotations: dict[str, str] | None) -> dict | None:

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.

Nit (non-blocking): _build_pod_metadata is duplicated in async_sandbox_client.py (identical body/docstring; the signature is just wrapped across multiple lines). It and _validate_labels (plus its _validate_label_name/regex-constant helpers) could move to utils.py to de-dup across both clients.

of *now + shutdown_after_seconds* (UTC) and a ``shutdownPolicy``
of ``"Delete"``, so the controller auto-deletes the claim on
expiry. Must be a positive integer.
pod_labels: Optional labels stamped onto the running Sandbox **Pod**

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.

"readable from inside the sandbox through the Downward API" is accurate, but it's worth noting that client-side validation only checks label syntax (RFC-1123) — it does not replicate the controller's domain allow-list or system-label restrictions, so a client-valid pod_labels value can still be rejected server-side.

A half-sentence clarifying that client validation is fail-fast (not a guarantee of admission) would help. Related, a system-reserved key isn't rejected — it's silently dropped when the Pod is rendered (only a V(1) debug log, no error), so such a key would simply be absent on the Pod with no signal in the client.

asyncio.run(main())
```

### 7. Labels and Pod Metadata

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.

Optional: a couple of behavioral caveats worth a line here.

  • A pod_label/pod_annotation whose key is already defined on the warmpool template with a different value is rejected by the controller's "No Overrides" rule, so the reconcile errors — there's no client-side signal for this, so a caller may see a stuck claim with the reason only in controller logs.

  • Once pod_labels/pod_annotations are set, the controller treats a template-fetch failure as fatal (reconcile retried) rather than a silent best-effort skip, and may issue an extra Sandbox update on the first reconcile after adoption.

Neither is a problem, just non-obvious when debugging.

Move the shared label-validation and pod-metadata assembly out of both
SandboxClient and AsyncSandboxClient into a single pod_metadata module so the
sync and async clients no longer carry identical copies of _build_pod_metadata,
_validate_labels, and _validate_label_name. Behavior and error messages are
unchanged; the migrated unit tests call the module-level functions.

Document that client-side validation only checks RFC-1123 label syntax and does
not replicate the controller's domain allow-list or system-label restrictions,
both in the build_pod_metadata docstring and the README. Add a README note that
a pod_label/pod_annotation whose key already exists on the warmpool template
with a different value is rejected by the controller's No Overrides rule.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
@mvanhorn

Copy link
Copy Markdown
Contributor Author

Thanks @igooch, all three addressed in 127dfbb:

  • Pulled _build_pod_metadata, _validate_labels, and _validate_label_name (plus the regex/length constants) out of both clients into a new pod_metadata module, so the sync and async clients share one copy. Behavior and error messages are unchanged; the unit tests now call the module-level functions.
  • Added a note that client-side validation only checks RFC-1123 label syntax and does not replicate the controller's domain allow-list or system-label restrictions, in both the build_pod_metadata docstring and the README.
  • Added the README caveat about the No Overrides rule: a pod_label/pod_annotation whose key already exists on the warmpool template with a different value is rejected by the controller.

Appreciate the careful review.

@igooch igooch 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.

/lgtm

@kubernetes-prow kubernetes-prow Bot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jun 23, 2026
@kubernetes-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: igooch, mvanhorn

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubernetes-prow kubernetes-prow Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 23, 2026
@kubernetes-prow kubernetes-prow Bot merged commit 52d1f97 into kubernetes-sigs:main Jun 23, 2026
11 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Agent Sandbox Jun 23, 2026
alexatakvelon pushed a commit to volatilemolotov/agent-sandbox that referenced this pull request Jun 24, 2026
…tes-sigs#979)

* feat(python): expose additionalPodMetadata in sandbox client

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>

* address review: dedup pod-metadata helpers, document validation scope

Move the shared label-validation and pod-metadata assembly out of both
SandboxClient and AsyncSandboxClient into a single pod_metadata module so the
sync and async clients no longer carry identical copies of _build_pod_metadata,
_validate_labels, and _validate_label_name. Behavior and error messages are
unchanged; the migrated unit tests call the module-level functions.

Document that client-side validation only checks RFC-1123 label syntax and does
not replicate the controller's domain allow-list or system-label restrictions,
both in the build_pod_metadata docstring and the README. Add a README note that
a pod_label/pod_annotation whose key already exists on the warmpool template
with a different value is rejected by the controller's No Overrides rule.

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>

---------

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. ready-for-review size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

5 participants