Kubernetes-REST
view release on metacpan or search on metacpan
t/07_example_crud.t view on Meta::CPAN
is($deploy->metadata->name, 'my-app', 'name');
is($deploy->kind, 'Deployment', 'kind');
ok(defined $deploy->status, 'has status from server');
};
# === 7. Update Deployment (scale) ===
subtest 'update Deployment' => sub {
# Retry loop: Kubernetes controllers may mutate the deployment between
# get and update (e.g. LimitRange injecting defaults), causing a 409 Conflict.
my $updated;
for my $attempt (1..3) {
my $deploy = $api->get('Deployment', 'my-app', namespace => 'perl-test');
$deploy->spec->replicas(3);
$updated = eval { $api->update($deploy) };
last if $updated;
diag "Update attempt $attempt failed (409 conflict), retrying..." if $attempt < 3;
sleep 1 if is_live();
}
ok($updated, 'update returns object');
is($updated->spec->replicas, 3, 'replicas updated');
};
# === 8. Create Service ===
subtest 'create Service' => sub {
my $svc = $api->new_object(Service =>
metadata => {
name => 'my-app',
namespace => 'perl-test',
},
spec => {
type => 'NodePort',
selector => { app => 'my-app' },
ports => [{
port => 80,
targetPort => 80,
protocol => 'TCP',
}],
},
);
my $created = $api->create($svc);
ok($created, 'Service created');
is($created->metadata->name, 'my-app', 'name');
is($created->spec->type, 'NodePort', 'type');
ok($created->spec->ports->[0]->nodePort, 'has nodePort from server');
};
# === 9. Create Job ===
subtest 'create Job' => sub {
my $job = $api->new_object(Job =>
metadata => {
name => 'batch-job',
namespace => 'perl-test',
},
spec => {
backoffLimit => 2,
template => {
spec => {
restartPolicy => 'Never',
containers => [{
name => 'worker',
image => 'busybox:latest',
command => ['sh', '-c', 'echo "done"; exit 0'],
}],
},
},
},
);
my $created = $api->create($job);
ok($created, 'Job created');
is($created->metadata->name, 'batch-job', 'name');
is($created->kind, 'Job', 'kind');
is($created->api_version, 'batch/v1', 'apiVersion');
};
# === 10. Create CronJob ===
subtest 'create CronJob' => sub {
my $cron = $api->new_object(CronJob =>
metadata => {
name => 'scheduled-job',
namespace => 'perl-test',
},
spec => {
schedule => '*/5 * * * *',
jobTemplate => {
spec => {
template => {
spec => {
restartPolicy => 'OnFailure',
containers => [{
name => 'worker',
image => 'busybox:latest',
command => ['sh', '-c', 'date'],
}],
},
},
},
},
},
);
my $created = $api->create($cron);
ok($created, 'CronJob created');
is($created->metadata->name, 'scheduled-job', 'name');
is($created->spec->schedule, '*/5 * * * *', 'schedule');
};
# === 11. Create Role (RBAC) ===
subtest 'create Role' => sub {
my $role = $api->new_object(Role =>
metadata => {
name => 'pod-reader',
namespace => 'perl-test',
},
rules => [{
apiGroups => [''],
resources => ['pods'],
verbs => ['get', 'list', 'watch'],
}],
);
my $created = $api->create($role);
ok($created, 'Role created');
is($created->metadata->name, 'pod-reader', 'name');
is($created->kind, 'Role', 'kind');
is($created->api_version, 'rbac.authorization.k8s.io/v1', 'apiVersion');
};
# === 12. Create RoleBinding (RBAC) ===
subtest 'create RoleBinding' => sub {
my $binding = $api->new_object(RoleBinding =>
metadata => {
name => 'read-pods',
namespace => 'perl-test',
},
roleRef => {
apiGroup => 'rbac.authorization.k8s.io',
kind => 'Role',
name => 'pod-reader',
},
subjects => [{
kind => 'ServiceAccount',
name => 'default',
namespace => 'perl-test',
}],
);
my $created = $api->create($binding);
ok($created, 'RoleBinding created');
is($created->metadata->name, 'read-pods', 'name');
is($created->roleRef->name, 'pod-reader', 'roleRef');
};
# === 13. Create PVC ===
subtest 'create PersistentVolumeClaim' => sub {
t/07_example_crud.t view on Meta::CPAN
# === 15. Get ResourceQuota (with status) ===
subtest 'get ResourceQuota with usage' => sub {
# Kubernetes needs time to compute quota status after creation
my $q;
for my $attempt (1..10) {
$q = $api->get('ResourceQuota', 'ns-quota', namespace => 'perl-test');
last if $q && $q->status && $q->status->hard;
sleep 1 if is_live();
}
ok($q, 'got quota');
ok($q->status, 'has status');
ok($q->status->hard, 'has hard limits');
ok($q->status->used, 'has usage data');
my $hard = $q->status->hard;
my $used = $q->status->used;
ok(exists $hard->{pods}, 'hard has pods');
ok(exists $used->{pods}, 'used has pods');
};
# === 16. Delete operations ===
subtest 'delete CronJob' => sub {
ok($api->delete('CronJob', 'scheduled-job', namespace => 'perl-test'),
'CronJob deleted');
};
subtest 'delete Job' => sub {
ok($api->delete('Job', 'batch-job', namespace => 'perl-test'),
'Job deleted');
};
subtest 'delete Service' => sub {
ok($api->delete('Service', 'my-app', namespace => 'perl-test'),
'Service deleted');
};
subtest 'delete Deployment' => sub {
ok($api->delete('Deployment', 'my-app', namespace => 'perl-test'),
'Deployment deleted');
};
subtest 'delete Namespace' => sub {
ok($api->delete('Namespace', 'perl-test'),
'Namespace deleted');
};
# === 17. Serialization roundtrip ===
subtest 'serialization roundtrip' => sub {
my $deploy = $api->new_object(Deployment =>
metadata => {
name => 'roundtrip',
namespace => 'default',
},
spec => {
replicas => 5,
selector => { matchLabels => { app => 'rt' } },
template => {
metadata => { labels => { app => 'rt' } },
spec => {
containers => [{
name => 'test', image => 'busybox',
}],
},
},
},
);
# TO_JSON -> inflate roundtrip
my $data = $deploy->TO_JSON;
is($data->{kind}, 'Deployment', 'TO_JSON kind');
is($data->{apiVersion}, 'apps/v1', 'TO_JSON apiVersion');
my $rt = $api->inflate($data);
is($rt->metadata->name, 'roundtrip', 'roundtrip name');
is($rt->spec->replicas, 5, 'roundtrip replicas');
is($rt->kind, 'Deployment', 'roundtrip kind');
# YAML roundtrip
my $yaml = $deploy->to_yaml;
like($yaml, qr/kind:\s*Deployment/, 'YAML has kind');
like($yaml, qr/replicas:\s*5/, 'YAML has replicas');
};
done_testing;
( run in 0.561 second using v1.01-cache-2.11-cpan-39bf76dae61 )