feat(paperless-ngx)!: quote every operator scalar, pin the consume mover, drop oidcSecretRef #131
No files matched your search
@@ -5,7 +5,7 @@ icon: https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/dev/src-ui/s
|
||||
|
||||
type: application
|
||||
|
||||
version: 12.3.0+up3.2.1
|
||||
version: 13.0.0+up3.2.1
|
||||
|
||||
appVersion: 3.2.1
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
{{- $valkeySvc := printf "%s-valkey-service" .Release.Name -}}
|
||||
{{- $pre := default (list) $scripts.preConsumptionScripts -}}
|
||||
{{- $post := default (list) $scripts.postConsumptionScripts -}}
|
||||
{{- $cron := fromYaml (include "paperless-ngx.cron.scheduling" .Values.smbConnection.cron) -}}
|
||||
{{- $cronPinned := $cron.pinToServerNode -}}
|
||||
{{- $cronDeadline := include "paperless-ngx.formatNumber" $cron.activeDeadlineSeconds -}}
|
||||
{{- $httpRoute := .Values.httpRoute | default dict -}}
|
||||
{{- $hostnames := $httpRoute.hostnames | default list -}}
|
||||
{{- if not $hostnames -}}
|
||||
@@ -184,15 +187,14 @@ WHAT THE 1PASSWORD ITEMS MUST CONTAIN
|
||||
here, so a wrong field name surfaces as a mount failure on the CronJob's
|
||||
pod — a describe on that pod, never a missing variable.
|
||||
{{- end }}
|
||||
{{- if .Values.secrets.oidcSecretRef }}
|
||||
|
||||
secrets.oidcSecretRef IS SET AND IS READ BY NOTHING
|
||||
"{{ .Values.secrets.oidcSecretRef }}" — no template in this chart references
|
||||
that value. No OnePasswordItem is created for it and no Secret appears. The
|
||||
OIDC client id travels as plain env from oidc.provider.clientId, and no
|
||||
client secret is configured at all (see the OIDC section below). Whatever
|
||||
you put in that item has no effect on this release.
|
||||
{{- end }}
|
||||
{{- /*
|
||||
The warning that used to stand here - "secrets.oidcSecretRef IS SET AND IS READ
|
||||
BY NOTHING" - is gone with the value itself (13.0.0, Gitea #11). It cannot be
|
||||
printed any more and would not be reached: the key is out of values.schema.json
|
||||
too, so a release that still sets it fails BEFORE any template renders, with
|
||||
"additional properties 'oidcSecretRef' not allowed". The fix is to delete the
|
||||
key; values.yaml says why it is not coming back.
|
||||
*/}}
|
||||
|
||||
CHANGING A SECRET'S CONTENT RESTARTS NOTHING
|
||||
Helm never sees the content of a OnePasswordItem, so it cannot hash it, and
|
||||
@@ -340,12 +342,44 @@ HOW DOCUMENTS GET IN
|
||||
move, not a copy: once the job has run, the share no longer holds the file
|
||||
and the only copy is on the consume volume.
|
||||
|
||||
One thing to know before you edit that schedule: it is rendered into the
|
||||
manifest unquoted, so a schedule that STARTS with "*" ("*/15 * * * *")
|
||||
aborts the render with a YAML parse error on this template — YAML reads the
|
||||
leading * as an alias. Quoting it in your values file does not help, the
|
||||
quotes are gone by then. Use an equivalent that starts with a digit
|
||||
("5,20,35,50 * * * *"); a "*" anywhere but in first position is fine.
|
||||
The schedule is quoted on the way into the manifest since 13.0.0, so any
|
||||
cron notation works — one starting with "*" or an "@daily" macro included.
|
||||
Before that such a schedule aborted the render with a YAML parse error
|
||||
pointing at the template, and the advice here was to avoid it; if you are
|
||||
carrying a workaround schedule from that era ("5,20,35,50 * * * *" instead
|
||||
of every 15 minutes), you can write what you meant now.
|
||||
{{- if $cronPinned }}
|
||||
|
||||
PLACEMENT: the mover runs on the NODE OF THE STATEFULSET POD, via a
|
||||
required podAffinity on its label (Gitea #101). Without that it lands
|
||||
anywhere, and because it mounts the consume volume — RWX, from Longhorn —
|
||||
the kubelet of the StatefulSet's node then retries the mount every two
|
||||
minutes with "no Pending workload pods for volume ... to be mounted". It
|
||||
was measured at 291 such events in 10 hours.
|
||||
|
||||
THE PRICE, and it is the one thing to know before scaling down: with no
|
||||
StatefulSet pod the mover CANNOT BE SCHEDULED. scaleDown: true suspends
|
||||
this CronJob outright, so that case is covered — but paperless.replicas: 0
|
||||
with SMB still on is not, and there the job fails once per schedule after
|
||||
activeDeadlineSeconds ({{ $cronDeadline }}s) with reason DeadlineExceeded. That is
|
||||
deliberate: a bounded, visible failure rather than a Pending pod per
|
||||
schedule that nobody sees. Switch smbConnection.cron.pinToServerNode to
|
||||
false to go back to unpinned placement — and to the mount errors.
|
||||
|
||||
What the pinning does NOT fix: the consume volume stays RWX with a
|
||||
share-manager behind it. Co-locating the two consumers removes the
|
||||
cross-node mount, not the second consumer.
|
||||
{{- else }}
|
||||
|
||||
PLACEMENT IS OFF (smbConnection.cron.pinToServerNode: false). The mover
|
||||
will be scheduled wherever there is room, and when that is not the node of
|
||||
the StatefulSet pod, Longhorn refuses to mount the shared RWX consume
|
||||
volume there: the kubelet of the StatefulSet's node retries every two
|
||||
minutes with "no Pending workload pods for volume ... to be mounted"
|
||||
(Gitea #101, measured at 291 events in 10 hours). The errors stop when the
|
||||
StatefulSet pod is deleted and comes back Pending, and return with the next
|
||||
firing of this job.
|
||||
{{- end }}
|
||||
|
||||
The job has NO probes (the documented exception to the chart conventions),
|
||||
and it cannot fail usefully either: with nothing to move it exits 0, which
|
||||
@@ -432,8 +466,13 @@ OIDC IS ON, AND IT IS PKCE-ONLY
|
||||
{{- if not .Values.oidc.provider.tokenAuthMethod }}
|
||||
|
||||
oidc.provider.tokenAuthMethod is unset. Since the allauth update in 3.0 some
|
||||
providers need it spelled out (client_secret_basic); the symptom is a login
|
||||
that dies at the callback with invalid_client.
|
||||
providers need it spelled out, and the symptom when they do is a login that
|
||||
dies at the callback with invalid_client. The only value this chart accepts is
|
||||
"none" — it states the public client explicitly. A method that authenticates
|
||||
WITH a client secret cannot work here and is rejected by the schema (13.0.0,
|
||||
Gitea #11): this release configures no client secret and cannot, because
|
||||
paperless reads the whole OIDC configuration out of one json environment
|
||||
variable with no _FILE variant.
|
||||
{{- end }}
|
||||
{{- if or .Values.oidc.autoLoginWithProvider .Values.oidc.disableRegularLogin }}
|
||||
|
||||
@@ -453,6 +492,20 @@ paperless.scripts IS SET AND DOES NOTHING
|
||||
PAPERLESS_PRE_CONSUME_SCRIPT nor PAPERLESS_POST_CONSUME_SCRIPT is set on the
|
||||
container. These values render no object and change no behaviour.
|
||||
{{- end }}
|
||||
{{- if .Values.paperless.env }}
|
||||
|
||||
paperless.env ADDS {{ len .Values.paperless.env }} VARIABLE(S) — AND AN OVERRIDE ONLY BITES AT INSTALL
|
||||
{{ range $i, $e := .Values.paperless.env }}{{ if $i }}, {{ end }}{{ $e.name }}{{ end }}
|
||||
|
||||
These are appended AFTER the variables the chart sets itself, so at INSTALL an
|
||||
entry repeating one of those names wins. On an upgrade of an EXISTING release
|
||||
the same entry is lost: Kubernetes merges env by name, the pair collapses to
|
||||
the chart's own value, and helm upgrade still reports success. Read the value
|
||||
back off the StatefulSet, never off the upgrade's exit code:
|
||||
|
||||
kubectl -n {{ .Release.Namespace }} get statefulset {{ .Release.Name }}-{{ .Chart.Name }}-sfs \
|
||||
-o jsonpath='{.spec.template.spec.containers[0].env}'
|
||||
{{- end }}
|
||||
{{- if gt (int $replicas) 1 }}
|
||||
|
||||
paperless.replicas IS {{ $replicas }} — EXPECT ONE POD TO WORK AND THE REST TO WAIT
|
||||
|
||||
@@ -340,3 +340,41 @@ The same three fallbacks for the SMB consume-mover CronJob. It has no probes
|
||||
{{- $merged := fromYaml (include "paperless-ngx.defaultedDict" (dict "defaults" $defaults "given" . "path" "smbConnection.cron.resources")) -}}
|
||||
{{- include "paperless-ngx.resources" $merged -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
The whole smbConnection.cron block, merged onto its defaults (YAML; consume
|
||||
with fromYaml).
|
||||
|
||||
Input: .Values.smbConnection.cron (may be nil, empty or partial).
|
||||
|
||||
WHY EVERY READ OF THAT BLOCK GOES THROUGH HERE. The CronJob template used to
|
||||
dereference .Values.smbConnection.cron.<key> directly, so an override that had
|
||||
erased the block - the leftover childless
|
||||
|
||||
cron:
|
||||
|
||||
that issue #61 is about - did not fall back to the defaults the way this chart
|
||||
promises everywhere else. It aborted the render with
|
||||
|
||||
nil pointer evaluating interface {}.securityContext
|
||||
|
||||
naming an internal template path and neither the value nor the cause. Measured
|
||||
on origin/main before 13.0.0, so this is a fix and not a precaution.
|
||||
|
||||
The scalar defaults below MUST stay in sync with the smbConnection.cron block
|
||||
in values.yaml, which stays the documented, user-facing place for them; this
|
||||
copy is only the fallback. The sub-blocks (securityContext, resources, env)
|
||||
keep their own defaults in the helpers above and are only passed through here.
|
||||
|
||||
An explicit "pinToServerNode: false" survives this, which is the whole reason
|
||||
it is not a "default true" pipe: sprig's default would read false as unset and
|
||||
silently turn the pinning back on - the one value whose explicit false has to
|
||||
be obeyed, since it exists to switch off a hard placement rule.
|
||||
*/}}
|
||||
{{- define "paperless-ngx.cron.scheduling" -}}
|
||||
{{- $defaults := dict
|
||||
"pinToServerNode" true
|
||||
"startingDeadlineSeconds" 600
|
||||
"activeDeadlineSeconds" 3600 -}}
|
||||
{{- include "paperless-ngx.defaultedDict" (dict "defaults" $defaults "given" . "path" "smbConnection.cron") -}}
|
||||
{{- end -}}
|
||||
@@ -1,26 +1,165 @@
|
||||
{{- if .Values.smbConnection.enabled }}
|
||||
{{- /*
|
||||
The cron block, merged onto its defaults, so that an override which erased it
|
||||
falls back instead of aborting with a nil-pointer error naming an internal
|
||||
template path - which is what every direct .Values.smbConnection.cron.<key>
|
||||
read did before 13.0.0. The helper carries the measurement.
|
||||
*/}}
|
||||
{{- $cron := fromYaml (include "paperless-ngx.cron.scheduling" .Values.smbConnection.cron) -}}
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-{{ .Chart.Name }}-consume-cronjob
|
||||
spec:
|
||||
schedule: {{ .Values.smbConnection.cronJob }}
|
||||
{{- /*
|
||||
QUOTED, AND THAT IS THE WHOLE OF GITEA #11's FIRST HALF. The default
|
||||
"0 0,12 * * *" renders safely only because it begins with a digit. The most
|
||||
common cron notation of all - a star, a slash and an interval, every 15
|
||||
minutes - begins with "*", which YAML reads as an alias indicator, and the
|
||||
render ABORTS:
|
||||
|
||||
(and the notation cannot be written out in this comment, because the two
|
||||
characters that start it would end a Go template comment right here)
|
||||
|
||||
Error: YAML parse error on paperless-ngx/templates/paperless-consume.cronjob.yaml:
|
||||
error converting YAML to JSON: yaml: line 6: did not find expected
|
||||
alphabetic or numeric character
|
||||
|
||||
The trap is that quoting it in the VALUES does not help: Helm passes the
|
||||
string's content through, and the star stands bare in the manifest again. So
|
||||
the operator writes a correctly quoted value and gets an error that points at
|
||||
this template instead of at their value. "@daily" fails the same way ("@" is
|
||||
a reserved YAML indicator).
|
||||
|
||||
ci/run-yaml-scalar-render-test.sh is the test that would have caught it, and
|
||||
ci/yaml-scalar/paperless-ngx.yaml is the overlay that feeds it a star-leading
|
||||
schedule - needed because this whole template sits behind
|
||||
smbConnection.enabled, which is false in every other fixture, so nothing else
|
||||
in the repository renders this object at all.
|
||||
*/}}
|
||||
schedule: {{ .Values.smbConnection.cronJob | quote }}
|
||||
{{- /*
|
||||
Forbid, hard-wired rather than a value: two movers running at once race on
|
||||
the same files ("mv /mnt/smb/*.pdf" twice over one directory), and the loser
|
||||
reports a file that is already gone. There is no configuration in which
|
||||
overlapping runs of this job are wanted, so there is nothing to configure.
|
||||
*/}}
|
||||
concurrencyPolicy: Forbid
|
||||
{{- /*
|
||||
Bounds how late a missed run may still start, and it is here for a second
|
||||
reason that has nothing to do with lateness: while the CronJob is suspended
|
||||
(scaleDown) or its runs are being suppressed, the controller counts missed
|
||||
schedules, and above 100 it STOPS SCHEDULING ALTOGETHER with only
|
||||
"Cannot determine if job needs to be started: too many missed start times" in
|
||||
the controller log. With this field set, the controller only counts misses
|
||||
inside this window, so that state cannot be reached. The cost is stated in
|
||||
values.yaml: a run that could not start inside the window is skipped, not
|
||||
queued.
|
||||
*/}}
|
||||
startingDeadlineSeconds: {{ include "paperless-ngx.formatNumber" $cron.startingDeadlineSeconds }}
|
||||
{{- if .Values.scaleDown }}
|
||||
suspend: true
|
||||
{{- end }}
|
||||
jobTemplate:
|
||||
spec:
|
||||
{{- /*
|
||||
THE SAFETY NET UNDER THE AFFINITY BELOW (Gitea #101). activeDeadlineSeconds
|
||||
counts from the job's start, not from the pod's, so it also bounds a pod
|
||||
that NEVER gets scheduled: when the StatefulSet pod is absent and the
|
||||
required affinity cannot be satisfied, the job fails after this many
|
||||
seconds with reason DeadlineExceeded instead of sitting Pending forever
|
||||
and gaining a sibling every schedule. That turns the one new failure mode
|
||||
this issue's fix could introduce into a visible, self-clearing one -
|
||||
failedJobsHistoryLimit (Kubernetes' default, 1) keeps the evidence.
|
||||
*/}}
|
||||
activeDeadlineSeconds: {{ include "paperless-ngx.formatNumber" $cron.activeDeadlineSeconds }}
|
||||
template:
|
||||
spec:
|
||||
{{- if $cron.pinToServerNode }}
|
||||
{{- /*
|
||||
GITEA #101: this pod mounts the consume volume, which is RWX and comes
|
||||
from Longhorn. Without a placement rule the scheduler puts it on any
|
||||
node, and Longhorn's CSI driver then refuses the mount on the node
|
||||
where the StatefulSet pod runs:
|
||||
|
||||
MountVolume.SetUp failed ... code = Aborted desc = no Pending
|
||||
workload pods for volume pvc-... to be mounted:
|
||||
map[Running:[...-sfs-0] Succeeded:[...-consume-cronjob-...]]
|
||||
|
||||
Measured at every two minutes, 291 events in 10 hours, in both
|
||||
releases that have SMB switched on; the release with
|
||||
smbConnection.enabled: false and therefore no CronJob produced none of
|
||||
them on identically built volumes.
|
||||
|
||||
WHY podAffinity AND NOT nodeSelector: the StatefulSet pod is not
|
||||
pinned either - it sits wherever the scheduler put it and moves on
|
||||
every restart. A static nodeSelector would be correct exactly until
|
||||
then and silently wrong afterwards. The affinity follows the pod.
|
||||
|
||||
WHY required AND NOT preferred: preferred falls back to another node
|
||||
in the very situation this exists for, which reproduces the bug more
|
||||
rarely and therefore harder to find. The fallback is what went
|
||||
unnoticed for ten hours.
|
||||
|
||||
The label is the one the StatefulSet puts on its pod template
|
||||
(paperless-server.sfs.yaml, metadata.labels.app) and is read off the
|
||||
running pod, not assumed.
|
||||
|
||||
WHAT THIS DOES NOT FIX: the volume stays RWX and keeps its
|
||||
share-manager; co-locating the two consumers removes the CROSS-NODE
|
||||
attach, not the second consumer. The design that removes it is moving
|
||||
the mv into the StatefulSet pod, which cannot be done for the running
|
||||
installations because RWX cannot be taken back off an existing PVC.
|
||||
Gitea #101 names it as the alternative and not as the proposal.
|
||||
*/}}
|
||||
affinity:
|
||||
podAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
- labelSelector:
|
||||
matchLabels:
|
||||
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
||||
topologyKey: kubernetes.io/hostname
|
||||
{{- end }}
|
||||
automountServiceAccountToken: false
|
||||
securityContext:
|
||||
{{- include "paperless-ngx.cron.podSecurityContext" .Values.smbConnection.cron.securityContext | nindent 12 }}
|
||||
{{- include "paperless-ngx.cron.podSecurityContext" $cron.securityContext | nindent 12 }}
|
||||
containers:
|
||||
- name: paperless-consume-cronjob
|
||||
image: "{{ .Values.paperless.busybox.image.repository }}:{{ .Values.paperless.busybox.image.tag }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
securityContext:
|
||||
{{- include "paperless-ngx.cron.containerSecurityContext" .Values.smbConnection.cron.securityContext | nindent 16 }}
|
||||
{{- include "paperless-ngx.cron.containerSecurityContext" $cron.securityContext | nindent 16 }}
|
||||
{{- if $cron.env }}
|
||||
env:
|
||||
{{- range $value := $cron.env }}
|
||||
{{- if $value.value }}
|
||||
{{- /*
|
||||
quote is required, not cosmetic: a value like "8080" or "true"
|
||||
renders unquoted as a YAML number/bool, and the API server then
|
||||
rejects the whole manifest with "cannot unmarshal number into
|
||||
Go struct field EnvVar.value of type string". A template
|
||||
comment and not a "#" one, because a "#" line here is part of
|
||||
the rendered manifest.
|
||||
*/}}
|
||||
- name: {{ $value.name | quote }}
|
||||
value: {{ tpl $value.value $ | quote }}
|
||||
{{- else if $value.valueFrom }}
|
||||
{{- if $value.valueFrom.secretKeyRef }}
|
||||
- name: {{ $value.name }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ tpl $value.valueFrom.secretKeyRef.name $ | quote }}
|
||||
key: {{ tpl $value.valueFrom.secretKeyRef.key $ | quote }}
|
||||
{{- else if $value.valueFrom.configMapKeyRef }}
|
||||
- name: {{ $value.name }}
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: {{ tpl $value.valueFrom.configMapKeyRef.name $ | quote }}
|
||||
key: {{ tpl $value.valueFrom.configMapKeyRef.key $ | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
@@ -33,7 +172,7 @@ spec:
|
||||
mountPath: "/mnt/consume"
|
||||
readOnly: false
|
||||
resources:
|
||||
{{- include "paperless-ngx.cron.effectiveResources" .Values.smbConnection.cron.resources | nindent 16 }}
|
||||
{{- include "paperless-ngx.cron.effectiveResources" $cron.resources | nindent 16 }}
|
||||
restartPolicy: Never
|
||||
volumes:
|
||||
- name: smb-share
|
||||
@@ -42,4 +181,4 @@ spec:
|
||||
- name: consume-folder
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Release.Name }}-{{ .Chart.Name }}-consume-volume-claim
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -204,23 +204,41 @@ spec:
|
||||
- name: PAPERLESS_DISABLE_REGULAR_LOGIN
|
||||
value: "{{ .Values.oidc.disableRegularLogin }}"
|
||||
{{- end }}
|
||||
{{- /*
|
||||
QUOTED SINCE 13.0.0, and it is a fix rather than a tidy-up (Gitea
|
||||
#112, same genus as #11). This block is the chart's additive env
|
||||
interface, and until 13.0.0 it rendered its operand bare. Two
|
||||
measured consequences, both of them the operator's value arriving
|
||||
as something else:
|
||||
|
||||
- a value beginning with "*" aborted the render, with the error
|
||||
pointing at THIS template rather than at the value;
|
||||
- "true" and "0755" rendered as a YAML bool and an integer, and
|
||||
the API server rejects those with "cannot unmarshal number
|
||||
into Go struct field EnvVar.value of type string" - the chart
|
||||
renders, the install fails.
|
||||
|
||||
The quoted form is the repository's documented one
|
||||
(template-chart/templates/xxx.deployment.yaml carries the rule);
|
||||
this chart was one of seven that had not been brought to it.
|
||||
*/}}
|
||||
{{- range $value := .Values.paperless.env }}
|
||||
{{- if $value.value }}
|
||||
- name: {{ $value.name }}
|
||||
value: {{ tpl $value.value $ }}
|
||||
- name: {{ $value.name | quote }}
|
||||
value: {{ tpl $value.value $ | quote }}
|
||||
{{- else if $value.valueFrom }}
|
||||
{{- if $value.valueFrom.secretKeyRef }}
|
||||
- name: {{ $value.name }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ tpl $value.valueFrom.secretKeyRef.name $ }}
|
||||
key: {{ tpl $value.valueFrom.secretKeyRef.key $ }}
|
||||
name: {{ tpl $value.valueFrom.secretKeyRef.name $ | quote }}
|
||||
key: {{ tpl $value.valueFrom.secretKeyRef.key $ | quote }}
|
||||
{{- else if $value.valueFrom.configMapKeyRef }}
|
||||
- name: {{ $value.name }}
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: {{ tpl $value.valueFrom.configMapKeyRef.name $ }}
|
||||
key: {{ tpl $value.valueFrom.configMapKeyRef.key $ }}
|
||||
name: {{ tpl $value.valueFrom.configMapKeyRef.name $ | quote }}
|
||||
key: {{ tpl $value.valueFrom.configMapKeyRef.key $ | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -23,8 +23,16 @@ metadata:
|
||||
name: {{ .Release.Name }}-{{ .Chart.Name }}-ingress
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: {{ required "A Cluster-Issuer must be provided" .Values.ingress.clusterIssuer }}
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: {{ .Values.paperless.files.maxUploadFileSize }}
|
||||
nginx.org/client-max-body-size: {{ .Values.paperless.files.maxUploadFileSize }}
|
||||
{{- /*
|
||||
Quoted for the same reason as the env block in the StatefulSet and the
|
||||
schedule in the CronJob (Gitea #11's genus): an annotation value must be a
|
||||
string, maxUploadFileSize is free operator text, and a unit-less "100" -
|
||||
schema-valid, since the schema types it as a string - renders bare as an
|
||||
integer and the API server rejects the whole Ingress. The default "10m"
|
||||
happens to be safe, which is exactly how this survived.
|
||||
*/}}
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: {{ .Values.paperless.files.maxUploadFileSize | quote }}
|
||||
nginx.org/client-max-body-size: {{ .Values.paperless.files.maxUploadFileSize | quote }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -69,9 +69,6 @@
|
||||
},
|
||||
"smbConnectionSecretRef": {
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"oidcSecretRef": {
|
||||
"type": ["string", "null"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -279,13 +276,28 @@
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"cronJob": {
|
||||
"description": "Cron schedule of the consume-mover job.",
|
||||
"description": "Cron schedule of the consume-mover job. Deliberately without a pattern: cron syntax is richer than a regex here would admit, and the failure this value used to have was not a wrong schedule but a correct one rendered bare into the manifest - fixed at the render site with | quote (Gitea #11), not by narrowing what may be written.",
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"cron": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"pinToServerNode": {
|
||||
"description": "Place the consume-mover pod on the node that carries the StatefulSet pod, via a required podAffinity on its label (Gitea #101). Default true: the mover mounts the RWX consume volume, and Longhorn's CSI driver refuses that mount unless it finds a workload pod in Pending, so a mover on another node leaves the StatefulSet node's kubelet retrying every two minutes. false restores the unpinned behaviour and with it those mount errors.",
|
||||
"type": ["boolean", "null"]
|
||||
},
|
||||
"startingDeadlineSeconds": {
|
||||
"description": "How late a missed run may still start. Also bounds the CronJob controller's missed-schedule count, which above 100 stops this job being scheduled at all, reported only in the controller's own log.",
|
||||
"type": ["integer", "null"]
|
||||
},
|
||||
"activeDeadlineSeconds": {
|
||||
"description": "Hard stop for one mover run, counted from the job's start, so it also bounds a pod that never gets scheduled - with pinToServerNode on and no StatefulSet pod, the job fails with DeadlineExceeded instead of leaving a Pending pod per schedule.",
|
||||
"type": ["integer", "null"]
|
||||
},
|
||||
"env": {
|
||||
"$ref": "#/definitions/env"
|
||||
},
|
||||
"securityContext": {
|
||||
"$ref": "#/definitions/securityContext"
|
||||
},
|
||||
@@ -326,8 +338,9 @@
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"tokenAuthMethod": {
|
||||
"description": "Explicit token endpoint auth method (e.g. client_secret_basic). Some providers need this since the allauth update in paperless-ngx 3.0.",
|
||||
"type": ["string", "null"]
|
||||
"description": "Explicit token endpoint auth method. Restricted to 'none' in 13.0.0 (Gitea #11): this chart registers a PUBLIC client with PKCE and configures no client secret, so a method that authenticates with one cannot work - it produced a login that died at the callback with invalid_client while reading like a working secret setup. null omits the key; 'none' states the public client explicitly, which some providers need spelled out since the allauth update in paperless-ngx 3.0.",
|
||||
"type": ["string", "null"],
|
||||
"enum": ["none", null]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+121
-5
@@ -13,7 +13,26 @@ secrets:
|
||||
databaseSecretRef: null
|
||||
paperlessSecretRef: null
|
||||
smbConnectionSecretRef: null
|
||||
oidcSecretRef: null
|
||||
# REMOVED IN 13.0.0: secrets.oidcSecretRef (Gitea #11). It is gone from the
|
||||
# schema too, so a release that still sets it now fails to render with
|
||||
# additional properties 'oidcSecretRef' not allowed
|
||||
# instead of being ignored - DELETE THE KEY from your values, nothing else is
|
||||
# needed. It never did anything: no template read it, no OnePasswordItem was
|
||||
# created for it, and whatever the item held never reached the pod.
|
||||
#
|
||||
# It is not coming back as a feature, and the reason is in paperless itself:
|
||||
# the whole OIDC configuration travels in ONE json environment variable
|
||||
# (SOCIALACCOUNT_PROVIDERS = json.loads(os.getenv(
|
||||
# "PAPERLESS_SOCIALACCOUNT_PROVIDERS", "{}"))), and there is no _FILE variant
|
||||
# of it. A secretKeyRef cannot substitute a fragment INTO a json variable, so
|
||||
# an honest implementation would have to either inline the secret in the pod
|
||||
# spec or push the complete json into the vault by hand - and the second one
|
||||
# makes every required() in the oidc block below dead wood.
|
||||
#
|
||||
# The operational question behind it is answered rather than dodged: this
|
||||
# chart registers a PUBLIC client with PKCE, Keycloak (Gitea #83) can mark a
|
||||
# client public per client, and so no provider constraint forces a
|
||||
# confidential client here. See the oidc block below.
|
||||
|
||||
ingress:
|
||||
# Set to false to omit the Ingress entirely. NEW in the chart version that
|
||||
@@ -179,7 +198,38 @@ paperless:
|
||||
timeoutSeconds: 5
|
||||
# Extra environment variables, appended to the ones the chart always sets.
|
||||
# Values are rendered through tpl; value, secretKeyRef and configMapKeyRef
|
||||
# forms are supported.
|
||||
# forms are supported. paperless-ngx reads around 80 PAPERLESS_* variables and
|
||||
# this chart maps a handful of them explicitly - everything else goes here.
|
||||
#
|
||||
# env:
|
||||
# - name: PAPERLESS_LOGGING_DIR
|
||||
# value: /tmp/paperless-log
|
||||
#
|
||||
# ADDITIVE MEANS ADDITIVE, and the timing matters: an entry repeating a name
|
||||
# the chart already sets reaches the pod twice, and at INSTALL the later one -
|
||||
# yours - wins. On an EXISTING release the same entry is lost, because
|
||||
# Kubernetes merges env by name and the pair collapses to the chart's own
|
||||
# value, while helm upgrade still reports success. Read an override back off
|
||||
# the StatefulSet, never off the upgrade's exit code.
|
||||
#
|
||||
# VALUES MUST BE STRINGS (the schema enforces it) and they are quoted on the
|
||||
# way out since 13.0.0. Before that a value beginning with "*" aborted the
|
||||
# render and "true" or "0755" arrived as a bool or an integer, which the API
|
||||
# server rejects - see the comment at the render site in
|
||||
# templates/paperless-server.sfs.yaml.
|
||||
#
|
||||
# THE WORKED EXAMPLE, PAPERLESS_LOGGING_DIR, AND WHAT IT DOES NOT FIX
|
||||
# (jk-cluster-config #360): with the data directory on an RWX volume that
|
||||
# Longhorn exports over NFS, paperless' concurrent_log_handler cannot hold its
|
||||
# lock file there and fails with "RuntimeError: Cannot acquire lock after 20
|
||||
# attempts" - measured at 45 % of the application log across all three
|
||||
# installations. Pointing PAPERLESS_LOGGING_DIR at a path under the /tmp
|
||||
# emptyDir this pod already mounts takes the log off the NFS share and the
|
||||
# noise stops. It does NOT fix the RWX data volume, which is the actual root
|
||||
# cause and a cluster-config decision (the PVC asks for ReadWriteOnce and was
|
||||
# provisioned ReadWriteMany; taking RWX back means recreating the volume).
|
||||
# A log under /tmp does not survive a pod restart - no loss here, paperless
|
||||
# also writes to stdout, which is what kubectl logs reads.
|
||||
env: []
|
||||
resources:
|
||||
# Limits are optional: memory and ephemeral-storage limits default to
|
||||
@@ -214,8 +264,58 @@ paperless:
|
||||
smbConnection:
|
||||
enabled: false
|
||||
server: null
|
||||
# Cron schedule of the consume mover. Quote it or not as you like - the chart
|
||||
# quotes it on the way into the manifest (since 13.0.0, Gitea #11), which is
|
||||
# what a schedule beginning with "*" or "@" needs. Before that such a
|
||||
# schedule aborted the render with a YAML error pointing at the template.
|
||||
cronJob: "0 0,12 * * *"
|
||||
cron:
|
||||
# Run the mover on the node that carries the StatefulSet pod (Gitea #101).
|
||||
#
|
||||
# WHY IT DEFAULTS TO TRUE: this pod mounts the consume volume, which is RWX
|
||||
# and comes from Longhorn, and Longhorn's CSI driver refuses to mount an RWX
|
||||
# volume unless it finds a workload pod in Pending - a mover pod that ran on
|
||||
# a different node leaves the kubelet of the StatefulSet's node retrying
|
||||
# every two minutes with "no Pending workload pods for volume ... to be
|
||||
# mounted" (291 events in 10 hours, in both releases with SMB on; the
|
||||
# release without the CronJob produced none).
|
||||
#
|
||||
# It is a switch and not a free-form affinity block on purpose: the default
|
||||
# has to name the StatefulSet pod's label, that label contains the RELEASE
|
||||
# NAME, and a values.yaml is never templated - so the useful default cannot
|
||||
# be expressed here at all (the same property that forces
|
||||
# valkey.secrets.authSecretRef to be typed out twice, further down).
|
||||
#
|
||||
# WHAT IT COSTS, AND IT IS A REAL COST: the affinity is required, so with
|
||||
# the StatefulSet pod absent the mover cannot be scheduled. The two ways
|
||||
# that happens are handled rather than left open - scaleDown: true suspends
|
||||
# this CronJob outright, and activeDeadlineSeconds below fails a job that
|
||||
# cannot be placed instead of letting Pending pods pile up. What stays: with
|
||||
# paperless.replicas: 0 and SMB on, the mover does not run and reports one
|
||||
# failed job per schedule. Set this to false to get the old, unpinned
|
||||
# behaviour back - including the mount errors.
|
||||
pinToServerNode: true
|
||||
# How late a missed run may still start. Also keeps the CronJob controller
|
||||
# from reaching "too many missed start times (> 100)", after which it stops
|
||||
# scheduling this job entirely and says so only in its own log - reachable
|
||||
# by leaving scaleDown: true in place for months. The cost of a window: a
|
||||
# run that could not start inside it is skipped, not queued. Must stay well
|
||||
# above 10s, below which the controller may never schedule at all.
|
||||
startingDeadlineSeconds: 600
|
||||
# Hard stop for one mover run, counted from the job's start - so it also
|
||||
# bounds a pod that never gets scheduled (see pinToServerNode). Exceeding it
|
||||
# shows up as a job with reason DeadlineExceeded, which is the point: a
|
||||
# loud, self-clearing failure instead of a Pending pod that nobody sees.
|
||||
# Raise it if a run legitimately moves more than an hour's worth of files;
|
||||
# the mover copies across filesystems, so large scans are not instant.
|
||||
activeDeadlineSeconds: 3600
|
||||
# Extra environment variables for the mover container, same form as
|
||||
# paperless.env above. Deliberately empty: the container is a busybox
|
||||
# running one mv, so this exists for the odd proxy or locale variable and
|
||||
# for the convention that every workload container offers the block - not
|
||||
# because the mover needs configuration. Note that the cron SCHEDULE's time
|
||||
# zone is the controller's and cannot be changed with a TZ entry here.
|
||||
env: []
|
||||
# The consume mover only shuffles files between the SMB share (mounted
|
||||
# with 0777 modes) and the consume PVC, so it runs as the same non-root
|
||||
# uid/gid as paperless itself with a fully hardened context.
|
||||
@@ -251,9 +351,25 @@ oidc:
|
||||
name: null
|
||||
clientId: null
|
||||
discoveryUrl: null
|
||||
# Optional: explicit token endpoint auth method (e.g. client_secret_basic).
|
||||
# Some providers need this since the allauth update in paperless-ngx 3.0
|
||||
# (login fails at the callback with invalid_client otherwise).
|
||||
# Optional: explicit token endpoint auth method. "none" is the only accepted
|
||||
# value, and the schema enforces that (Gitea #11).
|
||||
#
|
||||
# WHY IT IS RESTRICTED, since the previous comment here named
|
||||
# client_secret_basic as the example and that was actively misleading: this
|
||||
# chart registers a PUBLIC client and configures NO client secret - it
|
||||
# renders OAUTH_PKCE_ENABLED and nothing else, and secrets.oidcSecretRef was
|
||||
# removed in 13.0.0 because no secret can reach paperless through a json
|
||||
# environment variable anyway. A method that authenticates WITH a client
|
||||
# secret therefore cannot work here; asking for one produced a login that
|
||||
# died at the callback with invalid_client, while the two values read
|
||||
# together looked like a working secret setup.
|
||||
#
|
||||
# So: null omits the key and lets allauth decide, "none" states the public
|
||||
# client explicitly - which is what some providers need spelled out since
|
||||
# the allauth update in paperless-ngx 3.0, and the symptom when they do is
|
||||
# that same invalid_client at the callback. If a future paperless release
|
||||
# supports an auth method that needs no secret, widening the enum is a minor
|
||||
# bump; allowing one that needs a secret is not on the table.
|
||||
tokenAuthMethod: null
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in new issue
Block a user