Detect multiple inventories automatically

When inventory arguments are not passed but an environment is specified,
detect if there are inventory directories present in the shared Kayobe
configuration directory and in the environment directory. Using
os.path.exists() is sufficient because we validate these paths later
with utils.is_readable_dir().

Change-Id: Ieb2b7ff07cd43029399e9e19002b82940246f8c4
Story: 2002009
Task: 41739
This commit is contained in:
Pierre Riteau 2021-02-02 17:02:38 +01:00
parent c15ed69b18
commit ea54f82299
2 changed files with 131 additions and 2 deletions

View File

@ -91,8 +91,19 @@ def _get_inventories_paths(parsed_args, env_path):
if parsed_args.inventory:
return parsed_args.inventory
else:
return [os.path.join(env_path if env_path else parsed_args.config_path,
"inventory")]
inventories = []
shared_inventory = os.path.join(parsed_args.config_path, "inventory")
if env_path:
if os.path.exists(shared_inventory):
inventories.append(shared_inventory)
env_inventory = os.path.join(env_path, "inventory")
if os.path.exists(env_inventory):
inventories.append(env_inventory)
else:
# Preserve existing behaviour: don't check if an inventory
# directory exists when no environment is specified
inventories.append(shared_inventory)
return inventories
def _validate_args(parsed_args, playbooks):

View File

@ -587,3 +587,121 @@ class TestCase(unittest.TestCase):
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with(["/etc/kayobe"])
@mock.patch.object(os.path, "exists")
@mock.patch.object(utils, "run_command")
@mock.patch.object(ansible, "_get_vars_files")
@mock.patch.object(ansible, "_validate_args")
def test_multiple_inventories(self, mock_validate, mock_vars, mock_run,
mock_exists):
mock_vars.return_value = []
# os.path.exists gets called three times:
# 1) shared inventory
# 2) environment inventory
# 3) ansible.cfg
mock_exists.side_effect = [True, True, False]
parser = argparse.ArgumentParser()
ansible.add_args(parser)
vault.add_args(parser)
args = [
"--environment", "test-env",
]
parsed_args = parser.parse_args(args)
ansible.run_playbooks(parsed_args, ["playbook1.yml", "playbook2.yml"])
expected_cmd = [
"ansible-playbook",
"--inventory", "/etc/kayobe/inventory",
"--inventory", "/etc/kayobe/environments/test-env/inventory",
"playbook1.yml",
"playbook2.yml",
]
expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe",
"KAYOBE_ENVIRONMENT": "test-env"}
expected_calls = [
mock.call("/etc/kayobe/inventory"),
mock.call("/etc/kayobe/environments/test-env/inventory"),
mock.call("/etc/kayobe/ansible.cfg"),
]
self.assertEqual(expected_calls, mock_exists.mock_calls)
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with(
["/etc/kayobe", "/etc/kayobe/environments/test-env"])
@mock.patch.object(os.path, "exists")
@mock.patch.object(utils, "run_command")
@mock.patch.object(ansible, "_get_vars_files")
@mock.patch.object(ansible, "_validate_args")
def test_shared_inventory_only(self, mock_validate, mock_vars, mock_run,
mock_exists):
mock_vars.return_value = []
# os.path.exists gets called three times:
# 1) shared inventory
# 2) environment inventory
# 3) ansible.cfg
mock_exists.side_effect = [True, False, False]
parser = argparse.ArgumentParser()
ansible.add_args(parser)
vault.add_args(parser)
args = [
"--environment", "test-env",
]
parsed_args = parser.parse_args(args)
ansible.run_playbooks(parsed_args, ["playbook1.yml", "playbook2.yml"])
expected_cmd = [
"ansible-playbook",
"--inventory", "/etc/kayobe/inventory",
"playbook1.yml",
"playbook2.yml",
]
expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe",
"KAYOBE_ENVIRONMENT": "test-env"}
expected_calls = [
mock.call("/etc/kayobe/inventory"),
mock.call("/etc/kayobe/environments/test-env/inventory"),
mock.call("/etc/kayobe/ansible.cfg"),
]
self.assertEqual(expected_calls, mock_exists.mock_calls)
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with(
["/etc/kayobe", "/etc/kayobe/environments/test-env"])
@mock.patch.object(os.path, "exists")
@mock.patch.object(utils, "run_command")
@mock.patch.object(ansible, "_get_vars_files")
@mock.patch.object(ansible, "_validate_args")
def test_env_inventory_only(self, mock_validate, mock_vars, mock_run,
mock_exists):
mock_vars.return_value = []
# os.path.exists gets called three times:
# 1) shared inventory
# 2) environment inventory
# 3) ansible.cfg
mock_exists.side_effect = [False, True, False]
parser = argparse.ArgumentParser()
ansible.add_args(parser)
vault.add_args(parser)
args = [
"--environment", "test-env",
]
parsed_args = parser.parse_args(args)
ansible.run_playbooks(parsed_args, ["playbook1.yml", "playbook2.yml"])
expected_cmd = [
"ansible-playbook",
"--inventory", "/etc/kayobe/environments/test-env/inventory",
"playbook1.yml",
"playbook2.yml",
]
expected_env = {"KAYOBE_CONFIG_PATH": "/etc/kayobe",
"KAYOBE_ENVIRONMENT": "test-env"}
expected_calls = [
mock.call("/etc/kayobe/inventory"),
mock.call("/etc/kayobe/environments/test-env/inventory"),
mock.call("/etc/kayobe/ansible.cfg"),
]
self.assertEqual(expected_calls, mock_exists.mock_calls)
mock_run.assert_called_once_with(expected_cmd, check_output=False,
quiet=False, env=expected_env)
mock_vars.assert_called_once_with(
["/etc/kayobe", "/etc/kayobe/environments/test-env"])