-
Notifications
You must be signed in to change notification settings - Fork 851
Fix autest compatibility with Fedora 43 / Python 3.14 #12857
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: master
Are you sure you want to change the base?
Conversation
This commit addresses multiple compatibility issues that cause autests to fail on modern systems like Fedora 43 with Python 3.14 and OpenSSL 3.x: Python 3.14 compatibility: - Fix kwargs.iteritems() -> kwargs.items() in microserver.test.ext - Add missing 'os' import in microserver.test.ext and conditions.test.ext - Add explicit UTF-8 encoding to socket decode() calls - Replace deprecated socket.error with OSError in ports.py - Add socket timeout to prevent recv() from hanging indefinitely TLS protocol deprecation: - Add HasLegacyTLSSupport() condition to detect TLSv1.0/TLSv1.1 support - Skip tls_client_versions tests on systems without legacy TLS support - Modern OpenSSL 3.x disables TLSv1.0/TLSv1.1 by default Timing improvements: - Increase sleep duration in polite_hook_wait.cc from 200ms to 500ms to account for variable scheduling latency on different systems
On Fedora/RHEL systems with crypto-policies, TLSv1.0/TLSv1.1 may be disabled at the system level even though OpenSSL accepts the -tls1 flag. The previous check would incorrectly report TLSv1 as supported because 'openssl ciphers -v -tls1' returns ciphers (TLSv1.2/TLSv1.3) even when TLSv1.0 is disabled. This fix improves detection by parsing the cipher list output and checking if any cipher is specifically marked as TLSv1 or SSLv3 in the version column, rather than just checking if the command succeeds.
The previous check looked for TLSv1 ciphers in 'openssl ciphers' output,
but on Fedora with crypto-policies, TLSv1 ciphers are still listed even
though the protocol is disabled at runtime ("no protocols available").
This fix tests actual TLSv1 protocol availability by attempting an
s_client connection and checking for the "no protocols available" error
that indicates the protocol is blocked by system crypto-policy.
Testing TLSv1 against localhost:1 fails at TCP layer before reaching TLS, so "no protocols available" error is never shown. Need to connect to a real HTTPS server to properly detect crypto-policy restrictions.
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.
Pull request overview
This PR updates the autest gold-test infrastructure to better tolerate modern platforms (Fedora 43 / Python 3.14 / OpenSSL 3.x), primarily by addressing Python API deprecations, preventing socket hangs, and conditionally skipping legacy-TLS-dependent tests.
Changes:
- Add a new
HasLegacyTLSSupport()condition and use it to gate TLSv1.0/TLSv1.1 client-version tests. - Improve Python 3.14 compatibility in autest extensions (e.g.,
iteritems()removal, missing imports) and harden microserver socket reads (timeout + explicit UTF-8 decode). - Reduce timing flakiness in the polite hook wait plugin test by increasing the sleep delay.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/gold_tests/tls/tls_client_versions.test.py | Skip legacy-TLS tests when legacy TLS is unavailable. |
| tests/gold_tests/tls/tls_client_versions_minmax.test.py | Skip legacy-TLS tests when legacy TLS is unavailable. |
| tests/gold_tests/autest-site/conditions.test.ext | Add HasLegacyTLSSupport() condition for runtime legacy TLS detection. |
| tests/gold_tests/autest-site/microserver.test.ext | Python 3.14 fixes; add socket recv timeout and explicit UTF-8 decode. |
| tests/gold_tests/autest-site/ports.py | Update exception type from deprecated socket.error to OSError. |
| tests/gold_tests/pluginTest/polite_hook_wait/polite_hook_wait.cc | Increase sleep duration to reduce scheduling-related flakes. |
Comments suppressed due to low confidence (1)
tests/gold_tests/autest-site/ports.py:84
PortOpencatchesOSErrorbeforesocket.timeout, butsocket.timeoutis a subclass ofOSError, so theexcept socket.timeoutblock will never run. Swap the order (catchsocket.timeoutfirst) or drop the second handler to avoid dead code and ensure timeout-specific logging works.
except OSError:
host.WriteDebug(
'PortOpen', f"socket error for port {port}, port is closed, "
"and therefore a future connection can use it")
except socket.timeout:
host.WriteDebug(
'PortOpen', f"Timeout error for port {port}, port is closed, "
"and therefore a future connection can use it")
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def HasLegacyTLSSupport(self): | ||
| """Check if the system supports legacy TLS protocols (TLSv1.0 and TLSv1.1). | ||
|
|
||
| Modern OpenSSL 3.x installations often disable these protocols entirely, | ||
| even if the openssl binary still accepts the -tls1 flag and lists TLSv1 ciphers. | ||
|
|
||
| On Fedora/RHEL systems, the crypto-policies framework may disable legacy | ||
| TLS at runtime even when OpenSSL is compiled with support for it. This | ||
| causes 'openssl ciphers -v -tls1' to still list TLSv1 ciphers, but actual | ||
| TLS 1.0 connections will fail with "no protocols available". | ||
|
|
||
| This check attempts to create an actual TLSv1 connection to a known HTTPS | ||
| server to detect if the protocol is truly available at runtime. | ||
| """ | ||
|
|
||
| def check_tls1_support(): | ||
| try: | ||
| # Try to actually use TLSv1 against a real HTTPS server | ||
| # This catches crypto-policy restrictions that aren't visible in cipher listings | ||
| # We use a well-known server that's likely to be reachable | ||
| result = subprocess.run( | ||
| ['openssl', 's_client', '-tls1', '-connect', 'www.google.com:443'], | ||
| capture_output=True, | ||
| text=True, | ||
| timeout=10, | ||
| input='' # Don't wait for input | ||
| ) | ||
| # Combine stdout and stderr for checking | ||
| output = result.stdout + result.stderr | ||
| # If we get "no protocols available", TLSv1 is disabled by policy | ||
| if 'no protocols available' in output: | ||
| return False | ||
| # If we get a successful connection or a server-side TLS version mismatch, | ||
| # it means TLSv1 is enabled on this client | ||
| return True | ||
| except subprocess.TimeoutExpired: | ||
| # If network is slow but TLSv1 was attempted, assume it's available | ||
| return True | ||
| except Exception: | ||
| # If we can't determine, assume TLSv1 is not available (safer) | ||
| return False |
Copilot
AI
Feb 4, 2026
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.
HasLegacyTLSSupport shells out to openssl s_client against www.google.com:443, which makes the test outcome depend on external network/DNS availability and the remote server’s configuration. In offline/locked-down CI this can lead to long timeouts or incorrect results (especially since TimeoutExpired currently returns True). Prefer an offline/local capability check (e.g., attempt to create a TLSv1/TLSv1.1 context with the ssl module, or run openssl s_client -tls1 against localhost and treat connection-refused as "protocol available" but policy-disabled errors as unsupported), and treat timeouts as "unsupported" to avoid false positives.
| def HasLegacyTLSSupport(self): | ||
| """Check if the system supports legacy TLS protocols (TLSv1.0 and TLSv1.1). | ||
|
|
||
| Modern OpenSSL 3.x installations often disable these protocols entirely, | ||
| even if the openssl binary still accepts the -tls1 flag and lists TLSv1 ciphers. | ||
|
|
||
| On Fedora/RHEL systems, the crypto-policies framework may disable legacy | ||
| TLS at runtime even when OpenSSL is compiled with support for it. This | ||
| causes 'openssl ciphers -v -tls1' to still list TLSv1 ciphers, but actual | ||
| TLS 1.0 connections will fail with "no protocols available". | ||
|
|
||
| This check attempts to create an actual TLSv1 connection to a known HTTPS | ||
| server to detect if the protocol is truly available at runtime. | ||
| """ | ||
|
|
||
| def check_tls1_support(): | ||
| try: | ||
| # Try to actually use TLSv1 against a real HTTPS server | ||
| # This catches crypto-policy restrictions that aren't visible in cipher listings | ||
| # We use a well-known server that's likely to be reachable | ||
| result = subprocess.run( | ||
| ['openssl', 's_client', '-tls1', '-connect', 'www.google.com:443'], | ||
| capture_output=True, | ||
| text=True, | ||
| timeout=10, | ||
| input='' # Don't wait for input | ||
| ) | ||
| # Combine stdout and stderr for checking | ||
| output = result.stdout + result.stderr | ||
| # If we get "no protocols available", TLSv1 is disabled by policy | ||
| if 'no protocols available' in output: | ||
| return False | ||
| # If we get a successful connection or a server-side TLS version mismatch, | ||
| # it means TLSv1 is enabled on this client | ||
| return True |
Copilot
AI
Feb 4, 2026
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.
This condition claims to check TLSv1.0 and TLSv1.1 support, but it only probes -tls1. Either also probe -tls1_1 (and require both, if that’s what the tests need) or rename/update the messaging so it matches what is actually being detected.
| # for special domain foo.com only offer TLSv1 and TLSv1_1 | ||
|
|
||
| Test.SkipUnless(Condition.HasOpenSSLVersion("1.1.1")) | ||
| Test.SkipUnless(Condition.HasLegacyTLSSupport(), "This test requires TLSv1.0/TLSv1.1 support which is disabled on modern systems") |
Copilot
AI
Feb 4, 2026
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.
This SkipUnless call passes a second positional string argument. In this repository, Test.SkipUnless(...) is only used with Condition objects (optionally multiple), not a separate reason string (see e.g. tests/README.md:328-331). If SkipUnless doesn’t accept a reason parameter, this will raise at runtime or be treated as an invalid condition. Consider relying on Condition.HasLegacyTLSSupport()’s own failure message (or extend the condition message) instead of passing a raw string here.
| Test.SkipUnless(Condition.HasLegacyTLSSupport(), "This test requires TLSv1.0/TLSv1.1 support which is disabled on modern systems") | |
| Test.SkipUnless(Condition.HasLegacyTLSSupport()) |
| # for special domain foo.com only offer TLSv1 and TLSv1_1 | ||
|
|
||
| Test.SkipUnless(Condition.HasOpenSSLVersion("1.1.1")) | ||
| Test.SkipUnless(Condition.HasLegacyTLSSupport(), "This test requires TLSv1.0/TLSv1.1 support which is disabled on modern systems") |
Copilot
AI
Feb 4, 2026
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.
This SkipUnless call passes a second positional string argument. In this repository, Test.SkipUnless(...) is only used with Condition objects (optionally multiple), not a separate reason string (see e.g. tests/README.md:328-331). If SkipUnless doesn’t accept a reason parameter, this will raise at runtime or be treated as an invalid condition. Consider relying on Condition.HasLegacyTLSSupport()’s own failure message (or extend the condition message) instead of passing a raw string here.
| Test.SkipUnless(Condition.HasLegacyTLSSupport(), "This test requires TLSv1.0/TLSv1.1 support which is disabled on modern systems") | |
| Test.SkipUnless(Condition.HasLegacyTLSSupport()) |
- Replace www.google.com:443 with 127.0.0.1:1 to avoid external network dependency in CI environments. Connection refused means TLSv1 protocol is available; "no protocols available" means crypto-policy blocks it. - Fix TimeoutExpired to return False (not True) since timeout on localhost indicates something is wrong, not that TLSv1 is available. - Remove invalid reason string from SkipUnless calls -- SkipUnless only accepts Condition objects, not extra string arguments. The skip reason is already embedded in the Condition definition. - Add docstring rationale for why only TLSv1.0 is probed (crypto-policies always disable TLSv1.0 and TLSv1.1 together).
Review Feedback ResponseAddressed Copilot's comments in commit 20a8baf. Fixed
Not Fixed (with rationale)
|
Summary
This PR fixes multiple autest compatibility issues that cause test failures on modern systems like Fedora 43 with Python 3.14 and OpenSSL 3.x.
Python 3.14 Compatibility Fixes
kwargs.iteritems()→kwargs.items()inmicroserver.test.extosimport inmicroserver.test.extandconditions.test.extdecode()callssocket.errorwithOSErrorinports.pyrecv()from hanging indefinitelyTLS Protocol Deprecation Fixes
HasLegacyTLSSupport()condition to detect TLSv1.0/TLSv1.1 supporttls_client_versionstests on systems without legacy TLS supportTiming Improvements
polite_hook_wait.ccfrom 200ms to 500ms to account for variable scheduling latencyTest Plan
Tested on:
Results: