OnePasswordItem references: required() for the four that are not optional #38
No files matched your search
@@ -363,7 +363,13 @@ Reference implementation: `template-chart/values.schema.json` and `template-char
|
|||||||
|
|
||||||
**Two things about the mechanics that are easy to lose:**
|
**Two things about the mechanics that are easy to lose:**
|
||||||
|
|
||||||
- **`null` reaches the schema in one case and not the other.** For a key **with** a chart default, Helm's coalescing treats the `null` as a deletion and removes it *before* schema validation — the schema never sees it, and only the helper can catch it. For a key **without** a default (e.g. `resources.limits`), there is nothing to delete, the `null` survives into validation, and a strict `"type": "object"` aborts the render. That is why the helper **and** the widened `["object", "null"]` types are both needed: each covers a case the other cannot. Everything else stays strictly typed.
|
- **`null` reaches the schema in two cases out of three, and which one you are in depends on the chart's default, not on what the user wrote.** Measured on Helm 3.22.0 with a probe chart, because the wording here was wrong before and was applied to exactly the case it got wrong:
|
||||||
|
- a user `null` over a **non-null** chart default (`--set x=null`, or `x: null` in a values file) is treated as a deletion and the key is removed *before* schema validation — the schema never sees it, and only the helper can catch it;
|
||||||
|
- a **`null` chart default** survives into validation with or without a user `null` on top of it, because there is nothing to delete it against, and the schema sees the key present with value `null`;
|
||||||
|
- a key **without any default** (e.g. `resources.limits`) likewise survives, and a strict `"type": "object"` aborts the render.
|
||||||
|
|
||||||
|
That is why the helper **and** the widened `["object", "null"]` / `["string", "null"]` types are both needed: each covers a case the other cannot. Everything else stays strictly typed.
|
||||||
|
- **A value that has no valid default cannot be enforced by `values.schema.json` at all, and `required()` is the whole enforcement.** The chain is short and it has no exit (#18): the key ships as `null`, so it is *present* at validation time, and JSON Schema's `required` tests presence and not null-ness — the array never fires, under any input. The two ways to make it fire both break the rule that `helm lint --strict` passes on chart defaults, since lint validates those defaults against the schema: dropping the `null` default gives `missing property '<key>'`, and narrowing `["string", "null"]` to `"string"` gives `got null, want string` — each a red `validate-charts` run on a chart nobody touched. So such a key is guarded with `required()` in the template and **described** as required in the schema; a `required` array next to it would be a guard that cannot fire, which is the same defect as a value that does nothing, dressed as a safeguard. This is why no chart in this repo carries one.
|
||||||
- **Validate every schema against the real values, not only against chart defaults.** Defaults are valid by construction, so they prove nothing. Render each chart against its bundle in `cluster-config/fleet-cd/` **and** against `helm get values` — the two differ more often than expected (four of ten bundles in one wave, always with the repo file being the worse one). This step caught a schema of mine that would have broken three live paperless releases, because all three write `tikaGotenberg.enabled` as the string `"true"` and the first draft demanded a boolean. Result per chart is a **list of rejected keys**, which is the cluster side's work order before the version is pulled up.
|
- **Validate every schema against the real values, not only against chart defaults.** Defaults are valid by construction, so they prove nothing. Render each chart against its bundle in `cluster-config/fleet-cd/` **and** against `helm get values` — the two differ more often than expected (four of ten bundles in one wave, always with the repo file being the worse one). This step caught a schema of mine that would have broken three live paperless releases, because all three write `tikaGotenberg.enabled` as the string `"true"` and the first draft demanded a boolean. Result per chart is a **list of rejected keys**, which is the cluster side's work order before the version is pulled up.
|
||||||
|
|
||||||
### 13. Everything else
|
### 13. Everything else
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ name: calibre-web-automated
|
|||||||
description: A Helm chart to deploy calibere-web-automated
|
description: A Helm chart to deploy calibere-web-automated
|
||||||
icon: https://raw.githubusercontent.com/crocodilestick/Calibre-Web-Automated/main/README_images/cwa-logo-round-dark.png
|
icon: https://raw.githubusercontent.com/crocodilestick/Calibre-Web-Automated/main/README_images/cwa-logo-round-dark.png
|
||||||
|
|
||||||
version: 6.1.0+upv4.0.6
|
version: 7.0.0+upv4.0.6
|
||||||
appVersion: v4.0.6
|
appVersion: v4.0.6
|
||||||
|
|
||||||
maintainers:
|
maintainers:
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ THE FIRST START BUILDS A LIBRARY, AND THE STARTUP PROBE IS TIMING IT
|
|||||||
kubectl -n {{ .Release.Namespace }} logs {{ $pod }} -f
|
kubectl -n {{ .Release.Namespace }} logs {{ $pod }} -f
|
||||||
|
|
||||||
SECRETS — THE FIELDS THE 1PASSWORD ITEM MUST CARRY
|
SECRETS — THE FIELDS THE 1PASSWORD ITEM MUST CARRY
|
||||||
{{ if .Values.secrets.cwaSecretRef }}{{ .Values.secrets.cwaSecretRef }}{{ else }}(secrets.cwaSecretRef is unset — see below){{ end }}
|
{{ .Values.secrets.cwaSecretRef }}
|
||||||
→ Secret {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
→ Secret {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
||||||
hardcover-api-token becomes the HARDCOVER_TOKEN environment variable
|
hardcover-api-token becomes the HARDCOVER_TOKEN environment variable
|
||||||
calibre-plugins mounted as /config/.config/calibre/customize.py.json,
|
calibre-plugins mounted as /config/.config/calibre/customize.py.json,
|
||||||
@@ -74,13 +74,10 @@ SECRETS — THE FIELDS THE 1PASSWORD ITEM MUST CARRY
|
|||||||
hardcover-api-token parks the pod in CreateContainerConfigError; a missing
|
hardcover-api-token parks the pod in CreateContainerConfigError; a missing
|
||||||
calibre-plugins fails the mount instead ("couldn't find key calibre-plugins"),
|
calibre-plugins fails the mount instead ("couldn't find key calibre-plugins"),
|
||||||
and the container is never created at all.
|
and the container is never created at all.
|
||||||
{{- if not .Values.secrets.cwaSecretRef }}
|
|
||||||
|
|
||||||
secrets.cwaSecretRef IS UNSET. The OnePasswordItem has been created with an
|
secrets.cwaSecretRef itself is required: the template guards it with
|
||||||
empty itemPath, the operator has nothing to resolve, and the Secret above will
|
required(), so a release without it does not render at all rather than
|
||||||
never appear — the pod cannot start. Set it to the item's op:// path and
|
creating a OnePasswordItem with an empty itemPath.
|
||||||
upgrade.
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
CHANGING THE ITEM'S CONTENT RESTARTS NOTHING
|
CHANGING THE ITEM'S CONTENT RESTARTS NOTHING
|
||||||
Helm never sees what is inside a OnePasswordItem, so it cannot hash it into the
|
Helm never sees what is inside a OnePasswordItem, so it cannot hash it into the
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ kind: OnePasswordItem
|
|||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
||||||
spec:
|
spec:
|
||||||
itemPath: {{ .Values.secrets.cwaSecretRef }}
|
itemPath: {{ required "A 1Password item reference must be provided (secrets.cwaSecretRef)" .Values.secrets.cwaSecretRef }}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"cwaSecretRef": {
|
"cwaSecretRef": {
|
||||||
|
"description": "op:// item path carrying \"hardcover-api-token\" and \"calibre-plugins\". Required - the OnePasswordItem, the HARDCOVER_TOKEN env reference and the plugin volume are all rendered unconditionally. Enforced by required() in the template, not here: the null default keeps this type widened, and a schema \"required\" entry could never fire against a key that is present with value null.",
|
||||||
"type": ["string", "null"]
|
"type": ["string", "null"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,15 @@
|
|||||||
scaleDown: false
|
scaleDown: false
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
|
# REQUIRED - op:// item path for the 1Password operator, carrying the fields
|
||||||
|
# "hardcover-api-token" and "calibre-plugins". Both are needed even if you
|
||||||
|
# never use Hardcover: the OnePasswordItem is created unconditionally, and so
|
||||||
|
# are the environment reference and the volume. Without this value the render
|
||||||
|
# aborts - required() in calibre-web-automated.secret.yaml is the whole
|
||||||
|
# enforcement, and values.schema.json cannot help here (README, guideline 12).
|
||||||
|
# Before the guard existed it produced an OnePasswordItem with an empty
|
||||||
|
# itemPath, the operator had nothing to resolve, the Secret never appeared and
|
||||||
|
# the pod could not start.
|
||||||
cwaSecretRef: null
|
cwaSecretRef: null
|
||||||
|
|
||||||
cwa:
|
cwa:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type: application
|
|||||||
# continuously, so a pod restart could pull a different image than the last one
|
# continuously, so a pod restart could pull a different image than the last one
|
||||||
# without anything being recorded anywhere. The dated tags are immutable, so the
|
# without anything being recorded anywhere. The dated tags are immutable, so the
|
||||||
# image is now pinned by the chart version like every other chart in this repo.
|
# image is now pinned by the chart version like every other chart in this repo.
|
||||||
version: 5.0.0+up2026.8.2.java25
|
version: 6.0.0+up2026.8.2.java25
|
||||||
appVersion: "2026.8.2-java25"
|
appVersion: "2026.8.2-java25"
|
||||||
|
|
||||||
maintainers:
|
maintainers:
|
||||||
|
|||||||
@@ -204,9 +204,11 @@ OPERATORS ARE SYNCHRONIZED FROM VALUES, NOT FROM THE GAME
|
|||||||
SECRETS — WHICH FIELDS THE 1PASSWORD ITEM MUST CARRY
|
SECRETS — WHICH FIELDS THE 1PASSWORD ITEM MUST CARRY
|
||||||
The OnePasswordItem CR {{ $secret }} is rendered unconditionally, so
|
The OnePasswordItem CR {{ $secret }} is rendered unconditionally, so
|
||||||
secrets.minecraftServerRef has to be set even when nothing consumes a key from
|
secrets.minecraftServerRef has to be set even when nothing consumes a key from
|
||||||
it; it has no default and would otherwise render an itemPath of null.
|
it; it has no default and would otherwise render an itemPath of null. The
|
||||||
|
template guards it with required(), so a release without it does not render at
|
||||||
|
all — this note tells you why the guard is there, not that you got past it.
|
||||||
|
|
||||||
secrets.minecraftServerRef → {{ .Values.secrets.minecraftServerRef | default "NOT SET" }}
|
secrets.minecraftServerRef → {{ .Values.secrets.minecraftServerRef }}
|
||||||
|
|
||||||
The operator copies the item's FIELDS into the Kubernetes Secret, one key per
|
The operator copies the item's FIELDS into the Kubernetes Secret, one key per
|
||||||
field name. This release consumes:
|
field name. This release consumes:
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ kind: OnePasswordItem
|
|||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-secret
|
||||||
spec:
|
spec:
|
||||||
itemPath: {{ .Values.secrets.minecraftServerRef }}
|
itemPath: {{ required "A 1Password item reference must be provided (secrets.minecraftServerRef) - the OnePasswordItem is rendered unconditionally, so it is needed even with RCON and CurseForge both off" .Values.secrets.minecraftServerRef }}
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"minecraftServerRef": {
|
"minecraftServerRef": {
|
||||||
|
"description": "op:// item path for the 1Password operator. Required even when RCON and the CurseForge downloader are both off: the OnePasswordItem is rendered unconditionally. The item's CONTENT may be empty in that case, the reference may not. Enforced by required() in the template, not here: the null default keeps this type widened, and a schema \"required\" entry could never fire against a key that is present with value null.",
|
||||||
"type": ["string", "null"]
|
"type": ["string", "null"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,15 @@ image:
|
|||||||
tagOverride: ""
|
tagOverride: ""
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
# op:// item path for the 1Password operator. Only needs to carry a
|
# REQUIRED - op:// item path for the 1Password operator. The OnePasswordItem
|
||||||
# "curseforge-api-key" key when curseforge.enabled is true and an
|
# CR is rendered unconditionally, so this reference has to be set even when
|
||||||
# "rcon-password" key when server.rcon.enabled is true.
|
# nothing consumes a key from it; it has no default and would otherwise render
|
||||||
|
# an itemPath of null. The item's CONTENT may be empty in that case: it only
|
||||||
|
# needs to carry a "curseforge-api-key" key when curseforge.enabled is true
|
||||||
|
# and an "rcon-password" key when server.rcon.enabled is true. Without the
|
||||||
|
# value the render aborts - required() in minecraft-server.secret.yaml is the
|
||||||
|
# whole enforcement, and values.schema.json cannot help here (README,
|
||||||
|
# guideline 12).
|
||||||
#
|
#
|
||||||
# Documented limit (chart 3.0.0): changing this path rolls the pod (the
|
# Documented limit (chart 3.0.0): changing this path rolls the pod (the
|
||||||
# StatefulSet hashes the rendered OnePasswordItem into checksum/secret-ref),
|
# StatefulSet hashes the rendered OnePasswordItem into checksum/secret-ref),
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ type: application
|
|||||||
# (2026.8.22-9fea41204) and "latest" moves daily. appVersion is therefore the
|
# (2026.8.22-9fea41204) and "latest" moves daily. appVersion is therefore the
|
||||||
# exact tag, and the +up suffix normalizes the dash to a dot as the versioning
|
# exact tag, and the +up suffix normalizes the dash to a dot as the versioning
|
||||||
# scheme requires - the same shape rss-bridge and minecraft-server use.
|
# scheme requires - the same shape rss-bridge and minecraft-server use.
|
||||||
version: 1.0.5+up2026.9.13.3b8009065
|
version: 1.0.6+up2026.9.13.3b8009065
|
||||||
appVersion: "2026.9.13-3b8009065"
|
appVersion: "2026.9.13-3b8009065"
|
||||||
|
|
||||||
maintainers:
|
maintainers:
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ Usage (the values path to the resources dict varies per chart):
|
|||||||
resources:
|
resources:
|
||||||
{{- include "searxng.resources" .Values.<app>.resources | nindent 12 }}
|
{{- include "searxng.resources" .Values.<app>.resources | nindent 12 }}
|
||||||
|
|
||||||
NOTE: charts in this repo deliberately have no dependencies, so this helper is
|
NOTE: this helper is duplicated into every chart's templates/_helpers.tpl with
|
||||||
duplicated into every chart's templates/_helpers.tpl with the chart's name as
|
the chart's name as define prefix, because sharing it would mean a library
|
||||||
define prefix. template-chart holds the reference implementation.
|
chart, and a dependency is admissible but expensive (README, guideline 14) -
|
||||||
|
this chart takes none. template-chart holds the reference implementation.
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "searxng.resources" -}}
|
{{- define "searxng.resources" -}}
|
||||||
{{- $resources := . | default dict -}}
|
{{- $resources := . | default dict -}}
|
||||||
@@ -168,9 +169,9 @@ Rules:
|
|||||||
- keys only present in the override are passed through unchanged.
|
- keys only present in the override are passed through unchanged.
|
||||||
|
|
||||||
NOTE: like the helpers above, this is duplicated into every chart's
|
NOTE: like the helpers above, this is duplicated into every chart's
|
||||||
templates/_helpers.tpl with the chart's name as define prefix, because charts
|
templates/_helpers.tpl with the chart's name as define prefix, because sharing
|
||||||
in this repo deliberately have no dependencies. template-chart holds the
|
it would mean a library chart and this chart takes no dependency (README,
|
||||||
reference implementation.
|
guideline 14). template-chart holds the reference implementation.
|
||||||
*/}}
|
*/}}
|
||||||
{{- define "searxng.defaultedDict" -}}
|
{{- define "searxng.defaultedDict" -}}
|
||||||
{{- $defaults := .defaults | default dict -}}
|
{{- $defaults := .defaults | default dict -}}
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
# Optional, and only meaningful with settings.limiter: true. The item must
|
# Optional, and only meaningful with settings.limiter: true. The item must
|
||||||
# carry a "valkey-url" field pointing at an EXTERNAL Valkey/Redis; the chart
|
# carry a "valkey-url" field pointing at an EXTERNAL Valkey/Redis; the chart
|
||||||
# deploys none of its own, because charts in this repo have no dependencies.
|
# deploys none of its own. Chart dependencies are admissible since #214
|
||||||
|
# (README, guideline 14), but this chart declares none: an external connection
|
||||||
|
# through a secret is the same shape outline and paperless-ngx use for their
|
||||||
|
# databases.
|
||||||
itemPath: {{ .Values.secrets.valkeySecretRef }}
|
itemPath: {{ .Values.secrets.valkeySecretRef }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
+4
-4
@@ -32,10 +32,10 @@ secrets:
|
|||||||
# unless the bot detection below is switched on.
|
# unless the bot detection below is switched on.
|
||||||
#
|
#
|
||||||
# Setting it renders a second OnePasswordItem and passes the URL to the
|
# Setting it renders a second OnePasswordItem and passes the URL to the
|
||||||
# container as SEARXNG_VALKEY_URL. The chart ships NO Valkey of its own -
|
# container as SEARXNG_VALKEY_URL. The chart ships NO Valkey of its own.
|
||||||
# charts in this repo have no dependencies (README, guideline 13), so an
|
# Chart dependencies are admissible since #214 (README, guideline 14), but
|
||||||
# external connection through a secret is the same shape outline and
|
# this chart declares none, so an external connection through a secret is the
|
||||||
# paperless-ngx use for their databases.
|
# same shape outline and paperless-ngx use for their databases.
|
||||||
valkeySecretRef: null
|
valkeySecretRef: null
|
||||||
#
|
#
|
||||||
# Documented limitation (README, guideline 9): the CONTENT of these items is
|
# Documented limitation (README, guideline 9): the CONTENT of these items is
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ description: A Helm chart to deploy urlaubsverwaltung on Kubernetes
|
|||||||
icon: https://raw.githubusercontent.com/urlaubsverwaltung/urlaubsverwaltung/main/src/main/resources/static/favicons/apple-touch-icon.png
|
icon: https://raw.githubusercontent.com/urlaubsverwaltung/urlaubsverwaltung/main/src/main/resources/static/favicons/apple-touch-icon.png
|
||||||
type: application
|
type: application
|
||||||
|
|
||||||
version: 9.0.5+up6.13.1
|
version: 10.0.0+up6.13.1
|
||||||
appVersion: 6.13.1
|
appVersion: 6.13.1
|
||||||
@@ -3,4 +3,4 @@ kind: OnePasswordItem
|
|||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret
|
||||||
spec:
|
spec:
|
||||||
itemPath: {{ .Values.secrets.databaseSecretRef }}
|
itemPath: {{ required "A database Secret Reference must be provided (secrets.databaseSecretRef)" .Values.secrets.databaseSecretRef }}
|
||||||
@@ -3,4 +3,4 @@ kind: OnePasswordItem
|
|||||||
metadata:
|
metadata:
|
||||||
name: {{ .Release.Name }}-{{ .Chart.Name }}-oidc-secret
|
name: {{ .Release.Name }}-{{ .Chart.Name }}-oidc-secret
|
||||||
spec:
|
spec:
|
||||||
itemPath: {{ .Values.secrets.oidcSecretRef }}
|
itemPath: {{ required "An OIDC Secret Reference must be provided (secrets.oidcSecretRef)" .Values.secrets.oidcSecretRef }}
|
||||||
@@ -17,9 +17,11 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"databaseSecretRef": {
|
"databaseSecretRef": {
|
||||||
|
"description": "op:// item path carrying \"jdbc-url\", \"username\" and \"password\". Required - the chart deploys no database and all state lives in the external one. Enforced by required() in the template, not here: the null default keeps this type widened, and a schema \"required\" entry could never fire against a key that is present with value null.",
|
||||||
"type": ["string", "null"]
|
"type": ["string", "null"]
|
||||||
},
|
},
|
||||||
"oidcSecretRef": {
|
"oidcSecretRef": {
|
||||||
|
"description": "op:// item path carrying \"client-id\", \"client-secret\", \"issuer-name\", \"issuer-uri\", \"jwt-issuer-uri\" and \"redirect-uri\". Required - the chart offers no non-OIDC login and the app resolves the issuer at boot. Enforced by required() in the template, not here: the null default keeps this type widened, and a schema \"required\" entry could never fire against a key that is present with value null.",
|
||||||
"type": ["string", "null"]
|
"type": ["string", "null"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,21 @@
|
|||||||
scaleDown: false
|
scaleDown: false
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
|
# REQUIRED - op:// item path carrying "jdbc-url", "username" and "password".
|
||||||
|
# This chart deploys NO database; all state lives in the external one, and
|
||||||
|
# three datasource environment variables read these keys unconditionally.
|
||||||
|
# Without the value the render aborts - required() in
|
||||||
|
# urlaubsverwaltung-db.secret.yaml is the whole enforcement, and
|
||||||
|
# values.schema.json cannot help here (README, guideline 12).
|
||||||
databaseSecretRef: null
|
databaseSecretRef: null
|
||||||
|
# REQUIRED - op:// item path carrying "client-id", "client-secret",
|
||||||
|
# "issuer-name", "issuer-uri", "jwt-issuer-uri" and "redirect-uri". This chart
|
||||||
|
# offers no login path besides OIDC, and the identity provider is required at
|
||||||
|
# boot, not at login: the application resolves <issuer-uri>/.well-known/
|
||||||
|
# openid-configuration while building its security beans, before the HTTP port
|
||||||
|
# opens, and exits when it cannot. Without the value the render aborts -
|
||||||
|
# required() in urlaubsverwaltung-oidc.secret.yaml is the whole enforcement,
|
||||||
|
# and values.schema.json cannot help here (README, guideline 12).
|
||||||
oidcSecretRef: null
|
oidcSecretRef: null
|
||||||
|
|
||||||
ingress:
|
ingress:
|
||||||
|
|||||||
Reference in new issue
Block a user