NAME

Kubernetes::REST - A Perl REST Client for the Kubernetes API

VERSION

version 1.000

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.

list

my $list = $api->list('Pod', namespace => 'default');

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.

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.

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. The resource_map attribute controls this mapping.

  • Dynamic resource map

    Use resource_map_from_cluster => 1 to 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 null values 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.

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.

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.

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

Configuration and Authentication

HTTP Backends

Data Objects

CLI Tools

Examples and Documentation

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