diff --git a/helm/README.md b/helm/README.md index 283fc9ea..a3c75f68 100644 --- a/helm/README.md +++ b/helm/README.md @@ -42,6 +42,38 @@ data: Rendering fails with a clear error when `existingConfigmap` is not set. +### Custom catalog profile + +The catalog profile (queryables, record type, xsd) is a plugin built into the +image. To serve a different one, e.g. an older profile version on a newer +image, set `profile.name` and `profile.schemaFile` and add the keys +`profile.py` and `profile.xsd` to the team ConfigMap. They are mounted over +`pycsw/plugins/profiles//`. + +The profile reads `mappings['pycsw:X']` keys that `mappings.py` must define, +or pycsw crashes at startup with `KeyError`. Validate them together where the +ConfigMap is built: + +```yaml +# wrapper values.yaml +pycsw: + profile: + name: mc_dem + schemaFile: mcdem-record.xsd +``` + +```yaml +# wrapper templates/configmap.yaml +{{- include "pycsw.profile.validate" (dict "profile" (.Files.Get "config/profile.py") "mappings" (.Files.Get "config/mappings.py") "schemaFile" .Values.pycsw.profile.schemaFile) }} +data: + # ...pycsw.cfg, mappings.py as above + profile.py: {{ .Files.Get "config/profile.py" | quote }} + profile.xsd: {{ .Files.Get "config/profile.xsd" | quote }} +``` + +The render fails when the profile references a key missing from `mappings.py`, +or when `schemaFile` is not the xsd the profile references. + ### Environment variables available to pycsw.cfg pycsw expands `${VAR}` placeholders in `pycsw.cfg` at runtime, so the team config diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index 79e0dcf0..f25fda63 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -143,6 +143,33 @@ Fails rendering when not provided. {{- required "existingConfigmap is required: set it to the name of a ConfigMap containing the keys pycsw.cfg and mappings.py (provided by the consuming chart)" $name -}} {{- end -}} +{{/* +A profile key missing from mappings.py only fails at runtime (KeyError); this +fails the render instead. A subchart can't read its parent's files, so the +consuming chart must call it where it builds the ConfigMap. +*/}} +{{- define "pycsw.profile.validate" -}} +{{- $profile := regexReplaceAll "(?m)^\\s*#.*$" (required "pycsw.profile.validate: profile content is empty" .profile) "" -}} +{{- $mappings := regexReplaceAll "(?m)^\\s*#.*$" (required "pycsw.profile.validate: mappings content is empty" .mappings) "" -}} +{{- $defined := list -}} +{{- range regexFindAll "'pycsw:[A-Za-z0-9_]+'\\s*:" $mappings -1 -}} +{{- $defined = append $defined (regexFind "pycsw:[A-Za-z0-9_]+" .) -}} +{{- end -}} +{{- $missing := list -}} +{{- range regexFindAll "\\[\\s*'pycsw:[A-Za-z0-9_]+'\\s*\\]" $profile -1 -}} +{{- $key := regexFind "pycsw:[A-Za-z0-9_]+" . -}} +{{- if not (has $key $defined) -}} +{{- $missing = append $missing $key -}} +{{- end -}} +{{- end -}} +{{- if $missing -}} +{{- fail (printf "pycsw profile references keys missing from mappings.py: %s" (join ", " ($missing | uniq))) -}} +{{- end -}} +{{- if not (contains (printf "'%s'" (required "pycsw.profile.validate: schemaFile is required" .schemaFile)) $profile) -}} +{{- fail (printf "pycsw profile does not reference schemaFile '%s' in its schemas_paths" .schemaFile) -}} +{{- end -}} +{{- end -}} + {{/* Returns the postgres connection string as an environment variable value. Uses Kubernetes dependent-variable syntax $(VAR) so it is resolved at container diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index 2bc70bd4..9a7ab306 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -89,6 +89,14 @@ spec: - name: pyscw-mappings mountPath: /etc/pycsw/mappings.py subPath: mappings.py + {{- with .Values.profile.name }} + - name: pycsw-profile + mountPath: {{ printf "/home/pycsw/pycsw/plugins/profiles/%s/%s.py" . . }} + subPath: profile.py + - name: pycsw-profile + mountPath: {{ printf "/home/pycsw/pycsw/plugins/profiles/%s/schemas/%s" . (required "profile.schemaFile is required when profile.name is set" $.Values.profile.schemaFile) }} + subPath: profile.xsd + {{- end }} - mountPath: "/pycsw/uwsgi.ini" name: uwsgi-config subPath: uwsgi.ini @@ -184,6 +192,16 @@ spec: items: - key: "mappings.py" path: "mappings.py" + {{- if .Values.profile.name }} + - name: pycsw-profile + configMap: + name: {{ $existingConfigmapName }} + items: + - key: "profile.py" + path: "profile.py" + - key: "profile.xsd" + path: "profile.xsd" + {{- end }} - name: uwsgi-config configMap: name: {{ $configmapName }} diff --git a/helm/values.yaml b/helm/values.yaml index 11728751..a5e50685 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -45,6 +45,14 @@ nameOverride: "" # The value may be a template, e.g. '{{ .Release.Name }}-pycsw-config' existingConfigmap: "" +# When name is set, existingConfigmap must also hold profile.py and profile.xsd; +# build it with "pycsw.profile.validate" so a mappings mismatch fails the render. +profile: + # must match "profiles=" in pycsw.cfg; "" keeps the image's built-in profile + name: "" + # must match the xsd file name in the profile's schemas_paths + schemaFile: "" + enabled: true environment: development replicaCount: 1