diff --git a/ansible_deployer/command_line.py b/ansible_deployer/command_line.py index 35c90406..5d227521 100644 --- a/ansible_deployer/command_line.py +++ b/ansible_deployer/command_line.py @@ -2,6 +2,8 @@ import sys import os +import stat +import re import argparse import logging import datetime @@ -15,6 +17,7 @@ APP_CONF = "/etc/ansible-deploy/ansible-deploy.yaml" +CFG_PERMISSIONS = "0o644" def verify_subcommand(command: str): @@ -154,10 +157,11 @@ def validate_options(options: dict): def load_configuration_file(config_file: str): """Function responsible for single file loading and validation""" #TODO: Add verification of owner/group/persmissions + config_path = os.path.join(conf["global_paths"]["config_dir"], config_file) + check_cfg_permissions_and_owner(config_path) logger.debug("Loading :%s", config_file) - with open(os.path.join(conf["global_paths"]["config_dir"], config_file), "r", encoding="utf8") \ - as config_stream: + with open(config_path, "r", encoding="utf8") as config_stream: try: config = yaml.safe_load(config_stream) except yaml.YAMLError as e: @@ -180,6 +184,22 @@ def load_configuration_file(config_file: str): logger.debug("Loaded:\n%s", str(config)) return config +def check_cfg_permissions_and_owner(cfg_path: str): + """Function to verify permissions and owner for config files""" + stat_info = os.stat(cfg_path) + + if stat_info.st_uid == 0: + if oct(stat.S_IMODE(stat_info.st_mode)) == CFG_PERMISSIONS: + logger.debug("Correct permissions and owner for config file %s.", cfg_path) + else: + logger.error("File %s permissions are incorrect! Contact your sys admin.", cfg_path) + logger.error("Program will exit now.") + sys.exit(40) + else: + logger.error("File %s owner is not root! Contact your sys admin.", cfg_path) + logger.error("Program will exit now.") + sys.exit(41) + def load_configuration(): """Function responsible for reading configuration files and running a schema validator against it @@ -224,6 +244,32 @@ def validate_user_task(): """Function checking if user has rights to execute the task Rquired for: run""" +def validate_commit(options: dict, config: dict): + """Function to validate commit value against config and assign final value""" + if not options["commit"]: + commit_id = None + logger.debug("Using default commit.") + else: + for item in config["tasks"]["tasks"]: + if item["name"] == options["task"]: + for elem in item["allowed_for"]: + available_commits = elem.get("commit", []) + for commit in available_commits: + if re.fullmatch(commit, options["commit"]): + commit_id = commit + logger.debug("Using commit: %s .", commit_id) + break + else: + continue + break + else: + logger.error("Requested commit %s is not valid for task %s.", + options["commit"], options["task"]) + logger.error("Program will exit now.") + sys.exit(56) + + return commit_id + def validate_option_values_against_config(config: dict, options: dict): """ Function responsible for checking if option values match configuration @@ -253,10 +299,13 @@ def validate_option_values_against_config(config: dict, options: dict): logger.critical("Limit %s is not available for task %s.", options["limit"], options["task"]) sys.exit(54) + + selected_items["commit"] = validate_commit(options, config) + logger.debug("Completed validate_option_values_with_config") + #TODO: validate if user is allowed to use --commit #TODO: validate if user is allowed to execute the task on infra/stag pair #(validate_user_infra_stage(), validate_usr_task()) - logger.debug("Completed validate_option_values_with_config") return selected_items @@ -490,6 +539,7 @@ def get_all_user_groups(): def load_global_configuration(): """Function responsible for single file loading and validation""" + check_cfg_permissions_and_owner(APP_CONF) with open(APP_CONF, "r", encoding="utf8") as config_stream: try: config = yaml.safe_load(config_stream) diff --git a/etc/schema/tasks.yaml b/etc/schema/tasks.yaml index 0bcc326e..7e752afa 100644 --- a/etc/schema/tasks.yaml +++ b/etc/schema/tasks.yaml @@ -74,6 +74,11 @@ tasks: stages: type: list required: True + commit: + type: list + required: False + schema: + type: string allow_limit: type: boolean required: True diff --git a/etc/tasks.yaml b/etc/tasks.yaml index e37b2f9f..95c03dcf 100644 --- a/etc/tasks.yaml +++ b/etc/tasks.yaml @@ -29,6 +29,8 @@ tasks: - name: testInfra stages: - testing + commit: + - test_version - group: root infra: - name: testInfra @@ -36,6 +38,8 @@ tasks: - testing - prod - locked + commit: + - test_version allow_limit: True - name: task_empty @@ -135,3 +139,22 @@ tasks: - prod allow_limit: True + - name: task_with_commit + play_items: + - run_bin_true + allowed_for: + - group: root + infra: + - name: testInfra + stages: + - testing + - name: testInfra2 + stages: + - testing + commit: + - tags/v1.\d+ + - tags/v2.4 + - tags/v2.5.\d+ + - tags/v3.\d+.5 + - tags/v5.\d+ + allow_limit: True diff --git a/setup.py b/setup.py index 893afbdf..bc60e4af 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ def read(fname): # This call to setup() does all the work setup( name="ansible-deployer", - version="0.0.17", + version="0.0.18", description="Wrapper around ansible-playbook allowing configurable tasks and permissions", long_description=README, long_description_content_type="text/markdown", diff --git a/tests/02-checkrun.sh b/tests/02-checkrun.sh index dd5a9290..9d6ab17d 100755 --- a/tests/02-checkrun.sh +++ b/tests/02-checkrun.sh @@ -26,6 +26,16 @@ check_message_in_output "ansible-deployer run -t task_skipping -s prod -i testIn check_message_not_in_output "ansible-deployer run -t task_skipping -s testing -i testInfra2" "ran succesfully"# # Never skip check_message_not_in_output "ansible-deployer run -t task_skipping -s prod -i testInfra3" "\[INFO\]: Skipping playbook" +echo -e " ___ ____ _ _\n / _ \___ \ _____ _____ ___ _ _| |_(_) ___ _ __\n| | | |__) | _____ / _ \ \/ / _ \/ __| | | | __| |/ _ \| '_ \ \n| |_| / __/ |_____| | __/> < __/ (__| |_| | |_| | (_) | | | |\n \___/_____| \___/_/\_\___|\___|\__,_|\__|_|\___/|_| |_|\n\n _ _\n ___ ___ _ __ ___ _ __ ___ (_) |_ ___\n / __/ _ \| '_ \` _ \| '_ \` _ \| | __/ __|\n| (_| (_) | | | | | | | | | | | | |_\__ \ \n \___\___/|_| |_| |_|_| |_| |_|_|\__|___/\n" +# Check --commit option +check_run_ok "ansible-deployer run -t task_with_commit -s testing -i testInfra -c tags/v1.1" +check_run_ok "ansible-deployer run -t task_with_commit -s testing -i testInfra -c tags/v2.4" +check_run_ok "ansible-deployer run -t task_with_commit -s testing -i testInfra -c tags/v2.5.1" +check_run_ok "ansible-deployer run -t task_with_commit -s testing -i testInfra -c tags/v3.6.5" +check_message_in_output "ansible-deployer run -t task_with_commit -s testing -i testInfra -c tags/v1.0.1" '\[ERROR\]: Requested commit tags/v1.0.1 is not valid for task task_with_commit.' +check_message_in_output "ansible-deployer run -t task_with_commit -s testing -i testInfra -c tags/v2.1" '\[ERROR\]: Requested commit tags/v2.1 is not valid for task task_with_commit.' +check_message_in_output "ansible-deployer run -t task_with_commit -s testing -i testInfra -c tags/v3.6.6" '\[ERROR\]: Requested commit tags/v3.6.6 is not valid for task task_with_commit.' + echo -e " ___ ____ _ _\n / _ \___ \ _____ _____ ___ _ _| |_(_) ___ _ __\n | | | |__) | _____ / _ \ \/ / _ \/ __| | | | __| |/ _ \| '_ \ \n | |_| / __/ |_____| | __/> < __/ (__| |_| | |_| | (_) | | | |\n \___/_____| \___/_/\_\___|\___|\__,_|\__|_|\___/|_| |_|\n \n _ _\n ___ | |_| |__ ___ _ __ ___\n / _ \| __| '_ \ / _ \ '__/ __|\n | (_) | |_| | | | __/ | \__ \\n \___/ \__|_| |_|\___|_| |___/\n \n" # misc check_message_in_output 'ansible-deployer run -t task_empty -s testing -i testInfra' '\[CRITICAL\]: No playbooks found for requested task'