pip install kplane — Python SDK for kplane virtual
control planes.
kplane gives you thousands of real Kubernetes control planes (≈3MB and ≈50ms per plane). This SDK exposes two primitives that turn that density into something useful for AI/RL workloads:
| Primitive | What it does | Why you want it |
|---|---|---|
plane.state() |
Aggregate, in-memory snapshot of every resource in a VCP | One round-trip to score an agent rollout or capture a trajectory step |
client.create_fleet(...) |
Declare N VCPs, get back an async-iterable handle | Parallel RL environments without writing a controller |
pip install kplaneRequires Python 3.10+.
import kplane
client = kplane.Client(
"https://127.0.0.1:6443",
token="…", # bearer token
verify_tls=False, # for local dev
)
# 1) snapshot a single plane (created on first access)
snap = client.plane("team-alpha").state(warm=True)
print(snap.cluster, snap.live_resources, snap.total_items())
# 2) fleet of N planes for an RL rollout
with client.ephemeral_fleet("rl-rollout", replicas=64) as fleet:
fleet.wait_until_ready(timeout=120)
for plane in fleet.planes():
score = score_state(plane.state())ephemeral_fleet deletes the Fleet object on context exit. The member VCPs
themselves are kept in V0 (intentional — see Deliberate omissions below).
Construct once per apiserver. Holds an HTTPX connection pool.
client = kplane.Client(
"https://127.0.0.1:6443",
token="…",
verify_tls=True,
)A handle to one virtual control plane. Lightweight — no I/O on construction.
plane = client.plane("team-alpha")
plane.state() # full snapshot
plane.state(resource="pods") # filter
plane.state(resource=["pods", "configmaps"], include_empty=True)
plane.state(warm=True) # force-create informers on the server
plane.is_ready() # cheap /readyz probestate() returns a Snapshot:
snap.cluster # str
snap.snapshot_time # datetime
snap.live_resources # int — count of resources with informers
snap.resources # list[SnapshotResource]
snap.by_resource("pods") # SnapshotResource | None
snap.total_items() # int — sum of all itemCountEach SnapshotResource has group, resource, synced, item_count, and
items (a list of dict because the items can be any K8s resource shape).
metadata.managedFields is stripped server-side to keep payloads tractable.
Declarative N-VCP provisioning. The apiserver runs an in-process controller that primes each member through the same bootstrap path organic traffic uses.
fleet = client.create_fleet(
name="rl-rollout",
replicas=64,
name_prefix="rl-", # optional
# names=["a", "b", ...], # OR an explicit list of cluster IDs
)
fleet.wait_until_ready(timeout=120)
fleet.status.ready_replicas # int
fleet.members # list[FleetMember]
fleet.planes() # list[Plane], one per member
fleet.refresh() # re-fetch from apiserver
fleet.delete() # delete the Fleet object (V0: not the VCPs)For one-shot RL rollouts, use the context-managed helper:
with client.ephemeral_fleet("rl-rollout", replicas=10) as fleet:
fleet.wait_until_ready()
...
# Fleet deleted automatically on exit, even on exception.The SDK is generated against a single source-of-truth spec, also embedded in this package:
from kplane.openapi import spec_yaml, load_spec
print(spec_yaml()) # raw YAML
spec = load_spec() # requires `pip install kplane[yaml]`
print(spec["info"]["version"])The same document is served live by the apiserver at:
GET /openapi/kplane.yamlGET /openapi/kplane.json
These endpoints are server-scoped (not per-VCP) and intentionally unauthenticated so SDK generators and CI can fetch them anonymously.
These are not shipped yet. Each is a known follow-up rather than a missed feature.
- No member-VCP garbage collection on Fleet delete. Deleting a
Fleetremoves the object; the VCPs that were primed by it stay live. This avoids surprise data loss while we get the cleanup semantics right (TTLs, finalizers, scenario isolation). - No scenario seeding. A Fleet today provisions empty planes. We expect
users to push manifests with the standard
kubectl/ client-go path in V0 and will move common seeding patterns (Helm chart, kustomize, manifest bundle) into the spec once we see how people use it. - No state delta / streaming snapshots.
state()returns a full snapshot. The apiserver has the bits to stream deltas (thesdeccodec is already there) but the wire format is not finalised. - No async client. Sync only. The
httpx.AsyncClientbased variant is trivial to add but waiting on user demand before locking the surface. - No
kubectl-style raw resource access. Use the officialkubernetesPython client againstclient.cluster_base_url(cid)for now; we'll either re-export it or wrap the common operations once the surface area is well understood.
pip install -e ".[dev,yaml]"
ruff check src tests
mypy src
pytest -vSmoke tests against a real apiserver:
export KPLANE_HOST=https://127.0.0.1:6443
export KPLANE_TOKEN=$(cat /tmp/kplane-token)
pytest tests/test_smoke.py -vApache-2.0. See LICENSE.