162 lines
6.6 KiB
Python
162 lines
6.6 KiB
Python
|
import argparse
|
||
|
import regex
|
||
|
import sys
|
||
|
import yaml
|
||
|
|
||
|
parser = argparse.ArgumentParser(description='Optimizes the playbook based on enabled components found in vars.yml files')
|
||
|
parser.add_argument('--vars-paths', help='Path to vars.yml configuration files to process', required=True)
|
||
|
parser.add_argument('--src-requirements-yml-path', help='Path to source requirements.yml file with all role definitions', required=True)
|
||
|
parser.add_argument('--src-setup-yml-path', help='Path to source setup.yml file', required=True)
|
||
|
parser.add_argument('--src-group-vars-yml-path', help='Path to source group vars file', required=True)
|
||
|
parser.add_argument('--dst-requirements-yml-path', help='Path to destination requirements.yml file, where role definitions will be saved', required=True)
|
||
|
parser.add_argument('--dst-setup-yml-path', help='Path to destination setup.yml file', required=True)
|
||
|
parser.add_argument('--dst-group-vars-yml-path', help='Path to destination group vars file', required=True)
|
||
|
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
def load_combined_variable_names_from_files(vars_yml_file_paths):
|
||
|
variable_names = set({})
|
||
|
for vars_path in vars_yml_file_paths:
|
||
|
with open(vars_path, 'r') as file:
|
||
|
yaml_data = yaml.safe_load(file)
|
||
|
|
||
|
variable_names = variable_names | set(yaml_data.keys())
|
||
|
return variable_names
|
||
|
|
||
|
def load_yaml_file(path):
|
||
|
with open(path, 'r') as file:
|
||
|
return yaml.safe_load(file)
|
||
|
|
||
|
def is_role_definition_in_use(role_definition, used_variable_names):
|
||
|
for variable_name in used_variable_names:
|
||
|
if 'activation_prefix' in role_definition:
|
||
|
if role_definition['activation_prefix'] == '':
|
||
|
# Special value indicating "always activate".
|
||
|
# We don't really need this dedicated if, but it's more obvious with it.
|
||
|
return True
|
||
|
if variable_name.startswith(role_definition['activation_prefix']):
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def write_yaml_to_file(definitions, path):
|
||
|
with open(path, 'w') as file:
|
||
|
yaml.dump(definitions, file)
|
||
|
|
||
|
def read_file(path):
|
||
|
with open(path, 'r') as file:
|
||
|
return file.read()
|
||
|
|
||
|
def write_to_file(contents, path):
|
||
|
with open(path, 'w') as file:
|
||
|
file.write(contents)
|
||
|
|
||
|
# Matches the beginning of role-specific blocks.
|
||
|
# Example: `# role-specific:playbook_help`
|
||
|
regex_role_specific_block_start = regex.compile('^\s*#\s*role-specific\:\s*([^\s]+)$')
|
||
|
|
||
|
# Matches the end of role-specific blocks.
|
||
|
# Example: `# /role-specific:playbook_help`
|
||
|
regex_role_specific_block_end = regex.compile('^\s*#\s*/role-specific\:\s*([^\s]+)$')
|
||
|
|
||
|
def process_file_contents(file_name, enabled_role_names, known_role_names):
|
||
|
contents = read_file(file_name)
|
||
|
|
||
|
lines_preserved = []
|
||
|
role_specific_stack = []
|
||
|
|
||
|
for line_number, line in enumerate(contents.split("\n")):
|
||
|
# Stage 1: looking for a role-specific starting block
|
||
|
start_role_matches = regex_role_specific_block_start.match(line)
|
||
|
if start_role_matches is not None:
|
||
|
role_name = start_role_matches.group(1)
|
||
|
if role_name not in known_role_names:
|
||
|
raise Exception('Found start block for role {0} on line {1} in file {2}, but it is not a known role name found among: {3}'.format(
|
||
|
role_name,
|
||
|
line_number,
|
||
|
file_name,
|
||
|
known_role_names,
|
||
|
))
|
||
|
role_specific_stack.append(role_name)
|
||
|
continue
|
||
|
|
||
|
# Stage 2: looking for role-specific closing blocks
|
||
|
end_role_matches = regex_role_specific_block_end.match(line)
|
||
|
if end_role_matches is not None:
|
||
|
role_name = end_role_matches.group(1)
|
||
|
if role_name not in known_role_names:
|
||
|
raise Exception('Found end block for role {0} on line {1} in file {2}, but it is not a known role name found among: {3}'.format(
|
||
|
role_name,
|
||
|
line_number,
|
||
|
file_name,
|
||
|
known_role_names,
|
||
|
))
|
||
|
|
||
|
if len(role_specific_stack) == 0:
|
||
|
raise Exception('Found end block for role {0} on line {1} in file {2}, but there is no opening statement for it'.format(
|
||
|
role_name,
|
||
|
line_number,
|
||
|
file_name,
|
||
|
))
|
||
|
|
||
|
last_role_name = role_specific_stack[len(role_specific_stack) - 1]
|
||
|
if role_name != last_role_name:
|
||
|
raise Exception('Found end block for role {0} on line {1} in file {2}, but the last starting block was for role {3}'.format(
|
||
|
role_name,
|
||
|
line_number,
|
||
|
file_name,
|
||
|
last_role_name,
|
||
|
))
|
||
|
|
||
|
role_specific_stack.pop()
|
||
|
|
||
|
continue
|
||
|
|
||
|
# Stage 3: regular line
|
||
|
all_roles_allowed = True
|
||
|
for role_name in role_specific_stack:
|
||
|
if role_name not in enabled_role_names:
|
||
|
all_roles_allowed = False
|
||
|
break
|
||
|
|
||
|
if all_roles_allowed:
|
||
|
lines_preserved.append(line)
|
||
|
|
||
|
if len(role_specific_stack) != 0:
|
||
|
raise Exception('Expected one or more closing block for role-specific tags in file {0}: {1}'.format(file_name, role_specific_stack))
|
||
|
|
||
|
lines_final = []
|
||
|
sequential_blank_lines_count = 0
|
||
|
for line in lines_preserved:
|
||
|
if line != "":
|
||
|
lines_final.append(line)
|
||
|
sequential_blank_lines_count = 0
|
||
|
continue
|
||
|
|
||
|
if sequential_blank_lines_count <= 1:
|
||
|
lines_final.append(line)
|
||
|
sequential_blank_lines_count += 1
|
||
|
continue
|
||
|
|
||
|
return "\n".join(lines_final)
|
||
|
|
||
|
vars_paths = args.vars_paths.split(' ')
|
||
|
used_variable_names = load_combined_variable_names_from_files(vars_paths)
|
||
|
|
||
|
all_role_definitions = load_yaml_file(args.src_requirements_yml_path)
|
||
|
|
||
|
enabled_role_definitions = []
|
||
|
for role_definition in all_role_definitions:
|
||
|
if is_role_definition_in_use(role_definition, used_variable_names):
|
||
|
enabled_role_definitions.append(role_definition)
|
||
|
|
||
|
write_yaml_to_file(enabled_role_definitions, args.dst_requirements_yml_path)
|
||
|
|
||
|
known_role_names = tuple(map(lambda definition: definition['name'], all_role_definitions))
|
||
|
enabled_role_names = tuple(map(lambda definition: definition['name'], enabled_role_definitions))
|
||
|
|
||
|
setup_yml_processed = process_file_contents(args.src_setup_yml_path, enabled_role_names, known_role_names)
|
||
|
write_to_file(setup_yml_processed, args.dst_setup_yml_path)
|
||
|
|
||
|
group_vars_yml_processed = process_file_contents(args.src_group_vars_yml_path, enabled_role_names, known_role_names)
|
||
|
write_to_file(group_vars_yml_processed, args.dst_group_vars_yml_path)
|