Patch, scale, rollout & dry-run
patch() sends a targeted change with the Content-Type of the chosen strategy and returns the updated resource — no read-modify-write:
use RoundlyConsulting\KubernetesApi\DataTransferObjects\KubernetesPatch;
// Strategic merge patch
$cluster->deployments()->setNamespace('shop')->withName('checkout')
->patch(KubernetesPatch::strategicMerge(['spec' => ['replicas' => 4]]));
// JSON merge patch
$cluster->deployments()->setNamespace('shop')->withName('checkout')
->patch(KubernetesPatch::merge(['spec' => ['paused' => true]]));
// JSON Patch — a list of operations
$cluster->deployments()->setNamespace('shop')->withName('checkout')
->patch(KubernetesPatch::json([
['op' => 'replace', 'path' => '/spec/replicas', 'value' => 6],
['op' => 'add', 'path' => '/metadata/labels/release', 'value' => 'v1.4.0'],
]));Server-side apply
KubernetesPatch::apply() sends the body as JSON under the application/apply-patch+yaml content type (JSON is valid YAML). Every write carries your manager name as fieldManager; a server-side apply without one uses kubernetes-api-for-laravel. force: true sends force=true — only ever on a server-side apply — to take over fields another manager owns instead of failing with a 409 conflict:
use RoundlyConsulting\KubernetesApi\DataTransferObjects\KubernetesPatch;
// Server-side apply: sent with your manager name as fieldManager. force: true takes over
// fields another manager owns instead of failing with a 409 conflict.
$cluster->deployments()->setNamespace('shop')->withName('checkout')->patch(
KubernetesPatch::apply([
'apiVersion' => 'apps/v1',
'kind' => 'Deployment',
'metadata' => ['name' => 'checkout', 'namespace' => 'shop'],
'spec' => ['replicas' => 4],
], force: true),
);
// A fieldManager in the query array wins over the manager name.
$cluster->deployments()->setNamespace('shop')->withName('checkout')->patch(
KubernetesPatch::apply(['apiVersion' => 'apps/v1', 'kind' => 'Deployment', 'metadata' => ['name' => 'checkout']]),
['fieldManager' => 'shop-app'],
);Patch types
Each strategy is a PatchType case whose backing value is its Content-Type. The enum carries the shared enum helpers from enums-for-laravel — handy when the strategy comes from user input:
use RoundlyConsulting\KubernetesApi\DataTransferObjects\KubernetesPatch;
use RoundlyConsulting\KubernetesApi\Enums\PatchType;
PatchType::StrategicMerge->contentType(); // 'application/strategic-merge-patch+json'
PatchType::StrategicMerge->label(); // 'Strategic Merge'
PatchType::values(); // the four content types
PatchType::toOptions(); // content type => label, for a select input
PatchType::validationRule(); // 'in:application/strategic-merge-patch+json,…'
PatchType::fromName('Json'); // PatchType::Json
// Build a patch from validated input
$patch = new KubernetesPatch(PatchType::from($request->input('type')), $request->input('body'));Scale and rollout restart
scale() merge-patches spec.replicas through the /scale subresource and returns the apiserver’s Scale answer — a DTO with name, namespace, replicas (desired), currentReplicas (observed), selector and resourceVersion — not the workload; find() the workload for that. rolloutRestart() stamps the pod template’s kubectl.kubernetes.io/restartedAt annotation with the current time — exactly what kubectl rollout restart does:
// Scale via the /scale subresource. Returns the apiserver's Scale answer (a DTO), not the
// Deployment: $scale->replicas (desired), ->currentReplicas (observed), ->selector.
$scale = $cluster->deployments()->setNamespace('shop')->withName('checkout')->scale(5);
$cluster->statefulSets()->setNamespace('shop')->withName('search')->scale(3);
// Roll the pods by stamping the restartedAt annotation (like kubectl rollout restart).
$cluster->deployments()->setNamespace('shop')->withName('checkout')->rolloutRestart();
$cluster->daemonSets()->setNamespace('monitoring')->withName('node-agent')->rolloutRestart();
// Validate a write without persisting it by appending ?dryRun=All.
$preview = $cluster->deployments()->setNamespace('shop')->withName('checkout')->dryRun()->scale(5);- scale() — deployments, replica sets, stateful sets and replication controllers.
- rolloutRestart() — deployments, stateful sets and daemon sets.
- dryRun() appends dryRun=All to every subsequent write — create, update, updateOrCreate, patch, delete, scale and rolloutRestart — so the apiserver validates the request and returns the result without persisting it. dryRun(false) switches it off again.
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.