This is an automated email from the ASF dual-hosted git repository. zwoop pushed a commit to branch 9.0.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/9.0.x by this push: new a3d9bea Adding a log pipe buffer size test. a3d9bea is described below commit a3d9bead4101f54b750d9f8c8facc76315eaf1fb Author: bneradt <bner...@verizonmedia.com> AuthorDate: Fri Apr 3 19:49:38 2020 +0000 Adding a log pipe buffer size test. (cherry picked from commit 15e749ea4cf3b5e66ffd0ad53d20fa4c611e2ca5) --- proxy/logging/LogFile.cc | 10 +- src/traffic_layout/info.cc | 6 + tests/gold_tests/autest-site/when.test.ext | 36 ++++ tests/gold_tests/logging/log_pipe.test.py | 186 +++++++++++++++++++++ .../logging/pipe_buffer_is_larger_than.py | 69 ++++++++ 5 files changed, 302 insertions(+), 5 deletions(-) diff --git a/proxy/logging/LogFile.cc b/proxy/logging/LogFile.cc index 59c6bde..26fcca6 100644 --- a/proxy/logging/LogFile.cc +++ b/proxy/logging/LogFile.cc @@ -199,21 +199,21 @@ LogFile::open_file() if (m_pipe_buffer_size) { long pipe_size = (long)fcntl(m_fd, F_GETPIPE_SZ); if (pipe_size == -1) { - Error("get pipe size failed for pipe %s", m_name); + Error("Get pipe size failed for pipe %s: %s", m_name, strerror(errno)); } else { - Debug("log-file", "Default pipe size for pipe %s = %ld", m_name, pipe_size); + Debug("log-file", "Previous buffer size for pipe %s: %ld", m_name, pipe_size); } int ret = fcntl(m_fd, F_SETPIPE_SZ, m_pipe_buffer_size); if (ret == -1) { - Error("set pipe size failed for pipe %s", m_name); + Error("Set pipe size failed for pipe %s to size %d: %s", m_name, m_pipe_buffer_size, strerror(errno)); } pipe_size = (long)fcntl(m_fd, F_GETPIPE_SZ); if (pipe_size == -1) { - Error("get pipe size failed for pipe %s", m_name); + Error("Get pipe size after setting it failed for pipe %s: %s", m_name, strerror(errno)); } else { - Debug("log-file", "NEW pipe size for pipe %s = %ld", m_name, pipe_size); + Debug("log-file", "New buffer size for pipe %s: %ld", m_name, pipe_size); } } #endif // F_GETPIPE_SZ diff --git a/src/traffic_layout/info.cc b/src/traffic_layout/info.cc index 062aa7a..4cb6195 100644 --- a/src/traffic_layout/info.cc +++ b/src/traffic_layout/info.cc @@ -21,6 +21,7 @@ limitations under the License. */ +#include <fcntl.h> #include <openssl/crypto.h> #include "tscore/I_Layout.h" #include "tscore/Filenames.h" @@ -88,6 +89,11 @@ produce_features(bool json) #else print_feature("TS_HAS_BROTLI", 0, json); #endif +#ifdef F_GETPIPE_SZ + print_feature("TS_HAS_PIPE_BUFFER_SIZE_CONFIG", 1, json); +#else + print_feature("TS_HAS_PIPE_BUFFER_SIZE_CONFIG", 0, json); +#endif /* F_GETPIPE_SZ */ print_feature("TS_HAS_JEMALLOC", TS_HAS_JEMALLOC, json); print_feature("TS_HAS_TCMALLOC", TS_HAS_TCMALLOC, json); print_feature("TS_HAS_IN6_IS_ADDR_UNSPECIFIED", TS_HAS_IN6_IS_ADDR_UNSPECIFIED, json); diff --git a/tests/gold_tests/autest-site/when.test.ext b/tests/gold_tests/autest-site/when.test.ext new file mode 100644 index 0000000..d44c9ba --- /dev/null +++ b/tests/gold_tests/autest-site/when.test.ext @@ -0,0 +1,36 @@ +''' +When extensions. +''' +# 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. + +from autest.api import AddWhenFunction +import hosts.output as host + + +def FileContains(haystack, needle): + with open(haystack) as f: + result = needle in f.read() + + host.WriteDebug( + ['FileExists', 'when'], + "Testing for file content '{0}' in '{1}' : {2}".format( + needle, haystack, result)) + + return result + + +AddWhenFunction(FileContains) diff --git a/tests/gold_tests/logging/log_pipe.test.py b/tests/gold_tests/logging/log_pipe.test.py new file mode 100644 index 0000000..cdadbd0 --- /dev/null +++ b/tests/gold_tests/logging/log_pipe.test.py @@ -0,0 +1,186 @@ +''' +''' +# 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 + +Test.Summary = ''' +Test custom log file format +''' +Test.SkipUnless( + Condition.HasATSFeature('TS_HAS_PIPE_BUFFER_SIZE_CONFIG') +) + +ts_counter = 1 + + +def get_ts(logging_config): + """ + Create a Traffic Server process. + """ + global ts_counter + ts = Test.MakeATSProcess("ts{}".format(ts_counter)) + ts_counter += 1 + + ts.Disk.records_config.update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'log-file', + }) + + # Since we're only verifying logs and not traffic, we don't need an origin + # server. The following will simply deny the requests and emit a log + # message. + ts.Disk.remap_config.AddLine( + 'map / http://www.linkedin.com/ @action=deny' + ) + + ts.Disk.logging_yaml.AddLines(logging_config) + + return ts + + +# +# Test 1: Default configured log pipe size. +# +tr = Test.AddTestRun() +pipe_name = "default_pipe_size.pipe" +ts = get_ts( + ''' +logging: + formats: + - name: custom + format: "%<hii> %<hiih>" + logs: + - filename: '{}' + mode: ascii_pipe + format: custom +'''.format(pipe_name).split("\n") +) + +pipe_path = os.path.join(ts.Variables.LOGDIR, pipe_name) + +ts.Streams.All += Testers.ContainsExpression( + "Created named pipe .*{}".format(pipe_name), + "Verify that the named pipe was created") + +ts.Streams.All += Testers.ContainsExpression( + "no readers for pipe .*{}".format(pipe_name), + "Verify that no readers for the pipe was detected.") + +ts.Streams.All += Testers.ExcludesExpression( + "New buffer size for pipe".format(pipe_name), + "Verify that the default pipe size was used.") + +curl = tr.Processes.Process("client_request", 'curl "http://127.0.0.1:{0}" --verbose'.format( + ts.Variables.port)) + +reader_output = os.path.join(ts.Variables.LOGDIR, "reader_output") +pipe_reader = tr.Processes.Process("pipe_reader", 'cat {} | tee {}'.format(pipe_path, reader_output)) +curl_ready = tr.Processes.Process("curl_ready", 'sleep 30') +# In the AuTest environment, it can take more than 10 seconds for the log file +# to be created. +curl_ready.StartupTimeout = 30 +curl_ready.Ready = When.FileContains(reader_output, '127.0.0.1') + +tr.Processes.Default.Command = "sleep 10" +tr.Processes.Default.Return = 0 + +# Process ordering. +tr.Processes.Default.StartBefore(curl_ready) +curl_ready.StartBefore(curl) +curl.StartBefore(pipe_reader) +pipe_reader.StartBefore(ts) + + +# +# Test 2: Change the log's buffer size. +# +tr = Test.AddTestRun() +pipe_name = "change_pipe_size.pipe" +# 64 KB is the default, so set the size larger than that to verify we can +# increase the size. +pipe_size = 75000 +ts = get_ts( + ''' +logging: + formats: + - name: custom + format: "%<hii> %<hiih>" + logs: + - filename: '{}' + mode: ascii_pipe + format: custom + pipe_buffer_size: {} + '''.format(pipe_name, pipe_size).split("\n") +) + +pipe_path = os.path.join(ts.Variables.LOGDIR, pipe_name) + +ts.Streams.All += Testers.ContainsExpression( + "Created named pipe .*{}".format(pipe_name), + "Verify that the named pipe was created") + +ts.Streams.All += Testers.ContainsExpression( + "no readers for pipe .*{}".format(pipe_name), + "Verify that no readers for the pipe was detected.") + +ts.Streams.All += Testers.ContainsExpression( + "Previous buffer size for pipe .*{}".format(pipe_name), + "Verify that the named pipe's size was adjusted") + +# See fcntl: +# "Attempts to set the pipe capacity below the page size +# are silently rounded up to the page size." +# +# As a result of this, we cannot check that the pipe size is the exact size we +# requested, but it should be at least that big. We use the +# pipe_buffer_is_larger_than.py helper script to verify that the pipe grew in +# size. +ts.Streams.All += Testers.ContainsExpression( + "New buffer size for pipe.*{}".format(pipe_name), + "Verify that the named pipe's size was adjusted") +buffer_verifier = "pipe_buffer_is_larger_than.py" +tr.Setup.Copy(buffer_verifier) +verify_buffer_size = tr.Processes.Process( + "verify_buffer_size", + "python3 {} {} {}".format(buffer_verifier, pipe_path, pipe_size)) +verify_buffer_size.Return = 0 +verify_buffer_size.Streams.All += Testers.ContainsExpression( + "Success", + "The buffer size verifier should report success.") + +curl = tr.Processes.Process("client_request", 'curl "http://127.0.0.1:{0}" --verbose'.format( + ts.Variables.port)) + +reader_output = os.path.join(ts.Variables.LOGDIR, "reader_output") +pipe_reader = tr.Processes.Process("pipe_reader", 'cat {} | tee {}'.format(pipe_path, reader_output)) +curl_ready = tr.Processes.Process("curl_ready", 'sleep 30') +# In the AuTest environment, it can take more than 10 seconds for the log file +# to be created. +curl_ready.StartupTimeout = 30 +curl_ready.Ready = When.FileContains(reader_output, '127.0.0.1') + +tr.Processes.Default.Command = "sleep 10" +tr.Processes.Default.Return = 0 + + +# Process ordering. +tr.Processes.Default.StartBefore(verify_buffer_size) +verify_buffer_size.StartBefore(curl_ready) +curl_ready.StartBefore(curl) +curl.StartBefore(pipe_reader) +pipe_reader.StartBefore(ts) diff --git a/tests/gold_tests/logging/pipe_buffer_is_larger_than.py b/tests/gold_tests/logging/pipe_buffer_is_larger_than.py new file mode 100644 index 0000000..d598505 --- /dev/null +++ b/tests/gold_tests/logging/pipe_buffer_is_larger_than.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +''' +''' +# 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 argparse +import fcntl +import sys + +F_SETPIPE_SZ = 1031 # Linux 2.6.35+ +F_GETPIPE_SZ = 1032 # Linux 2.6.35+ + + +def parse_args(): + parser = parser = argparse.ArgumentParser( + description='Verify that a FIFO has a buffer of at least a certain size') + + parser.add_argument( + 'pipe_name', + help='The pipe name upon which to verify the size is large enough.') + + parser.add_argument( + 'minimum_buffer_size', + help='The minimu buffer size for the pipe to expect.') + + return parser.parse_args() + + +def test_fifo(fifo, minimum_buffer_size): + try: + fifo_fd = open(fifo, "rb+", buffering=0) + buffer_size = fcntl.fcntl(fifo_fd, F_GETPIPE_SZ) + + if buffer_size >= int(minimum_buffer_size): + print("Success. Size is: {} which is larger than: {}".format( + buffer_size, + minimum_buffer_size)) + return 0 + else: + print("Fail. Size is: {} which is smaller than: {}".format( + buffer_size, + minimum_buffer_size)) + return 1 + except Exception as e: + print("Unable to open fifo, error: {}".format(str(e))) + return 2 + + +def main(): + args = parse_args() + return test_fifo(args.pipe_name, args.minimum_buffer_size) + + +if __name__ == '__main__': + sys.exit(main())