From b979b26a74af6dc10de42f295dfad276418d7108 Mon Sep 17 00:00:00 2001 From: Tad Merchant Date: Wed, 6 Mar 2019 07:40:32 -0500 Subject: [PATCH] Add launch type to ecs task (#49081) * adds fargate launch_type to ecs_task module * White space changes * fix documentation for running ecs task on fargate * remove extraneous example from ecs_task * White space changes * Adds changelog fragment * Pluralize minor_changes in changelog fragment * Add Stop and Start task permissions --- .../49081-add-launch-type-to-ecs-task.yml | 2 + .../testing_policies/container-policy.json | 2 + lib/ansible/modules/cloud/amazon/ecs_task.py | 51 +++++++++++++++++-- .../ecs_cluster/files/ecs-trust-policy.json | 5 +- .../roles/ecs_cluster/tasks/main.yml | 33 ++++++++++-- 5 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 changelogs/fragments/49081-add-launch-type-to-ecs-task.yml diff --git a/changelogs/fragments/49081-add-launch-type-to-ecs-task.yml b/changelogs/fragments/49081-add-launch-type-to-ecs-task.yml new file mode 100644 index 0000000000..f7cb1001d0 --- /dev/null +++ b/changelogs/fragments/49081-add-launch-type-to-ecs-task.yml @@ -0,0 +1,2 @@ +minor_changes: + - adds launch type to ecs task to support fargate launch type. diff --git a/hacking/aws_config/testing_policies/container-policy.json b/hacking/aws_config/testing_policies/container-policy.json index 225efc7dfa..944559c159 100644 --- a/hacking/aws_config/testing_policies/container-policy.json +++ b/hacking/aws_config/testing_policies/container-policy.json @@ -41,6 +41,8 @@ "ecs:List*", "ecs:RegisterTaskDefinition", "ecs:RunTask", + "ecs:StartTask", + "ecs:StopTask", "ecs:UpdateService", "elasticloadbalancing:Describe*", "iam:AttachRolePolicy", diff --git a/lib/ansible/modules/cloud/amazon/ecs_task.py b/lib/ansible/modules/cloud/amazon/ecs_task.py index 5d930b811e..2e80c0ad36 100644 --- a/lib/ansible/modules/cloud/amazon/ecs_task.py +++ b/lib/ansible/modules/cloud/amazon/ecs_task.py @@ -60,6 +60,12 @@ options: - I(network_configuration) has two keys, I(subnets), a list of subnet IDs to which the task is attached and I(security_groups), a list of group names or group IDs for the task version_added: 2.6 + launch_type: + description: + - The launch type on which to run your service + required: false + version_added: 2.8 + choices: ["EC2", "FARGATE"] extends_documentation_fragment: - aws - ec2 @@ -95,6 +101,22 @@ EXAMPLES = ''' - my_security_group register: task_output +- name: RUN a task on Fargate + ecs_task: + operation: run + cluster: console-sample-app-static-cluster + task_definition: console-sample-app-static-taskdef + task: "arn:aws:ecs:us-west-2:172139249013:task/3f8353d1-29a8-4689-bbf6-ad79937ffe8a" + started_by: ansible_user + launch_type: FARGATE + network_configuration: + subnets: + - subnet-abcd1234 + security_groups: + - sg-aaaa1111 + - my_security_group + register: task_output + - name: Stop a task ecs_task: operation: stop @@ -160,6 +182,10 @@ task: description: The timestamp of when the task was stopped. returned: only when details is true type: str + launchType: + description: The launch type on which to run your task. + returned: always + type: str ''' from ansible.module_utils.aws.core import AnsibleAWSModule @@ -208,13 +234,15 @@ class EcsExecManager: return c return None - def run_task(self, cluster, task_definition, overrides, count, startedBy): + def run_task(self, cluster, task_definition, overrides, count, startedBy, launch_type): if overrides is None: overrides = dict() params = dict(cluster=cluster, taskDefinition=task_definition, overrides=overrides, count=count, startedBy=startedBy) if self.module.params['network_configuration']: params['networkConfiguration'] = self.format_network_configuration(self.module.params['network_configuration']) + if launch_type: + params['launchType'] = launch_type try: response = self.ecs.run_task(**params) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: @@ -247,6 +275,13 @@ class EcsExecManager: response = self.ecs.stop_task(cluster=cluster, task=task) return response['task'] + def ecs_api_handles_launch_type(self): + from distutils.version import LooseVersion + # There doesn't seem to be a nice way to inspect botocore to look + # for attributes (and networkConfiguration is not an explicit argument + # to e.g. ecs.run_task, it's just passed as a keyword argument) + return LooseVersion(botocore.__version__) >= LooseVersion('1.8.4') + def ecs_api_handles_network_configuration(self): from distutils.version import LooseVersion # There doesn't seem to be a nice way to inspect botocore to look @@ -266,10 +301,12 @@ def main(): task=dict(required=False, type='str'), # P* container_instances=dict(required=False, type='list'), # S* started_by=dict(required=False, type='str'), # R S - network_configuration=dict(required=False, type='dict') + network_configuration=dict(required=False, type='dict'), + launch_type=dict(required=False, choices=['EC2', 'FARGATE']) )) - module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) + module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, + required_if=[('launch_type', 'FARGATE', ['network_configuration'])]) # Validate Inputs if module.params['operation'] == 'run': @@ -295,8 +332,13 @@ def main(): status_type = "STOPPED" service_mgr = EcsExecManager(module) + if module.params['network_configuration'] and not service_mgr.ecs_api_handles_network_configuration(): module.fail_json(msg='botocore needs to be version 1.7.44 or higher to use network configuration') + + if module.params['launch_type'] and not service_mgr.ecs_api_handles_launch_type(): + module.fail_json(msg='botocore needs to be version 1.8.4 or higher to use launch type') + existing = service_mgr.list_tasks(module.params['cluster'], task_to_list, status_type) results = dict(changed=False) @@ -311,7 +353,8 @@ def main(): module.params['task_definition'], module.params['overrides'], module.params['count'], - module.params['started_by']) + module.params['started_by'], + module.params['launch_type']) results['changed'] = True elif module.params['operation'] == 'start': diff --git a/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/files/ecs-trust-policy.json b/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/files/ecs-trust-policy.json index f6fe17de72..f871b34d91 100644 --- a/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/files/ecs-trust-policy.json +++ b/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/files/ecs-trust-policy.json @@ -5,7 +5,10 @@ "Sid": "", "Effect": "Allow", "Principal": { - "Service": "ecs.amazonaws.com" + "Service": [ + "ecs.amazonaws.com", + "ecs-tasks.amazonaws.com" + ] }, "Action": "sts:AssumeRole" } diff --git a/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/tasks/main.yml b/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/tasks/main.yml index 2a0c783bda..2b8cc9037c 100644 --- a/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/tasks/main.yml +++ b/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/tasks/main.yml @@ -664,7 +664,7 @@ assert: that: - ecs_fargate_task_definition_bridged_with_host_port is failed - - 'ecs_fargate_task_definition_bridged_with_host_port.msg == "To use FARGATE launch type, network_mode must be awsvpc"' + - 'ecs_fargate_task_definition_bridged_with_host_port.msg == "To use FARGATE launch type, network_mode must be awsvpc"' - name: create Fargate VPC-networked task definition without CPU or Memory (expected to Fail) ecs_taskdefinition: @@ -681,7 +681,7 @@ assert: that: - ecs_fargate_task_definition_vpc_no_mem is failed - - 'ecs_fargate_task_definition_vpc_no_mem.msg == "launch_type is FARGATE but all of the following are missing: cpu, memory"' + - 'ecs_fargate_task_definition_vpc_no_mem.msg == "launch_type is FARGATE but all of the following are missing: cpu, memory"' - name: create Fargate VPC-networked task definition with CPU or Memory and execution role ecs_taskdefinition: @@ -715,13 +715,13 @@ <<: *aws_connection_info register: ecs_fargate_service_network_without_awsvpc ignore_errors: yes - + - name: assert that using Fargate ECS service fails assert: that: - ecs_fargate_service_network_without_awsvpc is failed - - name: create fargate ECS service with network config + - name: create fargate ECS service with network config ecs_service: state: present name: "{{ ecs_service_name }}4" @@ -738,6 +738,22 @@ <<: *aws_connection_info register: ecs_fargate_service_network_with_awsvpc + - name: create fargate ECS task with run task + ecs_task: + operation: run + cluster: "{{ ecs_cluster_name }}" + task_definition: "{{ ecs_task_name }}-vpc" + launch_type: FARGATE + count: 1 + network_configuration: + subnets: "{{ setup_subnet.results | json_query('[].subnet.id') }}" + security_groups: + - '{{ setup_sg.group_id }}' + assign_public_ip: true + started_by: ansible_user + <<: *aws_connection_info + register: fargate_run_task_output + - name: assert that public IP assignment is enabled assert: that: @@ -879,6 +895,15 @@ ignore_errors: yes register: ecs_service_scale_down + - name: stop Fargate ECS task + ecs_task: + task: "{{ fargate_run_task_output.task[0].taskArn }}" + task_definition: "{{ ecs_task_name }}-vpc" + operation: stop + cluster: "{{ ecs_cluster_name }}" + <<: *aws_connection_info + ignore_errors: yes + - name: pause to allow services to scale down pause: seconds: 60