NAME
Kubernetes::REST - A Perl REST Client for the Kubernetes API
VERSION
version 1.100
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');
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.
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.
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.
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,watch.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)). 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
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> (JLMARTIN, original author, inactive)
COPYRIGHT AND LICENSE
This software is Copyright (c) 2019 by Jose Luis Martinez.
This is free software, licensed under:
The Apache License, Version 2.0, January 2004