Kubernetes-REST
view release on metacpan or search on metacpan
.claude/skills/kubernetes-rest-core/SKILL.md view on Meta::CPAN
into a method.
- The 1002 old `Call::*` classes and 10 v0-only helpers no longer ship here â they are
tombstoned in the separate **Kubernetes-REST-Deprecated** distribution. This layer is
the last thing keeping the v0 names alive and can go once no downstream code uses them.
`Kubernetes::REST::Error` / `::RemoteError` belong to the same layer: v1 croaks, it does
not throw structured exceptions. `RemoteError` inherits from `Error`, so `Error.pm` cannot
load it â code that *throws* one must `use Kubernetes::REST::RemoteError` itself.
## One package per file, one `$VERSION` everywhere
`our $VERSION` appears in **every** `.pm` and in both `bin/` scripts, all identical. That
is correct: the `[@Author::GETTY]` bundle only narrows `version_finder` to `:MainModule`
for `no_cpan` dists, and this one ships to CPAN, so every package needs its own version
for PAUSE indexing. Never bump by hand â `RewriteVersion`/`BumpVersionAfterRelease` own it.
The corollary is a hard rule: **one `package` per file.** Those plugins rewrite only the
*first* `our $VERSION` per file, so a second package in a file silently keeps the version
it was written with â five packages here sat at 1.003 until 1.106 while the metadata
reported the release version. `t/25_one_package_per_file.t` pins both properties.
Every `.pm` also needs a `# ABSTRACT:` line; PodWeaver builds NAME from it.
## A new file has to be `git add`ed to exist
`[@Author::GETTY]` gathers through `Git::GatherDir`, and its `include_untracked` defaults
to false. An untracked file is therefore absent from `dzil build`, from the release test
gate and from the CPAN tarball â while `prove -lr t/` runs it and passes. Nothing warns:
the release simply never saw the test. `git add` a new test or module the moment you
create it.
## The test harness
`t/lib/Test/Kubernetes/Mock.pm` exports `mock_api`, `live_api`, `is_live`. `mock_api`
builds a real `Kubernetes::REST` with a mock IO backend consuming
`Kubernetes::REST::Role::IO` â the pipeline under test is the real one, only transport is
replaced.
- Fixture lookup: `lc(method) . path`, slashes â underscores, collapsed, leading one
stripped â `GET /api/v1/namespaces` â `t/mock/get_api_v1_namespaces.json`. Query strings
are stripped for matching. A miss returns a 404 Status body, so a missing fixture looks
like a real "not found"; `MOCK_DEBUG=1` prints the key being looked up.
- Programmatic responses (`add_response`, `add_watch_events`, `add_log_lines`) take
precedence over files â prefer them for new behaviour and reserve `t/mock/*.json` for
recorded cluster shapes.
- **The mock encodes with `utf8 => 1`** so it hands back bytes like a real backend. A mock
that returns characters makes the encoding tests pass for the wrong reason.
- Live tests require `TEST_KUBERNETES_REST_KUBECONFIG` â deliberately long, so no one
points the suite at production by accident. `TEST_KUBERNETES_REST_CONTEXT` picks the
context; `t/record_fixtures.pl` re-records fixtures from a live cluster.
## Kubeconfig and CLI
`Kubernetes::REST::Kubeconfig` parses the YAML and resolves cert data (inline base64 or a
file path) for the CA, client cert and client key independently. Inline base64 data is
decoded and passed to `Server` as an in-memory PEM string (`ssl_ca_pem`/`ssl_cert_pem`/
`ssl_key_pem`); a plain path is passed through as-is (`ssl_ca_file`/`ssl_cert_file`/
`ssl_key_file`). No temp file is written for inline data â the built `api` stays usable
after the `Kubeconfig` object is dropped. (Earlier versions wrote inline certs to temp
files tied to the `Kubeconfig` object's lifetime; that was removed because
`IO::Socket::SSL` doesn't accept scalar-ref cert files, and `t/14_kubeconfig.t` pins the
in-memory-PEM behavior surviving kubeconfig destruction â don't reintroduce temp files.)
It also handles `exec` credential plugins and in-cluster service-account config.
The CLI layer is `MooX::Cmd` + `MooX::Options`: `Kubernetes::REST::CLI` with
`CLI::Cmd::{Get,Create,Delete,Raw}`, plus the standalone `CLI::Watch`. Shared
`--kubeconfig`/`--context` options and the lazy `api` live in `CLI::Role::Connection`.
Entry points are `bin/kube_client` and `bin/kube_watch`. CLI JSON output sets `utf8` â
without it non-ASCII values trigger "Wide character in print".
## Duplex subresources
`port_forward`, `exec` and `attach` build a WebSocket-upgrade request
(`Sec-WebSocket-Protocol: v4.channel.k8s.io`) and hand it to `$io->call_duplex`. **Neither
shipped backend implements `call_duplex`** â LWP and HTTP::Tiny are sync-only, so these
methods croak with "IO backend does not support â¦" by design. `Net::Async::Kubernetes`
provides the duplex transport. Their tests therefore assert argument validation and
request shape, not a round trip.
( run in 1.737 second using v1.01-cache-2.11-cpan-c221a9de4ec )