This is an automated email from the ASF dual-hosted git repository.
bneradt 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 39cf62e632 remap_action_to_add: helper script for new ACL actions
(#11583)
39cf62e632 is described below
commit 39cf62e6323c82fc4a90f713cbd1ccf5d5731401
Author: Brian Neradt <[email protected]>
AuthorDate: Thu Aug 1 14:53:47 2024 -0500
remap_action_to_add: helper script for new ACL actions (#11583)
This helper script converts 9.2.x @action=allow and @action=deny to
@action=add_allow and @action=add_deny in a provided input remap.config
filename. This prepares a 9.2.x remap.config file to be compatible with
the new 10.x ACL action behavior.
---
.../tools_tests/test_convert_remap_actions_to_10x | 99 +++++++++++
tools/remap/convert_remap_actions_to_10x | 196 +++++++++++++++++++++
2 files changed, 295 insertions(+)
diff --git a/tests/tools_tests/test_convert_remap_actions_to_10x
b/tests/tools_tests/test_convert_remap_actions_to_10x
new file mode 100755
index 0000000000..fe89033de3
--- /dev/null
+++ b/tests/tools_tests/test_convert_remap_actions_to_10x
@@ -0,0 +1,99 @@
+#!/usr/bin/env bash
+# Verify convert_remap_actions_to_10x 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.
+#
+
+tmp_dir=$(mktemp -d -t test_convert_remap_actions_to_10x_XXXXXXXX)
+start_remap_config=${tmp_dir}/remap.config
+start_remap_copy=${tmp_dir}/remap.orig
+expected_10x_config=${tmp_dir}/remap.gold
+backup_base=remap.config.convert_actions.bak
+script=$(realpath $(dirname $0)/../../tools/remap/convert_remap_actions_to_10x)
+
+fail()
+{
+ echo $1
+ exit 1
+}
+
+create_start_remap_config()
+{
+ cat > ${start_remap_config} <<EOF
+# Some comment.
+map http://one.com http://backend.one.com @action=allow @method=GET
+
+# Some comment with @action=allow
+map http:://two.com http://backend.two.com
+map http://three.com http://backend.three.com @action=deny @method=POST
+EOF
+}
+
+reset_test_directory()
+{
+ rm -rf ${tmp_dir}
+ mkdir -p ${tmp_dir}
+ cd ${tmp_dir}
+ create_start_remap_config
+ cp ${start_remap_config} ${start_remap_copy}
+ cp ${start_remap_config} ${expected_10x_config}
+ sed -i 's/@action=allow/@action=add_allow/g' ${expected_10x_config}
+ sed -i 's/@action=deny/@action=add_deny/g' ${expected_10x_config}
+}
+
+[ -f ${script} -a -x ${script} ] || fail "${script} is not an executable file."
+
+# Test a run without parameters. A backup file should be created.
+reset_test_directory
+${script} remap.config
+diff remap.config ${expected_10x_config} || fail "Unexpected content after
\"${script} remap.config\"."
+[ -f ${backup_base}.0 ] || fail "${backup_base}.0 does not exist."
+diff ${backup_base}.0 ${start_remap_copy} || fail "Wrong ${backup_base}.0
content."
+
+# Re-run on the same file that is now updated. A new backup should be created.
+${script} remap.config
+diff remap.config ${expected_10x_config} || fail "Unexpected content after
second \"${script} remap.config\"."
+[ -f ${backup_base}.1 ] || fail "${backup_base}.1 does not exist."
+diff ${backup_base}.1 ${expected_10x_config} || fail "Wrong ${backup_base}.1
content."
+
+# Verify that a third backup is created after re-applying the start content.
+cp ${start_remap_copy} remap.config
+${script} remap.config
+diff remap.config ${expected_10x_config} || fail "Unexpected content after
third \"${script} remap.config\"."
+[ -f ${backup_base}.2 ] || fail "${backup_base}.2 does not exist."
+diff ${backup_base}.2 ${start_remap_copy} || fail "Wrong ${backup_base}.2
content."
+
+# Test the --no-backup option.
+cp ${start_remap_copy} remap.config
+${script} remap.config -n
+diff remap.config ${expected_10x_config} || fail "Unexpected content after
\"${script} remap.config -n\"."
+[ -f ${backup_base}.3 ] && fail "${backup_base}.3 should not have been
created."
+
+# Test -o option.
+reset_test_directory
+${script} remap.config -o myremap.config
+diff remap.config ${start_remap_copy} || fail "Unexpected input content after
\"${script} remap.config -o myremap.config\""
+diff myremap.config ${expected_10x_config} || fail "Unexpected myremap.config
content."
+
+# Verify sane -o with -n behavior.
+reset_test_directory
+${script} remap.config -o myremap2.config -n
+diff remap.config ${start_remap_copy} || fail "Unexpected input content after
\"${script} remap.config -o myremap.config -n\""
+diff myremap2.config ${expected_10x_config} || fail "Unexpected
myremap2.config output."
+
+# Cleanup
+rm -rf ${tmp_dir}
diff --git a/tools/remap/convert_remap_actions_to_10x
b/tools/remap/convert_remap_actions_to_10x
new file mode 100755
index 0000000000..e1321529e7
--- /dev/null
+++ b/tools/remap/convert_remap_actions_to_10x
@@ -0,0 +1,196 @@
+#!/usr/bin/env python3
+'''Convert allow and deny @actions to add_allow and add_deny.'''
+#
+# 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.
+#
+
+# NOTE: there is a corresponding
+# tests/tools_tests/test_convert_remap_actions_to_10x test script that is
+# associated with this file. As this file is updated, please update that script
+# as appropriate and make sure it passes all tests.
+
+import argparse
+import os
+import shutil
+import sys
+import tempfile
+from typing import Optional, Tuple
+
+CLI_HELP_TEXT = '''Convert allow and deny @actions to add_allow and add_deny.
+
+This script is used to convert a pre-10.x remap.config file to be functionally
+equivalent to a 10.x remap.config file. In 10.x, the pre-10.x @action=allow and
+@action=deny actions are renamed to @action=add_allow and @action=add_deny,
+respectively. This script will convert the former to the latter. For further
+details, see the remap.config documentation.
+'''
+
+
+def convert_line(input_line: str) -> str:
+ '''Convert an input line to an output line.
+
+ This function modifies the input line for that of the new 9.2.x format.
+
+ :param input_line: The line to convert.
+ :return: The converted line.
+
+ :examples:
+ >>> convert_line('# nothing to convert')
+ '# nothing to convert'
+ >>> convert_line('map http://example.com http://backend.example.com')
+ 'map http://example.com http://backend.example.com'
+
+ >>> convert_line('map http://example.com http://backend.example.com
@action=allow @method=GET')
+ 'map http://example.com http://backend.example.com @action=add_allow
@method=GET'
+ >>> convert_line('map http://example.com http://backend.example.com
@action=deny @method=PUT')
+ 'map http://example.com http://backend.example.com @action=add_deny
@method=PUT'
+
+ # Verify that add_allow and add_deny are not converted.
+ >>> convert_line('map http://example.com http://backend.example.com
@action=add_deny @method=PUT')
+ 'map http://example.com http://backend.example.com @action=add_deny
@method=PUT'
+ >>> convert_line('map http://example.com http://backend.example.com
@action=add_allow @method=GET')
+ 'map http://example.com http://backend.example.com @action=add_allow
@method=GET'
+
+ # Comments should be converted too.
+ >>> convert_line('# Using @action=allow is nice.')
+ '# Using @action=add_allow is nice.'
+ >>> convert_line('# map http://example.com http://backend.example.com
@action=allow @method=GET')
+ '# map http://example.com http://backend.example.com @action=add_allow
@method=GET'
+ '''
+ output_line = input_line.replace('@action=allow', '@action=add_allow')
+ output_line = output_line.replace('@action=deny', '@action=add_deny')
+ return output_line
+
+
+def get_max_rotation_index(files: list[str], rotation_base: str) -> int:
+ '''Find the largest <num> in <rotation_base>.convert_actions.bak.<num>.
+
+ :param files: The list of files to search for backup files for.
+ :rotation base: The base backup prefix preceding the index.
+ :return: The index for the highest rotation index. -1 if no backup files
are found.
+
+ :examples:
+ >>> get_max_rotation_index(['base.0', 'base.1', 'base.2'], 'base.')
+ 2
+ >>> get_max_rotation_index([], 'base.')
+ -1
+ >>> get_max_rotation_index(['some', 'other', 'files'], 'base.')
+ -1
+ >>> get_max_rotation_index(['some', 'other', 'files', 'base.0', 'base.1'],
'base.')
+ 1
+ >>> get_max_rotation_index(['remap.config',
'remap.config.convert_actions.bak.0', 'remap.config.convert_actions.bak.1'],
'remap.config.convert_actions.bak.')
+ 1
+ '''
+ max_index = -1
+ for file in files:
+ if not file.startswith(rotation_base):
+ continue
+ suffix = file.split('.')[-1]
+ if not suffix.isnumeric():
+ continue
+ max_index = max(max_index, int(suffix))
+ return max_index
+
+
+def copy_input_file(input: str, /, no_backup: bool) -> str:
+ '''Create a copy of the input file.
+
+ :param input: The input file path from which to create a copy.
+
+ :param no_backup: Do not create a backup file from input. Rather simply
+ create a temporary file that will be later removed. Otherwise, create a
+ backup file of the format <input>.convert_actions.bak.<num>, where <num> is
+ one greater than the greatest value in the directory where @a input exists.
+
+ :return: The path to the copied file.
+ '''
+ if no_backup:
+ copy_path = tempfile.NamedTemporaryFile(delete=False).name
+ else:
+ input_dir = os.path.dirname(input)
+ input_dir = '.' if input_dir == '' else input_dir
+ rotation_base = f'{os.path.basename(input)}.convert_actions.bak'
+ max_index = get_max_rotation_index(os.listdir(input_dir),
rotation_base)
+ copy_path = os.path.join(input_dir, f'{rotation_base}.{max_index + 1}')
+ shutil.copyfile(input, copy_path)
+ return copy_path
+
+
+def prepare_files(input: str, output: Optional[str], /, no_backup: bool) ->
Tuple[str, str, bool]:
+ '''Prepare the input and output files.
+
+ :param input: The input file path.
+ :param output: The output file path.
+ :param no_backup: Do not create a backup of the input file when <input> is
modified.
+
+ :return: A tuple containing the input and output filenames, in that order,
and
+ a boolean telling the caller whether to delete the input file.
+ '''
+ if not os.path.exists(input):
+ raise FileNotFoundError(f'Input file does not exist: {input}')
+
+ if input == output or output is None:
+ input_path = copy_input_file(input, no_backup=no_backup)
+ output_path = input
+ delete_input_file = no_backup
+ else:
+ input_path = input
+ output_path = output
+ delete_input_file = False
+ return input_path, output_path, delete_input_file
+
+
+def parse_args() -> argparse.Namespace:
+ '''Parse command line arguments.
+
+ :return: The parsed arguments.
+ '''
+ parser = argparse.ArgumentParser(description=CLI_HELP_TEXT)
+ parser.add_argument('input', help='The input remap.config file to modify.')
+ parser.add_argument(
+ '-o',
+ '--output',
+ help='The output remap.config file. By default, output is written to
<input> and a backup file is generated.')
+ parser.add_argument('-n', '--no-backup', action='store_true', help='Do not
create a backup file.')
+ return parser.parse_args()
+
+
+def main() -> int:
+ '''Convert the input remap.config file.
+
+ :return: The process exit code.
+ '''
+
+ args = parse_args()
+ input_path, output_path, should_delete_input = prepare_files(args.input,
args.output, no_backup=args.no_backup)
+
+ try:
+ with open(input_path, 'r') as fd_in, open(output_path, 'w') as fd_out:
+ for input_line in fd_in:
+ output_line = convert_line(input_line)
+ fd_out.write(output_line)
+ finally:
+ if should_delete_input and os.path.exists(input_path):
+ os.remove(input_path)
+
+ return 0
+
+
+if __name__ == '__main__':
+ import doctest
+ doctest.testmod()
+ sys.exit(main())