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

Policies for ecs_service, ecs_cluster, ecs_taskdefinition, ecs_task #210

Closed
wants to merge 15 commits into from
33 changes: 33 additions & 0 deletions aws/policy/paas.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,36 @@ Statement:
StringLike:
lambda:FunctionArn:
- arn:aws:lambda:{{ aws_region }}:{{ aws_account_id }}:function:*

- Sid: AllowGlobalUnrestrictedResourceActionsWhichIncurFees
Effect: Allow
Action:
- ecs:CreateCluster
Resource: "*"

- Sid: AllowGlobalUnrestrictedResourceActionsWhichIncurNoFees
Effect: Allow
Action:
- ecs:Describe*
- ecs:List*
- ecs:TagResource
- ecs:UntagResource
- ecs:PutAccountSetting
- ecs:RegisterTaskDefinition
- ecs:DeregisterTaskDefinition
Resource:
- "*"

- Sid: AllowGlobalRestrictedResourceActionsWhichIncurFees
Effect: Allow
Action:
- ecs:RunTask
- ecs:StartTask
- ecs:StopTask
- ecs:DeleteCluster
- ecs:CreateService
- ecs:DeleteService
- ecs:UpdateService
- ecs:UpdateCluster
Resource:
- 'arn:aws:ecs:{{ aws_region }}:{{ aws_account_id }}:*'
128 changes: 128 additions & 0 deletions aws/terminator/paas.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,131 @@ def terminate(self):
else:
# delete streaming distribution
self.client.delete_streaming_distribution(Id=self.Id, IfMatch=ETag)


class Ecs(DbTerminator):
@property
def age_limit(self):
return datetime.timedelta(minutes=20)

@property
def name(self):
return self.instance['clusterName']

@staticmethod
def create(credentials):
def _paginate_cluster_results(client):
names = client.get_paginator('list_clusters').paginate(
PaginationConfig={
'PageSize': 100,
}
).build_full_result()['clusterArns']

if not names:
return []

return client.describe_clusters(clusters=names)['clusters']

return Terminator._create(credentials, Ecs, 'ecs', _paginate_cluster_results)

def terminate(self):
def _paginate_task_results(container_instance=None):
params = {
'cluster': self.name,
'PaginationConfig': {
'PageSize': 100,
}
}

if container_instance:
params['containerInstance'] = container_instance

names = self.client.get_paginator('list_tasks').paginate(
**params
).build_full_result()['taskArns']

return [] if not names else names

def _paginate_task_definition_results():
names = self.client.get_paginator('list_task_definitions').paginate(
PaginationConfig={
'PageSize': 100,
}
).build_full_result()['taskDefinitionArns']

return [] if not names else names

def _paginate_container_instance_results():
names = self.client.get_paginator('list_container_instances').paginate(
cluster=self.name,
PaginationConfig={
'PageSize': 100,
}
).build_full_result()['containerInstanceArns']

return [] if not names else names

def _paginate_service_results():
names = self.client.get_paginator('list_services').paginate(
cluster=self.name,
PaginationConfig={
'PageSize': 100,
}
).build_full_result()['serviceArns']

return [] if not names else names

# If there are running services, delete them first
services = _paginate_service_results()
for each in services:
self.client.delete_service(cluster=self.name, service=each, force=True)

# Deregister container instances and stop any running task
container_instances = _paginate_container_instance_results()
for each in container_instances:
self.client.deregister_container_instance(containerInstance=each['containerInstanceArn'], force=True)

# Deregister task definitions
task_definitions = _paginate_task_definition_results()
for each in task_definitions:
self.client.deregister_task_definition(taskDefinition=each)

# Stop all the tasks
tasks = _paginate_task_results()
for each in tasks:
self.client.stop_task(cluster=self.name, task=each)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before deleting the cluster, you will also need to delete all the tasks. In the case of fargate, there are no container instances and you may have tasks running that aren't managed by a service. I'm also not clear on whether using force=True when deleting a service also deletes the tasks or just orphans them. Either way, there should be a step that stops all tasks.

# Delete cluster
try:
self.client.delete_cluster(cluster=self.name)
except (self.client.exceptions.ClusterContainsServicesException, self.client.exceptions.ClusterContainsTasksException):
pass


class EcsCluster(DbTerminator):
@property
def age_limit(self):
return datetime.timedelta(minutes=30)

@property
def name(self):
return self.instance['clusterName']

@staticmethod
def create(credentials):
def _paginate_cluster_results(client):
names = client.get_paginator('list_clusters').paginate(
PaginationConfig={
'PageSize': 100,
}
).build_full_result()['clusterArns']

if not names:
return []

return client.describe_clusters(clusters=names)['clusters']

return Terminator._create(credentials, EcsCluster, 'ecs', _paginate_cluster_results)

def terminate(self):
self.client.delete_cluster(cluster=self.name)