summaryrefslogtreecommitdiffstats
path: root/ansible_collections/amazon/aws/plugins/module_utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:18:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-05 16:18:41 +0000
commitb643c52cf29ce5bbab738b43290af3556efa1ca9 (patch)
tree21d5c53d7a9b696627a255777cefdf6f78968824 /ansible_collections/amazon/aws/plugins/module_utils
parentReleasing progress-linux version 9.5.1+dfsg-1~progress7.99u1. (diff)
downloadansible-b643c52cf29ce5bbab738b43290af3556efa1ca9.tar.xz
ansible-b643c52cf29ce5bbab738b43290af3556efa1ca9.zip
Merging upstream version 10.0.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/amazon/aws/plugins/module_utils')
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/acm.py2
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/botocore.py20
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/common.py2
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/ec2.py18
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/elbv2.py108
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/iam.py10
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/modules.py10
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/policy.py57
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/rds.py41
-rw-r--r--ansible_collections/amazon/aws/plugins/module_utils/s3.py4
10 files changed, 159 insertions, 113 deletions
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/acm.py b/ansible_collections/amazon/aws/plugins/module_utils/acm.py
index ab3a9f073..4febe8743 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/acm.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/acm.py
@@ -40,7 +40,7 @@ def acm_catch_boto_exception(func):
return func(*args, **kwargs)
except is_boto3_error_code(ignore_error_codes):
return None
- except (BotoCoreError, ClientError) as e:
+ except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except
if not module:
raise
module.fail_json_aws(e, msg=error)
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/botocore.py b/ansible_collections/amazon/aws/plugins/module_utils/botocore.py
index 858e4e593..d5ad7ea83 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/botocore.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/botocore.py
@@ -202,7 +202,14 @@ def _aws_region(params):
return None
-def get_aws_region(module, boto3=None):
+def get_aws_region(module, boto3=None): # pylint: disable=redefined-outer-name
+ if boto3 is not None:
+ module.deprecate(
+ "get_aws_region(): the boto3 parameter will be removed in a release after 2025-05-01. "
+ "The parameter has been ignored since release 4.0.0.",
+ date="2025-05-01",
+ collection_name="amazon.aws",
+ )
try:
return _aws_region(module.params)
except AnsibleBotocoreError as e:
@@ -266,7 +273,14 @@ def _aws_connection_info(params):
return region, endpoint_url, boto_params
-def get_aws_connection_info(module, boto3=None):
+def get_aws_connection_info(module, boto3=None): # pylint: disable=redefined-outer-name
+ if boto3 is not None:
+ module.deprecate(
+ "get_aws_connection_info(): the boto3 parameter will be removed in a release after 2025-05-01. "
+ "The parameter has been ignored since release 4.0.0.",
+ date="2025-05-01",
+ collection_name="amazon.aws",
+ )
try:
return _aws_connection_info(module.params)
except AnsibleBotocoreError as e:
@@ -335,7 +349,7 @@ def is_boto3_error_code(code, e=None):
import sys
dummy, e, dummy = sys.exc_info()
- if not isinstance(code, list):
+ if not isinstance(code, (list, tuple, set)):
code = [code]
if isinstance(e, ClientError) and e.response["Error"]["Code"] in code:
return ClientError
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/common.py b/ansible_collections/amazon/aws/plugins/module_utils/common.py
index 41ba80231..e802a8d80 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/common.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/common.py
@@ -4,7 +4,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
AMAZON_AWS_COLLECTION_NAME = "amazon.aws"
-AMAZON_AWS_COLLECTION_VERSION = "7.5.0"
+AMAZON_AWS_COLLECTION_VERSION = "8.0.0"
_collection_info_context = {
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/ec2.py b/ansible_collections/amazon/aws/plugins/module_utils/ec2.py
index afe8208f5..f3aa9f3f1 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/ec2.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/ec2.py
@@ -39,6 +39,7 @@ up in this module because "that's where the AWS code was" (originally).
import re
+import ansible.module_utils.common.warnings as ansible_warnings
from ansible.module_utils.ansible_release import __version__
# Used to live here, moved into ansible.module_utils.common.dict_transformations
@@ -72,7 +73,6 @@ from .modules import aws_argument_spec as ec2_argument_spec # pylint: disable=u
# Used to live here, moved into ansible_collections.amazon.aws.plugins.module_utils.policy
from .policy import _py3cmp as py3cmp # pylint: disable=unused-import
from .policy import compare_policies # pylint: disable=unused-import
-from .policy import sort_json_policy_dict # pylint: disable=unused-import
# Used to live here, moved into ansible_collections.amazon.aws.plugins.module_utils.retries
from .retries import AWSRetry # pylint: disable=unused-import
@@ -99,12 +99,22 @@ def get_ec2_security_group_ids_from_names(sec_group_list, ec2_connection, vpc_id
a try block
"""
- def get_sg_name(sg, boto3=None):
+ def get_sg_name(sg):
return str(sg["GroupName"])
- def get_sg_id(sg, boto3=None):
+ def get_sg_id(sg):
return str(sg["GroupId"])
+ if boto3 is not None:
+ ansible_warnings.deprecate(
+ (
+ "The boto3 parameter for get_ec2_security_group_ids_from_names() has been deprecated."
+ "The parameter has been ignored since release 4.0.0."
+ ),
+ date="2025-05-01",
+ collection_name="amazon.aws",
+ )
+
sec_group_id_list = []
if isinstance(sec_group_list, string_types):
@@ -124,7 +134,7 @@ def get_ec2_security_group_ids_from_names(sec_group_list, ec2_connection, vpc_id
else:
all_sec_groups = ec2_connection.describe_security_groups()["SecurityGroups"]
- unmatched = set(sec_group_list).difference(str(get_sg_name(all_sg, boto3)) for all_sg in all_sec_groups)
+ unmatched = set(sec_group_list).difference(str(get_sg_name(all_sg)) for all_sg in all_sec_groups)
sec_group_name_list = list(set(sec_group_list) - set(unmatched))
if len(unmatched) > 0:
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py b/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py
index 758eb9a33..3da2114c7 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py
@@ -449,7 +449,7 @@ class ApplicationLoadBalancer(ElasticLoadBalancerV2):
if module.params.get("security_groups") is not None:
try:
self.security_groups = AWSRetry.jittered_backoff()(get_ec2_security_group_ids_from_names)(
- module.params.get("security_groups"), self.connection_ec2, boto3=True
+ module.params.get("security_groups"), self.connection_ec2
)
except ValueError as e:
self.module.fail_json(msg=str(e), exception=traceback.format_exc())
@@ -775,6 +775,9 @@ class ELBListeners:
dict((x, listener_dict[x]) for x in listener_dict if listener_dict[x] is not None)
for listener_dict in listeners
]
+ # AlpnPolicy is set as str into input but API is expected a list
+ # Transform a single item into a list of one element
+ listeners = self._ensure_listeners_alpn_policy(listeners)
self.listeners = self._ensure_listeners_default_action_has_arn(listeners)
self.current_listeners = self._get_elb_listeners()
self.purge_listeners = module.params.get("purge_listeners")
@@ -805,6 +808,16 @@ class ELBListeners:
except (BotoCoreError, ClientError) as e:
self.module.fail_json_aws(e)
+ @staticmethod
+ def _ensure_listeners_alpn_policy(listeners):
+ result = []
+ for l in listeners:
+ update_listener = deepcopy(l)
+ if "AlpnPolicy" in l:
+ update_listener["AlpnPolicy"] = [update_listener["AlpnPolicy"]]
+ result.append(update_listener)
+ return result
+
def _ensure_listeners_default_action_has_arn(self, listeners):
"""
If a listener DefaultAction has been passed with a Target Group Name instead of ARN, lookup the ARN and
@@ -863,7 +876,8 @@ class ELBListeners:
return listeners_to_add, listeners_to_modify, listeners_to_delete
- def _compare_listener(self, current_listener, new_listener):
+ @staticmethod
+ def _compare_listener(current_listener, new_listener):
"""
Compare two listeners.
@@ -882,43 +896,53 @@ class ELBListeners:
if current_listener["Protocol"] != new_listener["Protocol"]:
modified_listener["Protocol"] = new_listener["Protocol"]
- # If Protocol is HTTPS, check additional attributes
- if current_listener["Protocol"] == "HTTPS" and new_listener["Protocol"] == "HTTPS":
- # Cert
- if current_listener["SslPolicy"] != new_listener["SslPolicy"]:
- modified_listener["SslPolicy"] = new_listener["SslPolicy"]
- if (
- current_listener["Certificates"][0]["CertificateArn"]
- != new_listener["Certificates"][0]["CertificateArn"]
+ # If Protocol is HTTPS or TLS, check additional attributes
+ # SslPolicy
+ new_ssl_policy = new_listener.get("SslPolicy")
+ if new_ssl_policy and new_listener["Protocol"] in ("HTTPS", "TLS"):
+ current_ssl_policy = current_listener.get("SslPolicy")
+ if not current_ssl_policy or (current_ssl_policy and current_ssl_policy != new_ssl_policy):
+ modified_listener["SslPolicy"] = new_ssl_policy
+
+ # Certificates
+ new_certificates = new_listener.get("Certificates")
+ if new_certificates and new_listener["Protocol"] in ("HTTPS", "TLS"):
+ current_certificates = current_listener.get("Certificates")
+ if not current_certificates or (
+ current_certificates
+ and current_certificates[0]["CertificateArn"] != new_certificates[0]["CertificateArn"]
):
- modified_listener["Certificates"] = []
- modified_listener["Certificates"].append({})
- modified_listener["Certificates"][0]["CertificateArn"] = new_listener["Certificates"][0][
- "CertificateArn"
- ]
- elif current_listener["Protocol"] != "HTTPS" and new_listener["Protocol"] == "HTTPS":
- modified_listener["SslPolicy"] = new_listener["SslPolicy"]
- modified_listener["Certificates"] = []
- modified_listener["Certificates"].append({})
- modified_listener["Certificates"][0]["CertificateArn"] = new_listener["Certificates"][0]["CertificateArn"]
+ modified_listener["Certificates"] = [{"CertificateArn": new_certificates[0]["CertificateArn"]}]
# Default action
# If the lengths of the actions are the same, we'll have to verify that the
# contents of those actions are the same
- if len(current_listener["DefaultActions"]) == len(new_listener["DefaultActions"]):
- current_actions_sorted = _sort_actions(current_listener["DefaultActions"])
- new_actions_sorted = _sort_actions(new_listener["DefaultActions"])
-
- new_actions_sorted_no_secret = [_prune_secret(i) for i in new_actions_sorted]
-
- if [_prune_ForwardConfig(i) for i in current_actions_sorted] != [
- _prune_ForwardConfig(i) for i in new_actions_sorted_no_secret
- ]:
- modified_listener["DefaultActions"] = new_listener["DefaultActions"]
- # If the action lengths are different, then replace with the new actions
- else:
- modified_listener["DefaultActions"] = new_listener["DefaultActions"]
+ current_default_actions = current_listener.get("DefaultActions")
+ new_default_actions = new_listener.get("DefaultActions")
+ if new_default_actions:
+ if current_default_actions and len(current_default_actions) == len(new_default_actions):
+ current_actions_sorted = _sort_actions(current_default_actions)
+ new_actions_sorted = _sort_actions(new_default_actions)
+
+ new_actions_sorted_no_secret = [_prune_secret(i) for i in new_actions_sorted]
+
+ if [_prune_ForwardConfig(i) for i in current_actions_sorted] != [
+ _prune_ForwardConfig(i) for i in new_actions_sorted_no_secret
+ ]:
+ modified_listener["DefaultActions"] = new_default_actions
+ # If the action lengths are different, then replace with the new actions
+ else:
+ modified_listener["DefaultActions"] = new_default_actions
+
+ new_alpn_policy = new_listener.get("AlpnPolicy")
+ if new_alpn_policy:
+ if current_listener["Protocol"] == "TLS" and new_listener["Protocol"] == "TLS":
+ current_alpn_policy = current_listener.get("AlpnPolicy")
+ if not current_alpn_policy or current_alpn_policy[0] != new_alpn_policy[0]:
+ modified_listener["AlpnPolicy"] = new_alpn_policy
+ elif current_listener["Protocol"] != "TLS" and new_listener["Protocol"] == "TLS":
+ modified_listener["AlpnPolicy"] = new_alpn_policy
if modified_listener:
return modified_listener
@@ -946,7 +970,23 @@ class ELBListener:
# Rules is not a valid parameter for create_listener
if "Rules" in self.listener:
self.listener.pop("Rules")
- AWSRetry.jittered_backoff()(self.connection.create_listener)(LoadBalancerArn=self.elb_arn, **self.listener)
+
+ # handle multiple certs by adding only 1 cert during listener creation and make calls to add_listener_certificates to add other certs
+ listener_certificates = self.listener.get("Certificates", [])
+ first_certificate, other_certs = [], []
+ if len(listener_certificates) > 0:
+ first_certificate, other_certs = listener_certificates[0], listener_certificates[1:]
+ self.listener["Certificates"] = [first_certificate]
+ # create listener
+ create_listener_result = AWSRetry.jittered_backoff()(self.connection.create_listener)(
+ LoadBalancerArn=self.elb_arn, **self.listener
+ )
+ # only one cert can be specified per call to add_listener_certificates
+ for cert in other_certs:
+ AWSRetry.jittered_backoff()(self.connection.add_listener_certificates)(
+ ListenerArn=create_listener_result["Listeners"][0]["ListenerArn"], Certificates=[cert]
+ )
+
except (BotoCoreError, ClientError) as e:
self.module.fail_json_aws(e)
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/iam.py b/ansible_collections/amazon/aws/plugins/module_utils/iam.py
index 56920d53e..155a63152 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/iam.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/iam.py
@@ -49,14 +49,14 @@ def detach_iam_group_policy(client, arn, group):
@IAMErrorHandler.deletion_error_handler("detach role policy")
@AWSRetry.jittered_backoff()
def detach_iam_role_policy(client, arn, role):
- client.detach_group_policy(PolicyArn=arn, RoleName=role)
+ client.detach_role_policy(PolicyArn=arn, RoleName=role)
return True
@IAMErrorHandler.deletion_error_handler("detach user policy")
@AWSRetry.jittered_backoff()
def detach_iam_user_policy(client, arn, user):
- client.detach_group_policy(PolicyArn=arn, UserName=user)
+ client.detach_user_policy(PolicyArn=arn, UserName=user)
return True
@@ -446,8 +446,6 @@ def normalize_iam_access_keys(access_keys: BotoResourceList) -> AnsibleAWSResour
def normalize_iam_instance_profile(profile: BotoResource) -> AnsibleAWSResource:
"""
Converts a boto3 format IAM instance profile into "Ansible" format
-
- _v7_compat is deprecated and will be removed in release after 2025-05-01 DO NOT USE.
"""
transforms = {"Roles": _normalize_iam_roles}
transformed_profile = boto3_resource_to_ansible_dict(profile, nested_transforms=transforms)
@@ -458,10 +456,10 @@ def normalize_iam_role(role: BotoResource, _v7_compat: bool = False) -> AnsibleA
"""
Converts a boto3 format IAM instance role into "Ansible" format
- _v7_compat is deprecated and will be removed in release after 2025-05-01 DO NOT USE.
+ _v7_compat is deprecated and will be removed in release after 2026-05-01 DO NOT USE.
"""
transforms = {"InstanceProfiles": _normalize_iam_instance_profiles}
- ignore_list = [] if _v7_compat else ["AssumeRolePolicyDocument"]
+ ignore_list = ["AssumeRolePolicyDocument"]
transformed_role = boto3_resource_to_ansible_dict(role, nested_transforms=transforms, ignore_list=ignore_list)
if _v7_compat and role.get("AssumeRolePolicyDocument"):
transformed_role["assume_role_policy_document_raw"] = role["AssumeRolePolicyDocument"]
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/modules.py b/ansible_collections/amazon/aws/plugins/module_utils/modules.py
index 8a2ff3c0b..82a81811d 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/modules.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/modules.py
@@ -84,11 +84,11 @@ class AnsibleAWSModule:
def __init__(self, **kwargs):
local_settings = {}
- for key in AnsibleAWSModule.default_settings:
+ for key, default_value in AnsibleAWSModule.default_settings.items():
try:
local_settings[key] = kwargs.pop(key)
except KeyError:
- local_settings[key] = AnsibleAWSModule.default_settings[key]
+ local_settings[key] = default_value
self.settings = local_settings
if local_settings["default_args"]:
@@ -192,21 +192,21 @@ class AnsibleAWSModule:
return self._module.md5(*args, **kwargs)
def client(self, service, retry_decorator=None, **extra_params):
- region, endpoint_url, aws_connect_kwargs = get_aws_connection_info(self, boto3=True)
+ region, endpoint_url, aws_connect_kwargs = get_aws_connection_info(self)
kw_args = dict(region=region, endpoint=endpoint_url, **aws_connect_kwargs)
kw_args.update(extra_params)
conn = boto3_conn(self, conn_type="client", resource=service, **kw_args)
return conn if retry_decorator is None else RetryingBotoClientWrapper(conn, retry_decorator)
def resource(self, service, **extra_params):
- region, endpoint_url, aws_connect_kwargs = get_aws_connection_info(self, boto3=True)
+ region, endpoint_url, aws_connect_kwargs = get_aws_connection_info(self)
kw_args = dict(region=region, endpoint=endpoint_url, **aws_connect_kwargs)
kw_args.update(extra_params)
return boto3_conn(self, conn_type="resource", resource=service, **kw_args)
@property
def region(self):
- return get_aws_region(self, True)
+ return get_aws_region(self)
def fail_json_aws(self, exception, msg=None, **kwargs):
"""call fail_json with processed exception
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/policy.py b/ansible_collections/amazon/aws/plugins/module_utils/policy.py
index 60b096f84..61b5edc1c 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/policy.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/policy.py
@@ -30,7 +30,6 @@
from functools import cmp_to_key
-import ansible.module_utils.common.warnings as ansible_warnings
from ansible.module_utils._text import to_text
from ansible.module_utils.six import binary_type
from ansible.module_utils.six import string_types
@@ -151,59 +150,3 @@ def compare_policies(current_policy, new_policy, default_version="2008-10-17"):
new_policy.setdefault("Version", default_version)
return set(_hashable_policy(new_policy, [])) != set(_hashable_policy(current_policy, []))
-
-
-def sort_json_policy_dict(policy_dict):
- """
- DEPRECATED - will be removed in amazon.aws 8.0.0
-
- Sort any lists in an IAM JSON policy so that comparison of two policies with identical values but
- different orders will return true
- Args:
- policy_dict (dict): Dict representing IAM JSON policy.
- Basic Usage:
- >>> my_iam_policy = {'Principle': {'AWS':["31","7","14","101"]}
- >>> sort_json_policy_dict(my_iam_policy)
- Returns:
- Dict: Will return a copy of the policy as a Dict but any List will be sorted
- {
- 'Principle': {
- 'AWS': [ '7', '14', '31', '101' ]
- }
- }
- """
-
- ansible_warnings.deprecate(
- (
- "amazon.aws.module_utils.policy.sort_json_policy_dict has been deprecated, consider using "
- "amazon.aws.module_utils.policy.compare_policies instead"
- ),
- version="8.0.0",
- collection_name="amazon.aws",
- )
-
- def value_is_list(my_list):
- checked_list = []
- for item in my_list:
- if isinstance(item, dict):
- checked_list.append(sort_json_policy_dict(item))
- elif isinstance(item, list):
- checked_list.append(value_is_list(item))
- else:
- checked_list.append(item)
-
- # Sort list. If it's a list of dictionaries, sort by tuple of key-value
- # pairs, since Python 3 doesn't allow comparisons such as `<` between dictionaries.
- checked_list.sort(key=lambda x: sorted(x.items()) if isinstance(x, dict) else x)
- return checked_list
-
- ordered_policy_dict = {}
- for key, value in policy_dict.items():
- if isinstance(value, dict):
- ordered_policy_dict[key] = sort_json_policy_dict(value)
- elif isinstance(value, list):
- ordered_policy_dict[key] = value_is_list(value)
- else:
- ordered_policy_dict[key] = value
-
- return ordered_policy_dict
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/rds.py b/ansible_collections/amazon/aws/plugins/module_utils/rds.py
index 85cde2e4e..20e0ae5e0 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/rds.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/rds.py
@@ -5,6 +5,9 @@
from collections import namedtuple
from time import sleep
+from typing import Any
+from typing import Dict
+from typing import List
try:
from botocore.exceptions import BotoCoreError
@@ -16,6 +19,8 @@ except ImportError:
from ansible.module_utils._text import to_text
from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict
+from .botocore import is_boto3_error_code
+from .core import AnsibleAWSModule
from .retries import AWSRetry
from .tagging import ansible_dict_to_boto3_tag_list
from .tagging import boto3_tag_list_to_ansible_dict
@@ -440,3 +445,39 @@ def update_iam_roles(client, module, instance_id, roles_to_add, roles_to_remove)
params = {"DBInstanceIdentifier": instance_id, "RoleArn": role["role_arn"], "FeatureName": role["feature_name"]}
_result, changed = call_method(client, module, method_name="add_role_to_db_instance", parameters=params)
return changed
+
+
+@AWSRetry.jittered_backoff()
+def describe_db_cluster_parameter_groups(
+ module: AnsibleAWSModule, connection: Any, group_name: str
+) -> List[Dict[str, Any]]:
+ result = []
+ try:
+ params = {}
+ if group_name is not None:
+ params["DBClusterParameterGroupName"] = group_name
+ paginator = connection.get_paginator("describe_db_cluster_parameter_groups")
+ result = paginator.paginate(**params).build_full_result()["DBClusterParameterGroups"]
+ except is_boto3_error_code("DBParameterGroupNotFound"):
+ pass
+ except ClientError as e: # pylint: disable=duplicate-except
+ module.fail_json_aws(e, msg="Couldn't access parameter groups information")
+ return result
+
+
+@AWSRetry.jittered_backoff()
+def describe_db_cluster_parameters(
+ module: AnsibleAWSModule, connection: Any, group_name: str, source: str = "all"
+) -> List[Dict[str, Any]]:
+ result = []
+ try:
+ paginator = connection.get_paginator("describe_db_cluster_parameters")
+ params = {"DBClusterParameterGroupName": group_name}
+ if source != "all":
+ params["Source"] = source
+ result = paginator.paginate(**params).build_full_result()["Parameters"]
+ except is_boto3_error_code("DBParameterGroupNotFound"):
+ pass
+ except ClientError as e: # pylint: disable=duplicate-except
+ module.fail_json_aws(e, msg="Couldn't access RDS cluster parameters information")
+ return result
diff --git a/ansible_collections/amazon/aws/plugins/module_utils/s3.py b/ansible_collections/amazon/aws/plugins/module_utils/s3.py
index 73297ffc7..961f36f22 100644
--- a/ansible_collections/amazon/aws/plugins/module_utils/s3.py
+++ b/ansible_collections/amazon/aws/plugins/module_utils/s3.py
@@ -58,7 +58,7 @@ def calculate_etag(module, filename, etag, s3, bucket, obj, version=None):
if not HAS_MD5:
return None
- if "-" in etag:
+ if etag is not None and "-" in etag:
# Multi-part ETag; a hash of the hashes of each part.
parts = int(etag[1:-1].split("-")[1])
try:
@@ -73,7 +73,7 @@ def calculate_etag_content(module, content, etag, s3, bucket, obj, version=None)
if not HAS_MD5:
return None
- if "-" in etag:
+ if etag is not None and "-" in etag:
# Multi-part ETag; a hash of the hashes of each part.
parts = int(etag[1:-1].split("-")[1])
try: