From 9a6a7879b7f50a505cc0cb5a0ef3051cffaf4593 Mon Sep 17 00:00:00 2001 From: Jan Zmeskal Date: Tue, 12 May 2020 08:43:25 +0200 Subject: [PATCH] Add vault_password_file param to PlaybookRunner (#129) * Add vault_password_file param to PlaybookRunner * Make PlaybookRunner.run docstring clearer --- rrmngmnt/playbook_runner.py | 21 ++++++++++--- tests/test_playbook_runner.py | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/rrmngmnt/playbook_runner.py b/rrmngmnt/playbook_runner.py index d07ba2c..1f4f10d 100644 --- a/rrmngmnt/playbook_runner.py +++ b/rrmngmnt/playbook_runner.py @@ -107,7 +107,7 @@ def _generate_default_inventory(self): def run( self, playbook, extra_vars=None, vars_files=None, inventory=None, verbose_level=1, run_in_check_mode=False, ssh_common_args=None, - upload_playbook=True + upload_playbook=True, vault_password_file=None, ): """ Run Ansible playbook on host @@ -121,7 +121,8 @@ def run( vars_files (list): List of additional variable files to be included using -e@ parameter. If one variable is specified both in extra_vars and in one of the vars_files, the one in vars_files - takes precedence. + takes precedence. Provide local paths to vars file(s), they + will be uploaded to a remote host automatically. inventory (str): Path to an inventory file (on your machine) to be used for playbook execution. If none is provided, default inventory including only localhost will be generated and used @@ -134,8 +135,13 @@ def run( replace) the list of default options that Ansible uses when calling ssh/sftp/scp. Example: ["-o StrictHostKeyChecking=no", "-o UserKnownHostsFile=/dev/null"] - upload_playbook (bool): If the playbook is going to be uploaded - from the local machine (True - Default) or not (False) + upload_playbook (bool): Whether the playbook is going to be + automatically uploaded from the local machine to a remote host + (True - Default) or not (False). + vault_password_file (str): Local path to a vault password file. + It will be automatically uploaded to a remote host, + same as vars_files. This is required if any of the vars_files + are vault-protected. Returns: tuple: tuple of (rc, out, err) @@ -158,6 +164,13 @@ def run( for f in vars_files: self.cmd.append("-e@{}".format(self._upload_file(f))) + if vault_password_file: + self.cmd.append( + "--vault-password-file={}".format( + self._upload_file(vault_password_file) + ) + ) + self.cmd.append("-i") if inventory: self.cmd.append(self._upload_file(inventory)) diff --git a/tests/test_playbook_runner.py b/tests/test_playbook_runner.py index 246a24c..d1af036 100644 --- a/tests/test_playbook_runner.py +++ b/tests/test_playbook_runner.py @@ -17,6 +17,8 @@ class PlaybookRunnerBase(object): playbook_content = '' vars_file_name = 'my_vars.yml' vars_file_content = '' + vault_password_file_name = 'key.txt' + vault_password_file_content = '' inventory_name = 'my_inventory' inventory_content = '' ssh_no_strict_host_key_checking = "-o StrictHostKeyChecking=no" @@ -36,6 +38,9 @@ class PlaybookRunnerBase(object): '[ -d {tmp_dir}/{vars_file} ]'.format( tmp_dir=tmp_dir, vars_file=vars_file_name ): failure, + '[ -d {tmp_dir}/{vault_password_file} ]'.format( + tmp_dir=tmp_dir, vault_password_file=vault_password_file_name, + ): failure, '[ -d {tmp_dir}/{inventory} ]'.format( tmp_dir=tmp_dir, inventory=inventory_name ): failure, @@ -65,6 +70,17 @@ class PlaybookRunnerBase(object): inventory=PlaybookRunner.default_inventory_name, playbook=playbook_name ): success, + # Vault password file has been provided + '{bin} -e@{tmp_dir}/{vars_file} ' + '--vault-password-file={tmp_dir}/{vault_password_file} ' + '-i {tmp_dir}/{inventory} -v {tmp_dir}/{playbook}'.format( + bin=PlaybookRunner.binary, + vars_file=vars_file_name, + vault_password_file=vault_password_file_name, + tmp_dir=tmp_dir, + inventory=PlaybookRunner.default_inventory_name, + playbook=playbook_name + ): success, # Custom inventory has been provided '{bin} -i {tmp_dir}/{inventory} -v {tmp_dir}/{playbook}'.format( bin=PlaybookRunner.binary, @@ -236,6 +252,47 @@ def test_vars_file(self, playbook_runner, fake_playbook, fake_vars_file): ) +class TestVaultPasswordFile(PlaybookRunnerBase): + + files = {} + + @pytest.fixture() + def fake_protected_file(self, tmpdir): + fpf = tmpdir.join(self.vars_file_name) + fpf.write(self.vars_file_content) + return str(fpf) + + @pytest.fixture() + def fake_vault_password_file(self, tmpdir): + fvpf = tmpdir.join(self.vault_password_file_name) + fvpf.write(self.vault_password_file_content) + return str(fvpf) + + def test_vault_password_file( + self, playbook_runner, fake_playbook, + fake_protected_file, fake_vault_password_file + ): + """ + User has provided YAML file with variables protected by ansible-vault. + They also provided vault password file unlocking vars file. + """ + rc, _, _ = playbook_runner.run( + playbook=fake_playbook, + vars_files=[fake_protected_file], + vault_password_file=fake_vault_password_file, + ) + assert not rc + assert self.check_files_on_host( + [ + os.path.join( + self.tmp_dir, PlaybookRunner.default_inventory_name + ), + os.path.join(self.tmp_dir, self.vars_file_name), + os.path.join(self.tmp_dir, self.vault_password_file_name) + ] + ) + + class TestInventory(PlaybookRunnerBase): files = {}