blob: 5f94f3236df9a1c552ee3b342d6ec57b4d611dee [file] [log] [blame]
'''
Verify log file naming behavior.
'''
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import ports
Test.Summary = '''
Verify log file naming behavior.
'''
class LogFilenamesTest:
""" Common test configuration logic across the filename tests.
"""
# A counter for the ATS process to make each of them unique.
__ts_counter = 1
# The default log names for the various system logs.
default_log_data = {
'diags': 'diags.log',
'error': 'error.log',
'manager': 'manager.log'
}
def __init__(self, description, log_data=default_log_data):
''' Handle initialization tasks common across the tests.
Args:
description (str): The description of the test. This is passed to
the TestRun.
log_data (dict): The log name information passed to the
MakeATSProcess extension.
'''
self.__description = description
self.ts = self.__configure_traffic_manager(log_data)
self.tr = self.__configure_traffic_TestRun(description)
self.__configure_await_TestRun(self.sentinel_log_path)
def __configure_traffic_manager(self, log_data):
''' Common ATS configuration logic.
Args:
log_data (dict): The log name information passed to the
MakeATSProcess extension.
Return:
The traffic_manager process.
'''
self._ts_name = f"ts{LogFilenamesTest.__ts_counter}"
LogFilenamesTest.__ts_counter += 1
self.ts = Test.MakeATSProcess(self._ts_name, command="traffic_manager",
use_traffic_out=False, log_data=log_data)
self.ts.Disk.records_config.update({
'proxy.config.diags.debug.enabled': 0,
'proxy.config.diags.debug.tags': 'log',
'proxy.config.log.periodic_tasks_interval': 1,
})
# Intentionally retrieve a port that is closed, that is no server is
# listening on it. We will use this to attempt talking with a
# non-existent server, which will result in an error log entry.
ports.get_port(self.ts, 'closed_port')
self.ts.Disk.remap_config.AddLines([
f'map /server/down http://127.0.0.1:{self.ts.Variables.closed_port}',
'map / https://trafficserver.apache.org @action=deny',
])
# The following log is configured so that we can wait upon it being
# written so we know that ATS is done writing logs.
self.sentinel_log_filename = "sentinel"
self.ts.Disk.logging_yaml.AddLine(f'''
logging:
formats:
- name: url_and_return_code
format: "%<cqu>: %<pssc>"
logs:
- filename: {self.sentinel_log_filename}
format: url_and_return_code
''')
self.sentinel_log_path = os.path.join(
self.ts.Variables.LOGDIR,
f"{self.sentinel_log_filename}.log")
return self.ts
def __configure_await_TestRun(self, log_path):
''' Configure a TestRun that awaits upon the provided log_path to
exist.
Args:
log_path (str): The log file upon which we will wait.
'''
description = self.__description
tr = Test.AddTestRun(f'Awaiting log files to be written for: {description}')
condwait_path = os.path.join(Test.Variables.AtsTestToolsDir, 'condwait')
tr.Processes.Default.Command = f'{condwait_path} 60 1 -f {log_path}'
tr.Processes.Default.ReturnCode = 0
def __configure_traffic_TestRun(self, description):
''' Configure a TestRun to run the expected transactions.
Args:
description (str): The description to use for the TestRun.
'''
tr = Test.AddTestRun(f'Run traffic for: {description}')
tr.Processes.Default.Command = (
f'curl http://127.0.0.1:{self.ts.Variables.port}/some/path --verbose --next '
f'http://127.0.0.1:{self.ts.Variables.port}/server/down --verbose'
)
tr.Processes.Default.ReturnCode = 0
tr.Processes.Default.StartBefore(self.ts)
def configure_named_custom_log(self, custom_log_filename):
""" Configure ATS to log to the custom log file via logging.yaml.
Args:
custom_log_filename (str): The name of the custom log file to
configure.
Return:
The path to the configured custom log file.
"""
self.custom_log_filename = custom_log_filename
self.ts.Disk.logging_yaml.AddLine(f'''
- filename: {custom_log_filename}
format: url_and_return_code
''')
if custom_log_filename in ('stdout', 'stderr'):
self.custom_log_path = custom_log_filename
if custom_log_filename == 'stdout':
self.ts.Disk.custom_log = self.ts.Streams.stdout
else:
self.ts.Disk.custom_log = self.ts.Streams.stderr
else:
self.custom_log_path = os.path.join(
self.ts.Variables.LOGDIR,
f"{custom_log_filename}.log")
self.ts.Disk.File(self.custom_log_path, id="custom_log")
return self.custom_log_path
def set_log_expectations(self):
''' Configure sanity checks for each of the log types (manager, error,
etc.) to verify they are emitting the expected content.
'''
manager_path = self.ts.Disk.manager_log.AbsPath
self.ts.Disk.manager_log.Content += Testers.ContainsExpression(
"Launching ts process",
f"{manager_path} should contain traffic_manager log messages")
diags_path = self.ts.Disk.diags_log.AbsPath
self.ts.Disk.diags_log.Content += Testers.ContainsExpression(
"Traffic Server is fully initialized",
f"{diags_path} should contain traffic_server diag messages")
error_log_path = self.ts.Disk.error_log.AbsPath
self.ts.Disk.error_log.Content += Testers.ContainsExpression(
"CONNECT: attempt fail",
f"{error_log_path} should contain connection error messages")
custom_log_path = self.ts.Disk.custom_log.AbsPath
self.ts.Disk.custom_log.Content += Testers.ContainsExpression(
"https://trafficserver.apache.org/some/path: 403",
f"{custom_log_path} should contain the custom transaction logs")
class DefaultNamedTest(LogFilenamesTest):
''' Verify that if custom names are not configured, then the default
'diags.log', 'manager.log', and 'error.log' are written to.
'''
def __init__(self):
super().__init__('default log filename configuration')
# For these tests, more important than the listening port is the
# existence of the log files. In particular, it can take a few seconds
# for traffic_manager to open diags.log.
self.diags_log = self.ts.Disk.diags_log.AbsPath
self.ts.Ready = When.FileExists(self.diags_log)
self.configure_named_custom_log('my_custom_log')
self.set_log_expectations()
class CustomNamedTest(LogFilenamesTest):
''' Verify that the user can assign custom filenames to manager.log, etc.
'''
def __init__(self):
log_data = {
'diags': 'my_diags.log',
'error': 'my_error.log',
'manager': 'my_manager.log'
}
super().__init__('specify log filename configuration', log_data)
# Configure custom names for manager.log, etc.
self.ts.Disk.records_config.update({
'proxy.node.config.manager_log_filename': 'my_manager.log',
'proxy.config.diags.logfile.filename': 'my_diags.log',
'proxy.config.error.logfile.filename': 'my_error.log',
})
# For these tests, more important than the listening port is the
# existence of the log files. In particular, it can take a few seconds
# for traffic_manager to open diags.log.
self.diags_log = self.ts.Disk.diags_log.AbsPath
self.ts.Ready = When.FileExists(self.diags_log)
self.configure_named_custom_log('my_custom_log')
self.set_log_expectations()
class stdoutTest(LogFilenamesTest):
''' Verify that we can configure the logs to go to stdout.
'''
def __init__(self):
log_data = {
'diags': 'stdout',
'error': 'stdout',
'manager': 'stdout'
}
super().__init__('specify logs to go to stdout', log_data)
# Configure custom names for manager.log, etc.
self.ts.Disk.records_config.update({
'proxy.node.config.manager_log_filename': 'stdout',
'proxy.config.diags.logfile.filename': 'stdout',
'proxy.config.error.logfile.filename': 'stdout',
})
self.configure_named_custom_log('stdout')
# The diags.log file will not be created since we are piping to stdout.
# Therefore, simply wait upon the port being open.
self.ts.Ready = When.PortOpen(self.ts.Variables.port)
self.set_log_expectations()
class stderrTest(LogFilenamesTest):
'''
Verify that we can configure the logs to go to stderr.
'''
def __init__(self):
log_data = {
'diags': 'stderr',
'error': 'stderr',
'manager': 'stderr'
}
super().__init__('specify logs to go to stderr', log_data)
# Configure custom names for manager.log, etc.
self.ts.Disk.records_config.update({
'proxy.node.config.manager_log_filename': 'stderr',
'proxy.config.diags.logfile.filename': 'stderr',
'proxy.config.error.logfile.filename': 'stderr',
})
self.configure_named_custom_log('stderr')
# The diags.log file will not be created since we are piping to stderr.
# Therefore, simply wait upon the port being open.
self.ts.Ready = When.PortOpen(self.ts.Variables.port)
self.set_log_expectations()
#
# Run the tests.
#
DefaultNamedTest()
CustomNamedTest()
stdoutTest()
# The following stderr test can be run successfully by hand using the replay
# files from the sandbox. All the expected output goes to stderr. However, for
# some reason during the AuTest run, the stderr output stops emitting after the
# logging.yaml file is parsed. This is left here for now because it is valuable
# for use during development, but it is left commented out so that it doesn't
# produce the false failure in CI and developer test runs.
# stderrTest()