Kubernetes-REST

 view release on metacpan or  search on metacpan

eg/demo.pl  view on Meta::CPAN

# ============================================================
# 14. Rolling Update
# ============================================================
say "\n" . "=" x 60;
say "  14. ROLLING UPDATE";
say "=" x 60;

say "\n--- Updating image to nginx:1.27-bookworm ---";
my $rolled;
for my $attempt (1..5) {
    my $dep = $api->get('Deployment', 'demo-nginx', namespace => $NS);
    my $old_image = $dep->spec->template->spec->containers->[0]->image;
    say "  Old image: $old_image" if $attempt == 1;

    # Update the container image
    $dep->spec->template->spec->containers->[0]->image('nginx:1.27-bookworm');
    # Add annotation to track the change
    my $tpl_annotations = $dep->spec->template->metadata->annotations // {};
    $tpl_annotations->{'kubernetes.io/change-cause'} = 'Updated nginx to bookworm variant via Perl';
    $dep->spec->template->metadata->annotations($tpl_annotations);

    $rolled = eval { $api->update($dep) };
    last if $rolled;
    say "  Attempt $attempt conflict, retrying...";
    sleep 1;
}
die "Failed to update deployment after 5 attempts\n" unless $rolled;
say "  New image: " . $rolled->spec->template->spec->containers->[0]->image;

wait_for("rolling update to complete", sub {
    my $d = $api->get('Deployment', 'demo-nginx', namespace => $NS);
    my $updated = $d->status->updatedReplicas // 0;
    my $ready   = $d->status->readyReplicas // 0;
    my $desired = $d->spec->replicas // 3;
    return ($updated == $desired && $ready == $desired) ? 1 : 0;
}, 90);

# ============================================================
# 15. Job
# ============================================================
say "\n" . "=" x 60;
say "  15. JOB (run to completion)";
say "=" x 60;

say "\n--- Creating Job ---";
my $job = create_or_get($api, 'Job', 'demo-job',
    $api->new_object(Job =>
        metadata => {
            name      => 'demo-job',
            namespace => $NS,
            labels    => { app => 'demo-app', type => 'batch' },
        },
        spec => {
            backoffLimit          => 2,
            ttlSecondsAfterFinished => 300,
            template => {
                spec => {
                    restartPolicy => 'Never',
                    containers => [{
                        name    => 'worker',
                        image   => 'busybox:latest',
                        command => ['sh', '-c',
                            'echo "Job started at $(date)"; '
                            . 'echo "Processing items..."; '
                            . 'for i in 1 2 3 4 5; do echo "  Item $i done"; sleep 1; done; '
                            . 'echo "Job completed at $(date)"'
                        ],
                        resources => {
                            requests => { cpu => '25m', memory => '16Mi' },
                            limits   => { cpu => '50m', memory => '32Mi' },
                        },
                    }],
                },
            },
        },
    ),
    namespace => $NS,
);
say "  Job: " . $job->metadata->name;

wait_for("job to complete", sub {
    my $j = $api->get('Job', 'demo-job', namespace => $NS);
    return ($j->status->succeeded // 0) >= 1 ? 1 : 0;
}, 60);

my $j = $api->get('Job', 'demo-job', namespace => $NS);
say "  Succeeded: " . ($j->status->succeeded // 0);
say "  Completion: " . ($j->status->completionTime // 'pending');

# ============================================================
# 16. CronJob
# ============================================================
say "\n" . "=" x 60;
say "  16. CRONJOB";
say "=" x 60;

say "\n--- Creating CronJob (every minute) ---";
my $cron = create_or_get($api, 'CronJob', 'demo-cronjob',
    $api->new_object(CronJob =>
        metadata => {
            name      => 'demo-cronjob',
            namespace => $NS,
            labels    => { app => 'demo-app', type => 'scheduled' },
        },
        spec => {
            schedule                   => '*/1 * * * *',
            successfulJobsHistoryLimit => 2,
            failedJobsHistoryLimit     => 1,
            jobTemplate => {
                spec => {
                    template => {
                        spec => {
                            restartPolicy => 'OnFailure',
                            containers => [{
                                name    => 'cron-worker',
                                image   => 'busybox:latest',
                                command => ['sh', '-c', 'echo "Cron tick at $(date)"'],
                                resources => {
                                    requests => { cpu => '10m', memory => '8Mi' },
                                    limits   => { cpu => '25m', memory => '16Mi' },
                                },
                            }],
                        },
                    },
                },
            },
        },
    ),
    namespace => $NS,
);
say "  CronJob: " . $cron->metadata->name;
say "  Schedule: " . $cron->spec->schedule;

# ============================================================
# 17. Utility Pod
# ============================================================
say "\n" . "=" x 60;
say "  17. UTILITY POD";
say "=" x 60;

say "\n--- Creating utility pod with all config ---";
my $util_pod = create_or_get($api, 'Pod', 'demo-utility',
    $api->new_object(Pod =>
        metadata => {
            name      => 'demo-utility',
            namespace => $NS,
            labels    => { app => 'demo-app', component => 'utility' },
            annotations => {
                'purpose' => 'Demonstrates ConfigMap + Secret access from a pod',
            },
        },
        spec => {
            serviceAccountName => 'demo-sa',
            restartPolicy => 'Never',
            containers => [{
                name    => 'util',
                image   => 'busybox:latest',
                command => ['sh', '-c', join('; ',
                    'echo "=== Environment ==="',
                    'echo "APP_ENV=$APP_ENV"',
                    'echo "LOG_LEVEL=$LOG_LEVEL"',
                    'echo "DB_HOST=$DB_HOST"',
                    'echo ""',
                    'echo "=== Config Files ==="',
                    'echo "nginx.conf:"',
                    'cat /config/nginx.conf',
                    'echo ""',
                    'echo "=== Persistent Storage ==="',
                    'echo "Hello from Perl" > /data/test.txt',
                    'cat /data/test.txt',
                    'echo ""',
                    'echo "=== Service Account ==="',
                    'ls /var/run/secrets/kubernetes.io/serviceaccount/',
                    'sleep 120',
                )],
                env => [{
                    name => 'APP_ENV',
                    valueFrom => {
                        configMapKeyRef => { name => 'app-config', key => 'app.env' },
                    },
                }, {
                    name => 'LOG_LEVEL',
                    valueFrom => {
                        configMapKeyRef => { name => 'app-config', key => 'app.log_level' },
                    },
                }, {
                    name => 'DB_HOST',
                    valueFrom => {
                        secretKeyRef => { name => 'db-credentials', key => 'host' },
                    },
                }],
                volumeMounts => [{
                    name      => 'config',
                    mountPath => '/config',
                }, {
                    name      => 'data',
                    mountPath => '/data',
                }],
                resources => {
                    requests => { cpu => '10m', memory => '8Mi' },
                    limits   => { cpu => '25m', memory => '16Mi' },
                },
            }],
            volumes => [{
                name => 'config',
                configMap => { name => 'app-config' },
            }, {
                name => 'data',
                persistentVolumeClaim => { claimName => 'demo-storage' },
            }],
        },
    ),
    namespace => $NS,
);
say "  Utility Pod: " . $util_pod->metadata->name;

wait_for("utility pod to be running", sub {



( run in 0.540 second using v1.01-cache-2.11-cpan-39bf76dae61 )