Skip to content

Azure: Add default timeout of 5s for all requests#188

Merged
JAVGan merged 2 commits intomainfrom
azure_timeout
Apr 8, 2026
Merged

Azure: Add default timeout of 5s for all requests#188
JAVGan merged 2 commits intomainfrom
azure_timeout

Conversation

@JAVGan
Copy link
Copy Markdown
Collaborator

@JAVGan JAVGan commented Apr 6, 2026

This commit changes the PartnerPortalSession to always request a connection using a default timeout of 5 seconds.

The default timeout is defined via AZURE_SESSION_TIMEOUT which can be overridden by an envvar with the same name.

With the timeout in place it prevents issues of hanging connections whenever the server doesn't properly respond.

Refers to SPSTRAT-708

Summary by Sourcery

Introduce a configurable default timeout for Azure partner portal HTTP requests to avoid hanging connections.

Enhancements:

  • Add a module-level AZURE_SESSION_TIMEOUT constant, configurable via the AZURE_SESSION_TIMEOUT environment variable, and use it as the default timeout for Azure session login and generic requests.

Tests:

  • Update Azure session tests to assert that all HTTP requests use AZURE_SESSION_TIMEOUT as their timeout parameter.

This commit changes the `PartnerPortalSession` to always request a
connection using a default timeout of 5 seconds.

The default timeout is defined via `AZURE_SESSION_TIMEOUT` which can be
overridden by an envvar with the same name.

With the timeout in place it prevents issues of hanging connections
whenever the server doesn't properly respond.

Refers to SPSTRAT-708

Signed-off-by: Jonathan Gangi <jgangi@redhat.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 6, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adds a configurable default 5-second timeout for all Azure PartnerPortalSession HTTP requests, replacing hardcoded timeouts and ensuring all login and generic requests respect the shared AZURE_SESSION_TIMEOUT constant.

Sequence diagram for PartnerPortalSession request with default timeout

sequenceDiagram
    actor Caller
    participant PartnerPortalSession
    participant requests_Session as requests_Session

    Caller->>PartnerPortalSession: _request(method, path, params, headers)
    PartnerPortalSession->>PartnerPortalSession: timeout = AZURE_SESSION_TIMEOUT
    PartnerPortalSession->>requests_Session: request(method, url, params, headers, timeout)
    requests_Session-->>PartnerPortalSession: Response
    PartnerPortalSession-->>Caller: Response
Loading

Sequence diagram for PartnerPortalSession login with shared timeout

sequenceDiagram
    actor Caller
    participant PartnerPortalSession
    participant requests_Session as requests_Session

    Caller->>PartnerPortalSession: _login()
    PartnerPortalSession->>requests_Session: post(url, headers, data, timeout=AZURE_SESSION_TIMEOUT)
    requests_Session-->>PartnerPortalSession: Response
    PartnerPortalSession->>PartnerPortalSession: AccessToken(Response.json())
    PartnerPortalSession-->>Caller: AccessToken
Loading

Class diagram for updated Azure PartnerPortalSession timeout handling

classDiagram
    class SessionModule {
        +float AZURE_SESSION_TIMEOUT
    }

    class PartnerPortalSession {
        -session requests_Session
        -_prefix_url str
        -auth_keys dict
        +_login() AccessToken
        +_request(method str, path str, params dict, headers dict, kwargs dict) requests_Response
        +get(path str, kwargs dict) requests_Response
        +post(path str, kwargs dict) requests_Response
    }

    class AccessToken {
        -token_data dict
    }

    SessionModule <.. PartnerPortalSession : uses
    PartnerPortalSession o-- AccessToken
    PartnerPortalSession o-- requests_Session

    class requests_Session {
        +post(url str, headers dict, data dict, timeout float) requests_Response
        +request(method str, url str, params dict, headers dict, timeout float, kwargs dict) requests_Response
    }

    class requests_Response {
        +raise_for_status() void
        +json() dict
    }
Loading

File-Level Changes

Change Details Files
Introduce a global, configurable Azure session timeout and apply it to login requests.
  • Define AZURE_SESSION_TIMEOUT as a module-level float, sourced from the AZURE_SESSION_TIMEOUT environment variable with a default of 5.0 seconds.
  • Update the PartnerPortalSession._login method to use AZURE_SESSION_TIMEOUT instead of the previously hardcoded 30-second timeout when performing the token POST request.
cloudpub/ms_azure/session.py
tests/ms_azure/test_session.py
Ensure all PartnerPortalSession HTTP requests use a timeout by default while still allowing per-call overrides.
  • Modify PartnerPortalSession._request to pop an optional timeout kwarg, defaulting to AZURE_SESSION_TIMEOUT when not provided.
  • Pass the resolved timeout explicitly into self.session.request alongside url, params, headers, and any remaining kwargs.
  • Update tests to expect AZURE_SESSION_TIMEOUT in login and request calls, for both JSON and non-JSON request paths.
cloudpub/ms_azure/session.py
tests/ms_azure/test_session.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • Parsing AZURE_SESSION_TIMEOUT with float(os.environ.get(...)) will raise a ValueError if the env var is set to a non-numeric value; consider handling invalid input (e.g., with a try/except and falling back to the default) to avoid import-time failures.
  • Because AZURE_SESSION_TIMEOUT is evaluated at import time, changing the environment variable after startup has no effect; if you expect runtime configurability, consider moving the env lookup into session initialization or a small helper function that is called when creating a PartnerPortalSession.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Parsing `AZURE_SESSION_TIMEOUT` with `float(os.environ.get(...))` will raise a `ValueError` if the env var is set to a non-numeric value; consider handling invalid input (e.g., with a try/except and falling back to the default) to avoid import-time failures.
- Because `AZURE_SESSION_TIMEOUT` is evaluated at import time, changing the environment variable after startup has no effect; if you expect runtime configurability, consider moving the env lookup into session initialization or a small helper function that is called when creating a `PartnerPortalSession`.

## Individual Comments

### Comment 1
<location path="cloudpub/ms_azure/session.py" line_range="14" />
<code_context>

 log = logging.getLogger(__name__)

+AZURE_SESSION_TIMEOUT: float = float(os.environ.get("AZURE_SESSION_TIMEOUT", 5.0))
+

</code_context>
<issue_to_address>
**issue (bug_risk):** Parsing the timeout env var at import time can raise on invalid values and crash the module import.

If `AZURE_SESSION_TIMEOUT` is non-numeric, `float(...)` will raise `ValueError` at import and can crash the process. Please parse this defensively (e.g., `try/except` with a sane default) so bad config values don’t prevent startup or at least fail in a controlled way.
</issue_to_address>

### Comment 2
<location path="tests/ms_azure/test_session.py" line_range="75" />
<code_context>
         session_mock.return_value.request.assert_called_once()
         session_mock.return_value.post.assert_called_once_with(
-            login_url, headers=login_header, data=login_data, timeout=30
+            login_url, headers=login_header, data=login_data, timeout=AZURE_SESSION_TIMEOUT
         )

</code_context>
<issue_to_address>
**suggestion (testing):** Add tests to verify the default timeout value and env-var override for `AZURE_SESSION_TIMEOUT`.

This assertion now checks that `AZURE_SESSION_TIMEOUT` is passed, but there’s no test confirming its behavior. Please add tests that (1) verify the default timeout is 5.0 when the env var is unset, and (2) override `AZURE_SESSION_TIMEOUT` via `monkeypatch`/`os.environ` and confirm the login request uses that value. This will more robustly cover the timeout configuration and prevent regressions.

Suggested implementation:

```python
import importlib

import pytest

from cloudpub.ms_azure.session import AZURE_SESSION_TIMEOUT, AccessToken, PartnerPortalSession
from cloudpub.utils import join_url

```

```python
        session_mock.return_value.request.assert_called_once()
        session_mock.return_value.post.assert_called_once_with(
            login_url, headers=login_header, data=login_data, timeout=AZURE_SESSION_TIMEOUT
        )


def test_azure_session_timeout_default(monkeypatch):
    """Default timeout should be 5.0s when AZURE_SESSION_TIMEOUT is not set."""
    # Ensure the env var is not set before reloading the module
    monkeypatch.delenv("AZURE_SESSION_TIMEOUT", raising=False)

    import cloudpub.ms_azure.session as session_module

    session_module = importlib.reload(session_module)
    assert session_module.AZURE_SESSION_TIMEOUT == 5.0


def test_azure_session_timeout_env_override_is_used_in_login(
    monkeypatch,
    session_mock,
    login_url,
    login_header,
    login_data,
):
    """AZURE_SESSION_TIMEOUT env var should override the default and be used for the login call."""
    monkeypatch.setenv("AZURE_SESSION_TIMEOUT", "12.5")

    import cloudpub.ms_azure.session as session_module

    session_module = importlib.reload(session_module)

    # Re-create PartnerPortalSession from the reloaded module so it uses the new timeout value.
    partner_portal_session = session_module.PartnerPortalSession()

    # Trigger the login flow; adjust arguments to match the real method signature.
    partner_portal_session.login(login_url, login_header, login_data)

    # The login request should use the overridden timeout value from the module.
    session_mock.return_value.post.assert_called_once_with(
        login_url,
        headers=login_header,
        data=login_data,
        timeout=session_module.AZURE_SESSION_TIMEOUT,
    )

```

These edits assume a few details that you may need to align with the existing test suite:

1. **`PartnerPortalSession.login` signature**  
   - Update the `partner_portal_session.login(...)` call to match the actual method signature.  
   - If the login flow is invoked differently (e.g. `partner_portal_session.get_access_token(...)` or similar), call that instead.

2. **`session_mock` fixture wiring**  
   - The new test assumes `session_mock` is already correctly patching the underlying HTTP session used by `PartnerPortalSession` (e.g. `requests.Session` in `cloudpub.ms_azure.session`).  
   - If `PartnerPortalSession` in the reloaded module isn’t using the existing `session_mock` fixture, ensure your fixture patches the correct symbol in `cloudpub.ms_azure.session` and that the patch still applies after `importlib.reload`.

3. **Existing fixtures (`login_url`, `login_header`, `login_data`)**  
   - The new test reuses these fixtures; if they are named differently or constructed via a helper, adjust the parameter list and call site accordingly.

With those adjustments, the two tests will (1) validate the default timeout of `5.0` when the env var is unset and (2) confirm that overriding `AZURE_SESSION_TIMEOUT` via the environment is honored by the login HTTP request.
</issue_to_address>

### Comment 3
<location path="tests/ms_azure/test_session.py" line_range="113-118" />
<code_context>
             getattr(session, method)(path, json)
             mock_session.return_value.request.assert_called_once_with(
-                method, url=url, params=put_param, headers=put_headers, json={"foo": "bar"}
+                method,
+                url=url,
+                params=put_param,
+                headers=put_headers,
+                json={"foo": "bar"},
+                timeout=AZURE_SESSION_TIMEOUT,
             )
         else:
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test case to ensure an explicitly provided timeout overrides `AZURE_SESSION_TIMEOUT` in `_request`.

Since `_request` now accepts an explicit `timeout` via `**kwargs`, please add coverage for the override behavior—for example, calling `session.put(path, json, timeout=10)` and asserting that `request` is invoked with `timeout=10` instead of `AZURE_SESSION_TIMEOUT` to verify precedence and avoid regressions.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread cloudpub/ms_azure/session.py
Comment thread tests/ms_azure/test_session.py
Comment thread tests/ms_azure/test_session.py
- Patches `AZURE_SESSION_TIMEOUT` in the environment, `importlib.reload`s `cloudpub.ms_azure.session` (because `AZURE_SESSION_TIMEOUT` is set at import time in `14:14:cloudpub/ms_azure/session.py`).
- Asserts the reloaded module’s constant and checks that both `_login`’s post and the API request use that timeout.
- Uses `try / finally` with a final reload so the module is reset even when a check fails, and so later tests see the normal timeout again.

- Parametrizes `get`, `post`, and `put` with `timeout=88.0`.
- Asserts `session.request` is called with `timeout=88.0`, matching `_request`’s `kwargs.pop("timeout", AZURE_SESSION_TIMEOUT)` in `165:168:cloudpub/ms_azure/session.py`.
- Imports added: `importlib`, `os`.

Refers to SPSTRAT-708

Signed-off-by: Jonathan Gangi <jgangi@redhat.com>

Assisted-by: Cursor/Gemini
Copy link
Copy Markdown
Collaborator

@lslebodn lslebodn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW based on logs in RHELDST-34940. We hit timeout mostly in Sending a get request to ....

Maybe we should have automatic retry for get/put requests similar as in starmap-client. I am not sure about automatic retry for Post requests. But that can be done in different MR.

Comment thread cloudpub/ms_azure/session.py
Copy link
Copy Markdown
Collaborator

@lslebodn lslebodn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@JAVGan JAVGan merged commit 8be3534 into main Apr 8, 2026
22 checks passed
@JAVGan JAVGan deleted the azure_timeout branch April 8, 2026 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants