Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support new enableExecuteCommand options for ECS service #488

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/fragments/488-ecs_service-support_exec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
minor_changes:
- ecs_service - added new parameter ``enable_execute_command`` (https://github.com/ansible-collections/community.aws/pull/488).
- ecs_service - handle SDK errors more cleanly on update failures (https://github.com/ansible-collections/community.aws/pull/488).
156 changes: 106 additions & 50 deletions plugins/modules/ecs_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@
rollback:
type: bool
description: If enabled, ECS will roll back your service to the last completed deployment after a failure.
enable_execute_command:
description:
- Whether or not to enable the execute command functionality for the containers in the ECS task.
- If I(enable_execute_command=true) execute command functionality is enabled on all containers in the ECS task.
required: false
type: bool
version_added: 5.4.0
placement_constraints:
description:
- The placement constraints for the tasks in the service.
Expand Down Expand Up @@ -778,6 +785,9 @@ def is_matching_service(self, expected, existing):
if boto3_tag_list_to_ansible_dict(existing.get('tags', [])) != (expected['tags'] or {}):
return False

if (expected["enable_execute_command"] or False) != existing.get("enableExecuteCommand", False):
return False

# expected is params. DAEMON scheduling strategy returns desired count equal to
# number of instances running; don't check desired count if scheduling strat is daemon
if (expected['scheduling_strategy'] != 'DAEMON'):
Expand All @@ -786,11 +796,30 @@ def is_matching_service(self, expected, existing):

return True

def create_service(self, service_name, cluster_name, task_definition, load_balancers,
desired_count, client_token, role, deployment_controller, deployment_configuration,
placement_constraints, placement_strategy, health_check_grace_period_seconds,
network_configuration, service_registries, launch_type, platform_version,
scheduling_strategy, capacity_provider_strategy, tags, propagate_tags):
def create_service(
self,
service_name,
cluster_name,
task_definition,
load_balancers,
desired_count,
client_token,
role,
deployment_controller,
deployment_configuration,
placement_constraints,
placement_strategy,
health_check_grace_period_seconds,
network_configuration,
service_registries,
launch_type,
platform_version,
scheduling_strategy,
capacity_provider_strategy,
tags,
propagate_tags,
enable_execute_command,
):

params = dict(
cluster=cluster_name,
Expand Down Expand Up @@ -836,14 +865,30 @@ def create_service(self, service_name, cluster_name, task_definition, load_balan

if scheduling_strategy:
params['schedulingStrategy'] = scheduling_strategy
if enable_execute_command:
params["enableExecuteCommand"] = enable_execute_command

response = self.ecs.create_service(**params)
return self.jsonize(response['service'])

def update_service(self, service_name, cluster_name, task_definition, desired_count,
deployment_configuration, placement_constraints, placement_strategy,
network_configuration, health_check_grace_period_seconds,
force_new_deployment, capacity_provider_strategy, load_balancers,
purge_placement_constraints, purge_placement_strategy):
def update_service(
self,
service_name,
cluster_name,
task_definition,
desired_count,
deployment_configuration,
placement_constraints,
placement_strategy,
network_configuration,
health_check_grace_period_seconds,
force_new_deployment,
capacity_provider_strategy,
load_balancers,
purge_placement_constraints,
purge_placement_strategy,
enable_execute_command,
):
params = dict(
cluster=cluster_name,
service=service_name,
Expand Down Expand Up @@ -875,11 +920,14 @@ def update_service(self, service_name, cluster_name, task_definition, desired_co
# desired count is not required if scheduling strategy is daemon
if desired_count is not None:
params['desiredCount'] = desired_count
if enable_execute_command is not None:
params["enableExecuteCommand"] = enable_execute_command

if load_balancers:
params['loadBalancers'] = load_balancers

response = self.ecs.update_service(**params)

return self.jsonize(response['service'])

def jsonize(self, service):
Expand Down Expand Up @@ -967,8 +1015,9 @@ def main():
base=dict(type='int')
)
),
propagate_tags=dict(required=False, choices=['TASK_DEFINITION', 'SERVICE']),
tags=dict(required=False, type='dict'),
propagate_tags=dict(required=False, choices=["TASK_DEFINITION", "SERVICE"]),
tags=dict(required=False, type="dict"),
enable_execute_command=dict(required=False, type="bool"),
)

module = AnsibleAWSModule(argument_spec=argument_spec,
Expand Down Expand Up @@ -1081,47 +1130,54 @@ def main():
if task_definition is None and module.params['force_new_deployment']:
task_definition = existing['taskDefinition']

# update required
response = service_mgr.update_service(module.params['name'],
module.params['cluster'],
task_definition,
module.params['desired_count'],
deploymentConfiguration,
module.params['placement_constraints'],
module.params['placement_strategy'],
network_configuration,
module.params['health_check_grace_period_seconds'],
module.params['force_new_deployment'],
capacityProviders,
updatedLoadBalancers,
module.params['purge_placement_constraints'],
module.params['purge_placement_strategy'],
)
try:
# update required
response = service_mgr.update_service(
module.params["name"],
module.params["cluster"],
task_definition,
module.params["desired_count"],
deploymentConfiguration,
module.params["placement_constraints"],
module.params["placement_strategy"],
network_configuration,
module.params["health_check_grace_period_seconds"],
module.params["force_new_deployment"],
capacityProviders,
updatedLoadBalancers,
module.params["purge_placement_constraints"],
module.params["purge_placement_strategy"],
module.params["enable_execute_command"],
)
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
module.fail_json_aws(e, msg="Couldn't create service")

else:
try:
response = service_mgr.create_service(module.params['name'],
module.params['cluster'],
module.params['task_definition'],
loadBalancers,
module.params['desired_count'],
clientToken,
role,
deploymentController,
deploymentConfiguration,
module.params['placement_constraints'],
module.params['placement_strategy'],
module.params['health_check_grace_period_seconds'],
network_configuration,
serviceRegistries,
module.params['launch_type'],
module.params['platform_version'],
module.params['scheduling_strategy'],
capacityProviders,
module.params['tags'],
module.params['propagate_tags'],
)
except botocore.exceptions.ClientError as e:
response = service_mgr.create_service(
module.params["name"],
module.params["cluster"],
module.params["task_definition"],
loadBalancers,
module.params["desired_count"],
clientToken,
role,
deploymentController,
deploymentConfiguration,
module.params["placement_constraints"],
module.params["placement_strategy"],
module.params["health_check_grace_period_seconds"],
network_configuration,
serviceRegistries,
module.params["launch_type"],
module.params["platform_version"],
module.params["scheduling_strategy"],
capacityProviders,
module.params["tags"],
module.params["propagate_tags"],
module.params["enable_execute_command"],
)
except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:
module.fail_json_aws(e, msg="Couldn't create service")

if response.get('tags', None):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@
managed_policy:
- AmazonEC2ContainerServiceRole
wait: True
register: iam_role_creation

- name: ensure AmazonECSTaskExecutionRolePolicy exists
iam_role:
name: "{{ ecs_task_role_name }}"
assume_role_policy_document: "{{ lookup('file','ecs-trust-policy.json') }}"
description: "Allows ECS containers to make calls to ECR"
state: present
create_instance_profile: false
managed_policy:
- AmazonECSTaskExecutionRolePolicy
wait: True
register: iam_execution_role

- name: ensure AWSServiceRoleForECS role exists
iam_role_info:
Expand Down
55 changes: 39 additions & 16 deletions tests/integration/targets/ecs_cluster/tasks/20_ecs_service.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,44 @@
- not ecs_service_scale_down.changed
- ecs_service_scale_down.service.desiredCount == 0

- name: update task definition
ecs_taskdefinition:
containers: "{{ ecs_task_containers }}"
family: "{{ ecs_task_name }}"
task_role_arn: "{{ ecs_task_role_name }}"
state: present
register: ecs_task_update

- name: check that initial task definition changes
assert:
that:
- ecs_task_update.changed

- name: Enable ExecuteCommand
ecs_service:
state: present
name: "{{ ecs_service_name }}"
cluster: "{{ ecs_cluster_name }}"
task_definition: "{{ ecs_task_name }}:{{ ecs_task_update.taskdefinition.revision }}"
desired_count: 0
deployment_configuration: "{{ ecs_service_deployment_configuration }}"
placement_strategy: "{{ ecs_service_placement_strategy }}"
placement_constraints:
- type: distinctInstance
health_check_grace_period_seconds: "{{ ecs_service_health_check_grace_period }}"
load_balancers:
- targetGroupArn: "{{ elb_target_group_instance.target_group_arn }}"
containerName: "{{ ecs_task_name }}"
containerPort: "{{ ecs_task_container_port }}"
role: "{{ ecs_service_role_name }}"
enable_execute_command: True
register: ecs_service_execute

- name: check that ECS service changed
assert:
that:
- ecs_service_execute.changed

- name: delete ECS service definition
ecs_service:
state: absent
Expand Down Expand Up @@ -650,25 +688,10 @@
that:
- ecs_service_remove_strategy.changed
- "ecs_service_remove_strategy.service.placementStrategy | length == 0"

# ============================================================
# Begin tests for Fargate

- name: ensure AmazonECSTaskExecutionRolePolicy exists
iam_role:
name: "{{ ecs_task_role_name }}"
assume_role_policy_document: "{{ lookup('file','ecs-trust-policy.json') }}"
description: "Allows ECS containers to make calls to ECR"
state: present
create_instance_profile: false
managed_policy:
- AmazonECSTaskExecutionRolePolicy
wait: True
register: iam_execution_role

- name: pause for iam availability
ansible.builtin.pause:
seconds: 20

- name: create Fargate VPC-networked task definition with host port set to 8080 and unsupported network mode (expected to fail)
ecs_taskdefinition:
containers: "{{ ecs_fargate_task_containers }}"
Expand Down