Resource accessors
Every packaged resource has a typed accessor — on the facade, bound to the default cluster, and on every Cluster — that returns a fresh, cluster-bound resource object. Each accessor reads the resources map at call time, so a name you point at your own subclass returns that class; custom names resolve the same way:
use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;
use RoundlyConsulting\KubernetesApi\Resources\Deployment;
Kubernetes::pods(); // Resources\Pod, on the default cluster
$cluster = Kubernetes::cluster('production');
$cluster->pods(); // Resources\Pod
$cluster->deployments(); // Resources\Deployment
$cluster->namespaces(); // Resources\KubernetesNamespace
$cluster->traefikIngressRoutes(); // Resources\TraefikIngressRoute
// Namespaced resources target the cluster's default namespace unless you set one
$cluster->pods()->setNamespace('shop')->get();
// List across every namespace
$cluster->pods()->allNamespaces()->get();
// Cluster-scoped resources ignore setNamespace()
$cluster->nodes()->get();
// Any resource class, with or without an accessor
Kubernetes::resource(Deployment::class)->setNamespace('shop')->get();
$cluster->resource(Deployment::class)->setNamespace('shop')->get();
Deployment::make()->setCluster($cluster)->setNamespace('shop')->get();Built-in resources
| Accessor | Class | Kind | apiVersion | Scope |
|---|---|---|---|---|
clusterRoles() | ClusterRole | ClusterRole | rbac.authorization.k8s.io/v1 | Cluster |
clusterRoleBindings() | ClusterRoleBinding | ClusterRoleBinding | rbac.authorization.k8s.io/v1 | Cluster |
configMaps() | ConfigMap | ConfigMap | v1 | Namespace |
cronJobs() | CronJob | CronJob | batch/v1 | Namespace |
daemonSets() | DaemonSet | DaemonSet | apps/v1 | Namespace |
deployments() | Deployment | Deployment | apps/v1 | Namespace |
endpoints() | Endpoints | Endpoints | v1 | Namespace |
events() | Event | Event | v1 | Namespace |
horizontalPodAutoscalers() | HorizontalPodAutoscaler | HorizontalPodAutoscaler | autoscaling/v2 | Namespace |
ingresses() | Ingress | Ingress | networking.k8s.io/v1 | Namespace |
jobs() | Job | Job | batch/v1 | Namespace |
limitRanges() | LimitRange | LimitRange | v1 | Namespace |
namespaces() | KubernetesNamespace | Namespace | v1 | Cluster |
networkPolicies() | NetworkPolicy | NetworkPolicy | networking.k8s.io/v1 | Namespace |
nodes() | Node | Node | v1 | Cluster |
persistentVolumes() | PersistentVolume | PersistentVolume | v1 | Cluster |
persistentVolumeClaims() | PersistentVolumeClaim | PersistentVolumeClaim | v1 | Namespace |
pods() | Pod | Pod | v1 | Namespace |
replicaSets() | ReplicaSet | ReplicaSet | apps/v1 | Namespace |
replicationControllers() | ReplicationController | ReplicationController | v1 | Namespace |
resourceQuotas() | ResourceQuota | ResourceQuota | v1 | Namespace |
roles() | Role | Role | rbac.authorization.k8s.io/v1 | Namespace |
roleBindings() | RoleBinding | RoleBinding | rbac.authorization.k8s.io/v1 | Namespace |
secrets() | Secret | Secret | v1 | Namespace |
serviceAccounts() | ServiceAccount | ServiceAccount | v1 | Namespace |
services() | Service | Service | v1 | Namespace |
statefulSets() | StatefulSet | StatefulSet | apps/v1 | Namespace |
storageClasses() | StorageClass | StorageClass | storage.k8s.io/v1 | Cluster |
traefikIngressRoutes() | TraefikIngressRoute | IngressRoute | traefik.group | Namespace |
traefikMiddlewares() | TraefikMiddleware | Middleware | traefik.group | Namespace |
traefikServersTransports() | TraefikServersTransport | ServersTransport | traefik.group | Namespace |
traefikTlsOptions() | TraefikTlsOption | TLSOption | traefik.group | Namespace |
traefikTlsStores() | TraefikTlsStore | TLSStore | traefik.group | Namespace |
Classes live in RoundlyConsulting\KubernetesApi\Resources. The Namespace class is KubernetesNamespace, because namespace is a reserved word in PHP; its accessor is still namespaces(). apiVersion v1 maps to /api/v1, every other version to /apis/<version>. Traefik resources take their group from the traefik.group config key (traefik.io/v1alpha1 by default).
Scoping to a namespace
namespace() scopes a cluster to one namespace, and everything it hands out is pinned there: every request goes to that namespace, and any attempt to leave it — setNamespace('other'), allNamespaces(), ignoreNamespace(), re-scoping the cluster, binding the resource to a cluster scoped elsewhere — throws NamespaceScopeException. Items a scoped listing returns stay pinned too:
use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;
$shop = Kubernetes::namespace('shop'); // or Kubernetes::cluster('production')->namespace('shop')
$shop->pods()->whereLabel('app', 'web')->get(); // GET /api/v1/namespaces/shop/pods?labelSelector=app%3Dweb
$shop->pods()->setNamespace('kube-system'); // NamespaceScopeException
$shop->pods()->allNamespaces(); // NamespaceScopeException
$shop->nodes()->get(); // cluster-scoped kinds are unaffected
$shop->namespaceScope(); // 'shop'
// A soft default instead of a boundary — setNamespace() still works
$cluster = Kubernetes::cluster('production')->withDefaultNamespace('shop');A namespace on a clusters entry, or withDefaultNamespace(), is only a default; namespace() is the boundary. Cluster-scoped kinds — nodes, namespaces, cluster roles and the like — are unaffected.
Names can’t get around it either. Before a request is sent, every value that goes into the URL path is checked — anything else throws InvalidResourceException and nothing is sent, so withName('../../kube-system/secrets/admin-token') can’t leave shop:
- Names must be a single path segment: not . or .., and no /, % or whitespace. They are also percent-encoded.
- Namespaces must be valid DNS-1123 labels.
- Plurals and apiVersions must match their Kubernetes grammar.
- The raw request($method, $path) escape hatch is the exception: it sends the path you give it exactly as written and is not scoped.
Per-kind operations
- scale() — deployments, replica sets, stateful sets and replication controllers.
- rolloutRestart() — deployments, stateful sets and daemon sets.
- logs(), streamLogs() and exec() — pods.
- Everything else — get, find, create, update, updateOrCreate, patch, delete, watch, dryRun — works on every resource.
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.