assay.k8s
Kubernetes API client. 30+ resource types, CRDs, readiness checks, pod logs, rollouts. Module-level
functions: auto-discovers cluster API via KUBERNETES_SERVICE_HOST env var. Auth: uses service
account token from /var/run/secrets/kubernetes.io/serviceaccount/token. All functions accept
optional opts with {base_url, token} overrides.
Supported kinds: pod, service, secret, configmap, endpoints, serviceaccount, persistentvolumeclaim (pvc), limitrange, resourcequota, event, namespace, node, persistentvolume (pv), deployment, statefulset, daemonset, replicaset, job, cronjob, ingress, ingressclass, networkpolicy, storageclass, role, rolebinding, clusterrole, clusterrolebinding, hpa, poddisruptionbudget (pdb).
CRD Registration
M.register_crd(kind, api_group, version, plural, cluster_scoped?)— Register custom resource for use with get/list/create
Raw HTTP Verbs
M.get(path, opts?)→ resource — Raw GET any K8s API pathM.post(path, body, opts?)→ resource — Raw POST to any K8s API pathM.put(path, body, opts?)→ resource — Raw PUT to any K8s API pathM.patch(path, body, opts?)→ resource — Raw PATCH any K8s API path.opts.content_typedefaults to merge-patch.M.delete(path, opts?)→ nil — Raw DELETE any K8s API path
Resources (M.resources)
Generic CRUD operations for any resource kind.
M.resources:get(namespace, kind, name, opts?)→ resource — Get resource by kind and nameM.resources:list(namespace, kind, opts?)→{items}— List resources.opts:{label_selector, field_selector, limit}M.resources:create(namespace, kind, body, opts?)→ resource — Create resourceM.resources:update(namespace, kind, name, body, opts?)→ resource — Replace resourceM.resources:patch(namespace, kind, name, body, opts?)→ resource — Patch resourceM.resources:delete(namespace, kind, name, opts?)→ nil — Delete resourceM.resources:exists(namespace, kind, name, opts?)→ bool — Check if resource existsM.resources:is_ready(namespace, kind, name, opts?)→ bool — Check if resource is ready (deployment, statefulset, daemonset, job, node)M.resources:wait_ready(namespace, kind, name, timeout_secs?, opts?)→ true — Wait for readiness, errors on timeout. Default 60s.
Secrets (M.secrets)
M.secrets:get(namespace, name, opts?)→{key=value}— Get decoded secret data (base64-decoded)
ConfigMaps (M.configmaps)
M.configmaps:get(namespace, name, opts?)→{key=value}— Get ConfigMap data
Pods (M.pods)
M.pods:list(namespace, opts?)→{items}— List pods in namespaceM.pods:status(namespace, opts?)→{running, pending, succeeded, failed, unknown, total}— Get pod status countsM.pods:logs(namespace, pod_name, opts?)→ string — Get pod logs.opts:{tail, container, previous, since}M.pods:exec(namespace, pod_name, command, opts?)→{stdout, stderr, exit_code}— Run a command in a pod over the streaming exec endpoint (WebSocket,v4.channel.k8s.iosubprotocol).commandis a string (single argv element) or an array of strings.opts:{container, stdin, tty, timeout_secs, token, base_url, insecure}.insecuredefaults totruebecause in-cluster API servers present a cluster-CA certificate the runtime does not trust by default. Gated: exec opens its stream viaws.connect, so read-only mode blocks it and approval mode suspends it — no separate gate entry. The v4 protocol demultiplexes stdout (channel 1) and stderr (channel 2), and reads the exit code from thev1.Statusobject the server sends on the error channel (channel 3) at process exit.
local k8s = require("assay.k8s")
local result = k8s.pods:exec("default", "my-pod", { "sh", "-c", "echo hi" }, { container = "app" })
print(result.stdout) -- "hi\n"
print(result.exit_code) -- 0
Services (M.services)
M.services:endpoints(namespace, name, opts?)→ [ip] — Get service endpoint IP addresses
Deployments (M.deployments)
M.deployments:rollout_status(namespace, name, opts?)→{desired, updated, ready, available, unavailable, complete}— Get deployment rollout status
Nodes (M.nodes)
M.nodes:status(opts?)→[{name, ready, roles, capacity, allocatable}]— Get all node statuses
Namespaces (M.namespaces)
M.namespaces:exists(name, opts?)→ bool — Check if namespace exists
Events (M.events)
M.events:list(namespace, opts?)→{items}— List events in namespaceM.events:for_resource(namespace, kind, name, opts?)→{items}— Get events for a specific resource
Backward Compatibility
All legacy flat functions (M.get_resource, M.list, M.get_secret, M.pod_status, etc.) remain
available and delegate to the sub-objects above.
Example:
local k8s = require("assay.k8s")
-- New sub-object style
k8s.resources:wait_ready("default", "deployment", "my-app", 120)
local secret = k8s.secrets:get("default", "my-secret")
log.info("DB password: " .. secret["password"])
-- Legacy style still works
k8s.wait_ready("default", "deployment", "my-app", 120)
local secret = k8s.get_secret("default", "my-secret")