NAME
Kubernetes::REST - A Perl REST Client for the Kubernetes API
VERSION
version 1.104
SYNOPSIS
use Kubernetes::REST;
my $api = Kubernetes::REST->new(
server => {
endpoint => 'https://kubernetes.local:6443',
ssl_verify_server => 1,
ssl_ca_file => '/path/to/ca.crt',
},
credentials => { token => $token },
);
# List all namespaces
my $namespaces = $api->list('Namespace');
for my $ns (@{ $namespaces->items }) {
say $ns->metadata->name;
}
# List pods in a namespace
my $pods = $api->list('Pod', namespace => 'default');
# Get a specific pod
my $pod = $api->get('Pod', name => 'my-pod', namespace => 'default');
# Create a namespace
my $ns = $api->new_object(Namespace => {
metadata => { name => 'my-namespace' },
});
my $created = $api->create($ns);
# Create multiple namespaces
for my $i (1..10) {
$api->create($api->new_object(Namespace =>
metadata => { name => "test-ns-$i" },
));
}
# Update a resource (full replacement)
$pod->metadata->labels({ app => 'updated' });
my $updated = $api->update($pod);
# Patch a resource (partial update)
my $patched = $api->patch('Pod', 'my-pod',
namespace => 'default',
patch => { metadata => { labels => { env => 'staging' } } },
);
# Delete a resource
$api->delete($pod);
# or by name:
$api->delete('Pod', name => 'my-pod', namespace => 'default');
# Idempotent create-or-update (from a typed object or a manifest hashref)
$api->ensure($pod);
$api->ensure({
apiVersion => 'v1',
kind => 'Secret',
metadata => { name => 'my-secret', namespace => 'default' },
stringData => { password => 'hunter2' },
});
# Batch apply
$api->ensure_all(@objects);
# Apply a labeled set and prune anything with that label not in the set
$api->ensure_only(
label => 'app.kubernetes.io/component=queen',
objects => \@rbac_objects,
kinds => [qw(Role RoleBinding ClusterRoleBinding)],
namespaces => ['default', undef],
);
DESCRIPTION
This module provides a simple REST client for the Kubernetes API using IO::K8s resource classes. The IO::K8s classes know their own metadata (API version, kind, whether they're namespaced), so URL building is automatic.
server
Required. Kubernetes::REST::Server instance or hashref with server connection configuration.
server => { endpoint => 'https://kubernetes.local:6443' }
Automatically coerces hashrefs to Kubernetes::REST::Server objects.
credentials
Required. Authentication credentials. Can be a hashref, Kubernetes::REST::AuthToken, or any object with a token() method.
credentials => { token => $bearer_token }
Automatically coerces hashrefs to Kubernetes::REST::AuthToken objects.
io
HTTP backend for making requests. Must consume Kubernetes::REST::Role::IO. Defaults to Kubernetes::REST::LWPIO (LWP::UserAgent).
To use HTTP::Tiny instead:
use Kubernetes::REST::HTTPTinyIO;
my $api = Kubernetes::REST->new(
...,
io => Kubernetes::REST::HTTPTinyIO->new(...),
);
See "PLUGGABLE IO ARCHITECTURE" for custom backends.
k8s
IO::K8s instance configured with the same resource map. Automatically created when needed.
Provides delegated methods: new_object, inflate, json_to_object, struct_to_object, expand_class.
resource_map_from_cluster
Boolean. If true, dynamically loads the resource map from the cluster's OpenAPI spec. Defaults to 1.
Set to 0 to use IO::K8s built-in resource map instead (faster startup, but may not match your cluster version).
cluster_version
Read-only. The Kubernetes cluster version string (e.g., v1.31.0). Fetched automatically from the /version endpoint when first accessed.
resource_map
Hashref mapping short resource names to IO::K8s class paths. By default loads dynamically from the cluster (if resource_map_from_cluster is true) or uses IO::K8s built-in map.
Override for custom resources:
resource_map => {
%{ IO::K8s->default_resource_map },
MyResource => '+My::K8s::V1::MyResource',
}
The + prefix tells IO::K8s that this is a custom class (not in the IO::K8s:: namespace).
fetch_resource_map
my $map = $api->fetch_resource_map;
Fetch the resource map from the cluster's OpenAPI spec (/openapi/v2 endpoint). Returns a hashref mapping short resource names (e.g., Pod) to full IO::K8s class paths.
Called automatically if resource_map_from_cluster is enabled.
schema_for
my $schema = $api->schema_for('Pod');
Get the OpenAPI schema definition for a resource type from the cluster. Accepts short names (Pod), full class names (IO::K8s::Api::Core::V1::Pod), or OpenAPI definition names (io.k8s.api.core.v1.Pod).
Returns a hashref with the OpenAPI v2 schema definition.
compare_schema
my $result = $api->compare_schema('Pod');
Compare the local IO::K8s class definition against the cluster's OpenAPI schema. Useful for detecting version skew between your IO::K8s installation and the cluster.
Returns the comparison result from IO::K8s::Resource->compare_to_schema.
build_path
my $class = $api->expand_class('Pod');
my $path = $api->build_path($class, name => 'my-pod', namespace => 'default');
# => /api/v1/namespaces/default/pods/my-pod
Build the REST API URL path for a resource class. Takes a fully-qualified class name (from "expand_class") and optional name/namespace arguments.
This is a public API for async wrappers like Net::Async::Kubernetes that need to construct request paths independently.
prepare_request
my $req = $api->prepare_request('GET', $path,
parameters => \%params,
body => \%body,
);
Build a Kubernetes::REST::HTTPRequest with method, full URL, authorization headers, and optional query parameters or JSON body.
Query parameter values may be scalars or arrayrefs (arrayrefs are emitted as repeated key=value pairs). Extra request headers can be provided via headers => \%headers.
This is a public API for async wrappers that execute HTTP requests through their own event loop.
check_response
$api->check_response($response, "get Pod");
Validate an HTTP response. Croaks with a descriptive error if the status code is >= 400. Returns the response on success.
inflate_object
my $pod = $api->inflate_object($class, $response);
Decode the JSON response body and inflate it into a typed IO::K8s object.
inflate_list
my $list = $api->inflate_list($class, $response);
Decode the JSON response body and inflate the items array into an IO::K8s::List of typed objects.
process_watch_chunk
my @results = $api->process_watch_chunk($class, \$buffer, $chunk);
Process a chunk of NDJSON watch data. Appends the chunk to the buffer, extracts complete lines, and returns a list of hashrefs with event (Kubernetes::REST::WatchEvent), resourceVersion, is_error, and error_code.
This is a public API for async wrappers that handle streaming watch responses through their own event loop.
process_log_chunk
my @events = $api->process_log_chunk(\$buffer, $chunk);
Process a chunk of plain-text log data. Appends the chunk to the buffer, extracts complete lines, and returns a list of Kubernetes::REST::LogEvent objects.
This is a public API for async wrappers that handle streaming log responses through their own event loop.
list
my $list = $api->list('Pod', namespace => 'default');
my $list = $api->list('Namespace', labelSelector => 'app=web');
List resources. Returns an IO::K8s::List object.
Accepts short class names (Pod) or full class paths. For namespaced resources, pass namespace parameter. Omit namespace to list cluster-scoped resources.
Supports labelSelector and fieldSelector query parameters for server-side filtering.
get
my $pod = $api->get('Pod', name => 'my-pod', namespace => 'default');
# or shorthand:
my $pod = $api->get('Pod', 'my-pod', namespace => 'default');
Get a single resource by name. Returns a typed IO::K8s object.
create
my $created = $api->create($pod);
Create a resource from an IO::K8s object. Returns the created object with server-assigned fields (UID, resourceVersion, etc.).
update
my $updated = $api->update($pod);
Update an existing resource. Replaces the entire object server-side. Returns the updated object.
For partial updates, use "patch" instead.
patch
my $patched = $api->patch('Pod', 'my-pod',
namespace => 'default',
patch => { metadata => { labels => { env => 'staging' } } },
);
# Or with an object:
my $patched = $api->patch($pod,
patch => { metadata => { labels => { env => 'staging' } } },
);
Partially update a resource. Unlike update() which replaces the entire object, patch() only modifies specified fields.
Supports three patch strategies via the type parameter:
strategic(default) - Strategic Merge Patch (Kubernetes-native, understands array merge semantics)merge- JSON Merge Patch (RFC 7396, simple recursive merge)json- JSON Patch (RFC 6902, array of operations)
See "patch" in Kubernetes::REST for detailed examples.
delete
$api->delete($pod);
# or by name:
$api->delete('Pod', name => 'my-pod', namespace => 'default');
# or shorthand:
$api->delete('Pod', 'my-pod', namespace => 'default');
Delete a resource. Returns true on success.
ensure
my $obj = $api->ensure($pod);
# or from a plain hashref (treated as a Kubernetes manifest):
my $secret = $api->ensure({
apiVersion => 'v1',
kind => 'Secret',
metadata => { name => 'foo', namespace => 'default' },
stringData => { password => 'hunter2' },
});
Idempotent create-or-update. Fetches the resource by kind/name/namespace; if it exists, updates it (preserving resourceVersion), otherwise creates it. Returns the resulting IO::K8s object.
Accepts either a typed IO::K8s object or a plain hashref. A hashref must carry a kind field and is inflated to a typed object via "struct_to_object" in IO::K8s. Hashref keys follow the Kubernetes API convention (camelCase, e.g. stringData, not string_data).
Handles common race conditions:
404 on initial get is treated as "does not exist" and falls through to create.
409 AlreadyExists on create (resource appeared between get and create) is retried as an update.
409 Conflict on update (resourceVersion changed server-side, e.g. a controller wrote status) is retried by re-fetching and re-applying.
Special-cases for kinds with server-side mutation constraints:
PersistentVolumeClaim- spec is immutable after creation, so an existing PVC is returned unchanged.Job- spec is immutable; an existing Job that is active or has succeeded is returned unchanged. A failed Job is deleted and recreated.
ensure_all
my @results = $api->ensure_all(@objects);
Batch version of "ensure". Applies create-or-update to each object in order and returns the list of resulting objects.
ensure_only
$api->ensure_only(
label => 'app.kubernetes.io/component=queen',
objects => \@objects,
kinds => [qw(Role RoleBinding ClusterRoleBinding)],
namespaces => ['default', 'kube-system', undef],
);
Like "ensure_all", but also deletes any resources matching the label selector in the given kinds and namespaces that are not present in objects. Use this for resources where stale objects must not survive (e.g. RBAC).
Pass undef inside namespaces to scan cluster-scoped resources. If namespaces is omitted, only cluster-scoped resources are scanned.
Returns the list of applied objects (from "ensure_all").
watch
my $last_rv = $api->watch('Pod',
namespace => 'default',
on_event => sub {
my ($event) = @_;
say $event->type . ": " . $event->object->metadata->name;
},
timeout => 300,
resourceVersion => '12345',
labelSelector => 'app=web',
fieldSelector => 'status.phase=Running',
);
Watch for changes to resources. Uses the Kubernetes Watch API with chunked transfer encoding to stream events. The call blocks until the server-side timeout expires.
Returns the last resourceVersion seen. Croaks on 410 Gone (resourceVersion too old).
See "watch" in Kubernetes::REST for detailed documentation and resumable watch patterns.
log
# One-shot: get full log as string
my $text = $api->log('Pod', 'my-pod',
namespace => 'default',
tailLines => 100,
);
# Streaming: callback per log line
$api->log('Pod', 'my-pod',
namespace => 'default',
follow => 1,
on_line => sub {
my ($event) = @_; # Kubernetes::REST::LogEvent
say $event->line;
},
);
Retrieve logs from a pod. Supports two modes:
One-shot (without on_line): Returns the full log text as a string.
Streaming (with on_line): Calls the callback for each log line with a Kubernetes::REST::LogEvent object. Blocks until the stream ends (or the server closes the connection).
The streaming mode is designed for event-based systems like IO::Async — see Net::Async::Kubernetes for async integration.
port_forward
my $session = $api->port_forward('Pod', 'my-pod',
namespace => 'default',
ports => [8080, 8443],
on_frame => sub { my ($channel, $payload) = @_; ... },
);
Start a full-duplex pod port-forward session.
This method requires an IO backend that implements call_duplex. The default Kubernetes::REST::LWPIO and Kubernetes::REST::HTTPTinyIO backends do not currently provide duplex transport.
Returns whatever the IO backend returns for call_duplex (typically a session/handle object managed by that backend).
exec
my $session = $api->exec('Pod', 'my-pod',
namespace => 'default',
command => ['sh', '-c', 'echo hello'],
stdin => 0,
stdout => 1,
stderr => 1,
tty => 0,
on_frame => sub { my ($channel, $payload) = @_; ... },
);
Start a full-duplex pod exec session via the /exec subresource.
This method requires an IO backend that implements call_duplex. The default Kubernetes::REST::LWPIO and Kubernetes::REST::HTTPTinyIO backends do not currently provide duplex transport.
Returns whatever the IO backend returns for call_duplex (typically a session/handle object managed by that backend).
attach
my $session = $api->attach('Pod', 'my-pod',
namespace => 'default',
container => 'app',
stdin => 1,
stdout => 1,
stderr => 1,
tty => 0,
on_frame => sub { my ($channel, $payload) = @_; ... },
);
Start a full-duplex pod attach session via the /attach subresource.
This method requires an IO backend that implements call_duplex. The default Kubernetes::REST::LWPIO and Kubernetes::REST::HTTPTinyIO backends do not currently provide duplex transport.
Returns whatever the IO backend returns for call_duplex (typically a session/handle object managed by that backend).
NAME
Kubernetes::REST - A Perl REST Client for the Kubernetes API
UPGRADING FROM 0.02
WARNING: Version 1.00 contains breaking changes!
This version has been completely rewritten. Key changes that may affect your code:
New simplified API
The old method-per-operation API (e.g.,
$api->Core->ListNamespacedPod(...)) has been replaced with a simple API:list,get,create,update,patch,delete,ensure,ensure_all,ensure_only,watch,log,port_forward,exec,attach.Old API still works but deprecated
The old API is still available for backwards compatibility but will emit deprecation warnings. Set
$ENV{HIDE_KUBERNETES_REST_V0_API_WARNING}to suppress warnings.Uses IO::K8s classes
Results are now returned as typed IO::K8s objects instead of raw hashrefs. Lists are returned as IO::K8s::List objects.
Note: IO::K8s has also been completely rewritten (Moose to Moo, updated to Kubernetes v1.31 API). See "UPGRADING FROM 0.04" in IO::K8s for details.
Short resource names
You can now use short names like
'Pod'instead of full class paths. Theresource_mapattribute controls this mapping.Dynamic resource map
Use
resource_map_from_cluster => 1to load the resource map from the cluster's OpenAPI spec, ensuring compatibility with any Kubernetes version.
ATTRIBUTES
server
Required. Connection details for the Kubernetes API server. Can be a hashref or a Kubernetes::REST::Server object.
server => { endpoint => 'https://kubernetes.local:6443' }
credentials
Required. Authentication credentials. Can be a hashref or a Kubernetes::REST::AuthToken object.
credentials => { token => $bearer_token }
io
Optional. HTTP backend for making requests. Must consume the Kubernetes::REST::Role::IO role (i.e. implement call($req) and call_streaming($req, $callback); optional call_duplex($req, %callbacks) for full-duplex subresources such as pod port-forward). Defaults to Kubernetes::REST::LWPIO (LWP::UserAgent), which supports LWP::ConsoleLogger for HTTP debugging.
To use the lighter HTTP::Tiny backend instead:
use Kubernetes::REST::HTTPTinyIO;
my $api = Kubernetes::REST->new(
server => ...,
credentials => ...,
io => Kubernetes::REST::HTTPTinyIO->new(
ssl_verify_server => 1,
),
);
To use an async event loop, provide your own IO backend:
my $api = Kubernetes::REST->new(
server => ...,
credentials => ...,
io => My::AsyncIO->new(loop => $loop),
);
k8s
Optional. IO::K8s instance configured with the same resource map as this client. Automatically created when needed.
resource_map_from_cluster
Optional boolean. If true, loads the resource map dynamically from the cluster's OpenAPI spec. Defaults to true (loads from cluster).
resource_map_from_cluster => 1
resource_map
Optional hashref. Maps short resource names to IO::K8s class paths. By default loads dynamically from the cluster (if resource_map_from_cluster is true) or uses IO::K8s built-in map. Can be overridden for custom resources.
resource_map => { MyResource => 'Custom::V1::MyResource' }
cluster_version
Read-only. The Kubernetes cluster version string (e.g., "v1.31.0"). Fetched automatically from the /version endpoint when first accessed.
METHODS
new_object($class, \%attrs) or new_object($class, %attrs)
Create a new IO::K8s object. Accepts short class names (e.g., 'Pod', 'Namespace') and either a hashref or a hash of attributes.
# With hashref
my $ns = $api->new_object(Namespace => { metadata => { name => 'foo' } });
# With hash
my $ns = $api->new_object(Namespace => metadata => { name => 'foo' });
list($class, %args)
List resources. Returns an IO::K8s::List.
my $pods = $api->list('Pod', namespace => 'default');
get($class, %args)
Get a single resource by name.
my $pod = $api->get('Pod', name => 'my-pod', namespace => 'default');
create($object)
Create a resource from an IO::K8s object.
my $created = $api->create($pod);
update($object)
Update an existing resource.
my $updated = $api->update($pod);
patch($class_or_object, %args)
Partially update a resource. Unlike update() which replaces the entire object, patch() only modifies the fields you specify.
# Add a label (strategic merge patch - default)
my $patched = $api->patch('Pod', 'my-pod',
namespace => 'default',
patch => { metadata => { labels => { env => 'staging' } } },
);
# Same thing with an object reference
my $patched = $api->patch($pod,
patch => { metadata => { labels => { env => 'staging' } } },
);
# Explicit patch type
my $patched = $api->patch('Deployment', 'my-app',
namespace => 'default',
type => 'merge',
patch => { spec => { replicas => 5 } },
);
Required arguments:
- patch
-
A hashref (or arrayref for JSON Patch) describing the changes to apply.
- name
-
The resource name (when using class name, not object reference).
Optional arguments:
- type
-
The patch strategy. One of:
strategic(default)-
Strategic Merge Patch. The Kubernetes-native patch type that understands array merge semantics (e.g., adding a container to a pod spec without removing existing containers).
merge-
JSON Merge Patch (RFC 7396). Simple recursive merge where
nullvalues delete keys. Arrays are replaced entirely. json-
JSON Patch (RFC 6902). An array of operations:
patch => [ { op => 'replace', path => '/spec/replicas', value => 3 }, { op => 'add', path => '/metadata/labels/env', value => 'prod' }, ]
- namespace
-
For namespaced resources, the namespace.
Returns the full updated object from the server.
delete($class_or_object, %args)
Delete a resource.
$api->delete($pod);
$api->delete('Pod', name => 'my-pod', namespace => 'default');
watch($class, %args)
Watch for changes to resources. Uses the Kubernetes Watch API with chunked transfer encoding to stream events. The call blocks until the server-side timeout expires.
my $last_rv = $api->watch('Pod',
namespace => 'default',
on_event => sub {
my ($event) = @_;
say $event->type; # ADDED, MODIFIED, DELETED
say $event->object->metadata->name; # inflated IO::K8s object
},
timeout => 300, # server-side timeout (default: 300)
resourceVersion => '12345', # resume from this version
labelSelector => 'app=web', # optional label filter
fieldSelector => 'status.phase=Running', # optional field filter
);
# $last_rv is the last resourceVersion seen - use it to resume watching
Required arguments:
- on_event
-
Callback called for each watch event with a Kubernetes::REST::WatchEvent object.
Optional arguments:
- timeout
-
Server-side timeout in seconds (default: 300). The API server will close the connection after this many seconds.
- resourceVersion
-
Resume watching from a specific resource version. Use the return value from a previous
watch()call to avoid missing events. - labelSelector
-
Filter by label selector (e.g.,
'app=web,env=prod'). - fieldSelector
-
Filter by field selector (e.g.,
'status.phase=Running'). - namespace
-
For namespaced resources, the namespace to watch.
Resumable watch pattern:
my $rv;
while (1) {
$rv = eval {
$api->watch('Pod',
namespace => 'default',
resourceVersion => $rv,
on_event => \&handle_event,
);
};
if ($@ && $@ =~ /410 Gone/) {
# resourceVersion expired, re-list to get fresh version
my $list = $api->list('Pod', namespace => 'default');
$rv = undef; # start fresh
}
}
Returns the last resourceVersion seen. Croaks on 410 Gone with a message to re-list.
log($class, $name, %args)
Retrieve logs from a pod. Two modes:
One-shot (without on_line): Returns the full log text as a string.
my $text = $api->log('Pod', 'my-pod',
namespace => 'default',
tailLines => 100,
);
Streaming (with on_line): Calls the callback for each log line with a Kubernetes::REST::LogEvent object. Blocks until the stream ends.
$api->log('Pod', 'my-pod',
namespace => 'default',
follow => 1,
on_line => sub {
my ($event) = @_;
say $event->line;
},
);
Optional arguments:
- container - Container name (for multi-container pods)
- follow - Stream logs (like
kubectl logs -f) - tailLines - Number of lines from the end to show
- sinceSeconds - Logs from the last N seconds
- sinceTime - Logs since RFC3339 timestamp
- timestamps - Prepend timestamps to each line
- previous - Logs from the previous container restart
- limitBytes - Byte limit for the response
port_forward($class, $name, %args)
Start a Kubernetes pod port-forward session via the /portforward subresource.
my $session = $api->port_forward('Pod', 'my-pod',
namespace => 'default',
ports => [8080, 8443],
on_frame => sub { my ($channel, $payload) = @_; ... },
on_close => sub { ... },
on_error => sub { my ($err) = @_; ... },
);
Required arguments:
Optional arguments:
- namespace - Namespace (for namespaced resources)
- subprotocol - WebSocket subprotocol (default:
v4.channel.k8s.io) - on_open, on_frame, on_close, on_error - Duplex transport callbacks passed to IO backend
Requires an IO backend implementing call_duplex. The default sync backends currently do not provide duplex transport.
exec($class, $name, %args)
Start a Kubernetes pod exec session via the /exec subresource.
my $session = $api->exec('Pod', 'my-pod',
namespace => 'default',
command => ['sh', '-c', 'echo hello'],
on_frame => sub { my ($channel, $payload) = @_; ... },
on_close => sub { ... },
on_error => sub { my ($err) = @_; ... },
);
Required arguments:
Optional arguments:
- namespace - Namespace (for namespaced resources)
- container - Container name (for multi-container pods)
- stdin, stdout, stderr, tty - Stream toggles (defaults: stdin=false, stdout=true, stderr=true, tty=false)
- subprotocol - WebSocket subprotocol (default:
v4.channel.k8s.io) - on_open, on_frame, on_close, on_error - Duplex transport callbacks passed to IO backend
Requires an IO backend implementing call_duplex. The default sync backends currently do not provide duplex transport.
attach($class, $name, %args)
Start a Kubernetes pod attach session via the /attach subresource.
my $session = $api->attach('Pod', 'my-pod',
namespace => 'default',
container => 'app',
stdin => 1,
stdout => 1,
stderr => 1,
tty => 0,
on_frame => sub { my ($channel, $payload) = @_; ... },
on_close => sub { ... },
on_error => sub { my ($err) = @_; ... },
);
Required arguments:
Optional arguments:
- namespace - Namespace (for namespaced resources)
- container - Container name (for multi-container pods)
- stdin, stdout, stderr, tty - Stream toggles (defaults: stdin=false, stdout=true, stderr=true, tty=false)
- subprotocol - WebSocket subprotocol (default:
v4.channel.k8s.io) - on_open, on_frame, on_close, on_error - Duplex transport callbacks passed to IO backend
Requires an IO backend implementing call_duplex. The default sync backends currently do not provide duplex transport.
fetch_resource_map()
Fetch the resource map from the cluster's OpenAPI spec (/openapi/v2 endpoint). Returns a hashref mapping short resource names (e.g., "Pod") to full IO::K8s class paths. This method is called automatically if resource_map_from_cluster is enabled.
BUILDING BLOCKS FOR ASYNC WRAPPERS
Async wrappers like Net::Async::Kubernetes need access to the request/response pipeline without going through the synchronous convenience methods. The following public methods provide this:
expand_class($short)- Resolve short name to full classbuild_path($class, %args)- Build REST API URL pathprepare_request($method, $path, %opts)- Build HTTP request with authcheck_response($response, $context)- Validate HTTP statusinflate_object($class, $response)- JSON to typed objectinflate_list($class, $response)- JSON to typed listprocess_watch_chunk($class, \$buf, $chunk)- Parse NDJSON watch streamprocess_log_chunk(\$buf, $chunk)- Parse plain-text log stream
Example async integration:
# Build request using Kubernetes::REST
my $class = $rest->expand_class('Pod');
my $path = $rest->build_path($class, name => $name, namespace => $ns) . '/log';
my $req = $rest->prepare_request('GET', $path, parameters => { follow => 'true' });
# Execute through your own event loop
my $buffer = '';
$async_http->request($req->url, sub {
my ($chunk) = @_;
for my $event ($rest->process_log_chunk(\$buffer, $chunk)) {
$on_line->($event);
}
});
PLUGGABLE IO ARCHITECTURE
The HTTP transport is decoupled from request preparation and response processing. This makes it possible to swap the default LWP::UserAgent backend for HTTP::Tiny or an async backend (e.g. Net::Async::HTTP) without changing any API logic.
The pipeline for each API call:
1. prepare_request() - builds HTTPRequest (method, url, headers, body)
2. io->call() - executes request (pluggable backend)
3. check_response() - validates HTTP status
4. inflate_object/list - decodes JSON + inflates IO::K8s objects
For watch, step 2 uses io->call_streaming() and step 4 uses process_watch_chunk() which parses NDJSON and inflates each event.
For log, step 2 uses io->call_streaming() and step 4 uses process_log_chunk() which parses plain-text lines into Kubernetes::REST::LogEvent objects.
To implement a custom IO backend, consume Kubernetes::REST::Role::IO and implement call($req) and call_streaming($req, $callback). See Kubernetes::REST::LWPIO and Kubernetes::REST::HTTPTinyIO for reference implementations.
SEE ALSO
Related Modules
IO::K8s - Kubernetes resource classes (required dependency)
Net::Async::Kubernetes - Async Kubernetes client for IO::Async
Configuration and Authentication
Kubernetes::REST::Kubeconfig - Load settings from kubeconfig
Kubernetes::REST::Server - Server connection configuration
Kubernetes::REST::AuthToken - Authentication credentials
HTTP Backends
Kubernetes::REST::Role::IO - IO interface role
Kubernetes::REST::LWPIO - LWP::UserAgent backend (default)
Kubernetes::REST::HTTPTinyIO - HTTP::Tiny backend
LWP::ConsoleLogger - HTTP debugging for LWPIO
Data Objects
Kubernetes::REST::WatchEvent - Watch event object
Kubernetes::REST::LogEvent - Log event object
Kubernetes::REST::HTTPRequest - HTTP request object
Kubernetes::REST::HTTPResponse - HTTP response object
CLI Tools
Kubernetes::REST::CLI - CLI base class
Kubernetes::REST::CLI::Watch - kube_watch CLI tool
Kubernetes::REST::CLI::Role::Connection - Shared CLI options
Examples and Documentation
Kubernetes::REST::Example - Comprehensive examples with Minikube/K3s
https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/ - Kubernetes API reference
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/pplu/kubernetes-rest/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.
AUTHORS
Torsten Raudssus <torsten@raudssus.de>
Jose Luis Martinez Torres <jlmartin@cpan.org>
COPYRIGHT AND LICENSE
This software is Copyright (c) 2019-2026 by Jose Luis Martinez Torres <jlmartin@cpan.org>.
This is free software, licensed under:
The Apache License, Version 2.0, January 2004