Repository navigation
Add a Kubernetes container provider - #1254
Conversation
KubernetesProvider was a placeholder that could not be instantiated, while the runtime docs still described it as planned. A trainer running in the same cluster needs the same ContainerProvider contract as local Docker: one environment, one Pod and one Service, and an in-cluster HTTP URL. The kubernetes client is an optional extra, imported only when the provider is constructed. Readiness polls /health, as LocalDockerProvider does. The SDK sits behind a private adapter so tests can mock it, as ModalProvider does. A failed start deletes only the objects this instance created, and stop and close are safe to call twice. The Service URL and raw API bodies are left out of errors.
k21993
left a comment
There was a problem hiding this comment.
Thanks for putting this together. The overall design matches the discussion on the issue. A few things I think should be fixed before this leaves draft:
-
ensure_namespace()callsread_namespace, which requires cluster-level permission. A trainer with only namespace-level Pod and Service permissions will fail before creating anything. Please remove this preflight. Also handle a 404 from Pod or Service creation as "namespace does not exist" so that error does not become the generic "Failed to create Pod" message. -
A Pod or Service can be created even if the client loses the API response. In that case, the current flags stay false, cleanup skips the resource, and
_nameis cleared. For a non-409 create error, please check the generated name, verify the OpenEnv ownership labels, and try to delete it. If cleanup cannot be confirmed, keep the name and state sostop_container()can retry. -
The returned URL hardcodes
.svc.cluster.local. Kubernetes clusters can use a different cluster domain. Since this provider is only for in-cluster use,service.namespaceis the simplest portable address. A configurable cluster domain would also work. -
Separate non-blocking note:
proxies={"http": None, "https": None}still allowsALL_PROXY. The same pattern already exists inLocalDockerProviderandDockerSwarmProvider, so I do not think this PR should own that fix. It should be tracked separately for all three providers.
The namespace preflight called a cluster-scoped API, so a trainer with
only a namespace Role could not start. A 404 from Pod or Service create
now reports that the namespace does not exist.
A lost create response used to clear the name while leaving the object
in the cluster. Cleanup now reads that name and deletes it only when
the OpenEnv ownership labels match. A 409 is left untouched. If the
read or delete cannot be confirmed, the name stays so stop_container()
can retry.
The Service URL is the short name http://{name}.{namespace}:{port}.
Pod DNS search completes the cluster domain, so .svc.cluster.local is
not hardcoded.
|
Thanks for the review. The three points are in 74d80ba:
The PR is still a draft. |
|
Thanks for the update @rohitrsh . I reviewed The original three concerns are addressed, but I found one remaining cleanup issue.
There is also a sequencing issue: if Pod creation fails before Service creation is attempted, cleanup still probes the Service with I think the fix should keep ownership-unknown separate from confirmed ownership, only mark uncertainty for the resource whose create was actually attempted, and re-check labels before deleting an unconfirmed resource. If that read fails again, retain the state for a later retry. |
A failed ownership read was stored as a created flag, so stop_container() deleted that name without reading the labels again. A Pod create failure also marked the Service, which had never been created. Unknown ownership is now its own state, and only the resource whose create was attempted can enter it. stop_container() reads the labels again and deletes only on a match. A failed read keeps that state for another retry.
|
Thanks @k21993. That cleanup hole is fixed in 7eed439. A failed ownership read is now unconfirmed, not a confirmed create. Only the resource whose create was attempted can become unconfirmed. A Pod create failure does not mark the Service. |
|
Thanks, reviewed The unconfirmed ownership state is now kept separate, LGTM! |
|
Hey @k21993, this is ready for review. The workflows are waiting for approval so CI can run. |
Main now lists UVProvider separately and still described KubernetesProvider as planned. The README and runtime-providers guide keep that newer wording and record the provider as available.
…ovider. Main added HFSandboxProvider and still described KubernetesProvider as planned. The docs keep both: Hugging Face sandboxes in core, and the Kubernetes provider as the optional extra.
|
Hey @sergiopaniego, @k21993 LGTM'd the Kubernetes provider. CI is waiting on a maintainer to approve the workflows so they can run. Could you approve them? |
|
Hey @burtenshaw, this is the Kubernetes provider from the decisions you made on #1153 (#1153 (comment)). @k21993 LGTM'd it. CI is waiting on a maintainer to approve the workflows so they can run. Could you approve them, or point me at who can? |
|
Hey @burtenshaw, bumping this. CI is still waiting on a maintainer to approve the workflows. Could you approve them, or point me at who can? |
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
|
The independent readiness review is addressed in 14fbf7e: in-cluster health checks now exclude ALL_PROXY as well as HTTP/HTTPS proxies, and Kubernetes status reads use the remaining monotonic deadline with SDK retries disabled on a private configuration. HTTP polling and sleeps also respect the remaining budget; transport failures do not expose the API endpoint. The provider suite passes 65 tests, including transport-level proxy selection and bounded SDK request regressions. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 14fbf7e. Configure here.
|
Validated exact head dacf113 against a disposable Kind cluster using Kubernetes SDK 35.0.0. The provider created its Pod and Service, reached health through in-cluster DNS with a deliberately broken ALL_PROXY, and served health through a host port-forward. The child Pod had service-account token mounting disabled. Two stop calls completed successfully; readback confirmed both provider resources were deleted. The temporary cluster was also removed. Provider source SHA-256: 23ead80db8c78ee380f6051a5f75057b814d390209186b07729bf9d23e2cc4ec. |
burtenshaw
left a comment
There was a problem hiding this comment.
Reviewed the one-Pod/one-Service provider and its ownership and cleanup paths, configuration validation, lazy optional dependency, in-cluster HTTP scope, and readiness handling against the agreed #1153 design. Startup interrupts retain ownership-checked cleanup; context exit preserves the original failure; proxy inheritance, transient API retries, and deadline propagation are covered.
All 72 focused tests pass with Kubernetes SDK 29.0.0 and 35.0.0, including timeout construction through the real SDK transport. The exact-head Kind smoke passed Pod/Service creation, readiness with broken ALL_PROXY, in-cluster and forwarded health, repeat-safe resource cleanup, and cluster removal. All 13 checks pass and all review threads are resolved at dacf113. The serialized merger will refresh against main and verify the resulting head again before merging.

Summary
Replaces the
KubernetesProviderplaceholder with a realContainerProvider. A trainer running in the same cluster gets one Pod and one Service per environment, and an in-cluster HTTP URL, matching the decisions on #1153.base_urlishttp://{name}.{namespace}.svc.cluster.local:{port}. This is the in-cluster HTTP exception.ModalProviderandACASandboxProviderstill requirehttps://.pip install openenv[kubernetes]. The client is imported only when the provider is constructed, and it is not re-exported fromopenenv.core.containers.runtime.namespaceand kube config. Resources,runtimeClassName,imagePullSecrets, and labels arestart_containerkwargs.GET /healthonce the Pod isRunning, asLocalDockerProviderdoes. AFailedorSucceededPod raises immediately.stop_container()andclose()are safe to call twice. The Service URL and raw API bodies are left out of errors.app.kubernetes.io/managed-by=openenvand the instance label the Service selects on) cannot be replaced by caller labels.Closes nothing by itself. Follows #1153 and #441.
Test plan
pytest tests/test_core/test_kubernetes_provider.py(46 passed, no cluster)ruff check,ruff format, andusorton the changed filespytest tests/excluding integration, network, and docker: 3075 passed, 103 skipped. The only failure was the existingtest_start_refuses_a_port_held_by_a_non_capture_listenertimeout, which does not import this provider.kind-acroflow, namespaceopenenv: one Pod and one Service, in-clusterGET /healthreturnedok,stop_container()removed both.Note
Medium Risk
Adds optional cluster lifecycle code (create/delete Pods and Services) with RBAC and partial-failure cleanup implications, though scope is limited to labeled resources in a caller-chosen namespace and is covered by extensive unit tests.
Overview
Implements in-cluster Kubernetes runtime support by replacing the
KubernetesProviderstub with a realContainerProviderthat provisions one Pod and one Service per instance in an existing namespace (no Deployment, Ingress, or port-forward).Install via
pip install openenv[kubernetes]; the client loads only when the provider is constructed andKubernetesProvideris not re-exported fromopenenv.core.containers.runtime(import fromkubernetes_provider).start_containerreturns a plaintext in-cluster URLhttp://{name}.{namespace}:{port}for trainers running inside the cluster; readiness followsLocalDockerProvider(Running +GET /health). Failed starts and shutdown use ownership labels and conservative cleanup so only OpenEnv-managed objects are deleted; errors avoid leaking URLs and API bodies.Docs and
pyproject.tomlmark Kubernetes as a supported provider; a largetest_kubernetes_provider.pysuite covers lifecycle, cleanup edge cases, and the SDK adapter without requiring a live cluster.Reviewed by Cursor Bugbot for commit dacf113. Bugbot is set up for automated code reviews on this repo. Configure here.