Merge pull request #897 from gjtempleton/TaskDraining
ContainerInstance deregistration
This commit is contained in:
commit
df84675ae6
@ -45,3 +45,4 @@ Moto is written by Steve Pulec with contributions from:
|
||||
* [Tom Viner](https://github.com/tomviner)
|
||||
* [Justin Wiley](https://github.com/SectorNine50)
|
||||
* [Adam Stauffer](https://github.com/adamstauffer)
|
||||
* [Guy Templeton](https://github.com/gjtempleton)
|
||||
|
@ -262,8 +262,9 @@ class ContainerInstance(BaseObject):
|
||||
|
||||
def __init__(self, ec2_instance_id):
|
||||
self.ec2_instance_id = ec2_instance_id
|
||||
self.agent_connected = True
|
||||
self.status = 'ACTIVE'
|
||||
self.registeredResources = [
|
||||
self.registered_resources = [
|
||||
{'doubleValue': 0.0,
|
||||
'integerValue': 4096,
|
||||
'longValue': 0,
|
||||
@ -286,11 +287,10 @@ class ContainerInstance(BaseObject):
|
||||
'name': 'PORTS_UDP',
|
||||
'stringSetValue': [],
|
||||
'type': 'STRINGSET'}]
|
||||
self.agentConnected = True
|
||||
self.containerInstanceArn = "arn:aws:ecs:us-east-1:012345678910:container-instance/{0}".format(
|
||||
self.container_instance_arn = "arn:aws:ecs:us-east-1:012345678910:container-instance/{0}".format(
|
||||
str(uuid.uuid1()))
|
||||
self.pendingTaskCount = 0
|
||||
self.remainingResources = [
|
||||
self.pending_task_count = 0
|
||||
self.remaining_resources = [
|
||||
{'doubleValue': 0.0,
|
||||
'integerValue': 4096,
|
||||
'longValue': 0,
|
||||
@ -314,18 +314,17 @@ class ContainerInstance(BaseObject):
|
||||
'stringSetValue': [],
|
||||
'type': 'STRINGSET'}
|
||||
]
|
||||
self.runningTaskCount = 0
|
||||
self.versionInfo = {
|
||||
self.running_task_count = 0
|
||||
self.version_info = {
|
||||
'agentVersion': "1.0.0",
|
||||
'agentHash': '4023248',
|
||||
'dockerVersion': 'DockerVersion: 1.5.0'
|
||||
}
|
||||
|
||||
@property
|
||||
def response_object(self):
|
||||
response_object = self.gen_response_object()
|
||||
del response_object['name'], response_object['arn']
|
||||
return response_object
|
||||
@property
|
||||
def response_object(self):
|
||||
response_object = self.gen_response_object()
|
||||
return response_object
|
||||
|
||||
|
||||
class ContainerInstanceFailure(BaseObject):
|
||||
@ -456,7 +455,7 @@ class EC2ContainerServiceBackend(BaseBackend):
|
||||
placed_count = 0
|
||||
for container_instance in active_container_instances:
|
||||
container_instance = self.container_instances[cluster_name][container_instance]
|
||||
container_instance_arn = container_instance.containerInstanceArn
|
||||
container_instance_arn = container_instance.container_instance_arn
|
||||
try_to_place = True
|
||||
while try_to_place:
|
||||
can_be_placed, message = self._can_be_placed(container_instance, resource_requirements)
|
||||
@ -497,7 +496,7 @@ class EC2ContainerServiceBackend(BaseBackend):
|
||||
remaining_cpu = 0
|
||||
remaining_memory = 0
|
||||
reserved_ports = []
|
||||
for resource in container_instance.remainingResources:
|
||||
for resource in container_instance.remaining_resources:
|
||||
if resource.get("name") == "CPU":
|
||||
remaining_cpu = resource.get("integerValue")
|
||||
elif resource.get("name") == "MEMORY":
|
||||
@ -534,7 +533,7 @@ class EC2ContainerServiceBackend(BaseBackend):
|
||||
container_instance = self.container_instances[cluster_name][
|
||||
container_instance_id
|
||||
]
|
||||
task = Task(cluster, task_definition, container_instance.containerInstanceArn,
|
||||
task = Task(cluster, task_definition, container_instance.container_instance_arn,
|
||||
resource_requirements, overrides or {}, started_by or '')
|
||||
tasks.append(task)
|
||||
self.update_container_instance_resources(container_instance, resource_requirements)
|
||||
@ -670,10 +669,11 @@ class EC2ContainerServiceBackend(BaseBackend):
|
||||
container_instance = ContainerInstance(ec2_instance_id)
|
||||
if not self.container_instances.get(cluster_name):
|
||||
self.container_instances[cluster_name] = {}
|
||||
container_instance_id = container_instance.containerInstanceArn.split(
|
||||
container_instance_id = container_instance.container_instance_arn.split(
|
||||
'/')[-1]
|
||||
self.container_instances[cluster_name][
|
||||
container_instance_id] = container_instance
|
||||
self.clusters[cluster_name].registered_container_instances_count += 1
|
||||
return container_instance
|
||||
|
||||
def list_container_instances(self, cluster_str):
|
||||
@ -681,7 +681,7 @@ class EC2ContainerServiceBackend(BaseBackend):
|
||||
container_instances_values = self.container_instances.get(
|
||||
cluster_name, {}).values()
|
||||
container_instances = [
|
||||
ci.containerInstanceArn for ci in container_instances_values]
|
||||
ci.container_instance_arn for ci in container_instances_values]
|
||||
return sorted(container_instances)
|
||||
|
||||
def describe_container_instances(self, cluster_str, list_container_instance_ids):
|
||||
@ -726,7 +726,7 @@ class EC2ContainerServiceBackend(BaseBackend):
|
||||
resource_multiplier = 1
|
||||
if removing:
|
||||
resource_multiplier = -1
|
||||
for resource in container_instance.remainingResources:
|
||||
for resource in container_instance.remaining_resources:
|
||||
if resource.get("name") == "CPU":
|
||||
resource["integerValue"] -= task_resources.get('CPU') * resource_multiplier
|
||||
elif resource.get("name") == "MEMORY":
|
||||
@ -737,8 +737,33 @@ class EC2ContainerServiceBackend(BaseBackend):
|
||||
resource["stringSetValue"].remove(str(port))
|
||||
else:
|
||||
resource["stringSetValue"].append(str(port))
|
||||
container_instance.running_task_count += resource_multiplier * 1
|
||||
|
||||
def deregister_container_instance(self, cluster_str, container_instance_str):
|
||||
def deregister_container_instance(self, cluster_str, container_instance_str, force):
|
||||
failures = []
|
||||
cluster_name = cluster_str.split('/')[-1]
|
||||
if cluster_name not in self.clusters:
|
||||
raise Exception("{0} is not a cluster".format(cluster_name))
|
||||
container_instance_id = container_instance_str.split('/')[-1]
|
||||
container_instance = self.container_instances[cluster_name].get(container_instance_id)
|
||||
if container_instance is None:
|
||||
raise Exception("{0} is not a container id in the cluster")
|
||||
if not force and container_instance.running_task_count > 0:
|
||||
raise Exception("Found running tasks on the instance.")
|
||||
# Currently assume that people might want to do something based around deregistered instances
|
||||
# with tasks left running on them - but nothing if no tasks were running already
|
||||
elif force and container_instance.running_task_count > 0:
|
||||
if not self.container_instances.get('orphaned'):
|
||||
self.container_instances['orphaned'] = {}
|
||||
self.container_instances['orphaned'][container_instance_id] = container_instance
|
||||
del(self.container_instances[cluster_name][container_instance_id])
|
||||
self._respond_to_cluster_state_update(cluster_str)
|
||||
return container_instance, failures
|
||||
|
||||
def _respond_to_cluster_state_update(self, cluster_str):
|
||||
cluster_name = cluster_str.split('/')[-1]
|
||||
if cluster_name not in self.clusters:
|
||||
raise Exception("{0} is not a cluster".format(cluster_name))
|
||||
pass
|
||||
|
||||
|
||||
|
@ -203,6 +203,19 @@ class EC2ContainerServiceResponse(BaseResponse):
|
||||
'containerInstance': container_instance.response_object
|
||||
})
|
||||
|
||||
def deregister_container_instance(self):
|
||||
cluster_str = self._get_param('cluster')
|
||||
if not cluster_str:
|
||||
cluster_str = 'default'
|
||||
container_instance_str = self._get_param('containerInstance')
|
||||
force = self._get_param('force')
|
||||
container_instance, failures = self.ecs_backend.deregister_container_instance(
|
||||
cluster_str, container_instance_str, force
|
||||
)
|
||||
return json.dumps({
|
||||
'containerInstance': container_instance.response_object
|
||||
})
|
||||
|
||||
def list_container_instances(self):
|
||||
cluster_str = self._get_param('cluster')
|
||||
container_instance_arns = self.ecs_backend.list_container_instances(
|
||||
|
@ -11,6 +11,7 @@ from uuid import UUID
|
||||
from moto import mock_cloudformation
|
||||
from moto import mock_ecs
|
||||
from moto import mock_ec2
|
||||
from nose.tools import assert_raises
|
||||
|
||||
|
||||
@mock_ecs
|
||||
@ -548,6 +549,92 @@ def test_register_container_instance():
|
||||
'dockerVersion'].should.equal('DockerVersion: 1.5.0')
|
||||
|
||||
|
||||
@mock_ec2
|
||||
@mock_ecs
|
||||
def test_deregister_container_instance():
|
||||
ecs_client = boto3.client('ecs', region_name='us-east-1')
|
||||
ec2 = boto3.resource('ec2', region_name='us-east-1')
|
||||
|
||||
test_cluster_name = 'test_ecs_cluster'
|
||||
|
||||
_ = ecs_client.create_cluster(
|
||||
clusterName=test_cluster_name
|
||||
)
|
||||
|
||||
test_instance = ec2.create_instances(
|
||||
ImageId="ami-1234abcd",
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
)[0]
|
||||
|
||||
instance_id_document = json.dumps(
|
||||
ec2_utils.generate_instance_identity_document(test_instance)
|
||||
)
|
||||
|
||||
response = ecs_client.register_container_instance(
|
||||
cluster=test_cluster_name,
|
||||
instanceIdentityDocument=instance_id_document
|
||||
)
|
||||
container_instance_id = response['containerInstance']['containerInstanceArn']
|
||||
response = ecs_client.deregister_container_instance(
|
||||
cluster=test_cluster_name,
|
||||
containerInstance=container_instance_id
|
||||
)
|
||||
container_instances_response = ecs_client.list_container_instances(
|
||||
cluster=test_cluster_name
|
||||
)
|
||||
len(container_instances_response['containerInstanceArns']).should.equal(0)
|
||||
|
||||
response = ecs_client.register_container_instance(
|
||||
cluster=test_cluster_name,
|
||||
instanceIdentityDocument=instance_id_document
|
||||
)
|
||||
container_instance_id = response['containerInstance']['containerInstanceArn']
|
||||
_ = ecs_client.register_task_definition(
|
||||
family='test_ecs_task',
|
||||
containerDefinitions=[
|
||||
{
|
||||
'name': 'hello_world',
|
||||
'image': 'docker/hello-world:latest',
|
||||
'cpu': 1024,
|
||||
'memory': 400,
|
||||
'essential': True,
|
||||
'environment': [{
|
||||
'name': 'AWS_ACCESS_KEY_ID',
|
||||
'value': 'SOME_ACCESS_KEY'
|
||||
}],
|
||||
'logConfiguration': {'logDriver': 'json-file'}
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
response = ecs_client.start_task(
|
||||
cluster='test_ecs_cluster',
|
||||
taskDefinition='test_ecs_task',
|
||||
overrides={},
|
||||
containerInstances=[container_instance_id],
|
||||
startedBy='moto'
|
||||
)
|
||||
with assert_raises(Exception) as e:
|
||||
ecs_client.deregister_container_instance(
|
||||
cluster=test_cluster_name,
|
||||
containerInstance=container_instance_id
|
||||
).should.have.raised(Exception)
|
||||
container_instances_response = ecs_client.list_container_instances(
|
||||
cluster=test_cluster_name
|
||||
)
|
||||
len(container_instances_response['containerInstanceArns']).should.equal(1)
|
||||
ecs_client.deregister_container_instance(
|
||||
cluster=test_cluster_name,
|
||||
containerInstance=container_instance_id,
|
||||
force=True
|
||||
)
|
||||
container_instances_response = ecs_client.list_container_instances(
|
||||
cluster=test_cluster_name
|
||||
)
|
||||
len(container_instances_response['containerInstanceArns']).should.equal(0)
|
||||
|
||||
|
||||
@mock_ec2
|
||||
@mock_ecs
|
||||
def test_list_container_instances():
|
||||
|
Loading…
Reference in New Issue
Block a user