k8s
Cloud functionality
The k8s module is available in kite (all-in-one) and kitecloud. It is not available in kitecmd or kiteai. See Infrastructure.
The k8s module provides full Kubernetes resource management — CRUD, high-level workloads, watches, logs, exec, port-forward, node operations, metrics, controllers, admission webhooks, and typed object constructors.
All functions that perform I/O accept a timeout kwarg (duration string, e.g., "30s", "5m"). Most take an optional namespace kwarg; when omitted, the client's default namespace is used.
Quick reference¶
| Category | Functions |
|---|---|
| CRUD & Inspection | get, list, create, apply, delete, patch, label, annotate, status, event, events, validate, claims, pvcs, pvs, storage_classes |
| Conditions & Finalizers | k8s.condition.*, k8s.finalizer.*, k8s.is_deleting |
| Watch & wait | watch, wait_for |
| High-level workloads | deploy, run, expose, route, scale, autoscale, rollout, resize, set_image, set_env, set_resources |
| Logs, exec, port-forward, copy | logs, logs_follow, exec, debug, port_forward, cp |
| Describe | describe |
| Node operations | drain, cordon, uncordon, taint, untaint |
| Metrics | top_nodes, top_pods |
| Context helpers | context, namespace_name, version, api_resources |
| Controllers | control |
| Webhooks | webhook |
| Object constructors & utils | k8s.obj.*, k8s.yaml, k8s.config |
CRUD¶
| Function | Returns | Description |
|---|---|---|
k8s.get(kind, name, namespace="", timeout="") |
AttrDict |
Get a single resource |
k8s.list(kind, namespace="", labels="", fields="", timeout="") |
list[AttrDict] |
List resources with optional label/field selectors |
k8s.create(manifest, namespace="", dry_run=False, timeout="") |
AttrDict |
Create a resource from a manifest (dict, AttrDict, or YAML string) |
k8s.apply(manifest, namespace="", field_manager="starkite", dry_run=False, force=False, prune=False, prune_labels={}, timeout="") |
AttrDict |
Apply a resource (server-side apply). When prune=True, prunes omitted resources managed by field_manager matching prune_labels |
k8s.diff(manifest, namespace="", field_manager="starkite", timeout="") |
AttrDict |
Server-side apply dry-run diff against live cluster state (has_drift, drifted_fields, conflicts, diff, live, applied) |
k8s.evict(name, namespace="", dry_run=False, timeout="") |
AttrDict |
Evict a Pod via /eviction subresource, honoring PodDisruptionBudgets |
k8s.delete(kind, name, namespace="", propagation="Background", timeout="") |
None |
Delete a resource |
k8s.patch(kind, name, patch, namespace="", type="merge", timeout="") |
AttrDict |
Patch a resource. type: "merge", "strategic", or "json" |
k8s.label(kind, name, labels, namespace="", timeout="") |
AttrDict |
Set labels on a resource |
k8s.annotate(kind, name, annotations, namespace="", timeout="") |
AttrDict |
Set annotations on a resource |
k8s.status(obj, status, namespace="", timeout="") |
AttrDict |
Update the status subresource of a resource. Pass the resource as obj and the new status dict as status |
k8s.event(obj, reason, message, type="Normal", namespace="", timeout="") |
AttrDict |
Emit a Kubernetes event attached to obj. type can be "Normal" or "Warning" |
k8s.events(target=None, kind="", name="", namespace="", type="", reason="", since="", limit=0, timeout="") |
list[AttrDict] |
Query diagnostic event streams with dual-API normalization (events.k8s.io/v1 and core/v1). Filter by target object, kind, name, type ("Warning"/"Normal"), reason substring, or relative duration (since="30m"). Returns items sorted newest first with relative age |
k8s.validate(manifest, policy=None, expression="", strict=False) |
AttrDict |
Evaluate CEL validation policies or expressions against a manifest client-side prior to cluster submission. Returns {"valid": bool, "violations": list[str], "policy": str} |
k8s.claims(namespace="", labels="", timeout="") |
list[AttrDict] |
List resource.k8s.io/v1 ResourceClaim objects for Dynamic Resource Allocation (DRA) |
k8s.pvcs(namespace="", labels="", timeout="") |
list[AttrDict] |
List PersistentVolumeClaim objects |
k8s.pvs(labels="", timeout="") |
list[AttrDict] |
List cluster-scoped PersistentVolume objects |
k8s.storage_classes(labels="", timeout="") |
list[AttrDict] |
List cluster-scoped StorageClass objects |
Example — status subresource update¶
# Update the .status of a custom resource
obj = k8s.get("myapp", "demo", namespace="default")
k8s.status(obj, {"ready": True, "message": "initialized"}, namespace="default")
Example — emitting a Kubernetes event¶
deploy = k8s.get("deployment", "web", namespace="default")
k8s.event(deploy, reason="DriftCorrected", message="Replicas scaled down to policy limit", type="Normal")
Example — querying diagnostic events¶
# Query recent Warning events for a pod within the past 30 minutes
events = k8s.events(
kind = "Pod",
name = "llm-inference-76b9f47b-x8q2z",
namespace = "production",
type = "Warning",
since = "30m",
)
for ev in events:
print(ev.age, ev.type, ev.reason, ev.message, ev.count)
# Query events by binding directly to an object
pod = k8s.get("pod", "llm-inference-76b9f47b-x8q2z", namespace="production")
for ev in k8s.events(pod, since="1h"):
print(ev.last_time, ev.reason, ev.regarding.name)
Example — client-side admission validation via CEL¶
# Evaluate a manifest against a CEL expression prior to applying
pod = k8s.obj.pod(name="test-pod", containers=[k8s.obj.container(name="app", image="alpine")])
result = k8s.validate(pod, expression="!object.spec.containers.exists(c, c.securityContext.?privileged.orValue(false))")
if not result.valid:
print("Policy violations:", result.violations)
Conditions and Finalizers¶
| Function | Returns | Description |
|---|---|---|
k8s.condition.get(obj, type) |
AttrDict or None |
Query a condition by type (e.g., "Ready") from obj.status.conditions |
k8s.condition.set(obj, type, status, reason="", message="", timeout="") |
AttrDict |
Set or update a condition in obj.status.conditions and persist it via status subresource |
k8s.finalizer.has(obj, name) |
bool |
Check if name is present in obj.metadata.finalizers |
k8s.finalizer.add(obj, name, timeout="") |
AttrDict |
Add a finalizer string to obj.metadata.finalizers via merge patch |
k8s.finalizer.remove(obj, name, timeout="") |
AttrDict |
Remove a finalizer string from obj.metadata.finalizers via merge patch |
k8s.is_deleting(obj) |
bool |
Check if obj.metadata.deletionTimestamp is set |
Watch and wait¶
| Function | Returns | Description |
|---|---|---|
k8s.watch(kind, namespace="", labels="", timeout="", handler=None) |
list[AttrDict] or None |
Watch a resource kind. If handler is supplied, call it per event (handler(event_type, obj)) and return None; otherwise collect events and return a list of {"type": ..., "object": ...} AttrDicts. timeout caps wall-clock duration |
k8s.wait_for(kind, name, condition="", namespace="", timeout="") |
AttrDict |
Block until the named resource meets the given condition (e.g., "Available", "Ready", "Complete") or the timeout expires. Returns {"ready": bool, "resource": AttrDict, "message": str} |
Example — watch deployments in a namespace¶
def on_event(event_type, obj):
printf("%s: %s\n", event_type, obj.metadata.name)
k8s.watch("deployment", namespace="default", timeout="30s", handler=on_event)
Example — wait for rollout¶
result = k8s.wait_for("deployment", "web", condition="Available",
namespace="default", timeout="5m")
if result.ready:
print("Deployment is ready")
High-level workloads¶
Deploy and run¶
| Function | Returns | Description |
|---|---|---|
k8s.deploy(name, image, replicas=1, port=0, namespace="", labels=None, env=None, timeout="") |
AttrDict |
Create a Deployment (returns {"deployment": str, "service": str}) |
k8s.run(name, image, command=None, namespace="", restart="Never", rm=False, timeout="3m") |
AttrDict |
Run a one-off Pod (like kubectl run) |
k8s.expose(kind, name, port, target_port=0, type="ClusterIP", namespace="", timeout="") |
AttrDict |
Expose a resource as a Service |
k8s.route(name, gateway, service, port, prefix="/", host="", namespace="", timeout="") |
AttrDict |
Create or update an HTTPRoute binding a Gateway to a Service backend (returns {"route": str}) |
Scale and rollout¶
| Function | Returns | Description |
|---|---|---|
k8s.scale(kind, name, replicas, namespace="", timeout="") |
AttrDict |
Scale a resource to the given replica count |
k8s.autoscale(kind, name, min=1, max=10, cpu_percent=80, namespace="", timeout="") |
AttrDict |
Create a HorizontalPodAutoscaler |
k8s.rollout(kind, name, action="status", namespace="", timeout="") |
AttrDict |
Manage rollouts. action: "status", "restart", "pause", "resume" |
k8s.resize(name, container, cpu=None, memory=None, requests=None, limits=None, namespace="", timeout="30s") |
AttrDict |
Resize CPU and memory allocations of a running container in-place without pod restart |
Configuration¶
| Function | Returns | Description |
|---|---|---|
k8s.set_image(kind, name, container, image, namespace="", timeout="") |
AttrDict |
Update the container image of a resource |
k8s.set_env(kind, name, env, namespace="", container="", timeout="") |
AttrDict |
Set environment variables on a resource |
k8s.set_resources(kind, name, requests=None, limits=None, namespace="", container="", timeout="") |
AttrDict |
Set resource requests and limits |
Logs, exec, port-forward, copy¶
| Function | Returns | Description |
|---|---|---|
k8s.logs(name, namespace="", container="", tail=0, since="", previous=False, timeout="") |
string |
Fetch pod logs. tail caps line count; since is a duration string (e.g., "10m"); previous=True reads the previous container instance |
k8s.logs_follow(name, handler, namespace="", container="", tail=0, timeout="") |
None |
Stream logs, calling handler(line) per line. Blocks until the pod ends or timeout elapses |
k8s.exec(name, command, namespace="", container="", timeout="") |
AttrDict |
Run a command in a pod. command may be a string (executed via /bin/sh -c) or a list (argv). Returns {"stdout", "stderr", "code"} |
k8s.debug(name, image="nicolaka/netshoot", target_container="", command=None, namespace="", timeout="3m") |
AttrDict |
Attach an ephemeral container to a running pod for zero-shell diagnostics. Returns {"stdout", "stderr", "code", "container", "pod"} |
k8s.port_forward(name, port, local_port=0, namespace="") |
PortForwardHandle |
Forward a local port to a pod port. Blocks until interrupted. local_port=0 picks a free port |
k8s.cp(pod, src, dst, namespace="", container="", timeout="") |
AttrDict |
Copy files to/from a pod. Use pod:path as src to download, pod:path as dst to upload |
Example — tail logs¶
def handle_line(line):
if "ERROR" in line:
printf("!! %s\n", line)
k8s.logs_follow("web-abc123", handle_line, namespace="default", tail=100)
Example — exec into a pod¶
Example — zero-shell diagnostics with ephemeral containers¶
# Attach an ephemeral debug container to a distroless pod
result = k8s.debug(
"auth-service-xyz",
image = "nicolaka/netshoot",
target_container = "auth",
command = ["tcpdump", "-i", "any", "-c", "5", "port", "8080"],
namespace = "default",
)
print("Exit code:", result.code)
print(result.stdout)
Example — copy a file out of a pod¶
Describe¶
| Function | Returns | Description |
|---|---|---|
k8s.describe(kind, name, namespace="", timeout="") |
AttrDict |
Return detailed description of a resource, with .resource, .conditions, and .events |
info = k8s.describe("pod", "web-abc123", namespace="default")
print("Pod Phase:", info.resource.status.phase)
Node operations¶
| Function | Returns | Description |
|---|---|---|
k8s.drain(node, force=False, ignore_daemonsets=False, timeout="") |
AttrDict |
Drain a node (evict pods). force continues past pods not backed by a controller; ignore_daemonsets leaves DaemonSet pods in place |
k8s.cordon(node, timeout="") |
AttrDict |
Mark a node unschedulable |
k8s.uncordon(node, timeout="") |
AttrDict |
Re-enable scheduling on a node |
k8s.taint(node, key, value="", effect="", timeout="") |
AttrDict |
Add a taint to a node. effect: "NoSchedule", "PreferNoSchedule", or "NoExecute" |
k8s.untaint(node, key, timeout="") |
AttrDict |
Remove a taint from a node by key |
Example — roll a node¶
k8s.cordon("node-01")
k8s.drain("node-01", ignore_daemonsets=True, timeout="5m")
# ... maintenance ...
k8s.uncordon("node-01")
Metrics¶
Requires metrics-server running in the cluster.
| Function | Returns | Description |
|---|---|---|
k8s.top_nodes(timeout="") |
list[AttrDict] |
CPU/memory capacity and allocatable per node |
k8s.top_pods(namespace="", sort_by="", timeout="") |
list[AttrDict] |
Resource requests and status per pod |
Example¶
for pod in k8s.top_pods(namespace="default", sort_by="cpu"):
printf("%s cpu=%s mem=%s\n",
pod.name, pod.cpu_request, pod.memory_request)
Context helpers¶
| Function | Returns | Description |
|---|---|---|
k8s.context() |
string |
Current kubeconfig context name |
k8s.namespace_name() |
string |
Default namespace for the current context |
k8s.version(timeout="") |
AttrDict |
Kubernetes server version info (.major, .minor, .git_version, .platform) |
k8s.api_resources(timeout="") |
list[AttrDict] |
Available API resources |
print("context:", k8s.context())
print("default namespace:", k8s.namespace_name())
print("server version:", k8s.version().git_version)
Controllers¶
k8s.control() runs an active reconciliation loop over a resource kind. It is the Starkite equivalent of writing a controller in controller-runtime — you supply handlers, while the runtime manages the informers, workqueue, self-echo loop suppression, status conditions, event emission, child resource lifecycle, health endpoints, and distributed leader election.
k8s.control(
kind,
reconcile = None,
finalize = None,
on_create = None,
on_update = None,
on_delete = None,
namespace = "",
labels = "",
field_selector = "",
resync = "",
poll = "",
finalizer = "",
health_port = 0,
workers = 1,
max_retries = 5,
backoff = "5s",
generation_changed = True,
watch_owned = [],
watch_related = [],
predicate = None,
leader_election = False,
leader_election_id = "",
leader_election_namespace = "",
identity = "",
)
| Kwarg | Type | Default | Description |
|---|---|---|---|
kind |
string | required (positional) | Resource kind to watch |
reconcile |
callable | — | Primary reconcile handler: fn(obj) or fn(event, obj). Can return None, a requeue duration string (e.g., "10s"), or a list[KubeResource] of desired child resources |
finalize |
callable | — | Declarative teardown handler: fn(obj). Executed when metadata.deletionTimestamp is set before the finalizer is stripped |
finalizer |
string | auto | Custom finalizer string. Injected on creation and removed after finalize() completes cleanly |
on_create / on_update / on_delete |
callable | — | Granular per-event handlers (optional alternative to reconcile) |
namespace |
string | default | Scope the controller to a namespace (or cluster-wide for cluster-scoped resources) |
labels |
string | — | Label selector (e.g., "app=web") |
field_selector |
string | — | Field selector (e.g., "metadata.name=my-resource") |
resync |
duration string | — | Informer resync interval (e.g., "10m") |
poll |
duration string | — | Periodic background reconciliation interval (e.g., "30s") |
health_port |
int | 0 |
Port for embedded HTTP health server exposing /healthz and /readyz |
generation_changed |
bool | True |
Filter out metadata/status-only updates that did not change .metadata.generation |
workers |
int | 1 |
Number of concurrent workqueue workers |
max_retries |
int | 5 |
Retry cap per item on error before dropping |
backoff |
duration string | "5s" |
Base retry backoff |
watch_owned |
list[string] | — | Owned resource kinds to watch whose ownerReferences point to the primary kind |
watch_related |
list | — | Secondary resource mappings: [{"kind": "secrets", "map_func": fn(sec) -> ["ns/name"]}] or [("secrets", fn)] |
predicate |
callable | — | fn(obj) -> bool filter applied before enqueueing |
leader_election |
bool | False |
Run under distributed coordination.k8s.io/v1 Lease locking |
leader_election_id |
string | <kind>-controller |
Name of the Lease resource |
leader_election_namespace |
string | controller ns | Namespace for the Lease resource |
identity |
string | <host>_<pid> |
Replica identity string for the lease lock candidate |
Blocks until interrupted (SIGINT/SIGTERM).
Substrate Automation Features¶
When using reconcile = reconcile_fn:
* Functional Child Returns: When reconcile(obj) returns a list of child resources (e.g., return [child_dep, child_svc]), the runtime automatically:
1. Injects ownerReferences pointing to the parent resource.
2. Spawns informers (auto-watch) on child kinds so child modifications trigger parent re-reconciliation.
3. Applies child resources via Server-Side Apply (fieldManager="starkite").
4. Prunes orphaned child resources when removed from the returned list.
* Status Conditions & Events: The runtime automatically updates .status.conditions (Type="Ready", Status="True", Reason="Reconciled") and emits a Normal Reconciled Kubernetes event upon successful reconciliation.
* Loop Immunity: Built-in self-echo suppression prevents updates or patches made by the controller from triggering infinite reconciliation loops.
* Dynamic Readiness Probes: Under leader election, /readyz responds with 200 OK on the active leader and 503 Service Unavailable on standby replicas until failover occurs.
Example — Operator with Child Reconciliation and Finalizer¶
def reconcile(site):
name = site.metadata.name
ns = site.metadata.namespace
replicas = site.spec.get("replicas", 1)
# Return desired child workloads; substrate manages ownership, SSA apply, and pruning
child_dep = k8s.obj.deployment(
name = name,
namespace = ns,
replicas = replicas,
containers = [k8s.obj.container(name="web", image="nginx:alpine")],
)
return [child_dep]
def finalize(site):
print("Executing teardown for %s" % site.metadata.name)
return None
k8s.control(
"staticsites",
reconcile = reconcile,
finalize = finalize,
finalizer = "tutorial.starkite.io/finalizer",
health_port = 8081,
poll = "30s",
workers = 2,
)
Admission Webhooks¶
k8s.webhook() creates an HTTPS server that handles Kubernetes admission review requests. It blocks like http.serve() and k8s.control().
| Parameter | Type | Default | Description |
|---|---|---|---|
path |
string | required (first positional) | URL path (e.g., /validate-myapp) |
validate |
function | None | fn(obj) -> {"allowed": bool, "message": str} |
mutate |
function | None | fn(obj) -> modified obj |
port |
int | 9443 | HTTPS port |
tls_cert |
string | required | Path to TLS certificate |
tls_key |
string | required | Path to TLS private key |
Validating Webhook¶
Rejects resources that don't meet criteria. Return {"allowed": True} to accept or {"allowed": False, "message": "reason"} to reject.
def validate(obj):
if obj.spec.replicas > 10:
return {"allowed": False, "message": "max 10 replicas"}
if not obj.metadata.labels.get("team"):
return {"allowed": False, "message": "team label required"}
return {"allowed": True}
k8s.webhook("/validate",
validate = validate,
port = 9443,
tls_cert = "/certs/tls.crt",
tls_key = "/certs/tls.key",
)
Mutating Webhook¶
Modifies resources before they are persisted. The object is passed as a mutable AttrDict — modify it using bracket notation and return it. Changes are automatically converted to an RFC 6902 JSON patch.
def mutate(obj):
# Both dot-access (read) and bracket-access (read/write) work
printf("Mutating: %s\n", obj.metadata.name)
# Write via bracket notation
obj["metadata"]["labels"]["managed-by"] = "starkite"
obj["metadata"]["annotations"]["mutated"] = "true"
return obj
k8s.webhook("/mutate",
mutate = mutate,
port = 9443,
tls_cert = "/certs/tls.crt",
tls_key = "/certs/tls.key",
)
Combined Webhook¶
Both validate and mutate on the same server. Validation runs first — if rejected, mutation is skipped.
k8s.webhook("/webhook",
validate = validate_fn,
mutate = mutate_fn,
port = 9443,
tls_cert = "/certs/tls.crt",
tls_key = "/certs/tls.key",
)
AttrDict Object Access¶
Objects passed to webhook handlers are AttrDicts with both dot-access and bracket-access:
# Dot-access for reading (convenient)
name = obj.metadata.name
replicas = obj.spec.replicas
labels = obj.metadata.labels
# Bracket-access for reading and writing
obj["metadata"]["labels"]["key"] = "value"
obj["spec"]["replicas"] = 3
Nested maps share the same underlying data — mutations via bracket notation on a nested AttrDict propagate to the parent object automatically.
See the webhooks guide for a full end-to-end workflow including gen-webhook-artifacts.
Object constructors¶
The k8s.obj namespace provides declarative constructors for building validated Kubernetes resource manifests and sub-objects programmatically.
Resource constructors¶
| Constructor | Returns | Description |
|---|---|---|
k8s.obj.deployment(name, replicas=1, containers=[], resource_claims=[], labels={}, annotations={}, selector={}, template=None) |
KubeResource |
Construct a Deployment manifest |
k8s.obj.service(name, ports=[], selector={}, type="ClusterIP", traffic_distribution="", labels={}, annotations={}) |
KubeResource |
Construct a Service manifest (traffic_distribution sets spec.trafficDistribution, GA 1.35) |
k8s.obj.pod_disruption_budget(name, min_available=None, max_unavailable=None, selector={}, unhealthy_pod_eviction_policy="", labels={}, annotations={}) |
KubeResource |
Construct a policy/v1 PodDisruptionBudget manifest (alias k8s.obj.pdb) |
k8s.obj.config_map(name, data={}, binary_data={}, labels={}, annotations={}) |
KubeResource |
Construct a ConfigMap manifest |
k8s.obj.secret(name, data={}, string_data={}, type="Opaque", labels={}, annotations={}) |
KubeResource |
Construct a Secret manifest |
k8s.obj.pod(name, containers=[], resource_claims=[], restart_policy="Always", host_users=True, labels={}, annotations={}) |
KubeResource |
Construct a Pod manifest |
k8s.obj.job(name, containers=[], resource_claims=[], completions=1, parallelism=1, labels={}, annotations={}) |
KubeResource |
Construct a Job manifest |
k8s.obj.cron_job(name, schedule, job_template=None, containers=[], resource_claims=[], labels={}, annotations={}) |
KubeResource |
Construct a CronJob manifest |
k8s.obj.stateful_set(name, service_name="", replicas=1, containers=[], resource_claims=[], labels={}, annotations={}) |
KubeResource |
Construct a StatefulSet manifest |
k8s.obj.daemon_set(name, containers=[], resource_claims=[], labels={}, annotations={}) |
KubeResource |
Construct a DaemonSet manifest |
k8s.obj.ingress(name, rules=[], tls=[], ingress_class_name="", labels={}, annotations={}) |
KubeResource |
Construct an Ingress manifest |
k8s.obj.persistent_volume(name, capacity={}, storage="", access_modes=[], storage_class_name="", reclaim_policy="", host_path={}, nfs={}, csi={}, labels={}, annotations={}) |
KubeResource |
Construct a PersistentVolume manifest (cluster-scoped) |
k8s.obj.persistent_volume_claim(name, access_modes=[], storage="", storage_class_name="", volume_name="", selector={}, data_source={}, data_source_ref={}, labels={}, annotations={}) |
KubeResource |
Construct a PersistentVolumeClaim manifest |
k8s.obj.storage_class(name, provisioner="", volume_binding_mode="", reclaim_policy="", allow_volume_expansion=False, parameters={}, mount_options=[], labels={}, annotations={}) |
KubeResource |
Construct a StorageClass manifest (cluster-scoped) |
k8s.obj.namespace(name, labels={}, annotations={}) |
KubeResource |
Construct a Namespace manifest |
k8s.obj.service_account(name, labels={}, annotations={}) |
KubeResource |
Construct a ServiceAccount manifest |
k8s.obj.device_class(name, selectors=[], config=[], suitable_nodes={}, labels={}, annotations={}) |
KubeResource |
Construct a resource.k8s.io/v1 DeviceClass (cluster-scoped) |
k8s.obj.resource_claim(name, device_class="", count=1, allocation_mode="", device_tolerations=[], selectors=[], devices={}, labels={}, annotations={}) |
KubeResource |
Construct a resource.k8s.io/v1 ResourceClaim |
k8s.obj.resource_claim_template(name, spec=None, claim_metadata={}, labels={}, annotations={}) |
KubeResource |
Construct a resource.k8s.io/v1 ResourceClaimTemplate |
k8s.obj.resource_slice(name, node_name="", driver="", pool={}, devices={}, labels={}, annotations={}) |
KubeResource |
Construct a resource.k8s.io/v1 ResourceSlice |
k8s.obj.crd(group, version, kind, plural, scope="Namespaced", spec={}, status={}) |
CRDResource |
Construct a CustomResourceDefinition manifest |
k8s.obj.gateway_class(name, controller_name, description="", parameters_ref={}, labels={}, annotations={}) |
KubeResource |
Construct a gateway.networking.k8s.io/v1 GatewayClass manifest (cluster-scoped) |
k8s.obj.gateway(name, gateway_class, listeners=[], addresses=[], infrastructure={}, labels={}, annotations={}) |
KubeResource |
Construct a gateway.networking.k8s.io/v1 Gateway manifest |
k8s.obj.http_route(name, parent_refs=[], hostnames=[], rules=[], labels={}, annotations={}) |
KubeResource |
Construct a gateway.networking.k8s.io/v1 HTTPRoute manifest |
k8s.obj.grpc_route(name, parent_refs=[], hostnames=[], rules=[], labels={}, annotations={}) |
KubeResource |
Construct a gateway.networking.k8s.io/v1 GRPCRoute manifest |
k8s.obj.reference_grant(name, from_refs=[], to_refs=[], labels={}, annotations={}) |
KubeResource |
Construct a gateway.networking.k8s.io/v1 ReferenceGrant manifest |
k8s.obj.validating_admission_policy(name, failure_policy="Fail", match_constraints={}, match_conditions=[], validations=[], audit_annotations=[], variables=[], param_kind={}, labels={}, annotations={}) |
KubeResource |
Construct an admissionregistration.k8s.io/v1 ValidatingAdmissionPolicy manifest (cluster-scoped, alias k8s.obj.vap) |
k8s.obj.validating_admission_policy_binding(name, policy_name, param_ref={}, match_resources={}, validation_actions=["Deny"], labels={}, annotations={}) |
KubeResource |
Construct an admissionregistration.k8s.io/v1 ValidatingAdmissionPolicyBinding manifest (cluster-scoped) |
k8s.obj.mutating_admission_policy(name, failure_policy="Fail", match_constraints={}, match_conditions=[], mutations=[], reinvocation_policy="", variables=[], param_kind={}, labels={}, annotations={}) |
KubeResource |
Construct an admissionregistration.k8s.io/v1alpha1 MutatingAdmissionPolicy manifest (cluster-scoped, alias k8s.obj.map) |
k8s.obj.mutating_admission_policy_binding(name, policy_name, param_ref={}, match_resources={}, labels={}, annotations={}) |
KubeResource |
Construct an admissionregistration.k8s.io/v1alpha1 MutatingAdmissionPolicyBinding manifest (cluster-scoped) |
Sub-object constructors¶
| Constructor | Returns | Description |
|---|---|---|
k8s.obj.container(name, image, ports=[], env=[], command=[], args=[], volume_mounts=[], resources=None, claims=[], resize_policy=[], restart_policy="", liveness_probe=None, readiness_probe=None) |
KubeResource |
Container specification |
k8s.obj.container_port(container_port, name="", protocol="TCP", host_port=0) |
KubeResource |
Container port definition |
k8s.obj.service_port(port, target_port=0, name="", protocol="TCP", node_port=0) |
KubeResource |
Service port definition |
k8s.obj.env_var(name, value="", value_from={}) |
KubeResource |
Environment variable definition |
k8s.obj.env_from(config_map_ref={}, secret_ref={}, prefix="") |
KubeResource |
Environment variable source from ConfigMap or Secret |
k8s.obj.resource_requirements(requests={}, limits={}, claims=[]) |
KubeResource |
Resource requests, limits, and claim bindings |
k8s.obj.probe(http_get={}, tcp_socket={}, exec={}, initial_delay_seconds=0, period_seconds=10, timeout_seconds=1, failure_threshold=3) |
KubeResource |
Health probe configuration |
k8s.obj.security_context(run_as_user=0, run_as_group=0, run_as_non_root=False, read_only_root_filesystem=False, privileged=False, allow_privilege_escalation=False, capabilities={}, se_linux_options={}, seccomp_profile={}, apparmor_profile={}, fs_group=0, fs_group_change_policy="", windows_options={}) |
KubeResource |
Pod or container security context |
k8s.obj.volume(name, pvc=None, claim_name="", config_map=None, secret=None, empty_dir=None, ephemeral=None, host_path={}, nfs={}, csi={}, projected={}, downward_api={}) |
KubeResource |
Pod volume definition with ergonomic shortcuts for PVCs, ConfigMaps, Secrets, EmptyDir, and Ephemeral volumes |
k8s.obj.volume_mount(name, mount_path, sub_path="", sub_path_expr="", read_only=False, mount_propagation="", recursive_read_only="") |
KubeResource |
Container volume mount specification |
Example — construct and apply workload¶
# Construct Deployment and Service with typed constructors
dep = k8s.obj.deployment(
name = "web",
replicas = 3,
labels = {"app": "web", "tier": "frontend"},
containers = [
k8s.obj.container(
name = "nginx",
image = "nginx:1.27",
ports = [k8s.obj.container_port(container_port=80, name="http")],
env = [
k8s.obj.env_var(name="PORT", value="80"),
k8s.obj.env_var(name="ENV", value="production"),
],
readiness_probe = k8s.obj.probe(http_get={"path": "/", "port": 80}, initial_delay_seconds=5),
),
],
)
svc = k8s.obj.service(
name = "web",
labels = {"app": "web"},
selector = {"app": "web"},
ports = [
k8s.obj.service_port(port=80, target_port=80, name="http"),
],
)
# Apply directly using Server-Side Apply
k8s.apply([dep, svc], namespace="default")
k8s.obj.crd()¶
Constructs a CustomResourceDefinition manifest.
| Parameter | Type | Default | Description |
|---|---|---|---|
group |
string |
required | API group for the CRD (e.g., "example.io") |
version |
string |
required | API version (e.g., "v1", "v1alpha1") |
kind |
string |
required | Resource kind in PascalCase (e.g., "MyApp") |
plural |
string |
required | Plural name used in API paths (e.g., "myapps") |
scope |
string |
"Namespaced" |
"Namespaced" or "Cluster" |
spec |
dict |
{} |
Schema fields for the spec section. Each key maps to {"type": "<type>"} with optional "required" and "default" |
status |
dict |
{} |
Schema fields for the status subresource, same format as spec |
Spec and status schema format:
Each field is a dict entry where the key is the field name and the value describes the type and constraints:
{
"fieldName": {"type": "string"}, # simple field
"replicas": {"type": "integer", "default": 1}, # with default
"image": {"type": "string", "required": True}, # required field
"ready": {"type": "boolean"}, # boolean field
}
Supported types: "string", "integer", "boolean", "number", "array", "object".
Example — define and apply a CRD:
crd = k8s.obj.crd(
group = "example.io",
version = "v1",
kind = "MyApp",
plural = "myapps",
scope = "Namespaced",
spec = {
"image": {"type": "string", "required": True},
"replicas": {"type": "integer", "default": 1},
},
status = {
"ready": {"type": "boolean"},
"message": {"type": "string"},
},
)
# Apply the CRD to the cluster
k8s.apply(crd)
# Print the generated YAML for review
print(k8s.yaml(crd))
Example — Dynamic Resource Allocation (DRA)¶
Dynamic Resource Allocation (resource.k8s.io/v1) allocates accelerators, GPUs, and hardware devices dynamically via scheduler-evaluated claims:
# 1. Define cluster-level device class
gpu_class = k8s.obj.device_class(
name = "gpu.nvidia.com",
selectors = [
{"cel": {"expression": "device.capacity.memory >= 40Gi"}}
],
)
k8s.apply(gpu_class)
# 2. Define resource claim requesting hardware allocation
claim = k8s.obj.resource_claim(
name = "ml-gpu-claim",
device_class = "gpu.nvidia.com",
count = 1,
device_tolerations = [
{"key": "gpu.nvidia.com/mig", "operator": "Exists"}
],
)
k8s.apply(claim)
# 3. Attach claim to deployment and container
workload = k8s.obj.deployment(
name = "llm-inference",
replicas = 2,
resource_claims = [{"name": "gpu", "claim_name": "ml-gpu-claim"}],
containers = [
k8s.obj.container(
name = "engine",
image = "vllm/vllm-openai:latest",
claims = [{"name": "gpu"}],
)
],
)
k8s.apply(workload)
# 4. Inspect claim allocation status
claims = k8s.claims(namespace="default")
for c in claims:
print(c.metadata.name, c.status.get("allocation"))
Example — Persistent Volumes and Workload Storage¶
Starkite supports typed constructors and ergonomic workload binding for Kubernetes storage primitives:
# 1. Define cluster-level StorageClass
sc = k8s.obj.storage_class(
name = "fast-ssd",
provisioner = "kubernetes.io/no-provisioner",
volume_binding_mode = "WaitForFirstConsumer",
allow_volume_expansion = True,
)
k8s.apply(sc)
# 2. Define PersistentVolumeClaim
pvc = k8s.obj.persistent_volume_claim(
name = "db-data",
storage = "20Gi",
storage_class_name = "fast-ssd",
access_modes = ["ReadWriteOnce"],
)
k8s.apply(pvc, namespace="default")
# 3. Mount volume in a Deployment using object shortcut
workload = k8s.obj.deployment(
name = "postgres",
replicas = 1,
volumes = [
k8s.obj.volume(name="data", pvc=pvc), # direct KubeResource reference
k8s.obj.volume(name="scratch", empty_dir=True), # bool shortcut
],
containers = [
k8s.obj.container(
name = "db",
image = "postgres:16",
volume_mounts = [
k8s.obj.volume_mount(name="data", mount_path="/var/lib/postgresql/data"),
k8s.obj.volume_mount(name="scratch", mount_path="/tmp"),
],
),
],
)
k8s.apply(workload, namespace="default")
# 4. Inspect storage resources
pvcs = k8s.pvcs(namespace="default")
for p in pvcs:
print(p.metadata.name, p.status.phase, p.spec.resources.requests.storage)
Example — Hardened Pod Isolation and Resize Policy¶
Starkite supports User Namespaces (rootless isolation) and in-place container resize policies:
# 1. Define hardened Pod with rootless User Namespaces and security context
pod = k8s.obj.pod(
name = "secure-worker",
host_users = False, # Maps container root UID 0 to an unprivileged host UID (GA 1.36)
security_context = k8s.obj.security_context(
run_as_non_root = True,
apparmor_profile = {"type": "RuntimeDefault"},
seccomp_profile = {"type": "RuntimeDefault"},
),
containers = [
k8s.obj.container(
name = "worker",
image = "alpine:latest",
command = ["sleep", "3600"],
resize_policy = [
{"resource_name": "cpu", "restart_policy": "NotRequired"},
{"resource_name": "memory", "restart_policy": "NotRequired"},
],
),
],
)
k8s.apply(pod, namespace="default")
# 2. Resize the running container in-place without pod restarts
k8s.resize("secure-worker", container="worker", cpu="500m", memory="256Mi", namespace="default")
Example — Gateway API Networking¶
Gateway API (gateway.networking.k8s.io/v1) models role-oriented traffic routing decoupling gateway infrastructure from route configurations:
# 1. Define GatewayClass and Gateway
gc = k8s.obj.gateway_class(
name = "cilium",
controller_name = "io.cilium/gateway-controller",
description = "Cilium GatewayClass",
)
k8s.apply(gc)
gw = k8s.obj.gateway(
name = "prod-gw",
gateway_class = gc, # unwraps KubeResource name
listeners = [
{
"name": "http",
"port": 80,
"protocol": "HTTP",
"allowed_routes": {"namespaces": {"from": "Same"}},
},
],
)
k8s.apply(gw, namespace="default")
# 2. Define HTTPRoute referencing Gateway
route = k8s.obj.http_route(
name = "api-route",
parent_refs = [gw], # unwraps to {"name": "prod-gw"}
hostnames = ["api.example.com"],
rules = [
{
"matches": [{"path": {"type": "PathPrefix", "value": "/v1"}}],
"backend_refs": [{"name": "api-service", "port": 8080}],
},
],
)
k8s.apply(route, namespace="default")
# 3. High-level routing shortcut
k8s.route("web-route", gateway="prod-gw", service="web-service", port=80, prefix="/web", namespace="default")
Example — In-Tree Admission Governance via CEL¶
ValidatingAdmissionPolicy (admissionregistration.k8s.io/v1) provides declarative admission evaluation inside the API server via CEL expressions:
# 1. Define ValidatingAdmissionPolicy
vap = k8s.obj.validating_admission_policy(
name = "disallow-privileged",
match_constraints = {
"resource_rules": [{
"api_groups": [""],
"api_versions": ["v1"],
"resources": ["pods"],
"operations": ["CREATE", "UPDATE"],
}],
},
validations = [
{
"expression": "!object.spec.containers.exists(c, c.securityContext.?privileged.orValue(false))",
"message": "Privileged containers are prohibited",
},
],
)
k8s.apply(vap)
# 2. Bind policy with actions
binding = k8s.obj.validating_admission_policy_binding(
name = "disallow-privileged-binding",
policy_name = vap, # unwraps to "disallow-privileged"
validation_actions = ["Deny"],
)
k8s.apply(binding)
# 3. Pre-flight client-side evaluation prior to submission
pod = k8s.obj.pod(
name = "insecure-pod",
containers = [
k8s.obj.container(
name = "app",
image = "alpine",
security_context = k8s.obj.security_context(privileged=True),
),
],
)
res = k8s.validate(pod, policy=vap)
if not res.valid:
print("Pre-flight validation failed:", res.violations)
Utilities¶
| Function | Returns | Description |
|---|---|---|
k8s.yaml(manifest) |
string |
Render a manifest dict or KubeResource as YAML |
k8s.config(kubeconfig="", context="", namespace="") |
Client |
Return a configured Kubernetes client bound to the given kubeconfig path, context, and namespace |
Examples¶
Get and list resources¶
# Get a specific pod
pod = k8s.get("pod", "web-abc123", namespace="default")
print(pod.status.phase)
# List pods by label
pods = k8s.list("pod", namespace="default", labels="app=web")
for p in pods:
print(p.metadata.name, p.status.phase)
Create from YAML¶
Apply a manifest¶
k8s.apply({
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {"name": "app-config"},
"data": {"key": "value"},
}, namespace="default")
Deploy and expose¶
result = k8s.deploy("web", "nginx:latest", replicas=3, port=80, namespace="default",
labels={"app": "web", "tier": "frontend"},
env={"ENV": "production"},
)
printf("Deployed: %s, Service: %s\n", result.deployment, result.service)
k8s.expose("deployment", "web", port=80, type="LoadBalancer", namespace="default")
Scale and autoscale¶
k8s.scale("deployment", "web", replicas=5, namespace="default")
k8s.autoscale("deployment", "web", min=2, max=10, cpu_percent=70, namespace="default")
Rollout management¶
# Restart a deployment
k8s.rollout("deployment", "web", action="restart", namespace="default")
# Check rollout status
status = k8s.rollout("deployment", "web", action="status", namespace="default")
printf("Complete: %s, Ready: %d/%d\n", status.complete, status.ready, status.replicas)
# Pause a rollout (e.g., during debugging)
k8s.rollout("deployment", "web", action="pause", namespace="default")
# Resume a paused rollout
k8s.rollout("deployment", "web", action="resume", namespace="default")
Update container image¶
Set environment variables¶
k8s.set_env("deployment", "web", {"LOG_LEVEL": "debug", "DB_HOST": "db-01"},
namespace="default", container="web")
Set resource limits¶
k8s.set_resources("deployment", "web",
requests={"cpu": "100m", "memory": "128Mi"},
limits={"cpu": "500m", "memory": "512Mi"},
namespace="default",
)
Run a one-off job¶
result = k8s.run("debug", "busybox", command=["sh", "-c", "nslookup kubernetes"],
namespace="default", rm=True, timeout="1m")
Delete and patch¶
k8s.delete("pod", "web-abc123", namespace="default")
k8s.patch("deployment", "web", {"spec": {"replicas": 5}},
namespace="default", type="merge")
Labeling and annotating¶
k8s.label("pod", "web-abc123", {"version": "v2", "canary": "true"}, namespace="default")
k8s.annotate("deployment", "web", {"deploy-note": "hotfix"}, namespace="default")
Server-Side Apply Diffing¶
Inspect drift between local manifests and live cluster state before applying:
diff_report = k8s.diff("deployment.yaml", namespace="production")
if diff_report.has_drift:
print("Detected drift in fields:", diff_report.drifted_fields)
print("Unified diff:\n", diff_report.diff)
if diff_report.conflicts:
print("Field ownership conflicts:", diff_report.conflicts)
Declarative Pruning¶
Apply a manifest set and automatically delete omitted resources previously managed by field_manager:
# Apply current desired resources and prune stale resources labeled with app=my-stack
k8s.apply(
[app_dep, app_svc],
namespace = "production",
prune = True,
prune_labels = {"app": "my-stack"},
)
Pod Eviction & Disruption Budgets¶
Evict a Pod via the /eviction subresource, honoring PodDisruptionBudget constraints:
# Test eviction using dry_run without terminating the pod
result = k8s.evict("web-worker-0", namespace="production", dry_run=True)
print("Eviction permitted:", result.evicted)
# Real eviction
k8s.evict("web-worker-0", namespace="production")
Cluster info¶
ver = k8s.version()
print("Kubernetes:", ver.git_version)
resources = k8s.api_resources()
for r in resources:
print(r.name, r.kind)
Note: All
k8sfunctions that can fail supporttry_variants that return aResultinstead of raising an error.