This is an automated email from the ASF dual-hosted git repository. shinrich pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push: new 4d5d377 Make double* and openclose* reliably wait for stats to be ready 4d5d377 is described below commit 4d5d377a785bec291a67a52b5186ff4c6d733459 Author: shinrich <shinr...@unknown-192-168-122-x.yahoo.com> AuthorDate: Thu Nov 8 14:51:06 2018 -0500 Make double* and openclose* reliably wait for stats to be ready --- tests/gold_tests/continuations/double.test.py | 137 +++++++++++---------- tests/gold_tests/continuations/double_h2.test.py | 92 +++++++------- tests/gold_tests/continuations/openclose.test.py | 73 ++++++----- .../gold_tests/continuations/openclose_h2.test.py | 136 ++++++++++---------- tests/tools/plugins/continuations_verify.cc | 55 ++++++--- tests/tools/plugins/ssntxnorder_verify.cc | 35 +++--- 6 files changed, 282 insertions(+), 246 deletions(-) diff --git a/tests/gold_tests/continuations/double.test.py b/tests/gold_tests/continuations/double.test.py index b3f98d0..f6ba705 100644 --- a/tests/gold_tests/continuations/double.test.py +++ b/tests/gold_tests/continuations/double.test.py @@ -17,112 +17,125 @@ # limitations under the License. import os +import subprocess Test.Summary = ''' -Test transactions and sessions, making sure two continuations catch the same number of hooks. +Test transactions and sessions for http1, making sure the two continuations catch the same number of hooks. ''' -Test.SkipIf(Condition.true('This test errors frequently, and so it is disabled.')) + Test.SkipUnless( - Condition.HasProgram("curl", "Curl needs to be installed on system for this test to work") + Condition.HasProgram("curl", "Curl needs to be installed on system for this test to work"), ) Test.ContinueOnFail = True # Define default ATS -ts = Test.MakeATSProcess("ts", command="traffic_manager") - +ts = Test.MakeATSProcess("ts", select_ports=False, command="traffic_manager") server = Test.MakeOriginServer("server") +server2 = Test.MakeOriginServer("server2") Test.testName = "" -request_header = {"headers": "GET / HTTP/1.1\r\nHost: double.test\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +request_header = {"headers": "GET / HTTP/1.1\r\nHost: double_h2.test\r\n\r\n", "timestamp": "1469733493.993", "body": ""} # expected response from the origin server -response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} - -Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'continuations_verify.cc'), ts) +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length:0\r\n\r\n", + "timestamp": "1469733493.993", "body": ""} # add response to the server dictionary server.addResponse("sessionfile.log", request_header, response_header) + +# add port and remap rule +ts.Disk.remap_config.AddLine( + 'map / http://127.0.0.1:{0}'.format(server.Variables.Port) +) + ts.Disk.records_config.update({ 'proxy.config.diags.debug.enabled': 1, 'proxy.config.diags.debug.tags': 'continuations_verify.*', - 'proxy.config.http.cache.http' : 0, #disable cache to simply the test. - 'proxy.config.cache.enable_read_while_writer' : 0 + 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.http.cache.http': 0, # disable cache to simply the test. + 'proxy.config.cache.enable_read_while_writer': 0, + 'proxy.config.ssl.client.verify.server': 0, + 'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2', + 'proxy.config.http2.max_concurrent_streams_in': 65535 }) -ts.Disk.remap_config.AddLine( - 'map http://double.test:{0} http://127.0.0.1:{1}'.format(ts.Variables.port, server.Variables.Port) -) -cmd = 'curl -vs http://127.0.0.1:{0}'.format(ts.Variables.port) -numberOfRequests = 25 +# add plugin to assist with test metrics +Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, + 'plugins', 'continuations_verify.cc'), ts) + +comparator_command = ''' +if test "`traffic_ctl metric get continuations_verify.{0}.close.1 | cut -d ' ' -f 2`" -eq "`traffic_ctl metric get continuations_verify.{0}.close.2 | cut -d ' ' -f 2`" ; then\ + echo yes;\ + else \ + echo no; \ + fi; \ + traffic_ctl metric match continuations_verify + ''' + +cmd = 'curl -vs http://127.0.0.1:{0}/'.format(ts.Variables.port) +numberOfRequests = 55 tr = Test.AddTestRun() # Create a bunch of curl commands to be executed in parallel. Default.Process is set in SpawnCommands. -ps = tr.SpawnCommands(cmdstr=cmd, count=numberOfRequests) +# On Fedora 28/29, it seems that curl will occaisionally timeout after a couple seconds and return exitcode 2 +# Examinig the packet capture shows that Traffic Server dutifully sends the response +ps = tr.SpawnCommands(cmdstr=cmd, count=numberOfRequests, retcode=Any(0,2)) tr.Processes.Default.Env = ts.Env +tr.Processes.Default.ReturnCode = Any(0,2) # Execution order is: ts/server, ps(curl cmds), Default Process. tr.Processes.Default.StartBefore( server, ready=When.PortOpen(server.Variables.Port)) # Adds a delay once the ts port is ready. This is because we cannot test the ts state. -tr.Processes.Default.StartBefore(ts, ready=10) +tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.port)) ts.StartAfter(*ps) server.StartAfter(*ps) tr.StillRunningAfter = ts -comparator_command = ''' -if test "`traffic_ctl metric get continuations_verify.{0}.close.1 | cut -d ' ' -f 2`" -eq "`traffic_ctl metric get continuations_verify.{0}.close.2 | cut -d ' ' -f 2`" ; then\ - echo yes;\ - else \ - echo no; \ - fi; - ''' +# Signal that all the curl processes have completed +tr = Test.AddTestRun("Curl Done") +tr.Processes.Default.Command = "traffic_ctl plugin msg done done" +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Env = ts.Env +tr.StillRunningAfter = ts -records = ts.Disk.File(os.path.join(ts.Variables.RUNTIMEDIR, "records.snap")) +# Parking this as a ready tester on a meaningless process +# To stall the test runs that check for the stats until the +# stats have propagated and are ready to read. +def make_done_stat_ready(tsenv): + def done_stat_ready(process, hasRunFor, **kw): + retval = subprocess.run("traffic_ctl metric get continuations_verify.test.done > done 2> /dev/null", shell=True, env=tsenv) + if retval.returncode == 0: + retval = subprocess.run("grep 1 done > /dev/null", shell = True, env=tsenv) + return retval.returncode == 0 + return done_stat_ready + # number of sessions/transactions opened and closed are equal -tr = Test.AddTestRun() -tr.DelayStart = 10 # wait for stats to be updated +tr = Test.AddTestRun("Check Ssn") +server2.StartupTimeout = 60 +# Again, here the imporant thing is the ready function not the server2 process +tr.Processes.Default.StartBefore(server2, ready=make_done_stat_ready(ts.Env)) tr.Processes.Default.Command = comparator_command.format('ssn') tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env tr.Processes.Default.Streams.stdout = Testers.ContainsExpression("yes", 'should verify contents') +# Session and Txn's should be non-zero +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression("continations_verify.ssn.close.1 0", 'should be nonzero') +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression("continations_verify.ssn.close.2 0", 'should be nonzero') +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression("continations_verify.txn.close.1 0", 'should be nonzero') +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression("continations_verify.txn.close.2 0", 'should be nonzero') +tr.Processes.Default.Streams.stdout += Testers.ContainsExpression( + "continuations_verify.txn.close.1 {}".format(numberOfRequests), 'should be the number of transactions we made') +tr.Processes.Default.Streams.stdout += Testers.ContainsExpression( + "continuations_verify.txn.close.2 {}".format(numberOfRequests), 'should be the number of transactions we made') tr.StillRunningAfter = ts -# for debugging session number -ssn1 = tr.Processes.Process("session1", 'traffic_ctl metric get continuations_verify.ssn.close.1 > ssn1') -ssn2 = tr.Processes.Process("session2", 'traffic_ctl metric get continuations_verify.ssn.close.2 > ssn2') -ssn1.Env = ts.Env -ssn2.Env = ts.Env -tr.Processes.Default.StartBefore(ssn1) -tr.Processes.Default.StartBefore(ssn2) +tr.StillRunningAfter = server2 -tr = Test.AddTestRun() -tr.DelayStart = 10 # wait for stats to be updated +tr = Test.AddTestRun("Check Txn") tr.Processes.Default.Command = comparator_command.format('txn') tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env tr.Processes.Default.Streams.stdout = Testers.ContainsExpression("yes", 'should verify contents') tr.StillRunningAfter = ts -# for debugging transaction number -txn1 = tr.Processes.Process("transaction1", 'traffic_ctl metric get continuations_verify.txn.close.1 > txn1') -txn2 = tr.Processes.Process("transaction2", 'traffic_ctl metric get continuations_verify.txn.close.2 > txn2') -txn1.Env = ts.Env -txn2.Env = ts.Env -tr.Processes.Default.StartBefore(txn1) -tr.Processes.Default.StartBefore(txn2) - -# session count is positive, -tr = Test.AddTestRun() -tr.DelayStart = 10 # wait for stats to be updated -tr.Processes.Default.Command = "traffic_ctl metric get continuations_verify.ssn.close.1" -tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Env = ts.Env -tr.Processes.Default.Streams.stdout = Testers.ExcludesExpression(" 0", 'should be nonzero') -tr.StillRunningAfter = ts +tr.StillRunningAfter = server2 -# and we receive the same number of transactions as we asked it to make -tr = Test.AddTestRun() -tr.DelayStart = 10 # wait for stats to be updated -tr.Processes.Default.Command = "traffic_ctl metric get continuations_verify.txn.close.1" -tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Env = ts.Env -tr.Processes.Default.Streams.stdout = Testers.ContainsExpression( - "continuations_verify.txn.close.1 {}".format(numberOfRequests), 'should be the number of transactions we made') -tr.StillRunningAfter = ts diff --git a/tests/gold_tests/continuations/double_h2.test.py b/tests/gold_tests/continuations/double_h2.test.py index 61da07d..86aace4 100644 --- a/tests/gold_tests/continuations/double_h2.test.py +++ b/tests/gold_tests/continuations/double_h2.test.py @@ -17,6 +17,7 @@ # limitations under the License. import os +import subprocess Test.Summary = ''' Test transactions and sessions for http2, making sure the two continuations catch the same number of hooks. ''' @@ -28,11 +29,12 @@ Test.ContinueOnFail = True # Define default ATS ts = Test.MakeATSProcess("ts", select_ports=False, command="traffic_manager") server = Test.MakeOriginServer("server") +server2 = Test.MakeOriginServer("server2") Test.testName = "" request_header = {"headers": "GET / HTTP/1.1\r\nHost: double_h2.test\r\n\r\n", "timestamp": "1469733493.993", "body": ""} # expected response from the origin server -response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length:0\r\n\r\n", "timestamp": "1469733493.993", "body": ""} # add response to the server dictionary @@ -45,7 +47,7 @@ ts.addSSLfile("ssl/server.key") # add port and remap rule ts.Variables.ssl_port = 4443 ts.Disk.remap_config.AddLine( - 'map http://double_h2.test:{0} http://127.0.0.1:{1}'.format(ts.Variables.port, server.Variables.Port) + 'map / http://127.0.0.1:{0}'.format(server.Variables.Port) ) ts.Disk.ssl_multicert_config.AddLine( @@ -54,7 +56,7 @@ ts.Disk.ssl_multicert_config.AddLine( ts.Disk.records_config.update({ 'proxy.config.diags.debug.enabled': 1, - 'proxy.config.diags.debug.tags': 'continuations_verify.*', + 'proxy.config.diags.debug.tags': 'continuations_verify', 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), 'proxy.config.http.cache.http': 0, # disable cache to simply the test. @@ -75,7 +77,8 @@ if test "`traffic_ctl metric get continuations_verify.{0}.close.1 | cut -d ' ' - echo yes;\ else \ echo no; \ - fi; + fi; \ + traffic_ctl metric match continuations_verify ''' # curl with http2 @@ -85,69 +88,66 @@ numberOfRequests = 25 tr = Test.AddTestRun() # Create a bunch of curl commands to be executed in parallel. Default.Process is set in SpawnCommands. -ps = tr.SpawnCommands(cmdstr=cmd, count=numberOfRequests) +# On Fedora 28/29, it seems that curl will occaisionally timeout after a couple seconds and return exitcode 2 +# Examinig the packet capture shows that Traffic Server dutifully sends the response +ps = tr.SpawnCommands(cmdstr=cmd, count=numberOfRequests, retcode=Any(0,2)) tr.Processes.Default.Env = ts.Env +tr.Processes.Default.ReturnCode = Any(0,2) # Execution order is: ts/server, ps(curl cmds), Default Process. tr.Processes.Default.StartBefore( server, ready=When.PortOpen(server.Variables.Port)) # Adds a delay once the ts port is ready. This is because we cannot test the ts state. -tr.Processes.Default.StartBefore(ts, ready=10) +tr.Processes.Default.StartBefore(Test.Processes.ts, ready=When.PortOpen(ts.Variables.ssl_port)) ts.StartAfter(*ps) server.StartAfter(*ps) tr.StillRunningAfter = ts -# Watch the records snapshot file. -records = ts.Disk.File(os.path.join(ts.Variables.RUNTIMEDIR, "records.snap")) +# Signal that all the curl processes have completed +tr = Test.AddTestRun("Curl Done") +tr.Processes.Default.Command = "traffic_ctl plugin msg done done" +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Env = ts.Env +tr.StillRunningAfter = ts +# Parking this as a ready tester on a meaningless process +# To stall the test runs that check for the stats until the +# stats have propagated and are ready to read. +def make_done_stat_ready(tsenv): + def done_stat_ready(process, hasRunFor, **kw): + retval = subprocess.run("traffic_ctl metric get continuations_verify.test.done > done 2> /dev/null", shell=True, env=tsenv) + if retval.returncode == 0: + retval = subprocess.run("grep 1 done > /dev/null", shell = True, env=tsenv) + return retval.returncode == 0 + + return done_stat_ready + # number of sessions/transactions opened and closed are equal -tr = Test.AddTestRun() -tr.DelayStart = 10 # wait for stats to be updated +tr = Test.AddTestRun("Check Ssn") +server2.StartupTimeout = 60 +# Again, here the imporant thing is the ready function not the server2 process +tr.Processes.Default.StartBefore(server2, ready=make_done_stat_ready(ts.Env)) tr.Processes.Default.Command = comparator_command.format('ssn') tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env tr.Processes.Default.Streams.stdout = Testers.ContainsExpression("yes", 'should verify contents') +# Session and Txn's should be non-zero +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression("continations_verify.ssn.close.1 0", 'should be nonzero') +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression("continations_verify.ssn.close.2 0", 'should be nonzero') +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression("continations_verify.txn.close.1 0", 'should be nonzero') +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression("continations_verify.txn.close.2 0", 'should be nonzero') +tr.Processes.Default.Streams.stdout += Testers.ContainsExpression( + "continuations_verify.txn.close.1 {}".format(numberOfRequests), 'should be the number of transactions we made') +tr.Processes.Default.Streams.stdout += Testers.ContainsExpression( + "continuations_verify.txn.close.2 {}".format(numberOfRequests), 'should be the number of transactions we made') tr.StillRunningAfter = ts +tr.StillRunningAfter = server2 -# for debugging session number -ssn1 = tr.Processes.Process("session1", 'traffic_ctl metric get continuations_verify.ssn.close.1 > ssn1') -ssn2 = tr.Processes.Process("session2", 'traffic_ctl metric get continuations_verify.ssn.close.2 > ssn2') -ssn1.Env = ts.Env -ssn2.Env = ts.Env -tr.Processes.Default.StartBefore(ssn1) -tr.Processes.Default.StartBefore(ssn2) - -tr = Test.AddTestRun() -tr.DelayStart = 10 # wait for stats to be updated +tr = Test.AddTestRun("Check Txn") tr.Processes.Default.Command = comparator_command.format('txn') tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env tr.Processes.Default.Streams.stdout = Testers.ContainsExpression("yes", 'should verify contents') tr.StillRunningAfter = ts +tr.StillRunningAfter = server2 -# for debugging transaction number -txn1 = tr.Processes.Process("transaction1", 'traffic_ctl metric get continuations_verify.txn.close.1 > txn1') -txn2 = tr.Processes.Process("transaction2", 'traffic_ctl metric get continuations_verify.txn.close.2 > txn2') -txn1.Env = ts.Env -txn2.Env = ts.Env -tr.Processes.Default.StartBefore(txn1) -tr.Processes.Default.StartBefore(txn2) - -# session count is positive, -tr = Test.AddTestRun() -tr.DelayStart = 10 # wait for stats to be updated -tr.Processes.Default.Command = "traffic_ctl metric get continuations_verify.ssn.close.1" -tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Env = ts.Env -tr.Processes.Default.Streams.stdout = Testers.ExcludesExpression(" 0", 'should be nonzero') -tr.StillRunningAfter = ts - -# and we receive the same number of transactions as we asked it to make -tr = Test.AddTestRun() -tr.DelayStart = 10 # wait for stats to be updated -tr.Processes.Default.Command = "traffic_ctl metric get continuations_verify.txn.close.1" -tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Env = ts.Env -tr.Processes.Default.Streams.stdout = Testers.ContainsExpression( - "continuations_verify.txn.close.1 {}".format(numberOfRequests), 'should be the number of transactions we made') -tr.StillRunningAfter = ts diff --git a/tests/gold_tests/continuations/openclose.test.py b/tests/gold_tests/continuations/openclose.test.py index 32d45bd..452ea13 100644 --- a/tests/gold_tests/continuations/openclose.test.py +++ b/tests/gold_tests/continuations/openclose.test.py @@ -17,6 +17,7 @@ # limitations under the License. import os +import subprocess Test.Summary = ''' Test transactions and sessions, making sure they open and close in the proper order. ''' @@ -29,12 +30,13 @@ Test.SkipUnless( ts = Test.MakeATSProcess("ts", command="traffic_manager") server = Test.MakeOriginServer("server") +server2 = Test.MakeOriginServer("server2") Test.testName = "" request_header = {"headers": "GET / HTTP/1.1\r\nHost: oc.test\r\n\r\n", "timestamp": "1469733493.993", "body": ""} # expected response from the origin server -response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length:0\r\n\r\n", "timestamp": "1469733493.993", "body": ""} Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, @@ -59,8 +61,11 @@ numberOfRequests = 25 tr = Test.AddTestRun() # Create a bunch of curl commands to be executed in parallel. Default.Process is set in SpawnCommands. -ps = tr.SpawnCommands(cmdstr=cmd, count=numberOfRequests) +# On Fedora 28/29, it seems that curl will occaisionally timeout after a couple seconds and return exitcode 2 +# Examinig the packet capture shows that Traffic Server dutifully sends the response +ps = tr.SpawnCommands(cmdstr=cmd, count=numberOfRequests, retcode=Any(0,2)) tr.Processes.Default.Env = ts.Env +tr.Processes.Default.ReturnCode = Any(0,2) # Execution order is: ts/server, ps(curl cmds), Default Process. tr.Processes.Default.StartBefore( @@ -71,13 +76,30 @@ ts.StartAfter(*ps) server.StartAfter(*ps) tr.StillRunningAfter = ts -# Watch the records snapshot file. -records = ts.Disk.File(os.path.join(ts.Variables.RUNTIMEDIR, "records.snap")) +# Signal that all the curl processes have completed +tr = Test.AddTestRun("Curl Done") +tr.Processes.Default.Command = "traffic_ctl plugin msg done done" +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Env = ts.Env +tr.StillRunningAfter = ts -# Check our work on traffic_ctl -# no errors happened, -tr = Test.AddTestRun() -tr.DelayStart = 10 +# Parking this as a ready tester on a meaningless process +# To stall the test runs that check for the stats until the +# stats have propagated and are ready to read. +def make_done_stat_ready(tsenv): + def done_stat_ready(process, hasRunFor, **kw): + retval = subprocess.run("traffic_ctl metric get ssntxnorder_verify.test.done > done 2> /dev/null", shell=True, env=tsenv) + if retval.returncode == 0: + retval = subprocess.run("grep 1 done > /dev/null", shell = True, env=tsenv) + return retval.returncode == 0 + + return done_stat_ready + +# number of sessions/transactions opened and closed are equal +tr = Test.AddTestRun("Check Ssn order errors") +server2.StartupTimeout = 60 +# Again, here the imporant thing is the ready function not the server2 process +tr.Processes.Default.StartBefore(server2, ready=make_done_stat_ready(ts.Env)) tr.Processes.Default.Command = 'traffic_ctl metric get ssntxnorder_verify.err' tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env @@ -91,48 +113,33 @@ if test "`traffic_ctl metric get ssntxnorder_verify.{0}.start | cut -d ' ' -f 2` echo yes;\ else \ echo no; \ - fi; + fi; \ + traffic_ctl metric match ssntxnorder_verify ''' # number of sessions/transactions opened and closed are equal -tr = Test.AddTestRun() -tr.DelayStart = 10 +tr = Test.AddTestRun("Check for ssn open/close") tr.Processes.Default.Command = comparator_command.format('ssn') tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env tr.Processes.Default.Streams.stdout = Testers.ContainsExpression( "yes", 'should verify contents') +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression( + "ssntxnorder_verify.ssn.start 0", 'should be nonzero') tr.StillRunningAfter = ts tr.StillRunningAfter = server -tr = Test.AddTestRun() -tr.DelayStart = 10 +tr = Test.AddTestRun("Check for txn/open/close") tr.Processes.Default.Command = comparator_command.format('txn') tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env tr.Processes.Default.Streams.stdout = Testers.ContainsExpression( "yes", 'should verify contents') -tr.StillRunningAfter = ts -tr.StillRunningAfter = server - -# session count is positive, -tr = Test.AddTestRun() -tr.DelayStart = 10 -tr.Processes.Default.Command = "traffic_ctl metric get ssntxnorder_verify.ssn.start" -tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Env = ts.Env -tr.Processes.Default.Streams.stdout = Testers.ExcludesExpression( - " 0", 'should be nonzero') -tr.StillRunningAfter = ts -tr.StillRunningAfter = server - +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression( + "ssntxnorder_verify.txn.start 0", 'should be nonzero') # and we receive the same number of transactions as we asked it to make -tr = Test.AddTestRun() -tr.DelayStart = 10 -tr.Processes.Default.Command = "traffic_ctl metric get ssntxnorder_verify.txn.start" -tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Env = ts.Env -tr.Processes.Default.Streams.stdout = Testers.ContainsExpression( +tr.Processes.Default.Streams.stdout += Testers.ContainsExpression( "ssntxnorder_verify.txn.start {}".format(numberOfRequests), 'should be the number of transactions we made') tr.StillRunningAfter = ts tr.StillRunningAfter = server + diff --git a/tests/gold_tests/continuations/openclose_h2.test.py b/tests/gold_tests/continuations/openclose_h2.test.py index f14db70..07aec81 100644 --- a/tests/gold_tests/continuations/openclose_h2.test.py +++ b/tests/gold_tests/continuations/openclose_h2.test.py @@ -17,75 +17,67 @@ # limitations under the License. import os +import subprocess Test.Summary = ''' -Test transactions and sessions for http2, making sure they open and close in the proper order. +Test transactions and sessions over http2, making sure they open and close in the proper order. ''' -Test.SkipIf(Condition.true('This test errors frequently, and so it is disabled.')) Test.SkipUnless( Condition.HasProgram("curl", "Curl needs to be installed on system for this test to work"), Condition.HasCurlFeature('http2') ) -Test.ContinueOnFail = True + # Define default ATS ts = Test.MakeATSProcess("ts", select_ports=False, command="traffic_manager") +ts.Variables.ssl_port = 4443 + server = Test.MakeOriginServer("server") +server2 = Test.MakeOriginServer("server2") Test.testName = "" -request_header = {"headers": "GET / HTTP/1.1\r\nHost: oc_h2.test\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +request_header = {"headers": "GET / HTTP/1.1\r\nHost: oc.test\r\n\r\n", + "timestamp": "1469733493.993", "body": ""} # expected response from the origin server -response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length:0\r\n\r\n", "timestamp": "1469733493.993", "body": ""} -# add response to the server dictionary -server.addResponse("sessionfile.log", request_header, response_header) - +# add ssl materials like key, certificates for the server ts.addSSLfile("ssl/server.pem") ts.addSSLfile("ssl/server.key") -ts.Variables.ssl_port = 4443 -ts.Disk.remap_config.AddLine( - 'map http://oc_h2.test:{0} http://127.0.0.1:{1}'.format(ts.Variables.port, server.Variables.Port) -) - -ts.Disk.ssl_multicert_config.AddLine( - 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' -) +Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, + 'plugins', 'ssntxnorder_verify.cc'), ts) +# add response to the server dictionary +server.addResponse("sessionfile.log", request_header, response_header) ts.Disk.records_config.update({ 'proxy.config.diags.debug.enabled': 1, - 'proxy.config.diags.debug.tags': 'ssntxnorder_verify.*', - 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), - 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.diags.debug.tags': 'ssntxnorder_verify', 'proxy.config.http.cache.http': 0, # disable cache to simply the test. 'proxy.config.cache.enable_read_while_writer': 0, - # enable ssl port + 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), 'proxy.config.http.server_ports': '{0} {1}:proto=http2;http:ssl'.format(ts.Variables.port, ts.Variables.ssl_port), - 'proxy.config.ssl.client.verify.server': 0, - 'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2', - 'proxy.config.http2.max_concurrent_streams_in': 65535 }) -# add plugin to assist with test metrics -Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, - 'plugins', 'ssntxnorder_verify.cc'), ts) - -comparator_command = ''' -if test "`traffic_ctl metric get ssntxnorder_verify.{0}.start | cut -d ' ' -f 2`" -eq "`traffic_ctl metric get ssntxnorder_verify.{0}.close | cut -d ' ' -f 2`" ; then\ - echo yes;\ - else \ - echo no; \ - fi; - ''' +ts.Disk.remap_config.AddLine( + 'map http://oc.test:{0} http://127.0.0.1:{1}'.format( + ts.Variables.port, server.Variables.Port) +) +ts.Disk.ssl_multicert_config.AddLine( + 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' +) -# curl with http2 -cmd = 'curl --http2 -k -vs https://127.0.0.1:{0}/'.format(ts.Variables.ssl_port) +cmd = 'curl -k --http2 -vs https://127.0.0.1:{0}'.format(ts.Variables.ssl_port) numberOfRequests = 25 tr = Test.AddTestRun() # Create a bunch of curl commands to be executed in parallel. Default.Process is set in SpawnCommands. -ps = tr.SpawnCommands(cmdstr=cmd, count=numberOfRequests) +# On Fedora 28/29, it seems that curl will occaisionally timeout after a couple seconds and return exitcode 2 +# Examinig the packet capture shows that Traffic Server dutifully sends the response +ps = tr.SpawnCommands(cmdstr=cmd, count=numberOfRequests, retcode=Any(0,2)) tr.Processes.Default.Env = ts.Env +tr.Processes.Default.ReturnCode = Any(0,2) # Execution order is: ts/server, ps(curl cmds), Default Process. tr.Processes.Default.StartBefore( @@ -96,13 +88,30 @@ ts.StartAfter(*ps) server.StartAfter(*ps) tr.StillRunningAfter = ts -# Watch the records snapshot file. -records = ts.Disk.File(os.path.join(ts.Variables.RUNTIMEDIR, "records.snap")) +# Signal that all the curl processes have completed +tr = Test.AddTestRun("Curl Done") +tr.Processes.Default.Command = "traffic_ctl plugin msg done done" +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Env = ts.Env +tr.StillRunningAfter = ts -# Check our work on traffic_ctl -# no errors happened, -tr = Test.AddTestRun() -tr.DelayStart = 10 +# Parking this as a ready tester on a meaningless process +# To stall the test runs that check for the stats until the +# stats have propagated and are ready to read. +def make_done_stat_ready(tsenv): + def done_stat_ready(process, hasRunFor, **kw): + retval = subprocess.run("traffic_ctl metric get ssntxnorder_verify.test.done > done 2> /dev/null", shell=True, env=tsenv) + if retval.returncode == 0: + retval = subprocess.run("grep 1 done > /dev/null", shell = True, env=tsenv) + return retval.returncode == 0 + + return done_stat_ready + +# number of sessions/transactions opened and closed are equal +tr = Test.AddTestRun("Check Ssn order errors") +server2.StartupTimeout = 60 +# Again, here the imporant thing is the ready function not the server2 process +tr.Processes.Default.StartBefore(server2, ready=make_done_stat_ready(ts.Env)) tr.Processes.Default.Command = 'traffic_ctl metric get ssntxnorder_verify.err' tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env @@ -111,45 +120,38 @@ tr.Processes.Default.Streams.All = Testers.ContainsExpression( tr.StillRunningAfter = ts tr.StillRunningAfter = server +comparator_command = ''' +if test "`traffic_ctl metric get ssntxnorder_verify.{0}.start | cut -d ' ' -f 2`" -eq "`traffic_ctl metric get ssntxnorder_verify.{0}.close | cut -d ' ' -f 2`" ; then\ + echo yes;\ + else \ + echo no; \ + fi; \ + traffic_ctl metric match ssntxnorder_verify + ''' + # number of sessions/transactions opened and closed are equal -tr = Test.AddTestRun() -tr.DelayStart = 10 +tr = Test.AddTestRun("Check for ssn open/close") tr.Processes.Default.Command = comparator_command.format('ssn') tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env tr.Processes.Default.Streams.stdout = Testers.ContainsExpression( "yes", 'should verify contents') +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression( + "ssntxnorder_verify.ssn.start 0", 'should be nonzero') tr.StillRunningAfter = ts tr.StillRunningAfter = server -tr = Test.AddTestRun() -tr.DelayStart = 10 +tr = Test.AddTestRun("Check for txn/open/close") tr.Processes.Default.Command = comparator_command.format('txn') tr.Processes.Default.ReturnCode = 0 tr.Processes.Default.Env = ts.Env tr.Processes.Default.Streams.stdout = Testers.ContainsExpression( "yes", 'should verify contents') -tr.StillRunningAfter = ts -tr.StillRunningAfter = server - -# session count is positive, -tr = Test.AddTestRun() -tr.DelayStart = 10 -tr.Processes.Default.Command = "traffic_ctl metric get ssntxnorder_verify.ssn.start" -tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Env = ts.Env -tr.Processes.Default.Streams.stdout = Testers.ExcludesExpression( - " 0", 'should be nonzero') -tr.StillRunningAfter = ts -tr.StillRunningAfter = server - +tr.Processes.Default.Streams.stdout += Testers.ExcludesExpression( + "ssntxnorder_verify.txn.start 0", 'should be nonzero') # and we receive the same number of transactions as we asked it to make -tr = Test.AddTestRun() -tr.DelayStart = 10 -tr.Processes.Default.Command = "traffic_ctl metric get ssntxnorder_verify.txn.start" -tr.Processes.Default.ReturnCode = 0 -tr.Processes.Default.Env = ts.Env -tr.Processes.Default.Streams.stdout = Testers.ContainsExpression( +tr.Processes.Default.Streams.stdout += Testers.ContainsExpression( "ssntxnorder_verify.txn.start {}".format(numberOfRequests), 'should be the number of transactions we made') tr.StillRunningAfter = ts tr.StillRunningAfter = server + diff --git a/tests/tools/plugins/continuations_verify.cc b/tests/tools/plugins/continuations_verify.cc index d87fd51..d452968 100644 --- a/tests/tools/plugins/continuations_verify.cc +++ b/tests/tools/plugins/continuations_verify.cc @@ -23,35 +23,46 @@ limitations under the License. */ -#define __STDC_FORMAT_MACROS 1 // for inttypes.h -#include <inttypes.h> // for PRIu64 -#include <iostream> #include <stdlib.h> // for abort -#include <string.h> // for NULL macro #include <ts/ts.h> // for debug -// TODO Is LIFECYCLE_MSG enabled in 6.2.0, or 7.0.0, might require push -// with version rework - // debug messages viewable by setting 'proxy.config.diags.debug.tags' // in 'records.config' // debug messages during one-time initialization static const char DEBUG_TAG_INIT[] = "continuations_verify.init"; +static const char DEBUG_TAG_MSG[] = "continuations_verify.msg"; +static const char DEBUG_TAG_HOOK[] = "continuations_verify.hook"; // plugin registration info static char plugin_name[] = "continuations_verify"; -static char vendor_name[] = "Yahoo! Inc."; -static char support_email[] = "ats-de...@yahoo-inc.com"; - -static TSMutex order_mutex_1; // lock on global data -static TSMutex order_mutex_2; // lock on global data +static char vendor_name[] = "apache"; +static char support_email[] = "shinr...@apache.org"; // Statistics provided by the plugin static int stat_ssn_close_1 = 0; // number of TS_HTTP_SSN_CLOSE hooks caught by the first continuation static int stat_ssn_close_2 = 0; // number of TS_HTTP_SSN_CLOSE hooks caught by the second continuation static int stat_txn_close_1 = 0; // number of TS_HTTP_TXN_CLOSE hooks caught by the first continuation static int stat_txn_close_2 = 0; // number of TS_HTTP_TXN_CLOSE hooks caught by the second continuation +static int stat_test_done = 0; // Incremented when receiving a traffic_ctl message + +static int +handle_msg(TSCont contp, TSEvent event, void *edata) +{ + if (event == TS_EVENT_LIFECYCLE_MSG) { // External trigger, such as traffic_ctl + TSDebug(DEBUG_TAG_MSG, "event TS_EVENT_LIFECYCLE_MSG"); + // Send to a ET net thread just to be sure. + // Turns out the msg is sent to a task thread, but task + // threads do not get their thread local copy of the stats + // merged in. So externally, test.done was stuck at 0 without + // the Schedule to a NET thread + TSContSchedule(contp, 0, TS_THREAD_POOL_NET); + } else { + TSDebug(DEBUG_TAG_MSG, "event %d", event); + TSStatIntIncrement(stat_test_done, 1); + } + return 0; +} /** This function is called on every request and logs session and transaction @@ -64,6 +75,8 @@ handle_order_1(TSCont contp, TSEvent event, void *edata) TSHttpSsn ssnp; // session data TSHttpTxn txnp; // transaction data + TSDebug(DEBUG_TAG_HOOK, "order_1 event %d", event); + // Find the event that happened switch (event) { case TS_EVENT_HTTP_TXN_CLOSE: // End of transaction @@ -92,6 +105,8 @@ handle_order_2(TSCont contp, TSEvent event, void *edata) TSHttpSsn ssnp; // session data TSHttpTxn txnp; // transaction data + TSDebug(DEBUG_TAG_HOOK, "order_2 event %d", event); + // Find the event that happened switch (event) { case TS_EVENT_HTTP_TXN_CLOSE: // End of transaction @@ -142,14 +157,11 @@ TSPluginInit(int argc, const char *argv[]) TSError("[%s] Plugin registration failed. \n", plugin_name); } - order_mutex_1 = TSMutexCreate(); - TSCont contp_1; - order_mutex_2 = TSMutexCreate(); - TSCont contp_2; + TSCont contp_1 = TSContCreate(handle_order_1, TSMutexCreate()); + TSCont contp_2 = TSContCreate(handle_order_2, TSMutexCreate()); + TSCont contp = TSContCreate(handle_msg, TSMutexCreate()); - contp_1 = TSContCreate(handle_order_1, order_mutex_1); - contp_2 = TSContCreate(handle_order_2, order_mutex_2); - if (contp_1 == NULL || contp_2 == NULL) { + if (contp_1 == nullptr || contp_2 == nullptr || contp == nullptr) { // Continuation initialization failed. Unrecoverable, report and exit. TSError("[%s] could not create continuation", plugin_name); abort(); @@ -164,6 +176,8 @@ TSPluginInit(int argc, const char *argv[]) TSStatCreate("continuations_verify.txn.close.2", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); stat_ssn_close_2 = TSStatCreate("continuations_verify.ssn.close.2", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); + stat_test_done = + TSStatCreate("continuations_verify.test.done", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); // Add all hooks. TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, contp_1); @@ -171,5 +185,8 @@ TSPluginInit(int argc, const char *argv[]) TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, contp_2); TSHttpHookAdd(TS_HTTP_SSN_CLOSE_HOOK, contp_2); + + // Respond to a traffic_ctl message + TSLifecycleHookAdd(TS_LIFECYCLE_MSG_HOOK, contp); } } diff --git a/tests/tools/plugins/ssntxnorder_verify.cc b/tests/tools/plugins/ssntxnorder_verify.cc index e2889d8..9ff16ae 100644 --- a/tests/tools/plugins/ssntxnorder_verify.cc +++ b/tests/tools/plugins/ssntxnorder_verify.cc @@ -22,19 +22,13 @@ See the License for the specific language governing permissions and limitations under the License. */ - -#define __STDC_FORMAT_MACROS 1 // for inttypes.h -#include <inttypes.h> // for PRIu64 #include <iostream> -#include <map> #include <set> #include <sstream> -#include <stdlib.h> // for abort -#include <string.h> // for NULL macro -#include <ts/ts.h> // for debug - -// TODO Is LIFECYCLE_MSG enabled in 6.2.0, or 7.0.0, might require push -// with version rework +#include <stdlib.h> // for abort +#include <ts/ts.h> // for debug +#include <inttypes.h> // for PRIu64 +#include <string.h> // debug messages viewable by setting 'proxy.config.diags.debug.tags' // in 'records.config' @@ -47,10 +41,8 @@ static const char DEBUG_TAG_HOOK[] = "ssntxnorder_verify.hook"; // plugin registration info static char plugin_name[] = "ssntxnorder_verify"; -static char vendor_name[] = "Yahoo! Inc."; -static char support_email[] = "ats-de...@yahoo-inc.com"; - -static TSMutex order_mutex; // lock on global data +static char vendor_name[] = "Apache"; +static char support_email[] = "shinr...@apache.org"; // List of started sessions, SSN_START seen, SSN_CLOSE not seen yet. static std::set<TSHttpSsn> started_ssns; @@ -84,6 +76,7 @@ static int stat_ssn_start = 0; // number of TS_HTTP_SSN_START hooks caught static int stat_txn_close = 0; // number of TS_HTTP_TXN_CLOSE hooks caught static int stat_txn_start = 0; // number of TS_HTTP_TXN_START hooks caught static int stat_err = 0; // number of inaccuracies encountered +static int stat_test_done = 0; // Set to 1 when the test is done // IPC information static char *ctl_tag = plugin_name; // name is a convenient identifier @@ -255,12 +248,18 @@ handle_order(TSCont contp, TSEvent event, void *edata) // Verify message is with the appropriate tag if (!strcmp(ctl_tag, msgp->tag) && strncmp(ctl_dump, reinterpret_cast<const char *>(msgp->data), strlen(ctl_dump)) == 0) { dump_tables(); + } else { + TSContSchedule(contp, 0, TS_THREAD_POOL_NET); } break; } #endif + case TS_EVENT_IMMEDIATE: + TSStatIntIncrement(stat_test_done, 1); + break; + // Just release the lock for all other states and do nothing default: break; @@ -297,11 +296,8 @@ TSPluginInit(int argc, const char *argv[]) TSError("[%s] Plugin registration failed. \n", plugin_name); } - order_mutex = TSMutexCreate(); - TSCont contp; - - contp = TSContCreate(handle_order, order_mutex); - if (contp == NULL) { + TSCont contp = TSContCreate(handle_order, TSMutexCreate()); + if (contp == nullptr) { // Continuation initialization failed. Unrecoverable, report and exit. TSError("[%s] could not create continuation", plugin_name); abort(); @@ -313,6 +309,7 @@ TSPluginInit(int argc, const char *argv[]) stat_txn_start = TSStatCreate("ssntxnorder_verify.txn.start", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); stat_txn_close = TSStatCreate("ssntxnorder_verify.txn.close", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); stat_err = TSStatCreate("ssntxnorder_verify.err", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); + stat_test_done = TSStatCreate("ssntxnorder_verify.test.done", TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); // Add all hooks. TSHttpHookAdd(TS_HTTP_SSN_START_HOOK, contp);