diff --git a/changelogs/fragments/85853-error-on-empty-inventory.yml b/changelogs/fragments/85853-error-on-empty-inventory.yml new file mode 100644 index 00000000000..03a543860aa --- /dev/null +++ b/changelogs/fragments/85853-error-on-empty-inventory.yml @@ -0,0 +1,2 @@ +minor_changes: + - add new configuration option ``ERROR_ON_EMPTY_INVENTORY`` (env var ``ANSIBLE_ERROR_ON_EMPTY_INVENTORY``) to control whether empty inventories should raise an error or show a warning. (https://github.com/ansible/ansible/issues/85853) diff --git a/lib/ansible/cli/__init__.py b/lib/ansible/cli/__init__.py index 30af48c0976..ee1dd7fdd4a 100644 --- a/lib/ansible/cli/__init__.py +++ b/lib/ansible/cli/__init__.py @@ -587,8 +587,10 @@ class CLI(ABC): def get_host_list(inventory, subset, pattern='all'): no_hosts = False + # Empty inventory if len(inventory.list_hosts()) == 0: - # Empty inventory + if C.ERROR_ON_EMPTY_INVENTORY: + raise AnsibleError("No hosts found in inventory") if C.LOCALHOST_WARNING and pattern not in C.LOCALHOST: display.warning("provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'") no_hosts = True diff --git a/lib/ansible/config/base.yml b/lib/ansible/config/base.yml index dc05c15544a..d943a30399d 100644 --- a/lib/ansible/config/base.yml +++ b/lib/ansible/config/base.yml @@ -1404,6 +1404,15 @@ DUPLICATE_YAML_DICT_KEY: warn: issue a warning but continue ignore: just continue silently version_added: "2.9" +ERROR_ON_EMPTY_INVENTORY: + name: Empty inventory error + default: False + description: "Toggle to allow empty inventory to become an error instead of a warning." + env: [{name: ANSIBLE_ERROR_ON_EMPTY_INVENTORY}] + ini: + - {key: error_on_empty_inventory, section: defaults} + type: boolean + version_added: "2.20" ERROR_ON_MISSING_HANDLER: name: Missing handler error default: True diff --git a/test/units/cli/test_cli.py b/test/units/cli/test_cli.py index 8208277e982..d212766ade0 100644 --- a/test/units/cli/test_cli.py +++ b/test/units/cli/test_cli.py @@ -29,6 +29,7 @@ from units.mock.loader import DictDataLoader from ansible.release import __version__ from ansible.parsing import vault from ansible import cli +from ansible.errors import AnsibleError class TestCliVersion(unittest.TestCase): @@ -364,3 +365,28 @@ class TestCliSetupVaultSecrets(unittest.TestCase): self.assertIsInstance(res, list) match = vault.match_secrets(res, ['some_vault_id'])[0][1] self.assertEqual(match.bytes, b'prompt1_password') + + +class TestGetHostListEmptyInventory: + def setup_method(self): + self.mock_inventory = MagicMock() + + @patch('ansible.cli.C.ERROR_ON_EMPTY_INVENTORY', True) + def test_get_host_list_empty_inventory_error_on(self): + """Test that AnsibleError is raised when ERROR_ON_EMPTY_INVENTORY is True and inventory is empty""" + + self.mock_inventory.list_hosts.return_value = [] + + with pytest.raises(AnsibleError, match="No hosts found in inventory"): + cli.CLI.get_host_list(self.mock_inventory, None, 'all') + + @patch('ansible.cli.C.ERROR_ON_EMPTY_INVENTORY', False) + @patch('ansible.cli.display.warning') + def test_get_host_list_empty_inventory_error_off(self, mock_warning): + """Test that warning is displayed when ERROR_ON_EMPTY_INVENTORY is False and inventory is empty""" + self.mock_inventory.list_hosts.side_effect = [[], []] + + result = cli.CLI.get_host_list(self.mock_inventory, None, 'webservers') + + mock_warning.assert_called_once() + assert result == []