Server IP : 85.214.239.14 / Your IP : 3.141.201.92 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /lib/python3/dist-packages/ansible_collections/community/aws/plugins/modules/ |
Upload File : |
#!/usr/bin/python # This file is part of Ansible # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import (absolute_import, division, print_function) __metaclass__ = type DOCUMENTATION = r''' --- module: ecs_taskdefinition version_added: 1.0.0 short_description: register a task definition in ecs description: - Registers or deregisters task definitions in the Amazon Web Services (AWS) EC2 Container Service (ECS). author: - Mark Chance (@Java1Guy) - Alina Buzachis (@alinabuzachis) options: state: description: - State whether the task definition should exist or be deleted. required: true choices: ['present', 'absent'] type: str arn: description: - The ARN of the task description to delete. required: false type: str family: description: - A Name that would be given to the task definition. required: false type: str revision: description: - A revision number for the task definition. required: False type: int force_create: description: - Always create new task definition. required: False type: bool default: false containers: description: - A list of containers definitions. - See U(https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ecs.html) for a complete list of parameters. required: True type: list elements: dict suboptions: name: description: The name of a container. required: False type: str image: description: The image used to start a container. required: False type: str repositoryCredentials: description: The private repository authentication credentials to use. required: False type: dict suboptions: credentialsParameter: description: - The Amazon Resource Name (ARN) of the secret containing the private repository credentials. required: True type: str cpu: description: The number of cpu units reserved for the container. required: False type: int memory: description: The amount (in MiB) of memory to present to the container. required: False type: int memoryReservation: description: The soft limit (in MiB) of memory to reserve for the container. required: False type: int links: description: - Allows containers to communicate with each other without the need for port mappings. - This parameter is only supported if I(network_mode=bridge). required: False type: list elements: str portMappings: description: The list of port mappings for the container. required: False type: list elements: dict suboptions: containerPort: description: The port number on the container that is bound to the user-specified or automatically assigned host port. required: False type: int hostPort: description: The port number on the container instance to reserve for your container. required: False type: int protocol: description: The protocol used for the port mapping. required: False type: str default: tcp choices: ['tcp', 'udp'] essential: description: - If I(essential=True), and the container fails or stops for any reason, all other containers that are part of the task are stopped. required: False type: bool entryPoint: description: The entry point that is passed to the container. required: False type: str command: description: The command that is passed to the container. If there are multiple arguments, each argument is a separated string in the array. required: False type: list elements: str environment: description: The environment variables to pass to a container. required: False type: list elements: dict suboptions: name: description: The name of the key-value pair. required: False type: str value: description: The value of the key-value pair. required: False type: str environmentFiles: description: A list of files containing the environment variables to pass to a container. required: False type: list elements: dict suboptions: value: description: The Amazon Resource Name (ARN) of the Amazon S3 object containing the environment variable file. required: False type: str type: description: The file type to use. The only supported value is C(s3). required: False type: str mountPoints: description: The mount points for data volumes in your container. required: False type: list elements: dict suboptions: sourceVolume: description: The name of the volume to mount. required: False type: str containerPath: description: The path on the container to mount the host volume at. required: False type: str readOnly: description: - If this value is C(True), the container has read-only access to the volume. - If this value is C(False), then the container can write to the volume. required: False default: False type: bool volumesFrom: description: Data volumes to mount from another container. required: False type: list elements: dict suboptions: sourceContainer: description: - The name of another container within the same task definition from which to mount volumes. required: False type: str readOnly: description: - If this value is C(True), the container has read-only access to the volume. - If this value is C(False), then the container can write to the volume. required: False default: False type: bool linuxParameters: description: Linux-specific modifications that are applied to the container, such as Linux kernel capabilities. required: False type: dict suboptions: capabilities: description: - The Linux capabilities for the container that are added to or dropped from the default configuration provided by Docker. required: False type: dict suboptions: add: description: - The Linux capabilities for the container that have been added to the default configuration provided by Docker. - If I(launch_type=FARGATE), this parameter is not supported. required: False type: list choices: ["ALL", "AUDIT_CONTROL", "AUDIT_WRITE", "BLOCK_SUSPEND", "CHOWN", "DAC_OVERRIDE", "DAC_READ_SEARCH", "FOWNER", "FSETID", "IPC_LOCK", "IPC_OWNER", "KILL", "LEASE", "LINUX_IMMUTABLE", "MAC_ADMIN", "MAC_OVERRIDE", "MKNOD", "NET_ADMIN", "NET_BIND_SERVICE", "NET_BROADCAST", "NET_RAW", "SETFCAP", "SETGID", "SETPCAP", "SETUID", "SYS_ADMIN", "SYS_BOOT", "SYS_CHROOT", "SYS_MODULE", "SYS_NICE", "SYS_PACCT", "SYS_PTRACE", "SYS_RAWIO", "SYS_RESOURCE", "SYS_TIME", "SYS_TTY_CONFIG", "SYSLOG", "WAKE_ALARM"] elements: str drop: description: - The Linux capabilities for the container that have been removed from the default configuration provided by Docker. required: False type: list choices: ["ALL", "AUDIT_CONTROL", "AUDIT_WRITE", "BLOCK_SUSPEND", "CHOWN", "DAC_OVERRIDE", "DAC_READ_SEARCH", "FOWNER", "FSETID", "IPC_LOCK", "IPC_OWNER", "KILL", "LEASE", "LINUX_IMMUTABLE", "MAC_ADMIN", "MAC_OVERRIDE", "MKNOD", "NET_ADMIN", "NET_BIND_SERVICE", "NET_BROADCAST", "NET_RAW", "SETFCAP", "SETGID", "SETPCAP", "SETUID", "SYS_ADMIN", "SYS_BOOT", "SYS_CHROOT", "SYS_MODULE", "SYS_NICE", "SYS_PACCT", "SYS_PTRACE", "SYS_RAWIO", "SYS_RESOURCE", "SYS_TIME", "SYS_TTY_CONFIG", "SYSLOG", "WAKE_ALARM"] elements: str devices: description: - Any host devices to expose to the container. - If I(launch_type=FARGATE), this parameter is not supported. required: False type: list elements: dict suboptions: hostPath: description: The path for the device on the host container instance. required: True type: str containerPath: description: The path inside the container at which to expose the host device. required: False type: str permissions: description: The explicit permissions to provide to the container for the device. required: False type: list elements: str initProcessEnabled: description: Run an init process inside the container that forwards signals and reaps processes. required: False type: bool sharedMemorySize: description: - The value for the size (in MiB) of the /dev/shm volume. - If I(launch_type=FARGATE), this parameter is not supported. required: False type: int tmpfs: description: - The container path, mount options, and size (in MiB) of the tmpfs mount. - If I(launch_type=FARGATE), this parameter is not supported. required: False type: list elements: dict suboptions: containerPath: description: The absolute file path where the tmpfs volume is to be mounted. required: True type: str size: description: The size (in MiB) of the tmpfs volume. required: True type: int mountOptions: description: The list of tmpfs volume mount options. required: False type: list choices: ["defaults", "ro", "rw", "suid", "nosuid", "dev", "nodev", "exec", "noexec", "sync", "async", "dirsync", "remount", "mand", "nomand", "atime", "noatime", "diratime", "nodiratime", "bind", "rbind", "unbindable", "runbindable", "private", "rprivate", "shared", "rshared", "slave", "rslave", "relatime", "norelatime", "strictatime", "nostrictatime", "mode", "uid", "gid", "nr_inodes", "nr_blocks", "mpol"] elements: str maxSwap: description: - The total amount of swap memory (in MiB) a container can use. - If I(launch_type=FARGATE), this parameter is not supported. required: False type: int swappiness: description: - This allows you to tune a container's memory swappiness behavior. - If I(launch_type=FARGATE), this parameter is not supported. required: False type: int secrets: description: The secrets to pass to the container. required: False type: list elements: dict suboptions: name: description: The value to set as the environment variable on the container. required: True type: str size: description: The secret to expose to the container. required: True type: str dependsOn: description: - The dependencies defined for container startup and shutdown. - When a dependency is defined for container startup, for container shutdown it is reversed. required: False type: list elements: dict suboptions: containerName: description: The name of a container. type: str required: True condition: description: The dependency condition of the container. type: str required: True choices: ["start", "complete", "success", "healthy"] startTimeout: description: Time duration (in seconds) to wait before giving up on resolving dependencies for a container. required: False type: int stopTimeout: description: Time duration (in seconds) to wait before the container is forcefully killed if it doesn't exit normally on its own. required: False type: int hostname: description: - The hostname to use for your container. - This parameter is not supported if I(network_mode=awsvpc). required: False type: str user: description: - The user to use inside the container. - This parameter is not supported for Windows containers. required: False type: str workingDirectory: description: The working directory in which to run commands inside the container. required: False type: str disableNetworking: description: When this parameter is C(True), networking is disabled within the container. required: False type: bool privileged: description: When this parameter is C(True), the container is given elevated privileges on the host container instance. required: False type: bool readonlyRootFilesystem: description: When this parameter is C(True), the container is given read-only access to its root file system. required: false type: bool dnsServers: description: - A list of DNS servers that are presented to the container. - This parameter is not supported for Windows containers. required: False type: list elements: str dnsSearchDomains: description: - A list of DNS search domains that are presented to the container. - This parameter is not supported for Windows containers. required: False type: list elements: str extraHosts: description: - A list of hostnames and IP address mappings to append to the /etc/hosts file on the container. - This parameter is not supported for Windows containers or tasks that use I(network_mode=awsvpc). required: False type: list elements: dict suboptions: hostname: description: The hostname to use in the /etc/hosts entry. type: str required: False ipAddress: description: The IP address to use in the /etc/hosts entry. type: str required: False dockerSecurityOptions: description: - A list of strings to provide custom labels for SELinux and AppArmor multi-level security systems. - This parameter is not supported for Windows containers. required: False type: list elements: str interactive: description: - When I(interactive=True), it allows to deploy containerized applications that require stdin or a tty to be allocated. required: False type: bool pseudoTerminal: description: When this parameter is C(True), a TTY is allocated. required: False type: bool dockerLabels: description: A key/value map of labels to add to the container. required: False type: dict ulimits: description: - A list of ulimits to set in the container. - This parameter is not supported for Windows containers. required: False type: list elements: dict suboptions: name: description: The type of the ulimit. type: str required: False choices: ['core', 'cpu', 'data', 'fsize', 'locks', 'memlock', 'msgqueue', 'nice', 'nofile', 'nproc', 'rss', 'rtprio', 'rttime', 'sigpending', 'stack'] softLimit: description: The soft limit for the ulimit type. type: int required: False hardLimit: description: The hard limit for the ulimit type. type: int required: False logConfiguration: description: The log configuration specification for the container. required: False type: dict suboptions: logDriver: description: - The log driver to use for the container. - For tasks on AWS Fargate, the supported log drivers are C(awslogs), C(splunk), and C(awsfirelens). - For tasks hosted on Amazon EC2 instances, the supported log drivers are C(awslogs), C(fluentd), C(gelf), C(json-file), C(journald), C(logentries), C(syslog), C(splunk), and C(awsfirelens). type: str required: False options: description: The configuration options to send to the log driver. required: False type: str secretOptions: description: The secrets to pass to the log configuration. required: False type: list elements: dict suboptions: name: description: The name of the secret. type: str required: False valueFrom: description: The secret to expose to the container. type: str required: False healthCheck: description: The health check command and associated configuration parameters for the container. required: False type: dict suboptions: command: description: - A string array representing the command that the container runs to determine if it is healthy. - > The string array must start with CMD to run the command arguments directly, or CMD-SHELL to run the command with the container's default shell. - An exit code of 0 indicates success, and non-zero exit code indicates failure. required: False type: list elements: str interval: description: - The time period in seconds between each health check execution. - You may specify between 5 and 300 seconds. The default value is 30 seconds. required: False type: int default: 30 retries: description: - The number of times to retry a failed health check before the container is considered unhealthy. - You may specify between 1 and 10 retries. The default value is 3. required: False type: int default: 3 startPeriod: description: - > The optional grace period to provide containers time to bootstrap before failed health checks count towards the maximum number of retries. - You can specify between 0 and 300 seconds. By default, the startPeriod is disabled. - > Note: If a health check succeeds within the startPeriod, then the container is considered healthy and any subsequent failures count toward the maximum number of retries. required: False type: int timeout: description: - The time period in seconds to wait for a health check to succeed before it is considered a failure. - You may specify between 2 and 60 seconds. The default value is 5. required: False type: int default: 5 systemControls: description: A list of namespaced kernel parameters to set in the container. required: False type: list elements: dict suboptions: namespace: description: The namespaced kernel parameter to set a C(value) for. type: str value: description: The value for the namespaced kernel parameter that's specified in C(namespace). type: str resourceRequirements: description: - The type and amount of a resource to assign to a container. - The only supported resources are C(GPU) and C(InferenceAccelerator). required: False type: list elements: dict suboptions: value: description: The value for the specified resource type. type: str type: description: The type of resource to assign to a container. type: str choices: ['GPU', 'InferenceAccelerator'] firelensConfiguration: description: - The FireLens configuration for the container. - This is used to specify and configure a log router for container logs. required: False type: dict suboptions: type: description: - The log router to use. The valid values are C(fluentd) or C(fluentbit). required: False type: str choices: - fluentd - fluentbit options: description: - The options to use when configuring the log router. - This field is optional and can be used to specify a custom configuration file or to add additional metadata, such as the task, task definition, cluster, and container instance details to the log event. - If specified, the syntax to use is C({"enable-ecs-log-metadata":"true|false","config-file-type:"s3|file","config-file-value":"arn:aws:s3:::mybucket/fluent.conf|filepath"}). - For more information, see U(https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_firelens.html#firelens-taskdef). required: False type: dict network_mode: description: - The Docker networking mode to use for the containers in the task. - Windows containers must use I(network_mode=default), which will utilize docker NAT networking. - Setting I(network_mode=default) for a Linux container will use C(bridge) mode. required: false default: bridge choices: [ 'default', 'bridge', 'host', 'none', 'awsvpc' ] type: str task_role_arn: description: - The Amazon Resource Name (ARN) of the IAM role that containers in this task can assume. All containers in this task are granted the permissions that are specified in this role. required: false type: str default: '' execution_role_arn: description: - The Amazon Resource Name (ARN) of the task execution role that the Amazon ECS container agent and the Docker daemon can assume. required: false type: str default: '' volumes: description: - A list of names of volumes to be attached. required: False type: list elements: dict suboptions: name: type: str description: The name of the volume. required: true launch_type: description: - The launch type on which to run your task. required: false type: str choices: ["EC2", "FARGATE"] cpu: description: - The number of cpu units used by the task. If I(launch_type=EC2), this field is optional and any value can be used. - If I(launch_type=FARGATE), this field is required and you must use one of C(256), C(512), C(1024), C(2048), C(4096). required: false type: str memory: description: - The amount (in MiB) of memory used by the task. If I(launch_type=EC2), this field is optional and any value can be used. - If I(launch_type=FARGATE), this field is required and is limited by the CPU. required: false type: str placement_constraints: version_added: 2.1.0 description: - Placement constraint objects to use for the task. - You can specify a maximum of 10 constraints per task. - Task placement constraints are not supported for tasks run on Fargate. required: false type: list elements: dict suboptions: type: description: The type of constraint. type: str expression: description: A cluster query language expression to apply to the constraint. type: str extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 - amazon.aws.boto3 ''' EXAMPLES = r''' - name: Create task definition community.aws.ecs_taskdefinition: containers: - name: simple-app cpu: 10 essential: true image: "httpd:2.4" memory: 300 mountPoints: - containerPath: /usr/local/apache2/htdocs sourceVolume: my-vol portMappings: - containerPort: 80 hostPort: 80 logConfiguration: logDriver: awslogs options: awslogs-group: /ecs/test-cluster-taskdef awslogs-region: us-west-2 awslogs-stream-prefix: ecs - name: busybox command: - > /bin/sh -c "while true; do echo '<html><head><title>Amazon ECS Sample App</title></head><body><div><h1>Amazon ECS Sample App</h1><h2>Congratulations! </h2><p>Your application is now running on a container in Amazon ECS.</p>' > top; /bin/date > date ; echo '</div></body></html>' > bottom; cat top date bottom > /usr/local/apache2/htdocs/index.html ; sleep 1; done" cpu: 10 entryPoint: - sh - "-c" essential: false image: busybox memory: 200 volumesFrom: - sourceContainer: simple-app volumes: - name: my-vol family: test-cluster-taskdef state: present register: task_output - name: Create task definition community.aws.ecs_taskdefinition: family: nginx containers: - name: nginx essential: true image: "nginx" portMappings: - containerPort: 8080 hostPort: 8080 cpu: 512 memory: 1024 state: present - name: Create task definition community.aws.ecs_taskdefinition: family: nginx containers: - name: nginx essential: true image: "nginx" portMappings: - containerPort: 8080 hostPort: 8080 launch_type: FARGATE cpu: 512 memory: 1024 state: present network_mode: awsvpc - name: Create task definition community.aws.ecs_taskdefinition: family: nginx containers: - name: nginx essential: true image: "nginx" portMappings: - containerPort: 8080 hostPort: 8080 cpu: 512 memory: 1024 dependsOn: - containerName: "simple-app" condition: "start" # Create Task Definition with Environment Variables and Secrets - name: Create task definition community.aws.ecs_taskdefinition: family: nginx containers: - name: nginx essential: true image: "nginx" environment: - name: "PORT" value: "8080" secrets: # For variables stored in Secrets Manager - name: "NGINX_HOST" valueFrom: "arn:aws:secretsmanager:us-west-2:123456789012:secret:nginx/NGINX_HOST" # For variables stored in Parameter Store - name: "API_KEY" valueFrom: "arn:aws:ssm:us-west-2:123456789012:parameter/nginx/API_KEY" launch_type: FARGATE cpu: 512 memory: 1GB state: present network_mode: awsvpc # Create Task Definition with health check - name: Create task definition community.aws.ecs_taskdefinition: family: nginx containers: - name: nginx essential: true image: "nginx" portMappings: - containerPort: 8080 hostPort: 8080 cpu: 512 memory: 1024 healthCheck: command: - CMD-SHELL - /app/healthcheck.py interval: 60 retries: 3 startPeriod: 15 timeout: 15 state: present ''' RETURN = r''' taskdefinition: description: a reflection of the input parameters type: dict returned: always ''' try: import botocore except ImportError: pass # caught by AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry class EcsTaskManager: """Handles ECS Tasks""" def __init__(self, module): self.module = module self.ecs = module.client('ecs', AWSRetry.jittered_backoff()) def describe_task(self, task_name): try: response = self.ecs.describe_task_definition(aws_retry=True, taskDefinition=task_name) return response['taskDefinition'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: return None def register_task(self, family, task_role_arn, execution_role_arn, network_mode, container_definitions, volumes, launch_type, cpu, memory, placement_constraints): validated_containers = [] # Ensures the number parameters are int as required by the AWS SDK for container in container_definitions: for param in ('memory', 'cpu', 'memoryReservation', 'startTimeout', 'stopTimeout'): if param in container: container[param] = int(container[param]) if 'portMappings' in container: for port_mapping in container['portMappings']: for port in ('hostPort', 'containerPort'): if port in port_mapping: port_mapping[port] = int(port_mapping[port]) if network_mode == 'awsvpc' and 'hostPort' in port_mapping: if port_mapping['hostPort'] != port_mapping.get('containerPort'): self.module.fail_json(msg="In awsvpc network mode, host port must be set to the same as " "container port or not be set") if 'linuxParameters' in container: for linux_param in container.get('linuxParameters'): if linux_param == 'tmpfs': for tmpfs_param in container['linuxParameters']['tmpfs']: if 'size' in tmpfs_param: tmpfs_param['size'] = int(tmpfs_param['size']) for param in ('maxSwap', 'swappiness', 'sharedMemorySize'): if param in linux_param: container['linuxParameters'][param] = int(container['linuxParameters'][param]) if 'ulimits' in container: for limits_mapping in container['ulimits']: for limit in ('softLimit', 'hardLimit'): if limit in limits_mapping: limits_mapping[limit] = int(limits_mapping[limit]) validated_containers.append(container) params = dict( family=family, taskRoleArn=task_role_arn, containerDefinitions=container_definitions, volumes=volumes ) if network_mode != 'default': params['networkMode'] = network_mode if cpu: params['cpu'] = cpu if memory: params['memory'] = memory if launch_type: params['requiresCompatibilities'] = [launch_type] if execution_role_arn: params['executionRoleArn'] = execution_role_arn if placement_constraints: params['placementConstraints'] = placement_constraints try: response = self.ecs.register_task_definition(aws_retry=True, **params) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: self.module.fail_json_aws(e, msg="Failed to register task") return response['taskDefinition'] def describe_task_definitions(self, family): data = { "taskDefinitionArns": [], "nextToken": None } def fetch(): # Boto3 is weird about params passed, so only pass nextToken if we have a value params = { 'familyPrefix': family } if data['nextToken']: params['nextToken'] = data['nextToken'] result = self.ecs.list_task_definitions(**params) data['taskDefinitionArns'] += result['taskDefinitionArns'] data['nextToken'] = result.get('nextToken', None) return data['nextToken'] is not None # Fetch all the arns, possibly across multiple pages while fetch(): pass # Return the full descriptions of the task definitions, sorted ascending by revision return list( sorted( [self.ecs.describe_task_definition(taskDefinition=arn)['taskDefinition'] for arn in data['taskDefinitionArns']], key=lambda td: td['revision'] ) ) def deregister_task(self, taskArn): response = self.ecs.deregister_task_definition(taskDefinition=taskArn) return response['taskDefinition'] def main(): argument_spec = dict( state=dict(required=True, choices=['present', 'absent']), arn=dict(required=False, type='str'), family=dict(required=False, type='str'), revision=dict(required=False, type='int'), force_create=dict(required=False, default=False, type='bool'), containers=dict(required=True, type='list', elements='dict'), network_mode=dict(required=False, default='bridge', choices=['default', 'bridge', 'host', 'none', 'awsvpc'], type='str'), task_role_arn=dict(required=False, default='', type='str'), execution_role_arn=dict(required=False, default='', type='str'), volumes=dict(required=False, type='list', elements='dict'), launch_type=dict(required=False, choices=['EC2', 'FARGATE']), cpu=dict(), memory=dict(required=False, type='str'), placement_constraints=dict(required=False, type='list', elements='dict', options=dict(type=dict(type='str'), expression=dict(type='str'))), ) module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True, required_if=[('launch_type', 'FARGATE', ['cpu', 'memory'])] ) task_to_describe = None task_mgr = EcsTaskManager(module) results = dict(changed=False) if module.params['state'] == 'present': if 'containers' not in module.params or not module.params['containers']: module.fail_json(msg="To use task definitions, a list of containers must be specified") if 'family' not in module.params or not module.params['family']: module.fail_json(msg="To use task definitions, a family must be specified") network_mode = module.params['network_mode'] launch_type = module.params['launch_type'] placement_constraints = module.params['placement_constraints'] if launch_type == 'FARGATE': if network_mode != 'awsvpc': module.fail_json(msg="To use FARGATE launch type, network_mode must be awsvpc") if placement_constraints: module.fail_json(msg="Task placement constraints are not supported for tasks run on Fargate") for container in module.params['containers']: if container.get('links') and network_mode == 'awsvpc': module.fail_json(msg='links parameter is not supported if network mode is awsvpc.') for environment in container.get('environment', []): environment['value'] = environment['value'] for environment_file in container.get('environmentFiles', []): if environment_file['type'] != 's3': module.fail_json(msg='The only supported value for environmentFiles is s3.') for linux_param in container.get('linuxParameters', {}): if linux_param == 'maxSwap' and launch_type == 'FARGATE': module.fail_json(msg='devices parameter is not supported with the FARGATE launch type.') if linux_param == 'maxSwap' and launch_type == 'FARGATE': module.fail_json(msg='maxSwap parameter is not supported with the FARGATE launch type.') elif linux_param == 'maxSwap' and int(container['linuxParameters']['maxSwap']) < 0: module.fail_json(msg='Accepted values for maxSwap are 0 or any positive integer.') if ( linux_param == 'swappiness' and (int(container['linuxParameters']['swappiness']) < 0 or int(container['linuxParameters']['swappiness']) > 100) ): module.fail_json(msg='Accepted values for swappiness are whole numbers between 0 and 100.') if linux_param == 'sharedMemorySize' and launch_type == 'FARGATE': module.fail_json(msg='sharedMemorySize parameter is not supported with the FARGATE launch type.') if linux_param == 'tmpfs' and launch_type == 'FARGATE': module.fail_json(msg='tmpfs parameter is not supported with the FARGATE launch type.') if container.get('hostname') and network_mode == 'awsvpc': module.fail_json(msg='hostname parameter is not supported when the awsvpc network mode is used.') if container.get('extraHosts') and network_mode == 'awsvpc': module.fail_json(msg='extraHosts parameter is not supported when the awsvpc network mode is used.') family = module.params['family'] existing_definitions_in_family = task_mgr.describe_task_definitions(module.params['family']) if 'revision' in module.params and module.params['revision']: # The definition specifies revision. We must guarantee that an active revision of that number will result from this. revision = int(module.params['revision']) # A revision has been explicitly specified. Attempt to locate a matching revision tasks_defs_for_revision = [td for td in existing_definitions_in_family if td['revision'] == revision] existing = tasks_defs_for_revision[0] if len(tasks_defs_for_revision) > 0 else None if existing and existing['status'] != "ACTIVE": # We cannot reactivate an inactive revision module.fail_json(msg="A task in family '%s' already exists for revision %d, but it is inactive" % (family, revision)) elif not existing: if not existing_definitions_in_family and revision != 1: module.fail_json(msg="You have specified a revision of %d but a created revision would be 1" % revision) elif existing_definitions_in_family and existing_definitions_in_family[-1]['revision'] + 1 != revision: module.fail_json(msg="You have specified a revision of %d but a created revision would be %d" % (revision, existing_definitions_in_family[-1]['revision'] + 1)) else: existing = None def _right_has_values_of_left(left, right): # Make sure the values are equivalent for everything left has for k, v in left.items(): if not ((not v and (k not in right or not right[k])) or (k in right and v == right[k])): # We don't care about list ordering because ECS can change things if isinstance(v, list) and k in right: left_list = v right_list = right[k] or [] if len(left_list) != len(right_list): return False for list_val in left_list: if list_val not in right_list: # if list_val is the port mapping, the key 'protocol' may be absent (but defaults to 'tcp') # fill in that default if absent and see if it is in right_list then if isinstance(list_val, dict) and not list_val.get('protocol'): modified_list_val = dict(list_val) modified_list_val.update(protocol='tcp') if modified_list_val in right_list: continue else: return False # Make sure right doesn't have anything that left doesn't for k, v in right.items(): if v and k not in left: # 'essential' defaults to True when not specified if k == 'essential' and v is True: pass else: return False return True def _task_definition_matches(requested_volumes, requested_containers, requested_task_role_arn, requested_launch_type, existing_task_definition): if td['status'] != "ACTIVE": return None if requested_task_role_arn != td.get('taskRoleArn', ""): return None if requested_launch_type is not None and requested_launch_type not in td.get('requiresCompatibilities', []): return None existing_volumes = td.get('volumes', []) or [] if len(requested_volumes) != len(existing_volumes): # Nope. return None if len(requested_volumes) > 0: for requested_vol in requested_volumes: found = False for actual_vol in existing_volumes: if _right_has_values_of_left(requested_vol, actual_vol): found = True break if not found: return None existing_containers = td.get('containerDefinitions', []) or [] if len(requested_containers) != len(existing_containers): # Nope. return None for requested_container in requested_containers: found = False for actual_container in existing_containers: if _right_has_values_of_left(requested_container, actual_container): found = True break if not found: return None return existing_task_definition # No revision explicitly specified. Attempt to find an active, matching revision that has all the properties requested for td in existing_definitions_in_family: requested_volumes = module.params['volumes'] or [] requested_containers = module.params['containers'] or [] requested_task_role_arn = module.params['task_role_arn'] requested_launch_type = module.params['launch_type'] existing = _task_definition_matches(requested_volumes, requested_containers, requested_task_role_arn, requested_launch_type, td) if existing: break if existing and not module.params.get('force_create'): # Awesome. Have an existing one. Nothing to do. results['taskdefinition'] = existing else: if not module.check_mode: # Doesn't exist. create it. volumes = module.params.get('volumes', []) or [] results['taskdefinition'] = task_mgr.register_task(module.params['family'], module.params['task_role_arn'], module.params['execution_role_arn'], module.params['network_mode'], module.params['containers'], volumes, module.params['launch_type'], module.params['cpu'], module.params['memory'], module.params['placement_constraints'],) results['changed'] = True elif module.params['state'] == 'absent': # When de-registering a task definition, we can specify the ARN OR the family and revision. if module.params['state'] == 'absent': if 'arn' in module.params and module.params['arn'] is not None: task_to_describe = module.params['arn'] elif 'family' in module.params and module.params['family'] is not None and 'revision' in module.params and \ module.params['revision'] is not None: task_to_describe = module.params['family'] + ":" + str(module.params['revision']) else: module.fail_json(msg="To use task definitions, an arn or family and revision must be specified") existing = task_mgr.describe_task(task_to_describe) if not existing: pass else: # It exists, so we should delete it and mark changed. Return info about the task definition deleted results['taskdefinition'] = existing if 'status' in existing and existing['status'] == "INACTIVE": results['changed'] = False else: if not module.check_mode: task_mgr.deregister_task(task_to_describe) results['changed'] = True module.exit_json(**results) if __name__ == '__main__': main()