Skip to content

chore: update template & general improvement - #633

Merged
andyne13 merged 39 commits into
linagora:developfrom
ThibautChoppy:feat/helm-improvement
Jul 30, 2026
Merged

chore: update template & general improvement#633
andyne13 merged 39 commits into
linagora:developfrom
ThibautChoppy:feat/helm-improvement

Conversation

@ThibautChoppy

@ThibautChoppy ThibautChoppy commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added configurable secrets provisioning (values, external secrets, or Vault-backed) with safer wiring and provider-based rendering.
    • Added templated extra objects support.
    • Added improved Helm install notes and richer guidance on modelSpec updates.
  • Bug Fixes

    • Standardized naming/labels across the stack and switched env/config/secret/PVC references to release-scoped names.
    • Updated health probes, security context merging, and conditional ingress exposure (app/admin UI/Ray).
  • Documentation

    • Updated Kubernetes deployment guide for the new chart path and required chart version.
  • Tests

    • Updated unit tests to match the new ingress rendering and secret reference behavior.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR updates the openrag-stack Helm chart to version 0.6.0 with shared helpers, configurable secrets, value-driven workloads, fullname-based resources, revised Ray and ingress behavior, deployment values, documentation, and nginx runtime permissions.

Changes

OpenRAG Helm Chart Overhaul

Layer / File(s) Summary
Chart metadata and shared helpers
infra/charts/openrag-stack/Chart.yaml, infra/charts/openrag-stack/templates/_helpers.tpl
Chart metadata and dependency conditions are updated, with shared component-label, secret-naming, and security-context helpers added.
Secrets, environment resources, and storage
infra/charts/openrag-stack/templates/secrets-env.yaml, infra/charts/openrag-stack/templates/configmap-env.yaml, infra/charts/openrag-stack/templates/extra-objects.yaml, infra/charts/openrag-stack/templates/pvc.yaml, infra/charts/openrag-stack/templates/postgres-migration-job.yaml
Secret providers, proxy variables, fullname-based environment resources, templated extra objects, configurable PVCs, and derived migration-job references are supported.
Application workloads and services
infra/charts/openrag-stack/templates/admin-ui.yaml, infra/charts/openrag-stack/templates/openrag.yaml, infra/charts/openrag-stack/templates/infinity.yaml, infra/charts/openrag-stack/templates/raycluster.yaml, infra/docker/ui.Dockerfile
Workloads use shared labels, configurable security and probes, registry-aware images, fullname-based resources, updated service and ingress routing, and explicit nginx runtime ownership.
Default and deployment overlay values
infra/charts/openrag-stack/values.yaml, infra/charts/openrag-stack/values-linagora.yaml
Values are revised for naming, persistence, Ray, vLLM, reranking, ingress, application settings, and secrets.
Validation, release notes, and documentation
tests/unit/infra/*.py, infra/charts/openrag-stack/templates/NOTES.txt, docs/content/docs/documentation/kubernetes.md
Tests follow updated helper and ingress behavior, Helm notes report release settings and vLLM secret-name mismatches, and Kubernetes documentation references chart v0.6.0 and the new values path.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: chore

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too generic and doesn't clearly describe the main Helm chart and documentation changes. Use a specific title that names the primary change, such as updating the openrag-stack Helm chart and related docs/tests.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the chore No production code impact, typically improve tooling, code quality, etc label Jul 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (1)
infra/charts/openrag-stack/values-linagora.yaml (1)

1-5: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Overlay storageClass: longhorn won't reach postgresql/milvus sub-charts.

See companion comment on values.yaml (persistence.storageClass anchor) — because postgresql.primary.persistence.storageClass and milvus.minio/milvus.etcd storageClass are set via a YAML alias to the base file's persistence.storageClass, this overlay's override only updates the top-level persistence.storageClass key and does not propagate to those sub-chart paths. Explicitly set postgresql.primary.persistence.storageClass: longhorn, milvus.minio.persistence.storageClass: longhorn, and milvus.etcd.persistence.storageClass: longhorn here as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/charts/openrag-stack/values-linagora.yaml` around lines 1 - 5, The
overlay only updates the top-level persistence.storageClass, so the PostgreSQL
and Milvus sub-charts still keep the base-file alias values. Update this values
overlay to explicitly set postgresql.primary.persistence.storageClass,
milvus.minio.persistence.storageClass, and milvus.etcd.persistence.storageClass
to longhorn alongside the existing persistence settings so the override applies
everywhere.
🧹 Nitpick comments (1)
infra/charts/openrag-stack/values.yaml (1)

348-364: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

adminUi.ingress is missing the fields its own doc references.

The comment above instructs setting the same host on both this Ingress block and openrag.ingress for single-vhost routing, but adminUi.ingress only declares enabled: true — no className, host, annotations, or tls stubs like openrag.ingress has (lines 411-423). If the admin-ui template reads these keys, they'll silently resolve to empty/nil; adding stub keys here would make the documented configuration surface discoverable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/charts/openrag-stack/values.yaml` around lines 348 - 364, The
adminUi.ingress values block is missing the configuration keys referenced by its
own routing note, so add stub fields for the admin UI ingress shape to match
openrag.ingress. Update the ingress section in values.yaml to include the same
discoverable keys (such as className, host, annotations, and tls) alongside
enabled, so templates like admin-ui ingress rendering can resolve them
consistently and the documented single-vhost setup is exposed in the chart
values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@infra/charts/openrag-stack/templates/admin-ui.yaml`:
- Around line 25-27: The admin UI service account template is using a `with`
block that treats explicit `false` as empty, so `automountServiceAccountToken`
gets omitted from the manifest. Update the `adminUi` template logic to check for
nil/defined presence instead of truthiness so `false` is rendered intentionally,
and keep the field emission tied to the `automountServiceAccountToken` value in
`admin-ui.yaml`.

In `@infra/charts/openrag-stack/templates/infinity.yaml`:
- Around line 25-27: The `with` block around `automountServiceAccountToken` in
the infinity template skips rendering when the value is explicitly `false`, so
the setting is omitted instead of being enforced. Update the template logic to
check the presence of `.Values.reranker.automountServiceAccountToken` without
using `with`, and ensure the `automountServiceAccountToken` field is emitted for
both `true` and `false` values so the `reranker` service account configuration
is applied consistently.

In `@infra/charts/openrag-stack/templates/NOTES.txt`:
- Around line 14-18: Update the NOTES text to warn only about whole-list
overrides of vllm.servingEngineSpec.modelSpec, since Helm replaces entire list
values from a values file but an indexed --set on
modelSpec[N].hf_token.secretName only affects that single entry. In the
NOTES.txt guidance, keep the warning focused on replacing the full modelSpec
list and remove language that says an indexed --set wipes
repository/tag/modelURL/resources for all entries; reference the
vllm.servingEngineSpec.modelSpec and hf_token.secretName examples when revising
the message.

In `@infra/charts/openrag-stack/templates/openrag.yaml`:
- Around line 26-28: The automountServiceAccountToken template block in the
OpenRAG manifest is using a truthiness check, so a false value gets omitted
entirely. Update the conditional around the openrag.automountServiceAccountToken
field to check for key presence instead of value truthiness, so the OpenRAG
chart can render an explicit false and allow operators to disable service
account token automounting.

In `@infra/charts/openrag-stack/templates/raycluster.yaml`:
- Around line 159-190: The Ray dashboard Ingress in raycluster.yaml is exposing
an unauthenticated administrative surface; update the ingress template around
the ray.ingress block to prevent external access unless protection is explicitly
enforced. Require an internal-only ingress class or mandatory auth-related
annotations for the dashboard route, and make the ray.ingress values document
the risk and default to a safer, non-public configuration. Use the existing
ray.ingress.enabled, className, annotations, and backend service wiring to keep
the change localized.
- Around line 25-27: The conditional rendering around
automountServiceAccountToken in the Ray cluster template is using truthiness, so
a false value is omitted and Kubernetes falls back to the default. Update the
template logic in the head and worker specs to check for nil/defined value
instead of using with, so the automountServiceAccountToken field is emitted for
both true and false. Locate the affected blocks in raycluster.yaml around the
automountServiceAccountToken entries and apply the same fix in both places.

In `@infra/charts/openrag-stack/values.yaml`:
- Around line 90-100: The PostgreSQL password is currently anchored as a literal
in the postgresql.auth.password values block, which can leave
env.secrets.POSTGRES_PASSWORD out of sync when overrides are applied. Update the
chart wiring so postgresql.auth.password and env.secrets.POSTGRES_PASSWORD both
resolve from the same template-backed value, or enforce that they must be set
together; use the existing postgresql.auth.password and
env.secrets.POSTGRES_PASSWORD entries as the points to align.

---

Duplicate comments:
In `@infra/charts/openrag-stack/values-linagora.yaml`:
- Around line 1-5: The overlay only updates the top-level
persistence.storageClass, so the PostgreSQL and Milvus sub-charts still keep the
base-file alias values. Update this values overlay to explicitly set
postgresql.primary.persistence.storageClass,
milvus.minio.persistence.storageClass, and milvus.etcd.persistence.storageClass
to longhorn alongside the existing persistence settings so the override applies
everywhere.

---

Nitpick comments:
In `@infra/charts/openrag-stack/values.yaml`:
- Around line 348-364: The adminUi.ingress values block is missing the
configuration keys referenced by its own routing note, so add stub fields for
the admin UI ingress shape to match openrag.ingress. Update the ingress section
in values.yaml to include the same discoverable keys (such as className, host,
annotations, and tls) alongside enabled, so templates like admin-ui ingress
rendering can resolve them consistently and the documented single-vhost setup is
exposed in the chart values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c0a0788f-d49f-4518-a9c6-dabab8c94513

📥 Commits

Reviewing files that changed from the base of the PR and between 13562ad and 54f8f0f.

⛔ Files ignored due to path filters (1)
  • infra/charts/openrag-stack/Chart.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • .github/workflows/build_rc.yml
  • docs/content/docs/documentation/kubernetes.md
  • infra/charts/openrag-stack/Chart.yaml
  • infra/charts/openrag-stack/templates/NOTES.txt
  • infra/charts/openrag-stack/templates/_helpers.tpl
  • infra/charts/openrag-stack/templates/admin-ui.yaml
  • infra/charts/openrag-stack/templates/configmap-env.yaml
  • infra/charts/openrag-stack/templates/extra-objects.yaml
  • infra/charts/openrag-stack/templates/infinity.yaml
  • infra/charts/openrag-stack/templates/ingress.yaml
  • infra/charts/openrag-stack/templates/openrag.yaml
  • infra/charts/openrag-stack/templates/postgres-migration-job.yaml
  • infra/charts/openrag-stack/templates/pvc.yaml
  • infra/charts/openrag-stack/templates/raycluster.yaml
  • infra/charts/openrag-stack/templates/secrets-env.yaml
  • infra/charts/openrag-stack/values-linagora.yaml
  • infra/charts/openrag-stack/values.yaml
  • infra/docker/ui.Dockerfile
💤 Files with no reviewable changes (1)
  • infra/charts/openrag-stack/templates/ingress.yaml

Comment thread infra/charts/openrag-stack/templates/admin-ui.yaml Outdated
Comment thread infra/charts/openrag-stack/templates/infinity.yaml Outdated
Comment thread infra/charts/openrag-stack/templates/NOTES.txt Outdated
Comment thread infra/charts/openrag-stack/templates/openrag.yaml Outdated
Comment thread infra/charts/openrag-stack/templates/raycluster.yaml Outdated
Comment thread infra/charts/openrag-stack/templates/raycluster.yaml
Comment thread infra/charts/openrag-stack/values.yaml Outdated

@Ahmath-Gadji Ahmath-Gadji left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice modernization overall — the NOTES.txt list-merge warning, proxy fail-fast checks, checksum/config annotation and the pinned infinity tag are all welcome. I rendered the chart with helm template and left inline comments on a handful of issues that I think should be addressed before merge (a few are confirmed rendering bugs, a few are security regressions vs the previous chart).

Two concerns that don't fit a single line:

Upgrade path / breaking renames. Every resource is renamed (rag-*openrag-*, Release.Name-* → fullname-based), and postgresql/milvus gain fullnameOverrides. For any existing release not named openrag, an upgrade renames the Postgres/Milvus StatefulSets and creates fresh empty PVCs; even for a release named openrag, the app PVCs change name (rag-dataopenrag-data), orphaning existing data (the old PVCs only survive via helm.sh/resource-policy: keep). The selector-label change (app.kubernetes.io/instance: .Release.Name → fullname) is part of the same story. Could we get an upgrade/migration note in NOTES.txt or docs/content/docs/documentation/kubernetes.md spelling this out?

venv coordination removed. The init-venv-sync / init-venv init containers (uv sync + .ready flag on the shared RWX venv PVC) are gone from both the openrag Deployment and the RayCluster, but the venv PVC is still mounted at /app/.venv everywhere. What populates the venv now, and what prevents the head, workers and app pod from racing concurrent uv runs on the same PVC at first boot? If the v2.0.0-rc.1 images bake the venv into the image, the PVC mount (which shadows it) should probably go too.

Comment thread infra/charts/openrag-stack/templates/secrets-env.yaml
Comment thread infra/charts/openrag-stack/values.yaml Outdated
Comment thread infra/charts/openrag-stack/values.yaml Outdated
Comment thread infra/charts/openrag-stack/templates/admin-ui.yaml Outdated
Comment thread infra/charts/openrag-stack/templates/admin-ui.yaml
Comment thread infra/charts/openrag-stack/values.yaml
Comment thread infra/charts/openrag-stack/values.yaml Outdated
Comment thread infra/charts/openrag-stack/values-linagora.yaml
Comment thread .github/workflows/build_rc.yml Outdated
@andyne13
andyne13 changed the base branch from refactor/hexagonal to main July 8, 2026 22:22
@andyne13
andyne13 changed the base branch from main to develop July 8, 2026 22:48
@Ahmath-Gadji
Ahmath-Gadji requested a review from andyne13 July 16, 2026 08:08

@andyne13 andyne13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ThibautChoppy. I resolved the Chart.yaml conflict with develop (kept your version: 0.6.0 and the added metadata; set appVersion: "2.0.0" for current GA), which triggered CI. Everything is green except tests (3.12), which fails 5 Helm security-hardening checks — this branch predates that hardening on develop, so the chart currently regresses it. Please reconcile with develop:

  1. Placeholder DB secretpostgresql.auth.password must not default to CHANGE_ME_STRONG_PASSWORD (empty / secret ref).
  2. Ray dashboardray.dashboardHost must default to 127.0.0.1, not 0.0.0.0 (ShadowRay / CVE-2023-48022).
  3. securityContexts — restore the restricted securityContext blocks on the workloads.
  4. Ingresstemplates/ingress.yaml was removed; keep the hardened template (not exposed by default + TLS).
  5. Migration Job — reference the rag-env secret instead of inlining literal secret values.

The two test files are the exact spec: tests/unit/infra/test_helm_security_hardening.py and tests/unit/infra/test_postgres_migration_job_template.py. uv run pytest tests/unit/infra/ -q should be green before pushing (that re-runs CI). The rest of the PR looks good — and if it's easy, splitting the app-code commits from the Helm work would review faster, though not required.

Comment thread infra/charts/openrag-stack/values-linagora.yaml
Every other optional model-registry toggle is explicitly listed in the
chart's env.config for discoverability (RERANKER_ENABLED,
WITH_CHAINLIT_UI, ...). This one was missing entirely, so Helm-based
deployments had no discoverable way to opt in even though the generic
config map passthrough already supported it.
…set warning

- values-linagora.yaml: persistence.storageClass only reaches the chart's
  own 4 PVCs — the postgresql/milvus/minio/etcd sub-charts read it through
  a YAML alias resolved before Helm merges this overlay, so it never
  reached them. Set storageClass explicitly per sub-chart here too.
  Verified with `helm template`: postgresql/etcd/minio now render
  storageClassName: longhorn alongside the 4 chart PVCs.
- NOTES.txt: the HF_TOKEN mismatch warning wrongly implied an indexed
  --set (modelSpec[N].hf_token.secretName=...) wipes sibling entries.
  Helm only replaces whole lists across values sources — an indexed
  override is safe. Narrowed the warning to the actual risk (redefining
  the entire modelSpec array).

Addresses the two open review threads on PR linagora#633.
@ThibautChoppy

Copy link
Copy Markdown
Contributor Author

LGTM, waiting for the next release version for image tag update in valuefile before merge

@Ahmath-Gadji Ahmath-Gadji added this to the v2.0.2 milestone Jul 29, 2026
@Ahmath-Gadji
Ahmath-Gadji self-requested a review July 29, 2026 09:12
…ement

# Conflicts:
#	infra/charts/openrag-stack/Chart.yaml
#	infra/charts/openrag-stack/values.yaml

@andyne13 andyne13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ThibautChoppy — I re-checked the five points from my earlier review and they're all addressed: the DB password placeholder is gone, ray.dashboardHost defaults to loopback, the shared security block is applied through the merge helpers, the ingress is off by default behind a required host guard, and the migration Job now goes through envFrom with no inlined literals. tests/unit/infra/ is green at this head. The secrets-env.yaml work is really nice — reading POSTGRES_PASSWORD live from postgresql.auth.password instead of the YAML anchor is a subtle trap well avoided, and the presence-based merge helpers neatly dodge Sprig merge's zero-value behaviour.

One thing I ran into while testing the built image, and I think it just needs a one-liner in infra/docker/ui.Dockerfile:

The new chown -R 10001:10001 lines up with the chart's podSecurityContext, but the image still ends with USER nginx, which is UID 101 in nginxinc/nginx-unprivileged. Under Helm that's all fine since the pod forces runAsUser: 10001 — it's only docker compose, where nothing overrides the user, that ends up running as 101 against dirs owned by 10001. Building from this branch:

$ docker exec <admin-ui> sh -c 'id; ls -ldn /var/cache/nginx; touch /var/cache/nginx/probe'
uid=101(nginx) gid=101(nginx)
drwxrwxr-x 1 10001 10001 /var/cache/nginx
touch: /var/cache/nginx/probe: Permission denied

Took me a while to spot, because the container starts and serves the UI perfectly happily — it only shows up once nginx spools a request body to client_temp, which is what client_max_body_size 1024M in the admin conf is there for. Either USER 10001:10001 in the Dockerfile or user: "10001:10001" on the compose service should cover it, whichever you prefer.

Non-blocking, just worth a line in the docs while you're here: the PVCs/ConfigMap/Secret get renamed (rag-data{{ fullname }}-data, etc.). helm.sh/resource-policy: keep means existing volumes survive, but they're no longer mounted, so an upgraded release would come up with empty volumes unless the operator sets fullnameOverride or migrates. An upgrade note would save someone a confusing afternoon.

Two small things for later, no need to touch them here: mergeSecurityContext is a shallow merge, so a component overriding capabilities would drop the shared drop: [ALL] (nothing does today); and the three vllm modelSpec entries still default to tag: "latest" — your own note says to pin, so the defaults could follow suit.

Happy to approve once the UID bit is sorted.

@linagora linagora deleted a comment from EnjoyBacon7 Jul 30, 2026
@linagora linagora deleted a comment from EnjoyBacon7 Jul 30, 2026
Comment thread infra/charts/openrag-stack/values.yaml Outdated
@Ahmath-Gadji

Copy link
Copy Markdown
Collaborator

@andyne13 — following up on your 2026-07-16 review. All 5 issues you flagged look resolved as of the current head (15db9a1):

  1. Placeholder DB secretpostgresql.auth.password now defaults to "", and secrets-env.yaml fails the install closed if env.secrets.POSTGRES_PASSWORD is left unset or still a known placeholder (CHANGE_ME_STRONG_PASSWORD, etc.).
  2. Ray dashboardray.dashboardHost defaults to 127.0.0.1 again, with a comment explicitly calling out CVE-2023-48022 / ShadowRay.
  3. securityContexts — restored via a shared security.podSecurityContext / containerSecurityContext block (runAsNonRoot, seccompProfile: RuntimeDefault, allowPrivilegeEscalation: false, capabilities.drop: [ALL]), merged per-workload through the new openrag-stack.mergeSecurityContext helper (presence-based, so an explicit false/0 override isn't silently dropped).
  4. Ingress — no longer a bare templates/ingress.yaml; openrag.yaml / raycluster.yaml each render their own optional Ingress, disabled by default and gated by a required host check so it can never render with a blank/wildcard host. TLS is supported.
  5. Migration Jobpostgres-migration-job.yaml now references the env Secret via configMapRef / secretRef (through the openrag-stack.secretName / fullname helpers) instead of inlining literal secret values.

tests/unit/infra/test_helm_security_hardening.py and test_postgres_migration_job_template.py assert all of the above and pass in CI; I also re-ran them locally against the current head (17/17 green), plus the full unit suite and a helm template/helm lint render.

The branch is also conflict-free against develop again (merged in the 2.0.1 release-version bump you were waiting on).

Could you take another look and update/clear your review when you get a chance? Thanks for catching these the first time around.

values.yaml described admin-ui and openrag/ray-serve as two separate
Ingress resources needing a shared host, but the chart now renders a
single combined Ingress in openrag.yaml where adminUi.ingress.enabled
only toggles the /app/ path. adminUi.ingress has no host/className/tls
fields at all, so the old text could lead an operator to configure
fields the template silently ignores.
Ahmath-Gadji
Ahmath-Gadji previously approved these changes Jul 30, 2026

@Ahmath-Gadji Ahmath-Gadji left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@andyne13

Copy link
Copy Markdown
Contributor

@Ahmath-Gadji thanks for the follow-up — agreed, and I confirmed the same five myself in this comment last night (I also re-ran tests/unit/infra/ against the head merged with develop: 17 passed).

That comment is easy to miss though, because it also raised a sixth item that came out of building the image, and it's still open at cb93280 — that's the one thing holding my approval, not the original five.

infra/docker/ui.Dockerfile: the chown -R 10001:10001 matches the chart's podSecurityContext, but the image still ends with USER nginx, which is UID 101 in nginxinc/nginx-unprivileged. Under Helm that's fine — the pod forces runAsUser: 10001. Under docker compose nothing overrides the user, so it runs as 101 against directories owned by 10001. Built from this branch:

$ docker build -f infra/docker/ui.Dockerfile -t adminui:pr633 .
$ docker run -d --name t -p 18080:8080 adminui:pr633
$ docker exec t sh -c 'id; ls -ldn /var/cache/nginx; touch /var/cache/nginx/probe'
uid=101(nginx) gid=101(nginx) groups=101(nginx)
drwxrwxr-x 1 10001 10001 /var/cache/nginx
touch: /var/cache/nginx/probe: Permission denied

For reference, the base image ships that directory owned by 101, so the chown is what introduces it:

$ docker run --rm --entrypoint sh nginxinc/nginx-unprivileged:1.27-alpine -c 'ls -ldn /var/cache/nginx'
drwxrwxr-x 1 101 0 /var/cache/nginx

It's easy to miss because the container still starts and serves the UI normally (I got a 301 from / as expected) — it only bites when nginx spools a request body to client_temp, which is what client_max_body_size 1024M in the admin conf exists for. So uploads through the proxy in the compose deployment.

Either USER 10001:10001 in the Dockerfile or user: "10001:10001" on the compose admin-ui service should cover it, whichever fits better.

Also non-blocking, from the same pass: the PVCs/ConfigMap/Secret are renamed (rag-data{{ fullname }}-data, etc.). helm.sh/resource-policy: keep means existing volumes survive, but they're no longer mounted, so an upgraded release comes up with empty volumes unless the operator sets fullnameOverride or migrates — worth a line in the upgrade docs.

Happy to approve as soon as the UID bit is sorted.

@andyne13

Copy link
Copy Markdown
Contributor

@ThibautChoppy one last thing before I can approve — a one-liner in infra/docker/ui.Dockerfile, details and reproduction in my earlier comment.

Short version: the new chown -R 10001:10001 matches the chart's podSecurityContext, but the image still ends with USER nginx, which is UID 101 in nginxinc/nginx-unprivileged. Under Helm that's fine (the pod forces runAsUser: 10001); under docker compose nothing overrides the user, so it runs as 101 against directories owned by 10001:

$ docker exec <admin-ui> sh -c 'id; ls -ldn /var/cache/nginx; touch /var/cache/nginx/probe'
uid=101(nginx) gid=101(nginx)
drwxrwxr-x 1 10001 10001 /var/cache/nginx
touch: /var/cache/nginx/probe: Permission denied

Either USER 10001:10001 in the Dockerfile or user: "10001:10001" on the compose admin-ui service works — whichever you prefer.

It's an easy one to miss because the container still starts and serves the UI normally; it only bites when nginx spools a request body to client_temp, which is what client_max_body_size 1024M in the admin conf is there for — so document uploads through the compose proxy.

Everything else from my 16 July review is confirmed resolved (thanks @Ahmath-Gadji for the follow-up), and tests/unit/infra/ is green at this head — 17 passed against it merged with develop. Push that change and I'll approve straight away.

@andyne13

Copy link
Copy Markdown
Contributor

@ThibautChoppy thanks for the work here — the five points from my 16 July review are all resolved, and tests/unit/infra/ is green at this head (17 passed against it merged with develop). One item left before I can approve, in infra/docker/ui.Dockerfile.

Correcting something from my earlier comment, where I framed this as compose-only: admin-ui is actually the one image that doesn't follow the arbitrary-UID pattern api.Dockerfile deliberately uses.

# api.Dockerfile — the OpenShift pattern
useradd --uid 10001 --gid 0 ... && chgrp -R 0 /app /opt/uv && chmod g=u ...
USER 10001
# values.yaml: openrag.podSecurityContext -> runAsUser: 10001, runAsGroup: 0

# ui.Dockerfile — group 10001, and the final user is still the base image's
chown -R 10001:10001 /var/cache/nginx /etc/nginx/conf.d /var/run && chmod -R g+w ...
USER nginx        # = UID 101 in nginxinc/nginx-unprivileged
# values.yaml: adminUi.podSecurityContext -> runAsUser: 10001, runAsGroup: 10001

1. Under docker compose this is broken — nothing overrides the user, so the container runs as 101 against directories owned by 10001. Built from this branch:

$ docker exec <admin-ui> sh -c 'id; ls -ldn /var/cache/nginx; touch /var/cache/nginx/probe'
uid=101(nginx) gid=101(nginx)
drwxrwxr-x 1 10001 10001 /var/cache/nginx
touch: /var/cache/nginx/probe: Permission denied

The base image ships that directory owned by 101, so the chown is what introduces it:

$ docker run --rm --entrypoint sh nginxinc/nginx-unprivileged:1.27-alpine -c 'ls -ldn /var/cache/nginx'
drwxrwxr-x 1 101 0 /var/cache/nginx

2. On OpenShift it depends on your SCC — and here I'd rather ask than assert, since you know the target better than I do. Under the default restricted-v2 the UID is assigned arbitrarily from the namespace range with GID 0, which is exactly why api.Dockerfile does --gid 0 + chgrp -R 0 + chmod g=u and why openrag.podSecurityContext keeps runAsGroup: 0. admin-ui asks for runAsUser: 10001 / runAsGroup: 10001, which only works where the SCC allows pinning that UID/GID. With a permissive SCC (anyuid or equivalent) it's fine; otherwise the pod is rejected, or runs as a UID that can't write those paths. I haven't verified this on a cluster — what does your deployment use?

The fix that covers both, aligning admin-ui with what the API image already does:

-RUN chown -R 10001:10001 /var/cache/nginx /etc/nginx/conf.d /var/run && \
+RUN chown -R 10001:0 /var/cache/nginx /etc/nginx/conf.d /var/run && \
     chmod -R g+w /var/cache/nginx /etc/nginx/conf.d /var/run

-USER nginx
+USER 10001:0

plus adminUi.podSecurityContext.runAsGroup: 0 in the chart, matching openrag. Under compose the user then owns the paths; on OpenShift any arbitrary UID writes via group 0. (Changing only the chown isn't enough for compose — uid 101 isn't in group 0, so the USER line has to move too.)

Worth saying why this is easy to miss: the container starts and serves the UI normally, so neither CI nor a smoke test catches it. It only fails when nginx spools a request body to client_temp — i.e. document uploads through the proxy.

Non-blocking, for the docs whenever you touch them: the PVCs/ConfigMap/Secret are renamed (rag-data{{ fullname }}-data, etc.). helm.sh/resource-policy: keep means existing volumes survive, but they're no longer mounted, so an upgraded release comes up with empty volumes unless the operator sets fullnameOverride or migrates. A line in the upgrade notes would save someone a confusing afternoon.

Let me know about the SCC, and I'll approve as soon as the UID part is sorted.

infra/docker/ui.Dockerfile chowned nginx's runtime paths to 10001:10001 but
left USER as the base image's `nginx` (uid 101, gid 101 only), so the running
user had neither owner nor group access to them. The Helm chart matched the
chown with adminUi.podSecurityContext.runAsGroup: 10001, which only works on a
cluster whose SCC allows pinning that GID.

Switch to the same arbitrary-UID pattern api.Dockerfile already uses: chown to
group 0, chmod g+w, and USER 10001:0, with adminUi.podSecurityContext.runAsGroup
set to 0 to match. Under compose the user then owns the paths; under OpenShift's
restricted-v2 SCC an arbitrary UID writes through group 0. The base image itself
ships these paths as 101:0 for the same reason.

Nothing writes under /var/cache/nginx today — nginx-unprivileged points every
*_temp_path at /tmp and openrag-admin.conf sets `proxy_cache off` — so the
mismatch was latent rather than actively breaking uploads. The one observable
symptom was docker-entrypoint.d/10-listen-on-ipv6-by-default.sh failing to
patch /etc/nginx/conf.d/default.conf at startup.

Add regression tests pinning the image's USER/ownership and the chart's
runAsGroup, and document the 0.6.0 rag-* -> {{ fullname }}-* resource rename,
which leaves kept PVCs unmounted unless the operator overrides fullnameOverride
or migrates the data.
templates/postgres-migration-job.yaml runs the OpenRAG image with the same
pinned securityContext as its Deployment (runAsUser: 10001, runAsGroup: 0) but
never rendered a serviceAccountName, so it fell back to the namespace's
`default` ServiceAccount.

On OpenShift that puts the Job under restricted-v2 (MustRunAsRange) rather than
whatever SecurityContextConstraints is bound to the app's own ServiceAccount,
and admission rejects a runAsUser outside the namespace's assigned UID range.
Because the Job is a pre-install/pre-upgrade hook, that failure aborts the whole
release — and it only shows up once postgresProvisioning.migrationJob is enabled,
which is exactly the managed-Postgres setup the docs recommend on OpenShift.

Reuse openrag.serviceAccountName through tpl, like the Deployment does, so a
templated value such as "{{ .Release.Name }}-openrag" resolves the same way.

@andyne13 andyne13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving.

The admin-ui permission blocker is resolved — re-verified empirically by replicating the final stage on the base image and probing /var/cache/nginx, /etc/nginx/conf.d and /var/run:

variant runs as writable
before (chown 10001:10001 + USER nginx) uid=101 gid=101 denied on all three
after (chown 10001:0 + USER 10001:0) uid=10001 gid=0 OK on all three
after, arbitrary UID (restricted-v2 sim) uid=1000670000 gid=0 OK on all three

Taking the portable variant over the minimal one-line fix was the right call: it repairs compose and makes the chart work under a restricted SCC, not only a RunAsAny one — which matters for a chart that ships publicly.

fix(helm): give the migration Job the same ServiceAccount as the app — correct. It renders the SA exactly as templates/openrag.yaml does, so a templated value resolves identically in both and an unset value behaves as before. The rationale holds: the Job pins runAsUser: 10001, which under the namespace's default SA lands on restricted-v2 and gets rejected — and as a pre-upgrade hook that aborts the whole release. Test covers it.

Six checks green on 70f3ab8e, no unresolved threads.

Merge note: Chart.yaml moves to version: 0.6.0 and keeps appVersion: "2.0.1" — appVersion and the image tags are set by the version bump, so nothing to change here.

Follow-up, not blocking: you flagged that raycluster and infinity weren't covered by the security-context changes. Worth a separate issue so it doesn't get lost — happy to file it.

@andyne13

Copy link
Copy Markdown
Contributor

Thanks Thibaut for the work here, and for the migration Job ServiceAccount fix. Everything is green on my side — approved, and we're merging this for 2.1.0.

@andyne13
andyne13 merged commit 187a359 into linagora:develop Jul 30, 2026
6 checks passed
@andyne13 andyne13 mentioned this pull request Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore No production code impact, typically improve tooling, code quality, etc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants