From de14b9a16084a566673a73d6bc1c0205ec3991de Mon Sep 17 00:00:00 2001 From: Masaori Koshiba Date: Fri, 26 Jun 2026 18:52:17 +0900 Subject: [PATCH 1/4] proxy/unit_tests: restore IpAllow::subjects stub definition #13278 dropped this definition, but test_proxy links ts::http (which references IpAllow::subjects) ahead of ts::proxy (which defines it). GNU ld's single-pass archive scan then leaves the symbol unresolved, breaking the Linux build; macOS links fine. master avoids this incidentally via test_PluginYAML.cc from the unbackported plugin.yaml migration (#13070). --- src/proxy/unit_tests/stub.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/proxy/unit_tests/stub.cc b/src/proxy/unit_tests/stub.cc index ba279051a34..a1a95fe8ccb 100644 --- a/src/proxy/unit_tests/stub.cc +++ b/src/proxy/unit_tests/stub.cc @@ -22,3 +22,5 @@ */ #include "proxy/IPAllow.h" + +uint8_t IpAllow::subjects[IpAllow::Subject::MAX_SUBJECTS]; From cb22905df9d55ec53931a9ab4e8966280afa8367 Mon Sep 17 00:00:00 2001 From: Masaori Koshiba Date: Fri, 26 Jun 2026 20:46:41 +0900 Subject: [PATCH 2/4] tls autests: use ssl_multicert.config instead of yaml These tests were backported from master, where the default cert config is ssl_multicert.yaml and the harness exposes ts.Disk.ssl_multicert_yaml. On 10.2.x the default is still legacy ssl_multicert.config and no ssl_multicert_yaml Disk attribute is registered, so the tests failed at collection. Switch them to the ssl_multicert.config one-liner used by the other 10.2.x TLS tests. --- tests/gold_tests/tls/tls_flow_control.test.py | 8 +------- tests/gold_tests/tls/tls_record_size.test.py | 8 +------- tests/gold_tests/tls/tls_reload_under_load.test.py | 12 +++--------- tests/gold_tests/tls/tls_renegotiation.test.py | 8 +------- .../gold_tests/tls/tls_renegotiation_allowed.test.py | 8 +------- 5 files changed, 7 insertions(+), 37 deletions(-) diff --git a/tests/gold_tests/tls/tls_flow_control.test.py b/tests/gold_tests/tls/tls_flow_control.test.py index f1da9221825..4ff3886f6e2 100644 --- a/tests/gold_tests/tls/tls_flow_control.test.py +++ b/tests/gold_tests/tls/tls_flow_control.test.py @@ -70,13 +70,7 @@ def _configure_trafficserver(self) -> 'Process': TestTlsFlowControl._ts_counter += 1 ts.addDefaultSSLFiles() - ts.Disk.ssl_multicert_yaml.AddLines( - """ -ssl_multicert: - - dest_ip: "*" - ssl_cert_name: server.pem - ssl_key_name: server.key -""".split("\n")) + ts.Disk.ssl_multicert_config.AddLine('dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key') ts.Disk.remap_config.AddLine(f'map / http://127.0.0.1:{self._server.Variables.Port}') ts.Disk.records_config.update( { diff --git a/tests/gold_tests/tls/tls_record_size.test.py b/tests/gold_tests/tls/tls_record_size.test.py index b40c33a6fa5..da1960a30d1 100644 --- a/tests/gold_tests/tls/tls_record_size.test.py +++ b/tests/gold_tests/tls/tls_record_size.test.py @@ -75,13 +75,7 @@ def _configure_trafficserver(self) -> 'Process': TestRecordSizeClamp._ts_counter += 1 ts.addDefaultSSLFiles() - ts.Disk.ssl_multicert_yaml.AddLines( - """ -ssl_multicert: - - dest_ip: "*" - ssl_cert_name: server.pem - ssl_key_name: server.key -""".split("\n")) + ts.Disk.ssl_multicert_config.AddLine('dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key') ts.Disk.remap_config.AddLine(f'map / http://127.0.0.1:{self._server.Variables.Port}') ts.Disk.records_config.update( { diff --git a/tests/gold_tests/tls/tls_reload_under_load.test.py b/tests/gold_tests/tls/tls_reload_under_load.test.py index 1828da760d2..1726a112d72 100644 --- a/tests/gold_tests/tls/tls_reload_under_load.test.py +++ b/tests/gold_tests/tls/tls_reload_under_load.test.py @@ -1,6 +1,6 @@ ''' Existing cert/SNI reload tests reload while the server is idle. This one drives -continuous concurrent TLS handshakes and reloads ssl_multicert.yaml on top of +continuous concurrent TLS handshakes and reloads ssl_multicert.config on top of them, stressing the SSL/BIO ownership boundary of the layered TLS VConnection. The swapped-in certificate must take effect, every handshake must succeed, and ATS must not crash. @@ -51,13 +51,7 @@ def _configure_trafficserver(self) -> 'Process': ts.addSSLfile("ssl/signed-bar.key") ts.addSSLfile("ssl/signed2-bar.pem") - ts.Disk.ssl_multicert_yaml.AddLines( - """ -ssl_multicert: - - dest_ip: "*" - ssl_cert_name: signed-bar.pem - ssl_key_name: signed-bar.key -""".split("\n")) + ts.Disk.ssl_multicert_config.AddLine('dest_ip=* ssl_cert_name=signed-bar.pem ssl_key_name=signed-bar.key') ts.Disk.records_config.update( { 'proxy.config.ssl.server.cert.path': f'{ts.Variables.SSLDir}', @@ -69,7 +63,7 @@ def _configure_trafficserver(self) -> 'Process': # The reload must actually have run (otherwise the test would be vacuous). ts.Disk.diags_log.Content = Testers.ContainsExpression( - "ssl_multicert.yaml finished loading", "the cert configuration must reload while load is in flight") + "ssl_multicert.config finished loading", "the cert configuration must reload while load is in flight") # The reload-under-load must not crash or trip an assertion / sanitizer. ts.Disk.traffic_out.Content = Testers.ExcludesExpression( "received signal|failed assertion", "ATS must not crash reloading certs under load") diff --git a/tests/gold_tests/tls/tls_renegotiation.test.py b/tests/gold_tests/tls/tls_renegotiation.test.py index 70fecc14df4..a65979bb232 100644 --- a/tests/gold_tests/tls/tls_renegotiation.test.py +++ b/tests/gold_tests/tls/tls_renegotiation.test.py @@ -61,13 +61,7 @@ def _configure_trafficserver(self) -> 'Process': ts.addSSLfile("ssl/server.pem") ts.addSSLfile("ssl/server.key") - ts.Disk.ssl_multicert_yaml.AddLines( - """ -ssl_multicert: - - dest_ip: "*" - ssl_cert_name: server.pem - ssl_key_name: server.key -""".split("\n")) + ts.Disk.ssl_multicert_config.AddLine('dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key') ts.Disk.records_config.update( { 'proxy.config.ssl.server.cert.path': f'{ts.Variables.SSLDir}', diff --git a/tests/gold_tests/tls/tls_renegotiation_allowed.test.py b/tests/gold_tests/tls/tls_renegotiation_allowed.test.py index a5f58ff3581..c903bfcd0b8 100644 --- a/tests/gold_tests/tls/tls_renegotiation_allowed.test.py +++ b/tests/gold_tests/tls/tls_renegotiation_allowed.test.py @@ -83,13 +83,7 @@ def _configure_trafficserver(self) -> 'Process': ts.addSSLfile("ssl/server.pem") ts.addSSLfile("ssl/server.key") - ts.Disk.ssl_multicert_yaml.AddLines( - """ -ssl_multicert: - - dest_ip: "*" - ssl_cert_name: server.pem - ssl_key_name: server.key -""".split("\n")) + ts.Disk.ssl_multicert_config.AddLine('dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key') ts.Disk.records_config.update( { 'proxy.config.ssl.server.cert.path': f'{ts.Variables.SSLDir}', From bc30f068398a010c7bce4d848f88794bb2ea2a56 Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Thu, 4 Jun 2026 15:40:15 -0500 Subject: [PATCH 3/4] curl 8.20 test update: curl PROXY destination changes (#13239) curl 8.20 intentionally mirrors --haproxy-clientip into both PROXY addresses to keep the header address family consistent. The TSVConnPPInfo AuTest still expected the older destination address, so jobs with newer curl failed even though ATS preserved the PROXY metadata it received. This relaxes the destination-address expectation to accept either curl behavior while continuing to verify the source address and PROXY metadata. This also wraps the long curl command strings while leaving the test's request flow unchanged. (cherry picked from commit ad0ce02abf550455896167fa79ca1224f939cf1c) --- .../pluginTest/tsapi/test_TSVConnPPInfo.test.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.test.py b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.test.py index 0c45109d971..e74e2c40d61 100644 --- a/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.test.py +++ b/tests/gold_tests/pluginTest/tsapi/test_TSVConnPPInfo.test.py @@ -73,7 +73,9 @@ # plaintext HTTP tr = Test.AddTestRun() tr.TimeOut = 10 -tr.Processes.Default.Command = f"curl --haproxy-protocol --haproxy-clientip 1.2.3.4 'http://127.0.0.1:{ts.Variables.proxy_protocol_port}/httpbin/get'" +tr.Processes.Default.Command = ( + f"curl --haproxy-protocol --haproxy-clientip 1.2.3.4 " + f"'http://127.0.0.1:{ts.Variables.proxy_protocol_port}/httpbin/get'") tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.StartBefore(httpbin) tr.Processes.Default.StartBefore(Test.Processes.ts) @@ -84,7 +86,9 @@ # HTTPS tr = Test.AddTestRun() tr.TimeOut = 10 -tr.Processes.Default.Command = f"curl --haproxy-protocol --haproxy-clientip 5.6.7.8 -k 'https://127.0.0.1:{ts.Variables.proxy_protocol_ssl_port}/httpbin/get'" +tr.Processes.Default.Command = ( + f"curl --haproxy-protocol --haproxy-clientip 5.6.7.8 -k " + f"'https://127.0.0.1:{ts.Variables.proxy_protocol_ssl_port}/httpbin/get'") tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Streams.stdout = "test_TSVConnPPInfo_curl1.gold" tr.StillRunningAfter = httpbin @@ -95,7 +99,8 @@ tr.Processes.Default.ReturnCode = 0 f = tr.Disk.File(log_path) f.Content = "test_TSVConnPPInfo_plugin_log.gold" +# curl 8.20+ intentionally uses --haproxy-clientip for both PROXY addresses so the address family matches. f.Content += Testers.ContainsExpression( - "PP Info Received:V1,P2,T1,SRC1.2.3.4,DST127.0.0.1", "Expected information should be received") + r"PP Info Received:V1,P2,T1,SRC1\.2\.3\.4,DST(127\.0\.0\.1|1\.2\.3\.4)", "Expected information should be received") f.Content += Testers.ContainsExpression( - "PP Info Received:V1,P2,T1,SRC5.6.7.8,DST127.0.0.1", "Expected information should be received") + r"PP Info Received:V1,P2,T1,SRC5\.6\.7\.8,DST(127\.0\.0\.1|5\.6\.7\.8)", "Expected information should be received") From bf8f0bd84f693ab8e69808b28a5a0a415de04710 Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Thu, 4 Jun 2026 16:32:14 -0500 Subject: [PATCH 4/4] fedora:44: Trim remap ACL reload waits (#13237) The remap ACL AuTests run hundreds of reload scenarios in a single case, and the Fedora 44 shard is sensitive to extra reload-wait overhead, causing the tests to hang. Their reload sentinel also counted only explicit reloads, even though the log contains the startup load marker too. This replaces the long-lived sleep Ready helper with a short command that exits once the expected reload marker count is present. This also waits for the startup marker plus the explicit reload count, so each scenario observes the reload it just requested. (cherry picked from commit c52eeda1b85149d7b1ddda091baf918a6816da91) --- tests/gold_tests/remap/remap_acl.test.py | 12 ++--- tests/tools/condwait | 66 ++++++++++++++++++++---- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/tests/gold_tests/remap/remap_acl.test.py b/tests/gold_tests/remap/remap_acl.test.py index 6aec7b8ad33..0f5b0f5f23e 100644 --- a/tests/gold_tests/remap/remap_acl.test.py +++ b/tests/gold_tests/remap/remap_acl.test.py @@ -166,13 +166,13 @@ def _configure_traffic_server(self) -> int: # tr = Test.AddTestRun("Await config reload") p = tr.Processes.Default - p.Command = 'echo awaiting config reload' - p.Env = ts.Env Test_remap_acl._ts_reload_counter += 1 - count = Test_remap_acl._ts_reload_counter - await_config_reload = tr.Processes.Process(f'config_reload_succeeded_{count}', 'sleep 30') - await_config_reload.Ready = When.FileContains(ts.Disk.diags_log.Name, "remap.config finished loading", count) - p.StartBefore(await_config_reload) + count = 1 + Test_remap_acl._ts_reload_counter + condwait = os.path.join(Test.Variables.AtsTestToolsDir, 'condwait') + p.Command = (f"'{condwait}' 30 --contains '{ts.Disk.diags_log.Name}' " + f"'remap.config finished loading' {count}") + p.Env = ts.Env + tr.StillRunningAfter = ts else: record_config = { diff --git a/tests/tools/condwait b/tests/tools/condwait index 0f208f168f8..2a627f98897 100755 --- a/tests/tools/condwait +++ b/tests/tools/condwait @@ -23,6 +23,7 @@ # # CONDITION is the ('test' command) condition to wait for. (It may contain white space.) # For file existence tests (-f, -e, -d), glob patterns are supported (e.g., -f /path/crash-*.log). +# Use "--contains FILE PATTERN COUNT" to wait until FILE contains PATTERN in at least COUNT lines. # # MAX-WAIT is the maximum number of seconds to wait for the condition. If it is omitted, it defaults to 60. # @@ -34,8 +35,13 @@ WAIT=60 POST_WAIT=0 +usage() { + echo "usage: condwait [ MAX-WAIT [ POST-WAIT ] ] TEST-CONDITION" >&2 + echo " condwait [ MAX-WAIT [ POST-WAIT ] ] --contains FILE PATTERN COUNT" >&2 +} + if [[ "$1" = "" ]] ; then - echo "usage: condwait [ MAX-WAIT [ POST-WAIT ] ] TEST-CONDTION" >&2 + usage exit 1 fi @@ -44,7 +50,7 @@ if [[ "$X" = "$1" ]] ; then WAIT=$1 shift if [[ "$1" = "" ]] ; then - echo "usage: condwait [ MAX-WAIT [ POST-WAIT ] ] TEST-CONDTION" >&2 + usage exit 1 fi X=$( echo "$1" | sed 's/x/yy/g' | sed 's/[^0-9]/x/g' ) @@ -55,24 +61,64 @@ if [[ "$X" = "$1" ]] ; then fi if [[ "$1" = "" ]] ; then - echo "usage: condwait [ MAX-WAIT [ POST-WAIT ] ] TEST-CONDTION" >&2 + usage exit 1 fi -# Check if this is a simple file existence test (-f, -e, -d). If so, use ls -d -# which handles glob patterns properly. Otherwise, use test for the condition. +if [[ "$1" = "--contains" ]]; then + if [[ $# -ne 4 ]] || ! [[ "$4" =~ ^[0-9]+$ ]]; then + usage + exit 1 + fi +fi + +# Check simple file existence tests (-f, -e, -d) specially so literal paths with +# whitespace work while glob patterns remain supported. check_condition() { - if [[ "$1" = "-f" || "$1" = "-e" || "$1" = "-d" ]] && [[ $# -eq 2 ]]; then - # Use ls -d for file existence tests to support glob patterns. - ls -d $2 >/dev/null 2>&1 + if [[ "$1" = "--contains" ]]; then + [[ -f "$2" ]] || return 1 + local matches + matches=$(awk -v pattern="$3" -v expected="$4" ' + BEGIN { + if (expected == 0) { + print 0 + found = 1 + exit + } + } + index($0, pattern) { + count++ + if (count >= expected) { + print count + found = 1 + exit + } + } + END { + if (!found) { + print count + 0 + } + } + ' "$2") + (( matches >= $4 )) + elif [[ "$1" = "-f" || "$1" = "-e" || "$1" = "-d" ]] && [[ $# -eq 2 ]]; then + if [[ "$2" = *[\*\?\[]* ]]; then + # Use compgen for file existence tests to support glob patterns. + local match + while IFS= read -r match; do + test "$1" "$match" && return 0 + done < <(compgen -G "$2") + return 1 + fi + test "$1" "$2" else - test $* + test "$@" fi } while (( WAIT > 0 )) do - if check_condition $* + if check_condition "$@" then if (( POST_WAIT > 0 )) then