diff --git a/changelogs/fragments/unit-tests_test_rds_instance_info_only.yaml b/changelogs/fragments/unit-tests_test_rds_instance_info_only.yaml new file mode 100644 index 00000000000..24e3d9d3e0b --- /dev/null +++ b/changelogs/fragments/unit-tests_test_rds_instance_info_only.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: +- "rds_instance_info - Add unit-tests coverage (https://github.com/ansible-collections/amazon.aws/pull/1132)." diff --git a/plugins/modules/rds_instance_info.py b/plugins/modules/rds_instance_info.py index 2e537e55288..09338e5c59a 100644 --- a/plugins/modules/rds_instance_info.py +++ b/plugins/modules/rds_instance_info.py @@ -377,11 +377,24 @@ def _describe_db_instances(conn, **params): return results -def instance_info(module, conn): - instance_name = module.params.get('db_instance_identifier') - filters = module.params.get('filters') +class RdsInstanceInfoFailure(Exception): + def __init__(self, original_e, user_message): + self.original_e = original_e + self.user_message = user_message + super().__init__(self) + + +def get_instance_tags(conn, arn): + try: + return boto3_tag_list_to_ansible_dict( + conn.list_tags_for_resource(ResourceName=arn, + aws_retry=True)['TagList']) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + raise RdsInstanceInfoFailure(e, "Couldn't get tags for instance %s" % arn) - params = dict() + +def instance_info(conn, instance_name, filters): + params = {} if instance_name: params['DBInstanceIdentifier'] = instance_name if filters: @@ -390,16 +403,15 @@ def instance_info(module, conn): try: results = _describe_db_instances(conn, **params) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, "Couldn't get instance information") + raise RdsInstanceInfoFailure(e, "Couldn't get instance information") for instance in results: - try: - instance['Tags'] = boto3_tag_list_to_ansible_dict(conn.list_tags_for_resource(ResourceName=instance['DBInstanceArn'], - aws_retry=True)['TagList']) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, "Couldn't get tags for instance %s" % instance['DBInstanceIdentifier']) + instance['Tags'] = get_instance_tags(conn, arn=instance['DBInstanceArn']) - return dict(changed=False, instances=[camel_dict_to_snake_dict(instance, ignore_list=['Tags']) for instance in results]) + return { + "changed": False, + "instances": [camel_dict_to_snake_dict(instance, ignore_list=['Tags']) for instance in results] + } def main(): @@ -415,7 +427,13 @@ def main(): conn = module.client('rds', retry_decorator=AWSRetry.jittered_backoff(retries=10)) - module.exit_json(**instance_info(module, conn)) + instance_name = module.params.get('db_instance_identifier') + filters = module.params.get('filters') + + try: + module.exit_json(**instance_info(conn, instance_name, filters)) + except RdsInstanceInfoFailure as e: + module.fail_json_aws(e.original_e, e.user_message) if __name__ == '__main__': diff --git a/tests/unit/plugins/modules/test_rds_instance_info.py b/tests/unit/plugins/modules/test_rds_instance_info.py new file mode 100644 index 00000000000..037da7bf0a2 --- /dev/null +++ b/tests/unit/plugins/modules/test_rds_instance_info.py @@ -0,0 +1,121 @@ +# (c) 2022 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from ansible_collections.amazon.aws.plugins.modules import rds_instance_info +from unittest.mock import MagicMock, Mock, patch, ANY +import ansible.module_utils.basic +import botocore.exceptions +import pytest + + +mod_name = "ansible_collections.amazon.aws.plugins.modules.rds_instance_info" + + +def a_boto_exception(): + return botocore.exceptions.UnknownServiceError( + service_name="Whoops", known_service_names="Oula" + ) + + +@patch(mod_name + "._describe_db_instances") +@patch(mod_name + ".get_instance_tags") +def test_instance_info_one_instance(m_get_instance_tags, m_describe_db_instances): + conn = MagicMock() + instance_name = "my-instance" + m_get_instance_tags.return_value = [] + m_describe_db_instances.return_value = [ + { + "DBInstanceIdentifier": instance_name, + "DBInstanceArn": "arn:aws:rds:us-east-2:123456789012:og:" + instance_name, + } + ] + rds_instance_info.instance_info(conn, instance_name, filters={}) + + m_describe_db_instances.assert_called_with(conn, DBInstanceIdentifier=instance_name) + m_get_instance_tags.assert_called_with( + conn, arn="arn:aws:rds:us-east-2:123456789012:og:" + instance_name + ) + + +@patch(mod_name + "._describe_db_instances") +@patch(mod_name + ".get_instance_tags") +def test_instance_info_all_instances(m_get_instance_tags, m_describe_db_instances): + conn = MagicMock() + m_get_instance_tags.return_value = [] + m_describe_db_instances.return_value = [ + { + "DBInstanceIdentifier": "first-instance", + "DBInstanceArn": "arn:aws:rds:us-east-2:123456789012:og:first-instance", + }, + { + "DBInstanceIdentifier": "second-instance", + "DBInstanceArn": "arn:aws:rds:us-east-2:123456789012:og:second-instance", + }, + ] + rds_instance_info.instance_info(conn, instance_name=None, filters={"engine": "postgres"}) + + m_describe_db_instances.assert_called_with(conn, Filters=[{'Name': 'engine', 'Values': ['postgres']}]) + assert m_get_instance_tags.call_count == 2 + m_get_instance_tags.assert_called_with( + conn, arn="arn:aws:rds:us-east-2:123456789012:og:second-instance" + ) + + +def test_get_instance_tags(): + conn = MagicMock() + conn.list_tags_for_resource.return_value = { + "TagList": [ + {"Key": "My-tag", "Value": "the-value$"}, + ], + "NextToken": "some-token", + } + + tags = rds_instance_info.get_instance_tags( + conn, "arn:aws:rds:us-east-2:123456789012:og:second-instance" + ) + conn.list_tags_for_resource.assert_called_with( + ResourceName="arn:aws:rds:us-east-2:123456789012:og:second-instance", + aws_retry=True, + ) + assert tags == {"My-tag": "the-value$"} + + +def test_api_failure_get_tag(): + conn = MagicMock() + conn.list_tags_for_resource.side_effect = a_boto_exception() + + with pytest.raises(rds_instance_info.RdsInstanceInfoFailure): + rds_instance_info.get_instance_tags(conn, "arn:blabla") + + +def test_api_failure_describe(): + conn = MagicMock() + conn.get_paginator.side_effect = a_boto_exception() + + with pytest.raises(rds_instance_info.RdsInstanceInfoFailure): + rds_instance_info.instance_info(conn, None, {}) + + +@patch(mod_name + ".AnsibleAWSModule") +def test_main_success(m_AnsibleAWSModule): + m_module = MagicMock() + m_AnsibleAWSModule.return_value = m_module + + rds_instance_info.main() + + m_module.client.assert_called_with("rds", retry_decorator=ANY) + m_module.exit_json.assert_called_with(changed=False, instances=[]) + + +@patch(mod_name + "._describe_db_instances") +@patch(mod_name + ".AnsibleAWSModule") +def test_main_failure(m_AnsibleAWSModule, m_describe_db_instances): + m_module = MagicMock() + m_AnsibleAWSModule.return_value = m_module + m_describe_db_instances.side_effect = a_boto_exception() + + rds_instance_info.main() + + m_module.client.assert_called_with("rds", retry_decorator=ANY) + m_module.fail_json_aws.assert_called_with(ANY, "Couldn't get instance information")