NAME
Net::Async::Kubernetes::Controller - Minimal controller runtime for Net::Async::Kubernetes
VERSION
version 0.009
SYNOPSIS
use IO::Async::Loop;
use Net::Async::Kubernetes;
my $loop = IO::Async::Loop->new;
my $kube = Net::Async::Kubernetes->new(
kubeconfig => "$ENV{HOME}/.kube/config",
);
$loop->add($kube);
my $controller = $kube->controller(
on_reconcile => sub {
my ($ctx) = @_;
return $ctx->{controller}->patch_status($ctx->{object},
status => { phase => 'Running' },
);
},
);
$controller->watch_resource('Pod', namespace => 'default');
$loop->run;
DESCRIPTION
Net::Async::Kubernetes::Controller is a minimal controller runtime built on top of Net::Async::Kubernetes and its watcher support.
It is intentionally small. The module focuses on the operational pieces most controllers need:
watch registration
keyed queueing with deduplication
serialized reconcile dispatch
retry hooks with configurable delay policy
status subresource helpers
It does not attempt to provide higher-level controller DSL sugar.
configure
Internal IO::Async::Notifier configuration method.
Accepted parameters:
kube-
Existing Net::Async::Kubernetes instance to bind to. The reference is weak, as in Net::Async::Kubernetes::Watcher, so the caller has to keep the client alive for as long as the controller is used.
kubeconfig,context,server,credentials,resource_map,resource_map_from_cluster-
Client construction parameters used when
kubeis not supplied. A client built this way is owned by the controller. on_reconcile-
Required reconcile callback. Receives a hashref with
controller,kube,resource,event_type,object,key, andattempt.controllerandkubeare weak references. Both are valid for the whole reconcile, including anything chained onto theFutureit returns, but a context kept past that keeps neither object alive. on_watch_error-
Optional callback for errors of a registered watch:
ERRORevents, for example a403arriving mid-stream, and failed watch attempts -- a transport error, the API server rejecting the request, or a stream that closes at once without an event or right after anERRORevent -- which the watcher retries with backoff. Receives($error, $ctx), where$erroris theStatushashref the watcher reports (see "on_error" in Net::Async::Kubernetes::Watcher; a failed attempt hasreasonWatchFailed) and$ctxcarriescontroller,kube, andresource. Errors are not reconcile objects, so they never enter the workqueue. Anon_errorpassed towatch_resourcetakes precedence for that watch. Without either, the watcherwarns about failed attempts.The watcher's reconnect settings (
reconnect_delay,max_reconnect_delay,reconnect_jitter,max_retries,min_watch_duration) are passed towatch_resourcewith the other watcher arguments. A watch that gave up onmax_retriesstays stopped;stopandstarton the controller build it anew. retry_delay-
Optional retry policy. Accepts a fixed scalar delay, an arrayref of delays, or a coderef receiving
($attempt, $ctx, $error).
kube
Returns the bound Net::Async::Kubernetes client.
on_reconcile
Returns the reconcile callback.
on_watch_error
Returns the watch error callback, if one is configured.
retry_delay
Returns the configured retry delay policy. Defaults to 1.
start
Starts all registered resource watches. Called automatically when the controller is added to an event loop.
stop
Stops registered watches and prevents further queue processing. Each stopped watch is also detached from the client it was registered on, so a watcher handle a caller kept from watch_resource does not survive its controller's stop: it is no longer attached to anything, calling start on it directly drives a notifier with no loop, and the controller's own start builds a fresh watcher for the spec regardless. Do not hold on to a watcher handle past a stop.
Pending work is dropped with them: the workqueue is cleared and any retry timer is cancelled, because a restart re-lists through fresh watches and delivers everything still present again. A key's failure count survives, so a key that was retrying picks up at its next attempt number rather than at attempt 1.
watch_resource
$controller->watch_resource('Pod',
namespace => 'default',
key_for => sub {
my ($object, $spec) = @_;
return $object->metadata->name;
},
);
Registers a watched resource and returns the watcher instance once started. An odd list of parameters croaks, as Invalid arguments to watch_resource(), before anything is registered. Repeated events for the same reconcile key are coalesced into a single queued entry. A key's entry is dropped once it reconciles cleanly, so the queue does not grow with the number of objects seen; a key that is still queued, dirty or retrying keeps its entry, and with it its attempt count.
get_object
Thin wrapper around $controller->kube->get(...). Returns its Future, resolving to the inflated IO::K8s object.
list_objects
Thin wrapper around $controller->kube->list(...). Returns its Future, resolving to an IO::K8s::List.
patch_status
$controller->patch_status('Pod', 'my-pod',
namespace => 'default',
status => { phase => 'Running' },
)->get;
$controller->patch_status($object,
status => { phase => 'Running' },
)->get;
Patch the /status subresource for an object. Accepts either a class/name pair (the name positional or as name => ...) or an object instance, plus a status payload; in the object form, status defaults to the object's own status when its class has one. The helper sends { status => $status } through "patch_status" in Net::Async::Kubernetes, with the patch type from type (merge by default, strategic or json). Returns a Future that resolves to the patched object.
Bad arguments, a missing status (including an object whose class has no status to fall back on), an unknown resource or patch type, and server errors fail the returned Future instead of dying. So does any option other than name, namespace, status and type -- in the object form only status and type, the object gives name and namespace -- before a request is sent: Unknown argument 'typ' to patch_status() (allowed: name, namespace, status, type). Such an option would otherwise be dropped when the patch is built. An odd list of options after the object or the name fails it as Invalid arguments to patch_status() too, before a request is sent.
update_status
$controller->update_status($object)->get;
Update the /status subresource for a full object instance, through "update_status" in Net::Async::Kubernetes. Returns a Future that resolves to the updated object. An object without metadata or metadata.name, or one that is no Kubernetes resource (an IO::K8s::List, a nested PodSpec), fails the Future rather than croaking as the client method does.
SEE ALSO
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/p5-net-async-kubernetes/issues.
IRC
Join #kubernetes on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <torsten@raudssus.de>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.