From ce30ca09f29eeafabb8214c3ec3385d8e2d5b907 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 6 May 2026 13:17:06 +0000 Subject: [PATCH 1/2] docs(secrets): document additional_env_froms, multi-pod env scoping, and Holmes add-on env vars - Warn that {{ env.X }} replaces the entire field value (no prefix/suffix concatenation) - Document runner.additional_env_froms for bulk-injecting Secret/ConfigMap keys - Clarify that runner and HolmesGPT pods do not share environment variables, so secrets used by both must be declared in both additional_env_vars blocks - Mention holmes.operator.additionalEnvVars and holmes.mcpAddons.{aws,azure}.additionalEnvVars for users running those add-ons --- docs/setup-robusta/configuration-secrets.rst | 61 ++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/docs/setup-robusta/configuration-secrets.rst b/docs/setup-robusta/configuration-secrets.rst index 1985f89c8..bec1e9928 100644 --- a/docs/setup-robusta/configuration-secrets.rst +++ b/docs/setup-robusta/configuration-secrets.rst @@ -64,6 +64,67 @@ You can now reference the environment variable elsewhere in your configuration u This setup keeps sensitive values out of your Helm files and version control, while still allowing them to be dynamically injected at runtime. +.. warning:: + + The placeholder must be the **entire** value of the field. Robusta replaces the + whole string with the environment variable's value, so ``"Bearer {{ env.TOKEN }}"`` + becomes just the token — the literal ``Bearer `` prefix is dropped. If you need a + prefix or suffix, bake it into the secret itself. + +.. note:: + + ``{{ env.X }}`` substitution only happens inside Robusta's configuration + (``sinks``, ``globalConfig``, ``customPlaybooks``, ``playbookRepos``). The + environment variable still needs to actually exist inside the pod that reads + that config — see :ref:`Runner vs. HolmesGPT pods` below. + +Injecting Many Keys at Once with ``additional_env_froms`` +----------------------------------------------------------- + +If you have a Secret (or ConfigMap) with multiple keys you want to expose to the +runner, ``runner.additional_env_froms`` is often less verbose than listing each +key under ``additional_env_vars``: + +.. code-block:: yaml + + runner: + additional_env_froms: + - secretRef: + name: my-robusta-secrets + +Every key in ``my-robusta-secrets`` becomes an environment variable on the runner +pod (key name = env var name), which you can then reference with +``{{ env.KEY_NAME }}``. + +.. _Runner vs. HolmesGPT pods: + +Runner vs. HolmesGPT Pods +-------------------------------------------------- + +Robusta runs as multiple pods. An environment variable defined under ``runner.additional_env_vars`` +is **only** available to the runner; one defined under ``holmes.additionalEnvVars`` is **only** +available to the HolmesGPT pod. If a value (e.g. an API key) is consumed by both, you must add +the env var to **both** blocks — they do not share environment. + +A common pitfall: a secret referenced inside ``globalConfig`` is read by the runner, so it +needs to be in ``runner.additional_env_vars``. The same secret used by HolmesGPT must +**also** be added to ``holmes.additionalEnvVars``. + +Secrets for HolmesGPT Add-Ons +-------------------------------------------------- + +The ``holmes.additionalEnvVars`` block only injects env vars into the main HolmesGPT pod. +HolmesGPT also ships several optional sub-deployments that have their own +``additionalEnvVars`` arrays: + +- ``holmes.operator.additionalEnvVars`` — for the HolmesGPT operator +- ``holmes.mcpAddons.aws.additionalEnvVars`` — for the AWS MCP add-on +- ``holmes.mcpAddons.azure.additionalEnvVars`` — for the Azure MCP add-on + +If you enable any of those add-ons and need to pass them a secret, add it under the +matching block — values placed only in ``holmes.additionalEnvVars`` will not reach +the add-on pods. + .. _Reading the Robusta UI Token from a secret in HolmesGPT: Using an Existing Secret for the Robusta UI Token From eb254f19523ab2f80c477224584fd37f4ca942c7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 6 May 2026 13:23:29 +0000 Subject: [PATCH 2/2] fix(env-substitution): replace placeholders in-place instead of clobbering the whole value Previously, get_env_replacement returned the env var's value as the new field value, so `"Bearer {{ env.TOKEN }}"` was silently replaced with just the token and any prefix/suffix was dropped. Switch to re.sub so only the placeholder is substituted, leaving the rest of the string intact. Multiple placeholders in the same field are also now supported (e.g. `"{{ env.USER }}:{{ env.PASSWORD }}"`). The "return None when there is no placeholder" contract is preserved so existing callers in runner_config.py and replace_env_vars_values keep working. Also updates the docs to advertise the supported concatenation/multi-placeholder patterns. --- docs/setup-robusta/configuration-secrets.rst | 9 +++------ src/robusta/core/playbooks/playbook_utils.py | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/setup-robusta/configuration-secrets.rst b/docs/setup-robusta/configuration-secrets.rst index bec1e9928..bf70ed054 100644 --- a/docs/setup-robusta/configuration-secrets.rst +++ b/docs/setup-robusta/configuration-secrets.rst @@ -64,12 +64,9 @@ You can now reference the environment variable elsewhere in your configuration u This setup keeps sensitive values out of your Helm files and version control, while still allowing them to be dynamically injected at runtime. -.. warning:: - - The placeholder must be the **entire** value of the field. Robusta replaces the - whole string with the environment variable's value, so ``"Bearer {{ env.TOKEN }}"`` - becomes just the token — the literal ``Bearer `` prefix is dropped. If you need a - prefix or suffix, bake it into the secret itself. +You can also combine static text with placeholders (e.g. ``"Bearer {{ env.TOKEN }}"``) +or reference multiple environment variables in the same field +(e.g. ``"{{ env.USER }}:{{ env.PASSWORD }}"``). .. note:: diff --git a/src/robusta/core/playbooks/playbook_utils.py b/src/robusta/core/playbooks/playbook_utils.py index b24fc2475..d97597ce0 100644 --- a/src/robusta/core/playbooks/playbook_utils.py +++ b/src/robusta/core/playbooks/playbook_utils.py @@ -7,16 +7,23 @@ from pydantic.types import SecretStr +_ENV_PLACEHOLDER_RE = re.compile(r"{{\s*env\.([^}\s]+)\s*}}") + + def get_env_replacement(value: str) -> Optional[str]: - env_values = re.findall(r"{{[ ]*env\.(.*)[ ]*}}", value) - if env_values: - env_var_value = os.environ.get(env_values[0].strip(), None) + if not _ENV_PLACEHOLDER_RE.search(value): + return None + + def _sub(match: "re.Match[str]") -> str: + name = match.group(1).strip() + env_var_value = os.environ.get(name, None) if not env_var_value: - msg = f"ENV var replacement {env_values[0]} does not exist for param: {value}" + msg = f"ENV var replacement {name} does not exist for param: {value}" logging.error(msg) raise Exception(msg) return env_var_value - return None + + return _ENV_PLACEHOLDER_RE.sub(_sub, value) def replace_env_vars_values(values: Dict) -> Dict: