Install-test / Install-test apache-tika (pull_request) Successful in 1m21s
Install-test / Install-test bentopdf (pull_request) Successful in 1m16s
Install-test / Install-test cyberchef (pull_request) Successful in 1m6s
Install-test / Install-test gotenberg (pull_request) Successful in 1m32s
Install-test / Install-test outline (pull_request) Failing after 1m16s
Install-test / Install-test rss-bridge (pull_request) Successful in 1m1s
Install-test / Install-test searxng (pull_request) Successful in 1m13s
Validate Helm Charts / Install-test (pull_request) Failing after 9m57s
Install-test / Install-test ytdlp-web-player (pull_request) Successful in 1m11s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Resource quantities (pull_request) Failing after 18s
Validate Helm Charts / HTTPRoute match (pull_request) Failing after 13s
Validate Helm Charts / Selector match (pull_request) Failing after 17s
Validate Helm Charts / Bump level (pull_request) Successful in 8s
Validate Helm Charts / YAML scalars (pull_request) Failing after 16s
Validate Helm Charts / NetworkPolicy (pull_request) Failing after 9s
Validate Helm Charts / Build apache-tika (pull_request) Successful in 5s
Validate Helm Charts / Trivy config apache-tika (pull_request) Successful in 13s
Validate Helm Charts / Build bentopdf (pull_request) Successful in 5s
Validate Helm Charts / Build cyberchef (pull_request) Successful in 6s
Validate Helm Charts / Build outline (pull_request) Successful in 5s
Validate Helm Charts / Build template-chart (pull_request) Successful in 5s
Validate Helm Charts / Trivy config outline (pull_request) Failing after 12s
Validate Helm Charts / Trivy config searxng (pull_request) Successful in 15s
Validate Helm Charts / Trivy config template-chart (pull_request) Successful in 14s
Validate Helm Charts / Build gotenberg (pull_request) Successful in 5s
Validate Helm Charts / Build rss-bridge (pull_request) Successful in 5s
Validate Helm Charts / Build searxng (pull_request) Successful in 5s
Validate Helm Charts / Build ytdlp-web-player (pull_request) Successful in 5s
Validate Helm Charts / Trivy config bentopdf (pull_request) Successful in 14s
Validate Helm Charts / Trivy config cyberchef (pull_request) Successful in 13s
Validate Helm Charts / Trivy config gotenberg (pull_request) Successful in 13s
Validate Helm Charts / Trivy config rss-bridge (pull_request) Successful in 14s
Validate Helm Charts / Trivy config ytdlp-web-player (pull_request) Successful in 15s
bentopdf, apache-tika, gotenberg, rss-bridge, searxng, ytdlp-web-player. One major each, networkPolicy.enabled defaults to true. Three of them differ from the simple case in ways worth naming: GOTENBERG gets DNS-only egress as a deliberate, documented trade. It drives a headless Chromium, and 3000 is its probe port - so it is reachable from the whole cluster by default, and an egress rule for 80/443 would turn a service anyone can POST to into a way of fetching arbitrary URLs. The price is the remote-asset half of HTML conversion: inline content converts, images the page would have fetched silently do not. That degrades a PDF instead of failing a pod, so nothing alerts on it, which is why it is in the template header, in values.yaml and in NOTES.txt, with extraPorts: [80, 443] named as the knowing opt-in. RSS-BRIDGE, SEARXNG and YTDLP-WEB-PLAYER get the open internet on their web ports, because reaching other people's servers IS the application and this object cannot name a host. Said plainly in each, so none of them reads stricter than it is. What the cap still buys is real: no other outbound port, so a compromised pod reaches no database, no mail server and no SSH anywhere. APACHE-TIKA and GOTENBERG render no Ingress - their consumers are other pods, in other namespaces here - so neither gets an ingressController value and both say in NOTES.txt that narrowing probeSources means listing the consumers' namespaces in allowedNamespaces in the same change, or extraction and conversion stop. YTDLP-WEB-PLAYER names the gap this convention has: its oauth2-proxy subchart renders its own Deployment with its own labels and ships no policy, so with the proxy enabled the pod that actually faces the Ingress is unrestricted in both directions. Nothing in this chart can change that - an upstream chart is consumed as it comes - so it is written down instead. ci/outline/test-values.yaml gains egress.extraPorts: [5556]. The CI dex runs there rather than on 443, and Outline fetches the discovery document at boot - so this is the chart's documented escape hatch used for exactly the case it was written for, and the one place in the repo where the cap's upgrade risk is actually exercised. Part of #5
142 lines
7.7 KiB
Plaintext
142 lines
7.7 KiB
Plaintext
{{- $replicas := include "gotenberg.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.gotenberg.replicas) -}}
|
|
{{- /* effectiveResources, not resources: an erased "resources:" block falls back
|
|
to the chart defaults in the Deployment (#99), so the notes must report the
|
|
limits the release actually gets, not "(none set)". */ -}}
|
|
{{- $limits := (include "gotenberg.effectiveResources" .Values.gotenberg.resources | fromYaml).limits | default dict -}}
|
|
{{- $svc := printf "%s-%s-service" .Release.Name .Chart.Name -}}
|
|
Gotenberg {{ .Chart.AppVersion }} — release {{ .Release.Name }} in namespace {{ .Release.Namespace }}.
|
|
|
|
ACCESS
|
|
This chart creates no Ingress. The API is cluster-internal:
|
|
|
|
http://{{ $svc }}.{{ .Release.Namespace }}.svc.cluster.local
|
|
|
|
Mind the port. The Service listens on 80 and forwards to the container's
|
|
3000, so consumers are configured without a port — e.g. paperless-ngx:
|
|
|
|
PAPERLESS_TIKA_GOTENBERG_ENDPOINT: http://{{ $svc }}.{{ .Release.Namespace }}.svc.cluster.local
|
|
|
|
To reach it from your machine:
|
|
|
|
kubectl -n {{ .Release.Namespace }} port-forward svc/{{ $svc }} 3000:80
|
|
curl http://localhost:3000/health
|
|
|
|
The health route reports Chromium and LibreOffice separately — it is the
|
|
quickest way to tell a broken converter from a broken caller. This chart has
|
|
no secrets and no volumes.
|
|
{{ if eq $replicas "0" }}
|
|
SCALED TO ZERO
|
|
{{ if .Values.scaleDown }}scaleDown: true{{ else }}gotenberg.replicas: 0{{ end }} — the deployment runs with replicas: 0. The Service
|
|
keeps its ClusterIP but has no endpoints, so callers fail with "connection
|
|
refused" rather than a timeout. Set it back and upgrade to return.
|
|
{{ end }}
|
|
NO AUTHENTICATION, AND THIS CHART CANNOT ADD ANY
|
|
Gotenberg's basic auth and OIDC, its SSRF guards for the downloadFrom
|
|
feature, and its request timeout are all command-line flags. This chart
|
|
passes no flags — it only offers gotenberg.env — so none of them can be
|
|
switched on through values. Everything that reaches the Service may convert,
|
|
and the URL routes make the server fetch whatever URL they are handed. Keep
|
|
it cluster-internal; do not put an Ingress in front of it without auth.
|
|
|
|
BIG CONVERSIONS RUN OUT OF TIME BEFORE THEY RUN OUT OF MEMORY
|
|
Chromium and LibreOffice hold the whole document in memory, so a conversion
|
|
is a short, memory-hungry burst. This release limits the container to
|
|
|
|
memory: {{ index $limits "memory" | default "(none set)" }}
|
|
ephemeral-storage: {{ index $limits "ephemeral-storage" | default "(none set)" }}
|
|
|
|
against deliberately smaller requests: the pod reserves little while it
|
|
idles and grows into the limits only while converting.
|
|
|
|
Those limits are set so the 30-second request timeout below is what a
|
|
too-large document hits first. An 18 MB HTML converts in about 22 seconds
|
|
and stays well inside them; a document heavy enough to exhaust the memory
|
|
limit would have run past the 30 seconds anyway, so raising memory further
|
|
does not turn a failing conversion into a successful one — splitting the
|
|
document does.
|
|
|
|
If you do need to change them, set gotenberg.resources.limits.memory and
|
|
.limits.ephemeralStorage. Both are set explicitly now, so raising the
|
|
matching request no longer drags the limit along at limitFactor x request.
|
|
|
|
Should a conversion still hit a limit, the two failures look different and
|
|
neither of them reports an HTTP error to the caller:
|
|
|
|
- Out of memory: the connection is dropped mid-request and the pod's
|
|
RESTARTS counter goes up. Confirm with
|
|
|
|
kubectl -n {{ .Release.Namespace }} get pod -l app={{ .Release.Name }}-{{ .Chart.Name }}-deployment -o jsonpath='{.items[*].status.containerStatuses[*].lastState.terminated.reason}'
|
|
|
|
which then prints OOMKilled.
|
|
|
|
- Out of ephemeral storage: /tmp and /home/gotenberg are emptyDirs and count
|
|
against the ephemeral-storage limit — Chromium's profile alone takes about
|
|
4 MB, and the uploaded file plus the rendered PDF come on top. The pod does
|
|
not restart, it disappears: a fresh one takes its place and the old one is
|
|
left in status Evicted, with the message naming ephemeral-storage.
|
|
|
|
REQUESTS TIME OUT AFTER 30 SECONDS
|
|
That is Gotenberg's own default, and since the chart passes no flags it
|
|
cannot be changed here. This is the ceiling large conversions actually meet:
|
|
an 18 MB HTML needs about 22 seconds, and a 25 MB one does not finish inside
|
|
the 30. Callers that need longer must split the document instead.
|
|
{{- if .Values.networkPolicy.enabled }}
|
|
|
|
NETWORKPOLICY IS ACTIVE — WHAT IT CAPS, AND WHAT IT DOES NOT
|
|
{{ .Release.Name }}-{{ .Chart.Name }}-networkpolicy selects this workload's pods.
|
|
Inbound: TCP 3000 only. Outbound: DNS{{ with .Values.networkPolicy.egress }}{{ with .extraPorts }} plus TCP {{ join ", " . }}{{ end }}{{ end }}.
|
|
|
|
EGRESS IS DNS ONLY ON PURPOSE, AND IT COSTS SOMETHING. Gotenberg drives a
|
|
headless Chromium, so an egress rule for 80/443 would turn a service the
|
|
whole cluster can POST to into a way of fetching arbitrary URLs. The price is
|
|
that HTML conversion loses remote assets: inline content converts, images the
|
|
page would have fetched silently do not. That degrades a PDF instead of
|
|
failing a pod, so nothing will alert on it. If this release converts URLs or
|
|
needs remote assets, set networkPolicy.egress.extraPorts: [80, 443] — and
|
|
know what it opens.
|
|
|
|
THIS CHART HAS NO INGRESS AND ITS CONSUMERS ARE OTHER PODS. While
|
|
probeSources is empty the 3000 rule has no source selector; narrowing it
|
|
means listing the consumers' namespaces in allowedNamespaces in the same
|
|
change, or every conversion stops.
|
|
|
|
Three limits belong in the same breath, because none of them is visible in
|
|
the object itself:
|
|
|
|
- EGRESS IS BY PORT, NEVER BY DESTINATION. A NetworkPolicy matches CIDRs and
|
|
selectors and cannot express a hostname, so every egress rule permits its
|
|
port to ANY address. That is a limit of the object, not a shortcut — this
|
|
cluster's CNI (Canal: Flannel plus Calico) has no FQDN rule either. An
|
|
outbound port the chart does not know about is BLOCKED: add it to
|
|
networkPolicy.egress.extraPorts, and expect to find out through an
|
|
application-level connection error rather than through this object.
|
|
{{- if .Values.networkPolicy.probeSources }}
|
|
- THE PROBE PATH IS RESTRICTED in this release, because
|
|
networkPolicy.probeSources is set to
|
|
{{ join ", " .Values.networkPolicy.probeSources }}
|
|
Probes come from the IP of the WORKER NODE running the pod. If none of those
|
|
CIDRs covers the node this pod lands on, the kubelet's probes are dropped
|
|
and THE POD NEVER BECOMES READY — the rollout then times out and the symptom
|
|
looks like a broken application. Compare
|
|
|
|
kubectl -n {{ .Release.Namespace }} describe pod -l app={{ .Release.Name }}-{{ .Chart.Name }}-deployment
|
|
|
|
against `kubectl get node -o wide`.
|
|
{{- else }}
|
|
- THE PROBED PORT IS OPEN INSIDE THE CLUSTER. networkPolicy.probeSources is
|
|
empty, so that rule carries no source selector at all and any pod the CNI
|
|
routes to can reach it. Not from the internet — but this is NOT pod-level
|
|
isolation. Narrowing it means listing the worker node IPs in
|
|
networkPolicy.probeSources, because the kubelet probes from the node's own
|
|
address and the chart cannot know it; a wrong guess there leaves the pod
|
|
permanently un-Ready.
|
|
{{- end }}
|
|
- IT IS ONLY A BOUNDARY WHERE THE CNI ENFORCES POLICIES. Where it does not,
|
|
this object is accepted and filters nothing, so everything reads as
|
|
configured while nothing is filtered. `kubectl -n {{ .Release.Namespace }} get networkpolicy`
|
|
shows the object exists; only a connection attempt from an unrelated
|
|
namespace shows that it bites.
|
|
|
|
Switch it off with networkPolicy.enabled: false.
|
|
{{- end }}
|