The Kubernetes facade
Everything starts at the Kubernetes facade (RoundlyConsulting\KubernetesApi\Facades\Kubernetes). It hands out clusters — immutable clients — and, through them, resource objects (Pod, Deployment, …) that read and write the apiserver. Calls without a cluster name go to the default cluster from config/kubernetes.php:
use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;
Kubernetes::pods()->get(); // default cluster, default namespace
Kubernetes::namespace('shop')->deployments()->get(); // default cluster, scoped to shop
Kubernetes::cluster('production')->services()->get(); // a named cluster
Kubernetes::ping(); // bool — does the apiserver answer?
Kubernetes::version()->gitVersion; // "v1.34.0" (a VersionInfo DTO)
Kubernetes::hasCluster('production'); // bool
Kubernetes::clusters(); // ['default', 'production']
Kubernetes::url('https://127.0.0.1:6443')->withToken($token); // a new, blank ad-hoc cluster
Kubernetes::fromKubeConfig(context: 'staging'); // a new cluster from a kubeconfig contextMethod reference
| Method | Returns | What it does |
|---|---|---|
cluster(?string $name = null) | Cluster | A configured or registered cluster — the default one when null. Resolved once and reused. |
hasCluster(string $name) · clusters() | bool · list<string> | Whether a cluster exists; every configured and registered name. |
registerCluster(string $name, Closure $definition) | void | Define a cluster in code; overrides a configured one of the same name. |
registerResource(string $name, string $class) | void | Register a resource class under an accessor name for every cluster. |
namespace(string $namespace) | Cluster | The default cluster, scoped to one namespace. |
ping() · version() | bool · VersionInfo | Health and build information of the default cluster. |
url(string $url) | Cluster | A new, blank ad-hoc cluster. |
connect(KubeConfig $config) | Cluster | A new ad-hoc cluster from a resolved connection; its namespace, if any, is the default namespace. |
fromKubeConfig(?string $path, ?string $context) · inCluster() | Cluster | A new ad-hoc cluster from a kubeconfig context (its namespace is the default namespace) or the pod’s service account (its token file is re-read for every request). |
resource(string $class) | Resource | Any resource class, bound to the default cluster. |
pods(), deployments(), services(), … (33 accessors) | the resource | Every packaged resource, bound to the default cluster. |
apps(), tenantNamespace(), … | resource / macro result | A custom resource registered under that name, or a Cluster macro — on the default cluster. |
fake() | KubernetesFake | Swap in the in-memory apiserver for tests — see Testing. Under the fake, the facade also forwards the fake’s assertions, assertTokenRequested() included. |
The Cluster handle
cluster(), namespace(), url(), connect(), fromKubeConfig() and inCluster() all return a RoundlyConsulting\KubernetesApi\Cluster. Every Cluster has the same 33 accessors as the facade, plus its own identity, health checks and a raw request() escape hatch that is authenticated, rate limited and faked like everything else:
$cluster = Kubernetes::cluster('production');
$cluster->name(); // 'production' (null for ad-hoc clusters)
$cluster->defaultNamespace(); // from clusters.production.namespace
$cluster->hasResource('apps'); // is an accessor registered under that name?
$cluster->getUrl(); // plus getToken(), getTokenFile(), getManagerName(), shouldVerify()…
$cluster->followsRedirects(); // false unless the cluster opted in
$cluster->redactsErrors(); // whether exception messages carry status + reason only
// Raw escape hatch — authenticated, rate limited and faked like the typed resources
$response = $cluster->request('GET', '/apis/apps/v1/namespaces/shop/deployments', ['limit' => 10]);
$response->json('items');| Cluster method | Returns | Notes |
|---|---|---|
name() | ?string | The registration name; null for ad-hoc clusters (url(), connect(), fromKubeConfig(), inCluster()). |
url() · with…() · without…() · withManagerName() · withDefaultNamespace() | a copy | Connection and credentials — see Connecting to a cluster. |
namespace(string) · namespaceScope() | Cluster · ?string | A copy scoped to one namespace; the scope, if any. |
defaultNamespace() | string | The soft default for namespaced resources. |
withRedirects() · withoutRedirects() · followsRedirects() | a copy · a copy · bool | Opt in to following HTTP redirects — off by default; see Connecting to a cluster. |
withRedactedErrors() · redactsErrors() | a copy · bool | Exception messages with status and reason only; see Error handling. |
applyConfig(KubeConfig) | a copy | Replaces URL, token, token file, certificate, key, CA and verification; a namespace becomes the default namespace (never over a namespace scope). The redirect opt-in and error redaction are kept. |
resource(class-string) · hasResource(string) | Resource · bool | Any resource class bound to this cluster; whether an accessor name is registered. |
ping() · version() | bool · VersionInfo | ping() is false on an apiserver error (an unfollowed redirect included), a connection failure or a missing URL; a rate-limit exhaustion still throws. |
request($method, $path, $query = [], $body = '', $contentType = null, $stream = false) | Response | Raw escape hatch through the cluster’s transport; a failed response throws KubernetesException, and so does a redirect that wasn’t followed. |
getUrl() · getToken() · getTokenFile() · getManagerName() · shouldVerify() · getPathTo…() | readers | The credentials the cluster carries. |
- Clusters are immutable: every with…() / without…() call, url(), withManagerName(), withDefaultNamespace() and namespace() return a new cluster, so a client handed out by the facade can never be re-pointed or re-credentialed by another caller.
- Named clusters are resolved once and reused; url(), connect(), fromKubeConfig() and inCluster() build a new ad-hoc cluster on every call.
- Cluster::execute() is @internal — it opens the exec WebSocket for Pod::exec(); call exec() on a pod instead.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.