Add support for setting tags on ecs task definitions
This also implements the ecs.list_tags_for_resources, although the resources it checks for are currently only the task definitions
This commit is contained in:
parent
feef7b2b5a
commit
64e2a74e8c
@ -1,5 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from moto.core.exceptions import RESTError
|
from moto.core.exceptions import RESTError, JsonRESTError
|
||||||
|
|
||||||
|
|
||||||
class ServiceNotFoundException(RESTError):
|
class ServiceNotFoundException(RESTError):
|
||||||
@ -11,3 +11,13 @@ class ServiceNotFoundException(RESTError):
|
|||||||
message="The service {0} does not exist".format(service_name),
|
message="The service {0} does not exist".format(service_name),
|
||||||
template='error_json',
|
template='error_json',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TaskDefinitionNotFoundException(JsonRESTError):
|
||||||
|
code = 400
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(TaskDefinitionNotFoundException, self).__init__(
|
||||||
|
error_type="ClientException",
|
||||||
|
message="The specified task definition does not exist.",
|
||||||
|
)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from random import random, randint
|
from random import random, randint
|
||||||
@ -10,7 +11,10 @@ from moto.core import BaseBackend, BaseModel
|
|||||||
from moto.ec2 import ec2_backends
|
from moto.ec2 import ec2_backends
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
from .exceptions import ServiceNotFoundException
|
from .exceptions import (
|
||||||
|
ServiceNotFoundException,
|
||||||
|
TaskDefinitionNotFoundException
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseObject(BaseModel):
|
class BaseObject(BaseModel):
|
||||||
@ -103,12 +107,13 @@ class Cluster(BaseObject):
|
|||||||
|
|
||||||
class TaskDefinition(BaseObject):
|
class TaskDefinition(BaseObject):
|
||||||
|
|
||||||
def __init__(self, family, revision, container_definitions, volumes=None):
|
def __init__(self, family, revision, container_definitions, volumes=None, tags=None):
|
||||||
self.family = family
|
self.family = family
|
||||||
self.revision = revision
|
self.revision = revision
|
||||||
self.arn = 'arn:aws:ecs:us-east-1:012345678910:task-definition/{0}:{1}'.format(
|
self.arn = 'arn:aws:ecs:us-east-1:012345678910:task-definition/{0}:{1}'.format(
|
||||||
family, revision)
|
family, revision)
|
||||||
self.container_definitions = container_definitions
|
self.container_definitions = container_definitions
|
||||||
|
self.tags = tags if tags is not None else []
|
||||||
if volumes is None:
|
if volumes is None:
|
||||||
self.volumes = []
|
self.volumes = []
|
||||||
else:
|
else:
|
||||||
@ -119,6 +124,7 @@ class TaskDefinition(BaseObject):
|
|||||||
response_object = self.gen_response_object()
|
response_object = self.gen_response_object()
|
||||||
response_object['taskDefinitionArn'] = response_object['arn']
|
response_object['taskDefinitionArn'] = response_object['arn']
|
||||||
del response_object['arn']
|
del response_object['arn']
|
||||||
|
del response_object['tags']
|
||||||
return response_object
|
return response_object
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -464,7 +470,7 @@ class EC2ContainerServiceBackend(BaseBackend):
|
|||||||
else:
|
else:
|
||||||
raise Exception("{0} is not a cluster".format(cluster_name))
|
raise Exception("{0} is not a cluster".format(cluster_name))
|
||||||
|
|
||||||
def register_task_definition(self, family, container_definitions, volumes):
|
def register_task_definition(self, family, container_definitions, volumes, tags=None):
|
||||||
if family in self.task_definitions:
|
if family in self.task_definitions:
|
||||||
last_id = self._get_last_task_definition_revision_id(family)
|
last_id = self._get_last_task_definition_revision_id(family)
|
||||||
revision = (last_id or 0) + 1
|
revision = (last_id or 0) + 1
|
||||||
@ -472,7 +478,7 @@ class EC2ContainerServiceBackend(BaseBackend):
|
|||||||
self.task_definitions[family] = {}
|
self.task_definitions[family] = {}
|
||||||
revision = 1
|
revision = 1
|
||||||
task_definition = TaskDefinition(
|
task_definition = TaskDefinition(
|
||||||
family, revision, container_definitions, volumes)
|
family, revision, container_definitions, volumes, tags)
|
||||||
self.task_definitions[family][revision] = task_definition
|
self.task_definitions[family][revision] = task_definition
|
||||||
|
|
||||||
return task_definition
|
return task_definition
|
||||||
@ -951,6 +957,24 @@ class EC2ContainerServiceBackend(BaseBackend):
|
|||||||
|
|
||||||
yield task_fam
|
yield task_fam
|
||||||
|
|
||||||
|
def list_tags_for_resource(self, resource_arn):
|
||||||
|
"""Currently only implemented for task definitions"""
|
||||||
|
match = re.match(
|
||||||
|
"^arn:aws:ecs:(?P<region>[^:]+):(?P<account_id>[^:]+):(?P<service>[^:]+)/(?P<id>.*)$",
|
||||||
|
resource_arn)
|
||||||
|
if not match:
|
||||||
|
raise JsonRESTError('InvalidParameterException', 'The ARN provided is invalid.')
|
||||||
|
|
||||||
|
service = match.group("service")
|
||||||
|
if service == "task-definition":
|
||||||
|
for task_definition in self.task_definitions.values():
|
||||||
|
for revision in task_definition.values():
|
||||||
|
if revision.arn == resource_arn:
|
||||||
|
return revision.tags
|
||||||
|
else:
|
||||||
|
raise TaskDefinitionNotFoundException()
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
def _get_last_task_definition_revision_id(self, family):
|
def _get_last_task_definition_revision_id(self, family):
|
||||||
definitions = self.task_definitions.get(family, {})
|
definitions = self.task_definitions.get(family, {})
|
||||||
if definitions:
|
if definitions:
|
||||||
|
@ -62,8 +62,9 @@ class EC2ContainerServiceResponse(BaseResponse):
|
|||||||
family = self._get_param('family')
|
family = self._get_param('family')
|
||||||
container_definitions = self._get_param('containerDefinitions')
|
container_definitions = self._get_param('containerDefinitions')
|
||||||
volumes = self._get_param('volumes')
|
volumes = self._get_param('volumes')
|
||||||
|
tags = self._get_param('tags')
|
||||||
task_definition = self.ecs_backend.register_task_definition(
|
task_definition = self.ecs_backend.register_task_definition(
|
||||||
family, container_definitions, volumes)
|
family, container_definitions, volumes, tags)
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
'taskDefinition': task_definition.response_object
|
'taskDefinition': task_definition.response_object
|
||||||
})
|
})
|
||||||
@ -313,3 +314,8 @@ class EC2ContainerServiceResponse(BaseResponse):
|
|||||||
results = self.ecs_backend.list_task_definition_families(family_prefix, status, max_results, next_token)
|
results = self.ecs_backend.list_task_definition_families(family_prefix, status, max_results, next_token)
|
||||||
|
|
||||||
return json.dumps({'families': list(results)})
|
return json.dumps({'families': list(results)})
|
||||||
|
|
||||||
|
def list_tags_for_resource(self):
|
||||||
|
resource_arn = self._get_param('resourceArn')
|
||||||
|
tags = self.ecs_backend.list_tags_for_resource(resource_arn)
|
||||||
|
return json.dumps({'tags': tags})
|
||||||
|
@ -94,6 +94,10 @@ def test_register_task_definition():
|
|||||||
}],
|
}],
|
||||||
'logConfiguration': {'logDriver': 'json-file'}
|
'logConfiguration': {'logDriver': 'json-file'}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
tags=[
|
||||||
|
{'key': 'createdBy', 'value': 'moto-unittest'},
|
||||||
|
{'key': 'foo', 'value': 'bar'},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
type(response['taskDefinition']).should.be(dict)
|
type(response['taskDefinition']).should.be(dict)
|
||||||
@ -2304,3 +2308,52 @@ def test_create_service_load_balancing():
|
|||||||
response['service']['status'].should.equal('ACTIVE')
|
response['service']['status'].should.equal('ACTIVE')
|
||||||
response['service']['taskDefinition'].should.equal(
|
response['service']['taskDefinition'].should.equal(
|
||||||
'arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1')
|
'arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1')
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ecs
|
||||||
|
def test_list_tags_for_resource():
|
||||||
|
client = boto3.client('ecs', region_name='us-east-1')
|
||||||
|
response = 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'}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tags=[
|
||||||
|
{'key': 'createdBy', 'value': 'moto-unittest'},
|
||||||
|
{'key': 'foo', 'value': 'bar'},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
type(response['taskDefinition']).should.be(dict)
|
||||||
|
response['taskDefinition']['revision'].should.equal(1)
|
||||||
|
response['taskDefinition']['taskDefinitionArn'].should.equal(
|
||||||
|
'arn:aws:ecs:us-east-1:012345678910:task-definition/test_ecs_task:1')
|
||||||
|
|
||||||
|
task_definition_arn = response['taskDefinition']['taskDefinitionArn']
|
||||||
|
response = client.list_tags_for_resource(resourceArn=task_definition_arn)
|
||||||
|
|
||||||
|
type(response['tags']).should.be(list)
|
||||||
|
response['tags'].should.equal([
|
||||||
|
{'key': 'createdBy', 'value': 'moto-unittest'},
|
||||||
|
{'key': 'foo', 'value': 'bar'},
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@mock_ecs
|
||||||
|
def test_list_tags_for_resource_unknown():
|
||||||
|
client = boto3.client('ecs', region_name='us-east-1')
|
||||||
|
task_definition_arn = 'arn:aws:ecs:us-east-1:012345678910:task-definition/unknown:1'
|
||||||
|
try:
|
||||||
|
client.list_tags_for_resource(resourceArn=task_definition_arn)
|
||||||
|
except ClientError as err:
|
||||||
|
err.response['Error']['Code'].should.equal('ClientException')
|
||||||
|
Loading…
Reference in New Issue
Block a user