219 lines
8.5 KiB
Python
219 lines
8.5 KiB
Python
from __future__ import unicode_literals
|
|
import uuid
|
|
|
|
from moto.core import BaseBackend
|
|
from moto.ec2 import ec2_backends
|
|
|
|
|
|
class BaseObject(object):
|
|
def camelCase(self, key):
|
|
words = []
|
|
for i, word in enumerate(key.split('_')):
|
|
if i > 0:
|
|
words.append(word.title())
|
|
else:
|
|
words.append(word)
|
|
return ''.join(words)
|
|
|
|
def gen_response_object(self):
|
|
response_object = self.__dict__.copy()
|
|
for key, value in response_object.items():
|
|
if '_' in key:
|
|
response_object[self.camelCase(key)] = value
|
|
del response_object[key]
|
|
return response_object
|
|
|
|
@property
|
|
def response_object(self):
|
|
return self.gen_response_object()
|
|
|
|
|
|
class Cluster(BaseObject):
|
|
def __init__(self, cluster_name):
|
|
self.active_services_count = 0
|
|
self.arn = 'arn:aws:ecs:us-east-1:012345678910:cluster/{0}'.format(cluster_name)
|
|
self.name = cluster_name
|
|
self.pending_tasks_count = 0
|
|
self.registered_container_instances_count = 0
|
|
self.running_tasks_count = 0
|
|
self.status = 'ACTIVE'
|
|
|
|
@property
|
|
def response_object(self):
|
|
response_object = self.gen_response_object()
|
|
response_object['clusterArn'] = self.arn
|
|
response_object['clusterName'] = self.name
|
|
del response_object['arn'], response_object['name']
|
|
return response_object
|
|
|
|
|
|
class TaskDefinition(BaseObject):
|
|
def __init__(self, family, revision, container_definitions, volumes=None):
|
|
self.family = family
|
|
self.arn = 'arn:aws:ecs:us-east-1:012345678910:task-definition/{0}:{1}'.format(family, revision)
|
|
self.container_definitions = container_definitions
|
|
if volumes is not None:
|
|
self.volumes = volumes
|
|
|
|
@property
|
|
def response_object(self):
|
|
response_object = self.gen_response_object()
|
|
response_object['taskDefinitionArn'] = response_object['arn']
|
|
del response_object['arn']
|
|
return response_object
|
|
|
|
|
|
class Service(BaseObject):
|
|
def __init__(self, cluster, service_name, task_definition, desired_count):
|
|
self.cluster_arn = cluster.arn
|
|
self.arn = 'arn:aws:ecs:us-east-1:012345678910:service/{0}'.format(service_name)
|
|
self.name = service_name
|
|
self.status = 'ACTIVE'
|
|
self.running_count = 0
|
|
self.task_definition = task_definition.arn
|
|
self.desired_count = desired_count
|
|
self.events = []
|
|
self.load_balancers = []
|
|
self.pending_count = 0
|
|
|
|
@property
|
|
def response_object(self):
|
|
response_object = self.gen_response_object()
|
|
del response_object['name'], response_object['arn']
|
|
response_object['serviceName'] = self.name
|
|
response_object['serviceArn'] = self.arn
|
|
return response_object
|
|
|
|
|
|
class EC2ContainerServiceBackend(BaseBackend):
|
|
def __init__(self):
|
|
self.clusters = {}
|
|
self.task_definitions = {}
|
|
self.services = {}
|
|
|
|
def fetch_task_definition(self, task_definition_str):
|
|
task_definition_components = task_definition_str.split(':')
|
|
if len(task_definition_components) == 2:
|
|
family, revision = task_definition_components
|
|
revision = int(revision)
|
|
else:
|
|
family = task_definition_components[0]
|
|
revision = -1
|
|
if family in self.task_definitions and 0 < revision <= len(self.task_definitions[family]):
|
|
return self.task_definitions[family][revision - 1]
|
|
elif family in self.task_definitions and revision == -1:
|
|
return self.task_definitions[family][revision]
|
|
else:
|
|
raise Exception("{0} is not a task_definition".format(task_definition_str))
|
|
|
|
def create_cluster(self, cluster_name):
|
|
cluster = Cluster(cluster_name)
|
|
self.clusters[cluster_name] = cluster
|
|
return cluster
|
|
|
|
def list_clusters(self):
|
|
"""
|
|
maxSize and pagination not implemented
|
|
"""
|
|
return [cluster.arn for cluster in self.clusters.values()]
|
|
|
|
def describe_clusters(self, list_clusters_name=None):
|
|
list_clusters = []
|
|
if list_clusters_name is None:
|
|
if 'default' in self.clusters:
|
|
list_clusters.append(self.clusters['default'].response_object)
|
|
else:
|
|
for cluster in list_clusters_name:
|
|
cluster_name = cluster.split('/')[-1]
|
|
if cluster_name in self.clusters:
|
|
list_clusters.append(self.clusters[cluster_name].response_object)
|
|
else:
|
|
raise Exception("{0} is not a cluster".format(cluster_name))
|
|
return list_clusters
|
|
|
|
def delete_cluster(self, cluster_str):
|
|
cluster_name = cluster_str.split('/')[-1]
|
|
if cluster_name in self.clusters:
|
|
return self.clusters.pop(cluster_name)
|
|
else:
|
|
raise Exception("{0} is not a cluster".format(cluster_name))
|
|
|
|
def register_task_definition(self, family, container_definitions, volumes):
|
|
if family in self.task_definitions:
|
|
revision = len(self.task_definitions[family]) + 1
|
|
else:
|
|
self.task_definitions[family] = []
|
|
revision = 1
|
|
task_definition = TaskDefinition(family, revision, container_definitions, volumes)
|
|
self.task_definitions[family].append(task_definition)
|
|
|
|
return task_definition
|
|
|
|
def list_task_definitions(self):
|
|
"""
|
|
Filtering not implemented
|
|
"""
|
|
task_arns = []
|
|
for task_definition_list in self.task_definitions.values():
|
|
task_arns.extend([task_definition.arn for task_definition in task_definition_list])
|
|
return task_arns
|
|
|
|
def deregister_task_definition(self, task_definition_str):
|
|
task_definition_name = task_definition_str.split('/')[-1]
|
|
family, revision = task_definition_name.split(':')
|
|
revision = int(revision)
|
|
if family in self.task_definitions and 0 < revision <= len(self.task_definitions[family]):
|
|
return self.task_definitions[family].pop(revision - 1)
|
|
else:
|
|
raise Exception("{0} is not a task_definition".format(task_definition_name))
|
|
|
|
def create_service(self, cluster_str, service_name, task_definition_str, desired_count):
|
|
cluster_name = cluster_str.split('/')[-1]
|
|
if cluster_name in self.clusters:
|
|
cluster = self.clusters[cluster_name]
|
|
else:
|
|
raise Exception("{0} is not a cluster".format(cluster_name))
|
|
task_definition = self.fetch_task_definition(task_definition_str)
|
|
desired_count = desired_count if desired_count is not None else 0
|
|
service = Service(cluster, service_name, task_definition, desired_count)
|
|
cluster_service_pair = '{0}:{1}'.format(cluster_name, service_name)
|
|
self.services[cluster_service_pair] = service
|
|
return service
|
|
|
|
def list_services(self, cluster_str):
|
|
cluster_name = cluster_str.split('/')[-1]
|
|
service_arns = []
|
|
for key, value in self.services.items():
|
|
if cluster_name + ':' in key:
|
|
service_arns.append(self.services[key].arn)
|
|
return sorted(service_arns)
|
|
|
|
def update_service(self, cluster_str, service_name, task_definition_str, desired_count):
|
|
cluster_name = cluster_str.split('/')[-1]
|
|
cluster_service_pair = '{0}:{1}'.format(cluster_name, service_name)
|
|
if cluster_service_pair in self.services:
|
|
if task_definition_str is not None:
|
|
task_definition = self.fetch_task_definition(task_definition_str)
|
|
self.services[cluster_service_pair].task_definition = task_definition
|
|
if desired_count is not None:
|
|
self.services[cluster_service_pair].desired_count = desired_count
|
|
return self.services[cluster_service_pair]
|
|
else:
|
|
raise Exception("cluster {0} or service {1} does not exist".format(cluster_name, service_name))
|
|
|
|
def delete_service(self, cluster_name, service_name):
|
|
cluster_service_pair = '{0}:{1}'.format(cluster_name, service_name)
|
|
if cluster_service_pair in self.services:
|
|
service = self.services[cluster_service_pair]
|
|
if service.desired_count > 0:
|
|
raise Exception("Service must have desiredCount=0")
|
|
else:
|
|
return self.services.pop(cluster_service_pair)
|
|
else:
|
|
raise Exception("cluster {0} or service {1} does not exist".format(cluster_name, service_name))
|
|
|
|
|
|
ecs_backends = {}
|
|
for region, ec2_backend in ec2_backends.items():
|
|
ecs_backends[region] = EC2ContainerServiceBackend()
|