From 358b5c68828e6e7de8bfc7654c7418c02380052b Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Thu, 11 Oct 2018 14:58:22 +0100 Subject: [PATCH] Fix use of --ask-vault-pass argument Currently, this argument does not work correctly, since the vault password is not passed to kayobe via $KAYOBE_VAULT_PASSWORD, meaning that it cannot update the kolla-ansible passwords.yml file. It also works non-optimally, since every invocation of ansible-playbook will prompt for a password. This change fixes the issue by prompting for a password once in the kayobe CLI, and storing the password in the $KAYOBE_VAULT_PASSWORD environment variable. The kayobe-vault-password-helper command is then used as the --vault-password-file to ansible-playbook, and the helper pulls the password out of the environment. Change-Id: I88b1b7b9e9be15b52e730d353ce1b1a6feacceb8 Story: 2001664 Task: 27009 --- kayobe/ansible.py | 18 +-- kayobe/kolla_ansible.py | 9 +- kayobe/tests/unit/test_ansible.py | 45 ++++---- kayobe/tests/unit/test_kolla_ansible.py | 40 +++++-- kayobe/tests/unit/test_vault.py | 104 ++++++++++++++++++ kayobe/vault.py | 101 +++++++++++++++-- .../ask-vault-pass-b6ced0220384dde1.yaml | 7 ++ 7 files changed, 262 insertions(+), 62 deletions(-) create mode 100644 kayobe/tests/unit/test_vault.py create mode 100644 releasenotes/notes/ask-vault-pass-b6ced0220384dde1.yaml diff --git a/kayobe/ansible.py b/kayobe/ansible.py index 7c1d4abcc..8655272b9 100644 --- a/kayobe/ansible.py +++ b/kayobe/ansible.py @@ -79,6 +79,7 @@ def _get_inventory_path(parsed_args): def _validate_args(parsed_args, playbooks): """Validate Kayobe Ansible arguments.""" + vault.validate_args(parsed_args) result = utils.is_readable_dir(parsed_args.config_path) if not result["result"]: LOG.error("Kayobe configuration path %s is invalid: %s", @@ -124,7 +125,7 @@ def build_args(parsed_args, playbooks, cmd += ["-" + "v" * verbose_level] if parsed_args.list_tasks: cmd += ["--list-tasks"] - cmd += vault.build_args(parsed_args) + cmd += vault.build_args(parsed_args, "--vault-password-file") inventory = _get_inventory_path(parsed_args) cmd += ["--inventory", inventory] vars_files = _get_vars_files(parsed_args.config_path) @@ -152,13 +153,6 @@ 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): @@ -168,13 +162,7 @@ def run_playbooks(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 + vault.update_environment(parsed_args, env) # If the configuration path has been specified via --config-path, ensure # the environment variable is set, so that it can be referenced by # playbooks. diff --git a/kayobe/kolla_ansible.py b/kayobe/kolla_ansible.py index 9abd15ea2..4aa9c94d3 100644 --- a/kayobe/kolla_ansible.py +++ b/kayobe/kolla_ansible.py @@ -19,6 +19,7 @@ import subprocess import sys from kayobe import utils +from kayobe import vault DEFAULT_CONFIG_PATH = "/etc/kolla" @@ -78,6 +79,7 @@ def _get_inventory_path(parsed_args, inventory_filename): def _validate_args(parsed_args, inventory_filename): """Validate Kayobe Ansible arguments.""" + vault.validate_args(parsed_args) result = utils.is_readable_dir(parsed_args.kolla_config_path) if not result["result"]: LOG.error("Kolla configuration path %s is invalid: %s", @@ -106,8 +108,7 @@ def build_args(parsed_args, command, inventory_filename, extra_vars=None, cmd += ["kolla-ansible", command] if verbose_level: cmd += ["-" + "v" * verbose_level] - if parsed_args.vault_password_file: - cmd += ["--key", parsed_args.vault_password_file] + cmd += vault.build_args(parsed_args, "--key") inventory = _get_inventory_path(parsed_args, inventory_filename) cmd += ["--inventory", inventory] if parsed_args.kolla_config_path != DEFAULT_CONFIG_PATH: @@ -144,8 +145,10 @@ def run(parsed_args, command, inventory_filename, extra_vars=None, verbose_level=verbose_level, extra_args=extra_args, limit=limit) + env = os.environ.copy() + vault.update_environment(parsed_args, env) try: - utils.run_command(" ".join(cmd), quiet=quiet, shell=True) + utils.run_command(" ".join(cmd), quiet=quiet, shell=True, env=env) except subprocess.CalledProcessError as e: LOG.error("kolla-ansible %s exited %d", command, e.returncode) sys.exit(e.returncode) diff --git a/kayobe/tests/unit/test_ansible.py b/kayobe/tests/unit/test_ansible.py index 1a28780e4..0c1806979 100644 --- a/kayobe/tests/unit/test_ansible.py +++ b/kayobe/tests/unit/test_ansible.py @@ -28,12 +28,12 @@ from kayobe import utils from kayobe import vault +@mock.patch.dict(os.environ, clear=True) 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"] @@ -58,7 +58,6 @@ 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_all_the_args(self, mock_validate, mock_vars, mock_run): mock_vars.return_value = ["/path/to/config/vars-file1.yml", @@ -102,14 +101,15 @@ 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_all_the_long_args(self, mock_validate, mock_vars, - mock_run): + @mock.patch.object(vault, "_ask_vault_pass") + def test_run_playbooks_all_the_long_args(self, mock_ask, mock_validate, + mock_vars, mock_run): mock_vars.return_value = ["/path/to/config/vars-file1.yml", "/path/to/config/vars-file2.yaml"] parser = argparse.ArgumentParser() ansible.add_args(parser) vault.add_args(parser) + mock_ask.return_value = "test-pass" args = [ "--ask-vault-pass", "--become", @@ -123,11 +123,12 @@ class TestCase(unittest.TestCase): "--list-tasks", ] parsed_args = parser.parse_args(args) + mock_run.return_value = "/path/to/kayobe-vault-password-helper" ansible.run_playbooks(parsed_args, ["playbook1.yml", "playbook2.yml"]) expected_cmd = [ "ansible-playbook", "--list-tasks", - "--ask-vault-pass", + "--vault-password-file", "/path/to/kayobe-vault-password-helper", "--inventory", "/path/to/inventory", "-e", "@/path/to/config/vars-file1.yml", "-e", "@/path/to/config/vars-file2.yaml", @@ -140,20 +141,24 @@ class TestCase(unittest.TestCase): "playbook1.yml", "playbook2.yml", ] - expected_env = {"KAYOBE_CONFIG_PATH": "/path/to/config"} - mock_run.assert_called_once_with(expected_cmd, quiet=False, - env=expected_env) + expected_env = {"KAYOBE_CONFIG_PATH": "/path/to/config", + "KAYOBE_VAULT_PASSWORD": "test-pass"} + expected_calls = [ + mock.call(["which", "kayobe-vault-password-helper"], + check_output=True), + mock.call(expected_cmd, quiet=False, env=expected_env) + ] + self.assertEqual(expected_calls, mock_run.mock_calls) 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.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.patch.object(vault, "update_environment") + def test_run_playbooks_vault_password_file(self, mock_update, + 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) @@ -168,10 +173,10 @@ class TestCase(unittest.TestCase): "--inventory", "/etc/kayobe/inventory", "playbook1.yml", ] - expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe", - "KAYOBE_VAULT_PASSWORD": "test-pass"} + expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe"} mock_run.assert_called_once_with(expected_cmd, quiet=False, env=expected_env) + mock_update.assert_called_once_with(mock.ANY, expected_env) @mock.patch.dict(os.environ, {"KAYOBE_VAULT_PASSWORD": "test-pass"}, clear=True) @@ -204,7 +209,6 @@ 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_vault_ask_and_file(self, mock_validate, mock_vars, mock_run): mock_vars.return_value = [] @@ -220,7 +224,6 @@ 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"] @@ -263,7 +266,6 @@ 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_failure(self, mock_validate, mock_vars, mock_run): parser = argparse.ArgumentParser() ansible.add_args(parser) @@ -387,10 +389,3 @@ class TestCase(unittest.TestCase): mock_is_readable.assert_called_once_with( "/etc/kayobe/ansible/requirements.yml") mock_mkdirs.assert_called_once_with("/etc/kayobe/ansible/roles") - - @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") diff --git a/kayobe/tests/unit/test_kolla_ansible.py b/kayobe/tests/unit/test_kolla_ansible.py index 8edb85d20..df947804f 100644 --- a/kayobe/tests/unit/test_kolla_ansible.py +++ b/kayobe/tests/unit/test_kolla_ansible.py @@ -25,6 +25,7 @@ from kayobe import vault @mock.patch.object(os, "getcwd", new=lambda: "/path/to/cwd") +@mock.patch.dict(os.environ, clear=True) class TestCase(unittest.TestCase): @mock.patch.object(utils, "run_command") @@ -41,7 +42,8 @@ class TestCase(unittest.TestCase): "--inventory", "/etc/kolla/inventory/overcloud", ] expected_cmd = " ".join(expected_cmd) - mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False) + mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False, + env={}) @mock.patch.object(utils, "run_command") @mock.patch.object(kolla_ansible, "_validate_args") @@ -69,14 +71,17 @@ class TestCase(unittest.TestCase): "--tags", "tag1,tag2", ] expected_cmd = " ".join(expected_cmd) - mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False) + mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False, + env={}) @mock.patch.object(utils, "run_command") @mock.patch.object(kolla_ansible, "_validate_args") - def test_run_all_the_long_args(self, mock_validate, mock_run): + @mock.patch.object(vault, "_ask_vault_pass") + def test_run_all_the_long_args(self, mock_ask, mock_validate, mock_run): parser = argparse.ArgumentParser() kolla_ansible.add_args(parser) vault.add_args(parser) + mock_ask.return_value = "test-pass" args = [ "--ask-vault-pass", "--kolla-config-path", "/path/to/config", @@ -87,10 +92,12 @@ class TestCase(unittest.TestCase): "--kolla-tags", "tag1,tag2", ] parsed_args = parser.parse_args(args) + mock_run.return_value = "/path/to/kayobe-vault-password-helper" kolla_ansible.run(parsed_args, "command", "overcloud") expected_cmd = [ ".", "/path/to/cwd/venvs/kolla-ansible/bin/activate", "&&", "kolla-ansible", "command", + "--key", "/path/to/kayobe-vault-password-helper", "--inventory", "/path/to/inventory", "--configdir", "/path/to/config", "--passwords", "/path/to/config/passwords.yml", @@ -100,11 +107,19 @@ class TestCase(unittest.TestCase): "--tags", "tag1,tag2", ] expected_cmd = " ".join(expected_cmd) - mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False) + expected_env = {"KAYOBE_VAULT_PASSWORD": "test-pass"} + expected_calls = [ + mock.call(["which", "kayobe-vault-password-helper"], + check_output=True), + mock.call(expected_cmd, shell=True, quiet=False, env=expected_env) + ] + self.assertEqual(expected_calls, mock_run.mock_calls) @mock.patch.object(utils, "run_command") @mock.patch.object(kolla_ansible, "_validate_args") - def test_run_vault_password_file(self, mock_validate, mock_run): + @mock.patch.object(vault, "update_environment") + def test_run_vault_password_file(self, mock_update, mock_validate, + mock_run): parser = argparse.ArgumentParser() kolla_ansible.add_args(parser) vault.add_args(parser) @@ -120,12 +135,15 @@ class TestCase(unittest.TestCase): "--inventory", "/etc/kolla/inventory/overcloud", ] expected_cmd = " ".join(expected_cmd) - mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False) + mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False, + env={}) + mock_update.assert_called_once_with(mock.ANY, {}) @mock.patch.dict(os.environ, {"KAYOBE_VAULT_PASSWORD": "test-pass"}) @mock.patch.object(utils, "run_command") @mock.patch.object(kolla_ansible, "_validate_args") - def test_run_vault_password_helper(self, mock_vars, mock_run): + @mock.patch.object(vault, "update_environment") + def test_run_vault_password_helper(self, mock_update, mock_vars, mock_run): mock_vars.return_value = [] parser = argparse.ArgumentParser() mock_run.return_value = "/path/to/kayobe-vault-password-helper" @@ -143,7 +161,10 @@ class TestCase(unittest.TestCase): "--inventory", "/etc/kolla/inventory/overcloud", ] expected_cmd = " ".join(expected_cmd) - mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False) + expected_env = {"KAYOBE_VAULT_PASSWORD": "test-pass"} + mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False, + env=expected_env) + mock_update.assert_called_once_with(mock.ANY, expected_env) @mock.patch.object(utils, "run_command") @mock.patch.object(kolla_ansible, "_validate_args") @@ -174,7 +195,8 @@ class TestCase(unittest.TestCase): "--arg1", "--arg2", ] expected_cmd = " ".join(expected_cmd) - mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False) + mock_run.assert_called_once_with(expected_cmd, shell=True, quiet=False, + env={}) @mock.patch.object(utils, "run_command") @mock.patch.object(kolla_ansible, "_validate_args") diff --git a/kayobe/tests/unit/test_vault.py b/kayobe/tests/unit/test_vault.py new file mode 100644 index 000000000..d4c8fd21e --- /dev/null +++ b/kayobe/tests/unit/test_vault.py @@ -0,0 +1,104 @@ +# Copyright (c) 2018 StackHPC Ltd. +# +# Licensed 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. + +import argparse +import os +import unittest + +import mock + +from kayobe import utils +from kayobe import vault + + +class TestCase(unittest.TestCase): + + def test_validate_args_ok(self): + parser = argparse.ArgumentParser() + vault.add_args(parser) + parsed_args = parser.parse_args([]) + vault.validate_args(parsed_args) + + @mock.patch.dict(os.environ, {"KAYOBE_VAULT_PASSWORD": "test-pass"}) + def test_validate_args_env(self): + parser = argparse.ArgumentParser() + vault.add_args(parser) + parsed_args = parser.parse_args([]) + vault.validate_args(parsed_args) + + @mock.patch.dict(os.environ, {"KAYOBE_VAULT_PASSWORD": "test-pass"}) + def test_validate_args_ask_vault_pass(self): + parser = argparse.ArgumentParser() + vault.add_args(parser) + parsed_args = parser.parse_args(["--ask-vault-pass"]) + self.assertRaises(SystemExit, vault.validate_args, parsed_args) + + @mock.patch.dict(os.environ, {"KAYOBE_VAULT_PASSWORD": "test-pass"}) + def test_validate_args_vault_password_file(self): + parser = argparse.ArgumentParser() + vault.add_args(parser) + parsed_args = parser.parse_args(["--vault-password-file", + "/path/to/file"]) + self.assertRaises(SystemExit, vault.validate_args, parsed_args) + + @mock.patch.object(vault.getpass, 'getpass') + def test__ask_vault_pass(self, mock_getpass): + mock_getpass.return_value = 'test-pass' + + # Call twice to verify that the user is only prompted once. + result = vault._ask_vault_pass() + self.assertEqual('test-pass', result) + mock_getpass.assert_called_once_with("Vault password: ") + + result = vault._ask_vault_pass() + self.assertEqual('test-pass', result) + mock_getpass.assert_called_once_with("Vault password: ") + + @mock.patch.object(utils, 'read_file') + def test__read_vault_password_file(self, mock_read): + mock_read.return_value = "test-pass\n" + result = vault._read_vault_password_file("/path/to/file") + self.assertEqual("test-pass", result) + mock_read.assert_called_once_with("/path/to/file") + + def test_update_environment_no_vault(self): + parser = argparse.ArgumentParser() + vault.add_args(parser) + parsed_args = parser.parse_args([]) + env = {} + vault.update_environment(parsed_args, env) + self.assertEqual({}, env) + + @mock.patch.object(vault, '_ask_vault_pass') + def test_update_environment_prompt(self, mock_ask): + mock_ask.return_value = "test-pass" + parser = argparse.ArgumentParser() + vault.add_args(parser) + parsed_args = parser.parse_args(["--ask-vault-pass"]) + env = {} + vault.update_environment(parsed_args, env) + self.assertEqual({"KAYOBE_VAULT_PASSWORD": "test-pass"}, env) + mock_ask.assert_called_once_with() + + @mock.patch.object(vault, '_read_vault_password_file') + def test_update_environment_file(self, mock_read): + mock_read.return_value = "test-pass" + parser = argparse.ArgumentParser() + vault.add_args(parser) + args = ["--vault-password-file", "/path/to/file"] + parsed_args = parser.parse_args(args) + env = {} + vault.update_environment(parsed_args, env) + self.assertEqual({"KAYOBE_VAULT_PASSWORD": "test-pass"}, env) + mock_read.assert_called_once_with("/path/to/file") diff --git a/kayobe/vault.py b/kayobe/vault.py index 10684ca87..db5adab4c 100644 --- a/kayobe/vault.py +++ b/kayobe/vault.py @@ -12,15 +12,30 @@ # License for the specific language governing permissions and limitations # under the License. +import getpass +import logging import os import subprocess +import sys from kayobe import utils +LOG = logging.getLogger(__name__) + VAULT_PASSWORD_ENV = "KAYOBE_VAULT_PASSWORD" +def _get_vault_password_helper(): + """Return the path to the kayobe-vault-password-helper executable.""" + cmd = ["which", "kayobe-vault-password-helper"] + try: + output = utils.run_command(cmd, check_output=True) + except subprocess.CalledProcessError: + return None + return output.strip() + + def _get_default_vault_password_file(): """Return the default value for the vault password file argument. @@ -29,12 +44,7 @@ def _get_default_vault_password_file(): """ if not os.getenv(VAULT_PASSWORD_ENV): return None - cmd = ["which", "kayobe-vault-password-helper"] - try: - output = utils.run_command(cmd, check_output=True) - except subprocess.CalledProcessError: - return None - return output.strip() + return _get_vault_password_helper() def add_args(parser): @@ -48,11 +58,82 @@ def add_args(parser): help="vault password file") -def build_args(parsed_args): +def build_args(parsed_args, password_file_arg_name): """Build a list of command line arguments for use with ansible-playbook.""" - cmd = [] + vault_password_file = None if parsed_args.ask_vault_pass: - cmd += ["--ask-vault-pass"] + vault_password_file = _get_vault_password_helper() elif parsed_args.vault_password_file: - cmd += ["--vault-password-file", parsed_args.vault_password_file] + vault_password_file = parsed_args.vault_password_file + + cmd = [] + if vault_password_file: + cmd += [password_file_arg_name, vault_password_file] return cmd + + +def validate_args(parsed_args): + """Validate command line arguments.""" + # Ensure that a password prompt or file has not been requested if the + # password environment variable is set. + if VAULT_PASSWORD_ENV not in os.environ: + return + + helper = _get_vault_password_helper() + invalid_arg = None + if parsed_args.ask_vault_pass: + invalid_arg = "--ask-vault-pass" + elif parsed_args.vault_password_file != helper: + invalid_arg = "--vault-password-file" + + if invalid_arg: + LOG.error("Cannot specify %s when $%s is specified" % + (invalid_arg, VAULT_PASSWORD_ENV)) + sys.exit(1) + + +def _ask_vault_pass(): + """Prompt the user for a Vault password. + + The first time this function is called, the user is prompted for a + password. To avoid prompting the user multiple times per invocation of + kayobe, we cache the password and return it without prompting on subsequent + calls. + + :return: The password entered by the user. + """ + if not hasattr(_ask_vault_pass, "password"): + password = getpass.getpass("Vault password: ") + setattr(_ask_vault_pass, "password", password) + return getattr(_ask_vault_pass, "password") + + +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 update_environment(parsed_args, env): + """Update environment variables with the vault password if necessary. + + :param parsed_args: Parsed command line arguments. + :params env: Dict of environment variables to update. + """ + # If the Vault password has been specified via --vault-password-file, or a + # prompt has been requested via --ask-vault-pass, ensure the environment + # variable is set, so that it can be referenced by playbooks to generate + # the kolla-ansible passwords.yml file. + if VAULT_PASSWORD_ENV in env: + return + + vault_password = None + if parsed_args.ask_vault_pass: + vault_password = _ask_vault_pass() + elif parsed_args.vault_password_file: + vault_password = _read_vault_password_file( + parsed_args.vault_password_file) + + if vault_password is not None: + env[VAULT_PASSWORD_ENV] = vault_password diff --git a/releasenotes/notes/ask-vault-pass-b6ced0220384dde1.yaml b/releasenotes/notes/ask-vault-pass-b6ced0220384dde1.yaml new file mode 100644 index 000000000..d27a89d89 --- /dev/null +++ b/releasenotes/notes/ask-vault-pass-b6ced0220384dde1.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixes an issue with the ``--ask-vault-pass`` argument, where Kayobe would + fail to generate the Kolla Ansible ``passwords.yml`` file. Also ensures + that the user is only prompted for the password once per execution of + kayobe.