diff --git a/docs/setup-robusta/configuration-secrets.rst b/docs/setup-robusta/configuration-secrets.rst index 1985f89c8..bf70ed054 100644 --- a/docs/setup-robusta/configuration-secrets.rst +++ b/docs/setup-robusta/configuration-secrets.rst @@ -64,6 +64,64 @@ 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. +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:: + + ``{{ 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 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: