Repository: ansible-collections/amazon.aws

Issue:
Add support for describe_global_clusters command to rds_cluster_info module
### Summary


The rds_cluster_info module currently provides support for retrieving information about RDS database clusters using the describe_db_clusters CLI command. To obtain information about global clusters, such as Aurora-MySQL, we will need to incorporate the describe_global_clusters CLI command into this module.

### Issue Type

Feature Idea

### Component Name

rds_cluster_info

### Additional Information

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)

```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:

Resolving patch:
diff --git a/plugins/modules/rds_global_cluster_info.py b/plugins/modules/rds_global_cluster_info.py
new file mode 100644
--- /dev/null
+++ b/plugins/modules/rds_global_cluster_info.py
@@ -0,0 +1,200 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2023 Ansible Project
+# Copyright (c) 2023 Gomathi Selvi Srinivasan (@GomathiselviS)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+DOCUMENTATION = r"""
+module: rds_global_cluster_info
+version_added: 7.0.0
+short_description: Obtain information about Aurora global database clusters
+description:
+  - Obtain information about Aurora global database clusters.
+options:
+    global_cluster_identifier:
+        description:
+          - The user-supplied Global DB cluster identifier.
+          - If this parameter is specified, information from only the specific DB cluster is returned.
+          - This parameter is not case-sensitive.
+          - If supplied, must match an existing DBClusterIdentifier.
+        type: str
+
+author:
+  - Gomathi Selvi Srinivasan (@GomathiselviS)
+notes:
+  - While developing this module, describe_global_cluster CLI did not yield any tag information.
+  - Consequently, the "tags" parameter is not included in this module.
+extends_documentation_fragment:
+  - amazon.aws.common.modules
+  - amazon.aws.region.modules
+  - amazon.aws.boto3
+"""
+
+EXAMPLES = r"""
+- name: Get info of all existing DB clusters
+  amazon.aws.rds_global_cluster_info:
+  register: _result_cluster_info
+
+- name: Get info on a specific DB cluster
+  amazon.aws.rds_global_cluster_info:
+    global_cluster_identifier: "{{ cluster_id }}"
+  register: _result_global_cluster_info
+
+"""
+
+RETURN = r"""
+global_clusters:
+  description: List of global clusters.
+  returned: always
+  type: list
+  elements: dict
+  contains:
+    global_cluster_identifier:
+        description: User-supplied global database cluster identifier.
+        type: str
+        sample: "ansible-test-global-cluster"
+    global_cluster_resource_id:
+        description:
+        - The Amazon Web Services Region-unique, immutable identifier for the global database cluster.
+        type: str
+        sample: cluster-xxx
+    global_cluster_arn:
+        description:
+        - The Amazon Resource Name (ARN) for the global database cluster.
+        type: str
+        sample: "arn:aws:rds::xxx:global-cluster:ansible-test-global-cluster"
+    status:
+        description: The status of the DB cluster.
+        type: str
+        sample: available
+    engine:
+        description: The database engine of the DB cluster.
+        type: str
+        sample: aurora-postgresql
+    engine_version:
+        description: The database engine version.
+        type: str
+        sample: 14.8
+    storage_encrypted:
+        description: Whether the DB cluster is storage encrypted.
+        type: bool
+        sample: false
+    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
+    gloabl_cluster_members:
+        description:
+        - The list of primary and secondary clusters within the global database
+          cluster.
+        type: list
+        elements: dict
+        contains:
+            db_cluster_arn:
+                description: The Amazon Resource Name (ARN) for each Aurora DB cluster in the global cluster.
+                type: str
+                sample: arn:aws:rds:us-east-1:123456789012:cluster:ansible-test-primary
+            readers:
+                description: The Amazon Resource Name (ARN) for each read-only secondary cluster associated with the global cluster.
+                type: list
+                elements: str
+                sample: arn:aws:rds:us-east-2:123456789012:cluster:ansible-test-secondary
+            is_writer:
+                description:
+                - Indicates whether the Aurora DB cluster is the primary cluster for the global cluster with which it is associated.
+                type: bool
+                sample: false
+            global_write_forwarding_status:
+                description: The status of write forwarding for a secondary cluster in the global cluster.
+                type: str
+                sample: disabled
+    failover_state:
+        description:
+        - A data object containing all properties for the current state of an in-process or
+          pending switchover or failover process for this global cluster (Aurora global database).
+        - This object is empty unless the SwitchoverGlobalCluster or FailoverGlobalCluster operation was called on this global cluster.
+        type: dict
+        contains:
+            status:
+                description:
+                - The current status of the global cluster.
+                type: str
+                sample: "pending"
+            from_db_cluster_arn:
+                description: The Amazon Resource Name (ARN) of the Aurora DB cluster that is currently being demoted, and which is associated with this state.
+                type: str
+                sample: arn:aws:rds:us-east-1:123456789012:cluster:ansible-test-primary
+            to_db_cluster_arn:
+                description: The Amazon Resource Name (ARN) of the Aurora DB cluster that is currently being promoted, and which is associated with this state.
+                type: str
+                sample: arn:aws:rds:us-east-2:123456789012:cluster:ansible-test-secondary
+            is_data_loss_allowed:
+                description:
+                - Indicates whether the operation is a global switchover or a global failover.
+                - If data loss is allowed, then the operation is a global failover. Otherwise, it is a switchover.
+                type: bool
+                sample: false
+"""
+
+
+try:
+    import botocore
+except ImportError:
+    pass  # handled by AnsibleAWSModule
+
+from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict
+
+from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code
+from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule
+from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry
+
+
+@AWSRetry.jittered_backoff(retries=10)
+def _describe_global_clusters(client, **params):
+    try:
+        paginator = client.get_paginator("describe_global_clusters")
+        return paginator.paginate(**params).build_full_result()["GlobalClusters"]
+    except is_boto3_error_code("GlobalClusterNotFoundFault"):
+        return []
+
+
+def cluster_info(client, module):
+    global_cluster_id = module.params.get("global_cluster_identifier")
+
+    params = dict()
+    if global_cluster_id:
+        params["GlobalClusterIdentifier"] = global_cluster_id
+
+    try:
+        result = _describe_global_clusters(client, **params)
+    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
+        module.fail_json_aws(e, "Couldn't get Global cluster information.")
+
+    return dict(
+        changed=False, global_clusters=[camel_dict_to_snake_dict(cluster, ignore_list=["Tags"]) for cluster in result]
+    )
+
+
+def main():
+    argument_spec = dict(
+        global_cluster_identifier=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()


Test patch:
diff --git a/tests/integration/targets/rds_global_cluster_create/aliases b/tests/integration/targets/rds_global_cluster_create/aliases
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/rds_global_cluster_create/aliases
@@ -0,0 +1,6 @@
+# Multi region not supported in the CI AWS account
+disabled
+time=20m
+cloud/aws
+rds_cluster
+rds_global_cluster_info
diff --git a/tests/integration/targets/rds_global_cluster_create/defaults/main.yml b/tests/integration/targets/rds_global_cluster_create/defaults/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/rds_global_cluster_create/defaults/main.yml
@@ -0,0 +1,12 @@
+# defaults file for rds_global_cluster_create
+
+# Create cluster
+global_cluster_id: global-cluster-{{ resource_prefix }}
+primary_cluster_id: primary-cluster-{{ resource_prefix }}
+primary_instance_id: primary-instance-{{ resource_prefix }}
+secondary_cluster_id: secondary-cluster-{{ resource_prefix }}
+instance_class: db.r5.large
+username: testrdsusername
+password: testrdspassword
+engine: aurora-mysql
+engine_version: 5.7
diff --git a/tests/integration/targets/rds_global_cluster_create/tasks/main.yaml b/tests/integration/targets/rds_global_cluster_create/tasks/main.yaml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/rds_global_cluster_create/tasks/main.yaml
@@ -0,0 +1,108 @@
+- module_defaults:
+    group/aws:
+      region: "{{ aws_region }}"
+      access_key: "{{ aws_access_key }}"
+      secret_key: "{{ aws_secret_key }}"
+      session_token: "{{ security_token | default(omit) }}"
+    group/amazon.cloud.aws:
+      aws_access_key: "{{ aws_access_key }}"
+      aws_secret_key: "{{ aws_secret_key }}"
+      security_token: "{{ security_token | default(omit) }}"
+      region: "{{ aws_region }}"
+  block:
+    - name: Create aurora global cluster
+      amazon.cloud.rds_global_cluster:
+        global_cluster_identifier: "{{ global_cluster_id }}"
+        engine: "{{ engine }}"
+        engine_version: "{{ engine_version }}"
+        region: "{{ aws_region }}"
+        state: present
+
+    - name: Create a primary cluster for global database
+      amazon.aws.rds_cluster:
+        db_cluster_identifier: "{{ primary_cluster_id }}"
+        engine: "{{ engine }}"
+        engine_version: "{{ engine_version }}"
+        master_username: "{{ username }}"
+        master_user_password: "{{ password }}"
+        global_cluster_identifier: "{{ global_cluster_id }}"
+        region: "{{ aws_region }}"
+      register: primary_cluster
+
+    - name: Create an instance connected to primary cluster
+      amazon.aws.rds_instance:
+        db_cluster_identifier: "{{ primary_cluster_id }}"
+        db_instance_identifier: "{{ primary_instance_id }}"
+        region: "{{ aws_region }}"
+        engine: "{{ engine }}"
+        db_instance_class: "{{ instance_class }}"
+
+    - name: Create a read replica cluster for global database
+      amazon.aws.rds_cluster:
+        db_cluster_identifier: "{{ secondary_cluster_id }}"
+        region: "eu-north-1"
+        engine: "{{ engine }}"
+        engine_version: "{{ engine_version }}"
+        global_cluster_identifier: "{{ global_cluster_id }}"
+      register: replica_cluster
+
+    - name: Get Global DB information
+      amazon.aws.rds_global_cluster_info:
+        global_cluster_identifier: "{{ global_cluster_id }}"
+        region: "{{ aws_region }}"
+      register: global_cluster_info
+
+    - name: Get primary cluster info
+      amazon.aws.rds_cluster_info:
+        db_cluster_identifier: "{{ primary_cluster_id }}"
+        region: "{{ aws_region }}"
+      register: primary_cluster_info_result
+
+    - name: Get secondary cluster info
+      amazon.aws.rds_cluster_info:
+        db_cluster_identifier: "{{ secondary_cluster_id }}"
+        region: "eu-north-1"
+      register: secondary_cluster_info_result
+
+    - name: Assert that the primary and secondary clusters are members of the global cluster
+      ansible.builtin.assert:
+        that:
+          - global_cluster_info.global_clusters[0].global_cluster_members[0].db_cluster_arn == primary_cluster_info_result.clusters[0].db_cluster_arn
+          - global_cluster_info.global_clusters[0].global_cluster_members[1].db_cluster_arn == secondary_cluster_info_result.clusters[0].db_cluster_arn
+          - global_cluster_info.global_clusters[0].engine == engine
+
+  always:
+    - name: Delete secondary cluster without creating a final snapshot
+      amazon.aws.rds_cluster:
+        cluster_id: "{{ secondary_cluster_id }}"
+        region: "eu-north-1"
+        global_cluster_identifier: "{{  global_cluster_id }}"
+        remove_from_global_db: true
+        skip_final_snapshot: True
+        state: absent
+      ignore_errors: true
+
+    - name: Delete instance attached to primary cluster
+      amazon.aws.rds_instance:
+        db_instance_identifier: "{{  primary_instance_id }}"
+        region: "{{ aws_region }}"
+        skip_final_snapshot: true
+        wait: false
+        state: absent
+      ignore_errors: true
+
+    - name: Delete primary cluster without creating a final snapshot
+      amazon.aws.rds_cluster:
+        cluster_id: "{{ primary_cluster_id }}"
+        global_cluster_identifier: "{{  global_cluster_id }}"
+        skip_final_snapshot: True
+        region: "{{ aws_region }}"
+        state: absent
+      ignore_errors: true
+
+    - name: Delete Global DB cluster
+      amazon.cloud.rds_global_cluster:
+        state: absent
+        global_cluster_identifier: "{{ global_cluster_id }}"
+        region: "{{ aws_region }}"
+      ignore_errors: true