From b86b25195e11c1de7ca25e7888bf6b5d2733a30d Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 4 Jan 2023 11:24:39 +0100 Subject: [PATCH 1/4] Update imports --- plugins/modules/ec2_instance.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/modules/ec2_instance.py b/plugins/modules/ec2_instance.py index 2158bc8c39e..5308752f9e0 100644 --- a/plugins/modules/ec2_instance.py +++ b/plugins/modules/ec2_instance.py @@ -958,12 +958,12 @@ from ansible.module_utils.six import string_types from ansible.module_utils.six.moves.urllib import parse as urlparse -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.core import is_boto3_error_message -from ansible_collections.amazon.aws.plugins.module_utils.core import parse_aws_arn -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_filter_list +from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_message +from ansible_collections.amazon.aws.plugins.module_utils.arn import parse_aws_arn +from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.transformation import ansible_dict_to_boto3_filter_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ensure_ec2_tags from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_ec2_security_group_ids_from_names from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict From a624b684ab816ab3eab32cd1dd6bceae373cd674 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 4 Jan 2023 11:26:10 +0100 Subject: [PATCH 2/4] reorder imports --- plugins/modules/ec2_instance.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/modules/ec2_instance.py b/plugins/modules/ec2_instance.py index 5308752f9e0..7b107277c3f 100644 --- a/plugins/modules/ec2_instance.py +++ b/plugins/modules/ec2_instance.py @@ -958,17 +958,17 @@ from ansible.module_utils.six import string_types from ansible.module_utils.six.moves.urllib import parse as urlparse -from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.arn import parse_aws_arn from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_message -from ansible_collections.amazon.aws.plugins.module_utils.arn import parse_aws_arn -from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.transformation import ansible_dict_to_boto3_filter_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ensure_ec2_tags from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_ec2_security_group_ids_from_names +from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_specifications from ansible_collections.amazon.aws.plugins.module_utils.tower import tower_callback_script +from ansible_collections.amazon.aws.plugins.module_utils.transformation import ansible_dict_to_boto3_filter_list module = None From 18980c79e987851b0be6a22199e48aac85120387 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 4 Jan 2023 13:25:43 +0100 Subject: [PATCH 3/4] Drop deprecated functionality --- .../1315-ec2_instance-instance_type.yml | 4 ++ plugins/modules/ec2_instance.py | 69 +++++++++++-------- 2 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 changelogs/fragments/1315-ec2_instance-instance_type.yml diff --git a/changelogs/fragments/1315-ec2_instance-instance_type.yml b/changelogs/fragments/1315-ec2_instance-instance_type.yml new file mode 100644 index 00000000000..0df2346360c --- /dev/null +++ b/changelogs/fragments/1315-ec2_instance-instance_type.yml @@ -0,0 +1,4 @@ +breaking_changes: +- ec2_instance - the default value for ``instance_type`` has been removed. + At least one of ``instance_type`` or ``launch_template`` must be specified when launching new + instances (https://github.com/ansible-collections/amazon.aws/pull/1315). diff --git a/plugins/modules/ec2_instance.py b/plugins/modules/ec2_instance.py index 7b107277c3f..5db16f856fc 100644 --- a/plugins/modules/ec2_instance.py +++ b/plugins/modules/ec2_instance.py @@ -48,11 +48,11 @@ type: int instance_type: description: - - Instance type to use for the instance, see U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html). + - Instance type to use for the instance, see + U(https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html). - Only required when instance is not already present. - - If not specified, C(t2.micro) will be used. - - In a release after 2023-01-01 the default will be removed and either I(instance_type) or - I(launch_template) must be specificed when launching an instance. + - At least one of I(instance_type) or I(launch_template) must be specificed when launching an + instance. type: str count: description: @@ -223,6 +223,8 @@ launch_template: description: - The EC2 launch template to base instance configuration on. + - At least one of I(instance_type) or I(launch_template) must be specificed when launching an + instance. type: dict suboptions: id: @@ -963,6 +965,7 @@ from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_message from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ensure_ec2_tags from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_ec2_security_group_ids_from_names +from ansible_collections.amazon.aws.plugins.module_utils.exceptions import AnsibleAWSError from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.tagging import boto3_tag_list_to_ansible_dict @@ -973,6 +976,10 @@ module = None +class Ec2InstanceAWSError(AnsibleAWSError): + pass + + def build_volume_spec(params): volumes = params.get('volumes') or [] for volume in volumes: @@ -1342,11 +1349,13 @@ def build_run_instance_spec(params): spec['MaxCount'] = params.get('count') spec['MinCount'] = params.get('count') - if not params.get('launch_template'): - spec['InstanceType'] = params['instance_type'] if params.get('instance_type') else 't2.micro' + if params.get("instance_type"): + spec["InstanceType"] = params["instance_type"] - if params.get('launch_template') and params.get('instance_type'): - spec['InstanceType'] = params['instance_type'] + if not (params.get("instance_type") or params.get("launch_template")): + raise Ec2InstanceAWSError( + "At least one of 'instance_type' and 'launch_template' " + "must be passed when launching instances.") return spec @@ -2049,11 +2058,6 @@ def main(): supports_check_mode=True ) - if not module.params.get('instance_type') and not module.params.get('launch_template'): - if module.params.get('state') not in ('absent', 'stopped'): - if module.params.get('count') or module.params.get('exact_count'): - module.deprecate("Default value instance_type has been deprecated, in the future you must set an instance_type or a launch_template", - date='2023-01-01', collection_name='amazon.aws') result = dict() if module.params.get('network'): @@ -2077,25 +2081,30 @@ def main(): if filters is None: filters = build_filters() - existing_matches = find_instances(filters=filters) + try: + existing_matches = find_instances(filters=filters) - if state in ('terminated', 'absent'): - if existing_matches: - result = ensure_instance_state(state, filters) + if state in ('terminated', 'absent'): + if existing_matches: + result = ensure_instance_state(state, filters) + else: + result = dict( + msg='No matching instances found', + changed=False, + ) + elif module.params.get('exact_count'): + enforce_count(existing_matches, module, desired_module_state=state) + elif existing_matches and not module.params.get('count'): + for match in existing_matches: + warn_if_public_ip_assignment_changed(match) + warn_if_cpu_options_changed(match) + result = handle_existing(existing_matches, state, filters=filters) else: - result = dict( - msg='No matching instances found', - changed=False, - ) - elif module.params.get('exact_count'): - enforce_count(existing_matches, module, desired_module_state=state) - elif existing_matches and not module.params.get('count'): - for match in existing_matches: - warn_if_public_ip_assignment_changed(match) - warn_if_cpu_options_changed(match) - result = handle_existing(existing_matches, state, filters=filters) - else: - result = ensure_present(existing_matches=existing_matches, desired_module_state=state) + result = ensure_present(existing_matches=existing_matches, desired_module_state=state) + except Ec2InstanceAWSError as e: + if e.exception: + module.fail_json_aws(e.exception, msg=e.message) + module.fail_json(msg=e.message) module.exit_json(**result) From ec0ebe4dc8f6aaf003cdad32cedab72f97b449f5 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 5 Jan 2023 09:04:23 +0100 Subject: [PATCH 4/4] Update unit tests --- .../test_build_run_instance_spec.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/unit/plugins/modules/ec2_instance/test_build_run_instance_spec.py b/tests/unit/plugins/modules/ec2_instance/test_build_run_instance_spec.py index 5000587546b..8554e4f7bf7 100644 --- a/tests/unit/plugins/modules/ec2_instance/test_build_run_instance_spec.py +++ b/tests/unit/plugins/modules/ec2_instance/test_build_run_instance_spec.py @@ -16,7 +16,7 @@ def params_object(): 'exact_count': None, 'count': None, 'launch_template': None, - 'instance_type': None, + 'instance_type': sentinel.INSTANCE_TYPE } return params @@ -68,12 +68,30 @@ def _assert_defaults(instance_spec, to_skip=None): assert 'TOP_LEVEL_OPTIONS' in instance_spec assert instance_spec['TOP_LEVEL_OPTIONS'] is sentinel.TOP_LEVEL + if 'InstanceType' not in to_skip: + assert 'InstanceType' in instance_spec + instance_spec['InstanceType'] == sentinel.INSTANCE_TYPE + def test_build_run_instance_spec_defaults(params_object, ec2_instance): instance_spec = ec2_instance.build_run_instance_spec(params_object) _assert_defaults(instance_spec) +def test_build_run_instance_spec_type_required(params_object, ec2_instance): + params_object['instance_type'] = None + params_object['launch_template'] = None + # Test that we throw an Ec2InstanceAWSError if passed neither + with pytest.raises(ec2_instance.Ec2InstanceAWSError): + instance_spec = ec2_instance.build_run_instance_spec(params_object) + + # Test that instance_type can be None if launch_template is set + params_object['launch_template'] = sentinel.LAUNCH_TEMPLATE + instance_spec = ec2_instance.build_run_instance_spec(params_object) + _assert_defaults(instance_spec, ['InstanceType']) + assert 'InstanceType' not in instance_spec + + def test_build_run_instance_spec_tagging(params_object, ec2_instance, monkeypatch): # build_instance_tags can return None, RunInstance doesn't like this monkeypatch.setattr(ec2_instance_module, 'build_instance_tags', lambda params: None)