Kubernetes-REST

 view release on metacpan or  search on metacpan

lib/Kubernetes/REST/Example.pm  view on Meta::CPAN

=head1 NAME

Kubernetes::REST::Example - Working examples for Kubernetes::REST with Minikube, K3s, and other clusters

=head1 CLUSTER SETUP

L<Kubernetes::REST> works with any Kubernetes cluster. Below are setup
instructions for common local development environments. All of them write
a kubeconfig to C<~/.kube/config> that L<Kubernetes::REST::Kubeconfig> picks
up automatically.

=head2 Minikube

L<Minikube|https://minikube.sigs.k8s.io/> creates a single-node cluster
inside a Docker container or VM.

    # Install (Debian/Ubuntu)
    curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
    sudo install minikube-linux-amd64 /usr/local/bin/minikube

    # Start with Docker driver
    minikube start --driver=docker

    # Verify
    minikube status

Minikube uses self-signed certificates and token authentication. The
kubeconfig context is named C<minikube>.

B<NodePort access:>

    minikube service <service-name> -n <namespace>

=head2 K3s

L<K3s|https://k3s.io/> is a lightweight Kubernetes distribution from Rancher.
It installs as a single binary and runs directly on the host (no Docker
required).

    # Install
    curl -sfL https://get.k3s.io | sh -

    # K3s writes its own kubeconfig to a different path
    export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

    # Or copy it so Kubernetes::REST::Kubeconfig finds it automatically
    mkdir -p ~/.kube
    sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
    sudo chown $USER ~/.kube/config
    chmod 600 ~/.kube/config

    # Verify
    kubectl get nodes

B<Key differences from Minikube:>

=over 4

=item * Kubeconfig lives at C</etc/rancher/k3s/k3s.yaml> (not C<~/.kube/config>)

=item * The server endpoint is C<https://127.0.0.1:6443>

=item * K3s includes Traefik as its default ingress controller

=item * K3s uses C<containerd> instead of Docker

=item * NodePort services are accessible directly on the host IP

=back

B<Connecting from Perl with K3s:>

    use Kubernetes::REST::Kubeconfig;

    # If you copied the kubeconfig to ~/.kube/config:
    my $api = Kubernetes::REST::Kubeconfig->new->api;

    # If using the K3s path directly:
    my $api = Kubernetes::REST::Kubeconfig->new(
        kubeconfig_path => '/etc/rancher/k3s/k3s.yaml',
    )->api;

=head2 Kind (Kubernetes in Docker)

L<Kind|https://kind.sigs.k8s.io/> runs Kubernetes nodes as Docker containers.
Good for CI and testing.

    # Install
    go install sigs.k8s.io/kind@latest

    # Create cluster
    kind create cluster --name perl-test

    # Context is named: kind-perl-test
    my $api = Kubernetes::REST::Kubeconfig->new(
        context_name => 'kind-perl-test',
    )->api;

=head2 Any cluster with a kubeconfig

If you have a kubeconfig (EKS, GKE, AKS, self-managed, etc.):

    use Kubernetes::REST::Kubeconfig;

    # Uses current context from ~/.kube/config
    my $api = Kubernetes::REST::Kubeconfig->new->api;

    # Or specify a context
    my $api = Kubernetes::REST::Kubeconfig->new(
        context_name => 'my-production-cluster',
    )->api;

=head1 INSTALL PERL DEPENDENCIES

    cpanm Kubernetes::REST

=head1 CONNECTING TO THE CLUSTER

=head2 Using the kubeconfig (recommended)

L<Kubernetes::REST::Kubeconfig> parses your kubeconfig and sets up
authentication, SSL certificates, and the server endpoint automatically:

    use Kubernetes::REST::Kubeconfig;

    my $kc  = Kubernetes::REST::Kubeconfig->new;
    my $api = $kc->api;

    say "Cluster version: " . $api->cluster_version;

If you have multiple contexts:

    my $contexts = $kc->contexts;
    say "Available: @$contexts";

    my $api = $kc->api('minikube');  # or 'default', 'kind-test', etc.

=head2 Manual configuration

    use Kubernetes::REST;

    my $api = Kubernetes::REST->new(
        server => {
            endpoint          => 'https://127.0.0.1:6443',
            ssl_verify_server => 0,
        },
        credentials => { token => $token },
        resource_map_from_cluster => 0,
    );

=head1 LISTING RESOURCES

=head2 Namespaces

    my $list = $api->list('Namespace');

    for my $ns (@{ $list->items }) {
        printf "%-20s %s\n",
            $ns->metadata->name,
            $ns->status->phase // 'Unknown';
    }

=head2 Pods in a namespace

    my $pods = $api->list('Pod', namespace => 'kube-system');

    for my $pod (@{ $pods->items }) {
        my $name   = $pod->metadata->name;
        my $phase  = $pod->status->phase // 'Unknown';
        my $ip     = $pod->status->podIP // 'pending';
        printf "%-45s %-10s %s\n", $name, $phase, $ip;
    }

=head2 Services

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

    for my $svc (@{ $services->items }) {
        my $name  = $svc->metadata->name;
        my $type  = $svc->spec->type // 'ClusterIP';
        my $ports = join ', ', map {
            $_->port . '/' . ($_->protocol // 'TCP')
        } @{ $svc->spec->ports // [] };
        printf "%-20s %-12s %s\n", $name, $type, $ports;
    }

=head2 Nodes

    my $nodes = $api->list('Node');

    for my $node (@{ $nodes->items }) {
        my $info = $node->status->nodeInfo;
        printf "%-20s OS=%-8s kubelet=%s\n",
            $node->metadata->name,
            $info->operatingSystem // '?',
            $info->kubeletVersion // '?';
    }

=head1 GETTING A SINGLE RESOURCE

C<get()> returns a fully typed L<IO::K8s> object:

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

lib/Kubernetes/REST/Example.pm  view on Meta::CPAN

                  properties:
                    domain:   { type: string }
                    image:    { type: string }
                    replicas: { type: integer, default: 1 }
                    tls:      { type: boolean, default: false }
                status:
                  type: object
                  properties:
                    readyReplicas: { type: integer }
                    url:           { type: string }
      scope: Namespaced
      names:
        plural: staticwebsites
        singular: staticwebsite
        kind: StaticWebSite
        shortNames: [sw]

    $ kubectl apply -f staticwebsite-crd.yaml

=head2 Writing a CRD class

Create a Perl class using C<IO::K8s::APIObject> with import parameters
for C<api_version> and C<resource_plural>:

    package My::StaticWebSite;
    use IO::K8s::APIObject
        api_version     => 'homelab.example.com/v1',
        resource_plural => 'staticwebsites';
    with 'IO::K8s::Role::Namespaced';

    k8s spec   => { Str => 1 };
    k8s status => { Str => 1 };
    1;

Key points:

=over 4

=item * C<api_version> - the CRD's C<group/version> (e.g. C<homelab.example.com/v1>)

=item * C<resource_plural> - must match the CRD's C<spec.names.plural>

=item * C<kind> is derived automatically from the class name (last C<::> segment)

=item * Apply C<IO::K8s::Role::Namespaced> for namespace-scoped CRDs

=item * C<spec> and C<status> are opaque hashrefs (C<< { Str => 1 } >>)

=back

=head2 Registering the CRD class

Register your class with the C<+> prefix in the resource map:

    my $api = Kubernetes::REST::Kubeconfig->new->api;
    $api->resource_map->{StaticWebSite} = '+My::StaticWebSite';

Or pass it at construction time:

    my $api = Kubernetes::REST->new(
        server      => { endpoint => '...' },
        credentials => { token => '...' },
        resource_map => {
            %{ IO::K8s->default_resource_map },
            StaticWebSite => '+My::StaticWebSite',
        },
    );

=head2 CRUD on Custom Resources

Once registered, CRDs work exactly like built-in resources:

    use JSON::PP ();

    # Create
    my $site = $api->create($api->new_object(StaticWebSite =>
        metadata => {
            name      => 'my-blog',
            namespace => 'default',
        },
        spec => {
            domain   => 'blog.example.com',
            image    => 'nginx:1.27-alpine',
            replicas => 2,
            tls      => JSON::PP::true,
        },
    ));

    # Get
    my $site = $api->get('StaticWebSite', 'my-blog', namespace => 'default');
    say $site->spec->{domain};    # blog.example.com

    # List
    my $list = $api->list('StaticWebSite', namespace => 'default');
    for my $s (@{ $list->items }) {
        say $s->metadata->name . ": " . $s->spec->{domain};
    }

    # Update
    $site->spec->{replicas} = 3;
    $api->update($site);

    # Delete
    $api->delete('StaticWebSite', 'my-blog', namespace => 'default');

B<Note:> For boolean CRD fields, use C<JSON::PP::true> and C<JSON::PP::false>
instead of C<1> and C<0>. The Kubernetes API validates JSON types strictly
for custom resources.

=head2 Serialization

CRD objects support the same serialization as built-in resources:

    say $site->to_yaml;
    # ---
    # apiVersion: homelab.example.com/v1
    # kind: StaticWebSite
    # metadata:
    #   name: my-blog
    #   namespace: default
    # spec:



( run in 0.837 second using v1.01-cache-2.11-cpan-524268b4103 )