From ede0dcc85635a544bdc30c45c0e90485a4f14acb Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 16 Apr 2022 06:31:05 +0000 Subject: [PATCH] rds_instance - fix check_mode and idempotence bugs and support adding/removing iam roles (#1002) (#1055) [PR #1002/c403552f backport][stable-3] rds_instance - fix check_mode and idempotence bugs and support adding/removing iam roles This is a backport of PR #1002 as merged into main (c403552). SUMMARY Support the addition and deletion of iam roles to db instances Fixes #464 Fixes #1013 Integration tests to test both this and the amazon.aws module_util rds changes Depends-On ansible-collections/amazon.aws#714 ISSUE TYPE Feature Pull Request COMPONENT NAME rds_instance ADDITIONAL INFORMATION Wasn't sure the best way to go about deleting IAM roles - ended up using a purge_iam_roles param that defaults to False, which seems consistent with other modules I've looked at. Reviewed-by: Mark Chappell Reviewed-by: Markus Bergholz --- ...stance-stabilize-and-support-iam-roles.yml | 4 + plugins/modules/rds_instance.py | 174 +++++++- .../targets/rds_instance/inventory | 3 + .../roles/rds_instance/defaults/main.yml | 4 + .../files/s3_integration_policy.json | 16 + .../files/s3_integration_trust_policy.json | 13 + .../roles/rds_instance/tasks/test_complex.yml | 74 ++++ .../rds_instance/tasks/test_iam_roles.yml | 400 ++++++++++++++++++ .../roles/rds_instance/tasks/test_modify.yml | 27 ++ .../rds_instance/tasks/test_processor.yml | 41 +- .../roles/rds_instance/tasks/test_replica.yml | 92 +++- .../roles/rds_instance/tasks/test_restore.yml | 44 +- .../roles/rds_instance/tasks/test_sgroups.yml | 130 +++++- .../roles/rds_instance/tasks/test_states.yml | 166 +++++++- .../roles/rds_instance/tasks/test_tagging.yml | 62 +++ .../roles/rds_instance/tasks/test_upgrade.yml | 46 +- 16 files changed, 1254 insertions(+), 42 deletions(-) create mode 100644 changelogs/fragments/1002-rds_instance-stabilize-and-support-iam-roles.yml create mode 100644 tests/integration/targets/rds_instance/roles/rds_instance/files/s3_integration_policy.json create mode 100644 tests/integration/targets/rds_instance/roles/rds_instance/files/s3_integration_trust_policy.json create mode 100644 tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_iam_roles.yml diff --git a/changelogs/fragments/1002-rds_instance-stabilize-and-support-iam-roles.yml b/changelogs/fragments/1002-rds_instance-stabilize-and-support-iam-roles.yml new file mode 100644 index 00000000000..141cc05b1a8 --- /dev/null +++ b/changelogs/fragments/1002-rds_instance-stabilize-and-support-iam-roles.yml @@ -0,0 +1,4 @@ +minor_changes: + - rds_instance - add support for addition/removal of iam roles to db instance (https://github.com/ansible-collections/community.aws/pull/1002). +bugfixes: + - rds_instance - fix check_mode and idempotency issues and added integration tests for all tests in suite (https://github.com/ansible-collections/community.aws/pull/1002). diff --git a/plugins/modules/rds_instance.py b/plugins/modules/rds_instance.py index cdd0d13fa90..4ae96546a0c 100644 --- a/plugins/modules/rds_instance.py +++ b/plugins/modules/rds_instance.py @@ -205,6 +205,23 @@ description: - Set to true to conduct the reboot through a MultiAZ failover. type: bool + iam_roles: + description: + - List of Amazon Web Services Identity and Access Management (IAM) roles to associate with DB instance. + type: list + elements: dict + suboptions: + feature_name: + description: + - The name of the feature associated with the IAM role. + type: str + required: yes + role_arn: + description: + - The ARN of the IAM role to associate with the DB instance. + type: str + required: yes + version_added: 3.3.0 iops: description: - The Provisioned IOPS (I/O operations per second) value. Is only set when using I(storage_type) is set to io1. @@ -316,6 +333,12 @@ a publicly resolvable DNS name, which resolves to a public IP address. A value of false specifies an internal instance with a DNS name that resolves to a private IP address. type: bool + purge_iam_roles: + description: + - Set to C(True) to remove any IAM roles that aren't specified in the task and are associated with the instance. + type: bool + default: False + version_added: 3.3.0 restore_time: description: - If using I(creation_source=instance) this indicates the UTC date and time to restore from the source instance. @@ -462,7 +485,49 @@ vpc_security_group_ids: - sg-0be17ba10c9286b0b purge_security_groups: false - register: result + register: result + +# Add IAM role to db instance +- name: Create IAM policy + community.aws.iam_managed_policy: + policy_name: "my-policy" + policy: "{{ lookup('file','files/policy.json') }}" + state: present + register: iam_policy + +- name: Create IAM role + community.aws.iam_role: + assume_role_policy_document: "{{ lookup('file','files/assume_policy.json') }}" + name: "my-role" + state: present + managed_policy: "{{ iam_policy.policy.arn }}" + register: iam_role + +- name: Create DB instance with added IAM role + community.aws.rds_instance: + id: "my-instance-id" + state: present + engine: postgres + engine_version: 14.2 + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: db.m6g.large + allocated_storage: "{{ allocated_storage }}" + iam_roles: + - role_arn: "{{ iam_role.arn }}" + feature_name: 's3Export' + +- name: Remove IAM role from DB instance + community.aws.rds_instance: + id: "my-instance-id" + state: present + engine: postgres + engine_version: 14.2 + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: db.m6g.large + allocated_storage: "{{ allocated_storage }}" + purge_iam_roles: yes ''' RETURN = r''' @@ -780,16 +845,23 @@ from ansible_collections.amazon.aws.plugins.module_utils.core import get_boto3_client_method_parameters from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict from ansible_collections.amazon.aws.plugins.module_utils.rds import arg_spec_to_rds_params from ansible_collections.amazon.aws.plugins.module_utils.rds import call_method +from ansible_collections.amazon.aws.plugins.module_utils.rds import compare_iam_roles from ansible_collections.amazon.aws.plugins.module_utils.rds import ensure_tags from ansible_collections.amazon.aws.plugins.module_utils.rds import get_final_identifier from ansible_collections.amazon.aws.plugins.module_utils.rds import get_rds_method_attribute from ansible_collections.amazon.aws.plugins.module_utils.rds import get_tags +from ansible_collections.amazon.aws.plugins.module_utils.rds import update_iam_roles + valid_engines = ['aurora', 'aurora-mysql', 'aurora-postgresql', 'mariadb', 'mysql', 'oracle-ee', 'oracle-ee-cdb', 'oracle-se2', 'oracle-se2-cdb', 'postgres', 'sqlserver-ee', 'sqlserver-se', 'sqlserver-ex', 'sqlserver-web'] +valid_engines_iam_roles = ['aurora-postgresql', 'oracle-ee', 'oracle-ee-cdb', 'oracle-se2', 'oracle-se2-cdb', + 'postgres', 'sqlserver-ee', 'sqlserver-se', 'sqlserver-ex', 'sqlserver-web'] + def get_rds_method_attribute_name(instance, state, creation_source, read_replica): method_name = None @@ -945,23 +1017,21 @@ def get_current_attributes_with_inconsistent_keys(instance): options['DBSecurityGroups'] = [sg['DBSecurityGroupName'] for sg in instance['DBSecurityGroups'] if sg['Status'] in ['adding', 'active']] options['VpcSecurityGroupIds'] = [sg['VpcSecurityGroupId'] for sg in instance['VpcSecurityGroups'] if sg['Status'] in ['adding', 'active']] options['DBParameterGroupName'] = [parameter_group['DBParameterGroupName'] for parameter_group in instance['DBParameterGroups']] - options['AllowMajorVersionUpgrade'] = None options['EnableIAMDatabaseAuthentication'] = instance['IAMDatabaseAuthenticationEnabled'] # PerformanceInsightsEnabled is not returned on older RDS instances it seems options['EnablePerformanceInsights'] = instance.get('PerformanceInsightsEnabled', False) - options['MasterUserPassword'] = None options['NewDBInstanceIdentifier'] = instance['DBInstanceIdentifier'] + # Neither of these are returned via describe_db_instances, so if either is specified during a check_mode run, changed=True + options['AllowMajorVersionUpgrade'] = None + options['MasterUserPassword'] = None + return options def get_changing_options_with_inconsistent_keys(modify_params, instance, purge_cloudwatch_logs, purge_security_groups): changing_params = {} current_options = get_current_attributes_with_inconsistent_keys(instance) - - if current_options.get("MaxAllocatedStorage") is None: - current_options["MaxAllocatedStorage"] = None - for option in current_options: current_option = current_options[option] desired_option = modify_params.pop(option, None) @@ -982,9 +1052,14 @@ def get_changing_options_with_inconsistent_keys(modify_params, instance, purge_c if desired_option in current_option: continue - if current_option == desired_option: + # Current option and desired option are the same - continue loop + if option != 'ProcessorFeatures' and current_option == desired_option: + continue + + if option == 'ProcessorFeatures' and current_option == boto3_tag_list_to_ansible_dict(desired_option, 'Name', 'Value'): continue + # Current option and desired option are different - add to changing_params list if option == 'ProcessorFeatures' and desired_option == []: changing_params['UseDefaultProcessorFeatures'] = True elif option == 'CloudwatchLogsExportConfiguration': @@ -1074,13 +1149,48 @@ def update_instance(client, module, instance, instance_id): def promote_replication_instance(client, module, instance, read_replica): changed = False if read_replica is False: - changed = bool(instance.get('ReadReplicaSourceDBInstanceIdentifier') or instance.get('StatusInfos')) - if changed: - try: - call_method(client, module, method_name='promote_read_replica', parameters={'DBInstanceIdentifier': instance['DBInstanceIdentifier']}) - changed = True - except is_boto3_error_message('DB Instance is not a read replica'): - pass + # 'StatusInfos' only exists when the instance is a read replica + # See https://awscli.amazonaws.com/v2/documentation/api/latest/reference/rds/describe-db-instances.html + if bool(instance.get('StatusInfos')): + try: + result, changed = call_method(client, module, method_name='promote_read_replica', + parameters={'DBInstanceIdentifier': instance['DBInstanceIdentifier']}) + except is_boto3_error_message('DB Instance is not a read replica'): + pass + return changed + + +def ensure_iam_roles(client, module, instance_id): + ''' + Ensure specified IAM roles are associated with DB instance + + Parameters: + client: RDS client + module: AWSModule + instance_id: DB's instance ID + + Returns: + changed (bool): True if changes were successfully made to DB instance's IAM roles; False if not + ''' + instance = camel_dict_to_snake_dict(get_instance(client, module, instance_id), ignore_list=['Tags', 'ProcessorFeatures']) + + # Ensure engine type supports associating IAM roles + engine = instance.get('engine') + if engine not in valid_engines_iam_roles: + module.fail_json(msg='DB engine {0} is not valid for adding IAM roles. Valid engines are {1}'.format(engine, valid_engines_iam_roles)) + + changed = False + purge_iam_roles = module.params.get('purge_iam_roles') + target_roles = module.params.get('iam_roles') if module.params.get('iam_roles') else [] + existing_roles = instance.get('associated_roles', []) + roles_to_add, roles_to_remove = compare_iam_roles(existing_roles, target_roles, purge_iam_roles) + if bool(roles_to_add or roles_to_remove): + changed = True + # Don't update on check_mode + if module.check_mode: + module.exit_json(changed=changed, **instance) + else: + update_iam_roles(client, module, instance_id, roles_to_add, roles_to_remove) return changed @@ -1121,6 +1231,7 @@ def main(): creation_source=dict(choices=['snapshot', 's3', 'instance']), force_update_password=dict(type='bool', default=False, no_log=False), purge_cloudwatch_logs_exports=dict(type='bool', default=True), + purge_iam_roles=dict(type='bool', default=False), purge_tags=dict(type='bool', default=True), read_replica=dict(type='bool'), wait=dict(type='bool', default=True), @@ -1154,6 +1265,7 @@ def main(): engine_version=dict(), final_db_snapshot_identifier=dict(aliases=['final_snapshot_identifier']), force_failover=dict(type='bool'), + iam_roles=dict(type='list', elements='dict'), iops=dict(type='int'), kms_key_id=dict(), license_model=dict(), @@ -1230,6 +1342,13 @@ def main(): if module.params['preferred_maintenance_window']: module.params['preferred_maintenance_window'] = module.params['preferred_maintenance_window'].lower() + # Throw warning regarding case when allow_major_version_upgrade is specified in check_mode + # describe_rds_instance never returns this value, so on check_mode, it will always return changed=True + # In non-check mode runs, changed will return the correct value, so no need to warn there. + # see: amazon.aws.module_util.rds.handle_errors. + if module.params.get('allow_major_version_upgrade') and module.check_mode: + module.warn('allow_major_version_upgrade is not returned when describing db instances, so changed will always be `True` on check mode runs.') + client = module.client('rds') changed = False state = module.params['state'] @@ -1239,17 +1358,30 @@ def main(): method_name = get_rds_method_attribute_name(instance, state, module.params['creation_source'], module.params['read_replica']) if method_name: + + # Exit on create/delete if check_mode + if module.check_mode and method_name in ['create_db_instance', 'delete_db_instance']: + module.exit_json(changed=True, **camel_dict_to_snake_dict(instance, ignore_list=['Tags', 'ProcessorFeatures'])) + raw_parameters = arg_spec_to_rds_params(dict((k, module.params[k]) for k in module.params if k in parameter_options)) - parameters = get_parameters(client, module, raw_parameters, method_name) + parameters_to_modify = get_parameters(client, module, raw_parameters, method_name) - if parameters: - result, changed = call_method(client, module, method_name, parameters) + if parameters_to_modify: + # Exit on check_mode when parameters to modify + if module.check_mode: + module.exit_json(changed=True, **camel_dict_to_snake_dict(instance, ignore_list=['Tags', 'ProcessorFeatures'])) + result, changed = call_method(client, module, method_name, parameters_to_modify) instance_id = get_final_identifier(method_name, module) - # Check tagging/promoting/rebooting/starting/stopping instance - if state != 'absent' and (not module.check_mode or instance): - changed |= update_instance(client, module, instance, instance_id) + if state != 'absent': + # Check tagging/promoting/rebooting/starting/stopping instance + if not module.check_mode or instance: + changed |= update_instance(client, module, instance, instance_id) + + # Check IAM roles + if module.params.get('iam_roles') or module.params.get('purge_iam_roles'): + changed |= ensure_iam_roles(client, module, instance_id) if changed: instance = get_instance(client, module, instance_id) diff --git a/tests/integration/targets/rds_instance/inventory b/tests/integration/targets/rds_instance/inventory index e0443d829d6..a8774ec9907 100644 --- a/tests/integration/targets/rds_instance/inventory +++ b/tests/integration/targets/rds_instance/inventory @@ -15,6 +15,9 @@ tagging replica upgrade +# TODO: uncomment after adding iam:CreatePolicy and iam:DeletePolicy +# iam_roles + # TODO: uncomment after adding rds_cluster module # aurora diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/defaults/main.yml b/tests/integration/targets/rds_instance/roles/rds_instance/defaults/main.yml index 10f39d1ee22..0dd2db59c2b 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/defaults/main.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/defaults/main.yml @@ -29,3 +29,7 @@ modified_processor_features: # For mariadb tests mariadb_engine_version: 10.3.31 mariadb_engine_version_2: 10.4.21 + +# For iam roles tests +postgres_db_instance_class: db.m6g.large # smallest psql instance +postgres_db_engine_version: 14.2 diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/files/s3_integration_policy.json b/tests/integration/targets/rds_instance/roles/rds_instance/files/s3_integration_policy.json new file mode 100644 index 00000000000..71f07d07c3b --- /dev/null +++ b/tests/integration/targets/rds_instance/roles/rds_instance/files/s3_integration_policy.json @@ -0,0 +1,16 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Action": [ + "s3:PutObject", + "s3:GetObject", + "s3:ListBucket", + "rds:*" + ], + "Resource": "*" + } + ] +} diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/files/s3_integration_trust_policy.json b/tests/integration/targets/rds_instance/roles/rds_instance/files/s3_integration_trust_policy.json new file mode 100644 index 00000000000..9ea5ec3b42a --- /dev/null +++ b/tests/integration/targets/rds_instance/roles/rds_instance/files/s3_integration_trust_policy.json @@ -0,0 +1,13 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Principal": { + "Service": "rds.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_complex.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_complex.yml index c9d8b3a4c5f..7e3ef087c32 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_complex.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_complex.yml @@ -41,11 +41,59 @@ - result.changed - "result.db_instance_identifier == '{{ instance_id }}'" + - name: Add IAM roles to mariab (should fail - iam roles not supported for mariadb) + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + engine_version: "{{ mariadb_engine_version }}" + allow_major_version_upgrade: true + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ io1_allocated_storage }}" + storage_type: "{{ storage_type }}" + iops: "{{ iops }}" + iam_roles: + - role_arn: 'my_role' + feature_name: 'my_feature' + register: result + ignore_errors: True + + - assert: + that: + - result.failed + - '"is not valid for adding IAM roles" in result.msg' + # TODO: test modifying db_subnet_group_name, db_security_groups, db_parameter_group_name, option_group_name, # monitoring_role_arn, monitoring_interval, domain, domain_iam_role_name, cloudwatch_logs_export_configuration # Test multiple modifications including enabling enhanced monitoring + - name: Modify several attributes - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + allocated_storage: "{{ io1_modified_allocated_storage }}" + storage_type: "{{ storage_type }}" + db_instance_class: "{{ modified_db_instance_class }}" + backup_retention_period: 2 + preferred_backup_window: "05:00-06:00" + preferred_maintenance_window: "{{ preferred_maintenance_window }}" + auto_minor_version_upgrade: false + monitoring_interval: "{{ monitoring_interval }}" + monitoring_role_arn: "{{ enhanced_monitoring_role.arn }}" + iops: "{{ iops }}" + port: 1150 + max_allocated_storage: 150 + apply_immediately: True + register: result + check_mode: yes + + - assert: + that: + - result.changed + - name: Modify several attributes rds_instance: id: "{{ instance_id }}" @@ -74,6 +122,32 @@ - '"db_instance_class" in result.pending_modified_values or result.db_instance_class == modified_db_instance_class' - '"monitoring_interval" in result.pending_modified_values or result.monitoring_interval == monitoring_interval' + - name: Idempotence modifying several pending attributes - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + allocated_storage: "{{ io1_modified_allocated_storage }}" + storage_type: "{{ storage_type }}" + db_instance_class: "{{ modified_db_instance_class }}" + backup_retention_period: 2 + preferred_backup_window: "05:00-06:00" + preferred_maintenance_window: "{{ preferred_maintenance_window }}" + auto_minor_version_upgrade: false + monitoring_interval: "{{ monitoring_interval }}" + monitoring_role_arn: "{{ enhanced_monitoring_role.arn }}" + iops: "{{ iops }}" + port: 1150 + max_allocated_storage: 150 + register: result + retries: 30 + delay: 10 + until: result is not failed + check_mode: yes + + - assert: + that: + - not result.changed + - name: Idempotence modifying several pending attributes rds_instance: id: "{{ instance_id }}" diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_iam_roles.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_iam_roles.yml new file mode 100644 index 00000000000..a12919d0f0e --- /dev/null +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_iam_roles.yml @@ -0,0 +1,400 @@ +--- +- block: + - name: Ensure the resource doesn't exist + rds_instance: + id: "{{ instance_id }}" + state: absent + skip_final_snapshot: True + register: result + + - assert: + that: + - not result.changed + ignore_errors: yes + + - name: Create s3 integration policy + iam_managed_policy: + policy_name: "{{ instance_id }}-s3-policy" + policy: "{{ lookup('file','files/s3_integration_policy.json') }}" + state: present + register: s3_integration_policy + + - name: Create an s3 integration role + iam_role: + assume_role_policy_document: "{{ lookup('file','files/s3_integration_trust_policy.json') }}" + name: "{{ instance_id }}-s3-role-1" + state: present + managed_policy: "{{ s3_integration_policy.policy.arn }}" + register: s3_integration_role_1 + + - name: Create an s3 integration role + iam_role: + assume_role_policy_document: "{{ lookup('file','files/s3_integration_trust_policy.json') }}" + name: "{{ instance_id }}-s3-role-2" + state: present + managed_policy: "{{ s3_integration_policy.policy.arn }}" + register: s3_integration_role_2 + + - name: Create an s3 integration role + iam_role: + assume_role_policy_document: "{{ lookup('file','files/s3_integration_trust_policy.json') }}" + name: "{{ instance_id }}-s3-role-3" + state: present + managed_policy: "{{ s3_integration_policy.policy.arn }}" + register: s3_integration_role_3 + + # ------------------------------------------------------------------------------------------ + + - name: Create DB instance with IAM roles - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: postgres + engine_version: "{{ postgres_db_engine_version }}" + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ postgres_db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + allow_major_version_upgrade: yes + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + - role_arn: "{{ s3_integration_role_2.arn }}" + feature_name: 'Lambda' + - role_arn: "{{ s3_integration_role_3.arn }}" + feature_name: 's3Import' + register: result + check_mode: yes + + - assert: + that: + - result.changed + + - name: Create DB instance with IAM roles + rds_instance: + id: "{{ instance_id }}" + state: present + engine: postgres + engine_version: "{{ postgres_db_engine_version }}" + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ postgres_db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + allow_major_version_upgrade: yes + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + - role_arn: "{{ s3_integration_role_2.arn }}" + feature_name: 'Lambda' + - role_arn: "{{ s3_integration_role_3.arn }}" + feature_name: 's3Import' + register: result + + - assert: + that: + - result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - result.associated_roles | length == 3 + - "{{ 's3Export' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 'Lambda' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 's3Import' in result.associated_roles | map(attribute='feature_name') }}" + + - name: Create DB instance with IAM roles (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: postgres + engine_version: "{{ postgres_db_engine_version }}" + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ postgres_db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + - role_arn: "{{ s3_integration_role_2.arn }}" + feature_name: 'Lambda' + - role_arn: "{{ s3_integration_role_3.arn }}" + feature_name: 's3Import' + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Create DB instance with IAM roles (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: present + engine: postgres + engine_version: "{{ postgres_db_engine_version }}" + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ postgres_db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + - role_arn: "{{ s3_integration_role_2.arn }}" + feature_name: 'Lambda' + - role_arn: "{{ s3_integration_role_3.arn }}" + feature_name: 's3Import' + register: result + + - assert: + that: + - not result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - result.associated_roles | length == 3 + - "{{ 's3Export' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 'Lambda' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 's3Import' in result.associated_roles | map(attribute='feature_name') }}" + + - name: Create DB instance with IAM roles (idempotence) - purge roles + rds_instance: + id: "{{ instance_id }}" + state: present + engine: postgres + engine_version: "{{ postgres_db_engine_version }}" + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ postgres_db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + - role_arn: "{{ s3_integration_role_2.arn }}" + feature_name: 'Lambda' + - role_arn: "{{ s3_integration_role_3.arn }}" + feature_name: 's3Import' + purge_iam_roles: yes + register: result + + - assert: + that: + - not result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - result.associated_roles | length == 3 + - "{{ 's3Export' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 'Lambda' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 's3Import' in result.associated_roles | map(attribute='feature_name') }}" + + # ------------------------------------------------------------------------------------------ + + - name: Remove s3Import IAM role from db instance - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + - role_arn: "{{ s3_integration_role_2.arn }}" + feature_name: 'Lambda' + purge_iam_roles: yes + register: result + check_mode: yes + + - assert: + that: + - result.changed + + - name: Remove s3Import IAM role from db instance + rds_instance: + id: "{{ instance_id }}" + state: present + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + - role_arn: "{{ s3_integration_role_2.arn }}" + feature_name: 'Lambda' + purge_iam_roles: yes + register: result + + - assert: + that: + - result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - result.associated_roles | length == 2 + - "{{ 's3Export' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 'Lambda' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 's3Import' not in result.associated_roles | map(attribute='feature_name') }}" + + - name: Remove s3Import IAM role from db instance (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + - role_arn: "{{ s3_integration_role_2.arn }}" + feature_name: 'Lambda' + purge_iam_roles: yes + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Remove s3Import IAM role from db instance (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: present + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + - role_arn: "{{ s3_integration_role_2.arn }}" + feature_name: 'Lambda' + purge_iam_roles: yes + register: result + + - assert: + that: + - not result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - result.associated_roles | length == 2 + - "{{ 's3Export' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 'Lambda' in result.associated_roles | map(attribute='feature_name') }}" + - "{{ 's3Import' not in result.associated_roles | map(attribute='feature_name') }}" + + # ------------------------------------------------------------------------------------------ + + - name: Remove IAM roles from db instance - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + purge_iam_roles: yes + register: result + check_mode: yes + + - assert: + that: + - result.changed + + - name: Remove IAM roles from db instance + rds_instance: + id: "{{ instance_id }}" + state: present + purge_iam_roles: yes + register: result + + - assert: + that: + - result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - result.associated_roles | length == 0 + + - name: Remove IAM roles from db instance (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + purge_iam_roles: yes + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Remove IAM roles from db instance (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: present + purge_iam_roles: yes + register: result + + - assert: + that: + - not result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - result.associated_roles | length == 0 + + # ------------------------------------------------------------------------------------------ + + - name: Add IAM role to existing db instance - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + register: result + check_mode: yes + + - assert: + that: + - result.changed + + - name: Add IAM role to existing db instance + rds_instance: + id: "{{ instance_id }}" + state: present + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + register: result + + - assert: + that: + - result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - result.associated_roles | length == 1 + - "{{ 's3Export' in result.associated_roles | map(attribute='feature_name') }}" + + - name: Add IAM role to existing db instance (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Add IAM role to existing db instance (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: present + + iam_roles: + - role_arn: "{{ s3_integration_role_1.arn }}" + feature_name: 's3Export' + register: result + + - assert: + that: + - not result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - result.associated_roles | length == 1 + - "{{ 's3Export' in result.associated_roles | map(attribute='feature_name') }}" + + always: + - name: Delete IAM policy + iam_managed_policy: + policy_name: "{{ instance_id }}-s3-policy" + state: absent + ignore_errors: yes + + - name: Delete IAM roles + iam_role: + name: "{{ item.role_name }}" + assume_role_policy_document: "{{ lookup('file','files/s3_integration_trust_policy.json') }}" + state: absent + ignore_errors: yes + with_items: + - "{{ s3_integration_role_1 }}" + - "{{ s3_integration_role_2 }}" + - "{{ s3_integration_role_3 }}" + + - name: Delete the instance + rds_instance: + id: "{{ instance_id }}" + state: absent + skip_final_snapshot: True + wait: false + ignore_errors: yes diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_modify.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_modify.yml index bfe04be2fbe..7206d8956af 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_modify.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_modify.yml @@ -39,6 +39,20 @@ - result.changed - "result.db_instance_identifier == '{{ instance_id }}'" + - name: Modify the instance name without immediate application - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + new_id: "{{ modified_instance_id }}" + password: "{{ password }}" + apply_immediately: False + register: result + check_mode: yes + + - assert: + that: + - result.changed + - name: Modify the instance name without immediate application rds_instance: id: "{{ instance_id }}" @@ -53,6 +67,19 @@ - result.changed - 'result.db_instance_identifier == "{{ instance_id }}"' + - name: Immediately apply the pending update - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + new_id: "{{ modified_instance_id }}" + apply_immediately: True + register: result + check_mode: yes + + - assert: + that: + - result.changed + - name: Immediately apply the pending update rds_instance: id: "{{ instance_id }}" diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_processor.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_processor.yml index c0ba221cf34..453097c1c45 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_processor.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_processor.yml @@ -30,7 +30,7 @@ that: - result.changed - - name: Check mode - modify the processor features + - name: Modify the processor features - check_mode rds_instance: id: "{{ instance_id }}" state: present @@ -69,6 +69,45 @@ - 'result.pending_modified_values.processor_features.coreCount == "{{ modified_processor_features.coreCount }}"' - 'result.pending_modified_values.processor_features.threadsPerCore == "{{ modified_processor_features.threadsPerCore }}"' + - name: Modify the processor features (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: oracle-ee + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ oracle_ee_db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + storage_encrypted: True + processor_features: "{{ modified_processor_features }}" + apply_immediately: true + register: result + check_mode: True + + - assert: + that: + - not result.changed + + - name: Modify the processor features (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: present + engine: oracle-ee + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ oracle_ee_db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + storage_encrypted: True + processor_features: "{{ modified_processor_features }}" + apply_immediately: true + register: result + + - assert: + that: + - not result.changed + - 'result.pending_modified_values.processor_features.coreCount == "{{ modified_processor_features.coreCount }}"' + - 'result.pending_modified_values.processor_features.threadsPerCore == "{{ modified_processor_features.threadsPerCore }}"' + always: - name: Delete the DB instance diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_replica.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_replica.yml index 7ca19747dde..80c02c1c327 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_replica.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_replica.yml @@ -40,6 +40,31 @@ - source_db.changed - "source_db.db_instance_identifier == '{{ instance_id }}'" + # ------------------------------------------------------------------------------------------ + + - name: Create a read replica in a different region - check_mode + rds_instance: + id: "{{ instance_id }}-replica" + state: present + source_db_instance_identifier: "{{ instance_id }}" + engine: mysql + username: "{{ username }}" + password: "{{ password }}" + read_replica: True + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + region: "{{ region_dest }}" + tags: + Name: "{{ instance_id }}" + Created_by: Ansible rds_instance tests + wait: yes + register: result + check_mode: yes + + - assert: + that: + - result.changed + - name: Create a read replica in a different region rds_instance: id: "{{ instance_id }}-replica" @@ -66,6 +91,27 @@ - "result.tags.Name == '{{ instance_id }}'" - "result.tags.Created_by == 'Ansible rds_instance tests'" + - name: Test idempotence with a read replica - check_mode + rds_instance: + id: "{{ instance_id }}-replica" + state: present + source_db_instance_identifier: "{{ instance_id }}" + engine: mysql + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + region: "{{ region_dest }}" + tags: + Name: "{{ instance_id }}" + Created_by: Ansible rds_instance tests + register: result + check_mode: yes + + - assert: + that: + - not result.changed + - name: Test idempotence with a read replica rds_instance: id: "{{ instance_id }}-replica" @@ -82,10 +128,9 @@ Created_by: Ansible rds_instance tests register: result - ## XXX Bug - # - assert: - # that: - # - not result.changed + - assert: + that: + - not result.changed - name: Test idempotence with read_replica=True rds_instance: @@ -104,6 +149,25 @@ Created_by: Ansible rds_instance tests register: result + - assert: + that: + - not result.changed + + # ------------------------------------------------------------------------------------------ + + - name: Promote the read replica - check_mode + rds_instance: + id: "{{ instance_id }}-replica" + state: present + read_replica: False + region: "{{ region_dest }}" + register: result + check_mode: yes + + - assert: + that: + - result.changed + - name: Promote the read replica rds_instance: id: "{{ instance_id }}-replica" @@ -116,6 +180,19 @@ that: - result.changed + - name: Test idempotence - check_mode + rds_instance: + id: "{{ instance_id }}-replica" + state: present + read_replica: False + region: "{{ region_dest }}" + register: result + check_mode: yes + + - assert: + that: + - not result.changed + - name: Test idempotence rds_instance: id: "{{ instance_id }}-replica" @@ -124,10 +201,9 @@ region: "{{ region_dest }}" register: result - ## XXX Bug - #- assert: - # that: - # - not result.changed + - assert: + that: + - not result.changed always: diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_restore.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_restore.yml index 3390f5759e7..5cfaf194c79 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_restore.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_restore.yml @@ -31,6 +31,25 @@ - source_db.changed - "source_db.db_instance_identifier == '{{ instance_id }}-s'" + - name: Create a point in time DB instance - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + source_db_instance_identifier: "{{ instance_id }}-s" + creation_source: instance + engine: mysql + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + use_latest_restorable_time: True + register: result + check_mode: yes + + - assert: + that: + result.changed + - name: Create a point in time DB instance rds_instance: id: "{{ instance_id }}" @@ -45,7 +64,30 @@ use_latest_restorable_time: True register: result - - name: Test idempotence with a point in time replica + - assert: + that: + result.changed + + - name: Create a point in time DB instance (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + source_db_instance_identifier: "{{ instance_id }}-s" + creation_source: instance + engine: mysql + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + restore_time: "{{ result.latest_restorable_time }}" + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Create a point in time DB instance (idempotence) rds_instance: id: "{{ instance_id }}" state: present diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_sgroups.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_sgroups.yml index 82e63578554..4624aa5d27d 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_sgroups.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_sgroups.yml @@ -40,8 +40,6 @@ - "{{ resource_prefix }}-sg-2" - "{{ resource_prefix }}-sg-3" - - debug: var=sgs_result - - name: Ensure the resource doesn't exist rds_instance: id: "{{ instance_id }}" @@ -54,6 +52,27 @@ - not result.changed ignore_errors: yes + # ------------------------------------------------------------------------------------------ + + - name: Create a DB instance in the VPC with two security groups - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + vpc_security_group_ids: + - "{{ sgs_result.results.0.group_id }}" + - "{{ sgs_result.results.1.group_id }}" + register: result + check_mode: yes + + - assert: + that: + - result.changed + - name: Create a DB instance in the VPC with two security groups rds_instance: id: "{{ instance_id }}" @@ -74,7 +93,48 @@ - "result.db_instance_identifier == '{{ instance_id }}'" - "result.vpc_security_groups | selectattr('status', 'in', ['active', 'adding']) | list | length == 2" - - name: Add a new security group without purge (check_mode) + - name: Create a DB instance in the VPC with two security groups (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + vpc_security_group_ids: + - "{{ sgs_result.results.0.group_id }}" + - "{{ sgs_result.results.1.group_id }}" + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Create a DB instance in the VPC with two security groups (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + vpc_security_group_ids: + - "{{ sgs_result.results.0.group_id }}" + - "{{ sgs_result.results.1.group_id }}" + register: result + + - assert: + that: + - not result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - "result.vpc_security_groups | selectattr('status', 'in', ['active', 'adding']) | list | length == 2" + + # ------------------------------------------------------------------------------------------ + + - name: Add a new security group without purge - check_mode rds_instance: id: "{{ instance_id }}" state: present @@ -106,7 +166,23 @@ - "result.db_instance_identifier == '{{ instance_id }}'" - "result.vpc_security_groups | selectattr('status', 'in', ['active', 'adding']) | list | length == 3" - - name: Add a new security group without purge (test idempotence) + - name: Add a new security group without purge (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + vpc_security_group_ids: + - "{{ sgs_result.results.2.group_id }}" + apply_immediately: true + purge_security_groups: false + register: result + check_mode: yes + + - assert: + that: + - not result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + + - name: Add a new security group without purge (idempotence) rds_instance: id: "{{ instance_id }}" state: present @@ -120,6 +196,23 @@ that: - not result.changed - "result.db_instance_identifier == '{{ instance_id }}'" + - "result.vpc_security_groups | selectattr('status', 'in', ['active', 'adding']) | list | length == 3" + + # ------------------------------------------------------------------------------------------ + + - name: Add a security group with purge - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + vpc_security_group_ids: + - "{{ sgs_result.results.2.group_id }}" + apply_immediately: true + register: result + check_mode: yes + + - assert: + that: + - result.changed - name: Add a security group with purge rds_instance: @@ -137,6 +230,35 @@ - "result.vpc_security_groups | selectattr('status', 'in', ['active', 'adding']) | list | length == 1" - "result.vpc_security_groups | selectattr('status', 'equalto', 'removing') | list | length == 2" + - name: Add a security group with purge (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + vpc_security_group_ids: + - "{{ sgs_result.results.2.group_id }}" + apply_immediately: true + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Add a security group with purge (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: present + vpc_security_group_ids: + - "{{ sgs_result.results.2.group_id }}" + apply_immediately: true + register: result + + - assert: + that: + - not result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - "result.vpc_security_groups | selectattr('status', 'in', ['active', 'adding']) | list | length == 1" + always: - name: Ensure the resource doesn't exist diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_states.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_states.yml index c0d36b85943..dee43a77405 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_states.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_states.yml @@ -12,6 +12,28 @@ - not result.changed ignore_errors: yes + + # ------------------------------------------------------------------------------------------ + + - name: Create a mariadb instance - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + tags: + Name: "{{ instance_id }}" + Created_by: Ansible rds_instance tests + register: result + check_mode: yes + + - assert: + that: + - result.changed + - name: Create a mariadb instance rds_instance: id: "{{ instance_id }}" @@ -34,8 +56,51 @@ - "result.tags.Name == '{{ instance_id }}'" - "result.tags.Created_by == 'Ansible rds_instance tests'" + - name: Create a mariadb instance (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + tags: + Name: "{{ instance_id }}" + Created_by: Ansible rds_instance tests + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Create a mariadb instance (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + tags: + Name: "{{ instance_id }}" + Created_by: Ansible rds_instance tests + register: result + + - assert: + that: + - not result.changed + - "result.db_instance_identifier == '{{ instance_id }}'" + - "result.tags | length == 2" + - "result.tags.Name == '{{ instance_id }}'" + - "result.tags.Created_by == 'Ansible rds_instance tests'" + + # ------------------------------------------------------------------------------------------ # Test stopping / rebooting instances - - name: Check mode - reboot a stopped instance + + - name: Reboot a stopped instance - check_mode rds_instance: id: "{{ instance_id }}" state: rebooted @@ -56,7 +121,9 @@ that: - result.changed - - name: Check Mode - stop the instance + # ------------------------------------------------------------------------------------------ + + - name: Stop the instance - check_mode rds_instance: id: "{{ instance_id }}" state: stopped @@ -77,7 +144,7 @@ that: - result.changed - - name: Check Mode - idempotence + - name: Stop the instance (idempotence) - check_mode rds_instance: id: "{{ instance_id }}" state: stopped @@ -88,7 +155,7 @@ that: - not result.changed - - name: Idempotence + - name: Stop the instance (idempotence) rds_instance: id: "{{ instance_id }}" state: stopped @@ -98,7 +165,9 @@ that: - not result.changed - - name: Check Mode - start the instance + # ------------------------------------------------------------------------------------------ + + - name: Start the instance - check_mode rds_instance: id: "{{ instance_id }}" state: started @@ -119,6 +188,93 @@ that: - result.changed + - name: Start the instance (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: started + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Start the instance (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: started + register: result + + - assert: + that: + - not result.changed + + # ------------------------------------------------------------------------------------------ + + - name: Ensure instance exists prior to deleting + rds_instance_info: + db_instance_identifier: '{{ instance_id }}' + register: db_info + + - assert: + that: + - db_info.instances | length == 1 + + - name: Delete the instance - check_mode + rds_instance: + id: "{{ instance_id }}" + state: absent + skip_final_snapshot: True + register: result + check_mode: yes + + - assert: + that: + - result.changed + + - name: Delete the instance + rds_instance: + id: "{{ instance_id }}" + state: absent + skip_final_snapshot: True + register: result + + - assert: + that: + - result.changed + + - name: Ensure instance was deleted + rds_instance_info: + db_instance_identifier: '{{ instance_id }}' + register: db_info + + - assert: + that: + - db_info.instances | length == 0 + + - name: Delete the instance (idempotence) - check_mode + rds_instance: + id: "{{ instance_id }}" + state: absent + skip_final_snapshot: True + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Delete the instance (idempotence) + rds_instance: + id: "{{ instance_id }}" + state: absent + skip_final_snapshot: True + register: result + + - assert: + that: + - not result.changed + always: - name: Remove DB instance rds_instance: diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_tagging.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_tagging.yml index 469c2fc0bb2..a84cda69445 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_tagging.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_tagging.yml @@ -56,6 +56,22 @@ - result.kms_key_id - result.storage_encrypted == true + - name: Test impotency omitting tags - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + register: result + check_mode: yes + + - assert: + that: + - not result.changed + - name: Test impotency omitting tags rds_instance: id: "{{ instance_id }}" @@ -103,6 +119,21 @@ - not result.changed - "result.tags | length == 2" + - name: Add a tag and remove a tag - check_mode + rds_instance: + db_instance_identifier: "{{ instance_id }}" + state: present + tags: + Name: "{{ instance_id }}-new" + Created_by: Ansible rds_instance tests + purge_tags: True + register: result + check_mode: yes + + - assert: + that: + - result.changed + - name: Add a tag and remove a tag rds_instance: db_instance_identifier: "{{ instance_id }}" @@ -119,6 +150,37 @@ - "result.tags | length == 2" - "result.tags.Name == '{{ instance_id }}-new'" + - name: Add a tag and remove a tag (idempotence) - check_mode + rds_instance: + db_instance_identifier: "{{ instance_id }}" + state: present + tags: + Name: "{{ instance_id }}-new" + Created_by: Ansible rds_instance tests + purge_tags: True + register: result + check_mode: yes + + - assert: + that: + - not result.changed + + - name: Add a tag and remove a tag (idempotence) + rds_instance: + db_instance_identifier: "{{ instance_id }}" + state: present + tags: + Name: "{{ instance_id }}-new" + Created_by: Ansible rds_instance tests + purge_tags: True + register: result + + - assert: + that: + - not result.changed + - "result.tags | length == 2" + - "result.tags.Name == '{{ instance_id }}-new'" + # Test final snapshot on deletion - name: Delete the DB instance rds_instance: diff --git a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_upgrade.yml b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_upgrade.yml index 90833bfd487..aee0ca5b27f 100644 --- a/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_upgrade.yml +++ b/tests/integration/targets/rds_instance/roles/rds_instance/tasks/test_upgrade.yml @@ -32,7 +32,26 @@ # Test upgrade of DB instance - - name: Uprade a mariadb instance + - name: Upgrade a mariadb instance - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + engine_version: "{{ mariadb_engine_version_2 }}" + allow_major_version_upgrade: true + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + apply_immediately: True + register: result + check_mode: yes + + - assert: + that: + - result.changed + + - name: Upgrade a mariadb instance rds_instance: id: "{{ instance_id }}" state: present @@ -51,7 +70,30 @@ - result.changed - '"engine_version" in result.pending_modified_values or result.engine_version == mariadb_engine_version_2' - - name: Idempotence uprading a mariadb instance + - name: Idempotence upgrading a mariadb instance - check_mode + rds_instance: + id: "{{ instance_id }}" + state: present + engine: mariadb + engine_version: "{{ mariadb_engine_version_2 }}" + allow_major_version_upgrade: true + username: "{{ username }}" + password: "{{ password }}" + db_instance_class: "{{ db_instance_class }}" + allocated_storage: "{{ allocated_storage }}" + register: result + retries: 30 + delay: 10 + until: result is not failed + check_mode: yes + + ### Specifying allow_major_version_upgrade with check_mode will always result in changed=True + ### since it's not returned in describe_db_instances api call + # - assert: + # that: + # - not result.changed + + - name: Idempotence upgrading a mariadb instance rds_instance: id: "{{ instance_id }}" state: present