v0.2.0
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.tlsnames 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.
minVersionaccepts 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_totalandprofgate_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.enabledmounts akubernetes.io/tlsSecret read-only
and rendersserver.tlsto 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 newserver.drainDelayholds 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 raiseterminationGracePeriodSecondsto 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/profgateversion0.2.0