-
Notifications
You must be signed in to change notification settings - Fork 38
feat: extensibility ums destination changes #260
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
Open
hariturangi
wants to merge
3
commits into
SAP:main
Choose a base branch
from
hariturangi:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+103
−58
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,6 @@ | |
| from typing import TYPE_CHECKING, Any, Dict, List, Optional | ||
|
|
||
| import httpx | ||
|
|
||
| from sap_cloud_sdk.core.telemetry import Module | ||
| from sap_cloud_sdk.destination import ConsumptionLevel | ||
| from sap_cloud_sdk.destination import create_client as create_destination_client | ||
|
|
@@ -41,6 +40,7 @@ | |
| OnFailure, | ||
| ) | ||
| from sap_cloud_sdk.extensibility.exceptions import TransportError | ||
| from sap_cloud_sdk.destination import ConsumptionOptions | ||
|
|
||
| if TYPE_CHECKING: | ||
| from sap_cloud_sdk.extensibility.config import ExtensibilityConfig | ||
|
|
@@ -53,7 +53,8 @@ | |
|
|
||
| ENV_CONHOS_LANDSCAPE = "APPFND_CONHOS_LANDSCAPE" | ||
| ENV_UMS_DESTINATION_NAME = "APPFND_UMS_DESTINATION_NAME" | ||
| _UMS_DESTINATION_PREFIX = "sap-managed-runtime-ums-" | ||
| ENV_UMS_URL = "APPFND_CONHOS_UMS_URL" | ||
| _IAS_DESTINATION_PREFIX = "sap-managed-runtime-ias-" | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # GraphQL query | ||
|
|
@@ -182,24 +183,25 @@ def _parse_method_safe(value: str) -> HTTPMethod: | |
|
|
||
|
|
||
| def _ums_destination_name(config_override: Optional[str] = None) -> Optional[str]: | ||
| """Construct the UMS destination name from configuration or environment. | ||
| """Construct the IAS destination name from configuration or environment. | ||
|
|
||
| Resolution order: | ||
|
|
||
| 1. **Config override** -- if ``config.destination_name`` is set, use | ||
| it directly. | ||
| 2. **Explicit env var override** -- if ``APPFND_UMS_DESTINATION_NAME`` | ||
| is set, use its value directly. This is useful in subaccounts | ||
| where the UMS destination follows a non-standard naming convention. | ||
| 3. **Landscape-based construction** -- the destination name is built as | ||
| ``sap-managed-runtime-ums-{APPFND_CONHOS_LANDSCAPE}``. | ||
| is set, use its value directly. | ||
| 3. **Landscape-based construction** -- built as | ||
| ``sap-managed-runtime-ias-{APPFND_CONHOS_LANDSCAPE}``. | ||
| ``APPFND_CONHOS_UMS_URL`` must be set; a warning is logged and | ||
| ``None`` is returned if it is absent. | ||
|
|
||
| Args: | ||
| config_override: Optional destination name from | ||
| :class:`ExtensibilityConfig`. Takes highest priority when set. | ||
|
|
||
| Returns: | ||
| The resolved UMS destination name, or ``None`` if no configuration | ||
| The resolved destination name, or ``None`` if no configuration | ||
| or environment variables are available to determine it. | ||
| """ | ||
| # 0. Config-level override takes highest priority | ||
|
|
@@ -232,9 +234,18 @@ def _ums_destination_name(config_override: Optional[str] = None) -> Optional[str | |
| ) | ||
| return None | ||
|
|
||
| destination_name = f"{_UMS_DESTINATION_PREFIX}{landscape}" | ||
| if not os.environ.get(ENV_UMS_URL): | ||
| logger.warning( | ||
| "%s is not set; cannot construct IAS destination name. " | ||
| "Set %s or %s to configure the destination name.", | ||
| ENV_UMS_URL, | ||
| ENV_UMS_DESTINATION_NAME, | ||
| ENV_UMS_URL, | ||
| ) | ||
| return None | ||
| destination_name = f"{_IAS_DESTINATION_PREFIX}{landscape}" | ||
| logger.debug( | ||
| "Resolved UMS destination name from %s: %s", | ||
| "Resolved IAS destination name from %s: %s", | ||
| ENV_CONHOS_LANDSCAPE, | ||
| destination_name, | ||
| ) | ||
|
|
@@ -428,17 +439,27 @@ def _transform_ums_response( | |
| class UmsTransport: | ||
| """UMS GraphQL transport for the extensibility service. | ||
|
|
||
| Resolves the UMS destination via the Destination SDK, then sends | ||
| a GraphQL query to the UMS ``/graphql`` endpoint and transforms | ||
| the response into an :class:`ExtensionCapabilityImplementation`. | ||
| Resolves the UMS destination, then sends a GraphQL query to the UMS | ||
| ``/graphql`` endpoint and transforms the response into an | ||
| :class:`ExtensionCapabilityImplementation`. | ||
|
|
||
| The destination name is resolved in order: | ||
| **Destination name** is resolved in order: | ||
|
|
||
| 1. ``config.destination_name`` (explicit config override). | ||
| 2. ``APPFND_UMS_DESTINATION_NAME`` environment variable. | ||
| 3. ``sap-managed-runtime-ums-{APPFND_CONHOS_LANDSCAPE}`` (constructed). | ||
| 3. Landscape-based construction: | ||
|
|
||
| * ``sap-managed-runtime-ias-{APPFND_CONHOS_LANDSCAPE}`` (requires | ||
| ``APPFND_CONHOS_UMS_URL`` to be set; logs a warning and returns | ||
| ``None`` otherwise). | ||
|
|
||
| If none of the above are available, resolution fails with a warning. | ||
| **Base URL** is resolved as: | ||
|
|
||
| * ``APPFND_CONHOS_UMS_URL`` environment variable when set (new flow). | ||
| * The URL configured on the resolved destination otherwise (legacy flow). | ||
|
|
||
| In both cases the **mTLS certificate** is taken from the resolved | ||
| destination. | ||
|
|
||
| Args: | ||
| agent_ord_id: ORD ID of the agent. | ||
|
|
@@ -502,15 +523,6 @@ def get_extension_capability_implementation( | |
| TransportError: If destination resolution, HTTP communication, | ||
| or response parsing fails. | ||
| """ | ||
| # Guard: destination name must be resolved | ||
| if self._destination_name is None: | ||
| raise TransportError( | ||
| "UMS destination name could not be resolved. " | ||
| "Set the APPFND_UMS_DESTINATION_NAME or " | ||
| "APPFND_CONHOS_LANDSCAPE environment variable, or provide " | ||
| "a destination_name in ExtensibilityConfig." | ||
| ) | ||
|
|
||
| # 0. Cache lookup ------------------------------------------------ | ||
| cache_key = (tenant, capability_id) | ||
| all_edges: List[Dict[str, Any]] = [] | ||
|
|
@@ -541,10 +553,20 @@ def get_extension_capability_implementation( | |
| ) | ||
|
|
||
| # 1. Resolve destination ----------------------------------------- | ||
| if self._destination_name is None: | ||
| raise TransportError( | ||
| "UMS destination name could not be resolved. " | ||
| "Either set APPFND_UMS_DESTINATION_NAME directly, " | ||
| "or set both APPFND_CONHOS_LANDSCAPE and APPFND_CONHOS_UMS_URL " | ||
| "to construct the IAS destination name, " | ||
| "or provide a destination_name in ExtensibilityConfig." | ||
| ) | ||
|
|
||
| try: | ||
| dest = self._dest_client.get_destination( | ||
| self._destination_name, | ||
| level=ConsumptionLevel.PROVIDER_SUBACCOUNT, | ||
| options=ConsumptionOptions(skip_token_retrieval=True) | ||
| ) | ||
| except Exception as exc: | ||
| raise TransportError( | ||
|
|
@@ -556,13 +578,28 @@ def get_extension_capability_implementation( | |
| f"Destination '{self._destination_name}' not found in Destination Service." | ||
| ) | ||
|
|
||
| base_url = dest.url | ||
| if base_url is None: | ||
| raise TransportError( | ||
| f"Destination '{self._destination_name}' has no URL configured." | ||
| # 2. Resolve base URL -------------------------------------------- | ||
| # New flow: use APPFND_CONHOS_UMS_URL env var directly. | ||
| # Legacy flow: use the URL from the destination itself. | ||
| ums_url_override = os.environ.get(ENV_UMS_URL) | ||
| if ums_url_override: | ||
| base_url = ums_url_override | ||
| logger.debug( | ||
| "Using UMS URL from %s: %s", ENV_UMS_URL, base_url | ||
| ) | ||
| else: | ||
| base_url = dest.url | ||
|
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. URL will never be on destination |
||
| if base_url is None: | ||
| raise TransportError( | ||
| f"Destination '{self._destination_name}' has no URL configured." | ||
| ) | ||
| logger.debug( | ||
| "Using UMS URL from destination '%s': %s", | ||
| self._destination_name, | ||
| base_url, | ||
| ) | ||
|
|
||
| # 2. Extract client certificate ---------------------------------- | ||
| # 3. Extract client certificate ---------------------------------- | ||
| if not dest.certificates: | ||
| raise TransportError( | ||
| f"Destination '{self._destination_name}' has no " | ||
|
|
@@ -578,7 +615,7 @@ def get_extension_capability_implementation( | |
| f"Failed to decode client certificate '{cert.name}': {exc}" | ||
| ) from exc | ||
|
|
||
| # 3. Build GraphQL request -------------------------------------- | ||
| # 4. Build GraphQL request -------------------------------------- | ||
| url = f"{base_url.rstrip('/')}{_UMS_GRAPHQL_PATH}" | ||
|
|
||
| agent_filter: dict[str, Any] = { | ||
|
|
@@ -599,7 +636,7 @@ def get_extension_capability_implementation( | |
| "X-Tenant": tenant, | ||
| } | ||
|
|
||
| # 4. Send paginated requests with mTLS -------------------------- | ||
| # 5. Send paginated requests with mTLS -------------------------- | ||
| all_edges = [] | ||
| cursor: Optional[str] = None | ||
| try: | ||
|
|
@@ -626,7 +663,7 @@ def get_extension_capability_implementation( | |
| headers=request_headers, | ||
| ) | ||
|
|
||
| # 5. Parse response --------------------------------- | ||
| # 6. Parse response --------------------------------- | ||
| try: | ||
| response.raise_for_status() | ||
| except httpx.HTTPStatusError as exc: | ||
|
|
@@ -674,7 +711,7 @@ def get_extension_capability_implementation( | |
| except Exception as exc: | ||
| raise TransportError(f"HTTP request to UMS endpoint failed: {exc}") from exc | ||
|
|
||
| # 6. Populate cache ---------------------------------------------- | ||
| # 7. Populate cache ---------------------------------------------- | ||
| now = time.monotonic() | ||
|
|
||
| with self._cache_lock: | ||
|
|
@@ -693,10 +730,10 @@ def get_extension_capability_implementation( | |
|
|
||
| self._cache[cache_key] = (now, all_edges) | ||
|
|
||
| # 7. Transform ----------------------------------------------------------- | ||
| # 8. Transform ----------------------------------------------------------- | ||
| combined_data: Dict[str, Any] = { | ||
| "EXTHUB__ExtCapImplementationInstances": {"edges": all_edges}, | ||
| } | ||
| result = _transform_ums_response(combined_data, capability_id) | ||
|
|
||
| return result | ||
| return result | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Do we still need this variable? As far as I know this is not being set