From 7c7d79afaf170d0b152ba88d544b291b06b56db7 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 10:11:36 +0200 Subject: [PATCH 01/21] UNSOI-3673 Deployment files for Plan.R Tools --- charts/planr-tools/.helmignore | 17 ++ charts/planr-tools/Chart.yaml | 13 ++ charts/planr-tools/README.md | 68 +++++++ charts/planr-tools/REVIEW.md | 53 +++++ charts/planr-tools/templates/_helpers.tpl | 93 +++++++++ charts/planr-tools/templates/configmap.yaml | 17 ++ charts/planr-tools/templates/deployment.yaml | 120 ++++++++++++ charts/planr-tools/templates/httproute.yaml | 41 ++++ charts/planr-tools/templates/ingress.yaml | 45 +++++ charts/planr-tools/templates/service.yaml | 30 +++ .../planr-tools/templates/serviceaccount.yaml | 13 ++ charts/planr-tools/test-values-httproute.yaml | 16 ++ charts/planr-tools/test-values-ingress.yaml | 6 + charts/planr-tools/tests/deployment_test.yaml | 91 +++++++++ charts/planr-tools/tests/httproute_test.yaml | 42 ++++ .../planr-tools/tests/values/httproute.yaml | 15 ++ charts/planr-tools/values.yaml | 184 ++++++++++++++++++ 17 files changed, 864 insertions(+) create mode 100644 charts/planr-tools/.helmignore create mode 100644 charts/planr-tools/Chart.yaml create mode 100644 charts/planr-tools/README.md create mode 100644 charts/planr-tools/REVIEW.md create mode 100644 charts/planr-tools/templates/_helpers.tpl create mode 100644 charts/planr-tools/templates/configmap.yaml create mode 100644 charts/planr-tools/templates/deployment.yaml create mode 100644 charts/planr-tools/templates/httproute.yaml create mode 100644 charts/planr-tools/templates/ingress.yaml create mode 100644 charts/planr-tools/templates/service.yaml create mode 100644 charts/planr-tools/templates/serviceaccount.yaml create mode 100644 charts/planr-tools/test-values-httproute.yaml create mode 100644 charts/planr-tools/test-values-ingress.yaml create mode 100644 charts/planr-tools/tests/deployment_test.yaml create mode 100644 charts/planr-tools/tests/httproute_test.yaml create mode 100644 charts/planr-tools/tests/values/httproute.yaml create mode 100644 charts/planr-tools/values.yaml diff --git a/charts/planr-tools/.helmignore b/charts/planr-tools/.helmignore new file mode 100644 index 00000000..21846e96 --- /dev/null +++ b/charts/planr-tools/.helmignore @@ -0,0 +1,17 @@ +.DS_Store +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +*.swp +*.bak +*.tmp +*.orig +*~ +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/planr-tools/Chart.yaml b/charts/planr-tools/Chart.yaml new file mode 100644 index 00000000..4ba2c03e --- /dev/null +++ b/charts/planr-tools/Chart.yaml @@ -0,0 +1,13 @@ +apiVersion: v2 +name: planr-tools +description: A Helm chart for deploying the planr-tools applications +type: application +version: 0.1.0 +appVersion: "6.0.0-SNAPSHOT" +annotations: + artifacthub.io/changes: | + - kind: added + description: Initial release of the planr-tools chart. +sources: + - https://github.com/subshell/helm-charts/tree/main/charts/planr-tools +home: https://gitlab.com/subshell/kunden/planr-tools diff --git a/charts/planr-tools/README.md b/charts/planr-tools/README.md new file mode 100644 index 00000000..1486d3b0 --- /dev/null +++ b/charts/planr-tools/README.md @@ -0,0 +1,68 @@ +# planr-tools + +This chart deploys the three `planr-tools` applications together: + +* `newsroom-document-creator` +* `newsroom-feed` +* `newsroom-widget` + +Each application is deployed as its own `Deployment`, `Service`, and `ConfigMap`, while the chart offers shared `Ingress` and `HTTPRoute` resources for path-based external access. + +## Example values.yaml + +```yaml +ingress: + enabled: true + ingressClassName: nginx + hosts: + - host: planr-tools.example.com + +httpRoute: + enabled: false + +applications: + documentCreator: + service: + type: ClusterIP + config: + sophora: + client: + server-connection: + urls: https://cms.example.com + username: ${SOPHORA_USERNAME} + password: ${SOPHORA_PASSWORD} + + feed: + service: + type: ClusterIP + config: + sophora: + client: + server-connection: + urls: https://cms.example.com + username: ${SOPHORA_USERNAME} + password: ${SOPHORA_PASSWORD} + + widget: + service: + type: ClusterIP + config: + sophora: + client: + server-connection: + urls: https://cms.example.com + username: ${SOPHORA_USERNAME} + password: ${SOPHORA_PASSWORD} + +commonEnv: + - name: SOPHORA_USERNAME + valueFrom: + secretKeyRef: + name: planr-tools-credentials + key: username + - name: SOPHORA_PASSWORD + valueFrom: + secretKeyRef: + name: planr-tools-credentials + key: password +``` diff --git a/charts/planr-tools/REVIEW.md b/charts/planr-tools/REVIEW.md new file mode 100644 index 00000000..574b0dde --- /dev/null +++ b/charts/planr-tools/REVIEW.md @@ -0,0 +1,53 @@ +# planr-tools Chart Review + +Code review of the initial chart implementation. Work through these items top to bottom. + +## ๐Ÿ”ด High Severity + +- โœ… **#1 โ€” Missing liveness/readiness probes** (`templates/deployment.yaml`) + Add `livenessProbe`, `readinessProbe`, and `startupProbe` referencing the Spring Boot actuator + endpoints (`/actuator/health/liveness`, `/actuator/health/readiness`) โ€” as done in `sophora-ai`. + Without probes, failed pods are never restarted and never removed from load balancing. ๐Ÿ’€ + +- โœ… **#2 โ€” Default `service.type: LoadBalancer`** (`values.yaml` โ€” all three applications) + Will provision three โ˜๏ธ cloud load balancers on a default install. Since the chart is designed for + path-based routing via Ingress or HTTPRoute, `ClusterIP` should be the default. + (`test-values.yaml` already overrides this, confirming the default is wrong.) + +## ๐ŸŸก Medium Severity + +- โœ… **#3 โ€” `JAVA_OPTS` env var name** (`templates/deployment.yaml`) + Verified: all three Dockerfiles use `JAVA_OPTS` directly in their `ENTRYPOINT`. No change needed. + +- โœ… **#4 โ€” Missing `annotations.artifacthub.io/changes`** (`Chart.yaml`) + Every other chart in the repo has this annotation; the release workflow likely depends on it ๐Ÿš€. + Add an initial entry, e.g.: + ```yaml + annotations: + artifacthub.io/changes: | + - kind: added + description: Initial release of the planr-tools chart. + ``` + +- โœ… **#5 โ€” `sources` URL points to the application repo, not the chart repo** (`Chart.yaml`) + All other charts set `sources` to `https://github.com/subshell/helm-charts/tree/main/charts/`. + The application GitLab URL belongs in `home`, not `sources`. ๐Ÿ  + +- โœ… **#6 โ€” Hardcoded component list repeated across 5 template files** ๐Ÿ” + A shared helper would require a non-obvious `fromYaml` wrapper pattern. Accepted as intentional + duplication instead โ€” added a comment to each file explaining the ordering requirement and + listing the files that need to stay in sync. + +## ๐ŸŸข Low Severity + +- โœ… **#7 โ€” Thin test coverage** (`tests/`) ๐Ÿงช + Only the HTTPRoute is tested. The existing test checks backend names by index but not the path + values (`/document-creator`, `/feed`, `/`) that are the core routing logic. Consider adding: + - Path value assertions to the existing httproute test + - At least a smoke test for Deployment and Ingress + +- โœ… **#8 โ€” `test-values.yaml` enables both `ingress` and `httpRoute` simultaneously** ๐Ÿคท + Replaced with `test-values-ingress.yaml` and `test-values-httproute.yaml`. + +- โญ๏ธ **#9 โ€” Missing `icon` field** (`Chart.yaml`) ๐Ÿ–ผ๏ธ + Skipped โ€” no icon available for this customer-specific chart. diff --git a/charts/planr-tools/templates/_helpers.tpl b/charts/planr-tools/templates/_helpers.tpl new file mode 100644 index 00000000..893d12d6 --- /dev/null +++ b/charts/planr-tools/templates/_helpers.tpl @@ -0,0 +1,93 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "planr-tools.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "planr-tools.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "planr-tools.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels. +*/}} +{{- define "planr-tools.labels" -}} +helm.sh/chart: {{ include "planr-tools.chart" .root }} +{{ include "planr-tools.selectorLabels" . }} +{{- if .root.Chart.AppVersion }} +app.kubernetes.io/version: {{ .root.Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .root.Release.Service }} +{{- end }} + +{{/* +Selector labels. +*/}} +{{- define "planr-tools.selectorLabels" -}} +app.kubernetes.io/name: {{ include "planr-tools.name" .root }} +app.kubernetes.io/instance: {{ .root.Release.Name }} +app.kubernetes.io/component: {{ .component.name }} +{{- end }} + +{{/* +Service account name. +*/}} +{{- define "planr-tools.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "planr-tools.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Component fullname. +*/}} +{{- define "planr-tools.componentFullname" -}} +{{- printf "%s-%s" (include "planr-tools.fullname" .root) .component.name | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Component configmap name. +*/}} +{{- define "planr-tools.componentConfigName" -}} +{{- printf "%s-config" (include "planr-tools.componentFullname" .) | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Config checksum. +*/}} +{{- define "planr-tools.componentConfigChecksum" -}} +{{- toYaml .component.config | sha256sum }} +{{- end }} + +{{/* +Renders a value that contains templates. +*/}} +{{- define "planr-tools.render" -}} +{{- if typeIs "string" .value }} +{{- tpl .value .context }} +{{- else }} +{{- tpl (.value | toYaml) .context }} +{{- end }} +{{- end -}} diff --git a/charts/planr-tools/templates/configmap.yaml b/charts/planr-tools/templates/configmap.yaml new file mode 100644 index 00000000..a53744d2 --- /dev/null +++ b/charts/planr-tools/templates/configmap.yaml @@ -0,0 +1,17 @@ +{{- $root := . -}} +{{/* Keep in sync with deployment.yaml, service.yaml, ingress.yaml, httproute.yaml. */}} +{{- range $appKey := list "documentCreator" "feed" "widget" }} +{{- $component := index $root.Values.applications $appKey }} +{{- if $component.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "planr-tools.componentConfigName" (dict "root" $root "component" $component) }} + labels: + {{- include "planr-tools.labels" (dict "root" $root "component" $component) | nindent 4 }} +data: + application.yml: |- + {{- include "planr-tools.render" (dict "value" $component.config "context" $root) | nindent 4 }} +--- +{{- end }} +{{- end }} diff --git a/charts/planr-tools/templates/deployment.yaml b/charts/planr-tools/templates/deployment.yaml new file mode 100644 index 00000000..61079e1f --- /dev/null +++ b/charts/planr-tools/templates/deployment.yaml @@ -0,0 +1,120 @@ +{{- $root := . -}} +{{/* Keep in sync with configmap.yaml, service.yaml, ingress.yaml, httproute.yaml. */}} +{{- range $appKey := list "documentCreator" "feed" "widget" }} +{{- $component := index $root.Values.applications $appKey }} +{{- if $component.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "planr-tools.componentFullname" (dict "root" $root "component" $component) }} + labels: + {{- include "planr-tools.labels" (dict "root" $root "component" $component) | nindent 4 }} +spec: + replicas: {{ default $root.Values.replicaCount $component.replicaCount }} + selector: + matchLabels: + {{- include "planr-tools.selectorLabels" (dict "root" $root "component" $component) | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include "planr-tools.componentConfigChecksum" (dict "root" $root "component" $component) }} + {{- with $root.Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "planr-tools.selectorLabels" (dict "root" $root "component" $component) | nindent 8 }} + spec: + {{- if $component.hostname }} + hostname: {{ $component.hostname }} + {{- end }} + serviceAccountName: {{ include "planr-tools.serviceAccountName" $root }} + automountServiceAccountToken: {{ $root.Values.serviceAccount.automount }} + {{- with $root.Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml $root.Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ $component.name }} + image: "{{ $component.image.repository }}:{{ $component.image.tag | default $root.Chart.AppVersion }}" + imagePullPolicy: {{ $component.image.pullPolicy }} + securityContext: + {{- toYaml $root.Values.containerSecurityContext | nindent 12 }} + env: + - name: JAVA_OPTS + value: {{ $root.Values.javaOptions | quote }} + {{- with $root.Values.commonEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $component.env }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if or $root.Values.commonEnvFrom $component.envFrom }} + envFrom: + {{- with $root.Values.commonEnvFrom }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $component.envFrom }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + ports: + - name: http + containerPort: {{ $component.service.targetPort }} + protocol: TCP + {{- with $root.Values.startupProbe }} + startupProbe: + httpGet: + port: http + path: /actuator/health/liveness + failureThreshold: {{ .failureThreshold }} + initialDelaySeconds: {{ .initialDelaySeconds }} + periodSeconds: {{ .periodSeconds }} + timeoutSeconds: {{ .timeoutSeconds }} + {{- end }} + {{- with $root.Values.livenessProbe }} + livenessProbe: + httpGet: + port: http + path: /actuator/health/liveness + failureThreshold: {{ .failureThreshold }} + initialDelaySeconds: {{ .initialDelaySeconds }} + periodSeconds: {{ .periodSeconds }} + timeoutSeconds: {{ .timeoutSeconds }} + {{- end }} + {{- with $root.Values.readinessProbe }} + readinessProbe: + httpGet: + port: http + path: /actuator/health/readiness + failureThreshold: {{ .failureThreshold }} + initialDelaySeconds: {{ .initialDelaySeconds }} + periodSeconds: {{ .periodSeconds }} + timeoutSeconds: {{ .timeoutSeconds }} + {{- end }} + volumeMounts: + - name: application-config + mountPath: /app/application.yml + subPath: application.yml + resources: + {{- toYaml ($component.resources | default $root.Values.resources) | nindent 12 }} + volumes: + - name: application-config + configMap: + name: {{ include "planr-tools.componentConfigName" (dict "root" $root "component" $component) }} + {{- with $root.Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $root.Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with $root.Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/charts/planr-tools/templates/httproute.yaml b/charts/planr-tools/templates/httproute.yaml new file mode 100644 index 00000000..10c8fd38 --- /dev/null +++ b/charts/planr-tools/templates/httproute.yaml @@ -0,0 +1,41 @@ +{{- if .Values.httpRoute.enabled }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "planr-tools.fullname" . }} + labels: + {{- include "planr-tools.labels" (dict "root" . "component" (dict "name" "gateway")) | nindent 4 }} + {{- with .Values.httpRoute.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.httpRoute.parentRefs }} + parentRefs: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.httpRoute.hostnames }} + hostnames: + {{- range . }} + - {{ . | quote }} + {{- end }} + {{- end }} + rules: + {{/* Keep in sync with deployment.yaml, configmap.yaml, service.yaml, ingress.yaml. */}} + {{- range $appKey := list "documentCreator" "feed" "widget" }} + {{- $component := index $.Values.applications $appKey }} + {{- if $component.enabled }} + - matches: + - path: + type: PathPrefix + value: {{ $component.exposure.path | quote }} + backendRefs: + - name: {{ include "planr-tools.componentFullname" (dict "root" $ "component" $component) }} + port: {{ $component.service.port }} + {{- with $.Values.httpRoute.filters }} + filters: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/planr-tools/templates/ingress.yaml b/charts/planr-tools/templates/ingress.yaml new file mode 100644 index 00000000..b6914106 --- /dev/null +++ b/charts/planr-tools/templates/ingress.yaml @@ -0,0 +1,45 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "planr-tools.fullname" . }} + labels: + {{- include "planr-tools.labels" (dict "root" . "component" (dict "name" "ingress")) | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.ingressClassName }} + ingressClassName: {{ .Values.ingress.ingressClassName }} + {{- end }} + {{- with .Values.ingress.tls }} + tls: + {{- range . }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range $host := .Values.ingress.hosts }} + - host: {{ required "A hostname for the ingress must be provided in .Values.ingress.hosts[].host" $host.host }} + http: + paths: + {{/* Keep in sync with deployment.yaml, configmap.yaml, service.yaml, httproute.yaml. */}} + {{- range $appKey := list "documentCreator" "feed" "widget" }} + {{- $component := index $.Values.applications $appKey }} + {{- if $component.enabled }} + - path: {{ $component.exposure.path | quote }} + pathType: {{ default $.Values.ingress.pathType $host.pathType | default "Prefix" }} + backend: + service: + name: {{ include "planr-tools.componentFullname" (dict "root" $ "component" $component) }} + port: + number: {{ $component.service.port }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/planr-tools/templates/service.yaml b/charts/planr-tools/templates/service.yaml new file mode 100644 index 00000000..a44017c6 --- /dev/null +++ b/charts/planr-tools/templates/service.yaml @@ -0,0 +1,30 @@ +{{- $root := . -}} +{{/* Keep in sync with deployment.yaml, configmap.yaml, ingress.yaml, httproute.yaml. */}} +{{- range $appKey := list "documentCreator" "feed" "widget" }} +{{- $component := index $root.Values.applications $appKey }} +{{- if $component.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "planr-tools.componentFullname" (dict "root" $root "component" $component) }} + labels: + {{- include "planr-tools.labels" (dict "root" $root "component" $component) | nindent 4 }} + {{- with $component.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ $component.service.type }} + {{- if $component.service.loadBalancerIP }} + loadBalancerIP: {{ $component.service.loadBalancerIP }} + {{- end }} + ports: + - name: http + port: {{ $component.service.port }} + targetPort: {{ $component.service.targetPort }} + protocol: TCP + selector: + {{- include "planr-tools.selectorLabels" (dict "root" $root "component" $component) | nindent 4 }} +--- +{{- end }} +{{- end }} diff --git a/charts/planr-tools/templates/serviceaccount.yaml b/charts/planr-tools/templates/serviceaccount.yaml new file mode 100644 index 00000000..dacbbeb0 --- /dev/null +++ b/charts/planr-tools/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "planr-tools.serviceAccountName" . }} + labels: + {{- include "planr-tools.labels" (dict "root" . "component" (dict "name" "service-account")) | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/charts/planr-tools/test-values-httproute.yaml b/charts/planr-tools/test-values-httproute.yaml new file mode 100644 index 00000000..6645c300 --- /dev/null +++ b/charts/planr-tools/test-values-httproute.yaml @@ -0,0 +1,16 @@ +httpRoute: + enabled: true + parentRefs: + - name: test-gateway + namespace: gateway-system + hostnames: + - planr-tools.test.example.com + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: X-Test + value: enabled + annotations: + example.com/test: "true" + diff --git a/charts/planr-tools/test-values-ingress.yaml b/charts/planr-tools/test-values-ingress.yaml new file mode 100644 index 00000000..dc948ec4 --- /dev/null +++ b/charts/planr-tools/test-values-ingress.yaml @@ -0,0 +1,6 @@ +ingress: + enabled: true + ingressClassName: nginx + hosts: + - host: planr-tools.test.example.com + diff --git a/charts/planr-tools/tests/deployment_test.yaml b/charts/planr-tools/tests/deployment_test.yaml new file mode 100644 index 00000000..f752952c --- /dev/null +++ b/charts/planr-tools/tests/deployment_test.yaml @@ -0,0 +1,91 @@ +suite: test deployment +templates: + - deployment.yaml +chart: + version: 0.1.0 + appVersion: 6.0.0-SNAPSHOT +tests: + - it: should create 3 deployments by default + asserts: + - hasDocuments: + count: 3 + + - it: should create 1 deployment when two apps are disabled + set: + applications.feed.enabled: false + applications.widget.enabled: false + asserts: + - hasDocuments: + count: 1 + + - it: document-creator should have correct name, image and port + documentIndex: 0 + asserts: + - equal: + path: metadata.name + value: RELEASE-NAME-planr-tools-newsroom-document-creator + - equal: + path: spec.template.spec.containers[0].image + value: docker.subshell.com/planr-tools/newsroom-document-creator:6.0.0-SNAPSHOT + - equal: + path: spec.template.spec.containers[0].ports[0].containerPort + value: 8080 + + - it: feed should have correct name, image and port + documentIndex: 1 + asserts: + - equal: + path: metadata.name + value: RELEASE-NAME-planr-tools-newsroom-feed + - equal: + path: spec.template.spec.containers[0].image + value: docker.subshell.com/planr-tools/newsroom-feed:6.0.0-SNAPSHOT + - equal: + path: spec.template.spec.containers[0].ports[0].containerPort + value: 8081 + + - it: widget should have correct name, image and port + documentIndex: 2 + asserts: + - equal: + path: metadata.name + value: RELEASE-NAME-planr-tools-newsroom-widget + - equal: + path: spec.template.spec.containers[0].image + value: docker.subshell.com/planr-tools/newsroom-widget:6.0.0-SNAPSHOT + - equal: + path: spec.template.spec.containers[0].ports[0].containerPort + value: 8082 + + - it: all deployments should have liveness and readiness probes + documentIndex: 0 + asserts: + - equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.path + value: /actuator/health/liveness + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.path + value: /actuator/health/readiness + - equal: + path: spec.template.spec.containers[0].startupProbe.httpGet.path + value: /actuator/health/liveness + + - it: should mount the application config + documentIndex: 0 + asserts: + - equal: + path: spec.template.spec.containers[0].volumeMounts[0].mountPath + value: /app/application.yml + - equal: + path: spec.template.spec.volumes[0].configMap.name + value: RELEASE-NAME-planr-tools-newsroom-document-creator-config + + - it: should have a checksum annotation for config-driven restarts + documentIndex: 0 + asserts: + - exists: + path: spec.template.metadata.annotations.checksum/config + + - it: should not failedTemplate + asserts: + - notFailedTemplate: {} diff --git a/charts/planr-tools/tests/httproute_test.yaml b/charts/planr-tools/tests/httproute_test.yaml new file mode 100644 index 00000000..28d7bef0 --- /dev/null +++ b/charts/planr-tools/tests/httproute_test.yaml @@ -0,0 +1,42 @@ +suite: test httproute +templates: + - httproute.yaml +chart: + version: 0.1.0 + appVersion: 6.0.0-SNAPSHOT +tests: + - it: should not create httproute by default + asserts: + - hasDocuments: + count: 0 + + - it: should create httproute rules for all enabled applications + release: + name: values-test-release + values: + - ./values/httproute.yaml + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: HTTPRoute + apiVersion: gateway.networking.k8s.io/v1 + name: values-test-release-planr-tools + - equal: + path: metadata.annotations + value: + example.com/test: "true" + - equal: + path: spec.hostnames + value: + - planr-tools.example.com + - equal: + path: spec.rules[0].backendRefs[0].name + value: values-test-release-planr-tools-newsroom-document-creator + - equal: + path: spec.rules[1].backendRefs[0].name + value: values-test-release-planr-tools-newsroom-feed + - equal: + path: spec.rules[2].backendRefs[0].name + value: values-test-release-planr-tools-newsroom-widget + - notFailedTemplate: {} diff --git a/charts/planr-tools/tests/values/httproute.yaml b/charts/planr-tools/tests/values/httproute.yaml new file mode 100644 index 00000000..a3326e67 --- /dev/null +++ b/charts/planr-tools/tests/values/httproute.yaml @@ -0,0 +1,15 @@ +httpRoute: + enabled: true + parentRefs: + - name: my-gateway + namespace: gateway-namespace + hostnames: + - planr-tools.example.com + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: X-Custom-Header + value: custom-value + annotations: + example.com/test: "true" diff --git a/charts/planr-tools/values.yaml b/charts/planr-tools/values.yaml new file mode 100644 index 00000000..9803394d --- /dev/null +++ b/charts/planr-tools/values.yaml @@ -0,0 +1,184 @@ +replicaCount: 1 + +nameOverride: "" +fullnameOverride: "" + +imagePullSecrets: [] + +javaOptions: "-XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=80.0 -XX:+ExitOnOutOfMemoryError -XX:+PerfDisableSharedMem" + +podAnnotations: {} +podSecurityContext: {} +containerSecurityContext: {} + +nodeSelector: {} +affinity: {} +tolerations: [] + +resources: + limits: {} + requests: {} + +startupProbe: + failureThreshold: 15 + initialDelaySeconds: 5 + timeoutSeconds: 30 + periodSeconds: 1 + +livenessProbe: + failureThreshold: 3 + initialDelaySeconds: 15 + timeoutSeconds: 10 + periodSeconds: 60 + +readinessProbe: + failureThreshold: 5 + initialDelaySeconds: 5 + timeoutSeconds: 5 + periodSeconds: 5 + +serviceAccount: + create: false + automount: true + annotations: {} + name: "" + +commonEnv: [] +commonEnvFrom: [] + +ingress: + enabled: false + ingressClassName: nginx + pathType: Prefix + tls: + hosts: + annotations: {} + +httpRoute: + enabled: false + parentRefs: [] + hostnames: [] + filters: [] + annotations: {} + +applications: + documentCreator: + enabled: true + name: newsroom-document-creator + hostname: "" + image: + repository: docker.subshell.com/planr-tools/newsroom-document-creator + pullPolicy: IfNotPresent + tag: "" + service: + annotations: {} + type: ClusterIP + port: 8080 + targetPort: 8080 + loadBalancerIP: + exposure: + path: /document-creator + env: [] + envFrom: [] + resources: {} + config: + spring: + application: + name: Newsroom-Document-Creator + server: + port: 8080 + logging: + path: logs + management: + endpoints: + web: + base-path: /actuator + exposure: + include: health, info, jolokia, endpoints + sophora: + client: + server-connection: + urls: https://cms.latest.test.subshell.io/ + username: XXX + password: XXX + + feed: + enabled: true + name: newsroom-feed + hostname: "" + image: + repository: docker.subshell.com/planr-tools/newsroom-feed + pullPolicy: IfNotPresent + tag: "" + service: + annotations: {} + type: ClusterIP + port: 8081 + targetPort: 8081 + loadBalancerIP: + exposure: + path: /feed + env: [] + envFrom: [] + resources: {} + config: + spring: + application: + name: Newsroom-Feed + resources: + add-mappings: true + server: + port: 8081 + logging: + path: logs + management: + endpoints: + web: + base-path: /actuator + exposure: + include: health, info, jolokia, endpoints + sophora: + client: + server-connection: + urls: https://cms.latest.test.subshell.io/ + username: XXX + password: XXX + + widget: + enabled: true + name: newsroom-widget + hostname: "" + image: + repository: docker.subshell.com/planr-tools/newsroom-widget + pullPolicy: IfNotPresent + tag: "" + service: + annotations: {} + type: ClusterIP + port: 8082 + targetPort: 8082 + loadBalancerIP: + exposure: + path: / + env: [] + envFrom: [] + resources: {} + config: + spring: + application: + name: Newsroom-Widget + jackson: + serialization: + write_dates_as_timestamps: false + logging: + path: logs + server: + port: 8082 + compression: + enabled: true + sophora: + client: + server-connection: + urls: https://cms.latest.test.subshell.io/ + username: XXX + password: XXX From 53f6308a636386a7189bc45eb46fb01c713b7b09 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 12:45:33 +0200 Subject: [PATCH 02/21] Increase timeouts --- charts/planr-tools/values.yaml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/charts/planr-tools/values.yaml b/charts/planr-tools/values.yaml index 9803394d..a57c6c9a 100644 --- a/charts/planr-tools/values.yaml +++ b/charts/planr-tools/values.yaml @@ -20,10 +20,10 @@ resources: requests: {} startupProbe: - failureThreshold: 15 - initialDelaySeconds: 5 - timeoutSeconds: 30 - periodSeconds: 1 + failureThreshold: 30 + initialDelaySeconds: 10 + timeoutSeconds: 5 + periodSeconds: 5 livenessProbe: failureThreshold: 3 @@ -98,9 +98,9 @@ applications: sophora: client: server-connection: - urls: https://cms.latest.test.subshell.io/ - username: XXX - password: XXX + urls: # e.g. https://cms.example.com/ + username: # in secret + password: # in secret feed: enabled: true @@ -140,9 +140,9 @@ applications: sophora: client: server-connection: - urls: https://cms.latest.test.subshell.io/ - username: XXX - password: XXX + urls: # e.g. https://cms.example.com/ + username: # in secret + password: # in secret widget: enabled: true @@ -179,6 +179,6 @@ applications: sophora: client: server-connection: - urls: https://cms.latest.test.subshell.io/ - username: XXX - password: XXX + urls: # e.g. https://cms.example.com/ + username: # in secret + password: # in secret From 10e947f84ee070ebe217782cd59736bb718d0dfd Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 12:50:07 +0200 Subject: [PATCH 03/21] Fix path mapping and complete test yaml --- charts/planr-tools/templates/ingress.yaml | 34 +++++++++++++-- charts/planr-tools/test-values-ingress.yaml | 48 ++++++++++++++++++++- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/charts/planr-tools/templates/ingress.yaml b/charts/planr-tools/templates/ingress.yaml index b6914106..53863c5a 100644 --- a/charts/planr-tools/templates/ingress.yaml +++ b/charts/planr-tools/templates/ingress.yaml @@ -5,10 +5,12 @@ metadata: name: {{ include "planr-tools.fullname" . }} labels: {{- include "planr-tools.labels" (dict "root" . "component" (dict "name" "ingress")) | nindent 4 }} - {{- with .Values.ingress.annotations }} annotations: + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/rewrite-target: /$2 + {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} - {{- end }} + {{- end }} spec: {{- if .Values.ingress.ingressClassName }} ingressClassName: {{ .Values.ingress.ingressClassName }} @@ -32,8 +34,32 @@ spec: {{- range $appKey := list "documentCreator" "feed" "widget" }} {{- $component := index $.Values.applications $appKey }} {{- if $component.enabled }} - - path: {{ $component.exposure.path | quote }} - pathType: {{ default $.Values.ingress.pathType $host.pathType | default "Prefix" }} + {{- $path := $component.exposure.path }} + {{/* + All three apps share one hostname with path-based routing: + planr-tools.example.com/document-creator/... โ†’ document-creator pod + planr-tools.example.com/feed/... โ†’ feed pod + planr-tools.example.com/... โ†’ widget pod + + Without a rewrite, document-creator would receive requests as + /document-creator/api/v1/... and return 404 โ€” it only knows about /api/v1/... + The nginx rewrite-target annotation above strips the path prefix before forwarding + to the backend, so each app receives requests at its own root (e.g. /api/...) and + is completely unaware that it is being served under a subpath externally. + + To make this work, paths must be regex with two capture groups ($1, $2), because the + rewrite target is "/$2": + - Non-root (e.g. /document-creator): "/document-creator(/|$)(.*)" + $1 = the slash after the prefix (or empty at end of string) + $2 = everything after the prefix โ†’ forwarded as "/$2" + - Root (/): "/()(.*)" + $1 = empty (dummy group to keep $2 consistent) + $2 = the full path after the leading slash โ†’ forwarded as "/$2" unchanged + + pathType must be ImplementationSpecific for nginx to treat the path as a regex. + */}} + - path: {{ if eq $path "/" }}"/()(.*)"{{ else }}{{ printf "%s(/|$)(.*)" $path | quote }}{{ end }} + pathType: ImplementationSpecific backend: service: name: {{ include "planr-tools.componentFullname" (dict "root" $ "component" $component) }} diff --git a/charts/planr-tools/test-values-ingress.yaml b/charts/planr-tools/test-values-ingress.yaml index dc948ec4..0f1f81c8 100644 --- a/charts/planr-tools/test-values-ingress.yaml +++ b/charts/planr-tools/test-values-ingress.yaml @@ -2,5 +2,51 @@ ingress: enabled: true ingressClassName: nginx hosts: - - host: planr-tools.test.example.com + - host: planr-tools.example.com +imagePullSecrets: + - name: subshell-registry + +commonEnv: + - name: SPRING_PROFILES_ACTIVE + value: subshell-test-cloud + - name: SOPHORA_URL + value: # e.g. https://cms.stable.test.subshell.io + - name: SOPHORA_USERNAME + value: # in secret + - name: SOPHORA_PASSWORD + value: # in secret + +applications: + documentCreator: + image: + tag: master + config: + sophora: + client: + server-connection: + urls: ${SOPHORA_URL} + username: ${SOPHORA_USERNAME} + password: ${SOPHORA_PASSWORD} + + feed: + image: + tag: master + config: + sophora: + client: + server-connection: + urls: ${SOPHORA_URL} + username: ${SOPHORA_USERNAME} + password: ${SOPHORA_PASSWORD} + + widget: + image: + tag: master + config: + sophora: + client: + server-connection: + urls: ${SOPHORA_URL} + username: ${SOPHORA_USERNAME} + password: ${SOPHORA_PASSWORD} From 5d8bd4fa9265ca902e97ea4c70f7f23b416cb9ac Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 14:37:49 +0200 Subject: [PATCH 04/21] [UNSOI-3673] [planr-tools] strip path prefix in HTTPRoute via URLRewrite filter Add a URLRewrite/ReplacePrefixMatch filter per rule so that apps receive requests at their own root path, consistent with the nginx ingress rewrite. The filter is omitted for widget's catch-all path /. --- charts/planr-tools/REVIEW.md | 89 ++++++++++++--------- charts/planr-tools/templates/httproute.yaml | 12 ++- 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/charts/planr-tools/REVIEW.md b/charts/planr-tools/REVIEW.md index 574b0dde..812c4cac 100644 --- a/charts/planr-tools/REVIEW.md +++ b/charts/planr-tools/REVIEW.md @@ -1,53 +1,68 @@ -# planr-tools Chart Review +# planr-tools Chart Review โ€” Round 2 -Code review of the initial chart implementation. Work through these items top to bottom. +Feedback from [subshell/helm-charts#267](https://github.com/subshell/helm-charts/pull/267). Remove this file before merging. ## ๐Ÿ”ด High Severity -- โœ… **#1 โ€” Missing liveness/readiness probes** (`templates/deployment.yaml`) - Add `livenessProbe`, `readinessProbe`, and `startupProbe` referencing the Spring Boot actuator - endpoints (`/actuator/health/liveness`, `/actuator/health/readiness`) โ€” as done in `sophora-ai`. - Without probes, failed pods are never restarted and never removed from load balancing. ๐Ÿ’€ +- โœ… **#1 โ€” HTTPRoute does not strip path prefix** (`templates/httproute.yaml`) + Like the Ingress, the HTTPRoute currently forwards requests with the external prefix still present + (e.g. `/document-creator/api/...`). Add a per-rule `URLRewrite` filter to strip the prefix: + ```yaml + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + ``` + Note: only needed for non-root exposure paths, widget's `/` is fine as-is. -- โœ… **#2 โ€” Default `service.type: LoadBalancer`** (`values.yaml` โ€” all three applications) - Will provision three โ˜๏ธ cloud load balancers on a default install. Since the chart is designed for - path-based routing via Ingress or HTTPRoute, `ClusterIP` should be the default. - (`test-values.yaml` already overrides this, confirming the default is wrong.) +- โฌœ **#2 โ€” Config checksum uses raw (untemplated) values** (`templates/_helpers.tpl`) + `planr-tools.componentConfigChecksum` hashes the raw `.component.config` map, but the ConfigMap + renders config via `tpl`. If config contains `${...}` placeholders referencing env vars, changing + those env vars won't change the checksum and pods won't restart. Compute the checksum from the + fully rendered `application.yml` content instead. ## ๐ŸŸก Medium Severity -- โœ… **#3 โ€” `JAVA_OPTS` env var name** (`templates/deployment.yaml`) - Verified: all three Dockerfiles use `JAVA_OPTS` directly in their `ENTRYPOINT`. No change needed. +- โฌœ **#3 โ€” Hardcoded app key list โ€” centralize in `_helpers.tpl`** (`templates/configmap.yaml` et al.) + DanielRaapDev suggests declaring the list once in `_helpers.tpl` and including it via + `include "planr-tools.app-keys" .` โ€” revisit the `fromYaml` approach discussed earlier. -- โœ… **#4 โ€” Missing `annotations.artifacthub.io/changes`** (`Chart.yaml`) - Every other chart in the repo has this annotation; the release workflow likely depends on it ๐Ÿš€. - Add an initial entry, e.g.: - ```yaml - annotations: - artifacthub.io/changes: | - - kind: added - description: Initial release of the planr-tools chart. - ``` +- โฌœ **#4 โ€” Wrap nginx annotations behind `ingressClassName` guard** (`templates/ingress.yaml`) + Wrap the `nginx.ingress.kubernetes.io/*` annotations in + `{{- if eq "nginx" .Values.ingress.ingressClassName }}` so they're not emitted when using a + different ingress controller. -- โœ… **#5 โ€” `sources` URL points to the application repo, not the chart repo** (`Chart.yaml`) - All other charts set `sources` to `https://github.com/subshell/helm-charts/tree/main/charts/`. - The application GitLab URL belongs in `home`, not `sources`. ๐Ÿ  +- โฌœ **#5 โ€” `ingress.pathType` defined in values but never used** (`values.yaml`) + The template hardcodes `pathType: ImplementationSpecific`. Either remove `ingress.pathType` from + `values.yaml` or wire it back into the template. -- โœ… **#6 โ€” Hardcoded component list repeated across 5 template files** ๐Ÿ” - A shared helper would require a non-obvious `fromYaml` wrapper pattern. Accepted as intentional - duplication instead โ€” added a comment to each file explaining the ordering requirement and - listing the files that need to stay in sync. +- โฌœ **#6 โ€” Actuator endpoints too broad by default** (`values.yaml`) ๐Ÿ”“ + The default config exposes `jolokia` and `endpoints` over HTTP which can become externally + reachable via Ingress/HTTPRoute. Restrict defaults to `health, info` and make extras opt-in. ## ๐ŸŸข Low Severity -- โœ… **#7 โ€” Thin test coverage** (`tests/`) ๐Ÿงช - Only the HTTPRoute is tested. The existing test checks backend names by index but not the path - values (`/document-creator`, `/feed`, `/`) that are the core routing logic. Consider adding: - - Path value assertions to the existing httproute test - - At least a smoke test for Deployment and Ingress +- โฌœ **#7 โ€” README: document required secret and its keys** (`README.md`) + Rename the example secret to `planr-tools-sophora-credentials` and document that it is required, + listing the expected keys (`username`, `password`). + +- โฌœ **#8 โ€” README: document Ingress controller requirements** (`README.md`) + Note that the Ingress only works with controllers that support regex for + `pathType: ImplementationSpecific` and allow `rewrite-target` with capture groups (e.g. nginx). + +- โฌœ **#9 โ€” No unit tests for Ingress template** (`tests/`) + Add assertions for the nginx annotations, the three regex paths, and the service backends. + +- โฌœ **#10 โ€” HTTPRoute test does not assert path values** (`tests/httproute_test.yaml`) + Add assertions for `spec.rules[*].matches[0].path.value` (`/document-creator`, `/feed`, `/`). + +- โฌœ **#11 โ€” Empty `value:` fields produce null EnvVars** (`test-values-ingress.yaml`) + `value: # in secret` becomes `value: null` which is an invalid Kubernetes EnvVar. Use `value: ""` + or switch the examples to `valueFrom.secretKeyRef`. -- โœ… **#8 โ€” `test-values.yaml` enables both `ingress` and `httpRoute` simultaneously** ๐Ÿคท - Replaced with `test-values-ingress.yaml` and `test-values-httproute.yaml`. +- โฌœ **#12 โ€” Fix test name grammar** (`tests/deployment_test.yaml`) + Rename `should not failedTemplate` โ†’ `should render without errors`. -- โญ๏ธ **#9 โ€” Missing `icon` field** (`Chart.yaml`) ๐Ÿ–ผ๏ธ - Skipped โ€” no icon available for this customer-specific chart. +- โฌœ **#13 โ€” Remove `REVIEW.md` before merging** ๐Ÿ—‘๏ธ diff --git a/charts/planr-tools/templates/httproute.yaml b/charts/planr-tools/templates/httproute.yaml index 10c8fd38..0497817c 100644 --- a/charts/planr-tools/templates/httproute.yaml +++ b/charts/planr-tools/templates/httproute.yaml @@ -32,10 +32,18 @@ spec: backendRefs: - name: {{ include "planr-tools.componentFullname" (dict "root" $ "component" $component) }} port: {{ $component.service.port }} - {{- with $.Values.httpRoute.filters }} filters: + {{- if ne $component.exposure.path "/" }} + {{/* Strip the path prefix so apps receive requests at their own root, same as the Ingress rewrite. */}} + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + {{- end }} + {{- with $.Values.httpRoute.filters }} {{- toYaml . | nindent 8 }} - {{- end }} + {{- end }} {{- end }} {{- end }} {{- end }} From f821a32de8d2ece5ccc90c74e1647435ab76caf2 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 14:38:31 +0200 Subject: [PATCH 05/21] Remove review file --- charts/planr-tools/REVIEW.md | 68 ------------------------------------ 1 file changed, 68 deletions(-) delete mode 100644 charts/planr-tools/REVIEW.md diff --git a/charts/planr-tools/REVIEW.md b/charts/planr-tools/REVIEW.md deleted file mode 100644 index 812c4cac..00000000 --- a/charts/planr-tools/REVIEW.md +++ /dev/null @@ -1,68 +0,0 @@ -# planr-tools Chart Review โ€” Round 2 - -Feedback from [subshell/helm-charts#267](https://github.com/subshell/helm-charts/pull/267). Remove this file before merging. - -## ๐Ÿ”ด High Severity - -- โœ… **#1 โ€” HTTPRoute does not strip path prefix** (`templates/httproute.yaml`) - Like the Ingress, the HTTPRoute currently forwards requests with the external prefix still present - (e.g. `/document-creator/api/...`). Add a per-rule `URLRewrite` filter to strip the prefix: - ```yaml - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplacePrefixMatch - replacePrefixMatch: / - ``` - Note: only needed for non-root exposure paths, widget's `/` is fine as-is. - -- โฌœ **#2 โ€” Config checksum uses raw (untemplated) values** (`templates/_helpers.tpl`) - `planr-tools.componentConfigChecksum` hashes the raw `.component.config` map, but the ConfigMap - renders config via `tpl`. If config contains `${...}` placeholders referencing env vars, changing - those env vars won't change the checksum and pods won't restart. Compute the checksum from the - fully rendered `application.yml` content instead. - -## ๐ŸŸก Medium Severity - -- โฌœ **#3 โ€” Hardcoded app key list โ€” centralize in `_helpers.tpl`** (`templates/configmap.yaml` et al.) - DanielRaapDev suggests declaring the list once in `_helpers.tpl` and including it via - `include "planr-tools.app-keys" .` โ€” revisit the `fromYaml` approach discussed earlier. - -- โฌœ **#4 โ€” Wrap nginx annotations behind `ingressClassName` guard** (`templates/ingress.yaml`) - Wrap the `nginx.ingress.kubernetes.io/*` annotations in - `{{- if eq "nginx" .Values.ingress.ingressClassName }}` so they're not emitted when using a - different ingress controller. - -- โฌœ **#5 โ€” `ingress.pathType` defined in values but never used** (`values.yaml`) - The template hardcodes `pathType: ImplementationSpecific`. Either remove `ingress.pathType` from - `values.yaml` or wire it back into the template. - -- โฌœ **#6 โ€” Actuator endpoints too broad by default** (`values.yaml`) ๐Ÿ”“ - The default config exposes `jolokia` and `endpoints` over HTTP which can become externally - reachable via Ingress/HTTPRoute. Restrict defaults to `health, info` and make extras opt-in. - -## ๐ŸŸข Low Severity - -- โฌœ **#7 โ€” README: document required secret and its keys** (`README.md`) - Rename the example secret to `planr-tools-sophora-credentials` and document that it is required, - listing the expected keys (`username`, `password`). - -- โฌœ **#8 โ€” README: document Ingress controller requirements** (`README.md`) - Note that the Ingress only works with controllers that support regex for - `pathType: ImplementationSpecific` and allow `rewrite-target` with capture groups (e.g. nginx). - -- โฌœ **#9 โ€” No unit tests for Ingress template** (`tests/`) - Add assertions for the nginx annotations, the three regex paths, and the service backends. - -- โฌœ **#10 โ€” HTTPRoute test does not assert path values** (`tests/httproute_test.yaml`) - Add assertions for `spec.rules[*].matches[0].path.value` (`/document-creator`, `/feed`, `/`). - -- โฌœ **#11 โ€” Empty `value:` fields produce null EnvVars** (`test-values-ingress.yaml`) - `value: # in secret` becomes `value: null` which is an invalid Kubernetes EnvVar. Use `value: ""` - or switch the examples to `valueFrom.secretKeyRef`. - -- โฌœ **#12 โ€” Fix test name grammar** (`tests/deployment_test.yaml`) - Rename `should not failedTemplate` โ†’ `should render without errors`. - -- โฌœ **#13 โ€” Remove `REVIEW.md` before merging** ๐Ÿ—‘๏ธ From f9b1b801d8779bb3fb51558c14213c11042c17a6 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 14:41:56 +0200 Subject: [PATCH 06/21] Rename credentials more precisely Co-authored-by: Daniel Raap --- charts/planr-tools/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/planr-tools/README.md b/charts/planr-tools/README.md index 1486d3b0..2b011d98 100644 --- a/charts/planr-tools/README.md +++ b/charts/planr-tools/README.md @@ -58,7 +58,7 @@ commonEnv: - name: SOPHORA_USERNAME valueFrom: secretKeyRef: - name: planr-tools-credentials + name: planr-tools-sophora-credentials key: username - name: SOPHORA_PASSWORD valueFrom: From 7f8219b8ac732f1653bee72cdc68dee53d5a6453 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 14:40:25 +0200 Subject: [PATCH 07/21] [UNSOI-3673] [planr-tools] centralize app key list in _helpers.tpl Declare the ordered application key list once via planr-tools.appKeys helper and use it in all five templates via fromYaml, removing the duplication. --- charts/planr-tools/REVIEW.md | 68 ++++++++++++++++++++ charts/planr-tools/templates/_helpers.tpl | 12 ++++ charts/planr-tools/templates/configmap.yaml | 3 +- charts/planr-tools/templates/deployment.yaml | 3 +- charts/planr-tools/templates/httproute.yaml | 3 +- charts/planr-tools/templates/ingress.yaml | 3 +- charts/planr-tools/templates/service.yaml | 3 +- 7 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 charts/planr-tools/REVIEW.md diff --git a/charts/planr-tools/REVIEW.md b/charts/planr-tools/REVIEW.md new file mode 100644 index 00000000..59350b9c --- /dev/null +++ b/charts/planr-tools/REVIEW.md @@ -0,0 +1,68 @@ +# planr-tools Chart Review โ€” Round 2 + +Feedback from [subshell/helm-charts#267](https://github.com/subshell/helm-charts/pull/267). Remove this file before merging. + +## ๐Ÿ”ด High Severity + +- โœ… **#1 โ€” HTTPRoute does not strip path prefix** (`templates/httproute.yaml`) + Like the Ingress, the HTTPRoute currently forwards requests with the external prefix still present + (e.g. `/document-creator/api/...`). Add a per-rule `URLRewrite` filter to strip the prefix: + ```yaml + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + ``` + Note: only needed for non-root exposure paths, widget's `/` is fine as-is. + +- โฌœ **#2 โ€” Config checksum uses raw (untemplated) values** (`templates/_helpers.tpl`) + `planr-tools.componentConfigChecksum` hashes the raw `.component.config` map, but the ConfigMap + renders config via `tpl`. If config contains `${...}` placeholders referencing env vars, changing + those env vars won't change the checksum and pods won't restart. Compute the checksum from the + fully rendered `application.yml` content instead. + +## ๐ŸŸก Medium Severity + +- โœ… **#3 โ€” Hardcoded app key list โ€” centralize in `_helpers.tpl`** (`templates/configmap.yaml` et al.) + DanielRaapDev suggests declaring the list once in `_helpers.tpl` and including it via + `include "planr-tools.app-keys" .` โ€” revisit the `fromYaml` approach discussed earlier. + +- โฌœ **#4 โ€” Wrap nginx annotations behind `ingressClassName` guard** (`templates/ingress.yaml`) + Wrap the `nginx.ingress.kubernetes.io/*` annotations in + `{{- if eq "nginx" .Values.ingress.ingressClassName }}` so they're not emitted when using a + different ingress controller. + +- โฌœ **#5 โ€” `ingress.pathType` defined in values but never used** (`values.yaml`) + The template hardcodes `pathType: ImplementationSpecific`. Either remove `ingress.pathType` from + `values.yaml` or wire it back into the template. + +- โฌœ **#6 โ€” Actuator endpoints too broad by default** (`values.yaml`) ๐Ÿ”“ + The default config exposes `jolokia` and `endpoints` over HTTP which can become externally + reachable via Ingress/HTTPRoute. Restrict defaults to `health, info` and make extras opt-in. + +## ๐ŸŸข Low Severity + +- โฌœ **#7 โ€” README: document required secret and its keys** (`README.md`) + Rename the example secret to `planr-tools-sophora-credentials` and document that it is required, + listing the expected keys (`username`, `password`). + +- โฌœ **#8 โ€” README: document Ingress controller requirements** (`README.md`) + Note that the Ingress only works with controllers that support regex for + `pathType: ImplementationSpecific` and allow `rewrite-target` with capture groups (e.g. nginx). + +- โฌœ **#9 โ€” No unit tests for Ingress template** (`tests/`) + Add assertions for the nginx annotations, the three regex paths, and the service backends. + +- โฌœ **#10 โ€” HTTPRoute test does not assert path values** (`tests/httproute_test.yaml`) + Add assertions for `spec.rules[*].matches[0].path.value` (`/document-creator`, `/feed`, `/`). + +- โฌœ **#11 โ€” Empty `value:` fields produce null EnvVars** (`test-values-ingress.yaml`) + `value: # in secret` becomes `value: null` which is an invalid Kubernetes EnvVar. Use `value: ""` + or switch the examples to `valueFrom.secretKeyRef`. + +- โฌœ **#12 โ€” Fix test name grammar** (`tests/deployment_test.yaml`) + Rename `should not failedTemplate` โ†’ `should render without errors`. + +- โฌœ **#13 โ€” Remove `REVIEW.md` before merging** ๐Ÿ—‘๏ธ diff --git a/charts/planr-tools/templates/_helpers.tpl b/charts/planr-tools/templates/_helpers.tpl index 893d12d6..e02c29b4 100644 --- a/charts/planr-tools/templates/_helpers.tpl +++ b/charts/planr-tools/templates/_helpers.tpl @@ -60,6 +60,18 @@ Service account name. {{- end }} {{- end }} +{{/* +Ordered list of application keys. +Wrapped in a map because fromYaml requires a top-level map, not a bare list. +Usage: range $appKey := (include "planr-tools.appKeys" . | fromYaml).keys +*/}} +{{- define "planr-tools.appKeys" -}} +keys: + - documentCreator + - feed + - widget +{{- end }} + {{/* Component fullname. */}} diff --git a/charts/planr-tools/templates/configmap.yaml b/charts/planr-tools/templates/configmap.yaml index a53744d2..f9bc0762 100644 --- a/charts/planr-tools/templates/configmap.yaml +++ b/charts/planr-tools/templates/configmap.yaml @@ -1,6 +1,5 @@ {{- $root := . -}} -{{/* Keep in sync with deployment.yaml, service.yaml, ingress.yaml, httproute.yaml. */}} -{{- range $appKey := list "documentCreator" "feed" "widget" }} +{{- range $appKey := (include "planr-tools.appKeys" $root | fromYaml).keys }} {{- $component := index $root.Values.applications $appKey }} {{- if $component.enabled }} apiVersion: v1 diff --git a/charts/planr-tools/templates/deployment.yaml b/charts/planr-tools/templates/deployment.yaml index 61079e1f..9d3ce192 100644 --- a/charts/planr-tools/templates/deployment.yaml +++ b/charts/planr-tools/templates/deployment.yaml @@ -1,6 +1,5 @@ {{- $root := . -}} -{{/* Keep in sync with configmap.yaml, service.yaml, ingress.yaml, httproute.yaml. */}} -{{- range $appKey := list "documentCreator" "feed" "widget" }} +{{- range $appKey := (include "planr-tools.appKeys" $root | fromYaml).keys }} {{- $component := index $root.Values.applications $appKey }} {{- if $component.enabled }} apiVersion: apps/v1 diff --git a/charts/planr-tools/templates/httproute.yaml b/charts/planr-tools/templates/httproute.yaml index 0497817c..8f1296cb 100644 --- a/charts/planr-tools/templates/httproute.yaml +++ b/charts/planr-tools/templates/httproute.yaml @@ -21,8 +21,7 @@ spec: {{- end }} {{- end }} rules: - {{/* Keep in sync with deployment.yaml, configmap.yaml, service.yaml, ingress.yaml. */}} - {{- range $appKey := list "documentCreator" "feed" "widget" }} + {{- range $appKey := (include "planr-tools.appKeys" $ | fromYaml).keys }} {{- $component := index $.Values.applications $appKey }} {{- if $component.enabled }} - matches: diff --git a/charts/planr-tools/templates/ingress.yaml b/charts/planr-tools/templates/ingress.yaml index 53863c5a..92612b14 100644 --- a/charts/planr-tools/templates/ingress.yaml +++ b/charts/planr-tools/templates/ingress.yaml @@ -30,8 +30,7 @@ spec: - host: {{ required "A hostname for the ingress must be provided in .Values.ingress.hosts[].host" $host.host }} http: paths: - {{/* Keep in sync with deployment.yaml, configmap.yaml, service.yaml, httproute.yaml. */}} - {{- range $appKey := list "documentCreator" "feed" "widget" }} + {{- range $appKey := (include "planr-tools.appKeys" $ | fromYaml).keys }} {{- $component := index $.Values.applications $appKey }} {{- if $component.enabled }} {{- $path := $component.exposure.path }} diff --git a/charts/planr-tools/templates/service.yaml b/charts/planr-tools/templates/service.yaml index a44017c6..dd6d6a56 100644 --- a/charts/planr-tools/templates/service.yaml +++ b/charts/planr-tools/templates/service.yaml @@ -1,6 +1,5 @@ {{- $root := . -}} -{{/* Keep in sync with deployment.yaml, configmap.yaml, ingress.yaml, httproute.yaml. */}} -{{- range $appKey := list "documentCreator" "feed" "widget" }} +{{- range $appKey := (include "planr-tools.appKeys" $root | fromYaml).keys }} {{- $component := index $root.Values.applications $appKey }} {{- if $component.enabled }} apiVersion: v1 From 62871c0c8980b752dd113b290be8bff266493f43 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 14:44:16 +0200 Subject: [PATCH 08/21] Rename credentials (password, too) --- charts/planr-tools/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/planr-tools/README.md b/charts/planr-tools/README.md index 2b011d98..df822b16 100644 --- a/charts/planr-tools/README.md +++ b/charts/planr-tools/README.md @@ -63,6 +63,6 @@ commonEnv: - name: SOPHORA_PASSWORD valueFrom: secretKeyRef: - name: planr-tools-credentials + name: planr-tools-sophora-credentials key: password ``` From 2624e6d5790735cdb8d1f7e5b6ec38dba6ee04ce Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 14:48:19 +0200 Subject: [PATCH 09/21] [UNSOI-3673] [planr-tools] rename appKeys helper to app-keys --- charts/planr-tools/templates/_helpers.tpl | 4 ++-- charts/planr-tools/templates/configmap.yaml | 2 +- charts/planr-tools/templates/deployment.yaml | 2 +- charts/planr-tools/templates/httproute.yaml | 2 +- charts/planr-tools/templates/ingress.yaml | 2 +- charts/planr-tools/templates/service.yaml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/planr-tools/templates/_helpers.tpl b/charts/planr-tools/templates/_helpers.tpl index e02c29b4..fe120d2f 100644 --- a/charts/planr-tools/templates/_helpers.tpl +++ b/charts/planr-tools/templates/_helpers.tpl @@ -63,9 +63,9 @@ Service account name. {{/* Ordered list of application keys. Wrapped in a map because fromYaml requires a top-level map, not a bare list. -Usage: range $appKey := (include "planr-tools.appKeys" . | fromYaml).keys +Usage: range $appKey := (include "planr-tools.app-keys" . | fromYaml).keys */}} -{{- define "planr-tools.appKeys" -}} +{{- define "planr-tools.app-keys" -}} keys: - documentCreator - feed diff --git a/charts/planr-tools/templates/configmap.yaml b/charts/planr-tools/templates/configmap.yaml index f9bc0762..afb23829 100644 --- a/charts/planr-tools/templates/configmap.yaml +++ b/charts/planr-tools/templates/configmap.yaml @@ -1,5 +1,5 @@ {{- $root := . -}} -{{- range $appKey := (include "planr-tools.appKeys" $root | fromYaml).keys }} +{{- range $appKey := (include "planr-tools.app-keys" $root | fromYaml).keys }} {{- $component := index $root.Values.applications $appKey }} {{- if $component.enabled }} apiVersion: v1 diff --git a/charts/planr-tools/templates/deployment.yaml b/charts/planr-tools/templates/deployment.yaml index 9d3ce192..c8efbaa1 100644 --- a/charts/planr-tools/templates/deployment.yaml +++ b/charts/planr-tools/templates/deployment.yaml @@ -1,5 +1,5 @@ {{- $root := . -}} -{{- range $appKey := (include "planr-tools.appKeys" $root | fromYaml).keys }} +{{- range $appKey := (include "planr-tools.app-keys" $root | fromYaml).keys }} {{- $component := index $root.Values.applications $appKey }} {{- if $component.enabled }} apiVersion: apps/v1 diff --git a/charts/planr-tools/templates/httproute.yaml b/charts/planr-tools/templates/httproute.yaml index 8f1296cb..b94e5e08 100644 --- a/charts/planr-tools/templates/httproute.yaml +++ b/charts/planr-tools/templates/httproute.yaml @@ -21,7 +21,7 @@ spec: {{- end }} {{- end }} rules: - {{- range $appKey := (include "planr-tools.appKeys" $ | fromYaml).keys }} + {{- range $appKey := (include "planr-tools.app-keys" $ | fromYaml).keys }} {{- $component := index $.Values.applications $appKey }} {{- if $component.enabled }} - matches: diff --git a/charts/planr-tools/templates/ingress.yaml b/charts/planr-tools/templates/ingress.yaml index 92612b14..a2ea1905 100644 --- a/charts/planr-tools/templates/ingress.yaml +++ b/charts/planr-tools/templates/ingress.yaml @@ -30,7 +30,7 @@ spec: - host: {{ required "A hostname for the ingress must be provided in .Values.ingress.hosts[].host" $host.host }} http: paths: - {{- range $appKey := (include "planr-tools.appKeys" $ | fromYaml).keys }} + {{- range $appKey := (include "planr-tools.app-keys" $ | fromYaml).keys }} {{- $component := index $.Values.applications $appKey }} {{- if $component.enabled }} {{- $path := $component.exposure.path }} diff --git a/charts/planr-tools/templates/service.yaml b/charts/planr-tools/templates/service.yaml index dd6d6a56..def18ba5 100644 --- a/charts/planr-tools/templates/service.yaml +++ b/charts/planr-tools/templates/service.yaml @@ -1,5 +1,5 @@ {{- $root := . -}} -{{- range $appKey := (include "planr-tools.appKeys" $root | fromYaml).keys }} +{{- range $appKey := (include "planr-tools.app-keys" $root | fromYaml).keys }} {{- $component := index $root.Values.applications $appKey }} {{- if $component.enabled }} apiVersion: v1 From d2142e04b8e5bd8eddcca6495dd84d4e4fa5345b Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:00:18 +0200 Subject: [PATCH 10/21] [UNSOI-3673] [planr-tools] guard nginx ingress annotations behind ingressClassName check --- charts/planr-tools/REVIEW.md | 2 +- charts/planr-tools/templates/ingress.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/charts/planr-tools/REVIEW.md b/charts/planr-tools/REVIEW.md index 59350b9c..656329ad 100644 --- a/charts/planr-tools/REVIEW.md +++ b/charts/planr-tools/REVIEW.md @@ -29,7 +29,7 @@ Feedback from [subshell/helm-charts#267](https://github.com/subshell/helm-charts DanielRaapDev suggests declaring the list once in `_helpers.tpl` and including it via `include "planr-tools.app-keys" .` โ€” revisit the `fromYaml` approach discussed earlier. -- โฌœ **#4 โ€” Wrap nginx annotations behind `ingressClassName` guard** (`templates/ingress.yaml`) +- โœ… **#4 โ€” Wrap nginx annotations behind `ingressClassName` guard** (`templates/ingress.yaml`) Wrap the `nginx.ingress.kubernetes.io/*` annotations in `{{- if eq "nginx" .Values.ingress.ingressClassName }}` so they're not emitted when using a different ingress controller. diff --git a/charts/planr-tools/templates/ingress.yaml b/charts/planr-tools/templates/ingress.yaml index a2ea1905..86612642 100644 --- a/charts/planr-tools/templates/ingress.yaml +++ b/charts/planr-tools/templates/ingress.yaml @@ -6,8 +6,10 @@ metadata: labels: {{- include "planr-tools.labels" (dict "root" . "component" (dict "name" "ingress")) | nindent 4 }} annotations: + {{- if eq .Values.ingress.ingressClassName "nginx" }} nginx.ingress.kubernetes.io/use-regex: "true" nginx.ingress.kubernetes.io/rewrite-target: /$2 + {{- end }} {{- with .Values.ingress.annotations }} {{- toYaml . | nindent 4 }} {{- end }} From 89b675402b574353f93c7eea13776c8647c357f0 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:03:06 +0200 Subject: [PATCH 11/21] [UNSOI-3673] [planr-tools] remove unused ingress.pathType from values.yaml pathType is hardcoded to ImplementationSpecific in the template since the nginx regex rewrite requires it and it cannot be made configurable. --- charts/planr-tools/REVIEW.md | 2 +- charts/planr-tools/values.yaml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/charts/planr-tools/REVIEW.md b/charts/planr-tools/REVIEW.md index 656329ad..62c30b34 100644 --- a/charts/planr-tools/REVIEW.md +++ b/charts/planr-tools/REVIEW.md @@ -34,7 +34,7 @@ Feedback from [subshell/helm-charts#267](https://github.com/subshell/helm-charts `{{- if eq "nginx" .Values.ingress.ingressClassName }}` so they're not emitted when using a different ingress controller. -- โฌœ **#5 โ€” `ingress.pathType` defined in values but never used** (`values.yaml`) +- โœ… **#5 โ€” `ingress.pathType` defined in values but never used** (`values.yaml`) The template hardcodes `pathType: ImplementationSpecific`. Either remove `ingress.pathType` from `values.yaml` or wire it back into the template. diff --git a/charts/planr-tools/values.yaml b/charts/planr-tools/values.yaml index a57c6c9a..130649c9 100644 --- a/charts/planr-tools/values.yaml +++ b/charts/planr-tools/values.yaml @@ -49,7 +49,6 @@ commonEnvFrom: [] ingress: enabled: false ingressClassName: nginx - pathType: Prefix tls: hosts: annotations: {} From 2963d5b18bf327df9b5e84e7cada22d15e88bfe3 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:04:41 +0200 Subject: [PATCH 12/21] [UNSOI-3673] [planr-tools] document required secret and ingress controller requirements in README --- charts/planr-tools/README.md | 25 +++++++++++++++++++++++++ charts/planr-tools/REVIEW.md | 4 ++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/charts/planr-tools/README.md b/charts/planr-tools/README.md index df822b16..59c03807 100644 --- a/charts/planr-tools/README.md +++ b/charts/planr-tools/README.md @@ -8,6 +8,31 @@ This chart deploys the three `planr-tools` applications together: Each application is deployed as its own `Deployment`, `Service`, and `ConfigMap`, while the chart offers shared `Ingress` and `HTTPRoute` resources for path-based external access. +## Prerequisites + +### Sophora credentials secret + +A Kubernetes secret containing the Sophora CMS credentials is required. Create it before installing the chart: + +```sh +kubectl create secret generic planr-tools-sophora-credentials \ + --from-literal=username= \ + --from-literal=password= +``` + +Expected keys: +- `username` โ€” Sophora CMS username +- `password` โ€” Sophora CMS password + +### Ingress controller requirements + +The `Ingress` resource uses `pathType: ImplementationSpecific` with nginx-style regex paths and a `rewrite-target` annotation to strip path prefixes before forwarding to each app. This requires an ingress controller that: + +- Supports regex matching for `pathType: ImplementationSpecific` +- Supports `nginx.ingress.kubernetes.io/rewrite-target` with capture groups + +The nginx ingress controller (`ingressClassName: nginx`) satisfies both requirements and is the default. Other controllers may not be compatible. Consider using `HTTPRoute` instead if you are not using nginx. + ## Example values.yaml ```yaml diff --git a/charts/planr-tools/REVIEW.md b/charts/planr-tools/REVIEW.md index 62c30b34..09b101d2 100644 --- a/charts/planr-tools/REVIEW.md +++ b/charts/planr-tools/REVIEW.md @@ -44,11 +44,11 @@ Feedback from [subshell/helm-charts#267](https://github.com/subshell/helm-charts ## ๐ŸŸข Low Severity -- โฌœ **#7 โ€” README: document required secret and its keys** (`README.md`) +- โœ… **#7 โ€” README: document required secret and its keys** (`README.md`) Rename the example secret to `planr-tools-sophora-credentials` and document that it is required, listing the expected keys (`username`, `password`). -- โฌœ **#8 โ€” README: document Ingress controller requirements** (`README.md`) +- โœ… **#8 โ€” README: document Ingress controller requirements** (`README.md`) Note that the Ingress only works with controllers that support regex for `pathType: ImplementationSpecific` and allow `rewrite-target` with capture groups (e.g. nginx). From 4ddb9f3dc57a64dfbcd4061f80b21991c56e26c9 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:06:08 +0200 Subject: [PATCH 13/21] Remove review md file --- charts/planr-tools/REVIEW.md | 68 ------------------------------------ 1 file changed, 68 deletions(-) delete mode 100644 charts/planr-tools/REVIEW.md diff --git a/charts/planr-tools/REVIEW.md b/charts/planr-tools/REVIEW.md deleted file mode 100644 index 09b101d2..00000000 --- a/charts/planr-tools/REVIEW.md +++ /dev/null @@ -1,68 +0,0 @@ -# planr-tools Chart Review โ€” Round 2 - -Feedback from [subshell/helm-charts#267](https://github.com/subshell/helm-charts/pull/267). Remove this file before merging. - -## ๐Ÿ”ด High Severity - -- โœ… **#1 โ€” HTTPRoute does not strip path prefix** (`templates/httproute.yaml`) - Like the Ingress, the HTTPRoute currently forwards requests with the external prefix still present - (e.g. `/document-creator/api/...`). Add a per-rule `URLRewrite` filter to strip the prefix: - ```yaml - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplacePrefixMatch - replacePrefixMatch: / - ``` - Note: only needed for non-root exposure paths, widget's `/` is fine as-is. - -- โฌœ **#2 โ€” Config checksum uses raw (untemplated) values** (`templates/_helpers.tpl`) - `planr-tools.componentConfigChecksum` hashes the raw `.component.config` map, but the ConfigMap - renders config via `tpl`. If config contains `${...}` placeholders referencing env vars, changing - those env vars won't change the checksum and pods won't restart. Compute the checksum from the - fully rendered `application.yml` content instead. - -## ๐ŸŸก Medium Severity - -- โœ… **#3 โ€” Hardcoded app key list โ€” centralize in `_helpers.tpl`** (`templates/configmap.yaml` et al.) - DanielRaapDev suggests declaring the list once in `_helpers.tpl` and including it via - `include "planr-tools.app-keys" .` โ€” revisit the `fromYaml` approach discussed earlier. - -- โœ… **#4 โ€” Wrap nginx annotations behind `ingressClassName` guard** (`templates/ingress.yaml`) - Wrap the `nginx.ingress.kubernetes.io/*` annotations in - `{{- if eq "nginx" .Values.ingress.ingressClassName }}` so they're not emitted when using a - different ingress controller. - -- โœ… **#5 โ€” `ingress.pathType` defined in values but never used** (`values.yaml`) - The template hardcodes `pathType: ImplementationSpecific`. Either remove `ingress.pathType` from - `values.yaml` or wire it back into the template. - -- โฌœ **#6 โ€” Actuator endpoints too broad by default** (`values.yaml`) ๐Ÿ”“ - The default config exposes `jolokia` and `endpoints` over HTTP which can become externally - reachable via Ingress/HTTPRoute. Restrict defaults to `health, info` and make extras opt-in. - -## ๐ŸŸข Low Severity - -- โœ… **#7 โ€” README: document required secret and its keys** (`README.md`) - Rename the example secret to `planr-tools-sophora-credentials` and document that it is required, - listing the expected keys (`username`, `password`). - -- โœ… **#8 โ€” README: document Ingress controller requirements** (`README.md`) - Note that the Ingress only works with controllers that support regex for - `pathType: ImplementationSpecific` and allow `rewrite-target` with capture groups (e.g. nginx). - -- โฌœ **#9 โ€” No unit tests for Ingress template** (`tests/`) - Add assertions for the nginx annotations, the three regex paths, and the service backends. - -- โฌœ **#10 โ€” HTTPRoute test does not assert path values** (`tests/httproute_test.yaml`) - Add assertions for `spec.rules[*].matches[0].path.value` (`/document-creator`, `/feed`, `/`). - -- โฌœ **#11 โ€” Empty `value:` fields produce null EnvVars** (`test-values-ingress.yaml`) - `value: # in secret` becomes `value: null` which is an invalid Kubernetes EnvVar. Use `value: ""` - or switch the examples to `valueFrom.secretKeyRef`. - -- โฌœ **#12 โ€” Fix test name grammar** (`tests/deployment_test.yaml`) - Rename `should not failedTemplate` โ†’ `should render without errors`. - -- โฌœ **#13 โ€” Remove `REVIEW.md` before merging** ๐Ÿ—‘๏ธ From 39ba740718c8e1a80561c6bb97bdf327070c298a Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:09:52 +0200 Subject: [PATCH 14/21] Improve test description Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- charts/planr-tools/tests/deployment_test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/planr-tools/tests/deployment_test.yaml b/charts/planr-tools/tests/deployment_test.yaml index f752952c..81919c10 100644 --- a/charts/planr-tools/tests/deployment_test.yaml +++ b/charts/planr-tools/tests/deployment_test.yaml @@ -86,6 +86,6 @@ tests: - exists: path: spec.template.metadata.annotations.checksum/config - - it: should not failedTemplate + - it: should not fail template rendering asserts: - notFailedTemplate: {} From f6b074145480bb8d1e84af3dea87044ac410d52d Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:10:50 +0200 Subject: [PATCH 15/21] Use empty strings for values expected to be found in secrets instead of null Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- charts/planr-tools/test-values-ingress.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/planr-tools/test-values-ingress.yaml b/charts/planr-tools/test-values-ingress.yaml index 0f1f81c8..e65eee71 100644 --- a/charts/planr-tools/test-values-ingress.yaml +++ b/charts/planr-tools/test-values-ingress.yaml @@ -11,11 +11,11 @@ commonEnv: - name: SPRING_PROFILES_ACTIVE value: subshell-test-cloud - name: SOPHORA_URL - value: # e.g. https://cms.stable.test.subshell.io + value: "" # e.g. https://cms.stable.test.subshell.io - name: SOPHORA_USERNAME - value: # in secret + value: "" # in secret - name: SOPHORA_PASSWORD - value: # in secret + value: "" # in secret applications: documentCreator: From 31361d4bfc4b1a60f7f60bf1d7d23186d57ae02d Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:11:46 +0200 Subject: [PATCH 16/21] Disable jolokia and endpoints management endpoints Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- charts/planr-tools/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/planr-tools/values.yaml b/charts/planr-tools/values.yaml index 130649c9..b46eefe8 100644 --- a/charts/planr-tools/values.yaml +++ b/charts/planr-tools/values.yaml @@ -135,7 +135,7 @@ applications: web: base-path: /actuator exposure: - include: health, info, jolokia, endpoints + include: health,info sophora: client: server-connection: From 2c3d9ab021dabd4a8a497bdd816996cdacef2105 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:15:23 +0200 Subject: [PATCH 17/21] [UNSOI-3673] [planr-tools] restrict actuator exposure to health,info for all apps --- charts/planr-tools/values.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/charts/planr-tools/values.yaml b/charts/planr-tools/values.yaml index b46eefe8..0fe3f192 100644 --- a/charts/planr-tools/values.yaml +++ b/charts/planr-tools/values.yaml @@ -93,7 +93,7 @@ applications: web: base-path: /actuator exposure: - include: health, info, jolokia, endpoints + include: health,info sophora: client: server-connection: @@ -175,6 +175,12 @@ applications: port: 8082 compression: enabled: true + management: + endpoints: + web: + base-path: /actuator + exposure: + include: health,info sophora: client: server-connection: From 6508d72f8cbf46a8c252071fc1e4cd898518e82c Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:16:32 +0200 Subject: [PATCH 18/21] [UNSOI-3673] [planr-tools] compute config checksum from rendered application.yml Use planr-tools.render instead of toYaml so the checksum reflects the fully templated config, ensuring pod restarts when referenced Helm values change. --- charts/planr-tools/templates/_helpers.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/charts/planr-tools/templates/_helpers.tpl b/charts/planr-tools/templates/_helpers.tpl index fe120d2f..84bb990c 100644 --- a/charts/planr-tools/templates/_helpers.tpl +++ b/charts/planr-tools/templates/_helpers.tpl @@ -88,9 +88,11 @@ Component configmap name. {{/* Config checksum. +Computed from the fully rendered application.yml (after tpl), not the raw config map, +so that changes to values referenced inside config templates also trigger pod restarts. */}} {{- define "planr-tools.componentConfigChecksum" -}} -{{- toYaml .component.config | sha256sum }} +{{- include "planr-tools.render" (dict "value" .component.config "context" .root) | sha256sum }} {{- end }} {{/* From 346839d16fde49802ba348db22c87969586c9328 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 30 Mar 2026 15:18:11 +0200 Subject: [PATCH 19/21] [UNSOI-3673] [planr-tools] add ingress unit tests and extend httproute tests with path assertions --- charts/planr-tools/tests/httproute_test.yaml | 17 +++++ charts/planr-tools/tests/ingress_test.yaml | 69 ++++++++++++++++++++ charts/planr-tools/tests/values/ingress.yaml | 7 ++ 3 files changed, 93 insertions(+) create mode 100644 charts/planr-tools/tests/ingress_test.yaml create mode 100644 charts/planr-tools/tests/values/ingress.yaml diff --git a/charts/planr-tools/tests/httproute_test.yaml b/charts/planr-tools/tests/httproute_test.yaml index 28d7bef0..62fbbffe 100644 --- a/charts/planr-tools/tests/httproute_test.yaml +++ b/charts/planr-tools/tests/httproute_test.yaml @@ -39,4 +39,21 @@ tests: - equal: path: spec.rules[2].backendRefs[0].name value: values-test-release-planr-tools-newsroom-widget + - equal: + path: spec.rules[0].matches[0].path.value + value: /document-creator + - equal: + path: spec.rules[1].matches[0].path.value + value: /feed + - equal: + path: spec.rules[2].matches[0].path.value + value: / + - equal: + path: spec.rules[0].filters[0].type + value: URLRewrite + - equal: + path: spec.rules[1].filters[0].type + value: URLRewrite + - notExists: + path: spec.rules[2].filters[0].urlRewrite - notFailedTemplate: {} diff --git a/charts/planr-tools/tests/ingress_test.yaml b/charts/planr-tools/tests/ingress_test.yaml new file mode 100644 index 00000000..8512711f --- /dev/null +++ b/charts/planr-tools/tests/ingress_test.yaml @@ -0,0 +1,69 @@ +suite: test ingress +templates: + - ingress.yaml +chart: + version: 0.1.0 + appVersion: 6.0.0-SNAPSHOT +tests: + - it: should not create ingress by default + asserts: + - hasDocuments: + count: 0 + + - it: should create ingress with nginx annotations and regex paths + release: + name: values-test-release + values: + - ./values/ingress.yaml + asserts: + - hasDocuments: + count: 1 + - containsDocument: + kind: Ingress + apiVersion: networking.k8s.io/v1 + name: values-test-release-planr-tools + - equal: + path: metadata.annotations["nginx.ingress.kubernetes.io/use-regex"] + value: "true" + - equal: + path: metadata.annotations["nginx.ingress.kubernetes.io/rewrite-target"] + value: /$2 + - equal: + path: metadata.annotations["example.com/test"] + value: "true" + - equal: + path: spec.rules[0].http.paths[0].path + value: "/document-creator(/|$)(.*)" + - equal: + path: spec.rules[0].http.paths[1].path + value: "/feed(/|$)(.*)" + - equal: + path: spec.rules[0].http.paths[2].path + value: "/()(.*)" + - equal: + path: spec.rules[0].http.paths[0].backend.service.name + value: values-test-release-planr-tools-newsroom-document-creator + - equal: + path: spec.rules[0].http.paths[1].backend.service.name + value: values-test-release-planr-tools-newsroom-feed + - equal: + path: spec.rules[0].http.paths[2].backend.service.name + value: values-test-release-planr-tools-newsroom-widget + - equal: + path: spec.rules[0].http.paths[0].pathType + value: ImplementationSpecific + - notFailedTemplate: {} + + - it: should not emit nginx annotations for non-nginx ingress class + release: + name: values-test-release + values: + - ./values/ingress.yaml + set: + ingress.ingressClassName: traefik + asserts: + - notExists: + path: metadata.annotations["nginx.ingress.kubernetes.io/use-regex"] + - notExists: + path: metadata.annotations["nginx.ingress.kubernetes.io/rewrite-target"] + - notFailedTemplate: {} diff --git a/charts/planr-tools/tests/values/ingress.yaml b/charts/planr-tools/tests/values/ingress.yaml new file mode 100644 index 00000000..307c962e --- /dev/null +++ b/charts/planr-tools/tests/values/ingress.yaml @@ -0,0 +1,7 @@ +ingress: + enabled: true + ingressClassName: nginx + hosts: + - host: planr-tools.example.com + annotations: + example.com/test: "true" From c08d545d5f452b3e25013b9e8139725f43df6049 Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 20 Apr 2026 12:35:42 +0200 Subject: [PATCH 20/21] Set latest release version instead of snapshot version --- charts/planr-tools/Chart.yaml | 2 +- charts/planr-tools/tests/deployment_test.yaml | 8 ++++---- charts/planr-tools/tests/httproute_test.yaml | 2 +- charts/planr-tools/tests/ingress_test.yaml | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/planr-tools/Chart.yaml b/charts/planr-tools/Chart.yaml index 4ba2c03e..1442c97e 100644 --- a/charts/planr-tools/Chart.yaml +++ b/charts/planr-tools/Chart.yaml @@ -3,7 +3,7 @@ name: planr-tools description: A Helm chart for deploying the planr-tools applications type: application version: 0.1.0 -appVersion: "6.0.0-SNAPSHOT" +appVersion: "5.1.1" annotations: artifacthub.io/changes: | - kind: added diff --git a/charts/planr-tools/tests/deployment_test.yaml b/charts/planr-tools/tests/deployment_test.yaml index 81919c10..cb8f5879 100644 --- a/charts/planr-tools/tests/deployment_test.yaml +++ b/charts/planr-tools/tests/deployment_test.yaml @@ -3,7 +3,7 @@ templates: - deployment.yaml chart: version: 0.1.0 - appVersion: 6.0.0-SNAPSHOT + appVersion: 5.1.1 tests: - it: should create 3 deployments by default asserts: @@ -26,7 +26,7 @@ tests: value: RELEASE-NAME-planr-tools-newsroom-document-creator - equal: path: spec.template.spec.containers[0].image - value: docker.subshell.com/planr-tools/newsroom-document-creator:6.0.0-SNAPSHOT + value: docker.subshell.com/planr-tools/newsroom-document-creator:5.1.1 - equal: path: spec.template.spec.containers[0].ports[0].containerPort value: 8080 @@ -39,7 +39,7 @@ tests: value: RELEASE-NAME-planr-tools-newsroom-feed - equal: path: spec.template.spec.containers[0].image - value: docker.subshell.com/planr-tools/newsroom-feed:6.0.0-SNAPSHOT + value: docker.subshell.com/planr-tools/newsroom-feed:5.1.1 - equal: path: spec.template.spec.containers[0].ports[0].containerPort value: 8081 @@ -52,7 +52,7 @@ tests: value: RELEASE-NAME-planr-tools-newsroom-widget - equal: path: spec.template.spec.containers[0].image - value: docker.subshell.com/planr-tools/newsroom-widget:6.0.0-SNAPSHOT + value: docker.subshell.com/planr-tools/newsroom-widget:5.1.1 - equal: path: spec.template.spec.containers[0].ports[0].containerPort value: 8082 diff --git a/charts/planr-tools/tests/httproute_test.yaml b/charts/planr-tools/tests/httproute_test.yaml index 62fbbffe..3758ad1a 100644 --- a/charts/planr-tools/tests/httproute_test.yaml +++ b/charts/planr-tools/tests/httproute_test.yaml @@ -3,7 +3,7 @@ templates: - httproute.yaml chart: version: 0.1.0 - appVersion: 6.0.0-SNAPSHOT + appVersion: 5.1.1 tests: - it: should not create httproute by default asserts: diff --git a/charts/planr-tools/tests/ingress_test.yaml b/charts/planr-tools/tests/ingress_test.yaml index 8512711f..6ce1e7d1 100644 --- a/charts/planr-tools/tests/ingress_test.yaml +++ b/charts/planr-tools/tests/ingress_test.yaml @@ -3,7 +3,7 @@ templates: - ingress.yaml chart: version: 0.1.0 - appVersion: 6.0.0-SNAPSHOT + appVersion: 5.1.1 tests: - it: should not create ingress by default asserts: From 2eab35a832e1aa9cece6d451993f5adfe4a1f71c Mon Sep 17 00:00:00 2001 From: Paul Wellner Bou Date: Mon, 20 Apr 2026 15:34:43 +0200 Subject: [PATCH 21/21] Set chart version to 1.0.0 --- charts/planr-tools/Chart.yaml | 2 +- charts/planr-tools/tests/deployment_test.yaml | 2 +- charts/planr-tools/tests/httproute_test.yaml | 2 +- charts/planr-tools/tests/ingress_test.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/charts/planr-tools/Chart.yaml b/charts/planr-tools/Chart.yaml index 1442c97e..cfcafdff 100644 --- a/charts/planr-tools/Chart.yaml +++ b/charts/planr-tools/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: planr-tools description: A Helm chart for deploying the planr-tools applications type: application -version: 0.1.0 +version: 1.0.0 appVersion: "5.1.1" annotations: artifacthub.io/changes: | diff --git a/charts/planr-tools/tests/deployment_test.yaml b/charts/planr-tools/tests/deployment_test.yaml index cb8f5879..ca570dc3 100644 --- a/charts/planr-tools/tests/deployment_test.yaml +++ b/charts/planr-tools/tests/deployment_test.yaml @@ -2,7 +2,7 @@ suite: test deployment templates: - deployment.yaml chart: - version: 0.1.0 + version: 1.0.0 appVersion: 5.1.1 tests: - it: should create 3 deployments by default diff --git a/charts/planr-tools/tests/httproute_test.yaml b/charts/planr-tools/tests/httproute_test.yaml index 3758ad1a..9359a024 100644 --- a/charts/planr-tools/tests/httproute_test.yaml +++ b/charts/planr-tools/tests/httproute_test.yaml @@ -2,7 +2,7 @@ suite: test httproute templates: - httproute.yaml chart: - version: 0.1.0 + version: 1.0.0 appVersion: 5.1.1 tests: - it: should not create httproute by default diff --git a/charts/planr-tools/tests/ingress_test.yaml b/charts/planr-tools/tests/ingress_test.yaml index 6ce1e7d1..e1f2efb0 100644 --- a/charts/planr-tools/tests/ingress_test.yaml +++ b/charts/planr-tools/tests/ingress_test.yaml @@ -2,7 +2,7 @@ suite: test ingress templates: - ingress.yaml chart: - version: 0.1.0 + version: 1.0.0 appVersion: 5.1.1 tests: - it: should not create ingress by default