-
Notifications
You must be signed in to change notification settings - Fork 6
feat(client): add ssl context cache and config #581
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: stable
Are you sure you want to change the base?
Conversation
WalkthroughBaseClient now loads TLS context early in init and passes ConfigBase.tls_context as the HTTP client's verification parameter (verify=self.config.tls_context) for both async and sync request paths. ConfigBase gained a private _ssl_context, a tls_context property that lazily creates or returns an ssl.SSLContext, and set_ssl_context(context) to set it; Config.clone propagates the SSL context. Tests were added covering TLS context caching, CA-file behavior, tls_insecure handling, and custom TLS context usage. The package public exports now include Config. Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (2)
📒 Files selected for processing (3)
🧰 Additional context used📓 Path-based instructions (5)**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
infrahub_sdk/client.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
tests/unit/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
tests/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
infrahub_sdk/config.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧬 Code graph analysis (2)infrahub_sdk/client.py (1)
tests/unit/sdk/test_client.py (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
🔇 Additional comments (14)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying infrahub-sdk-python with
|
| Latest commit: |
2623336
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://a7df3bfc.infrahub-sdk-python.pages.dev |
| Branch Preview URL: | https://fac-ssl-context.infrahub-sdk-python.pages.dev |
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## stable #581 +/- ##
==========================================
+ Coverage 75.69% 75.79% +0.10%
==========================================
Files 100 100
Lines 8894 8915 +21
Branches 1751 1758 +7
==========================================
+ Hits 6732 6757 +25
+ Misses 1680 1677 -3
+ Partials 482 481 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 1 file with indirect coverage changes 🚀 New features to boost your workflow:
|
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.
Actionable comments posted: 1
🧹 Nitpick comments (5)
tests/unit/sdk/test_client.py (3)
32-48: Extend test coverage to sync client.The test correctly validates default SSL context caching. However, both
InfrahubClient(async) andInfrahubClientSyncuse the same_get_verify_configmethod fromBaseClient, so consider adding a similar test for the sync client to ensure consistent behavior across both implementations.
51-73: Extend test coverage to sync client.The test correctly validates CA file-based SSL context caching and cache invalidation when the file path changes. Consider adding similar test coverage for
InfrahubClientSyncto ensure consistent behavior.
76-86: Add test coverage for custom SSL context and sync client.The test correctly validates that
tls_insecure=Truebypasses SSL context creation. However:
- Consider adding similar test coverage for
InfrahubClientSync.- No test validates that a custom
tls_ssl_contextprovided in the config is actually used by_get_verify_config.As per learnings: Use the custom pytest plugin (infrahub_sdk.pytest_plugin) fixtures for clients, configuration, and Infrahub-specific testing.
infrahub_sdk/client.py (2)
147-148: Consider thread safety for cache attributes.The cache attributes
_cached_ssl_contextand_cached_ssl_context_sourceare not thread-safe. While this is typically fine forInfrahubClient(async), theInfrahubClientSynccould be used in multi-threaded environments where concurrent access to these attributes might cause race conditions (e.g., one thread invalidating the cache while another is reading it).
156-177: Improve error handling for invalid CA files.The method correctly implements SSL context caching with appropriate priority (custom context → insecure → CA file → default). However, consider:
Error handling: If
tls_ca_filepoints to an invalid or non-existent file,create_default_context(cafile=...)will raise an exception. Consider wrapping this in a try-except to provide a clearer error message.Defensive checks: The RuntimeError checks on lines 168 and 176 seem unnecessary since
create_default_context()typically doesn't return None. If you want to keep them for defensive programming, clarify the rationale in a comment.Documentation: The priority order isn't documented in the Config class. Consider adding a note in the docstring of
tls_ssl_contextfield explaining when it takes precedence.Example error handling:
if self.config.tls_ca_file: if self._cached_ssl_context is None or self._cached_ssl_context_source != self.config.tls_ca_file: try: self._cached_ssl_context = create_default_context(cafile=self.config.tls_ca_file) self._cached_ssl_context_source = self.config.tls_ca_file except (FileNotFoundError, PermissionError) as exc: raise RuntimeError(f"Failed to load CA file '{self.config.tls_ca_file}': {exc}") from exc if self._cached_ssl_context is None: raise RuntimeError("Failed to create SSL context using provided CA file") return self._cached_ssl_context
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
infrahub_sdk/client.py(4 hunks)infrahub_sdk/config.py(2 hunks)tests/unit/sdk/test_client.py(2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
When implementing Infrahub checks, subclass InfrahubCheck and override validate(data); do not implement or rely on a check() method
Files:
infrahub_sdk/client.pyinfrahub_sdk/config.pytests/unit/sdk/test_client.py
infrahub_sdk/client.py
📄 CodeRabbit inference engine (CLAUDE.md)
infrahub_sdk/client.py: Use HTTPX for transport with proxy support (single proxy or HTTP/HTTPS mounts)
Support authentication via API tokens or JWT with automatic refresh
Files:
infrahub_sdk/client.py
infrahub_sdk/config.py
📄 CodeRabbit inference engine (CLAUDE.md)
infrahub_sdk/config.py: Use a Pydantic-based Config class with environment variable support for configuration
Environment variables for configuration must use the INFRAHUB_ prefix
Validate mutual exclusivity between INFRAHUB_PROXY and {INFRAHUB_PROXY_MOUNTS_HTTP, INFRAHUB_PROXY_MOUNTS_HTTPS}
Files:
infrahub_sdk/config.py
tests/unit/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Place and write unit tests under tests/unit/ (isolated component tests)
Files:
tests/unit/sdk/test_client.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use the custom pytest plugin (infrahub_sdk.pytest_plugin) fixtures for clients, configuration, and Infrahub-specific testing
Files:
tests/unit/sdk/test_client.py
🧠 Learnings (1)
📚 Learning: 2025-08-24T13:35:12.998Z
Learnt from: CR
PR: opsmill/infrahub-sdk-python#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-24T13:35:12.998Z
Learning: Applies to tests/**/*.py : Use the custom pytest plugin (infrahub_sdk.pytest_plugin) fixtures for clients, configuration, and Infrahub-specific testing
Applied to files:
tests/unit/sdk/test_client.py
🧬 Code graph analysis (2)
infrahub_sdk/client.py (1)
infrahub_sdk/context.py (1)
RequestContext(10-13)
tests/unit/sdk/test_client.py (2)
infrahub_sdk/client.py (16)
InfrahubClient(318-1607)get(390-406)get(409-425)get(428-444)get(447-463)get(466-482)get(485-501)get(503-561)get(2107-2123)get(2126-2142)get(2145-2161)get(2164-2180)get(2183-2199)get(2202-2218)get(2220-2278)_get_verify_config(156-177)tests/unit/sdk/conftest.py (1)
client(32-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: unit-tests (3.11)
- GitHub Check: unit-tests (3.12)
- GitHub Check: unit-tests (3.13)
- GitHub Check: unit-tests (3.10)
- GitHub Check: unit-tests (3.9)
- GitHub Check: integration-tests-latest-infrahub
- GitHub Check: Cloudflare Pages
🔇 Additional comments (5)
infrahub_sdk/config.py (1)
4-4: LGTM!The import is correctly added to support the new SSLContext type in the configuration.
tests/unit/sdk/test_client.py (1)
6-6: LGTM!The import correctly uses the public API by importing
Configfrominfrahub_sdk, aligning with the module's public exports.infrahub_sdk/client.py (3)
10-10: LGTM!The imports are correctly added to support SSL context creation and type annotations.
1053-1053: LGTM!The change correctly uses the centralized
_get_verify_config()method for TLS verification, providing consistent behavior and caching across all HTTP requests.
2777-2777: LGTM!The change correctly uses the centralized
_get_verify_config()method, ensuring the sync client has the same TLS verification and caching behavior as the async client.
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.
Added some suggestions. With this change I'd also modify it so that we try to load and cache the SSL config when we intialize the config so that an eventual error happens early and not when we first try to use the SDK.
ae4c8af to
4ff80bd
Compare
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.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
tests/unit/sdk/test_data/path-1.pemis excluded by!**/*.pemtests/unit/sdk/test_data/path-2.pemis excluded by!**/*.pem
📒 Files selected for processing (3)
infrahub_sdk/client.py(3 hunks)infrahub_sdk/config.py(4 hunks)tests/unit/sdk/test_client.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- infrahub_sdk/config.py
- infrahub_sdk/client.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
When implementing Infrahub checks, subclass InfrahubCheck and override validate(data); do not implement or rely on a check() method
Files:
tests/unit/sdk/test_client.py
tests/unit/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Place and write unit tests under tests/unit/ (isolated component tests)
Files:
tests/unit/sdk/test_client.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use the custom pytest plugin (infrahub_sdk.pytest_plugin) fixtures for clients, configuration, and Infrahub-specific testing
Files:
tests/unit/sdk/test_client.py
🧬 Code graph analysis (1)
tests/unit/sdk/test_client.py (2)
infrahub_sdk/config.py (2)
Config(164-206)tls_context(139-158)infrahub_sdk/client.py (14)
get(365-381)get(384-400)get(403-419)get(422-438)get(441-457)get(460-476)get(478-536)get(2082-2098)get(2101-2117)get(2120-2136)get(2139-2155)get(2158-2174)get(2177-2193)get(2195-2253)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: integration-tests-latest-infrahub
🔇 Additional comments (5)
tests/unit/sdk/test_client.py (5)
2-2: LGTM!The import additions are appropriate. Path is needed for the CURRENT_DIRECTORY constant, and Config is now part of the public API as intended by this PR.
Also applies to: 7-7
32-33: LGTM!Standard pattern for defining the test directory path.
83-94: LGTM!The test correctly verifies that when
tls_insecure=True, no SSL context is created andFalseis returned for httpx'sverifyparameter. The use ofAssertionErrorin the fake ensures the test fails if context creation is attempted.
35-52: Monkeypatch target is correctConfig imports
ssldirectly, so patchingssl.create_default_contextworks as intended and the caching test requires no changes.
54-81: Clarify TLS context caching behavior when updating tls_ca_file. The test confirms that changingtls_ca_fileafter first access does not recreate the SSL context, matching the current implementation. If this is intended, document the behavior; otherwise, clear the cached context when the CA file is updated.
4ff80bd to
9f288e7
Compare
This allows configuring a custom SSL context or cache a default one if not supplied instead of re-creating a default ssl context on each request (default httpx behavior). Signed-off-by: Fatih Acar <[email protected]>
9f288e7 to
2623336
Compare
|
@ogenstad thank you for the relevant suggestions, it's much better now. |
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.
LGTM, I added a suggestion along with a question about when we're returning False for tls_insecure.
Also can you please add a newsfragment?
One change in behaviour could be that when we move to creating the SSL context like we do that the behaviour gets slightly modified with regards to situations when we use the CA file as an environment variable.
Within Infrahub we have this code, it's quite hacky but you see that the parameters to the SSL context is different if we're loading a file cafile vs when we are loading a string from an environment variable cadata.
tls_ca_path = Path(self.tls_ca_bundle)
try:
possibly_file = tls_ca_path.exists()
except OSError:
# Raised if the filename is too long which can indicate
# that the value is a PEM certificate in string form.
possibly_file = False
if possibly_file and tls_ca_path.is_file():
context = ssl.create_default_context(cafile=str(tls_ca_path))
else:
context = ssl.create_default_context()
context.load_verify_locations(cadata=self.tls_ca_bundle)It might be that HTTPX manages this part differently internally (or it was never tested).
Can you please verify with your branch the behaviour if you do a:
export INFRAHUB_TLS_CA_FILE=PEM-CERT-AS-STRINGI thought we had tests for that in the backend but now see that we don't seem to have that :(
| return self._ssl_context | ||
|
|
||
| if self.tls_insecure: | ||
| return False |
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.
I thought you mentioned that there was some processing needed even if we just used HTTP to create the context each time? Is it that this doesn't happen if we use HTTP together with tls_insecure=True?
| self.group_context: InfrahubGroupContext | InfrahubGroupContextSync | ||
| self._initialize() | ||
| self._request_context: RequestContext | None = None | ||
| _ = self.config.tls_context # Early load of the TLS context to catch errors |
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.
I would prefer if this was cached by the config during the creation of the config object and we populated config.__ssl_context at that time. However if we do that I'm not sure that it it would be possible to use the .set_ssl_context() method within the Config.clone, or rather it would be possible but at that time we'd already have created a new SSL Context within the clone method. So I think this might be the best option for now. I guess the only thing to remember is that we'd need to initialize a Client with a config in order to populate the config for reuse elsewhere but I think we always do that.
In order to ensure this stays in place I think we'll want an additional unittest below where you also clone the config and verify that the context is the same object in both of the configs.
This allows configuring a custom SSL context or cache a default one if not supplied instead of re-creating a default ssl context on each request (default httpx behavior).
Summary by CodeRabbit
New Features
Refactor
Bug Fixes