diff --git a/kayobe/ansible.py b/kayobe/ansible.py index db081ffbf..ac3305816 100644 --- a/kayobe/ansible.py +++ b/kayobe/ansible.py @@ -150,6 +150,13 @@ def build_args(parsed_args, playbooks, return cmd +def _read_vault_password_file(vault_password_file): + """Return the password from a vault password file.""" + vault_password = utils.read_file(vault_password_file) + vault_password = vault_password.strip() + return vault_password + + def run_playbooks(parsed_args, playbooks, extra_vars=None, limit=None, tags=None, quiet=False, verbose_level=None, check=None): @@ -158,8 +165,20 @@ def run_playbooks(parsed_args, playbooks, cmd = build_args(parsed_args, playbooks, extra_vars=extra_vars, limit=limit, tags=tags, verbose_level=verbose_level, check=check) + env = os.environ.copy() + # If the Vault password has been specified via --vault-password-file, + # ensure the environment variable is set, so that it can be referenced by + # playbooks to generate the kolla-ansible passwords.yml file. + if vault.VAULT_PASSWORD_ENV not in env and parsed_args.vault_password_file: + vault_password = _read_vault_password_file( + parsed_args.vault_password_file) + env[vault.VAULT_PASSWORD_ENV] = vault_password + # If the configuration path has been specified via --config-path, ensure + # the environment variable is set, so that it can be referenced by + # playbooks. + env.setdefault(CONFIG_PATH_ENV, parsed_args.config_path) try: - utils.run_command(cmd, quiet=quiet) + utils.run_command(cmd, quiet=quiet, env=env) except subprocess.CalledProcessError as e: LOG.error("Kayobe playbook(s) %s exited %d", ", ".join(playbooks), e.returncode) diff --git a/kayobe/tests/unit/test_ansible.py b/kayobe/tests/unit/test_ansible.py index bf1d04ee1..9e2c80e59 100644 --- a/kayobe/tests/unit/test_ansible.py +++ b/kayobe/tests/unit/test_ansible.py @@ -31,6 +31,7 @@ class TestCase(unittest.TestCase): @mock.patch.object(utils, "run_command") @mock.patch.object(ansible, "_get_vars_files") @mock.patch.object(ansible, "_validate_args") + @mock.patch.dict(os.environ, clear=True) def test_run_playbooks(self, mock_validate, mock_vars, mock_run): mock_vars.return_value = ["/etc/kayobe/vars-file1.yml", "/etc/kayobe/vars-file2.yaml"] @@ -47,12 +48,15 @@ class TestCase(unittest.TestCase): "playbook1.yml", "playbook2.yml", ] - mock_run.assert_called_once_with(expected_cmd, quiet=False) + expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe"} + mock_run.assert_called_once_with(expected_cmd, quiet=False, + env=expected_env) mock_vars.assert_called_once_with("/etc/kayobe") @mock.patch.object(utils, "run_command") @mock.patch.object(ansible, "_get_vars_files") @mock.patch.object(ansible, "_validate_args") + @mock.patch.dict(os.environ, clear=True) def test_run_playbooks_all_the_args(self, mock_validate, mock_vars, mock_run): mock_vars.return_value = ["/path/to/config/vars-file1.yml", @@ -88,12 +92,15 @@ class TestCase(unittest.TestCase): "playbook1.yml", "playbook2.yml", ] - mock_run.assert_called_once_with(expected_cmd, quiet=False) + expected_env = {"KAYOBE_CONFIG_PATH": "/path/to/config"} + mock_run.assert_called_once_with(expected_cmd, quiet=False, + env=expected_env) mock_vars.assert_called_once_with("/path/to/config") @mock.patch.object(utils, "run_command") @mock.patch.object(ansible, "_get_vars_files") @mock.patch.object(ansible, "_validate_args") + @mock.patch.dict(os.environ, clear=True) def test_run_playbooks_all_the_long_args(self, mock_validate, mock_vars, mock_run): mock_vars.return_value = ["/path/to/config/vars-file1.yml", @@ -131,15 +138,20 @@ class TestCase(unittest.TestCase): "playbook1.yml", "playbook2.yml", ] - mock_run.assert_called_once_with(expected_cmd, quiet=False) + expected_env = {"KAYOBE_CONFIG_PATH": "/path/to/config"} + mock_run.assert_called_once_with(expected_cmd, quiet=False, + env=expected_env) mock_vars.assert_called_once_with("/path/to/config") @mock.patch.object(utils, "run_command") @mock.patch.object(ansible, "_get_vars_files") @mock.patch.object(ansible, "_validate_args") - def test_run_playbooks_vault_password_file(self, mock_validate, mock_vars, - mock_run): + @mock.patch.object(ansible, "_read_vault_password_file") + @mock.patch.dict(os.environ, clear=True) + def test_run_playbooks_vault_password_file(self, mock_read, mock_validate, + mock_vars, mock_run): mock_vars.return_value = [] + mock_read.return_value = "test-pass" parser = argparse.ArgumentParser() ansible.add_args(parser) vault.add_args(parser) @@ -154,9 +166,13 @@ class TestCase(unittest.TestCase): "--inventory", "/etc/kayobe/inventory", "playbook1.yml", ] - mock_run.assert_called_once_with(expected_cmd, quiet=False) + expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe", + "KAYOBE_VAULT_PASSWORD": "test-pass"} + mock_run.assert_called_once_with(expected_cmd, quiet=False, + env=expected_env) - @mock.patch.dict(os.environ, {"KAYOBE_VAULT_PASSWORD": "test-pass"}) + @mock.patch.dict(os.environ, {"KAYOBE_VAULT_PASSWORD": "test-pass"}, + clear=True) @mock.patch.object(utils, "run_command") @mock.patch.object(ansible, "_get_vars_files") @mock.patch.object(ansible, "_validate_args") @@ -178,11 +194,15 @@ class TestCase(unittest.TestCase): "--inventory", "/etc/kayobe/inventory", "playbook1.yml", ] - mock_run.assert_called_once_with(expected_cmd, quiet=False) + expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe", + "KAYOBE_VAULT_PASSWORD": "test-pass"} + mock_run.assert_called_once_with(expected_cmd, quiet=False, + env=expected_env) @mock.patch.object(utils, "run_command") @mock.patch.object(ansible, "_get_vars_files") @mock.patch.object(ansible, "_validate_args") + @mock.patch.dict(os.environ, clear=True) def test_run_playbooks_vault_ask_and_file(self, mock_validate, mock_vars, mock_run): mock_vars.return_value = [] @@ -198,6 +218,7 @@ class TestCase(unittest.TestCase): @mock.patch.object(utils, "run_command") @mock.patch.object(ansible, "_get_vars_files") @mock.patch.object(ansible, "_validate_args") + @mock.patch.dict(os.environ, clear=True) def test_run_playbooks_func_args(self, mock_validate, mock_vars, mock_run): mock_vars.return_value = ["/etc/kayobe/vars-file1.yml", "/etc/kayobe/vars-file2.yaml"] @@ -232,12 +253,15 @@ class TestCase(unittest.TestCase): "playbook1.yml", "playbook2.yml", ] - mock_run.assert_called_once_with(expected_cmd, quiet=False) + expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe"} + mock_run.assert_called_once_with(expected_cmd, quiet=False, + env=expected_env) mock_vars.assert_called_once_with("/etc/kayobe") @mock.patch.object(utils, "run_command") @mock.patch.object(ansible, "_get_vars_files") @mock.patch.object(ansible, "_validate_args") + @mock.patch.dict(os.environ, clear=True) def test_run_playbooks_failure(self, mock_validate, mock_vars, mock_run): parser = argparse.ArgumentParser() ansible.add_args(parser) @@ -281,3 +305,10 @@ class TestCase(unittest.TestCase): mock.call(os.path.join(dump_dir, "host1.yml")), mock.call(os.path.join(dump_dir, "host2.yml")), ]) + + @mock.patch.object(utils, 'read_file') + def test__read_vault_password_file(self, mock_read): + mock_read.return_value = "test-pass\n" + result = ansible._read_vault_password_file("/path/to/file") + self.assertEqual("test-pass", result) + mock_read.assert_called_once_with("/path/to/file")