diff --git a/lib/ansible/modules/cloud/amazon/ecs_service.py b/lib/ansible/modules/cloud/amazon/ecs_service.py index 7fb0ea808a4..2fbee919172 100644 --- a/lib/ansible/modules/cloud/amazon/ecs_service.py +++ b/lib/ansible/modules/cloud/amazon/ecs_service.py @@ -82,6 +82,12 @@ options: - The number of times to check that the service is available required: false default: 10 + force_new_deployment: + description: + - Force deployment of service even if there are no changes + required: false + version_added: 2.8 + type: bool deployment_configuration: description: - Optional parameters that control the deployment_configuration; format is '{"maximum_percent":, "minimum_healthy_percent":} @@ -412,18 +418,20 @@ class EcsServiceManager: return self.jsonize(response['service']) def update_service(self, service_name, cluster_name, task_definition, - desired_count, deployment_configuration, network_configuration, health_check_grace_period_seconds): + desired_count, deployment_configuration, network_configuration, + health_check_grace_period_seconds, force_new_deployment): params = dict( cluster=cluster_name, service=service_name, taskDefinition=task_definition, desiredCount=desired_count, - deploymentConfiguration=deployment_configuration - ) + deploymentConfiguration=deployment_configuration) if network_configuration: params['networkConfiguration'] = network_configuration if self.health_check_setable(params): params['healthCheckGracePeriodSeconds'] = health_check_grace_period_seconds + if force_new_deployment: + params['forceNewDeployment'] = force_new_deployment response = self.ecs.update_service(**params) return self.jsonize(response['service']) @@ -472,6 +480,7 @@ def main(): role=dict(required=False, default='', type='str'), delay=dict(required=False, type='int', default=10), repeat=dict(required=False, type='int', default=10), + force_new_deployment=dict(required=False, default=False, type='bool'), deployment_configuration=dict(required=False, default={}, type='dict'), placement_constraints=dict(required=False, default=[], type='list'), placement_strategy=dict(required=False, default=[], type='list'), @@ -513,6 +522,9 @@ def main(): if module.params['launch_type']: if not module.botocore_at_least('1.8.4'): module.fail_json(msg='botocore needs to be version 1.8.4 or higher to use launch_type') + if module.params['force_new_deployment']: + if not module.botocore_at_least('1.8.4'): + module.fail_json(msg='botocore needs to be version 1.8.4 or higher to use force_new_deployment') if module.params['state'] == 'present': @@ -520,7 +532,9 @@ def main(): update = False if existing and 'status' in existing and existing['status'] == "ACTIVE": - if service_mgr.is_matching_service(module.params, existing): + if module.params['force_new_deployment']: + update = True + elif service_mgr.is_matching_service(module.params, existing): matching = True results['service'] = existing else: @@ -552,8 +566,8 @@ def main(): module.params['desired_count'], deploymentConfiguration, network_configuration, - module.params['health_check_grace_period_seconds'] - ) + module.params['health_check_grace_period_seconds'], + module.params['force_new_deployment']) else: try: response = service_mgr.create_service(module.params['name'], diff --git a/test/integration/targets/ecs_cluster/playbooks/network_force_new_deployment.yml b/test/integration/targets/ecs_cluster/playbooks/network_force_new_deployment.yml new file mode 100644 index 00000000000..07b013696bb --- /dev/null +++ b/test/integration/targets/ecs_cluster/playbooks/network_force_new_deployment.yml @@ -0,0 +1,108 @@ +- hosts: localhost + connection: local + vars: + resource_prefix: 'ansible-testing-fnd' + + tasks: + - block: + - name: set up aws connection info + set_fact: + aws_connection_info: &aws_connection_info + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token }}" + region: "{{ aws_region }}" + no_log: True + + - name: create ecs cluster + ecs_cluster: + name: "{{ resource_prefix }}" + state: present + <<: *aws_connection_info + + - name: create ecs_taskdefinition + ecs_taskdefinition: + containers: + - name: my_container + image: ubuntu + memory: 128 + family: "{{ resource_prefix }}" + state: present + <<: *aws_connection_info + register: ecs_taskdefinition_creation + + - name: create ecs_service + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + state: present + <<: *aws_connection_info + register: ecs_service_creation + + - name: ecs_service works fine even when older botocore is used + assert: + that: + - ecs_service_creation.changed + + - name: create ecs_service using force_new_deployment + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + force_new_deployment: true + state: present + <<: *aws_connection_info + register: ecs_service_creation_force_new_deploy + ignore_errors: yes + + - name: check that module returns success + assert: + that: + - ecs_service_creation_force_new_deploy.changed + + always: + - name: scale down ecs service + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 0 + state: present + <<: *aws_connection_info + ignore_errors: yes + + - name: pause to wait for scale down + pause: + seconds: 30 + + - name: remove ecs service + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + state: absent + <<: *aws_connection_info + ignore_errors: yes + + - name: remove ecs task definition + ecs_taskdefinition: + containers: + - name: my_container + image: ubuntu + memory: 128 + family: "{{ resource_prefix }}" + revision: "{{ ecs_taskdefinition_creation.taskdefinition.revision }}" + state: absent + <<: *aws_connection_info + ignore_errors: yes + + - name: remove ecs cluster + ecs_cluster: + name: "{{ resource_prefix }}" + state: absent + <<: *aws_connection_info + ignore_errors: yes diff --git a/test/integration/targets/ecs_cluster/playbooks/network_force_new_deployment_fail.yml b/test/integration/targets/ecs_cluster/playbooks/network_force_new_deployment_fail.yml new file mode 100644 index 00000000000..ff9bd9f1445 --- /dev/null +++ b/test/integration/targets/ecs_cluster/playbooks/network_force_new_deployment_fail.yml @@ -0,0 +1,109 @@ +- hosts: localhost + connection: local + vars: + resource_prefix: 'ansible-testing-fndf' + + tasks: + - block: + - name: set up aws connection info + set_fact: + aws_connection_info: &aws_connection_info + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token }}" + region: "{{ aws_region }}" + no_log: True + + - name: create ecs cluster + ecs_cluster: + name: "{{ resource_prefix }}" + state: present + <<: *aws_connection_info + + - name: create ecs_taskdefinition + ecs_taskdefinition: + containers: + - name: my_container + image: ubuntu + memory: 128 + family: "{{ resource_prefix }}" + state: present + <<: *aws_connection_info + register: ecs_taskdefinition_creation + + - name: create ecs_service + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + state: present + <<: *aws_connection_info + register: ecs_service_creation + + - name: ecs_service works fine even when older botocore is used + assert: + that: + - ecs_service_creation.changed + + - name: create ecs_service using force_new_deployment + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + force_new_deployment: true + state: present + <<: *aws_connection_info + register: ecs_service_creation_force_new_deploy + ignore_errors: yes + + - name: check that graceful failure message is returned from ecs_service + assert: + that: + - ecs_service_creation_force_new_deploy.failed + - 'ecs_service_creation_force_new_deploy.msg == "botocore needs to be version 1.8.4 or higher to use force_new_deployment"' + + always: + - name: scale down ecs service + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 0 + state: present + <<: *aws_connection_info + ignore_errors: yes + + - name: pause to wait for scale down + pause: + seconds: 30 + + - name: remove ecs service + ecs_service: + name: "{{ resource_prefix }}" + cluster: "{{ resource_prefix }}" + task_definition: "{{ resource_prefix }}" + desired_count: 1 + state: absent + <<: *aws_connection_info + ignore_errors: yes + + - name: remove ecs task definition + ecs_taskdefinition: + containers: + - name: my_container + image: ubuntu + memory: 128 + family: "{{ resource_prefix }}" + revision: "{{ ecs_taskdefinition_creation.taskdefinition.revision }}" + state: absent + <<: *aws_connection_info + ignore_errors: yes + + - name: remove ecs cluster + ecs_cluster: + name: "{{ resource_prefix }}" + state: absent + <<: *aws_connection_info + ignore_errors: yes diff --git a/test/integration/targets/ecs_cluster/runme.sh b/test/integration/targets/ecs_cluster/runme.sh index 1d968957a56..790a06c1fb1 100755 --- a/test/integration/targets/ecs_cluster/runme.sh +++ b/test/integration/targets/ecs_cluster/runme.sh @@ -25,6 +25,20 @@ source "${MYTMPDIR}/botocore-1.7.44/bin/activate" $PYTHON -m pip install 'botocore>=1.7.44,<1.8.4' boto3 ansible-playbook -i ../../inventory -e @../../integration_config.yml -e @../../cloud-config-aws.yml -v playbooks/network_assign_public_ip_fail.yml "$@" +# Test graceful failure for force new deployment #42518 +# applies for botocore < 1.8.4 +virtualenv --system-site-packages --python "${PYTHON}" "${MYTMPDIR}/botocore-1.8.4" +source "${MYTMPDIR}/botocore-1.8.4/bin/activate" +$PYTHON -m pip install 'botocore>=1.7.44,<1.8.4' boto3 +ansible-playbook -i ../../inventory -e @../../integration_config.yml -e @../../cloud-config-aws.yml -v playbooks/network_force_new_deployment_fail.yml "$@" + +# Test force new deployment #42518 +# applies for botocore < 1.8.4 +virtualenv --system-site-packages --python "${PYTHON}" "${MYTMPDIR}/botocore-1.8.5" +source "${MYTMPDIR}/botocore-1.8.5/bin/activate" +$PYTHON -m pip install 'botocore>1.8.4' boto3 +ansible-playbook -i ../../inventory -e @../../integration_config.yml -e @../../cloud-config-aws.yml -v playbooks/network_force_new_deployment.yml "$@" + # Run full test suite virtualenv --system-site-packages --python "${PYTHON}" "${MYTMPDIR}/botocore-recent" source "${MYTMPDIR}/botocore-recent/bin/activate"