diff --git a/plugins/modules/rds_cluster.py b/plugins/modules/rds_cluster.py new file mode 100644 index 00000000000..16f2ed5a97a --- /dev/null +++ b/plugins/modules/rds_cluster.py @@ -0,0 +1,1026 @@ +#!/usr/bin/python +# Copyright (c) 2022 Ansible Project +# Copyright (c) 2022 Alina Buzachis (@alinabuzachis) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: rds_cluster +version_added: "3.2.0" +short_description: rds_cluster module +description: + - Create, modify, and delete RDS clusters. +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 +author: + - Sloane Hertel (@s-hertel) + - Alina Buzachis (@alinabuzachis) +options: + # General module options + state: + description: Whether the snapshot should exist or not. + choices: ['present', 'absent'] + default: 'present' + type: str + creation_source: + description: Which source to use if creating from a template (an existing cluster, S3 bucket, or snapshot). + choices: ['snapshot', 's3', 'cluster'] + type: str + force_update_password: + description: + - Set to C(true) to update your cluster password with I(master_user_password). + - Since comparing passwords to determine if it needs to be updated is not possible this is set to C(false) by default to allow idempotence. + type: bool + default: false + promote: + description: Set to C(true) to promote a read replica cluster. + type: bool + default: false + purge_cloudwatch_logs_exports: + description: + - Whether or not to disable Cloudwatch logs enabled for the DB cluster that are not provided in I(enable_cloudwatch_logs_exports). + Set I(enable_cloudwatch_logs_exports) to an empty list to disable all. + type: bool + default: true + purge_tags: + description: + - Whether or not to remove tags assigned to the DB cluster if not specified in the playbook. To remove all tags + set I(tags) to an empty dictionary in conjunction with this. + type: bool + default: true + purge_security_groups: + description: + - Set to C(false) to retain any enabled security groups that aren't specified in the task and are associated with the cluster. + - Can be applied to I(vpc_security_group_ids) + type: bool + default: true + wait: + description: Whether to wait for the cluster to be available or deleted. + type: bool + default: true + # Options that have a corresponding boto3 parameter + apply_immediately: + description: + - A value that specifies whether modifying a cluster with I(new_db_cluster_identifier) and I(master_user_password) + should be applied as soon as possible, regardless of the I(preferred_maintenance_window) setting. If C(false), changes + are applied during the next maintenance window. + type: bool + default: false + availability_zones: + description: + - A list of EC2 Availability Zones that instances in the DB cluster can be created in. + May be used when creating a cluster or when restoring from S3 or a snapshot. + aliases: + - zones + - az + type: list + elements: str + backtrack_to: + description: + - The timestamp of the time to backtrack the DB cluster to in ISO 8601 format, such as "2017-07-08T18:00Z". + type: str + backtrack_window: + description: + - The target backtrack window, in seconds. To disable backtracking, set this value to C(0). + - If specified, this value must be set to a number from C(0) to C(259,200) (72 hours). + default: 0 + type: int + backup_retention_period: + description: + - The number of days for which automated backups are retained (must be within C(1) to C(35)). + May be used when creating a new cluster, when restoring from S3, or when modifying a cluster. + type: int + default: 1 + character_set_name: + description: + - The character set to associate with the DB cluster. + type: str + database_name: + description: + - The name for your database. If a name is not provided Amazon RDS will not create a database. + aliases: + - db_name + type: str + db_cluster_identifier: + description: + - The DB cluster (lowercase) identifier. The identifier must contain from 1 to 63 letters, numbers, or + hyphens and the first character must be a letter and may not end in a hyphen or contain consecutive hyphens. + aliases: + - cluster_id + - id + - cluster_name + type: str + required: true + db_cluster_parameter_group_name: + description: + - The name of the DB cluster parameter group to associate with this DB cluster. + If this argument is omitted when creating a cluster, the default DB cluster parameter group for the specified DB engine and version is used. + type: str + db_subnet_group_name: + description: + - A DB subnet group to associate with this DB cluster if not using the default. + type: str + enable_cloudwatch_logs_exports: + description: + - A list of log types that need to be enabled for exporting to CloudWatch Logs. + - Engine aurora-mysql supports C(audit), C(error), C(general) and C(slowquery). + - Engine aurora-postgresql supports C(postgresql). + type: list + elements: str + deletion_protection: + description: + - A value that indicates whether the DB cluster has deletion protection enabled. + The database can't be deleted when deletion protection is enabled. + By default, deletion protection is disabled. + type: bool + global_cluster_identifier: + description: + - The global cluster ID of an Aurora cluster that becomes the primary cluster in the new global database cluster. + type: str + enable_http_endpoint: + description: + - A value that indicates whether to enable the HTTP endpoint for an Aurora Serverless DB cluster. + By default, the HTTP endpoint is disabled. + type: bool + copy_tags_to_snapshot: + description: + - Indicates whether to copy all tags from the DB cluster to snapshots of the DB cluster. + The default is not to copy them. + type: bool + domain: + description: + - The Active Directory directory ID to create the DB cluster in. + type: str + domain_iam_role_name: + description: + - Specify the name of the IAM role to be used when making API calls to the Directory Service. + type: str + enable_global_write_forwarding: + description: + - A value that indicates whether to enable this DB cluster to forward write operations to the primary cluster of an Aurora global database. + By default, write operations are not allowed on Aurora DB clusters that are secondary clusters in an Aurora global database. + - This value can be only set on Aurora DB clusters that are members of an Aurora global database. + type: bool + enable_iam_database_authentication: + description: + - Enable mapping of AWS Identity and Access Management (IAM) accounts to database accounts. + If this option is omitted when creating the cluster, Amazon RDS sets this to C(false). + type: bool + engine: + description: + - The name of the database engine to be used for this DB cluster. This is required to create a cluster. + choices: + - aurora + - aurora-mysql + - aurora-postgresql + type: str + engine_version: + description: + - The version number of the database engine to use. + - For Aurora MySQL that could be C(5.6.10a), C(5.7.12). + - Aurora PostgreSQL example, C(9.6.3). + type: str + final_snapshot_identifier: + description: + - The DB cluster snapshot identifier of the new DB cluster snapshot created when I(skip_final_snapshot=false). + type: str + force_backtrack: + description: + - A boolean to indicate if the DB cluster should be forced to backtrack when binary logging is enabled. + Otherwise, an error occurs when binary logging is enabled. + type: bool + kms_key_id: + description: + - The AWS KMS key identifier (the ARN, unless you are creating a cluster in the same account that owns the + KMS key, in which case the KMS key alias may be used). + - If I(replication_source_identifier) specifies an encrypted source Amazon RDS will use the key used toe encrypt the source. + - If I(storage_encrypted=true) and and I(replication_source_identifier) is not provided, the default encryption key is used. + type: str + master_user_password: + description: + - An 8-41 character password for the master database user. + - The password can contain any printable ASCII character except "/", """, or "@". + - To modify the password use I(force_password_update). Use I(apply immediately) to change + the password immediately, otherwise it is updated during the next maintenance window. + aliases: + - password + type: str + master_username: + description: + - The name of the master user for the DB cluster. Must be 1-16 letters or numbers and begin with a letter. + aliases: + - username + type: str + new_db_cluster_identifier: + description: + - The new DB cluster (lowercase) identifier for the DB cluster when renaming a DB cluster. + - The identifier must contain from 1 to 63 letters, numbers, or hyphens and the first character must be a + letter and may not end in a hyphen or contain consecutive hyphens. + - Use I(apply_immediately) to rename immediately, otherwise it is updated during the next maintenance window. + aliases: + - new_cluster_id + - new_id + - new_cluster_name + type: str + option_group_name: + description: + - The option group to associate with the DB cluster. + type: str + port: + description: + - The port number on which the instances in the DB cluster accept connections. If not specified, Amazon RDS + defaults this to C(3306) if the I(engine) is C(aurora) and c(5432) if the I(engine) is C(aurora-postgresql). + type: int + preferred_backup_window: + description: + - The daily time range (in UTC) of at least 30 minutes, during which automated backups are created if automated backups are + enabled using I(backup_retention_period). The option must be in the format of "hh24:mi-hh24:mi" and not conflict with + I(preferred_maintenance_window). + aliases: + - backup_window + type: str + preferred_maintenance_window: + description: + - The weekly time range (in UTC) of at least 30 minutes, during which system maintenance can occur. The option must + be in the format "ddd:hh24:mi-ddd:hh24:mi" where ddd is one of Mon, Tue, Wed, Thu, Fri, Sat, Sun. + aliases: + - maintenance_window + type: str + replication_source_identifier: + description: + - The Amazon Resource Name (ARN) of the source DB instance or DB cluster if this DB cluster is created as a Read Replica. + aliases: + - replication_src_id + type: str + restore_to_time: + description: + - The UTC date and time to restore the DB cluster to. Must be in the format "2015-03-07T23:45:00Z". + - If this is not provided while restoring a cluster, I(use_latest_restorable_time) must be. + May not be specified if I(restore_type) is copy-on-write. + type: str + restore_type: + description: + - The type of restore to be performed. If not provided, Amazon RDS uses full-copy. + choices: + - full-copy + - copy-on-write + type: str + role_arn: + description: + - The Amazon Resource Name (ARN) of the IAM role to associate with the Aurora DB cluster, for example + "arn:aws:iam::123456789012:role/AuroraAccessRole" + type: str + s3_bucket_name: + description: + - The name of the Amazon S3 bucket that contains the data used to create the Amazon Aurora DB cluster. + type: str + s3_ingestion_role_arn: + description: + - The Amazon Resource Name (ARN) of the AWS Identity and Access Management (IAM) role that authorizes Amazon RDS to access + the Amazon S3 bucket on your behalf. + type: str + s3_prefix: + description: + - The prefix for all of the file names that contain the data used to create the Amazon Aurora DB cluster. + - If you do not specify a SourceS3Prefix value, then the Amazon Aurora DB cluster is created by using all of the files in the Amazon S3 bucket. + type: str + skip_final_snapshot: + description: + - Whether a final DB cluster snapshot is created before the DB cluster is deleted. + - If this is C(false), I(final_snapshot_identifier) must be provided. + type: bool + default: false + snapshot_identifier: + description: + - The identifier for the DB snapshot or DB cluster snapshot to restore from. + - You can use either the name or the ARN to specify a DB cluster snapshot. However, you can use only the ARN to specify a DB snapshot. + type: str + source_db_cluster_identifier: + description: + - The identifier of the source DB cluster from which to restore. + type: str + source_engine: + description: + - The identifier for the database engine that was backed up to create the files stored in the Amazon S3 bucket. + choices: + - mysql + type: str + source_engine_version: + description: + - The version of the database that the backup files were created from. + type: str + source_region: + description: + - The ID of the region that contains the source for the DB cluster. + type: str + storage_encrypted: + description: + - Whether the DB cluster is encrypted. + type: bool + tags: + description: + - A dictionary of key value pairs to assign the DB cluster. + type: dict + use_earliest_time_on_point_in_time_unavailable: + description: + - If I(backtrack_to) is set to a timestamp earlier than the earliest backtrack time, this value backtracks the DB cluster to + the earliest possible backtrack time. Otherwise, an error occurs. + type: bool + use_latest_restorable_time: + description: + - Whether to restore the DB cluster to the latest restorable backup time. Only one of I(use_latest_restorable_time) + and I(restore_to_time) may be provided. + type: bool + vpc_security_group_ids: + description: + - A list of EC2 VPC security groups to associate with the DB cluster. + type: list + elements: str +''' + +EXAMPLES = r''' +# Note: These examples do not set authentication details, see the AWS Guide for details. +- name: Create minimal aurora cluster in default VPC and default subnet group + community.aws.rds_cluster: + cluster_id: "{{ cluster_id }}" + engine: "aurora" + password: "{{ password }}" + username: "{{ username }}" + +- name: Add a new security group without purge + community.aws.rds_cluster: + id: "{{ cluster_id }}" + state: present + vpc_security_group_ids: + - sg-0be17ba10c9286b0b + purge_security_groups: false + +- name: Modify password + community.aws.rds_cluster: + id: "{{ cluster_id }}" + state: present + password: "{{ new_password }}" + force_update_password: true + apply_immediately: true + +- name: Rename the cluster + community.aws.rds_cluster: + engine: aurora + password: "{{ password }}" + username: "{{ username }}" + cluster_id: "cluster-{{ resource_prefix }}" + new_cluster_id: "cluster-{{ resource_prefix }}-renamed" + apply_immediately: true + +- name: Delete aurora cluster without creating a final snapshot + community.aws.rds_cluster: + engine: aurora + password: "{{ password }}" + username: "{{ username }}" + cluster_id: "{{ cluster_id }}" + skip_final_snapshot: True + tags: + Name: "cluster-{{ resource_prefix }}" + Created_By: "Ansible_rds_cluster_integration_test" + state: absent + +- name: Restore cluster from source snapshot + community.aws.rds_cluster: + engine: aurora + password: "{{ password }}" + username: "{{ username }}" + cluster_id: "cluster-{{ resource_prefix }}-restored" + snapshot_identifier: "cluster-{{ resource_prefix }}-snapshot" +''' + +RETURN = r''' +activity_stream_status: + description: The status of the database activity stream. + returned: always + type: str + sample: stopped +allocated_storage: + description: + - The allocated storage size in gigabytes. Since aurora storage size is not fixed this is + always 1 for aurora database engines. + returned: always + type: int + sample: 1 +associated_roles: + description: + - A list of dictionaries of the AWS Identity and Access Management (IAM) roles that are associated + with the DB cluster. Each dictionary contains the role_arn and the status of the role. + returned: always + type: list + sample: [] +availability_zones: + description: The list of availability zones that instances in the DB cluster can be created in. + returned: always + type: list + sample: + - us-east-1c + - us-east-1a + - us-east-1e +backup_retention_period: + description: The number of days for which automatic DB snapshots are retained. + returned: always + type: int + sample: 1 +changed: + description: If the RDS cluster has changed. + returned: always + type: bool + sample: true +cluster_create_time: + description: The time in UTC when the DB cluster was created. + returned: always + type: str + sample: '2018-06-29T14:08:58.491000+00:00' +copy_tags_to_snapshot: + description: + - Specifies whether tags are copied from the DB cluster to snapshots of the DB cluster. + returned: always + type: bool + sample: false +cross_account_clone: + description: + - Specifies whether the DB cluster is a clone of a DB cluster owned by a different Amazon Web Services account. + returned: always + type: bool + sample: false +db_cluster_arn: + description: The Amazon Resource Name (ARN) for the DB cluster. + returned: always + type: str + sample: arn:aws:rds:us-east-1:123456789012:cluster:rds-cluster-demo +db_cluster_identifier: + description: The lowercase user-supplied DB cluster identifier. + returned: always + type: str + sample: rds-cluster-demo +db_cluster_members: + description: + - A list of dictionaries containing information about the instances in the cluster. + Each dictionary contains the db_instance_identifier, is_cluster_writer (bool), + db_cluster_parameter_group_status, and promotion_tier (int). + returned: always + type: list + sample: [] +db_cluster_parameter_group: + description: The parameter group associated with the DB cluster. + returned: always + type: str + sample: default.aurora5.6 +db_cluster_resource_id: + description: The AWS Region-unique, immutable identifier for the DB cluster. + returned: always + type: str + sample: cluster-D2MEQDN3BQNXDF74K6DQJTHASU +db_subnet_group: + description: The name of the subnet group associated with the DB Cluster. + returned: always + type: str + sample: default +deletion_protection: + description: + - Indicates if the DB cluster has deletion protection enabled. + The database can't be deleted when deletion protection is enabled. + returned: always + type: bool + sample: false +domain_memberships: + description: + - The Active Directory Domain membership records associated with the DB cluster. + returned: always + type: list + sample: [] +earliest_restorable_time: + description: The earliest time to which a database can be restored with point-in-time restore. + returned: always + type: str + sample: '2018-06-29T14:09:34.797000+00:00' +endpoint: + description: The connection endpoint for the primary instance of the DB cluster. + returned: always + type: str + sample: rds-cluster-demo.cluster-cvlrtwiennww.us-east-1.rds.amazonaws.com +engine: + description: The database engine of the DB cluster. + returned: always + type: str + sample: aurora +engine_mode: + description: The DB engine mode of the DB cluster. + returned: always + type: str + sample: provisioned +engine_version: + description: The database engine version. + returned: always + type: str + sample: 5.6.10a +hosted_zone_id: + description: The ID that Amazon Route 53 assigns when you create a hosted zone. + returned: always + type: str + sample: Z2R2ITUGPM61AM +http_endpoint_enabled: + description: + - A value that indicates whether the HTTP endpoint for an Aurora Serverless DB cluster is enabled. + returned: always + type: bool + sample: false +iam_database_authentication_enabled: + description: Whether IAM accounts may be mapped to database accounts. + returned: always + type: bool + sample: false +latest_restorable_time: + description: The latest time to which a database can be restored with point-in-time restore. + returned: always + type: str + sample: '2018-06-29T14:09:34.797000+00:00' +master_username: + description: The master username for the DB cluster. + returned: always + type: str + sample: username +multi_az: + description: Whether the DB cluster has instances in multiple availability zones. + returned: always + type: bool + sample: false +port: + description: The port that the database engine is listening on. + returned: always + type: int + sample: 3306 +preferred_backup_window: + description: The UTC weekly time range during which system maintenance can occur. + returned: always + type: str + sample: 10:18-10:48 +preferred_maintenance_window: + description: The UTC weekly time range during which system maintenance can occur. + returned: always + type: str + sample: tue:03:23-tue:03:53 +read_replica_identifiers: + description: A list of read replica ID strings associated with the DB cluster. + returned: always + type: list + sample: [] +reader_endpoint: + description: The reader endpoint for the DB cluster. + returned: always + type: str + sample: rds-cluster-demo.cluster-ro-cvlrtwiennww.us-east-1.rds.amazonaws.com +status: + description: The status of the DB cluster. + returned: always + type: str + sample: available +storage_encrypted: + description: Whether the DB cluster is storage encrypted. + returned: always + type: bool + sample: false +tag_list: + description: A list of tags consisting of key-value pairs. + returned: always + type: list + elements: dict + sample: [ + { + "key": "Created_By", + "value": "Ansible_rds_cluster_integration_test" + } + ] +tags: + description: A dictionary of key value pairs. + returned: always + type: dict + sample: { + "Name": "rds-cluster-demo" + } +vpc_security_groups: + description: A list of the DB cluster's security groups and their status. + returned: always + type: complex + contains: + status: + description: Status of the security group. + returned: always + type: str + sample: active + vpc_security_group_id: + description: Security group of the cluster. + returned: always + type: str + sample: sg-12345678 +''' + + +try: + import botocore +except ImportError: + pass # caught by AnsibleAWSModule + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list +from ansible_collections.amazon.aws.plugins.module_utils.rds import wait_for_cluster_status +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 get_tags +from ansible_collections.amazon.aws.plugins.module_utils.rds import ensure_tags +from ansible_collections.amazon.aws.plugins.module_utils.rds import call_method + + +@AWSRetry.jittered_backoff(retries=10) +def _describe_db_clusters(**params): + try: + paginator = client.get_paginator('describe_db_clusters') + return paginator.paginate(**params).build_full_result()['DBClusters'][0] + except is_boto3_error_code('DBClusterNotFoundFault'): + return {} + + +def get_add_role_options(params_dict, cluster): + current_role_arns = [role['RoleArn'] for role in cluster.get('AssociatedRoles', [])] + role = params_dict['RoleArn'] + if role is not None and role not in current_role_arns: + return {'RoleArn': role, 'DBClusterIdentifier': params_dict['DBClusterIdentifier']} + return {} + + +def get_backtrack_options(params_dict): + options = ['BacktrackTo', 'DBClusterIdentifier', 'UseEarliestTimeOnPointInTimeUnavailable'] + if params_dict['BacktrackTo'] is not None: + options = dict((k, params_dict[k]) for k in options if params_dict[k] is not None) + if 'ForceBacktrack' in params_dict: + options['Force'] = params_dict['ForceBacktrack'] + return options + return {} + + +def get_create_options(params_dict): + options = [ + 'AvailabilityZones', 'BacktrackWindow', 'BackupRetentionPeriod', 'PreferredBackupWindow', + 'CharacterSetName', 'DBClusterIdentifier', 'DBClusterParameterGroupName', 'DBSubnetGroupName', + 'DatabaseName', 'EnableCloudwatchLogsExports', 'EnableIAMDatabaseAuthentication', 'KmsKeyId', + 'Engine', 'EngineVersion', 'PreferredMaintenanceWindow', 'MasterUserPassword', 'MasterUsername', + 'OptionGroupName', 'Port', 'ReplicationSourceIdentifier', 'SourceRegion', 'StorageEncrypted', + 'Tags', 'VpcSecurityGroupIds', 'EngineMode', 'ScalingConfiguration', 'DeletionProtection', + 'EnableHttpEndpoint', 'CopyTagsToSnapshot', 'Domain', 'DomainIAMRoleName', + 'EnableGlobalWriteForwarding', + ] + + return dict((k, v) for k, v in params_dict.items() if k in options and v is not None) + + +def get_modify_options(params_dict, force_update_password): + options = [ + 'ApplyImmediately', 'BacktrackWindow', 'BackupRetentionPeriod', 'PreferredBackupWindow', + 'DBClusterIdentifier', 'DBClusterParameterGroupName', 'EnableIAMDatabaseAuthentication', + 'EngineVersion', 'PreferredMaintenanceWindow', 'MasterUserPassword', 'NewDBClusterIdentifier', + 'OptionGroupName', 'Port', 'VpcSecurityGroupIds', 'EnableIAMDatabaseAuthentication', + 'CloudwatchLogsExportConfiguration', 'DeletionProtection', 'EnableHttpEndpoint', + 'CopyTagsToSnapshot', 'EnableGlobalWriteForwarding', 'Domain', 'DomainIAMRoleName', + ] + modify_options = dict((k, v) for k, v in params_dict.items() if k in options and v is not None) + if not force_update_password: + modify_options.pop('MasterUserPassword', None) + return modify_options + + +def get_delete_options(params_dict): + options = ['DBClusterIdentifier', 'FinalSnapshotIdentifier', 'SkipFinalSnapshot'] + return dict((k, params_dict[k]) for k in options if params_dict[k] is not None) + + +def get_restore_s3_options(params_dict): + options = [ + 'AvailabilityZones', 'BacktrackWindow', 'BackupRetentionPeriod', 'CharacterSetName', + 'DBClusterIdentifier', 'DBClusterParameterGroupName', 'DBSubnetGroupName', 'DatabaseName', + 'EnableCloudwatchLogsExports', 'EnableIAMDatabaseAuthentication', 'Engine', 'EngineVersion', + 'KmsKeyId', 'MasterUserPassword', 'MasterUsername', 'OptionGroupName', 'Port', + 'PreferredBackupWindow', 'PreferredMaintenanceWindow', 'S3BucketName', 'S3IngestionRoleArn', + 'S3Prefix', 'SourceEngine', 'SourceEngineVersion', 'StorageEncrypted', 'Tags', + 'VpcSecurityGroupIds', 'DeletionProtection', 'EnableHttpEndpoint', 'CopyTagsToSnapshot', + 'Domain', 'DomainIAMRoleName', + ] + + return dict((k, v) for k, v in params_dict.items() if k in options and v is not None) + + +def get_restore_snapshot_options(params_dict): + options = [ + 'AvailabilityZones', 'BacktrackWindow', 'DBClusterIdentifier', 'DBSubnetGroupName', + 'DatabaseName', 'EnableCloudwatchLogsExports', 'EnableIAMDatabaseAuthentication', + 'Engine', 'EngineVersion', 'KmsKeyId', 'OptionGroupName', 'Port', 'SnapshotIdentifier', + 'Tags', 'VpcSecurityGroupIds', 'DBClusterParameterGroupName', 'DeletionProtection', + 'CopyTagsToSnapshot', 'Domain', 'DomainIAMRoleName', + ] + return dict((k, v) for k, v in params_dict.items() if k in options and v is not None) + + +def get_restore_cluster_options(params_dict): + options = [ + 'BacktrackWindow', 'DBClusterIdentifier', 'DBSubnetGroupName', 'EnableCloudwatchLogsExports', + 'EnableIAMDatabaseAuthentication', 'KmsKeyId', 'OptionGroupName', 'Port', 'RestoreToTime', + 'RestoreType', 'SourceDBClusterIdentifier', 'Tags', 'UseLatestRestorableTime', + 'VpcSecurityGroupIds', 'DeletionProtection', 'CopyTagsToSnapshot', 'Domain', + 'DomainIAMRoleName', + ] + return dict((k, v) for k, v in params_dict.items() if k in options and v is not None) + + +def get_rds_method_attribute_name(cluster): + state = module.params['state'] + creation_source = module.params['creation_source'] + method_name = None + method_options_name = None + + if state == 'absent': + if cluster and cluster['Status'] not in ['deleting', 'deleted']: + method_name = 'delete_db_cluster' + method_options_name = 'get_delete_options' + else: + if cluster: + method_name = 'modify_db_cluster' + method_options_name = 'get_modify_options' + elif creation_source == 'snapshot': + method_name = 'restore_db_cluster_from_db_snapshot' + method_options_name = 'get_restore_snapshot_options' + elif creation_source == 's3': + method_name = 'restore_db_cluster_from_s3' + method_options_name = 'get_restore_s3_options' + elif creation_source == 'cluster': + method_name = 'restore_db_cluster_to_point_in_time' + method_options_name = 'get_restore_cluster_options' + else: + method_name = 'create_db_cluster' + method_options_name = 'get_create_options' + + return method_name, method_options_name + + +def add_role(params): + if not module.check_mode: + try: + client.add_role_to_db_cluster(**params) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg=f"Unable to add role {params['RoleArn']} to cluster {params['DBClusterIdentifier']}") + wait_for_cluster_status(client, module, params['DBClusterIdentifier'], 'cluster_available') + + +def backtrack_cluster(params): + if not module.check_mode: + try: + client.backtrack_db_cluster(**params) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg=F"Unable to backtrack cluster {params['DBClusterIdentifier']}") + wait_for_cluster_status(client, module, params['DBClusterIdentifier'], 'cluster_available') + + +def get_cluster(db_cluster_id): + try: + return _describe_db_clusters(DBClusterIdentifier=db_cluster_id) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg="Failed to describe DB clusters") + + +def changing_cluster_options(modify_params, current_cluster): + changing_params = {} + apply_immediately = modify_params.pop('ApplyImmediately') + db_cluster_id = modify_params.pop('DBClusterIdentifier') + + enable_cloudwatch_logs_export = modify_params.pop('EnableCloudwatchLogsExports', None) + if enable_cloudwatch_logs_export is not None: + desired_cloudwatch_logs_configuration = {'EnableLogTypes': [], 'DisableLogTypes': []} + provided_cloudwatch_logs = set(enable_cloudwatch_logs_export) + current_cloudwatch_logs_export = set(current_cluster['EnabledCloudwatchLogsExports']) + + desired_cloudwatch_logs_configuration['EnableLogTypes'] = list(provided_cloudwatch_logs.difference(current_cloudwatch_logs_export)) + if module.params['purge_cloudwatch_logs_exports']: + desired_cloudwatch_logs_configuration['DisableLogTypes'] = list(current_cloudwatch_logs_export.difference(provided_cloudwatch_logs)) + changing_params['CloudwatchLogsExportConfiguration'] = desired_cloudwatch_logs_configuration + + password = modify_params.pop('MasterUserPassword', None) + if password: + changing_params['MasterUserPassword'] = password + + new_cluster_id = modify_params.pop('NewDBClusterIdentifier', None) + if new_cluster_id and new_cluster_id != current_cluster['DBClusterIdentifier']: + changing_params['NewDBClusterIdentifier'] = new_cluster_id + + option_group = modify_params.pop('OptionGroupName', None) + if ( + option_group and option_group not in [g['DBClusterOptionGroupName'] for g in current_cluster['DBClusterOptionGroupMemberships']] + ): + changing_params['OptionGroupName'] = option_group + + vpc_sgs = modify_params.pop('VpcSecurityGroupIds', None) + if vpc_sgs: + desired_vpc_sgs = [] + provided_vpc_sgs = set(vpc_sgs) + current_vpc_sgs = set([sg['VpcSecurityGroupId'] for sg in current_cluster['VpcSecurityGroups']]) + if module.params['purge_security_groups']: + desired_vpc_sgs = vpc_sgs + else: + if provided_vpc_sgs - current_vpc_sgs: + desired_vpc_sgs = list(provided_vpc_sgs | current_vpc_sgs) + + if desired_vpc_sgs: + changing_params['VpcSecurityGroupIds'] = desired_vpc_sgs + + for param in modify_params: + if modify_params[param] != current_cluster[param]: + changing_params[param] = modify_params[param] + + if changing_params: + changing_params['DBClusterIdentifier'] = db_cluster_id + if apply_immediately is not None: + changing_params['ApplyImmediately'] = apply_immediately + + return changing_params + + +def ensure_present(cluster, parameters, method_name, method_options_name): + changed = False + + if not cluster: + if parameters.get('Tags') is not None: + parameters['Tags'] = ansible_dict_to_boto3_tag_list(parameters['Tags']) + call_method(client, module, method_name, eval(method_options_name)(parameters)) + changed = True + else: + if get_backtrack_options(parameters): + backtrack_cluster(client, module, get_backtrack_options(parameters)) + changed = True + else: + modifiable_options = eval(method_options_name)(parameters, + force_update_password=module.params['force_update_password']) + modify_options = changing_cluster_options(modifiable_options, cluster) + if modify_options: + call_method(client, module, method_name, modify_options) + changed = True + if module.params['tags'] is not None: + existing_tags = get_tags(client, module, cluster['DBClusterArn']) + changed |= ensure_tags(client, module, cluster['DBClusterArn'], existing_tags, module.params['tags'], + module.params['purge_tags']) + + add_role_params = get_add_role_options(parameters, cluster) + if add_role_params: + add_role(client, module, add_role_params) + changed = True + + if module.params['promote'] and cluster.get('ReplicationSourceIdentifier'): + call_method(client, module, 'promote_read_replica_db_cluster', parameters={'DBClusterIdentifier': module.params['db_cluster_identifier']}) + changed = True + + return changed + + +def main(): + global module + global client + + arg_spec = dict( + state=dict(choices=['present', 'absent'], default='present'), + creation_source=dict(type='str', choices=['snapshot', 's3', 'cluster']), + force_update_password=dict(type='bool', default=False), + promote=dict(type='bool', default=False), + purge_cloudwatch_logs_exports=dict(type='bool', default=True), + purge_tags=dict(type='bool', default=True), + wait=dict(type='bool', default=True), + purge_security_groups=dict(type='bool', default=True), + ) + + parameter_options = dict( + apply_immediately=dict(type='bool', default=False), + availability_zones=dict(type='list', elements='str', aliases=['zones', 'az']), + backtrack_to=dict(), + backtrack_window=dict(type='int'), + backup_retention_period=dict(type='int', default=1), + character_set_name=dict(), + database_name=dict(aliases=['db_name']), + db_cluster_identifier=dict(required=True, aliases=['cluster_id', 'id', 'cluster_name']), + db_cluster_parameter_group_name=dict(), + db_subnet_group_name=dict(), + enable_cloudwatch_logs_exports=dict(type='list', elements='str'), + deletion_protection=dict(type='bool'), + global_cluster_identifier=dict(), + enable_http_endpoint=dict(type='bool'), + copy_tags_to_snapshot=dict(type='bool'), + domain=dict(), + domain_iam_role_name=dict(), + enable_global_write_forwarding=dict(type='bool'), + enable_iam_database_authentication=dict(type='bool'), + engine=dict(choices=["aurora", "aurora-mysql", "aurora-postgresql"]), + engine_version=dict(), + final_snapshot_identifier=dict(), + force_backtrack=dict(type='bool'), + kms_key_id=dict(), + master_user_password=dict(aliases=['password'], no_log=True), + master_username=dict(aliases=['username']), + new_db_cluster_identifier=dict(aliases=['new_cluster_id', 'new_id', 'new_cluster_name']), + option_group_name=dict(), + port=dict(type='int'), + preferred_backup_window=dict(aliases=['backup_window']), + preferred_maintenance_window=dict(aliases=['maintenance_window']), + replication_source_identifier=dict(aliases=['replication_src_id']), + restore_to_time=dict(), + restore_type=dict(choices=['full-copy', 'copy-on-write']), + role_arn=dict(), + s3_bucket_name=dict(), + s3_ingestion_role_arn=dict(), + s3_prefix=dict(), + skip_final_snapshot=dict(type='bool', default=False), + snapshot_identifier=dict(), + source_db_cluster_identifier=dict(), + source_engine=dict(choices=['mysql']), + source_engine_version=dict(), + source_region=dict(), + storage_encrypted=dict(type='bool'), + tags=dict(type='dict'), + use_earliest_time_on_point_in_time_unavailable=dict(type='bool'), + use_latest_restorable_time=dict(type='bool'), + vpc_security_group_ids=dict(type='list', elements='str'), + ) + arg_spec.update(parameter_options) + + module = AnsibleAWSModule( + argument_spec=arg_spec, + required_if=[ + ('creation_source', 'snapshot', ('snapshot_identifier', 'engine')), + ('creation_source', 's3', ( + 's3_bucket_name', 'engine', 'master_username', 'master_user_password', + 'source_engine', 'source_engine_version', 's3_ingestion_role_arn')), + ], + mutually_exclusive=[ + ('s3_bucket_name', 'source_db_cluster_identifier', 'snapshot_identifier'), + ('use_latest_restorable_time', 'restore_to_time'), + ], + supports_check_mode=True + ) + + retry_decorator = AWSRetry.jittered_backoff(retries=10) + + try: + client = module.client('rds', retry_decorator=retry_decorator) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to connect to AWS.') + + module.params['db_cluster_identifier'] = module.params['db_cluster_identifier'].lower() + cluster = get_cluster(module.params['db_cluster_identifier']) + + if module.params['new_db_cluster_identifier']: + module.params['new_db_cluster_identifier'] = module.params['new_db_cluster_identifier'].lower() + + if get_cluster(module.params['new_db_cluster_identifier']): + module.fail_json(f"A new cluster ID {module.params['new_db_cluster_identifier']} was provided but it already exists") + if not cluster: + module.fail_json(f"A new cluster ID {module.params['new_db_cluster_identifier']} was provided but the cluster to be renamed does not exist") + + if ( + module.params['state'] == 'absent' and module.params['skip_final_snapshot'] is False and + module.params['final_snapshot_identifier'] is None + ): + module.fail_json(msg='skip_final_snapshot is False but all of the following are missing: final_snapshot_identifier') + + parameters = arg_spec_to_rds_params(dict((k, module.params[k]) for k in module.params if k in parameter_options)) + + changed = False + method_name, method_options_name = get_rds_method_attribute_name(cluster) + + if method_name: + if method_name == 'delete_db_cluster': + call_method(client, module, method_name, eval(method_options_name)(parameters)) + changed = True + else: + changed |= ensure_present(cluster, parameters, method_name, method_options_name) + + if not module.check_mode and module.params['new_db_cluster_identifier'] and module.params['apply_immediately']: + cluster_id = module.params['new_db_cluster_identifier'] + else: + cluster_id = module.params['db_cluster_identifier'] + + result = camel_dict_to_snake_dict(get_cluster(cluster_id)) + + if result: + result['tags'] = get_tags(client, module, result['db_cluster_arn']) + + module.exit_json(changed=changed, **result) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/rds_cluster_info.py b/plugins/modules/rds_cluster_info.py new file mode 100644 index 00000000000..c53d661bd8b --- /dev/null +++ b/plugins/modules/rds_cluster_info.py @@ -0,0 +1,307 @@ +#!/usr/bin/python +# Copyright (c) 2022 Ansible Project +# Copyright (c) 2022 Alina Buzachis (@alinabuzachis) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +module: rds_cluster_info +version_added: 3.2.0 +short_description: Obtain information about one or more RDS clusters +description: + - Obtain information about one or more RDS clusters. +options: + db_cluster_identifier: + description: + - The user-supplied DB cluster identifier. + - If this parameter is specified, information from only the specific DB cluster is returned. + aliases: + - cluster_id + - id + - cluster_name + type: str + filters: + description: + - A filter that specifies one or more DB clusters to describe. + See U(https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeDBClusters.html). + type: dict +author: + - Alina Buzachis (@alinabuzachis) +extends_documentation_fragment: +- amazon.aws.aws +- amazon.aws.ec2 + +''' + +EXAMPLES = r''' +- name: Get info of all existing DB clusters + community.aws.rds_cluster_info: + register: _result_cluster_info + +- name: Get info on a specific DB cluster + community.aws.rds_cluster_info: + cluster_id: "{{ cluster_id }}" + register: _result_cluster_info + +- name: Get info all DB clusters with specific engine + community.aws.rds_cluster_info: + engine: "aurora" + register: _result_cluster_info +''' + +RETURN = r''' +clusters: + description: List of RDS clusters. + returned: always + type: list + contains: + activity_stream_status: + description: The status of the database activity stream. + type: str + sample: stopped + allocated_storage: + description: + - The allocated storage size in gigabytes. Since aurora storage size is not fixed this is + always 1 for aurora database engines. + type: int + sample: 1 + associated_roles: + description: + - A list of dictionaries of the AWS Identity and Access Management (IAM) roles that are associated + with the DB cluster. Each dictionary contains the role_arn and the status of the role. + type: list + sample: [] + availability_zones: + description: The list of availability zones that instances in the DB cluster can be created in. + type: list + sample: + - us-east-1c + - us-east-1a + - us-east-1e + backup_retention_period: + description: The number of days for which automatic DB snapshots are retained. + type: int + sample: 1 + cluster_create_time: + description: The time in UTC when the DB cluster was created. + type: str + sample: '2018-06-29T14:08:58.491000+00:00' + copy_tags_to_snapshot: + description: + - Specifies whether tags are copied from the DB cluster to snapshots of the DB cluster. + type: bool + sample: false + cross_account_clone: + description: + - Specifies whether the DB cluster is a clone of a DB cluster owned by a different Amazon Web Services account. + type: bool + sample: false + db_cluster_arn: + description: The Amazon Resource Name (ARN) for the DB cluster. + type: str + sample: arn:aws:rds:us-east-1:123456789012:cluster:rds-cluster-demo + db_cluster_identifier: + description: The lowercase user-supplied DB cluster identifier. + type: str + sample: rds-cluster-demo + db_cluster_members: + description: + - A list of dictionaries containing information about the instances in the cluster. + Each dictionary contains the I(db_instance_identifier), I(is_cluster_writer) (bool), + I(db_cluster_parameter_group_status), and I(promotion_tier) (int). + type: list + sample: [] + db_cluster_parameter_group: + description: The parameter group associated with the DB cluster. + type: str + sample: default.aurora5.6 + db_cluster_resource_id: + description: The AWS Region-unique, immutable identifier for the DB cluster. + type: str + sample: cluster-D2MEQDN3BQNXDF74K6DQJTHASU + db_subnet_group: + description: The name of the subnet group associated with the DB Cluster. + type: str + sample: default + deletion_protection: + description: + - Indicates if the DB cluster has deletion protection enabled. + The database can't be deleted when deletion protection is enabled. + type: bool + sample: false + domain_memberships: + description: + - The Active Directory Domain membership records associated with the DB cluster. + type: list + sample: [] + earliest_restorable_time: + description: The earliest time to which a database can be restored with point-in-time restore. + type: str + sample: '2018-06-29T14:09:34.797000+00:00' + endpoint: + description: The connection endpoint for the primary instance of the DB cluster. + type: str + sample: rds-cluster-demo.cluster-cvlrtwiennww.us-east-1.rds.amazonaws.com + engine: + description: The database engine of the DB cluster. + type: str + sample: aurora + engine_mode: + description: The DB engine mode of the DB cluster. + type: str + sample: provisioned + engine_version: + description: The database engine version. + type: str + sample: 5.6.10a + hosted_zone_id: + description: The ID that Amazon Route 53 assigns when you create a hosted zone. + type: str + sample: Z2R2ITUGPM61AM + http_endpoint_enabled: + description: + - A value that indicates whether the HTTP endpoint for an Aurora Serverless DB cluster is enabled. + type: bool + sample: false + iam_database_authentication_enabled: + description: Whether IAM accounts may be mapped to database accounts. + type: bool + sample: false + latest_restorable_time: + description: The latest time to which a database can be restored with point-in-time restore. + type: str + sample: '2018-06-29T14:09:34.797000+00:00' + master_username: + description: The master username for the DB cluster. + type: str + sample: username + multi_az: + description: Whether the DB cluster has instances in multiple availability zones. + type: bool + sample: false + port: + description: The port that the database engine is listening on. + type: int + sample: 3306 + preferred_backup_window: + description: The UTC weekly time range during which system maintenance can occur. + type: str + sample: 10:18-10:48 + preferred_maintenance_window: + description: The UTC weekly time range during which system maintenance can occur. + type: str + sample: tue:03:23-tue:03:53 + read_replica_identifiers: + description: A list of read replica ID strings associated with the DB cluster. + type: list + sample: [] + reader_endpoint: + description: The reader endpoint for the DB cluster. + type: str + sample: rds-cluster-demo.cluster-ro-cvlrtwiennww.us-east-1.rds.amazonaws.com + status: + description: The status of the DB cluster. + type: str + sample: available + storage_encrypted: + description: Whether the DB cluster is storage encrypted. + type: bool + sample: false + tag_list: + description: A list of tags consisting of key-value pairs. + type: list + elements: dict + sample: [ + { + "key": "Created_By", + "value": "Ansible_rds_cluster_integration_test" + } + ] + tags: + description: A dictionary of key value pairs. + type: dict + sample: { + "Name": "rds-cluster-demo" + } + vpc_security_groups: + description: A list of the DB cluster's security groups and their status. + type: complex + contains: + status: + description: Status of the security group. + type: str + sample: active + vpc_security_group_id: + description: Security group of the cluster. + type: str + sample: sg-12345678 +''' + + +try: + import botocore +except ImportError: + pass # handled by AnsibleAWSModule + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.rds import get_tags + + +@AWSRetry.jittered_backoff(retries=10) +def _describe_db_clusters(client, **params): + try: + paginator = client.get_paginator('describe_db_clusters') + return paginator.paginate(**params).build_full_result()['DBClusters'] + except is_boto3_error_code('DBClusterNotFoundFault'): + return [] + + +def cluster_info(client, module): + cluster_id = module.params.get('db_cluster_identifier') + filters = module.params.get('filters') + + params = dict() + if cluster_id: + params['DBClusterIdentifier'] = cluster_id + if filters: + params['Filters'] = ansible_dict_to_boto3_filter_list(filters) + + try: + result = _describe_db_clusters(client, **params) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, "Couldn't get RDS cluster information.") + + for cluster in result: + cluster['Tags'] = get_tags(client, module, cluster['DBClusterArn']) + + return dict(changed=False, clusters=[camel_dict_to_snake_dict(cluster, ignore_list=['Tags']) for cluster in result]) + + +def main(): + argument_spec = dict( + db_cluster_identifier=dict(aliases=['cluster_id', 'id', 'cluster_name']), + filters=dict(type='dict'), + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + try: + client = module.client('rds', retry_decorator=AWSRetry.jittered_backoff(retries=10)) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to connect to AWS.') + + module.exit_json(**cluster_info(client, module)) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/rds_cluster/aliases b/tests/integration/targets/rds_cluster/aliases new file mode 100644 index 00000000000..0d778791cb1 --- /dev/null +++ b/tests/integration/targets/rds_cluster/aliases @@ -0,0 +1,3 @@ +cloud/aws + +rds_cluster_info diff --git a/tests/integration/targets/rds_cluster/inventory b/tests/integration/targets/rds_cluster/inventory new file mode 100644 index 00000000000..1acd8642050 --- /dev/null +++ b/tests/integration/targets/rds_cluster/inventory @@ -0,0 +1,23 @@ +[tests] +# basic rds_cluster cretion tests +create + +# restore cluster tests +restore + +# TODO: Cannot be tested in the CI because: +# An error occurred (InvalidParameterValue) when calling the CreateDBCluster operation: Replication from cluster in same region is not supported +# promote + +# security groups db tests +create_sgs + +# basic modify operations applied on the rds cluster +modify + +# tag rds cluster test +tag + +[all:vars] +ansible_connection=local +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/tests/integration/targets/rds_cluster/main.yml b/tests/integration/targets/rds_cluster/main.yml new file mode 100644 index 00000000000..9e6ae338bbb --- /dev/null +++ b/tests/integration/targets/rds_cluster/main.yml @@ -0,0 +1,11 @@ +--- +# Beware: most of our tests here are run in parallel. +# To add new tests you'll need to add a new host to the inventory and a matching +# '{{ inventory_hostname }}'.yml file in roles/rds_cluster/tasks/ + +- hosts: all + gather_facts: no + strategy: free + serial: 6 + roles: + - rds_cluster diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/defaults/main.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/defaults/main.yml new file mode 100644 index 00000000000..f1fe2137b08 --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/defaults/main.yml @@ -0,0 +1,36 @@ +--- +# defaults file for rds_cluster + +# Create cluster +cluster_id: "ansible-test-{{ inventory_hostname | replace('_','-') }}{{ tiny_prefix }}" +username: 'testrdsusername' +password: 'test-rds_password' +engine: 'aurora' +port: 3306 +tags_create: + Name: 'ansible-test-cluster-{{ tiny_prefix }}' + Created_By: "Ansible_rds_cluster_integration_test" + +# Modify cluster +new_cluster_id: 'ansible-test-cluster-{{ tiny_prefix }}-new' +new_port: 1155 +new_password: 'test-rds_password-new' + +# Tag cluster +tags_patch: + Name: "{{ tiny_prefix }}-new" + Created_by: Ansible rds_cluster integration tests + +# Create cluster in a VPC +vpc_name: 'ansible-test-vpc-{{ tiny_prefix }}' +vpc_cidr: '10.{{ 256 | random(seed=tiny_prefix) }}.0.0/16' +subnets: + - {'cidr': '10.{{ 256 | random(seed=tiny_prefix) }}.1.0/24', 'zone': '{{ aws_region }}a'} + - {'cidr': '10.{{ 256 | random(seed=tiny_prefix) }}.2.0/24', 'zone': '{{ aws_region }}b'} + - {'cidr': '10.{{ 256 | random(seed=tiny_prefix) }}.3.0/24', 'zone': '{{ aws_region }}c'} + - {'cidr': '10.{{ 256 | random(seed=tiny_prefix) }}.4.0/24', 'zone': '{{ aws_region }}d'} + +security_groups: + - '{{ tiny_prefix }}-sg-1' + - '{{ tiny_prefix }}-sg-2' + - '{{ tiny_prefix }}-sg-3' diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/meta/main.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/meta/main.yml new file mode 100644 index 00000000000..73b314ff7c7 --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/meta/main.yml @@ -0,0 +1 @@ +--- \ No newline at end of file diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/main.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/main.yml new file mode 100644 index 00000000000..009d43476ab --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/main.yml @@ -0,0 +1,15 @@ +--- +- name: 'rds_cluster integration tests' + module_defaults: + group/aws: + region: "{{ aws_region }}" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + + collections: + - community.aws + - amazon.aws + + block: + - include: './test_{{ inventory_hostname }}.yml' diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_create.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_create.yml new file mode 100644 index 00000000000..1004f09e0b1 --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_create.yml @@ -0,0 +1,122 @@ +--- +- block: + - name: Ensure the resource doesn't exist + rds_cluster: + id: "{{ cluster_id }}" + state: absent + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + skip_final_snapshot: True + register: _result_delete_db_cluster + + - assert: + that: + - not _result_delete_db_cluster.changed + ignore_errors: yes + + - name: Get info of all existing clusters + rds_cluster_info: + register: _result_cluster_info + + - assert: + that: + - _result_cluster_info is successful + + - name: Create minimal aurora cluster in default VPC and default subnet group (CHECK MODE) + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: "{{ tags_create }}" + register: _result_create_db_cluster + check_mode: true + + - assert: + that: + - _result_create_db_cluster.changed + + - name: Create minimal aurora cluster in default VPC and default subnet group + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: "{{ tags_create }}" + register: _result_create_db_cluster + + - assert: + that: + - _result_create_db_cluster.changed + - "'allocated_storage' in _result_create_db_cluster" + - _result_create_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_create_db_cluster" + - _result_create_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_create_db_cluster" + - "'db_cluster_identifier' in _result_create_db_cluster" + - _result_create_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_create_db_cluster" + - "'db_cluster_resource_id' in _result_create_db_cluster" + - "'endpoint' in _result_create_db_cluster" + - "'engine' in _result_create_db_cluster" + - _result_create_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_create_db_cluster" + - _result_create_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_create_db_cluster" + - "'master_username' in _result_create_db_cluster" + - _result_create_db_cluster.master_username == "{{ username }}" + - "'port' in _result_create_db_cluster" + - "_result_create_db_cluster.port == {{ port }}" + - "'status' in _result_create_db_cluster" + - _result_create_db_cluster.status == 'available' + - _result_create_db_cluster.storage_encrypted == false + - "'tags' in _result_create_db_cluster" + - _result_create_db_cluster.tags | length == 2 + - _result_create_db_cluster.tags["Created_By"] == "{{ tags_create["Created_By"]}}" + - _result_create_db_cluster.tags["Name"] == "{{ tags_create["Name"]}}" + - "'vpc_security_groups' in _result_create_db_cluster" + + - name: Get info of the existing cluster + rds_cluster_info: + cluster_id: "{{ cluster_id }}" + register: result_cluster_info + + - assert: + that: + - result_cluster_info is successful + + - name: Create minimal aurora cluster in default VPC and default subnet group - idempotence (CHECK MODE) + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: "{{ tags_create }}" + register: _result_create_db_cluster + check_mode: true + + - assert: + that: + - not _result_create_db_cluster.changed + + - name: Create minimal aurora cluster in default VPC and default subnet group - idempotence + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: "{{ tags_create }}" + register: _result_create_db_cluster + + - assert: + that: + - not _result_create_db_cluster.changed + + always: + - name: Delete DB cluster without creating a final snapshot + rds_cluster: + state: absent + cluster_id: "{{ cluster_id }}" + skip_final_snapshot: True + ignore_errors: true diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_create_sgs.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_create_sgs.yml new file mode 100644 index 00000000000..01ef01c2c5b --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_create_sgs.yml @@ -0,0 +1,206 @@ +--- +- block: + - name: Ensure the resource doesn't exist + rds_cluster: + id: "{{ cluster_id }}" + state: absent + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + skip_final_snapshot: True + register: _result_delete_db_cluster + + - assert: + that: + - not _result_delete_db_cluster.changed + ignore_errors: yes + + - name: Create a VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + state: present + cidr_block: "{{ vpc_cidr }}" + tags: + Name: "{{ vpc_name }}" + Description: "Created by rds_cluster integration tests" + register: _result_create_vpc + + - name: Create subnets + ec2_vpc_subnet: + cidr: "{{ item.cidr }}" + az: "{{ item.zone }}" + vpc_id: "{{ _result_create_vpc.vpc.id }}" + tags: + Name: "{{ resource_prefix }}-subnet" + Description: "created by rds_cluster integration tests" + state: present + register: _result_create_subnet + loop: "{{ subnets }}" + + - name: Create security groups + ec2_group: + name: "{{ item }}" + description: "Created by rds_cluster integration tests" + state: present + register: _result_create_sg + loop: "{{ security_groups }}" + + - name: Create an RDS cluster in the VPC with two security groups + rds_cluster: + id: "{{ cluster_id }}" + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + vpc_security_group_ids: + - "{{ _result_create_sg.results.0.group_id }}" + - "{{ _result_create_sg.results.1.group_id }}" + register: _result_create_db_cluster + + - assert: + that: + - _result_create_db_cluster.changed + - "'allocated_storage' in _result_create_db_cluster" + - _result_create_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_create_db_cluster" + - _result_create_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_create_db_cluster" + - "'db_cluster_identifier' in _result_create_db_cluster" + - _result_create_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_create_db_cluster" + - "'db_cluster_resource_id' in _result_create_db_cluster" + - "'endpoint' in _result_create_db_cluster" + - "'engine' in _result_create_db_cluster" + - _result_create_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_create_db_cluster" + - _result_create_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_create_db_cluster" + - "'master_username' in _result_create_db_cluster" + - _result_create_db_cluster.master_username == "{{ username }}" + - "'port' in _result_create_db_cluster" + - "_result_create_db_cluster.port == {{ port }}" + - "'status' in _result_create_db_cluster" + - _result_create_db_cluster.status == 'available' + - _result_create_db_cluster.storage_encrypted == false + - "'tags' in _result_create_db_cluster" + - "'vpc_security_groups' in _result_create_db_cluster" + - "_result_create_db_cluster.vpc_security_groups | selectattr('status', 'in', ['active', 'adding']) | list | length == 2" + + - name: Add a new security group without purge (check_mode) + rds_cluster: + id: "{{ cluster_id }}" + state: present + vpc_security_group_ids: + - "{{ _result_create_sg.results.2.group_id }}" + apply_immediately: true + purge_security_groups: false + check_mode: true + register: _result_create_db_cluster + + - assert: + that: + - _result_create_db_cluster.changed + + - name: Add a new security group without purge + rds_cluster: + id: "{{ cluster_id }}" + state: present + vpc_security_group_ids: + - "{{ _result_create_sg.results.2.group_id }}" + apply_immediately: true + purge_security_groups: false + register: _result_create_db_cluster + + - assert: + that: + - _result_create_db_cluster.changed + - "'allocated_storage' in _result_create_db_cluster" + - _result_create_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_create_db_cluster" + - _result_create_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_create_db_cluster" + - "'db_cluster_identifier' in _result_create_db_cluster" + - _result_create_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_create_db_cluster" + - "'db_cluster_resource_id' in _result_create_db_cluster" + - "'endpoint' in _result_create_db_cluster" + - "'engine' in _result_create_db_cluster" + - _result_create_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_create_db_cluster" + - _result_create_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_create_db_cluster" + - "'master_username' in _result_create_db_cluster" + - _result_create_db_cluster.master_username == "{{ username }}" + - "'port' in _result_create_db_cluster" + - "_result_create_db_cluster.port == {{ port }}" + - "'status' in _result_create_db_cluster" + - _result_create_db_cluster.status == 'available' + - _result_create_db_cluster.storage_encrypted == false + - "'tags' in _result_create_db_cluster" + - "'vpc_security_groups' in _result_create_db_cluster" + - "_result_create_db_cluster.vpc_security_groups | selectattr('status', 'in', ['active', 'adding']) | list | length == 3" + + - name: Add a new security group without purge (test idempotence) + rds_cluster: + id: "{{ cluster_id }}" + state: present + vpc_security_group_ids: + - "{{ _result_create_sg.results.2.group_id }}" + apply_immediately: true + purge_security_groups: false + register: _result_create_db_cluster + + - assert: + that: + - not _result_create_db_cluster.changed + + - name: Add a security group with purge + rds_cluster: + id: "{{ cluster_id }}" + state: present + vpc_security_group_ids: + - "{{ _result_create_sg .results.2.group_id }}" + apply_immediately: true + register: _result_create_db_cluster + + - assert: + that: + - _result_create_db_cluster.changed + - "_result_create_db_cluster.db_cluster_identifier == '{{ cluster_id }}'" + - "_result_create_db_cluster.vpc_security_groups | selectattr('status', 'in', ['active', 'adding']) | list | length == 1" + + always: + - name: Delete DB cluster without creating a final snapshot + rds_cluster: + state: absent + cluster_id: "{{ cluster_id }}" + skip_final_snapshot: True + ignore_errors: true + + - name: Remove security groups + ec2_group: + name: "{{ item }}" + description: "created by rds_cluster integration tests" + state: absent + loop: "{{ security_groups }}" + + - name: Remove subnets + ec2_vpc_subnet: + cidr: "{{ item.cidr }}" + az: "{{ item.zone }}" + vpc_id: "{{ _result_create_vpc.vpc.id }}" + tags: + Name: "{{ resource_prefix }}-subnet" + Description: "Created by rds_cluster integration tests" + state: absent + ignore_errors: yes + loop: "{{ subnets }}" + + - name: Delete VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + state: absent + cidr_block: "{{ vpc_cidr }}" + tags: + Name: "{{ vpc_name }}" + Description: "Created by rds_cluster integration tests" + ignore_errors: yes diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_modify.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_modify.yml new file mode 100644 index 00000000000..b093029b54c --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_modify.yml @@ -0,0 +1,197 @@ +--- +- block: + - name: Ensure the resource doesn't exist + rds_cluster: + id: "{{ cluster_id }}" + state: absent + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + skip_final_snapshot: True + register: _result_delete_db_cluster + + - assert: + that: + - not _result_delete_db_cluster.changed + ignore_errors: yes + + - name: Create a DB cluster + rds_cluster: + id: "{{ cluster_id }}" + state: present + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + register: _result_create_source_db_cluster + + - assert: + that: + - _result_create_source_db_cluster.changed + - _result_create_source_db_cluster.changed + - "'allocated_storage' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_create_source_db_cluster" + - "_result_create_source_db_cluster.db_cluster_identifier == '{{ cluster_id }}'" + - "'db_cluster_parameter_group' in _result_create_source_db_cluster" + - "'db_cluster_resource_id' in _result_create_source_db_cluster" + - "'endpoint' in _result_create_source_db_cluster" + - "'engine' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_create_source_db_cluster" + - "'master_username' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.master_username == "{{ username }}" + - "'port' in _result_create_source_db_cluster" + - "_result_create_source_db_cluster.port == {{ port }}" + - "'status' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.status == "available" + - "'tags' in _result_create_source_db_cluster" + - "'vpc_security_groups' in _result_create_source_db_cluster" + + - name: Modify DB cluster password + rds_cluster: + id: "{{ cluster_id }}" + state: present + password: "{{ new_password }}" + force_update_password: True + apply_immediately: True + register: _result_modify_password + + - assert: + that: + - _result_modify_password.changed + - "'allocated_storage' in _result_modify_password" + - _result_modify_password.allocated_storage == 1 + - "'cluster_create_time' in _result_modify_password" + - _result_modify_password.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_modify_password" + - "_result_modify_password.db_cluster_identifier == '{{ cluster_id }}'" + - "'db_cluster_parameter_group' in _result_modify_password" + - "'db_cluster_resource_id' in _result_modify_password" + - "'endpoint' in _result_modify_password" + - "'engine' in _result_modify_password" + - _result_modify_password.engine == "{{ engine }}" + - "'engine_mode' in _result_modify_password" + - _result_modify_password.engine_mode == "provisioned" + - "'engine_version' in _result_modify_password" + - "'master_username' in _result_modify_password" + - _result_modify_password.master_username == "{{ username }}" + - "'port' in _result_create_source_db_cluster" + - "_result_modify_password.port == {{ port }}" + - "'status' in _result_modify_password" + - _result_modify_password.status == "available" + - "'tags' in _result_modify_password" + - "'vpc_security_groups' in _result_modify_password" + + - name: Modify DB cluster port + rds_cluster: + id: "{{ cluster_id }}" + state: present + port: "{{ new_port }}" + register: _result_modify_port + + - assert: + that: + - _result_modify_port.changed + - "'allocated_storage' in _result_modify_port" + - _result_modify_port.allocated_storage == 1 + - "'cluster_create_time' in _result_modify_port" + - _result_modify_port.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_modify_port" + - "_result_modify_port.db_cluster_identifier == '{{ cluster_id }}'" + - "'db_cluster_parameter_group' in _result_modify_port" + - "'db_cluster_resource_id' in _result_modify_port" + - "'endpoint' in _result_modify_port" + - "'engine' in _result_modify_port" + - _result_modify_port.engine == "{{ engine }}" + - "'engine_mode' in _result_modify_port" + - _result_modify_port.engine_mode == "provisioned" + - "'engine_version' in _result_modify_port" + - "'master_username' in _result_modify_port" + - _result_modify_port.master_username == "{{ username }}" + - "'port' in _result_modify_port" + - "_result_modify_port.port == {{ new_port }}" + - "'status' in _result_modify_port" + - _result_modify_port.status == "available" + - "'tags' in _result_modify_port" + - "'vpc_security_groups' in _result_modify_port" + + - name: Modify DB cluster identifier + rds_cluster: + id: "{{ cluster_id }}" + state: present + purge_tags: False + new_cluster_id: "{{ new_cluster_id }}" + apply_immediately: True + register: _result_modify_id + + - assert: + that: + - _result_modify_id.changed + - "'allocated_storage' in _result_modify_id" + - _result_modify_id.allocated_storage == 1 + - "'cluster_create_time' in _result_modify_id" + - _result_modify_id.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_modify_id" + - "_result_modify_id.db_cluster_identifier == '{{ new_cluster_id }}'" + - "'db_cluster_parameter_group' in _result_modify_id" + - "'db_cluster_resource_id' in _result_modify_id" + - "'endpoint' in _result_modify_id" + - "'engine' in _result_modify_id" + - _result_modify_id.engine == "{{ engine }}" + - "'engine_mode' in _result_modify_id" + - _result_modify_id.engine_mode == "provisioned" + - "'engine_version' in _result_modify_id" + - "'master_username' in _result_modify_id" + - _result_modify_id.master_username == "{{ username }}" + - "'port' in _result_modify_id" + - "_result_modify_id.port == {{ new_port }}" + - "'status' in _result_modify_id" + - _result_modify_id.status == "available" + - "'tags' in _result_modify_id" + - "'vpc_security_groups' in _result_modify_id" + + - name: Delete DB cluster without creating a final snapshot (CHECK MODE) + rds_cluster: + state: absent + cluster_id: "{{ new_cluster_id }}" + skip_final_snapshot: True + register: _result_delete_cluster + check_mode: true + + - assert: + that: + - _result_delete_cluster.changed + + - name: Delete DB cluster without creating a final snapshot + rds_cluster: + state: absent + cluster_id: "{{ new_cluster_id }}" + skip_final_snapshot: True + register: _result_delete_cluster + + - assert: + that: + - _result_delete_cluster.changed + + - name: Delete DB cluster without creating a final snapshot (idempotence) + rds_cluster: + state: absent + cluster_id: "{{ new_cluster_id }}" + skip_final_snapshot: True + register: _result_delete_cluster + + - assert: + that: + - not _result_delete_cluster.changed + + always: + - name: Delete DB cluster without creating a final snapshot + rds_cluster: + state: absent + cluster_id: "{{ cluster_id }}" + skip_final_snapshot: True + ignore_errors: true diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_promote.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_promote.yml new file mode 100644 index 00000000000..f894a96591d --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_promote.yml @@ -0,0 +1,186 @@ +--- +- block: + - name: Ensure the resource doesn't exist + rds_cluster: + id: "{{ cluster_id }}" + state: absent + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + skip_final_snapshot: True + register: _result_delete_db_cluster + + - assert: + that: + - not _result_delete_db_cluster.changed + ignore_errors: yes + + - name: Set the two regions for the source DB and the read replica + set_fact: + region_src: "{{ aws_region }}" + region_dest: "{{ aws_region }}" + + - name: Create a source DB cluster + rds_cluster: + cluster_id: "{{ cluster_id }}" + state: present + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + region: "{{ region_src }}" + tags: + Name: "{{ cluster_id }}" + Created_by: Ansible rds_cluster tests + register: _result_create_src_db_cluster + + - assert: + that: + - _result_create_src_db_cluster.changed + - "'allocated_storage' in _result_create_src_db_cluster" + - _result_create_src_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_create_src_db_cluster" + - _result_create_src_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_create_src_db_cluster" + - "_result_create_src_db_cluster.db_cluster_identifier == '{{ cluster_id }}'" + - "'db_cluster_parameter_group' in _result_create_src_db_cluster" + - "'db_cluster_resource_id' in _result_create_src_db_cluster" + - "'endpoint' in _result_create_src_db_cluster" + - "'engine' in _result_create_src_db_cluster" + - _result_create_src_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_create_src_db_cluster" + - _result_create_src_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_create_src_db_cluster" + - "'master_username' in _result_create_src_db_cluster" + - _result_create_src_db_cluster.master_username == "{{ username }}" + - "'port' in _result_create_src_db_cluster" + - "_result_create_src_db_cluster.port == {{ port }}" + - "'status' in _result_create_src_db_cluster" + - _result_create_src_db_cluster.status == "available" + - "'tags' in _result_create_src_db_cluster" + - "_result_create_src_db_cluster.tags | length == 2" + - "_result_create_src_db_cluster.tags.Name == '{{ cluster_id }}'" + - "_result_create_src_db_cluster.tags.Created_by == 'Ansible rds_cluster tests'" + - "'vpc_security_groups' in _result_create_src_db_cluster" + + - name: Get info on DB cluster + rds_cluster_info: + db_cluster_identifier: "{{ cluster_id }}" + register: _result_cluster_info + + - assert: + that: + - _result_cluster_info is successful + + - name: Set the ARN of the source DB cluster + set_fact: + src_db_cluster_arn: "{{ _result_cluster_info.clusters[0].db_cluster_arn}}" + + - name: Create a DB cluster read replica in a different region + rds_cluster: + id: "{{ cluster_id }}-replica" + state: present + replication_source_identifier: "{{ src_db_cluster_arn }}" + engine: "{{ engine}}" + region: "{{ region_dest }}" + tags: + Name: "{{ cluster_id }}" + Created_by: Ansible rds_cluster tests + wait: yes + register: _result_create_replica_db_cluster + + - assert: + that: + - _result_create_replica_db_cluster.changed + - "'allocated_storage' in _result_create_replica_db_cluster" + - _result_create_replica_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_create_replica_db_cluster" + - _result_create_replica_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_create_replica_db_cluster" + - "_result_create_replica_db_cluster.db_cluster_identifier == '{{ cluster_id }}'" + - "'db_cluster_parameter_group' in _result_create_replica_db_cluster" + - "'db_cluster_resource_id' in _result_create_replica_db_cluster" + - "'endpoint' in _result_create_replica_db_cluster" + - "'engine' in _result_create_replica_db_cluster" + - _result_create_replica_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_create_replica_db_cluster" + - _result_create_replica_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_create_replica_db_cluster" + - "'master_username' in _result_create_replica_db_cluster" + - _result_create_replica_db_cluster.master_username == "{{ username }}" + - "'port' in _result_create_replica_db_cluster" + - "_result_create_replica_db_cluster.port == {{ port }}" + - "'status' in _result_create_replica_db_cluster" + - _result_create_replica_db_cluster.status == "available" + - "'tags' in _result_create_replica_db_cluster" + - "_result_create_replica_db_cluster.tags | length == 2" + - "_result_create_replica_db_cluster.tags.Name == '{{ cluster_id }}'" + - "_result_create_replica_db_cluster.tags.Created_by == 'Ansible rds_cluster tests'" + - "'vpc_security_groups' in _result_create_replica_db_cluster" + + - name: Test idempotence with a DB cluster read replica + rds_cluster: + id: "{{ cluster_id }}-replica" + state: present + replication_source_identifier: "{{ src_db_cluster_arn }}" + engine: "{{ engine}}" + region: "{{ region_dest }}" + tags: + Name: "{{ cluster_id }}" + Created_by: Ansible rds_cluster tests + register: _result_create_replica_db_cluster + + - assert: + that: + - not _result_create_replica_db_cluster.changed + + - name: Get info of existing DB cluster + rds_cluster_info: + db_cluster_identifier: "{{ cluster_id }}-replica" + region: "{{ region_dest }}" + register: _result_cluster_info + + - assert: + that: + - _result_cluster_info is successful + # - _result_cluster_info.clusters | length == 0 + + - name: Promote the DB cluster read replica + rds_cluster: + cluster_id: "{{ cluster_id }}-replica" + state: present + promote: True + region: "{{ region_dest }}" + register: _result_promote_replica_db_cluster + + - assert: + that: + - _result_promote_replica_db_cluster.changed + + - name: Promote the DB cluster read replica (idempotence) + rds_cluster: + cluster_id: "{{ cluster_id }}-replica" + state: present + promote: True + region: "{{ region_dest }}" + register: _result_promote_replica_db_cluster + + - assert: + that: + - not _result_promote_replica_db_cluster.changed + + always: + - name: Remove the DB cluster + rds_cluster: + id: "{{ cluster_id }}" + state: absent + skip_final_snapshot: True + region: "{{ region_src }}" + ignore_errors: yes + + - name: Remove the DB cluster read replica + rds_cluster: + id: "{{ cluster_id }}-replica" + state: absent + skip_final_snapshot: True + region: "{{ region_dest }}" + ignore_errors: yes diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_restore.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_restore.yml new file mode 100644 index 00000000000..62315631054 --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_restore.yml @@ -0,0 +1,185 @@ +--- +- block: + - name: Ensure the resource doesn't exist + rds_cluster: + id: "{{ cluster_id }}" + state: absent + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + skip_final_snapshot: True + register: _result_delete_db_cluster + + - assert: + that: + - not _result_delete_db_cluster.changed + ignore_errors: yes + + - name: Create a source DB cluster + rds_cluster: + id: "{{ cluster_id }}" + state: present + engine: "{{ engine}}" + backup_retention_period: 1 + username: "{{ username }}" + password: "{{ password }}" + register: _result_create_source_db_cluster + + - assert: + that: + - _result_create_source_db_cluster.changed + - "'allocated_storage' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_create_source_db_cluster" + - "'db_cluster_identifier' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_create_source_db_cluster" + - "'db_cluster_resource_id' in _result_create_source_db_cluster" + - "'endpoint' in _result_create_source_db_cluster" + - "'engine' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_create_source_db_cluster" + - "'master_username' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.master_username == "{{ username }}" + - "'port' in _result_create_source_db_cluster" + - "_result_create_source_db_cluster.port == {{ port }}" + - "'status' in _result_create_source_db_cluster" + - _result_create_source_db_cluster.status == 'available' + - _result_create_source_db_cluster.storage_encrypted == false + - "'tags' in _result_create_source_db_cluster" + - "'vpc_security_groups' in _result_create_source_db_cluster" + + - name: Create a point in time DB cluster + rds_cluster: + state: present + id: "{{ cluster_id }}-point-in-time" + source_db_cluster_identifier: "{{ cluster_id }}" + creation_source: "cluster" + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + use_latest_restorable_time: true + tags: + Name: "{{ cluster_id }}" + Created_by: Ansible rds_cluster tests + register: _result_restored_db_cluster + + - assert: + that: + - _result_restored_db_cluster.changed + - "'allocated_storage' in _result_restored_db_cluster" + - _result_restored_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_restored_db_cluster" + - _result_restored_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_restored_db_cluster" + - "_result_restored_db_cluster.db_cluster_identifier == '{{ cluster_id }}-point-in-time'" + - "'db_cluster_parameter_group' in _result_restored_db_cluster" + - "'db_cluster_resource_id' in _result_restored_db_cluster" + - "'endpoint' in _result_restored_db_cluster" + - "'engine' in _result_restored_db_cluster" + - _result_restored_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_restored_db_cluster" + - _result_restored_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_restored_db_cluster" + - "'master_username' in _result_restored_db_cluster" + - _result_restored_db_cluster.master_username == "{{ username }}" + - "'port' in _result_restored_db_cluster" + - "_result_restored_db_cluster.port == {{ port }}" + - "'status' in _result_restored_db_cluster" + - _result_restored_db_cluster.status == "available" + - "'tags' in _result_restored_db_cluster" + - "_result_restored_db_cluster.tags | length == 2" + - "_result_restored_db_cluster.tags.Name == '{{ cluster_id }}'" + - "_result_restored_db_cluster.tags.Created_by == 'Ansible rds_cluster tests'" + - "'vpc_security_groups' in _result_restored_db_cluster" + + - name: Create a point in time DB cluster (idempotence) + rds_cluster: + state: present + id: "{{ cluster_id }}-point-in-time" + source_db_cluster_identifier: "{{ cluster_id }}" + creation_source: "cluster" + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + restore_to_time: "{{ _result_restored_db_cluster.latest_restorable_time }}" + tags: + Name: "{{ cluster_id }}" + Created_by: Ansible rds_cluster tests + register: _result_restored_db_cluster + + - assert: + that: + - not _result_restored_db_cluster.changed + + # TODO: Requires rds_cluster_snapshot module + # - name: Take a snapshot of the DB cluster + # rds_cluster_snapshot: + # state: present + # db_cluster_identifier: "{{ cluster_id }}" + # db_snapshot_identifier: "{{ cluster_id }}-snapshot" + # register: _result_cluster_snapshot + + # - assert: + # that: + # - _result_cluster_snapshot.changed + + # - name: Restore DB cluster from source (snapshot) + # rds_cluster: + # creation_source: 'snapshot' + # engine: "{{ engine }}" + # cluster_id: "cluster-{{ resource_prefix }}-restored-snapshot" + # snapshot_identifier: "cluster-{{ resource_prefix }}-snapshot" + # register: _result_restored_db_cluster + + # - assert: + # that: + # - _result_restored_db_cluster.changed + # - "'allocated_storage' in _result_restored_db_cluster" + # - _result_restored_db_cluster.allocated_storage == 1 + # - "'cluster_create_time' in _result_restored_db_cluster" + # - _result_restored_db_cluster.copy_tags_to_snapshot == false + # - "'db_cluster_arn' in _result_restored_db_cluster" + # - "_result_restored_db_cluster.db_cluster_identifier == '{{ cluster_id }}-restored-snapshot'" + # - "'db_cluster_parameter_group' in _result_restored_db_cluster" + # - "'db_cluster_resource_id' in _result_restored_db_cluster" + # - "'endpoint' in _result_restored_db_cluster" + # - "'engine' in _result_restored_db_cluster" + # - _result_restored_db_cluster.engine == "{{ engine }}" + # - "'engine_mode' in _result_restored_db_cluster" + # - _result_restored_db_cluster.engine_mode == "provisioned" + # - "'engine_version' in _result_restored_db_cluster" + # - "'master_username' in _result_restored_db_cluster" + # - _result_restored_db_cluster.master_username == "{{ username }}" + # - "'port' in _result_restored_db_cluster" + # - "_result_restored_db_cluster.port == {{ port }}" + # - "'status' in _result_restored_db_cluster" + # - _result_restored_db_cluster.status == "available" + # - "'tags' in _result_restored_db_cluster" + # - "_result_restored_db_cluster.tags | length == 2" + # - "_result_restored_db_cluster.tags.Name == '{{ cluster_id }}'" + # - "_result_restored_db_cluster.tags.Created_by == 'Ansible rds_cluster tests'" + # - "'vpc_security_groups' in _result_restored_db_cluster" + + # TODO: export a snapshot to an S3 bucket and restore cluster from it + # Requires rds_export_task module + + always: + # - name: Delete the snapshot + # rds_cluster: + # snapshot_identifier: "{{ cluster_id }}-snapshot" + # register: _result_delete_snapshot + + - name: Delete DB cluster without creating a final snapshot + rds_cluster: + state: absent + cluster_id: "{{ item }}" + skip_final_snapshot: True + ignore_errors: true + loop: + - "{{ cluster_id }}" + - "{{ cluster_id }}-point-in-time" diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_tag.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_tag.yml new file mode 100644 index 00000000000..e54c955e450 --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/tasks/test_tag.yml @@ -0,0 +1,291 @@ +--- +- block: + - name: Ensure the resource doesn't exist + rds_cluster: + id: "{{ cluster_id }}" + state: absent + engine: "{{ engine}}" + username: "{{ username }}" + password: "{{ password }}" + skip_final_snapshot: True + register: _result_delete_db_cluster + + - assert: + that: + - not _result_delete_db_cluster.changed + ignore_errors: yes + + - name: Create a DB cluster + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: "{{ tags_create }}" + register: _result_create_db_cluster + + - assert: + that: + - _result_create_db_cluster.changed + - "'allocated_storage' in _result_create_db_cluster" + - _result_create_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_create_db_cluster" + - _result_create_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_create_db_cluster" + - "'db_cluster_identifier' in _result_create_db_cluster" + - _result_create_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_create_db_cluster" + - "'db_cluster_resource_id' in _result_create_db_cluster" + - "'endpoint' in _result_create_db_cluster" + - "'engine' in _result_create_db_cluster" + - _result_create_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_create_db_cluster" + - _result_create_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_create_db_cluster" + - "'master_username' in _result_create_db_cluster" + - _result_create_db_cluster.master_username == "{{ username }}" + - "'port' in _result_create_db_cluster" + - "_result_create_db_cluster.port == {{ port }}" + - "'status' in _result_create_db_cluster" + - _result_create_db_cluster.status == 'available' + - _result_create_db_cluster.storage_encrypted == false + - "'tags' in _result_create_db_cluster" + - _result_create_db_cluster.tags | length == 2 + - _result_create_db_cluster.tags["Created_By"] == "{{ tags_create["Created_By"] }}" + - _result_create_db_cluster.tags["Name"] == "{{ tags_create["Name"]}}" + - "'vpc_security_groups' in _result_create_db_cluster" + + - name: Test tags are not purged if purge_tags is False + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ new_password }}" + cluster_id: "{{ cluster_id }}" + tags: {} + purge_tags: False + register: _result_tag_db_cluster + + - assert: + that: + - not _result_tag_db_cluster.changed + - "'allocated_storage' in _result_tag_db_cluster" + - _result_tag_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_tag_db_cluster" + - _result_tag_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_tag_db_cluster" + - "'db_cluster_identifier' in _result_tag_db_cluster" + - _result_tag_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_tag_db_cluster" + - "'db_cluster_resource_id' in _result_tag_db_cluster" + - "'endpoint' in _result_tag_db_cluster" + - "'engine' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_tag_db_cluster" + - "'master_username' in _result_tag_db_cluster" + - _result_tag_db_cluster.master_username == "{{ username }}" + - "'port' in _result_tag_db_cluster" + - "_result_tag_db_cluster.port == {{ port }}" + - "'status' in _result_tag_db_cluster" + - _result_tag_db_cluster.status == 'available' + - _result_tag_db_cluster.storage_encrypted == false + - "'tags' in _result_tag_db_cluster" + - _result_tag_db_cluster.tags | length == 2 + - _result_tag_db_cluster.tags["Created_By"] == "{{ tags_create["Created_By"] }}" + - _result_tag_db_cluster.tags["Name"] == "{{ tags_create["Name"] }}" + - "'vpc_security_groups' in _result_tag_db_cluster" + + - name: Add a tag and remove a tag (purge_tags is True) + rds_cluster: + cluster_id: "{{ cluster_id }}" + state: present + tags: "{{ tags_patch }}" + register: _result_tag_db_cluster + + - assert: + that: + - _result_tag_db_cluster.changed + - "'allocated_storage' in _result_tag_db_cluster" + - _result_tag_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_tag_db_cluster" + - _result_tag_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_tag_db_cluster" + - "'db_cluster_identifier' in _result_tag_db_cluster" + - _result_tag_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_tag_db_cluster" + - "'db_cluster_resource_id' in _result_tag_db_cluster" + - "'endpoint' in _result_tag_db_cluster" + - "'engine' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_tag_db_cluster" + - "'master_username' in _result_tag_db_cluster" + - _result_tag_db_cluster.master_username == "{{ username }}" + - "'port' in _result_tag_db_cluster" + - "_result_tag_db_cluster.port == {{ port }}" + - "'status' in _result_tag_db_cluster" + - _result_tag_db_cluster.status == 'available' + - _result_tag_db_cluster.storage_encrypted == false + - "'tags' in _result_tag_db_cluster" + - _result_tag_db_cluster.tags | length == 2 + - _result_tag_db_cluster.tags["Name"] == "{{ tags_patch['Name'] }}" + - "'vpc_security_groups' in _result_tag_db_cluster" + + - name: Purge a tag from the cluster (CHECK MODE) + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: + Created_By: "Ansible_rds_cluster_integration_test" + register: _result_tag_db_cluster + check_mode: true + + - assert: + that: + - _result_tag_db_cluster.changed + + - name: Purge a tag from the cluster + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: + Created_By: "Ansible_rds_cluster_integration_test" + register: _result_tag_db_cluster + + - assert: + that: + - _result_tag_db_cluster.changed + - "'allocated_storage' in _result_tag_db_cluster" + - _result_tag_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_tag_db_cluster" + - _result_tag_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_tag_db_cluster" + - "'db_cluster_identifier' in _result_tag_db_cluster" + - _result_tag_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_tag_db_cluster" + - "'db_cluster_resource_id' in _result_tag_db_cluster" + - "'endpoint' in _result_tag_db_cluster" + - "'engine' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_tag_db_cluster" + - "'master_username' in _result_tag_db_cluster" + - _result_tag_db_cluster.master_username == "{{ username }}" + - "'port' in _result_tag_db_cluster" + - "_result_tag_db_cluster.port == {{ port }}" + - "'status' in _result_tag_db_cluster" + - _result_tag_db_cluster.status == 'available' + - _result_tag_db_cluster.storage_encrypted == false + - "'tags' in _result_tag_db_cluster" + - _result_tag_db_cluster.tags | length == 1 + - _result_tag_db_cluster.tags["Created_By"] == "Ansible_rds_cluster_integration_test" + - "'vpc_security_groups' in _result_tag_db_cluster" + + - name: Add a tag to the cluster (CHECK MODE) + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: + Name: "cluster-{{ resource_prefix }}" + Created_By: "Ansible_rds_cluster_integration_test" + register: _result_tag_db_cluster + check_mode: true + + - assert: + that: + - _result_tag_db_cluster.changed + + - name: Add a tag to the cluster + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: "{{ tags_create }}" + register: _result_tag_db_cluster + + - assert: + that: + - _result_tag_db_cluster.changed + - "'allocated_storage' in _result_tag_db_cluster" + - _result_tag_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_tag_db_cluster" + - _result_tag_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_tag_db_cluster" + - "'db_cluster_identifier' in _result_tag_db_cluster" + - _result_tag_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_tag_db_cluster" + - "'db_cluster_resource_id' in _result_tag_db_cluster" + - "'endpoint' in _result_tag_db_cluster" + - "'engine' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_tag_db_cluster" + - "'master_username' in _result_tag_db_cluster" + - _result_tag_db_cluster.master_username == "{{ username }}" + - "'port' in _result_tag_db_cluster" + - "_result_tag_db_cluster.port == {{ port }}" + - "'status' in _result_tag_db_cluster" + - _result_tag_db_cluster.status == 'available' + - _result_tag_db_cluster.storage_encrypted == false + - "'tags' in _result_tag_db_cluster" + - _result_tag_db_cluster.tags | length == 2 + - _result_tag_db_cluster.tags["Created_By"] == "{{ tags_create["Created_By"]}}" + - _result_tag_db_cluster.tags["Name"] == "{{ tags_create["Name"]}}" + - "'vpc_security_groups' in _result_tag_db_cluster" + + - name: Remove all tags + rds_cluster: + engine: "{{ engine }}" + username: "{{ username }}" + password: "{{ password }}" + cluster_id: "{{ cluster_id }}" + tags: {} + register: _result_tag_db_cluster + + - assert: + that: + - _result_tag_db_cluster.changed + - "'allocated_storage' in _result_tag_db_cluster" + - _result_tag_db_cluster.allocated_storage == 1 + - "'cluster_create_time' in _result_tag_db_cluster" + - _result_tag_db_cluster.copy_tags_to_snapshot == false + - "'db_cluster_arn' in _result_tag_db_cluster" + - "'db_cluster_identifier' in _result_tag_db_cluster" + - _result_tag_db_cluster.db_cluster_identifier == "{{ cluster_id }}" + - "'db_cluster_parameter_group' in _result_tag_db_cluster" + - "'db_cluster_resource_id' in _result_tag_db_cluster" + - "'endpoint' in _result_tag_db_cluster" + - "'engine' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine == "{{ engine }}" + - "'engine_mode' in _result_tag_db_cluster" + - _result_tag_db_cluster.engine_mode == "provisioned" + - "'engine_version' in _result_tag_db_cluster" + - "'master_username' in _result_tag_db_cluster" + - _result_tag_db_cluster.master_username == "{{ username }}" + - "'port' in _result_tag_db_cluster" + - "_result_tag_db_cluster.port == {{ port }}" + - "'status' in _result_tag_db_cluster" + - _result_tag_db_cluster.status == 'available' + - _result_tag_db_cluster.storage_encrypted == false + - "'tags' in _result_tag_db_cluster" + - _result_tag_db_cluster.tags | length == 0 + - "'vpc_security_groups' in _result_tag_db_cluster" + + always: + - name: Delete DB cluster without creating a final snapshot + rds_cluster: + state: absent + cluster_id: "{{ cluster_id }}" + skip_final_snapshot: True + ignore_errors: true diff --git a/tests/integration/targets/rds_cluster/roles/rds_cluster/vars/main.yml b/tests/integration/targets/rds_cluster/roles/rds_cluster/vars/main.yml new file mode 100644 index 00000000000..ed97d539c09 --- /dev/null +++ b/tests/integration/targets/rds_cluster/roles/rds_cluster/vars/main.yml @@ -0,0 +1 @@ +--- diff --git a/tests/integration/targets/rds_cluster/runme.sh b/tests/integration/targets/rds_cluster/runme.sh new file mode 100755 index 00000000000..21720b263e1 --- /dev/null +++ b/tests/integration/targets/rds_cluster/runme.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# +# Beware: most of our tests here are run in parallel. +# To add new tests you'll need to add a new host to the inventory and a matching +# '{{ inventory_hostname }}'.yml file in roles/rds_cluster/tasks/ + + +set -eux + +export ANSIBLE_ROLES_PATH=../ + +ansible-playbook main.yml -i inventory "$@"