1'''
2'''
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#      http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing, software
14#  distributed under the License is distributed on an "AS IS" BASIS,
15#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16#  See the License for the specific language governing permissions and
17#  limitations under the License.
18
19import os
20
21Test.Summary = '''
22Test custom log file format
23'''
24Test.SkipUnless(
25    Condition.HasATSFeature('TS_HAS_PIPE_BUFFER_SIZE_CONFIG')
26)
27
28ts_counter = 1
29
30
31def get_ts(logging_config):
32    """
33    Create a Traffic Server process.
34    """
35    global ts_counter
36    ts = Test.MakeATSProcess("ts{}".format(ts_counter))
37    ts_counter += 1
38
39    ts.Disk.records_config.update({
40        'proxy.config.diags.debug.enabled': 1,
41        'proxy.config.diags.debug.tags': 'log-file',
42        'proxy.config.log.max_secs_per_buffer': 1,
43    })
44
45    # Since we're only verifying logs and not traffic, we don't need an origin
46    # server. The following will simply deny the requests and emit a log
47    # message.
48    ts.Disk.remap_config.AddLine(
49        'map / http://www.linkedin.com/ @action=deny'
50    )
51
52    ts.Disk.logging_yaml.AddLines(logging_config)
53
54    return ts
55
56
57#
58# Test 1: Default configured log pipe size.
59#
60tr = Test.AddTestRun()
61pipe_name = "default_pipe_size.pipe"
62ts = get_ts(
63    '''
64logging:
65  formats:
66    - name: custom
67      format: "%<hii> %<hiih>"
68  logs:
69    - filename: '{}'
70      mode: ascii_pipe
71      format: custom
72'''.format(pipe_name).split("\n")
73)
74
75pipe_path = os.path.join(ts.Variables.LOGDIR, pipe_name)
76
77ts.Streams.All += Testers.ContainsExpression(
78    "Created named pipe .*{}".format(pipe_name),
79    "Verify that the named pipe was created")
80
81ts.Streams.All += Testers.ContainsExpression(
82    "no readers for pipe .*{}".format(pipe_name),
83    "Verify that no readers for the pipe was detected.")
84
85ts.Streams.All += Testers.ExcludesExpression(
86    "New buffer size for pipe".format(pipe_name),
87    "Verify that the default pipe size was used.")
88
89curl = tr.Processes.Process("client_request", 'curl "http://127.0.0.1:{0}" --verbose'.format(
90    ts.Variables.port))
91
92reader_output = os.path.join(ts.Variables.LOGDIR, "reader_output")
93pipe_reader = tr.Processes.Process("pipe_reader", 'cat {} | tee {}'.format(pipe_path, reader_output))
94
95# Create an arbitrary process that just sleeps so that we can provide a wait
96# condition upon the log being emitted. The test won't wait this entire sleep
97# period, it will only poll until the FileContains Ready condition is
98# fulfilled.
99wait_for_log = tr.Processes.Process("wait_for_log", 'sleep 15')
100wait_for_log.Ready = When.FileContains(reader_output, '127.0.0.1')
101
102# This is an arbitrary Default process that will simply provide for
103# ordering of the Processes.
104tr.Processes.Default.Command = "echo 'Default place holder for process ordering.'"
105tr.Processes.Default.Return = 0
106
107# Process ordering.
108tr.Processes.Default.StartBefore(wait_for_log)
109wait_for_log.StartBefore(curl)
110curl.StartBefore(pipe_reader)
111pipe_reader.StartBefore(ts)
112
113
114#
115# Test 2: Change the log's buffer size.
116#
117tr = Test.AddTestRun()
118pipe_name = "change_pipe_size.pipe"
119# 64 KB is the default, so set the size larger than that to verify we can
120# increase the size.
121pipe_size = 75000
122ts = get_ts(
123    '''
124logging:
125  formats:
126    - name: custom
127      format: "%<hii> %<hiih>"
128  logs:
129    - filename: '{}'
130      mode: ascii_pipe
131      format: custom
132      pipe_buffer_size: {}
133      '''.format(pipe_name, pipe_size).split("\n")
134)
135
136pipe_path = os.path.join(ts.Variables.LOGDIR, pipe_name)
137
138ts.Streams.All += Testers.ContainsExpression(
139    "Created named pipe .*{}".format(pipe_name),
140    "Verify that the named pipe was created")
141
142ts.Streams.All += Testers.ContainsExpression(
143    "no readers for pipe .*{}".format(pipe_name),
144    "Verify that no readers for the pipe was detected.")
145
146ts.Streams.All += Testers.ContainsExpression(
147    "Previous buffer size for pipe .*{}".format(pipe_name),
148    "Verify that the named pipe's size was adjusted")
149
150# See fcntl:
151#   "Attempts to set the pipe capacity below the page size
152#    are silently rounded up to the page size."
153#
154# As a result of this, we cannot check that the pipe size is the exact size we
155# requested, but it should be at least that big. We use the
156# pipe_buffer_is_larger_than.py helper script to verify that the pipe grew in
157# size.
158ts.Streams.All += Testers.ContainsExpression(
159    "New buffer size for pipe.*{}".format(pipe_name),
160    "Verify that the named pipe's size was adjusted")
161buffer_verifier = "pipe_buffer_is_larger_than.py"
162tr.Setup.Copy(buffer_verifier)
163verify_buffer_size = tr.Processes.Process(
164    "verify_buffer_size",
165    "python3 {} {} {}".format(buffer_verifier, pipe_path, pipe_size))
166verify_buffer_size.Return = 0
167verify_buffer_size.Streams.All += Testers.ContainsExpression(
168    "Success",
169    "The buffer size verifier should report success.")
170
171curl = tr.Processes.Process("client_request", 'curl "http://127.0.0.1:{0}" --verbose'.format(
172    ts.Variables.port))
173
174reader_output = os.path.join(ts.Variables.LOGDIR, "reader_output")
175pipe_reader = tr.Processes.Process("pipe_reader", 'cat {} | tee {}'.format(pipe_path, reader_output))
176
177# Create an arbitrary process that just sleeps so that we can provide a wait
178# condition upon the log being emitted. The test won't wait this entire sleep
179# period, it will only poll until the FileContains Ready condition is
180# fulfilled.
181wait_for_log = tr.Processes.Process("wait_for_log", 'sleep 15')
182wait_for_log.Ready = When.FileContains(reader_output, '127.0.0.1')
183
184# This is an arbitrary Default process that will simply provide for
185# ordering of the Processes.
186tr.Processes.Default.Command = "echo 'Default place holder for process ordering.'"
187tr.Processes.Default.Return = 0
188
189
190# Process ordering.
191tr.Processes.Default.StartBefore(verify_buffer_size)
192verify_buffer_size.StartBefore(wait_for_log)
193wait_for_log.StartBefore(curl)
194curl.StartBefore(pipe_reader)
195pipe_reader.StartBefore(ts)
196