-
Notifications
You must be signed in to change notification settings - Fork 97
LCORE-1826: Expose OpenTelemetry configuration in v1/config endpoint #2273
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
Changes from all commits
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 |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| """Integration tests for the /config endpoint.""" | ||
|
|
||
| import os | ||
| from typing import cast | ||
|
|
||
| import pytest | ||
|
|
@@ -88,3 +89,79 @@ async def test_config_endpoint_fails_without_configuration( | |
| assert "response" in exc_info.value.detail | ||
| detail = cast(dict[str, str], exc_info.value.detail) | ||
| assert "configuration is not loaded" in detail["response"].lower() | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_config_endpoint_includes_observability( | ||
| current_config: AppConfig, # pylint: disable=unused-argument | ||
| test_request: Request, | ||
| test_auth: AuthTuple, | ||
| ) -> None: | ||
| """Test that config endpoint includes observability configuration. | ||
|
|
||
| This integration test verifies: | ||
| - Observability field is present in the configuration response | ||
| - OTEL environment variables are correctly collected | ||
| - Response structure includes observability.otel block | ||
|
|
||
| Parameters: | ||
| ---------- | ||
| current_config (AppConfig): Loads root configuration | ||
| test_request (Request): FastAPI request | ||
| test_auth (AuthTuple): noop authentication tuple | ||
| """ | ||
| response = await config_endpoint_handler(auth=test_auth, request=test_request) | ||
|
|
||
| # Verify observability field exists | ||
| assert hasattr(response.configuration, "observability") | ||
| assert response.configuration.observability is not None | ||
|
|
||
| # Verify otel field exists | ||
| assert hasattr(response.configuration.observability, "otel") | ||
| assert isinstance(response.configuration.observability.otel, dict) | ||
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_config_endpoint_observability_collects_otel_vars( | ||
| current_config: AppConfig, # pylint: disable=unused-argument | ||
| test_request: Request, # pylint: disable=unused-argument | ||
| test_auth: AuthTuple, # pylint: disable=unused-argument | ||
| monkeypatch: pytest.MonkeyPatch, | ||
| ) -> None: | ||
| """Test that observability config collects OTEL_* environment variables. | ||
|
|
||
| This integration test verifies: | ||
| - OTEL_* environment variables are collected into observability.otel | ||
| - Non-OTEL variables are not included | ||
| - Environment variables set at runtime are reflected in config | ||
|
|
||
| Parameters: | ||
| ---------- | ||
| current_config (AppConfig): Loads root configuration | ||
| test_request (Request): FastAPI request | ||
| test_auth (AuthTuple): noop authentication tuple | ||
| monkeypatch (pytest.MonkeyPatch): Fixture to modify environment variables | ||
| """ | ||
| # pylint: disable=import-outside-toplevel | ||
| from models.config import ObservabilityConfiguration | ||
|
|
||
| # Set OTEL environment variables | ||
| monkeypatch.setenv("OTEL_SDK_DISABLED", "true") | ||
| monkeypatch.setenv("OTEL_SERVICE_NAME", "test-service") | ||
| monkeypatch.setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317") | ||
|
|
||
| # Reload configuration to pick up new env vars | ||
| updated_observability = ObservabilityConfiguration.from_environment() | ||
|
|
||
| # Verify OTEL vars are present in the environment | ||
| # (Note: The config is loaded at startup, so these may not be in the response | ||
| # unless we reload the entire config. This test verifies the mechanism works.) | ||
| assert "OTEL_SDK_DISABLED" in os.environ | ||
| assert "OTEL_SERVICE_NAME" in os.environ | ||
| assert "OTEL_EXPORTER_OTLP_ENDPOINT" in os.environ | ||
|
|
||
| # Verify that when we call from_environment(), it picks up the vars | ||
| assert "OTEL_SDK_DISABLED" in updated_observability.otel | ||
| assert updated_observability.otel["OTEL_SDK_DISABLED"] == "true" | ||
| assert "OTEL_SERVICE_NAME" in updated_observability.otel | ||
| assert updated_observability.otel["OTEL_SERVICE_NAME"] == "test-service" | ||
|
Comment on lines
+125
to
+167
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. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Assert the mutated values through the endpoint. This test never invokes 🤖 Prompt for AI Agents
Comment on lines
+133
to
+167
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. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Assert the complete environment contract. Add an assertion that 🤖 Prompt for AI Agents
Comment on lines
+148
to
+167
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. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Exclude credential-bearing OTEL variables from the response contract.
As per coding guidelines, flag sensitive data leaked in API responses. 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,10 @@ | |
| "max_content_length": constants.SAVED_PROMPTS_DEFAULT_MAX_CONTENT_LENGTH, | ||
| } | ||
|
|
||
| _DEFAULT_OBSERVABILITY_DUMP: dict[str, dict[str, str]] = { | ||
| "otel": {}, | ||
| } | ||
|
|
||
|
Comment on lines
+47
to
+50
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. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Isolate OTEL environment state in dump tests. Because 🤖 Prompt for AI Agents |
||
| _MCP_SERVER_DUMP_DEFAULTS: dict[str, Any] = { | ||
| "authorization_headers": {}, | ||
| "headers": [], | ||
|
|
@@ -232,6 +236,7 @@ def test_dump_configuration_minimal_cfg(tmp_path: Path) -> None: | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
@@ -459,6 +464,7 @@ def test_dump_configuration_valid_values(tmp_path: Path) -> None: | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
@@ -837,6 +843,7 @@ def test_dump_configuration_with_quota_limiters(tmp_path: Path) -> None: | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
@@ -1099,6 +1106,7 @@ def test_dump_configuration_with_quota_limiters_different_values( | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
@@ -1394,6 +1402,7 @@ def test_dump_configuration_byok(tmp_path: Path) -> None: | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
@@ -1616,6 +1625,7 @@ def test_dump_configuration_pg_namespace(tmp_path: Path) -> None: | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
@@ -1998,6 +2008,7 @@ def test_dump_configuration_allow_degraded_mode(tmp_path: Path) -> None: | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
@@ -2226,6 +2237,7 @@ def test_dump_configuration_max_retries_settings(tmp_path: Path) -> None: | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
@@ -2454,6 +2466,7 @@ def test_dump_configuration_retry_count_settings(tmp_path: Path) -> None: | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
@@ -2689,6 +2702,7 @@ def test_dump_configuration_specific_compaction_values(tmp_path: Path) -> None: | |
| "quota_subject": None, | ||
| }, | ||
| "splunk": None, | ||
| "observability": _DEFAULT_OBSERVABILITY_DUMP, | ||
| "deployment_environment": "development", | ||
| "reranker": { | ||
| "enabled": False, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.