Largest values surface of the programme, and the first chart where the null-safety covers two workloads. 1. values.schema.json (#68): additionalProperties: false on every chart-owned object level, including files.barcodes, ocr (with enums for the 3.x modes), scripts, tikaGotenberg, the oidc provider block and the SMB CronJob. 2. Null-safety for hardening, probes and resources (#99), for the server AND the SMB consume-mover CronJob. It reuses the defaultedDict helper the barcodes block already introduced (#61) instead of adding a second mechanism - one merge helper, now with more callers. The server's pod context is consumed twice: as the pod securityContext and by the /run-ownership init container, which chowns /run to runAsUser:runAsGroup because s6-overlay refuses to start unless /run is owned by the runtime uid. Both reads now go through the same helper, so the chown cannot disagree with the context it has to match. Erasing that block used to abort rendering with a nil-pointer error; it now falls back to the defaults like every other chart. The CronJob has no probes (documented exception), so only its two contexts and resources are covered. files.storage stays deliberately not null-safe: an erased value yields an invalid PVC, which is the loud class #99 excludes. Rendering is byte-identical to 11.0.0, both with the CI values (which include the #61 regression guard) and with the production paperless-jk bundle values.
251 lines
11 KiB
YAML
251 lines
11 KiB
YAML
{{- /*
|
|
Barcode settings, defended against a null / empty "barcodes:" override:
|
|
an empty block falls back to the chart defaults (issue #61).
|
|
*/ -}}
|
|
{{- $barcodes := fromYaml (include "paperless-ngx.barcodes" .Values.paperless.files.barcodes) -}}
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
labels:
|
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
spec:
|
|
replicas: {{ include "paperless-ngx.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.paperless.replicas) }}
|
|
serviceName: {{ .Release.Name }}-{{ .Chart.Name }}-service
|
|
selector:
|
|
matchLabels:
|
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
template:
|
|
metadata:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
labels:
|
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
spec:
|
|
automountServiceAccountToken: false
|
|
securityContext:
|
|
{{- include "paperless-ngx.podSecurityContext" .Values.paperless.securityContext | nindent 8 }}
|
|
initContainers:
|
|
# s6-overlay refuses to start unless /run is owned by the runtime uid,
|
|
# but emptyDirs are always created root-owned. This minimal init
|
|
# container hands /run over to the paperless uid (see
|
|
# paperless.securityContext in values.yaml) so the main container can
|
|
# run fully hardened (non-root, no privilege escalation).
|
|
- name: init-run-ownership
|
|
image: "{{ .Values.paperless.busybox.image.repository }}:{{ .Values.paperless.busybox.image.tag }}"
|
|
imagePullPolicy: IfNotPresent
|
|
command:
|
|
- sh
|
|
- -c
|
|
{{- $podCtx := fromYaml (include "paperless-ngx.podSecurityContext" .Values.paperless.securityContext) }}
|
|
- chown {{ $podCtx.runAsUser }}:{{ $podCtx.runAsGroup }} /run
|
|
securityContext:
|
|
runAsNonRoot: false
|
|
runAsUser: 0
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: true
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
add:
|
|
- CHOWN
|
|
volumeMounts:
|
|
- mountPath: /run
|
|
name: run
|
|
resources:
|
|
requests:
|
|
cpu: 10m
|
|
memory: 16Mi
|
|
ephemeral-storage: 1Mi
|
|
limits:
|
|
cpu: 100m
|
|
memory: 32Mi
|
|
ephemeral-storage: 10Mi
|
|
containers:
|
|
- name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
image: "ghcr.io/paperless-ngx/paperless-ngx:{{ .Chart.AppVersion }}"
|
|
imagePullPolicy: IfNotPresent
|
|
securityContext:
|
|
{{- include "paperless-ngx.containerSecurityContext" .Values.paperless.securityContext | nindent 12 }}
|
|
ports:
|
|
- containerPort: 8000
|
|
protocol: TCP
|
|
name: paperless-port
|
|
env:
|
|
- name: PAPERLESS_REDIS
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-redis-secret
|
|
key: redis-connection-url
|
|
- name: PAPERLESS_REDIS_PREFIX
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-redis-secret
|
|
key: key-prefix
|
|
- name: PAPERLESS_DBENGINE
|
|
value: "postgresql"
|
|
- name: PAPERLESS_DBHOST
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: server
|
|
- name: PAPERLESS_DBPORT
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: port
|
|
- name: PAPERLESS_DBNAME
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: database
|
|
- name: PAPERLESS_DBUSER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: username
|
|
- name: PAPERLESS_DBPASS
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
|
key: password
|
|
- name: PAPERLESS_ADMIN_USER
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
|
key: username
|
|
- name: PAPERLESS_ADMIN_MAIL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
|
key: username
|
|
- name: PAPERLESS_ADMIN_PASSWORD
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
|
key: password
|
|
- name: PAPERLESS_SECRET_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
|
key: paperless-secret-key
|
|
- name: PAPERLESS_OCR_LANGUAGE
|
|
value: "{{ .Values.paperless.ocr.languages }}"
|
|
- name: PAPERLESS_OCR_MODE
|
|
value: "{{ .Values.paperless.ocr.mode }}"
|
|
- name: PAPERLESS_ARCHIVE_FILE_GENERATION
|
|
value: "{{ .Values.paperless.ocr.archiveFileGeneration }}"
|
|
- name: PAPERLESS_OCR_ROTATE_PAGES
|
|
value: "{{ .Values.paperless.ocr.rotate_pages }}"
|
|
- name: PAPERLESS_OCR_OUTPUT_TYPE
|
|
value: "{{ .Values.paperless.ocr.output_type }}"
|
|
- name: PAPERLESS_FILENAME_FORMAT
|
|
value: "{{ .Values.paperless.files.filenameFormat }}"
|
|
- name: PAPERLESS_URL
|
|
value: "https://{{ .Values.ingress.domain }}"
|
|
- name: PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE
|
|
value: "{{ $barcodes.asn.enabled }}"
|
|
- name: PAPERLESS_CONSUMER_ENABLE_BARCODES
|
|
value: "{{ $barcodes.enabled }}"
|
|
- name: PAPERLESS_CONSUMER_BARCODE_STRING
|
|
value: "{{ $barcodes.barcodeString }}"
|
|
- name: PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES
|
|
value: "{{ $barcodes.retainBarcodePage }}"
|
|
- name: PAPERLESS_CONSUMER_DELETE_DUPLICATES
|
|
value: "{{ .Values.paperless.files.deleteDuplicates }}"
|
|
{{- if .Values.paperless.files.tikaGotenberg.enabled }}
|
|
- name: PAPERLESS_TIKA_ENABLED
|
|
value: "{{ .Values.paperless.files.tikaGotenberg.enabled }}"
|
|
- name: PAPERLESS_TIKA_ENDPOINT
|
|
value: "http://{{ required "If Tika is enabled, a Tika-Endpoint must be provided" .Values.paperless.files.tikaGotenberg.tikaEndpoint }}"
|
|
- name: PAPERLESS_TIKA_GOTENBERG_ENDPOINT
|
|
value: "http://{{ required "If Tika is enabled, a Gotenberg-Endpoint must be provided" .Values.paperless.files.tikaGotenberg.gotenbergEndpoint }}"
|
|
{{- end }}
|
|
{{- if .Values.oidc.enabled }}
|
|
- name: PAPERLESS_APPS
|
|
value: "allauth.socialaccount.providers.openid_connect"
|
|
- name: PAPERLESS_SOCIALACCOUNT_PROVIDERS
|
|
value: |-
|
|
{
|
|
"openid_connect": {
|
|
"OAUTH_PKCE_ENABLED": true,
|
|
"APPS": [
|
|
{
|
|
"provider_id": "{{ required "A OIDC-provider-id must be provided" .Values.oidc.provider.id }}",
|
|
"name": "{{ required "A OIDC-provider-name must be provided" .Values.oidc.provider.name }}",
|
|
"client_id": "{{ required "A OIDC-client-id must be provided" .Values.oidc.provider.clientId }}",
|
|
"settings": {
|
|
"fetch_userinfo": true,
|
|
"oauth_pkce_enabled": true,
|
|
"server_url": "{{ required "A OIDC-discovery-document-url must be provided" .Values.oidc.provider.discoveryUrl }}",
|
|
{{- if .Values.oidc.provider.tokenAuthMethod }}
|
|
"token_auth_method": "{{ .Values.oidc.provider.tokenAuthMethod }}",
|
|
{{- end }}
|
|
"uid_field": "sub"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
- name: PAPERLESS_REDIRECT_LOGIN_TO_SSO
|
|
value: "{{ .Values.oidc.autoLoginWithProvider }}"
|
|
- name: PAPERLESS_DISABLE_REGULAR_LOGIN
|
|
value: "{{ .Values.oidc.disableRegularLogin }}"
|
|
{{- end }}
|
|
{{- range $value := .Values.paperless.env }}
|
|
{{- if $value.value }}
|
|
- name: {{ $value.name }}
|
|
value: {{ tpl $value.value $ }}
|
|
{{- 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 $ }}
|
|
{{- else if $value.valueFrom.configMapKeyRef }}
|
|
- name: {{ $value.name }}
|
|
valueFrom:
|
|
configMapKeyRef:
|
|
name: {{ tpl $value.valueFrom.configMapKeyRef.name $ }}
|
|
key: {{ tpl $value.valueFrom.configMapKeyRef.key $ }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
volumeMounts:
|
|
- mountPath: "/usr/src/paperless/data"
|
|
name: "paperless-volume"
|
|
subPath: data/
|
|
- mountPath: "/usr/src/paperless/media"
|
|
name: "paperless-volume"
|
|
subPath: media/
|
|
- mountPath: "/usr/src/paperless/export"
|
|
name: "paperless-volume"
|
|
subPath: export/
|
|
- mountPath: "/usr/src/paperless/consume"
|
|
name: "paperless-consume-volume"
|
|
# Writable emptyDirs so the root filesystem can stay read-only:
|
|
# s6-overlay needs /run, OCR/consumption uses /tmp for scratch files.
|
|
- mountPath: /tmp
|
|
name: tmp
|
|
- mountPath: /run
|
|
name: run
|
|
resources:
|
|
{{- include "paperless-ngx.effectiveResources" .Values.paperless.resources | nindent 12 }}
|
|
readinessProbe:
|
|
{{- include "paperless-ngx.readinessProbe" .Values.paperless.readinessProbe | nindent 12 }}
|
|
livenessProbe:
|
|
{{- include "paperless-ngx.livenessProbe" .Values.paperless.livenessProbe | nindent 12 }}
|
|
startupProbe:
|
|
{{- include "paperless-ngx.startupProbe" .Values.paperless.startupProbe | nindent 12 }}
|
|
restartPolicy: Always
|
|
volumes:
|
|
- name: tmp
|
|
emptyDir: {}
|
|
- name: run
|
|
emptyDir: {}
|
|
- name: paperless-volume
|
|
persistentVolumeClaim:
|
|
claimName: {{ .Release.Name }}-{{ .Chart.Name }}-volume-claim
|
|
- name: paperless-consume-volume
|
|
persistentVolumeClaim:
|
|
claimName: {{ .Release.Name }}-{{ .Chart.Name }}-consume-volume-claim |