From 1018ceab6a934540923c5c7ff8983545eca3d3c5 Mon Sep 17 00:00:00 2001 From: Berend de Boer Date: Mon, 4 Jul 2022 19:54:24 +1200 Subject: [PATCH] ecs_service - support setting deployment controller on a service (#340) ecs_service - support setting deployment controller on a service SUMMARY Support setting platform version to 1.4.0 (LATEST is 1.3.0) and deployment controller. The first allows access to new 1.4.0 features. The second change allows you to create a service that can be controlled with Code Deploy. Example: - name: create a Fargate service community.aws.ecs_service: state: present name: "my-service" cluster: "my-cluster" platform_version: 1.4.0 task_definition: "my-task" desired_count: "1" launch_type: FARGATE scheduling_strategy: REPLICA deployment_controller: type: CODE_DEPLOY load_balancers: - targetGroupArn: "arn:..." containerName: example containerPort: 80 network_configuration: subnets: - "{{vpc_zone_a.subnet.id}}" - "{{vpc_zone_b.subnet.id}}" security_groups: - "sg-example" assign_public_ip: true This fixes #338. ISSUE TYPE Feature Pull Request Reviewed-by: Jill R Reviewed-by: Mark Chappell Reviewed-by: Markus Bergholz --- ecs_service.py | 59 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/ecs_service.py b/ecs_service.py index aa455d72345..78e352447ee 100644 --- a/ecs_service.py +++ b/ecs_service.py @@ -12,16 +12,16 @@ version_added: 1.0.0 short_description: Create, terminate, start or stop a service in ECS description: - - Creates or terminates ECS. services. + - Creates or terminates ECS services. notes: - The service role specified must be assumable. (i.e. have a trust relationship for the ecs service, ecs.amazonaws.com) - For details of the parameters and returns see U(https://boto3.readthedocs.io/en/latest/reference/services/ecs.html). - An IAM role must have been previously created. author: - - "Mark Chance (@Java1Guy)" - - "Darek Kaczynski (@kaczynskid)" - - "Stephane Maarek (@simplesteph)" - - "Zac Blazic (@zacblazic)" + - "Mark Chance (@Java1Guy)" + - "Darek Kaczynski (@kaczynskid)" + - "Stephane Maarek (@simplesteph)" + - "Zac Blazic (@zacblazic)" options: state: description: @@ -46,11 +46,15 @@ description: - The task definition the service will run. - This parameter is required when I(state=present). + - This parameter is ignored when updating a service with a C(CODE_DEPLOY) deployment controller in which case + the task definition is managed by Code Pipeline and cannot be updated. required: false type: str load_balancers: description: - The list of ELBs defined for this service. + - Load balancers for an existing service cannot be updated, and it is an error to do so. + - When the deployment controller is CODE_DEPLOY changes to this value are simply ignored, and do not cause an error. required: false type: list elements: dict @@ -90,6 +94,17 @@ required: false type: bool default: false + deployment_controller: + description: + - The deployment controller to use for the service. If no deploymenet controller is specified, the ECS controller is used. + required: false + version_added: 4.1.0 + type: dict + suboptions: + type: + type: str + choices: ["ECS", "CODE_DEPLOY", "EXTERNAL"] + description: The deployment controller type to use. deployment_configuration: description: - Optional parameters that control the deployment_configuration. @@ -238,9 +253,8 @@ default: false version_added: 4.1.0 extends_documentation_fragment: -- amazon.aws.aws -- amazon.aws.ec2 - + - amazon.aws.aws + - amazon.aws.ec2 ''' EXAMPLES = r''' @@ -590,6 +604,10 @@ ''' import time +DEPLOYMENT_CONTROLLER_TYPE_MAP = { + 'type': 'str', +} + DEPLOYMENT_CONFIGURATION_TYPE_MAP = { 'maximum_percent': 'int', 'minimum_healthy_percent': 'int', @@ -664,7 +682,8 @@ def is_matching_service(self, expected, existing): # but the user is just entering # ansible-fargate-nginx:3 if expected['task_definition'] != existing['taskDefinition'].split('/')[-1]: - return False + if existing['deploymentController']['type'] != 'CODE_DEPLOY': + return False if expected.get('health_check_grace_period_seconds'): if expected.get('health_check_grace_period_seconds') != existing.get('healthCheckGracePeriodSeconds'): @@ -682,7 +701,7 @@ 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_configuration, + 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): @@ -699,6 +718,8 @@ def create_service(self, service_name, cluster_name, task_definition, load_balan ) if network_configuration: params['networkConfiguration'] = network_configuration + if deployment_controller: + params['deploymentController'] = deployment_controller if launch_type: params['launchType'] = launch_type if platform_version: @@ -786,6 +807,7 @@ def main(): repeat=dict(required=False, type='int', default=10), force_new_deployment=dict(required=False, default=False, type='bool'), force_deletion=dict(required=False, default=False, type='bool'), + deployment_controller=dict(required=False, default={}, type='dict'), deployment_configuration=dict(required=False, default={}, type='dict'), wait=dict(required=False, default=False, type='bool'), placement_constraints=dict( @@ -851,6 +873,11 @@ def main(): else: network_configuration = None + deployment_controller = map_complex_type(module.params['deployment_controller'], + DEPLOYMENT_CONTROLLER_TYPE_MAP) + + deploymentController = snake_dict_to_camel_dict(deployment_controller) + deployment_configuration = map_complex_type(module.params['deployment_configuration'], DEPLOYMENT_CONFIGURATION_TYPE_MAP) @@ -912,12 +939,19 @@ def main(): if 'capacityProviderStrategy' in existing.keys(): module.fail_json(msg="It is not possible to change an existing service from capacity_provider_strategy to launch_type.") if (existing['loadBalancers'] or []) != loadBalancers: - module.fail_json(msg="It is not possible to update the load balancers of an existing service") + if existing['deploymentController']['type'] != 'CODE_DEPLOY': + module.fail_json(msg="It is not possible to update the load balancers of an existing service") + + if existing.get('deploymentController', {}).get('type', None) == 'CODE_DEPLOY': + task_definition = '' + network_configuration = [] + else: + task_definition = module.params['task_definition'] # update required response = service_mgr.update_service(module.params['name'], module.params['cluster'], - module.params['task_definition'], + task_definition, module.params['desired_count'], deploymentConfiguration, network_configuration, @@ -935,6 +969,7 @@ def main(): module.params['desired_count'], clientToken, role, + deploymentController, deploymentConfiguration, module.params['placement_constraints'], module.params['placement_strategy'],