diff --git a/.github/workflows/units.yml b/.github/workflows/units.yml index adfa2dddd1f..d05d8c10294 100644 --- a/.github/workflows/units.yml +++ b/.github/workflows/units.yml @@ -6,63 +6,3 @@ on: [workflow_call] # allow this workflow to be called from other workflows jobs: unit-source: uses: ansible-network/github_actions/.github/workflows/unit_source.yml@main - with: - matrix_exclude: >- - [ - { - "python-version": "3.11" - }, - { - "ansible-version": "stable-2.12", - "python-version": "3.7" - }, - { - "ansible-version": "stable-2.13", - "python-version": "3.7" - }, - { - "ansible-version": "stable-2.12", - "python-version": "3.8" - }, - { - "ansible-version": "stable-2.13", - "python-version": "3.8" - }, - { - "ansible-version": "stable-2.14", - "python-version": "3.7" - }, - { - "ansible-version": "stable-2.14", - "python-version": "3.8" - }, - { - "ansible-version": "stable-2.15", - "python-version": "3.7" - }, - { - "ansible-version": "stable-2.15", - "python-version": "3.8" - }, - { - "ansible-version": "milestone", - "python-version": "3.7" - }, - { - "ansible-version": "milestone", - "python-version": "3.8" - }, - { - "ansible-version": "devel", - "python-version": "3.7" - }, - { - "ansible-version": "devel", - "python-version": "3.8" - }, - { - "ansible-version": "devel", - "python-version": "3.9" - } - ] - collection_pre_install: '' diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 79caa61e435..fdd8c166857 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,40 @@ amazon.aws Release Notes .. contents:: Topics +v6.2.0 +====== + +Release Summary +--------------- + +This release brings some new modules, features, and several bugfixes. + +Minor Changes +------------- + +- backup_selection - add validation and documentation for all conditions suboptions (https://github.com/ansible-collections/amazon.aws/pull/1633). +- ec2_instance - refactored ARN validation handling (https://github.com/ansible-collections/amazon.aws/pull/1619). +- iam_user - refactored ARN validation handling (https://github.com/ansible-collections/amazon.aws/pull/1619). +- module_utils.arn - add ``resource_id`` and ``resource_type`` to ``parse_aws_arn`` return values (https://github.com/ansible-collections/amazon.aws/pull/1619). +- module_utils.arn - added ``validate_aws_arn`` function to handle common pattern matching for ARNs (https://github.com/ansible-collections/amazon.aws/pull/1619). + +Bugfixes +-------- + +- cloudwatchevent_rule - Fixes changed status to report False when no change has been made. The module had incorrectly always reported a change. (https://github.com/ansible-collections/amazon.aws/pull/1589) +- lambda_execute - Fixes to the stack trace output, where it does not contain spaces between each character. The module had incorrectly always outputted extra spaces between each character. (https://github.com/ansible-collections/amazon.aws/pull/1615) +- backup_plan - Use existing ``scrub_none_values`` function from module_utils to remove None values from nested dicts in supplied params. Nested None values were being retained and causing an error when sent through to the boto3 client operation (https://github.com/ansible-collections/amazon.aws/pull/1611). +- backup_vault - fix error when updating tags on a backup vault by using the correct boto3 client methods for tagging and untagging backup resources (https://github.com/ansible-collections/amazon.aws/pull/1610). +- ec2_vpc_nat_gateway - adding a boolean parameter called ``default_create`` to allow users to have the option to choose whether they want to display an error message or create a NAT gateway when an EIP address is not found. The module (ec2_vpc_nat_gateway) had incorrectly failed silently if EIP didn't exist (https://github.com/ansible-collections/amazon.aws/issues/1295). +- ec2_vpc_nat_gateway - fixes to nat gateway so that when the user creates a private NAT gateway, an Elastic IP address should not be allocated. The module had inncorrectly always allocate elastic IP address when creating private nat gateway (https://github.com/ansible-collections/amazon.aws/pull/1632). +- module_utils.backup - get_selection_details fix empty list returned when multiple backup selections exist (https://github.com/ansible-collections/amazon.aws/pull/1633). + +New Modules +----------- + +- iam_instance_profile - manage IAM instance profiles +- iam_instance_profile_info - gather information on IAM instance profiles + v6.1.0 ====== diff --git a/PSF-license.txt b/PSF-license.txt deleted file mode 100644 index 35acd7fb5ff..00000000000 --- a/PSF-license.txt +++ /dev/null @@ -1,48 +0,0 @@ -PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 --------------------------------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using this software ("Python") in source or binary form and -its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby -grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, -analyze, test, perform and/or display publicly, prepare derivative works, -distribute, and otherwise use Python alone or in any derivative version, -provided, however, that PSF's License Agreement and PSF's notice of copyright, -i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation; -All Rights Reserved" are retained in Python alone or in any derivative version -prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python. - -4. PSF is making Python available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 3686ed2a1b6..5fdc9b178b7 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -2174,3 +2174,60 @@ releases: - release_summary.yml - test-reqs.yml release_date: '2023-06-07' + 6.2.0: + changes: + bugfixes: + - cloudwatchevent_rule - Fixes changed status to report False when no change has been made. The module + had incorrectly always reported a change. (https://github.com/ansible-collections/amazon.aws/pull/1589) + - lambda_execute - Fixes to the stack trace output, where it does not contain spaces between + each character. The module had incorrectly always outputted extra spaces between + each character. (https://github.com/ansible-collections/amazon.aws/pull/1615) + - backup_plan - Use existing ``scrub_none_values`` function from module_utils + to remove None values from nested dicts in supplied params. Nested None values + were being retained and causing an error when sent through to the boto3 client + operation (https://github.com/ansible-collections/amazon.aws/pull/1611). + - backup_vault - fix error when updating tags on a backup vault by using the + correct boto3 client methods for tagging and untagging backup resources (https://github.com/ansible-collections/amazon.aws/pull/1610). + - ec2_vpc_nat_gateway - adding a boolean parameter called ``default_create`` + to allow users to have the option to choose whether they want to display an + error message or create a NAT gateway when an EIP address is not found. The + module (ec2_vpc_nat_gateway) had incorrectly failed silently if EIP didn't + exist (https://github.com/ansible-collections/amazon.aws/issues/1295). + - ec2_vpc_nat_gateway - fixes to nat gateway so that when the user creates a + private NAT gateway, an Elastic IP address should not be allocated. The module + had inncorrectly always allocate elastic IP address when creating private + nat gateway (https://github.com/ansible-collections/amazon.aws/pull/1632). + - module_utils.backup - get_selection_details fix empty list returned when multiple + backup selections exist (https://github.com/ansible-collections/amazon.aws/pull/1633). + minor_changes: + - backup_selection - add validation and documentation for all conditions suboptions + (https://github.com/ansible-collections/amazon.aws/pull/1633). + - ec2_instance - refactored ARN validation handling (https://github.com/ansible-collections/amazon.aws/pull/1619). + - iam_user - refactored ARN validation handling (https://github.com/ansible-collections/amazon.aws/pull/1619). + - module_utils.arn - add ``resource_id`` and ``resource_type`` to ``parse_aws_arn`` + return values (https://github.com/ansible-collections/amazon.aws/pull/1619). + - module_utils.arn - added ``validate_aws_arn`` function to handle common pattern + matching for ARNs (https://github.com/ansible-collections/amazon.aws/pull/1619). + release_summary: This release brings some new modules, features, and several + bugfixes. + fragments: + - 1589-return_false_when_no_change..yml + - 1604-c2_vpc_nat_gateway-fails-silently.yml + - 1615-no_formatted_with_extra_space.yml + - 1632-changes-to-no-allocate-eip-when-connectivity_type=private.yml + - 1633-backup-selection-conditions.yml + - 1843-iam_instance_profile.yml + - 1846-arn-validation.yml + - 20230506-autoscaling_group-fix_sanity.yml + - 202306012-backup_plan-remove-none-from-nested-params.yml + - 20230612-backup_vault-fix-tag-update.yml + - 20230627-ci-fixup.yml + - release_summary.yml + modules: + - description: manage IAM instance profiles + name: iam_instance_profile + namespace: '' + - description: gather information on IAM instance profiles + name: iam_instance_profile_info + namespace: '' + release_date: '2023-07-05' diff --git a/changelogs/fragments/1587-LooseVersion.yml b/changelogs/fragments/1587-LooseVersion.yml new file mode 100644 index 00000000000..4e334cc0bb2 --- /dev/null +++ b/changelogs/fragments/1587-LooseVersion.yml @@ -0,0 +1,4 @@ +breaking_changes: +- module_utils._version - vendored copy of distutils.version has been dropped (https://github.com/ansible-collections/amazon.aws/pull/1587). +minor_changes: +- module_utils.botocore - migrate from vendored copy of LooseVersion to packaging.version.Version (https://github.com/ansible-collections/amazon.aws/pull/1587). diff --git a/changelogs/fragments/1589-return_false_when_no_change..yml b/changelogs/fragments/1589-return_false_when_no_change..yml deleted file mode 100644 index 5b5367c5f63..00000000000 --- a/changelogs/fragments/1589-return_false_when_no_change..yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: -- Fixes changed status to report False when no change has been made. The module had incorrectly always reported a change. (https://github.com/ansible-collections/amazon.aws/pull/1589) \ No newline at end of file diff --git a/changelogs/fragments/1604-c2_vpc_nat_gateway-fails-silently.yml b/changelogs/fragments/1604-c2_vpc_nat_gateway-fails-silently.yml deleted file mode 100644 index e72d9944607..00000000000 --- a/changelogs/fragments/1604-c2_vpc_nat_gateway-fails-silently.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: -- ec2_vpc_nat_gateway - adding a boolean parameter called ``default_create`` to allow users to have the option to choose whether they want to display an error message or create a NAT gateway when an EIP address is not found. The module (ec2_vpc_nat_gateway) had incorrectly failed silently if EIP didn't exist (https://github.com/ansible-collections/amazon.aws/issues/1295). \ No newline at end of file diff --git a/changelogs/fragments/1615-no_formatted_with_extra_space.yml b/changelogs/fragments/1615-no_formatted_with_extra_space.yml deleted file mode 100644 index 693362b6950..00000000000 --- a/changelogs/fragments/1615-no_formatted_with_extra_space.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: -- Fixes to the stack trace output, where it does not contain spaces between each character. The module had incorrectly always outputted extra spaces between each character. (https://github.com/ansible-collections/amazon.aws/pull/1615) \ No newline at end of file diff --git a/changelogs/fragments/1631-route53_health_check-added-calculcated.yml b/changelogs/fragments/1631-route53_health_check-added-calculcated.yml new file mode 100644 index 00000000000..ca1cc62dcb8 --- /dev/null +++ b/changelogs/fragments/1631-route53_health_check-added-calculcated.yml @@ -0,0 +1,5 @@ +--- +minor_changes: + - route53_health_check - add support for another ``type`` choice called ``CALCULATED`` (https://github.com/ansible-collections/amazon.aws/pull/1631). + - route53_health_check - add support for a string list parameter called ``child_health_checks`` to specify health checks that must be healthy for the calculated health check (https://github.com/ansible-collections/amazon.aws/pull/1631). + - route53_health_check - add support for an integer parameter called ``health_threshold`` to specify the minimum number of healthy child health checks that must be healthy for the calculated health check (https://github.com/ansible-collections/amazon.aws/pull/1631). \ No newline at end of file diff --git a/changelogs/fragments/1632-changes-to-no-allocate-eip-when-connectivity_type=private.yml b/changelogs/fragments/1632-changes-to-no-allocate-eip-when-connectivity_type=private.yml deleted file mode 100644 index 4b820a3498f..00000000000 --- a/changelogs/fragments/1632-changes-to-no-allocate-eip-when-connectivity_type=private.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: -- ec2_vpc_nat_gateway - fixes to nat gateway so that when the user creates a private NAT gateway, an Elastic IP address should not be allocated. The module had inncorrectly always allocate elastic IP address when creating private nat gateway (https://github.com/ansible-collections/amazon.aws/pull/1632). \ No newline at end of file diff --git a/changelogs/fragments/1633-backup-selection-conditions.yml b/changelogs/fragments/1633-backup-selection-conditions.yml deleted file mode 100644 index 8fac9ef4ce3..00000000000 --- a/changelogs/fragments/1633-backup-selection-conditions.yml +++ /dev/null @@ -1,5 +0,0 @@ -minor_changes: -- backup_selection - add validation and documentation for all conditions suboptions (https://github.com/ansible-collections/amazon.aws/pull/1633). - -bugfixes: -- module_utils.backup - get_selection_details fix empty list returned when multiple backup selections exist (https://github.com/ansible-collections/amazon.aws/pull/1633). diff --git a/changelogs/fragments/1843-iam_instance_profile.yml b/changelogs/fragments/1843-iam_instance_profile.yml deleted file mode 100644 index 3cfb18270d4..00000000000 --- a/changelogs/fragments/1843-iam_instance_profile.yml +++ /dev/null @@ -1,2 +0,0 @@ -trivial: -- new modules - iam_instance_profile, iam_instance_profile_info diff --git a/changelogs/fragments/1846-arn-validation.yml b/changelogs/fragments/1846-arn-validation.yml deleted file mode 100644 index 56be4417e26..00000000000 --- a/changelogs/fragments/1846-arn-validation.yml +++ /dev/null @@ -1,5 +0,0 @@ -minor_changes: -- ec2_instance - refactored ARN validation handling (https://github.com/ansible-collections/amazon.aws/pull/1619). -- iam_user - refactored ARN validation handling (https://github.com/ansible-collections/amazon.aws/pull/1619). -- module_utils.arn - added ``validate_aws_arn`` function to handle common pattern matching for ARNs (https://github.com/ansible-collections/amazon.aws/pull/1619). -- module_utils.arn - add ``resource_id`` and ``resource_type`` to ``parse_aws_arn`` return values (https://github.com/ansible-collections/amazon.aws/pull/1619). diff --git a/changelogs/fragments/20230506-autoscaling_group-fix_sanity.yml b/changelogs/fragments/20230506-autoscaling_group-fix_sanity.yml deleted file mode 100644 index 4a51fd47532..00000000000 --- a/changelogs/fragments/20230506-autoscaling_group-fix_sanity.yml +++ /dev/null @@ -1,2 +0,0 @@ -trivial: - - "Fix sanity errors autoscaling_group and autoscaling_group_info." diff --git a/changelogs/fragments/202306012-backup_plan-remove-none-from-nested-params.yml b/changelogs/fragments/202306012-backup_plan-remove-none-from-nested-params.yml deleted file mode 100644 index d244b1ee3de..00000000000 --- a/changelogs/fragments/202306012-backup_plan-remove-none-from-nested-params.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - backup_plan - Use existing `scrub_none_values` function from module_utils to remove None values from nested dicts in supplied params. Nested None values were being retained and causing an error when sent through to the boto3 client operation (https://github.com/ansible-collections/amazon.aws/pull/1611). diff --git a/changelogs/fragments/20230612-backup_vault-fix-tag-update.yml b/changelogs/fragments/20230612-backup_vault-fix-tag-update.yml deleted file mode 100644 index 7ed9c45ca0b..00000000000 --- a/changelogs/fragments/20230612-backup_vault-fix-tag-update.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -bugfixes: - - backup_vault - fix error when updating tags on a backup vault by using the correct boto3 client methods for tagging and untagging backup resources (https://github.com/ansible-collections/amazon.aws/pull/1610). diff --git a/changelogs/fragments/20230627-ci-fixup.yml b/changelogs/fragments/20230627-ci-fixup.yml deleted file mode 100644 index 160eb26b459..00000000000 --- a/changelogs/fragments/20230627-ci-fixup.yml +++ /dev/null @@ -1,3 +0,0 @@ -trivial: -- CI fixup - ``include:`` should be ``include_tasks:``. -- CI fixup - YAML ``Null`` no longer matches any explicit parameter type requirements. diff --git a/changelogs/fragments/20230713-rds_cluster-fix_params_passage.yml b/changelogs/fragments/20230713-rds_cluster-fix_params_passage.yml new file mode 100644 index 00000000000..ddac3a69de2 --- /dev/null +++ b/changelogs/fragments/20230713-rds_cluster-fix_params_passage.yml @@ -0,0 +1,2 @@ +bugfixes: + - rds_cluster - Add ``AllocatedStorage``, ``DBClusterInstanceClass``, ``StorageType``, ``Iops``, and ``EngineMode`` to the list of parameters that can be passed when creating or modifying a Multi-AZ RDS cluster (https://github.com/ansible-collections/amazon.aws/pull/1657). diff --git a/changelogs/fragments/20230725-rds_cluster-fix.yml b/changelogs/fragments/20230725-rds_cluster-fix.yml new file mode 100644 index 00000000000..9070a63cdb6 --- /dev/null +++ b/changelogs/fragments/20230725-rds_cluster-fix.yml @@ -0,0 +1,2 @@ +bugfixes: + - rds_cluster - Allow to pass GlobalClusterIdentifier to rds cluster on creation (https://github.com/ansible-collections/amazon.aws/pull/1663)." diff --git a/changelogs/fragments/ec2_vpc_route_table_info-filter-fix.yml b/changelogs/fragments/ec2_vpc_route_table_info-filter-fix.yml new file mode 100644 index 00000000000..9b3d315dcca --- /dev/null +++ b/changelogs/fragments/ec2_vpc_route_table_info-filter-fix.yml @@ -0,0 +1,2 @@ +bugfixes: + - ec2_vpc_route_table_info - default filters to empty dictionary (https://github.com/ansible-collections/amazon.aws/issues/1668). diff --git a/plugins/module_utils/_version.py b/plugins/module_utils/_version.py deleted file mode 100644 index d91cf3ab447..00000000000 --- a/plugins/module_utils/_version.py +++ /dev/null @@ -1,344 +0,0 @@ -# Vendored copy of distutils/version.py from CPython 3.9.5 -# -# Implements multiple version numbering conventions for the -# Python Module Distribution Utilities. -# -# PSF License (see PSF-license.txt or https://opensource.org/licenses/Python-2.0) -# - -"""Provides classes to represent module version numbers (one class for -each style of version numbering). There are currently two such classes -implemented: StrictVersion and LooseVersion. - -Every version number class implements the following interface: - * the 'parse' method takes a string and parses it to some internal - representation; if the string is an invalid version number, - 'parse' raises a ValueError exception - * the class constructor takes an optional string argument which, - if supplied, is passed to 'parse' - * __str__ reconstructs the string that was passed to 'parse' (or - an equivalent string -- ie. one that will generate an equivalent - version number instance) - * __repr__ generates Python code to recreate the version number instance - * _cmp compares the current instance with either another instance - of the same class or a string (which will be parsed to an instance - of the same class, thus must follow the same rules) -""" - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -import re - -try: - RE_FLAGS = re.VERBOSE | re.ASCII -except AttributeError: - RE_FLAGS = re.VERBOSE - - -class Version: - """Abstract base class for version numbering classes. Just provides - constructor (__init__) and reproducer (__repr__), because those - seem to be the same for all version numbering classes; and route - rich comparisons to _cmp. - """ - - def __init__(self, vstring=None): - if vstring: - self.parse(vstring) - - def __repr__(self): - return "%s ('%s')" % (self.__class__.__name__, str(self)) - - def __eq__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c == 0 - - def __lt__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c < 0 - - def __le__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c <= 0 - - def __gt__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c > 0 - - def __ge__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c >= 0 - - -# Interface for version-number classes -- must be implemented -# by the following classes (the concrete ones -- Version should -# be treated as an abstract class). -# __init__ (string) - create and take same action as 'parse' -# (string parameter is optional) -# parse (string) - convert a string representation to whatever -# internal representation is appropriate for -# this style of version numbering -# __str__ (self) - convert back to a string; should be very similar -# (if not identical to) the string supplied to parse -# __repr__ (self) - generate Python code to recreate -# the instance -# _cmp (self, other) - compare two version numbers ('other' may -# be an unparsed version string, or another -# instance of your version class) - - -class StrictVersion(Version): - """Version numbering for anal retentives and software idealists. - Implements the standard interface for version number classes as - described above. A version number consists of two or three - dot-separated numeric components, with an optional "pre-release" tag - on the end. The pre-release tag consists of the letter 'a' or 'b' - followed by a number. If the numeric components of two version - numbers are equal, then one with a pre-release tag will always - be deemed earlier (lesser) than one without. - - The following are valid version numbers (shown in the order that - would be obtained by sorting according to the supplied cmp function): - - 0.4 0.4.0 (these two are equivalent) - 0.4.1 - 0.5a1 - 0.5b3 - 0.5 - 0.9.6 - 1.0 - 1.0.4a3 - 1.0.4b1 - 1.0.4 - - The following are examples of invalid version numbers: - - 1 - 2.7.2.2 - 1.3.a4 - 1.3pl1 - 1.3c4 - - The rationale for this version numbering system will be explained - in the distutils documentation. - """ - - version_re = re.compile(r"^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$", RE_FLAGS) - - def parse(self, vstring): - match = self.version_re.match(vstring) - if not match: - raise ValueError("invalid version number '%s'" % vstring) - - (major, minor, patch, prerelease, prerelease_num) = match.group(1, 2, 4, 5, 6) - - if patch: - self.version = tuple(map(int, [major, minor, patch])) - else: - self.version = tuple(map(int, [major, minor])) + (0,) - - if prerelease: - self.prerelease = (prerelease[0], int(prerelease_num)) - else: - self.prerelease = None - - def __str__(self): - if self.version[2] == 0: - vstring = ".".join(map(str, self.version[0:2])) - else: - vstring = ".".join(map(str, self.version)) - - if self.prerelease: - vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) - - return vstring - - def _cmp(self, other): - if isinstance(other, str): - other = StrictVersion(other) - elif not isinstance(other, StrictVersion): - return NotImplemented - - if self.version != other.version: - # numeric versions don't match - # prerelease stuff doesn't matter - if self.version < other.version: - return -1 - else: - return 1 - - # have to compare prerelease - # case 1: neither has prerelease; they're equal - # case 2: self has prerelease, other doesn't; other is greater - # case 3: self doesn't have prerelease, other does: self is greater - # case 4: both have prerelease: must compare them! - - if not self.prerelease and not other.prerelease: - return 0 - elif self.prerelease and not other.prerelease: - return -1 - elif not self.prerelease and other.prerelease: - return 1 - elif self.prerelease and other.prerelease: - if self.prerelease == other.prerelease: - return 0 - elif self.prerelease < other.prerelease: - return -1 - else: - return 1 - else: - raise AssertionError("never get here") - - -# end class StrictVersion - -# The rules according to Greg Stein: -# 1) a version number has 1 or more numbers separated by a period or by -# sequences of letters. If only periods, then these are compared -# left-to-right to determine an ordering. -# 2) sequences of letters are part of the tuple for comparison and are -# compared lexicographically -# 3) recognize the numeric components may have leading zeroes -# -# The LooseVersion class below implements these rules: a version number -# string is split up into a tuple of integer and string components, and -# comparison is a simple tuple comparison. This means that version -# numbers behave in a predictable and obvious way, but a way that might -# not necessarily be how people *want* version numbers to behave. There -# wouldn't be a problem if people could stick to purely numeric version -# numbers: just split on period and compare the numbers as tuples. -# However, people insist on putting letters into their version numbers; -# the most common purpose seems to be: -# - indicating a "pre-release" version -# ('alpha', 'beta', 'a', 'b', 'pre', 'p') -# - indicating a post-release patch ('p', 'pl', 'patch') -# but of course this can't cover all version number schemes, and there's -# no way to know what a programmer means without asking him. -# -# The problem is what to do with letters (and other non-numeric -# characters) in a version number. The current implementation does the -# obvious and predictable thing: keep them as strings and compare -# lexically within a tuple comparison. This has the desired effect if -# an appended letter sequence implies something "post-release": -# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". -# -# However, if letters in a version number imply a pre-release version, -# the "obvious" thing isn't correct. Eg. you would expect that -# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison -# implemented here, this just isn't so. -# -# Two possible solutions come to mind. The first is to tie the -# comparison algorithm to a particular set of semantic rules, as has -# been done in the StrictVersion class above. This works great as long -# as everyone can go along with bondage and discipline. Hopefully a -# (large) subset of Python module programmers will agree that the -# particular flavour of bondage and discipline provided by StrictVersion -# provides enough benefit to be worth using, and will submit their -# version numbering scheme to its domination. The free-thinking -# anarchists in the lot will never give in, though, and something needs -# to be done to accommodate them. -# -# Perhaps a "moderately strict" version class could be implemented that -# lets almost anything slide (syntactically), and makes some heuristic -# assumptions about non-digits in version number strings. This could -# sink into special-case-hell, though; if I was as talented and -# idiosyncratic as Larry Wall, I'd go ahead and implement a class that -# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is -# just as happy dealing with things like "2g6" and "1.13++". I don't -# think I'm smart enough to do it right though. -# -# In any case, I've coded the test suite for this module (see -# ../test/test_version.py) specifically to fail on things like comparing -# "1.2a2" and "1.2". That's not because the *code* is doing anything -# wrong, it's because the simple, obvious design doesn't match my -# complicated, hairy expectations for real-world version numbers. It -# would be a snap to fix the test suite to say, "Yep, LooseVersion does -# the Right Thing" (ie. the code matches the conception). But I'd rather -# have a conception that matches common notions about version numbers. - - -class LooseVersion(Version): - """Version numbering for anarchists and software realists. - Implements the standard interface for version number classes as - described above. A version number consists of a series of numbers, - separated by either periods or strings of letters. When comparing - version numbers, the numeric components will be compared - numerically, and the alphabetic components lexically. The following - are all valid version numbers, in no particular order: - - 1.5.1 - 1.5.2b2 - 161 - 3.10a - 8.02 - 3.4j - 1996.07.12 - 3.2.pl0 - 3.1.1.6 - 2g6 - 11g - 0.960923 - 2.2beta29 - 1.13++ - 5.5.kw - 2.0b1pl0 - - In fact, there is no such thing as an invalid version number under - this scheme; the rules for comparison are simple and predictable, - but may not always give the results you want (for some definition - of "want"). - """ - - component_re = re.compile(r"(\d+ | [a-z]+ | \.)", re.VERBOSE) - - def __init__(self, vstring=None): - if vstring: - self.parse(vstring) - - def parse(self, vstring): - # I've given up on thinking I can reconstruct the version string - # from the parsed tuple -- so I just store the string here for - # use by __str__ - self.vstring = vstring - components = [x for x in self.component_re.split(vstring) if x and x != "."] - for i, obj in enumerate(components): - try: - components[i] = int(obj) - except ValueError: - pass - - self.version = components - - def __str__(self): - return self.vstring - - def __repr__(self): - return "LooseVersion ('%s')" % str(self) - - def _cmp(self, other): - if isinstance(other, str): - other = LooseVersion(other) - elif not isinstance(other, LooseVersion): - return NotImplemented - - if self.version == other.version: - return 0 - if self.version < other.version: - return -1 - if self.version > other.version: - return 1 - - -# end class LooseVersion diff --git a/plugins/module_utils/botocore.py b/plugins/module_utils/botocore.py index 8f6296a3a5b..c04b27d53a9 100644 --- a/plugins/module_utils/botocore.py +++ b/plugins/module_utils/botocore.py @@ -47,6 +47,13 @@ BOTO3_IMP_ERR = traceback.format_exc() HAS_BOTO3 = False +try: + from packaging.version import Version + + HAS_PACKAGING = True +except ImportError: + HAS_PACKAGING = False + from ansible.module_utils._text import to_native from ansible.module_utils.ansible_release import __version__ from ansible.module_utils.basic import missing_required_lib @@ -55,7 +62,6 @@ from .exceptions import AnsibleBotocoreError from .retries import AWSRetry -from .version import LooseVersion from .common import get_collection_info MINIMUM_BOTOCORE_VERSION = "1.25.0" @@ -444,6 +450,10 @@ def check_sdk_version_supported(botocore_version=None, boto3_version=None, warn= supported = True + if not HAS_PACKAGING: + if warn: + warn("packaging.version Python module not installed, unable to check AWS SDK versions") + return True if not botocore_at_least(botocore_version): supported = False if warn: @@ -456,6 +466,12 @@ def check_sdk_version_supported(botocore_version=None, boto3_version=None, warn= return supported +def _version_at_least(a, b): + if not HAS_PACKAGING: + return True + return Version(a) >= Version(b) + + def boto3_at_least(desired): """Check if the available boto3 version is greater than or equal to a desired version. @@ -465,7 +481,7 @@ def boto3_at_least(desired): module.fail_json(msg="Boto3 can't deal with EC2 IPv6 addresses before version 1.4.4.") """ existing = gather_sdk_versions() - return LooseVersion(existing["boto3_version"]) >= LooseVersion(desired) + return _version_at_least(existing["boto3_version"], desired) def botocore_at_least(desired): @@ -479,4 +495,4 @@ def botocore_at_least(desired): 'To wait until Service X resources are fully available, update botocore.') """ existing = gather_sdk_versions() - return LooseVersion(existing["botocore_version"]) >= LooseVersion(desired) + return _version_at_least(existing["botocore_version"], desired) diff --git a/plugins/module_utils/version.py b/plugins/module_utils/version.py index 51c77536aee..444bde5d6ee 100644 --- a/plugins/module_utils/version.py +++ b/plugins/module_utils/version.py @@ -5,9 +5,6 @@ """Provide version object to compare version numbers.""" -# Once we drop support for Ansible 2.9, ansible-base 2.10, and ansible-core 2.11, we can -# remove the _version.py file, and replace the following import by -# -# from ansible.module_utils.compat.version import LooseVersion - -from ._version import LooseVersion # pylint: disable=unused-import +# This should be directly imported by modules, rather than importing from here. +# The import is being kept for backwards compatibility. +from ansible.module_utils.compat.version import LooseVersion # pylint: disable=unused-import diff --git a/plugins/modules/ec2_vpc_route_table_info.py b/plugins/modules/ec2_vpc_route_table_info.py index 0e5bfb329c7..21c9fc6674a 100644 --- a/plugins/modules/ec2_vpc_route_table_info.py +++ b/plugins/modules/ec2_vpc_route_table_info.py @@ -20,6 +20,7 @@ - A dict of filters to apply. Each dict item consists of a filter key and a filter value. See U(https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeRouteTables.html) for possible filters. type: dict + default: {} extends_documentation_fragment: - amazon.aws.common.modules - amazon.aws.region.modules @@ -260,7 +261,7 @@ def list_ec2_vpc_route_tables(connection, module): def main(): argument_spec = dict( - filters=dict(default=None, type="dict"), + filters=dict(default={}, type="dict"), ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) diff --git a/plugins/modules/rds_cluster.py b/plugins/modules/rds_cluster.py index 3cc781a8bb2..7acf6717f56 100644 --- a/plugins/modules/rds_cluster.py +++ b/plugins/modules/rds_cluster.py @@ -770,6 +770,12 @@ def get_create_options(params_dict): "Domain", "DomainIAMRoleName", "EnableGlobalWriteForwarding", + "GlobalClusterIdentifier", + "AllocatedStorage", + "DBClusterInstanceClass", + "StorageType", + "Iops", + "EngineMode", ] return dict((k, v) for k, v in params_dict.items() if k in options and v is not None) @@ -799,6 +805,11 @@ def get_modify_options(params_dict, force_update_password): "EnableGlobalWriteForwarding", "Domain", "DomainIAMRoleName", + "AllocatedStorage", + "DBClusterInstanceClass", + "StorageType", + "Iops", + "EngineMode", ] 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: @@ -1028,6 +1039,7 @@ def ensure_present(cluster, parameters, method_name, method_options_name): 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: @@ -1214,7 +1226,6 @@ def main(): ) 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) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 99efc0143a0..b9325cae418 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -45,8 +45,22 @@ - The type of health check that you want to create, which indicates how Amazon Route 53 determines whether an endpoint is healthy. - Once health_check is created, type can not be changed. - choices: [ 'HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP' ] + - The CALCULATED choice was added in 6.3.0. + choices: [ 'HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP', 'CALCULATED' ] type: str + child_health_checks: + description: + - The child health checks used for a calculated health check. + - This parameter takes in the child health checks ids. + type: list + elements: str + version_added: 6.3.0 + health_threshold: + description: + - The minimum number of healthy child health checks for a calculated health check to be considered healthy. + default: 1 + type: int + version_added: 6.3.0 resource_path: description: - The path that you want Amazon Route 53 to request when performing @@ -373,7 +387,9 @@ def delete_health_check(check_id): return True, "delete" -def create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in): +def create_health_check( + ip_addr_in, fqdn_in, type_in, request_interval_in, port_in, child_health_checks_in, health_threshold_in +): # In general, if a request is repeated with the same CallerRef it won't # result in a duplicate check appearing. This means we can safely use our # retry decorators @@ -382,8 +398,6 @@ def create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_ health_check = dict( Type=type_in, - RequestInterval=request_interval_in, - Port=port_in, ) if module.params.get("disabled") is not None: health_check["Disabled"] = module.params.get("disabled") @@ -391,6 +405,8 @@ def create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_ health_check["IPAddress"] = ip_addr_in if fqdn_in: health_check["FullyQualifiedDomainName"] = fqdn_in + if port_in: + health_check["Port"] = port_in if type_in in ["HTTP", "HTTPS", "HTTP_STR_MATCH", "HTTPS_STR_MATCH"]: resource_path = module.params.get("resource_path") @@ -404,10 +420,19 @@ def create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_ missing_args.append("string_match") health_check["SearchString"] = module.params.get("string_match") - failure_threshold = module.params.get("failure_threshold") - if not failure_threshold: - failure_threshold = 3 - health_check["FailureThreshold"] = failure_threshold + if type_in == "CALCULATED": + if not child_health_checks_in: + missing_args.append("child_health_checks") + if not health_threshold_in: + missing_args.append("health_threshold") + health_check["ChildHealthChecks"] = child_health_checks_in + health_check["HealthThreshold"] = health_threshold_in + else: + failure_threshold = module.params.get("failure_threshold") + if not failure_threshold: + failure_threshold = 3 + health_check["FailureThreshold"] = failure_threshold + health_check["RequestInterval"] = request_interval_in if module.params.get("measure_latency") is not None: health_check["MeasureLatency"] = module.params.get("measure_latency") @@ -442,6 +467,8 @@ def update_health_check(existing_check): # - IPAddress # - Port # - FullyQualifiedDomainName + # - ChildHealthChecks + # - HealthThreshold changes = dict() existing_config = existing_check.get("HealthCheckConfig") @@ -455,9 +482,11 @@ def update_health_check(existing_check): if search_string and search_string != existing_config.get("SearchString"): changes["SearchString"] = search_string - failure_threshold = module.params.get("failure_threshold", None) - if failure_threshold and failure_threshold != existing_config.get("FailureThreshold"): - changes["FailureThreshold"] = failure_threshold + type_in = module.params.get("type", None) + if type_in != "CALCULATED": + failure_threshold = module.params.get("failure_threshold", None) + if failure_threshold and failure_threshold != existing_config.get("FailureThreshold"): + changes["FailureThreshold"] = failure_threshold disabled = module.params.get("disabled", None) if disabled is not None and disabled != existing_config.get("Disabled"): @@ -477,6 +506,15 @@ def update_health_check(existing_check): if fqdn is not None and fqdn != existing_config.get("FullyQualifiedDomainName"): changes["FullyQualifiedDomainName"] = module.params.get("fqdn") + if type_in == "CALCULATED": + child_health_checks = module.params.get("child_health_checks", None) + if child_health_checks is not None and child_health_checks != existing_config.get("ChildHealthChecks"): + changes["ChildHealthChecks"] = module.params.get("child_health_checks") + + health_threshold = module.params.get("health_threshold", None) + if health_threshold is not None and health_threshold != existing_config.get("HealthThreshold"): + changes["HealthThreshold"] = module.params.get("health_threshold") + # No changes... if not changes: return False, None, check_id @@ -522,7 +560,9 @@ def main(): disabled=dict(type="bool"), ip_address=dict(), port=dict(type="int"), - type=dict(choices=["HTTP", "HTTPS", "HTTP_STR_MATCH", "HTTPS_STR_MATCH", "TCP"]), + type=dict(choices=["HTTP", "HTTPS", "HTTP_STR_MATCH", "HTTPS_STR_MATCH", "TCP", "CALCULATED"]), + child_health_checks=dict(type="list", elements="str"), + health_threshold=dict(type="int", default=1), resource_path=dict(), fqdn=dict(), string_match=dict(), @@ -537,11 +577,12 @@ def main(): ) args_one_of = [ - ["ip_address", "fqdn", "health_check_id"], + ["ip_address", "fqdn", "health_check_id", "child_health_checks"], ] args_if = [ ["type", "TCP", ("port",)], + ["type", "CALCULATED", ("child_health_checks", "health_threshold")], ] args_required_together = [ @@ -550,6 +591,9 @@ def main(): args_mutually_exclusive = [ ["health_check_id", "health_check_name"], + ["child_health_checks", "ip_address"], + ["child_health_checks", "port"], + ["child_health_checks", "fqdn"], ] global module @@ -577,6 +621,8 @@ def main(): health_check_name = module.params.get("health_check_name") tags = module.params.get("tags") purge_tags = module.params.get("purge_tags") + child_health_checks_in = module.params.get("child_health_checks") + health_threshold_in = module.params.get("health_threshold") # Default port if port_in is None: @@ -624,7 +670,9 @@ def main(): # Create Health Check elif state_in == "present": if existing_check is None and not module.params.get("use_unique_names") and not update_delete_by_id: - changed, action, check_id = create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in) + changed, action, check_id = create_health_check( + ip_addr_in, fqdn_in, type_in, request_interval_in, port_in, child_health_checks_in, health_threshold_in + ) # Update Health Check else: @@ -642,7 +690,13 @@ def main(): else: # create a new health_check if another health check with same name does not exists changed, action, check_id = create_health_check( - ip_addr_in, fqdn_in, type_in, request_interval_in, port_in + ip_addr_in, + fqdn_in, + type_in, + request_interval_in, + port_in, + child_health_checks_in, + health_threshold_in, ) else: diff --git a/tests/integration/targets/route53_health_check/tasks/calculate_health_check.yml b/tests/integration/targets/route53_health_check/tasks/calculate_health_check.yml new file mode 100644 index 00000000000..99a27dd232b --- /dev/null +++ b/tests/integration/targets/route53_health_check/tasks/calculate_health_check.yml @@ -0,0 +1,156 @@ +--- +- block: + # Create Health Check ================================================================= + - name: 'Create Health Check with name' + amazon.aws.route53_health_check: + state: present + name: '{{ tiny_prefix }}-{{ resource_path }}-test-hc-tag-operations' + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http }}' + resource_path: '{{ resource_path }}' + use_unique_names: true + fqdn: '{{ fqdn }}' + register: create_result + + # Create and Update =================================================================== + - name: 'Create Invalid Parameter Calculated Health Check' + route53_health_check: + health_check_name: "calculated_health_check" + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: CALCULATED + use_unique_names: true + fqdn: '{{ fqdn }}' + health_threshold: 1 + child_health_checks: '{{ create_result.health_check.id }}' + ignore_errors: true + register: error_create_calculated + + - name: 'Check result - Create Invalid Parameter Calculated Health Check' + ansible.builtin.assert: + that: + - error_create_calculated is failed + - "error_create_calculated.msg == 'parameters are mutually exclusive: child_health_checks|ip_address, child_health_checks|port, child_health_checks|fqdn'" + + - name: 'Create Calculated Health Check - check_mode' + route53_health_check: + health_check_name: "calculated_health_check" + use_unique_names: true + type: CALCULATED + health_threshold: 1 + child_health_checks: '{{ create_result.health_check.id }}' + register: check_create_calculated + check_mode: true + + - name: 'Check result - Calculated Health Check' + ansible.builtin.assert: + that: + - check_create_calculated is not failed + - check_create_calculated is changed + + - name: 'Create Calculated Health Check' + route53_health_check: + health_check_name: "calculated_health_check" + use_unique_names: true + type: CALCULATED + health_threshold: 1 + child_health_checks: '{{ create_result.health_check.id }}' + register: create_calculated + + - name: 'Check result - Calculated Health Check' + ansible.builtin.assert: + that: + - create_calculated is not failed + - create_calculated is changed + + - name: 'Check result - Create Calculated Health Check - idempotency' + route53_health_check: + health_check_name: "calculated_health_check" + use_unique_names: true + type: CALCULATED + health_threshold: 1 + child_health_checks: '{{ create_result.health_check.id }}' + register: create_idem + + - name: 'Check result - Calculated Health Check - idempotency' + ansible.builtin.assert: + that: + - create_idem is not failed + - create_idem is not changed + + - name: 'Update Calculated Health Check' + route53_health_check: + health_check_name: "calculated_health_check" + use_unique_names: true + type: CALCULATED + health_threshold: 2 + child_health_checks: '{{ create_result.health_check.id }}' + register: check_updated_calculated + check_mode: true + + - name: 'Check result - Update Calculated Health Check - check_mode' + ansible.builtin.assert: + that: + - check_updated_calculated is not failed + - check_updated_calculated is changed + + - name: 'Update Calculated Health Check' + route53_health_check: + health_check_name: "calculated_health_check" + use_unique_names: true + type: CALCULATED + health_threshold: 2 + child_health_checks: '{{ create_result.health_check.id }}' + register: updated_calculated + + - name: 'Check result - Update Calculated Health Check' + ansible.builtin.assert: + that: + - updated_calculated is not failed + - updated_calculated is changed + + # Deleting Calculated Health Check ====================================================== + - name: 'Delete Calculated Health Check' + amazon.aws.route53_health_check: + state: absent + health_check_id: '{{ create_calculated.health_check.id }}' + register: deleted_calculated + + - name: 'Check if Calculated Health Check can be deleted' + ansible.builtin.assert: + that: + - deleted_calculated is not failed + - deleted_calculated is changed + + - name: 'Delete HTTP health check with use_unique_names' + amazon.aws.route53_health_check: + state: absent + name: '{{ tiny_prefix }}-{{ resource_path }}-test-hc-tag-operations' + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http }}' + resource_path: '{{ resource_path }}' + use_unique_names: true + fqdn: '{{ fqdn }}' + register: delete_result + + - name: 'Check if HTTP health check with use_unique_names can be deleted' + ansible.builtin.assert: + that: + - delete_result is changed + - delete_result is not failed + + always: + # Cleanup starts here ================================================================= + - name: 'Delete Calculated Health Check' + amazon.aws.route53_health_check: + state: absent + health_check_id: '{{ create_calculated.health_check.id }}' + ignore_errors: true + + - name: 'Delete HTTP health check with use_unique_names' + amazon.aws.route53_health_check: + state: absent + name: '{{ tiny_prefix }}-{{ resource_path }}-test-hc-tag-operations' + ignore_errors: true diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index 4a13464e343..4a1752d9713 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -41,6 +41,9 @@ - name: Run tests for update and delete health check by ID include_tasks: update_delete_by_id.yml + - name: Run tests for create, update, and delete calculated health check + include_tasks: calculate_health_check.yml + # Minimum possible definition - name: 'Create a TCP health check - check_mode' route53_health_check: