Skip to content

Commit 15e879b

Browse files
bneradtbneradt
andauthored
sigusr2 autest: simplify Process and Ready objects (#13021)
Rewrite the sigusr2 autest to drive each scenario from a single shell helper script instead of a chain of AuTest Processes. This removes the Ready-condition races from the old test, keeps the signal and log rotation ordering in one place, and makes the log file expectations easier to follow. This should address flakiness in the test that has been seen in CI. Co-authored-by: bneradt <[email protected]>
1 parent 4a5f558 commit 15e879b

3 files changed

Lines changed: 281 additions & 162 deletions

File tree

tests/gold_tests/logging/sigusr2.test.py

Lines changed: 101 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818
# limitations under the License.
1919

2020
import os
21+
import shlex
2122
import sys
2223

2324
TS_PID_SCRIPT = 'ts_process_handler.py'
25+
ROTATE_DIAGS_SCRIPT = 'sigusr2_rotate_diags.sh'
26+
ROTATE_CUSTOM_LOG_SCRIPT = 'sigusr2_rotate_custom_log.sh'
2427

2528

2629
class Sigusr2Test:
@@ -29,17 +32,29 @@ class Sigusr2Test:
2932
"""
3033

3134
__ts_counter = 1
32-
__server = None
33-
34-
def __init__(self):
35-
self.server = self.__configure_server()
36-
self.ts = self.__configure_traffic_server()
37-
38-
def __configure_traffic_server(self):
39-
self._ts_name = "sigusr2_ts{}".format(Sigusr2Test.__ts_counter)
40-
Sigusr2Test.__ts_counter += 1
41-
self.ts = Test.MakeATSProcess(self._ts_name)
42-
self.ts.Disk.records_config.update(
35+
__server_counter = 1
36+
37+
@classmethod
38+
def _next_ts_name(cls):
39+
ts_name = f"sigusr2_ts{cls.__ts_counter}"
40+
cls.__ts_counter += 1
41+
return ts_name
42+
43+
@classmethod
44+
def _next_server_name(cls):
45+
server_name = f"sigusr2_server{cls.__server_counter}"
46+
cls.__server_counter += 1
47+
return server_name
48+
49+
@staticmethod
50+
def _make_script_command(script_name, *args):
51+
quoted_args = ' '.join(shlex.quote(str(arg)) for arg in args)
52+
return f"bash ./{script_name} {quoted_args}"
53+
54+
def _configure_traffic_server(self, tr):
55+
ts_name = self._next_ts_name()
56+
ts = tr.MakeATSProcess(ts_name)
57+
ts.Disk.records_config.update(
4358
{
4459
'proxy.config.http.wait_for_cache': 1,
4560
'proxy.config.diags.debug.enabled': 1,
@@ -50,18 +65,54 @@ def __configure_traffic_server(self):
5065
'proxy.config.log.rolling_enabled': 0,
5166
'proxy.config.log.auto_delete_rolled_files': 0,
5267
})
68+
return ts, ts_name
5369

54-
self.diags_log = self.ts.Disk.diags_log.AbsPath
70+
def _configure_server(self):
71+
server = Test.MakeOriginServer(self._next_server_name())
5572

56-
# Add content handles for the rotated logs.
57-
self.rotated_diags_log = self.diags_log + "_old"
58-
self.ts.Disk.File(self.rotated_diags_log, id="diags_log_old")
73+
for path in ['/first', '/second', '/third']:
74+
request_header = {
75+
'headers': f'GET {path} HTTP/1.1\r\nHost: does.not.matter\r\n\r\n',
76+
'timestamp': '1469733493.993',
77+
'body': ''
78+
}
79+
response_header = {
80+
'headers': 'HTTP/1.1 200 OK\r\n'
81+
'Connection: close\r\n'
82+
'Cache-control: max-age=85000\r\n\r\n',
83+
'timestamp': '1469733493.993',
84+
'body': 'xxx'
85+
}
86+
server.addResponse('sessionlog.json', request_header, response_header)
87+
88+
return server
5989

60-
self.log_dir = os.path.dirname(self.diags_log)
90+
def add_system_log_test(self):
91+
tr = Test.AddTestRun('Verify system logs can be rotated')
92+
ts, ts_name = self._configure_traffic_server(tr)
6193

62-
self.ts.Disk.remap_config.AddLine(
63-
'map http://127.0.0.1:{0} http://127.0.0.1:{1}'.format(self.ts.Variables.port, self.server.Variables.Port))
64-
self.ts.Disk.logging_yaml.AddLine(
94+
rotated_diags_log = f'{ts.Disk.diags_log.AbsPath}_old'
95+
ts.Disk.File(rotated_diags_log, id='diags_log_old')
96+
97+
tr.Processes.Default.Command = self._make_script_command(
98+
ROTATE_DIAGS_SCRIPT, sys.executable, f'./{TS_PID_SCRIPT}', ts_name, ts.Disk.diags_log.AbsPath, rotated_diags_log)
99+
tr.Processes.Default.ReturnCode = 0
100+
tr.Processes.Default.StartBefore(ts)
101+
102+
ts.Disk.diags_log.Content += Testers.ExcludesExpression(
103+
'traffic server running', 'The new diags.log should not reference the running traffic server')
104+
ts.Disk.diags_log.Content += Testers.ContainsExpression(
105+
'Reseated diags.log', 'The new diags.log should indicate the newly opened diags.log')
106+
ts.Disk.diags_log_old.Content += Testers.ContainsExpression(
107+
'traffic server running', 'The rotated diags.log should keep the original startup message')
108+
109+
def add_configured_log_test(self):
110+
tr = Test.AddTestRun('Verify yaml.log logs can be rotated')
111+
ts, ts_name = self._configure_traffic_server(tr)
112+
server = self._configure_server()
113+
114+
ts.Disk.remap_config.AddLine(f'map http://127.0.0.1:{ts.Variables.port} http://127.0.0.1:{server.Variables.Port}')
115+
ts.Disk.logging_yaml.AddLine(
65116
'''
66117
logging:
67118
formats:
@@ -71,155 +122,43 @@ def __configure_traffic_server(self):
71122
- filename: test_rotation
72123
format: has_path
73124
''')
74-
self.configured_log = os.path.join(self.log_dir, "test_rotation.log")
75-
self.ts.Disk.File(self.configured_log, id="configured_log")
76-
77-
self.rotated_configured_log = self.configured_log + "_old"
78-
self.ts.Disk.File(self.rotated_configured_log, id="configured_log_old")
79-
self.ts.StartBefore(self.server)
80-
return self.ts
81-
82-
def __configure_server(self):
83-
if Sigusr2Test.__server:
84-
return Sigusr2Test.__server
85-
server = Test.MakeOriginServer("server")
86-
Sigusr2Test.__server = server
87-
for path in ['/first', '/second', '/third']:
88-
request_header = {
89-
"headers": "GET {} HTTP/1.1\r\n"
90-
"Host: does.not.matter\r\n\r\n".format(path),
91-
"timestamp": "1469733493.993",
92-
"body": ""
93-
}
94-
response_header = {
95-
"headers": "HTTP/1.1 200 OK\r\n"
96-
"Connection: close\r\n"
97-
"Cache-control: max-age=85000\r\n\r\n",
98-
"timestamp": "1469733493.993",
99-
"body": "xxx"
100-
}
101-
server.addResponse("sessionlog.json", request_header, response_header)
102-
return server
103-
104-
def get_sigusr2_signal_command(self):
105-
"""
106-
Return the command that will send a USR2 signal to the traffic server
107-
process.
108-
"""
109-
return (f"{sys.executable} {TS_PID_SCRIPT} "
110-
f"--signal SIGUSR2 {self._ts_name}")
111-
112-
113-
Test.Summary = '''
114-
Verify support of external log rotation via SIGUSR2.
115-
'''
116125

117-
Test.Setup.CopyAs(TS_PID_SCRIPT, Test.RunDirectory)
126+
configured_log = os.path.join(ts.Variables.LOGDIR, 'test_rotation.log')
127+
ts.Disk.File(configured_log, id='configured_log')
118128

119-
#
120-
# Test 1: Verify SIGUSR2 behavior for system logs.
121-
#
122-
tr1 = Test.AddTestRun("Verify system logs can be rotated")
129+
rotated_configured_log = f'{configured_log}_old'
130+
ts.Disk.File(rotated_configured_log, id='configured_log_old')
123131

124-
# Configure Server.
125-
diags_test = Sigusr2Test()
132+
ts.StartBefore(server)
133+
tr.Processes.Default.Command = self._make_script_command(
134+
ROTATE_CUSTOM_LOG_SCRIPT, sys.executable, f'./{TS_PID_SCRIPT}', ts_name, ts.Variables.port, configured_log,
135+
rotated_configured_log)
136+
tr.Processes.Default.ReturnCode = 0
137+
tr.Processes.Default.StartBefore(ts)
126138

127-
# Configure our rotation processes.
128-
rotate_diags_log = tr1.Processes.Process("rotate_diags_log", "mv {} {}".format(diags_test.diags_log, diags_test.rotated_diags_log))
139+
ts.Disk.configured_log.Content += Testers.ExcludesExpression(
140+
'/first', 'The new test_rotation.log should not have the first GET retrieval in it.')
141+
ts.Disk.configured_log.Content += Testers.ExcludesExpression(
142+
'/second', 'The new test_rotation.log should not have the second GET retrieval in it.')
143+
ts.Disk.configured_log.Content += Testers.ContainsExpression(
144+
'/third', 'The new test_rotation.log should have the third GET retrieval in it.')
129145

130-
# Configure the signaling of SIGUSR2 to traffic_server.
131-
tr1.Processes.Default.Command = diags_test.get_sigusr2_signal_command()
132-
tr1.Processes.Default.Return = 0
133-
tr1.Processes.Default.Ready = When.FileExists(diags_test.diags_log)
146+
ts.Disk.configured_log_old.Content += Testers.ContainsExpression(
147+
'/first', 'test_rotation.log_old should have the first GET retrieval in it.')
148+
ts.Disk.configured_log_old.Content += Testers.ContainsExpression(
149+
'/second', 'test_rotation.log_old should have the second GET retrieval in it.')
150+
ts.Disk.configured_log_old.Content += Testers.ExcludesExpression(
151+
'/third', 'test_rotation.log_old should not have the third GET retrieval in it.')
134152

135-
# Configure process order.
136-
tr1.Processes.Default.StartBefore(rotate_diags_log)
137-
rotate_diags_log.StartBefore(diags_test.ts)
138-
tr1.StillRunningAfter = diags_test.ts
139-
tr1.StillRunningAfter = diags_test.server
140153

141-
# diags.log should have been rotated. The old one had the reference to traffic
142-
# server running, this new one shouldn't. But it should indicate that the new
143-
# diags.log was opened.
144-
diags_test.ts.Disk.diags_log.Content += Testers.ExcludesExpression(
145-
"traffic server running", "The new diags.log should not reference the running traffic server")
154+
Test.Summary = '''
155+
Verify support of external log rotation via SIGUSR2.
156+
'''
146157

147-
diags_test.ts.Disk.diags_log.Content += Testers.ContainsExpression(
148-
"Reseated diags.log", "The new diags.log should indicate the newly opened diags.log")
158+
Test.Setup.Copy(TS_PID_SCRIPT)
159+
Test.Setup.Copy(ROTATE_DIAGS_SCRIPT)
160+
Test.Setup.Copy(ROTATE_CUSTOM_LOG_SCRIPT)
149161

150-
#
151-
# Test 2: Verify SIGUSR2 isn't needed for rotated configured logs.
152-
#
153-
tr2 = Test.AddTestRun("Verify yaml.log logs can be rotated")
154-
configured_test = Sigusr2Test()
155-
156-
first_curl = tr2.Processes.Process(
157-
"first_curl", 'curl "http://127.0.0.1:{0}/first" --verbose'.format(configured_test.ts.Variables.port))
158-
# Note that for each of these processes, aside from the final Default one, they
159-
# are all treated like long-running servers to AuTest. Thus the long sleeps
160-
# only allow us to wait until the logs get populated with the desired content,
161-
# the test will not wait the entire time for them to complete.
162-
first_curl_ready = tr2.Processes.Process("first_curl_ready", 'sleep 30')
163-
# In the autest environment, it can take more than 10 seconds for the log file to be created.
164-
first_curl_ready.StartupTimeout = 30
165-
first_curl_ready.Ready = When.FileContains(configured_test.configured_log, "/first")
166-
167-
rotate_log = tr2.Processes.Process(
168-
"rotate_log_file", "mv {} {}".format(configured_test.configured_log, configured_test.rotated_configured_log))
169-
170-
second_curl = tr2.Processes.Process(
171-
"second_curl", 'curl "http://127.0.0.1:{0}/second" --verbose'.format(configured_test.ts.Variables.port))
172-
173-
second_curl_ready = tr2.Processes.Process("second_curl_ready", 'sleep 30')
174-
# In the autest environment, it can take more than 10 seconds for the log file to be created.
175-
second_curl_ready.StartupTimeout = 30
176-
second_curl_ready.Ready = When.FileContains(configured_test.rotated_configured_log, "/second")
177-
178-
send_pkill = tr2.Processes.Process("Send_SIGUSR2", configured_test.get_sigusr2_signal_command())
179-
send_pkill_ready = tr2.Processes.Process("send_pkill_ready", 'sleep 30')
180-
send_pkill_ready.StartupTimeout = 30
181-
send_pkill_ready.Ready = When.FileExists(configured_test.configured_log)
182-
183-
third_curl = tr2.Processes.Process(
184-
"third_curl", 'curl "http://127.0.0.1:{0}/third" --verbose'.format(configured_test.ts.Variables.port))
185-
third_curl_ready = tr2.Processes.Process("third_curl_ready", 'sleep 30')
186-
# In the autest environment, it can take more than 10 seconds for the log file to be created.
187-
third_curl_ready.StartupTimeout = 30
188-
third_curl_ready.Ready = When.FileContains(configured_test.configured_log, "/third")
189-
190-
tr2.Processes.Default.Command = "echo waiting for test processes to be done"
191-
tr2.Processes.Default.Return = 0
192-
193-
# Configure process order:
194-
# 1. curl /first. The entry should be logged to current log which will be _old.
195-
# 2. mv the log to _old.
196-
# 3. curl /second. The entry should end up in _old log.
197-
# 4. Send a SIGUSR2 to traffic_server. The log should be recreated.
198-
# 5. curl /third. The entry should end up in the new, non-old, log file.
199-
#
200-
tr2.Processes.Default.StartBefore(third_curl_ready)
201-
third_curl_ready.StartBefore(third_curl)
202-
third_curl.StartBefore(send_pkill_ready)
203-
send_pkill_ready.StartBefore(send_pkill)
204-
send_pkill.StartBefore(second_curl_ready)
205-
second_curl_ready.StartBefore(second_curl)
206-
second_curl.StartBefore(rotate_log)
207-
rotate_log.StartBefore(first_curl_ready)
208-
first_curl_ready.StartBefore(first_curl)
209-
first_curl.StartBefore(configured_test.ts)
210-
tr2.StillRunningAfter = configured_test.ts
211-
212-
# Verify that the logs are in the correct files.
213-
configured_test.ts.Disk.configured_log.Content += Testers.ExcludesExpression(
214-
"/first", "The new test_rotation.log should not have the first GET retrieval in it.")
215-
configured_test.ts.Disk.configured_log.Content += Testers.ExcludesExpression(
216-
"/second", "The new test_rotation.log should not have the second GET retrieval in it.")
217-
configured_test.ts.Disk.configured_log.Content += Testers.ContainsExpression(
218-
"/third", "The new test_rotation.log should have the third GET retrieval in it.")
219-
220-
configured_test.ts.Disk.configured_log_old.Content += Testers.ContainsExpression(
221-
"/first", "test_rotation.log_old should have the first GET retrieval in it.")
222-
configured_test.ts.Disk.configured_log_old.Content += Testers.ContainsExpression(
223-
"/second", "test_rotation.log_old should have the second GET retrieval in it.")
224-
configured_test.ts.Disk.configured_log_old.Content += Testers.ExcludesExpression(
225-
"/third", "test_rotation.log_old should not have the third GET retrieval in it.")
162+
sigusr2_test = Sigusr2Test()
163+
sigusr2_test.add_system_log_test()
164+
sigusr2_test.add_configured_log_test()

0 commit comments

Comments
 (0)