Add tags to aws ecs task (#53717)
* Add tags to ecs_task remove older reference to credentials * uncomment shell commands to enable/disable account settings * Fix documentation and pep8 issues * fix review items for ecs_task tags use missing_required_lib for tags change fail_json message to suggested message switch from task_tags to tags for consisitency * Add import for missing_required_lib function * Tidy put-account-setting tasks and add permission Using `environment` and `command` rather than `shell` avoids the need for `no_log` and means that people can fix the problem * update version added for ecs_task tags * fix tests after removal of ansible_facts from ecs_service_info, add delay when service is still draining * Add documentation for sanity tests
This commit is contained in:
parent
1535d9fd90
commit
ab54736e12
4 changed files with 168 additions and 15 deletions
|
@ -84,6 +84,12 @@ options:
|
|||
version_added: 2.8
|
||||
choices: ["EC2", "FARGATE"]
|
||||
type: str
|
||||
tags:
|
||||
type: dict
|
||||
description:
|
||||
- Tags that will be added to ecs tasks on start and run
|
||||
required: false
|
||||
version_added: "2.10"
|
||||
extends_documentation_fragment:
|
||||
- aws
|
||||
- ec2
|
||||
|
@ -108,6 +114,11 @@ EXAMPLES = '''
|
|||
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"
|
||||
tags:
|
||||
resourceName: a_task_for_ansible_to_run
|
||||
type: long_running_task
|
||||
network: internal
|
||||
version: 1.4
|
||||
container_instances:
|
||||
- arn:aws:ecs:us-west-2:172139249013:container-instance/79c23f22-876c-438a-bddf-55c98a3538a8
|
||||
started_by: ansible_user
|
||||
|
@ -209,7 +220,8 @@ task:
|
|||
'''
|
||||
|
||||
from ansible.module_utils.aws.core import AnsibleAWSModule
|
||||
from ansible.module_utils.ec2 import ec2_argument_spec, get_ec2_security_group_ids_from_names
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
from ansible.module_utils.ec2 import ec2_argument_spec, get_ec2_security_group_ids_from_names, ansible_dict_to_boto3_tag_list
|
||||
|
||||
try:
|
||||
import botocore
|
||||
|
@ -254,7 +266,7 @@ class EcsExecManager:
|
|||
return c
|
||||
return None
|
||||
|
||||
def run_task(self, cluster, task_definition, overrides, count, startedBy, launch_type):
|
||||
def run_task(self, cluster, task_definition, overrides, count, startedBy, launch_type, tags):
|
||||
if overrides is None:
|
||||
overrides = dict()
|
||||
params = dict(cluster=cluster, taskDefinition=task_definition,
|
||||
|
@ -263,6 +275,10 @@ class EcsExecManager:
|
|||
params['networkConfiguration'] = self.format_network_configuration(self.module.params['network_configuration'])
|
||||
if launch_type:
|
||||
params['launchType'] = launch_type
|
||||
if tags:
|
||||
params['tags'] = ansible_dict_to_boto3_tag_list(tags, 'key', 'value')
|
||||
|
||||
# TODO: need to check if long arn format enabled.
|
||||
try:
|
||||
response = self.ecs.run_task(**params)
|
||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||
|
@ -270,7 +286,7 @@ class EcsExecManager:
|
|||
# include tasks and failures
|
||||
return response['tasks']
|
||||
|
||||
def start_task(self, cluster, task_definition, overrides, container_instances, startedBy):
|
||||
def start_task(self, cluster, task_definition, overrides, container_instances, startedBy, tags):
|
||||
args = dict()
|
||||
if cluster:
|
||||
args['cluster'] = cluster
|
||||
|
@ -284,6 +300,8 @@ class EcsExecManager:
|
|||
args['startedBy'] = startedBy
|
||||
if self.module.params['network_configuration']:
|
||||
args['networkConfiguration'] = self.format_network_configuration(self.module.params['network_configuration'])
|
||||
if tags:
|
||||
args['tags'] = ansible_dict_to_boto3_tag_list(tags, 'key', 'value')
|
||||
try:
|
||||
response = self.ecs.start_task(**args)
|
||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||
|
@ -302,6 +320,17 @@ class EcsExecManager:
|
|||
# to e.g. ecs.run_task, it's just passed as a keyword argument)
|
||||
return LooseVersion(botocore.__version__) >= LooseVersion('1.8.4')
|
||||
|
||||
def ecs_task_long_format_enabled(self):
|
||||
account_support = self.ecs.list_account_settings(name='taskLongArnFormat', effectiveSettings=True)
|
||||
return account_support['settings'][0]['value'] == 'enabled'
|
||||
|
||||
def ecs_api_handles_tags(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.12.46')
|
||||
|
||||
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
|
||||
|
@ -322,7 +351,8 @@ def main():
|
|||
container_instances=dict(required=False, type='list'), # S*
|
||||
started_by=dict(required=False, type='str'), # R S
|
||||
network_configuration=dict(required=False, type='dict'),
|
||||
launch_type=dict(required=False, choices=['EC2', 'FARGATE'])
|
||||
launch_type=dict(required=False, choices=['EC2', 'FARGATE']),
|
||||
tags=dict(required=False, type='dict')
|
||||
))
|
||||
|
||||
module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True,
|
||||
|
@ -359,6 +389,12 @@ def main():
|
|||
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')
|
||||
|
||||
if module.params['tags']:
|
||||
if not service_mgr.ecs_api_handles_tags():
|
||||
module.fail_json(msg=missing_required_lib("botocore >= 1.12.46", reason="to use tags"))
|
||||
if not service_mgr.ecs_task_long_format_enabled():
|
||||
module.fail_json(msg="Cannot set task tags: long format task arns are required to set tags")
|
||||
|
||||
existing = service_mgr.list_tasks(module.params['cluster'], task_to_list, status_type)
|
||||
|
||||
results = dict(changed=False)
|
||||
|
@ -374,7 +410,9 @@ def main():
|
|||
module.params['overrides'],
|
||||
module.params['count'],
|
||||
module.params['started_by'],
|
||||
module.params['launch_type'])
|
||||
module.params['launch_type'],
|
||||
module.params['tags'],
|
||||
)
|
||||
results['changed'] = True
|
||||
|
||||
elif module.params['operation'] == 'start':
|
||||
|
@ -388,7 +426,8 @@ def main():
|
|||
module.params['task_definition'],
|
||||
module.params['overrides'],
|
||||
module.params['container_instances'],
|
||||
module.params['started_by']
|
||||
module.params['started_by'],
|
||||
module.params['tags'],
|
||||
)
|
||||
results['changed'] = True
|
||||
|
||||
|
|
|
@ -623,7 +623,7 @@
|
|||
- name: check that facts contain network configuration
|
||||
assert:
|
||||
that:
|
||||
- "'networkConfiguration' in ecs_service_info.ansible_facts.services[0]"
|
||||
- "'networkConfiguration' in ecs_service_info.services[0]"
|
||||
|
||||
- name: attempt to get facts from missing task definition
|
||||
ecs_taskdefinition_info:
|
||||
|
@ -738,6 +738,11 @@
|
|||
<<: *aws_connection_info
|
||||
register: ecs_fargate_service_network_with_awsvpc
|
||||
|
||||
- name: assert that public IP assignment is enabled
|
||||
assert:
|
||||
that:
|
||||
- 'ecs_fargate_service_network_with_awsvpc.service.networkConfiguration.awsvpcConfiguration.assignPublicIp == "ENABLED"'
|
||||
|
||||
- name: create fargate ECS task with run task
|
||||
ecs_task:
|
||||
operation: run
|
||||
|
@ -754,10 +759,67 @@
|
|||
<<: *aws_connection_info
|
||||
register: fargate_run_task_output
|
||||
|
||||
- name: assert that public IP assignment is enabled
|
||||
assert:
|
||||
that:
|
||||
- 'ecs_fargate_service_network_with_awsvpc.service.networkConfiguration.awsvpcConfiguration.assignPublicIp == "ENABLED"'
|
||||
# aws cli not installed in docker container; make sure it's installed.
|
||||
- name: install awscli
|
||||
pip:
|
||||
state: present
|
||||
name: awscli
|
||||
|
||||
- name: disable taskLongArnFormat
|
||||
command: aws ecs put-account-setting --name taskLongArnFormat --value disabled
|
||||
environment:
|
||||
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
|
||||
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
|
||||
AWS_SESSION_TOKEN: "{{ security_token | default('') }}"
|
||||
AWS_DEFAULT_REGION: "{{ aws_region }}"
|
||||
|
||||
- name: create fargate ECS task with run task and tags (LF disabled) (should fail)
|
||||
ecs_task:
|
||||
operation: run
|
||||
cluster: "{{ ecs_cluster_name }}"
|
||||
task_definition: "{{ ecs_task_name }}-vpc"
|
||||
launch_type: FARGATE
|
||||
count: 1
|
||||
tags:
|
||||
tag_key: tag_value
|
||||
tag_key2: tag_value2
|
||||
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_with_tags_fail
|
||||
ignore_errors: yes
|
||||
|
||||
- name: enable taskLongArnFormat
|
||||
command: aws ecs put-account-setting --name taskLongArnFormat --value enabled
|
||||
environment:
|
||||
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
|
||||
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
|
||||
AWS_SESSION_TOKEN: "{{ security_token | default('') }}"
|
||||
AWS_DEFAULT_REGION: "{{ aws_region }}"
|
||||
|
||||
- name: create fargate ECS task with run task and tags
|
||||
ecs_task:
|
||||
operation: run
|
||||
cluster: "{{ ecs_cluster_name }}"
|
||||
task_definition: "{{ ecs_task_name }}-vpc"
|
||||
launch_type: FARGATE
|
||||
count: 1
|
||||
tags:
|
||||
tag_key: tag_value
|
||||
tag_key2: tag_value2
|
||||
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_with_tags
|
||||
|
||||
|
||||
# ============================================================
|
||||
# End tests for Fargate
|
||||
|
@ -795,12 +857,12 @@
|
|||
state: present
|
||||
name: "{{ ecs_service_name }}"
|
||||
cluster: "{{ ecs_cluster_name }}"
|
||||
task_definition: "{{ ecs_service_info.ansible_facts.services[0].taskDefinition }}"
|
||||
task_definition: "{{ ecs_service_info.services[0].taskDefinition }}"
|
||||
desired_count: 0
|
||||
deployment_configuration: "{{ ecs_service_deployment_configuration }}"
|
||||
placement_strategy: "{{ ecs_service_placement_strategy }}"
|
||||
load_balancers:
|
||||
- targetGroupArn: "{{ ecs_service_info.ansible_facts.services[0].loadBalancers[0].targetGroupArn }}"
|
||||
- targetGroupArn: "{{ ecs_service_info.services[0].loadBalancers[0].targetGroupArn }}"
|
||||
containerName: "{{ ecs_task_name }}"
|
||||
containerPort: "{{ ecs_task_container_port }}"
|
||||
<<: *aws_connection_info
|
||||
|
@ -821,12 +883,12 @@
|
|||
state: present
|
||||
name: "{{ ecs_service_name }}2"
|
||||
cluster: "{{ ecs_cluster_name }}"
|
||||
task_definition: "{{ ecs_service_info.ansible_facts.services[0].taskDefinition }}"
|
||||
task_definition: "{{ ecs_service_info.services[0].taskDefinition }}"
|
||||
desired_count: 0
|
||||
deployment_configuration: "{{ ecs_service_deployment_configuration }}"
|
||||
placement_strategy: "{{ ecs_service_placement_strategy }}"
|
||||
load_balancers:
|
||||
- targetGroupArn: "{{ ecs_service_info.ansible_facts.services[0].loadBalancers[0].targetGroupArn }}"
|
||||
- targetGroupArn: "{{ ecs_service_info.services[0].loadBalancers[0].targetGroupArn }}"
|
||||
containerName: "{{ ecs_task_name }}"
|
||||
containerPort: "{{ ecs_task_container_port }}"
|
||||
<<: *aws_connection_info
|
||||
|
@ -904,6 +966,14 @@
|
|||
<<: *aws_connection_info
|
||||
ignore_errors: yes
|
||||
|
||||
- name: stop Fargate ECS task
|
||||
ecs_task:
|
||||
task: "{{ fargate_run_task_output_with_tags.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
|
||||
|
|
|
@ -25,6 +25,28 @@
|
|||
<<: *aws_connection_info
|
||||
register: ecs_taskdefinition_creation
|
||||
|
||||
# even after deleting the cluster and recreating with a different name
|
||||
# the previous service can prevent the current service from starting
|
||||
# while it's in a draining state. Check the service info and sleep
|
||||
# if the service does not report as inactive.
|
||||
|
||||
- name: check if service is still running from a previous task
|
||||
ecs_service_info:
|
||||
service: "{{ resource_prefix }}"
|
||||
cluster: "{{ resource_prefix }}"
|
||||
details: yes
|
||||
<<: *aws_connection_info
|
||||
register: ecs_service_info_results
|
||||
- name: delay if the service was not inactive
|
||||
debug: var=ecs_service_info_results
|
||||
|
||||
- name: delay if the service was not inactive
|
||||
pause:
|
||||
seconds: 30
|
||||
when:
|
||||
- ecs_service_info_results.services|length >0
|
||||
- ecs_service_info_results.services[0]['status'] != 'INACTIVE'
|
||||
|
||||
- name: create ecs_service
|
||||
ecs_service:
|
||||
name: "{{ resource_prefix }}"
|
||||
|
|
|
@ -25,6 +25,28 @@
|
|||
<<: *aws_connection_info
|
||||
register: ecs_taskdefinition_creation
|
||||
|
||||
# even after deleting the cluster and recreating with a different name
|
||||
# the previous service can prevent the current service from starting
|
||||
# while it's in a draining state. Check the service info and sleep
|
||||
# if the service does not report as inactive.
|
||||
|
||||
- name: check if service is still running from a previous task
|
||||
ecs_service_info:
|
||||
service: "{{ resource_prefix }}"
|
||||
cluster: "{{ resource_prefix }}"
|
||||
details: yes
|
||||
<<: *aws_connection_info
|
||||
register: ecs_service_info_results
|
||||
- name: delay if the service was not inactive
|
||||
debug: var=ecs_service_info_results
|
||||
|
||||
- name: delay if the service was not inactive
|
||||
pause:
|
||||
seconds: 30
|
||||
when:
|
||||
- ecs_service_info_results.services|length >0
|
||||
- ecs_service_info_results.services[0]['status'] != 'INACTIVE'
|
||||
|
||||
- name: create ecs_service
|
||||
ecs_service:
|
||||
name: "{{ resource_prefix }}"
|
||||
|
|
Loading…
Reference in a new issue