NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Kubernetes API for Laravel

Custom resources & macros

Define a resource class for any kind — including your own CRDs — and register it in config/kubernetes.php or at runtime:

use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;
use RoundlyConsulting\KubernetesApi\Resources\Resource;

class Application extends Resource
{
    protected string $version = 'example.com/v2';

    protected string $kind = 'Application';

    protected bool $usesNamespaces = true;
}

Kubernetes::registerResource('apps', Application::class);

Kubernetes::cluster('production')->apps()->get(); // all Application resources
Kubernetes::apps()->get();                        // on the default cluster
  • $version — the apiVersion. v1 (the default) maps to /api/v1; anything else to /apis/<version>.
  • $kind — the kind written into every payload.
  • $usesNamespaces — true for namespaced kinds; the default false makes the resource cluster-scoped.
  • $plural — the REST plural. Left null it is the lower-cased kind, pluralised; set it when the CRD’s spec.names.plural differs.
  • Add the traits you need — HasSpec, HasStatus, HasStatusPhase, HasReplicas… from RoundlyConsulting\KubernetesApi\Traits\Resource.
namespace App\Kubernetes;

use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;
use RoundlyConsulting\KubernetesApi\Resources\Resource;
use RoundlyConsulting\KubernetesApi\Traits\Resource\HasSpec;
use RoundlyConsulting\KubernetesApi\Traits\Resource\HasStatus;

class DatabaseMigration extends Resource
{
    use HasSpec;
    use HasStatus;

    protected string $version = 'ops.example.com/v1';

    protected string $kind = 'DatabaseMigration';

    protected ?string $plural = 'dbmigrations';   // the CRD's spec.names.plural

    protected bool $usesNamespaces = true;
}

// Registered or not, any class resolves through resource():
Kubernetes::cluster('production')->resource(DatabaseMigration::class)
    ->setNamespace('shop')
    ->setName('add-orders-index')
    ->setSpec('database', 'orders')
    ->create();

Registering and swapping in config

The resources map is read at call time, and Kubernetes::registerResource() writes into the same map. Pointing a built-in name at your own subclass swaps it on every cluster, the facade included — resource classes are deliberately not final. A name a Cluster method already answers (url, namespace, …) is refused with InvalidResourceException:

// config/kubernetes.php
'resources' => [
    // …keep the built-in entries from the published file…
    'dbMigrations' => App\Kubernetes\DatabaseMigration::class,

    // Swap a built-in: every $cluster->pods() now returns your subclass
    'pods' => App\Kubernetes\Pod::class,
],
namespace App\Kubernetes;

use RoundlyConsulting\KubernetesApi\Resources\Pod as BasePod;

class Pod extends BasePod
{
    public function isServing(): bool
    {
        return $this->isRunning() && $this->containersReady();
    }
}

Macros and conditionals

Both Cluster and every resource use Laravel’s Macroable and Conditionable traits, so you can extend and branch fluently. A Cluster macro is also callable on the facade, for the default cluster:

use RoundlyConsulting\KubernetesApi\Cluster;
use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;
use RoundlyConsulting\KubernetesApi\Resources\Deployment;

// A resource macro — $this is the resource
Deployment::macro('isFullyAvailable', function (): bool {
    /** @var Deployment $this */
    return $this->getAvailableReplicasCount() >= $this->getReplicas();
});

// A client macro — $this is the cluster client
Cluster::macro('tenantNamespace', function (string $tenant) {
    /** @var Cluster $this */
    return $this->namespaces()->setName("tenant-{$tenant}");
});

$cluster = Kubernetes::cluster('production');

$cluster->deployments()->setNamespace('shop')->withName('checkout')->find()->isFullyAvailable();
$cluster->tenantNamespace('acme')->create();
Kubernetes::tenantNamespace('acme');   // a Cluster macro also works on the facade (default cluster)

// Conditionable — branch without breaking the chain
$cluster->deployments()->setNamespace('shop')->withName('checkout')->find()
    ->when($peakTraffic, fn (Deployment $deployment) => $deployment->setReplicas(12))
    ->unless($peakTraffic, fn (Deployment $deployment) => $deployment->setReplicas(3))
    ->update();

Resource macros whose names start with set, get, with, addTo or remove are intercepted by the magic attribute accessors — pick another prefix, such as is…, has… or find….

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 crypto

By 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.