Repository navigation
RHIDP-18071: extend environment variable parsing behaviour #2969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6bfe686
b95a4fb
62dbdb1
c7367f8
b3347ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,11 +26,11 @@ | |
| from urllib.parse import urljoin | ||
|
|
||
| import yaml | ||
| from ogx.core.stack import replace_env_vars | ||
| from pydantic import SecretStr | ||
|
|
||
| import constants | ||
| from log import get_logger, setup_logging | ||
| from utils.env_vars import normalize_env_references, resolve_env_references | ||
|
|
||
| logger = get_logger(__name__) | ||
|
|
||
|
|
@@ -315,10 +315,10 @@ def construct_vector_stores_section( | |
| output = ogx_config["registered_resources"]["vector_stores"].copy() | ||
|
|
||
| # append new vector_stores entries, skipping duplicates | ||
| # Resolve ${env.VAR} patterns so comparisons work when existing entries | ||
| # Resolve ${env.VAR} and ${VAR} so comparisons work when existing entries | ||
| # use environment variable references and new entries have resolved values. | ||
| existing_store_ids = { | ||
| replace_env_vars(vs.get("vector_store_id", "")) for vs in output | ||
| resolve_env_references(vs.get("vector_store_id", "")) for vs in output | ||
| } | ||
| added = 0 | ||
| for brag in byok_rag: | ||
|
|
@@ -879,8 +879,8 @@ def enrich_solr( # pylint: disable=too-many-locals,too-many-statements,too-many | |
| base_url_raw = ( | ||
| str(rhokp_raw) if rhokp_raw is not None else constants.RH_SERVER_OKP_DEFAULT_URL | ||
| ) | ||
| # Resolve environment variables in the URL (e.g., ${env.RH_SERVER_OKP}) | ||
| base_url = replace_env_vars(base_url_raw) | ||
| # Resolve ${env.RH_SERVER_OKP} and ${RH_SERVER_OKP} before joining the URL path. | ||
| base_url = resolve_env_references(base_url_raw) | ||
| solr_url = urljoin(base_url, "/solr") | ||
|
|
||
| logger.info("Enriching OGX config with OKP") | ||
|
|
@@ -1479,6 +1479,8 @@ def synthesize_configuration( # pylint: disable=too-many-locals | |
| dict[str, Any]: The synthesized OGX configuration. | ||
|
|
||
| """ | ||
| # Keep raw references deferred for OGX startup while canonicalizing their syntax. | ||
| lcs_config = normalize_env_references(lcs_config) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| unified = ogx_config_section(lcs_config).get("config") | ||
|
|
||
| # 1-2. Select the baseline. | ||
|
|
@@ -1712,6 +1714,9 @@ def generate_configuration( | |
| """ | ||
| logger.info("Reading OGX configuration from file %s", input_file) | ||
|
|
||
| # Normalize raw LCORE enrichment references without looking up their values. | ||
| config = normalize_env_references(config) | ||
|
|
||
| with open(input_file, encoding="utf-8") as file: | ||
| ogx_config = yaml.safe_load(file) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| """Normalize environment references and resolve them using OGX semantics.""" | ||
|
|
||
| import re | ||
| from typing import Any, Final | ||
|
|
||
| from ogx.core.stack import replace_env_vars | ||
|
|
||
| # Match ${VAR} and ${env.VAR}, optionally with :=default or :+value. | ||
| # Names use OGX's uppercase/digit/underscore grammar; operands end at the first }. | ||
| _ENV_REFERENCE_PATTERN: Final[re.Pattern[str]] = re.compile( | ||
| r"\$\{(?:env\.)?[A-Z0-9_]+(?::[=+][^}]*)?\}" | ||
| ) | ||
|
|
||
|
|
||
| def canonicalize_reference(match: re.Match[str]) -> str: | ||
| """Preserve OGX references and prefix short references with env. | ||
|
|
||
| Parameters: | ||
| match: A supported reference found by the normalizer's regex. | ||
|
|
||
| Returns: | ||
| The reference in canonical ${env.VAR} syntax, without resolving it. | ||
|
|
||
| """ | ||
| # group(0) is the complete matched reference, including ${ and }. | ||
| reference = match.group(0) | ||
| if reference.startswith("${env."): | ||
| return reference | ||
| return reference.replace("${", "${env.", 1) | ||
|
|
||
|
|
||
| def normalize_env_references(config: Any) -> Any: | ||
| """Convert supported ${VAR} references to ${env.VAR} without resolving them. | ||
|
|
||
| Recursively visit dictionary values and list elements, rebuilding each | ||
| container and normalizing its strings. Keys and existing OGX references are | ||
| unchanged. Variable names and operators follow OGX's resolver grammar. | ||
|
|
||
| Parameters: | ||
| config: A configuration value, including nested containers and scalars. | ||
|
|
||
| Returns: | ||
| The configuration with supported short references canonicalized. | ||
|
|
||
| """ | ||
| if isinstance(config, str): | ||
| # sub calls the canonicalizer for each match, preserving surrounding text. | ||
| # For ${HOST}:${PORT}, each reference gets its own canonical replacement. | ||
| return _ENV_REFERENCE_PATTERN.sub(canonicalize_reference, config) | ||
| if isinstance(config, dict): | ||
| return {key: normalize_env_references(value) for key, value in config.items()} | ||
| if isinstance(config, list): | ||
| return [normalize_env_references(value) for value in config] | ||
| return config | ||
|
|
||
|
|
||
| def resolve_env_references(config: Any) -> Any: | ||
| """Normalize environment references, then delegate their resolution to OGX. | ||
|
|
||
| Parameters: | ||
| config: A configuration value, including nested containers and scalars. | ||
|
|
||
| Returns: | ||
| The recursively resolved configuration. OGX may convert substituted | ||
| strings to numbers, booleans, or None; other scalar values pass through. | ||
|
|
||
| """ | ||
| normalized_config = normalize_env_references(config) | ||
| return replace_env_vars(normalized_config) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| """Fixtures shared by the unit and integration test suites.""" | ||
|
|
||
| import pytest | ||
|
|
||
|
|
||
| @pytest.fixture( | ||
| name="reference_prefix", params=["", "env."], ids=["short", "canonical"] | ||
| ) | ||
| def reference_prefix_fixture(request: pytest.FixtureRequest) -> str: | ||
| """Exercise both environment-reference spellings.""" | ||
| return str(request.param) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Resolve the BYOK
vector_db_idbefore the comparison too.The comment says new entries have resolved values. That premise is wrong in both callers:
synthesize_configuration(Line 1453) andgenerate_configuration(Line 1688) normalize their raw input but do not resolve it.brag["vector_db_id"]therefore reaches Line 333 as${env.STORE_ID}.existing_store_idsholds resolved values.Trigger: an operator writes
vector_db_id: ${STORE_ID}(either spelling), and an existingregistered_resources.vector_storesentry refers to the same store.Result: the membership check never matches, and a duplicate vector store registration is appended. If the existing entry is a literal ID, the result is the same.
Resolve both sides for the comparison. Keep the raw reference in the emitted entry.
🐛 Proposed fix
🤖 Prompt for AI Agents