Skip to content

v0.2.0

Choose a tag to compare

@arloliu arloliu released this 26 Aug 08:21
· 164 commits to main since this release

Puts HTTPS on the API listener, with a certificate that rotates without a restart,
and makes a shutdown drain everything it promises to drain.

Added

  • HTTPS on the API listener.
    server.tls names a certificate and a key,
    and when both are set the API listener serves HTTPS on the same port under the same name.
    The two paths are set together or not at all,
    because half a pair would leave a listener that fails every handshake,
    and both files are opened at startup so a path typo names its key instead of surfacing per connection.
    minVersion accepts 1.2 or 1.3 and defaults to 1.2.
    Leaving the block unset keeps the listener plaintext for an Ingress or a mesh terminating TLS,
    which remains a supported topology,
    and the ops listener stays plaintext for the kubelet's probe and the metrics scraper.

  • Certificate rotation without a restart.
    The pair is parsed once and handed to every handshake from an atomic pointer,
    and a goroutine re-reads both files every 30 seconds and swaps the pair only when the contents changed,
    so a cert-manager renewal is served without a rollout.
    A read or parse that fails leaves the pair already loaded in place,
    and re-reading rather than watching survives the kubelet's symlink-rename Secret updates.
    Two metrics make a rotation that quietly stopped working visible before the served certificate expires:
    profgate_tls_reloads_total and profgate_tls_certificate_expiry_seconds.
    An end-to-end scenario replaces the Secret with a certificate from a second authority
    and verifies the gateway serves it with no Pod restart.

  • TLS in the Helm chart.
    tls.enabled mounts a kubernetes.io/tls Secret read-only
    and renders server.tls to point inside it.
    The port, its name, the Service, and the NetworkPolicy stay as they are;
    only the scheme changes.
    The chart deliberately adds no checksum annotation over the Secret,
    because a renewal must be re-read from disk, not roll the Deployment.
    The kustomize base keeps serving plain HTTP and gains a commented example Secret.

  • Drain visibility.
    A finished drain now logs how long it took,
    whether the API listener closed on its own or on the deadline,
    how many requests a deadline close cut short,
    and whether the Collection drain finished or left Collections for another replica to reclaim,
    naming them.
    A drain still waiting on a Collection says so every 30 seconds,
    and a shutdown error that is not the deadline is logged rather than discarded.

Fixed

  • Requests are no longer reset during a rollout.
    Readiness turned 503 and the API listener closed in the same instant,
    which reset every request the endpoint controllers and the kube-proxies had not yet stopped routing here.
    The new server.drainDelay holds the listener open for that window:
    5 seconds by default, 60 at most, zero to turn it off.
    The gateway waits in process because the distroless image has no shell for a preStop hook,
    and the chart and the kustomize base raise terminationGracePeriodSeconds to 125 to match.

  • Discovery keeps moving through the drain.
    The informers descended from the context the stop signal cancels,
    so discovery froze the moment the drain began,
    even though an in-flight Collection re-resolves its targets every round
    and a profile request confirms its Pod before it dials.
    The informers now run under a context of their own,
    cancelled only once the interactive and Collection waits have ended.

  • Claims that land during the drain are drained too.
    The drain snapshotted the in-flight Collections once,
    so a claim past its capacity check but not yet committed owned nothing the snapshot could see,
    and the process could exit under a Collection still sampling and merging.
    The drain now refuses every later claim,
    waits for the claims already inside that window,
    and looks again after any wait before it returns.

  • A second stop signal cuts the drain short.
    The second SIGTERM went into a buffer nobody read,
    which left SIGKILL as the only way to end a drain waiting on a merge
    that would outlast the operator's patience.
    The first signal still asks for the graceful drain;
    the second logs that the drain is being cut short and exits non-zero.

  • A fatal listener error restarts fast.
    A listener failure ended the process through the same shutdown as SIGTERM,
    waiting without bound for in-flight Collections
    and spending the drain delay on an endpoint window that no longer received requests.
    A replica with no listener has nothing left to serve,
    so the fatal path now skips both waits,
    names the Collections it leaves running, and exits 1;
    they stop renewing their leases
    and another replica reclaims each one whose deadline has not passed and that has an attempt left,
    which is the documented recovery.

Artifacts

  • Image: ghcr.io/arloliu/profgate:v0.2.0
  • Chart: oci://ghcr.io/arloliu/charts/profgate version 0.2.0