Revision history for Net-Async-Kubernetes

0.008     2026-09-22 04:13:56Z
 - Add public `rest` and `new_object($kind, %fields)` on the client: the
   underlying `Kubernetes::REST` instance and a typed `IO::K8s` object for
   `create`/`update`, so callers no longer reach through the private `_rest`.
 - Require `IO::K8s` 1.108 and `Kubernetes::REST` 1.108. Older IO::K8s
   resolved a bare Kind such as `Role`, `Event`, `Job` or `Service` to any
   loadable top-level package of that name -- `Class::Mite` ships a
   `lib/Role.pm` -- so creating the object died with `Can't locate object
   method "_k8s_attr_info"` on smokers carrying such a module (GitHub issue
   #2). The pair also brings UTF-8 request bodies (a manifest with a
   non-ASCII character died with "HTTP::Message content must be bytes"), the
   right API group paths for Storagemigration and Apiserverinternal, working
   inflation of KubeAggregator's `APIService`, and the upstream Kubernetes
   v1.37 sync with 11 new Kinds, all reached through the `Kubernetes::REST`
   seam with no change here.
 - Accept a qualified `group/version/Kind` resource name anywhere the client
   takes one: `list('autoscaling/v1/HorizontalPodAutoscaler')` addresses that
   version, while the bare Kind keeps the shipped default of v2.
 - Move `Net::Async::Kubernetes::PortForwardSession` into its own file. It was
   declared inline in `Net::Async::Kubernetes`, so `use`ing the class the POD
   of `port_forward()`, `exec()` and `attach()` names failed, and every
   `dzil build` warned about it. The code is unchanged.
 - Add `on_watch_error` to `Net::Async::Kubernetes::Controller`. Watch `ERROR`
   events (a `403` mid-stream, for example) were wired nowhere; they carry a
   `Status` hashref rather than a keyed object, so they go to the callback
   instead of the reconcile workqueue. An `on_error` passed to
   `watch_resource` keeps precedence for that watch.
 - Fix two reference cycles that leaked a controller and its client. The
   controller held a strong `kube` while the client held the controller as a
   child, and every reconcile context held a strong `controller` inside an
   entry the controller owns. Both are now weak, as in `Watcher`; a client
   the controller constructs itself stays owned by it.
 - Fix the controller keeping one workqueue entry per key for the lifetime of
   the process, each pinning the last object it saw. A key is dropped once it
   reconciles cleanly; keys still queued, dirty or waiting on a retry keep
   their entry, so backoff history survives.
 - Fix a controller key stalling after a stop/start cycle. `stop()` left the
   workqueue filled and its keys flagged queued or dirty, so the event a
   restarted watch delivered was deduplicated away with no drain scheduled,
   and a dirty key finishing its reconcile requeued past the stop. `stop()`
   now clears queue and flags together and the requeue skips while stopped;
   the failure counts stay, so a retrying key resumes at its next attempt.
 - Fix `stop()` leaving a stopped watcher attached to the client on every
   stop/start cycle, so a repeatedly restarted controller piled up notifiers.
 - Fix the duplex transport naming `port_forward` in its "not added to an
   IO::Async::Loop" failure whichever method was called; `exec()` and
   `attach()` now name themselves.
 - Fix a resource name that `IO::K8s` cannot resolve killing the caller with
   "argument is not a module name" from `build_path`. `list()`, `get()`,
   `patch()`, `delete()`, `log()`, `port_forward()`, `exec()`, `attach()` and
   the controller's `patch_status()` now return a failed `Future` naming the
   resource; `$kube->expand_class` and starting a watcher croak with the same
   message.
 - Stop `t/03-configure.t` reading the kubeconfig of the machine it runs on.
   One CPAN Testers smoker carried a kubeconfig with `contexts: null`, so
   every 0.005 report from it was a FAIL nobody else saw. The subtest now
   runs against a fixture in a temporary HOME, and a companion subtest holds
   the degenerate config to a catchable croak. Reported by Andreas Koenig,
   GitHub issue #1.
 - Load `Net::Async::Kubernetes::Watcher` and `::Controller` at compile time
   instead of `require`ing them in `watcher()` and `controller()`: a broken
   child module now surfaces on `use Net::Async::Kubernetes`.
 - Move the example scripts from `ex/` to `eg/`; the 0.007 entry's
   `ex/live_features.pl` ships as `eg/live_features.pl`.
 - Documentation pass: PodWeaver generates the `NAME` sections,
   `PortForwardSession` is documented and in `SEE ALSO`, `list()` and the `cp`
   helpers describe what they do, the qualified resource name and the
   status-subresource write-strip trap are written down, and the `Controller`
   methods state the `Future` they return.

0.007     2026-04-15 18:21:50Z
 - Add `Net::Async::Kubernetes::Controller` as a minimal controller runtime.
   Provides watch registration, keyed workqueue deduplication, serialized
   reconcile dispatch, retry hooks, and small helpers for object reload and
   status subresource patch/update.
 - Add `$kube->controller(...)` factory to bind controller runtimes directly
   to an existing async Kubernetes client.
 - Fix async TLS handling for kubeconfigs with inline CA/client PEM data by
   materializing PEM content to temporary files before passing SSL options to
   Net::Async::HTTP / IO::Socket::SSL. This unblocks mTLS-authenticated async
   clients and controller runtimes against real clusters using embedded certs.
 - Add active `exec()` API that builds Kubernetes pod exec requests
   (`/exec` subresource) and delegates to the websocket duplex transport.
   Supports command, container, stdin/stdout/stderr/tty toggles, and
   subprotocol override.
 - Add active `attach()` API that builds Kubernetes pod attach requests
   (`/attach` subresource) and delegates to the websocket duplex transport.
   Supports container, stdin/stdout/stderr/tty toggles, and subprotocol
   override.
 - Extend `Net::Async::Kubernetes::PortForwardSession` with `write_stdin`
   (writes to channel 0) and `resize` (writes TTY resize JSON to channel 4)
   helpers for exec/attach sessions.
 - Add async `cp_to_pod()` and `cp_from_pod()` helpers that stream tar
   archives through an exec session for file transfer.
 - Add mock test coverage for exec (`t/14-mock-exec.t`), attach
   (`t/15-mock-attach.t`), and cp (`t/16-mock-cp.t`) flows.

0.006     2026-03-09 08:37:54Z
 - Add async Pod Log API via `log()`.
   Supports one-shot mode (Future resolves to full text) and streaming mode
   (`on_line` callback with Kubernetes::REST::LogEvent objects).
   Streaming mode supports follow, tailLines, sinceSeconds, sinceTime,
   timestamps, previous, limitBytes, and container parameters.
 - Add mock log test coverage (`t/09-mock-log.t`) and extend mock transport
   with generic streaming chunk support for log streams.
 - Add active `port_forward()` API that builds Kubernetes port-forward requests
   and delegates to duplex transport (`_do_duplex_request`).
 - Implement default websocket duplex transport for `port_forward()` using
   Net::Async::WebSocket::Client, including channel frame decoding and a
   `Net::Async::Kubernetes::PortForwardSession` helper (`write_channel`,
   `close`).
 - Add duplex transport tests (`t/13-duplex-transport.t`) for websocket
   handshake wiring, frame handling, session writes/closes, and connect errors.
 - Add active `exec()` API that builds Kubernetes pod `/exec` requests with
   repeated `command=` parameters and stream toggles
   (stdin/stdout/stderr/tty).
 - Add async exec test coverage (`t/14-mock-exec.t`) and duplex transport
   assertions for exec in `t/13-duplex-transport.t`.
 - Add active `attach()` API that builds Kubernetes pod `/attach` requests with
   stream toggles (stdin/stdout/stderr/tty).
 - Add async attach test coverage (`t/15-mock-attach.t`) and duplex transport
   assertions for attach in `t/13-duplex-transport.t`.
 - Extend `Net::Async::Kubernetes::PortForwardSession` with convenience
   helpers `write_stdin()` (channel 0) and `resize()` (channel 4 terminal
   resize payload for exec/attach).
 - Add async cp helpers `cp_to_pod()` and `cp_from_pod()` built on `exec()`.
   Supports single-file upload/download via channel streaming with Future-based
   completion and status/error propagation.
 - Add cp helper mock tests in `t/16-mock-cp.t`.
 - Add live demo script `ex/live_features.pl` to exercise log, exec, attach,
   port-forward, and cp helpers against a real cluster/kubeconfig.
 - Raise minimum versions to Kubernetes::REST 1.101 and IO::K8s 1.008.

0.005     2026-03-04 17:14:31Z
 - Use public Kubernetes::REST building-block API (build_path,
   prepare_request, check_response, inflate_object, inflate_list,
   process_watch_chunk) instead of private underscore methods.
   Requires Kubernetes::REST >= 1.100.

0.004     2026-02-28 01:39:32Z

0.003     2026-02-28 01:11:24Z
 - Auto-detect kubeconfig and in-cluster service account via
   Kubernetes::REST::Kubeconfig (requires Kubernetes::REST >= 1.004).
   When no explicit server/kubeconfig is provided, construction silently
   tries default kubeconfig and in-cluster auth before falling back to
   lazy resolution.

0.003     2026-02-28 00:00:00Z

0.002     2026-02-18 22:52:19Z
 - Updating minimum requirements

0.001     2026-02-13 06:03:10Z
 - Initial release
 - Async Kubernetes client built on IO::Async and Kubernetes::REST
 - Future-based CRUD: list(), get(), create(), update(), patch(), delete()
 - Three patch types: strategic-merge (default), merge, json
 - Delete by class+name or by object reference
 - Net::Async::Kubernetes::Watcher for streaming watch with auto-reconnect
 - Separate on_added, on_modified, on_deleted, on_error callbacks
 - Catch-all on_event callback for raw WatchEvent access
 - Client-side event filtering: names (regex/string/array) and event_types
 - Smart event type auto-derivation from registered callbacks
 - Resumable watches via resourceVersion tracking
 - Automatic 410 Gone recovery (clear resourceVersion and restart)
 - Auto-reconnect on stream completion (server timeout) and connection errors
 - Kubeconfig support via Kubernetes::REST::Kubeconfig
 - SSL/TLS with client certificate support
 - Custom resource_map for CRD support
 - Pass Content-Type correctly to Net::Async::HTTP for all request bodies
 - Defer watcher stop() HTTP close to avoid Net::Async::HTTP spurious read errors
 - Comprehensive inline POD documentation for all public methods and attributes
