Listing, pagination & watch
Push filtering to the apiserver instead of fetching everything. Selector methods build the labelSelector and fieldSelector query parameters and chain freely — several selectors are joined with a comma:
use RoundlyConsulting\KubernetesApi\Resources\Pod;
// Label and field selectors build the labelSelector / fieldSelector query params.
$pods = $cluster->pods()
->setNamespace('shop')
->whereLabel('app', 'checkout')
->whereLabelIn('tier', ['web', 'api'])
->whereLabelExists('team')
->whereLabelMissing('canary')
->whereField('status.phase', 'Running')
->limit(100)
->get();
$pods->map(fn (Pod $pod): string => $pod->getName());| Method | Sends |
|---|---|
whereLabel('app', 'web') | labelSelector=app=web |
whereLabelNot('tier', 'cache') | tier!=cache |
whereLabelIn('zone', ['a', 'b']) | zone in (a,b) |
whereLabelNotIn('zone', ['c']) | zone notin (c) |
whereLabelExists('team') | team |
whereLabelMissing('deprecated') | !deprecated |
whereField('status.phase', 'Running') | fieldSelector=status.phase=Running |
whereFieldNot('status.phase', 'Failed') | status.phase!=Failed |
limit(100) · continueFrom($token) | limit=100 · continue=<token> |
allNamespaces() | Lists from the cluster-wide path instead of one namespace. |
Pagination and lazy iteration
limit() caps a page and getPage() returns a ResourcePage with the items and the apiserver’s pagination metadata. lazy() and each() follow continue tokens for you, keeping one page in memory at a time — pair them with limit() to set the page size:
use RoundlyConsulting\KubernetesApi\Resources\Pod;
// List across every namespace.
$all = $cluster->pods()->allNamespaces()->whereLabel('app', 'checkout')->get();
// Page explicitly with a continue token.
$page = $cluster->pods()->setNamespace('shop')->limit(50)->getPage();
$page->items; // ResourcesCollection
$page->continue; // ?string — pass to ->continueFrom(...) for the next page
$page->remainingItemCount; // ?int
$page->hasMore(); // bool
if ($page->hasMore()) {
$next = $cluster->pods()->setNamespace('shop')->limit(50)->continueFrom($page->continue)->getPage();
}
// Or iterate every item lazily, auto-following continue tokens.
foreach ($cluster->pods()->allNamespaces()->limit(200)->lazy() as $pod) {
// one page in memory at a time
}
$cluster->pods()->allNamespaces()->limit(200)->each(function (Pod $pod): void {
// ...
});Watching for changes
watch() opens a streaming watch over the same listing — selectors and allNamespaces() apply — and calls your callback with a WatchEvent for every change until the connection ends. A second argument adds query parameters to the watch request:
use RoundlyConsulting\KubernetesApi\DataTransferObjects\WatchEvent;
$cluster->pods()
->setNamespace('shop')
->whereLabel('app', 'checkout')
->watch(function (WatchEvent $event): void {
$event->type; // ADDED / MODIFIED / DELETED
$event->object; // the Pod resource
if ($event->isDeleted()) {
logger()->warning("Pod {$event->object->getName()} was deleted");
}
});WatchEvent exposes type and object plus isAdded(), isModified() and isDeleted(). Each event reaches the callback as soon as the apiserver sends it, and watch() returns when the stream ends:
- when the apiserver closes the watch — it does so on its own timeout, usually after 30–60 minutes;
- after client.stream_timeout seconds of silence, if you set one (0, the default, waits indefinitely).
The request timeout in client.options only bounds connecting and the response headers. A connection that breaks off mid-stream throws Laravel’s ConnectionException. Long-lived watches suit queued or console contexts, not web requests — re-open the watch in a loop if you need it to run forever.
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.