Skip to content

fix(helm): expand CLICKHOUSE_PASSWORD in webapp CLICKHOUSE_URL via kubelet#3445

Closed
ThullyoCunha wants to merge 1 commit intotriggerdotdev:mainfrom
ThullyoCunha:fix/clickhouse-external-url-kubelet-expansion
Closed

fix(helm): expand CLICKHOUSE_PASSWORD in webapp CLICKHOUSE_URL via kubelet#3445
ThullyoCunha wants to merge 1 commit intotriggerdotdev:mainfrom
ThullyoCunha:fix/clickhouse-external-url-kubelet-expansion

Conversation

@ThullyoCunha
Copy link
Copy Markdown

Summary

When the official Helm chart is deployed with an external ClickHouse and clickhouse.external.existingSecret set — the documented path for not committing secrets to values.yaml — the webapp pod crash-loops on startup:

goose run: parse "http://default:${CLICKHOUSE_PASSWORD}@<host>:8123?secure=false": net/url: invalid userinfo

Context in vouch request #3443.

Root cause

Two pieces interact:

  1. hosting/k8s/helm/templates/_helpers.tpl renders CLICKHOUSE_URL (and RUN_REPLICATION_CLICKHOUSE_URL) with a shell-style literal ${CLICKHOUSE_PASSWORD} expecting bash expansion at container start.
  2. docker/scripts/entrypoint.sh does export GOOSE_DBSTRING="$CLICKHOUSE_URL" — single-pass POSIX sh substitution, so the inner ${...} survives as literal text and goose rejects it.

Reproduces against the latest published chart (oci://ghcr.io/triggerdotdev/charts/trigger:4.0.5) and main.

Fix

Switch the two helpers (external + existingSecret branch) from shell-style ${CLICKHOUSE_PASSWORD} to Kubernetes' $(CLICKHOUSE_PASSWORD). Kubelet substitutes $(VAR) at pod-creation time from earlier env entries, and the chart already declares CLICKHOUSE_PASSWORD from the Secret immediately before CLICKHOUSE_URL, so the URL reaches the entrypoint with the real password already inlined. No entrypoint change, no image change. The plain-password branch (no existingSecret) is unchanged.

Operator caveat added as template comments: CLICKHOUSE_PASSWORD must be URL-userinfo-safe since kubelet substitutes verbatim without percent-encoding. Hex-encoded passwords (e.g. openssl rand -hex 32) are safe by construction.

Verification

  • helm template against external.existingSecret now renders value: "http://default:$(CLICKHOUSE_PASSWORD)@<host>:8123?secure=false" (was ${CLICKHOUSE_PASSWORD}).
  • helm template against the plain-password branch is byte-identical to before.
  • Deployed end-to-end on a staging EKS cluster (Meistrari platform): webapp container reaches goose: successfully migrated database to version: 6, Node.js ClickHouse client connects at runtime.

Alternatives considered

  • Change entrypoint.sh to eval / envsubst the URL — larger surface, touches every deployment mode (Docker Compose + k8s) and every container image.
  • Mirror the Postgres pattern (chart reads the full URL via valueFrom.secretKeyRef, as in trigger-v4.postgres.useSecretUrl) — cleaner long-term but requires a new values.yaml field and a migration path for existing users. Happy to follow up with that as a separate PR if the minimal fix here isn't the preferred direction.

Closes the issue raised in #3443.

…belet

When clickhouse.external.existingSecret is set, the chart rendered the
CLICKHOUSE_URL env var with a literal shell-style ${CLICKHOUSE_PASSWORD}
placeholder, expecting bash to expand it at container start. But
docker/scripts/entrypoint.sh hands the value straight to goose with a
single-pass sh expansion (export GOOSE_DBSTRING="$CLICKHOUSE_URL"), so
the inner ${...} reaches goose as literal text and breaks the
ClickHouse migration:

  goose run: parse "http://default:${CLICKHOUSE_PASSWORD}@host:8123?secure=false":
  net/url: invalid userinfo

Switch to Kubernetes' $(VAR) syntax in both clickhouse URL helpers.
Kubelet substitutes $(CLICKHOUSE_PASSWORD) at container-creation time
from the CLICKHOUSE_PASSWORD env var the chart already sets just before
CLICKHOUSE_URL, so the URL arrives at the entrypoint with the real
password already inlined — no entrypoint change needed, works for any
container image / shell.

The plain-password branch (no existingSecret) is unchanged.

Operator caveat: CLICKHOUSE_PASSWORD must be URL-userinfo-safe because
kubelet substitutes verbatim without percent-encoding. Hex-encoded
passwords (e.g. openssl rand -hex 32) are safe by construction.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 24, 2026

⚠️ No Changeset found

Latest commit: d1f4748

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8b71a9ca-3430-4bb0-9625-1b149abf5a27

📥 Commits

Reviewing files that changed from the base of the PR and between d205955 and d1f4748.

📒 Files selected for processing (1)
  • hosting/k8s/helm/templates/_helpers.tpl

Walkthrough

This change modifies Helm template helper functions in hosting/k8s/helm/templates/_helpers.tpl that generate ClickHouse database URLs. When external secrets are enabled, the password reference syntax is updated from ${CLICKHOUSE_PASSWORD} to $(CLICKHOUSE_PASSWORD) in both the standard and replication URL helpers. Clarifying comments are added to document the placeholder expansion behavior and URL-safety expectations for the substituted password value.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

✨ 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 and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

Thanks for your contribution! We require all external PRs to be opened in draft status first so you can address CodeRabbit review comments and ensure CI passes before requesting a review. Please re-open this PR as a draft. See CONTRIBUTING.md for details.

@github-actions github-actions Bot closed this Apr 24, 2026
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

@ericallam
Copy link
Copy Markdown
Member

@ThullyoCunha make sure to open the PR in draft status or it gets auto-closed

@ThullyoCunha
Copy link
Copy Markdown
Author

Thanks @ericallam — my bad, missed the draft requirement. Reopened as a draft in #3449 (same patch).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants