Add elasticbeanstalk Tags handling

This commit is contained in:
Niels Laukens 2019-09-04 16:56:06 +02:00
parent 9bfbd8e008
commit 7f387b0bb9
No known key found for this signature in database
GPG Key ID: D1397B5A6435A6D8
4 changed files with 203 additions and 10 deletions

View File

@ -5,3 +5,9 @@ class InvalidParameterValueError(RESTError):
def __init__(self, message): def __init__(self, message):
super(InvalidParameterValueError, self).__init__( super(InvalidParameterValueError, self).__init__(
"InvalidParameterValue", message) "InvalidParameterValue", message)
class ResourceNotFoundException(RESTError):
def __init__(self, message):
super(ResourceNotFoundException, self).__init__(
"ResourceNotFoundException", message)

View File

@ -7,32 +7,70 @@ from .exceptions import InvalidParameterValueError
class FakeEnvironment(BaseModel): class FakeEnvironment(BaseModel):
def __init__(self, application, environment_name): def __init__(
self.environment_name = environment_name self,
application,
environment_name,
tags,
):
self.application = weakref.proxy(application) # weakref to break circular dependencies self.application = weakref.proxy(application) # weakref to break circular dependencies
self.environment_name = environment_name
self.tags = tags
@property @property
def application_name(self): def application_name(self):
return self.application.application_name return self.application.application_name
@property
def environment_arn(self):
return 'arn:aws:elasticbeanstalk:{region}:{account_id}:' \
'environment/{application_name}/{environment_name}'.format(
region=self.region,
account_id='123456789012',
application_name=self.application_name,
environment_name=self.environment_name,
)
@property
def platform_arn(self):
return 'TODO' # TODO
@property
def solution_stack_name(self):
return 'TODO' # TODO
@property
def region(self):
return self.application.region
class FakeApplication(BaseModel): class FakeApplication(BaseModel):
def __init__(self, application_name): def __init__(self, backend, application_name):
self.backend = weakref.proxy(backend) # weakref to break cycles
self.application_name = application_name self.application_name = application_name
self.environments = dict() self.environments = dict()
def create_environment(self, environment_name): def create_environment(
self,
environment_name,
tags,
):
if environment_name in self.environments: if environment_name in self.environments:
raise InvalidParameterValueError raise InvalidParameterValueError
env = FakeEnvironment( env = FakeEnvironment(
application=self, application=self,
environment_name=environment_name, environment_name=environment_name,
tags=tags,
) )
self.environments[environment_name] = env self.environments[environment_name] = env
return env return env
@property
def region(self):
return self.backend.region
class EBBackend(BaseBackend): class EBBackend(BaseBackend):
def __init__(self, region): def __init__(self, region):
@ -52,6 +90,7 @@ class EBBackend(BaseBackend):
"Application {} already exists.".format(application_name) "Application {} already exists.".format(application_name)
) )
new_app = FakeApplication( new_app = FakeApplication(
backend=self,
application_name=application_name, application_name=application_name,
) )
self.applications[application_name] = new_app self.applications[application_name] = new_app

View File

@ -1,6 +1,7 @@
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.core.utils import tags_from_query_string
from .models import eb_backends, EBBackend from .models import eb_backends, EBBackend
from .exceptions import InvalidParameterValueError from .exceptions import InvalidParameterValueError, ResourceNotFoundException
class EBResponse(BaseResponse): class EBResponse(BaseResponse):
@ -38,7 +39,11 @@ class EBResponse(BaseResponse):
"No Application named \'{}\' found.".format(application_name) "No Application named \'{}\' found.".format(application_name)
) )
env = app.create_environment(environment_name=environment_name) tags = tags_from_query_string(self.querystring, prefix="Tags.member")
env = app.create_environment(
environment_name=environment_name,
tags=tags,
)
template = self.response_template(EB_CREATE_ENVIRONMENT) template = self.response_template(EB_CREATE_ENVIRONMENT)
return template.render( return template.render(
@ -62,6 +67,48 @@ class EBResponse(BaseResponse):
def list_available_solution_stacks(): def list_available_solution_stacks():
return EB_LIST_AVAILABLE_SOLUTION_STACKS return EB_LIST_AVAILABLE_SOLUTION_STACKS
def _find_environment_by_arn(self, arn):
for app in self.backend.applications.keys():
for env in self.backend.applications[app].environments.values():
if env.environment_arn == arn:
return env
raise KeyError()
def update_tags_for_resource(self):
resource_arn = self._get_param('ResourceArn')
try:
res = self._find_environment_by_arn(resource_arn)
except KeyError:
raise ResourceNotFoundException(
"Resource not found for ARN \'{}\'.".format(resource_arn)
)
tags_to_add = tags_from_query_string(self.querystring, prefix="TagsToAdd.member")
for key, value in tags_to_add.items():
res.tags[key] = value
tags_to_remove = self._get_multi_param('TagsToRemove.member')
for key in tags_to_remove:
del res.tags[key]
return EB_UPDATE_TAGS_FOR_RESOURCE
def list_tags_for_resource(self):
resource_arn = self._get_param('ResourceArn')
try:
res = self._find_environment_by_arn(resource_arn)
except KeyError:
raise ResourceNotFoundException(
"Resource not found for ARN \'{}\'.".format(resource_arn)
)
tags = res.tags
template = self.response_template(EB_LIST_TAGS_FOR_RESOURCE)
return template.render(
tags=tags,
arn=resource_arn,
)
EB_CREATE_APPLICATION = """ EB_CREATE_APPLICATION = """
<CreateApplicationResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/"> <CreateApplicationResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
@ -136,11 +183,11 @@ EB_CREATE_ENVIRONMENT = """
<CreateEnvironmentResult> <CreateEnvironmentResult>
<SolutionStackName>{{ environment.solution_stack_name }}</SolutionStackName> <SolutionStackName>{{ environment.solution_stack_name }}</SolutionStackName>
<Health>Grey</Health> <Health>Grey</Health>
<EnvironmentArn>arn:aws:elasticbeanstalk:{{ region }}:111122223333:environment/{{ environment.application_name }}/{{ environment.environment_name }}</EnvironmentArn> <EnvironmentArn>{{ environment.environment_arn }}</EnvironmentArn>
<DateUpdated>2019-09-04T09:41:24.222Z</DateUpdated> <DateUpdated>2019-09-04T09:41:24.222Z</DateUpdated>
<DateCreated>2019-09-04T09:41:24.222Z</DateCreated> <DateCreated>2019-09-04T09:41:24.222Z</DateCreated>
<EnvironmentId>{{ environment_id }}</EnvironmentId> <EnvironmentId>{{ environment_id }}</EnvironmentId>
<PlatformArn>arn:aws:elasticbeanstalk:{{ region }}::platform/{{ environment.platform_arn }}</PlatformArn> <PlatformArn>{{ environment.platform_arn }}</PlatformArn>
<Tier> <Tier>
<Name>WebServer</Name> <Name>WebServer</Name>
<Type>Standard</Type> <Type>Standard</Type>
@ -165,7 +212,7 @@ EB_DESCRIBE_ENVIRONMENTS = """
<member> <member>
<SolutionStackName>{{ env.solution_stack_name }}</SolutionStackName> <SolutionStackName>{{ env.solution_stack_name }}</SolutionStackName>
<Health>Grey</Health> <Health>Grey</Health>
<EnvironmentArn>arn:aws:elasticbeanstalk:{{ region }}:123456789012:environment/{{ env.application_name }}/{{ env.environment_name }}</EnvironmentArn> <EnvironmentArn>{{ env.environment_arn }}</EnvironmentArn>
<MinCapacityEnabled>false</MinCapacityEnabled> <MinCapacityEnabled>false</MinCapacityEnabled>
<DateUpdated>2019-08-30T09:35:10.913Z</DateUpdated> <DateUpdated>2019-08-30T09:35:10.913Z</DateUpdated>
<AbortableOperationInProgress>false</AbortableOperationInProgress> <AbortableOperationInProgress>false</AbortableOperationInProgress>
@ -173,7 +220,7 @@ EB_DESCRIBE_ENVIRONMENTS = """
<DateCreated>2019-08-22T07:02:47.332Z</DateCreated> <DateCreated>2019-08-22T07:02:47.332Z</DateCreated>
<EnvironmentId>{{ env.environment_id }}</EnvironmentId> <EnvironmentId>{{ env.environment_id }}</EnvironmentId>
<VersionLabel>1</VersionLabel> <VersionLabel>1</VersionLabel>
<PlatformArn>arn:aws:elasticbeanstalk:{{ region }}::platform/{{ env.platform_arn }}</PlatformArn> <PlatformArn>{{ env.platform_arn }}</PlatformArn>
<Tier> <Tier>
<Name>WebServer</Name> <Name>WebServer</Name>
<Type>Standard</Type> <Type>Standard</Type>
@ -1347,3 +1394,32 @@ EB_LIST_AVAILABLE_SOLUTION_STACKS = """
</ResponseMetadata> </ResponseMetadata>
</ListAvailableSolutionStacksResponse> </ListAvailableSolutionStacksResponse>
""" """
EB_UPDATE_TAGS_FOR_RESOURCE = """
<UpdateTagsForResourceResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
<ResponseMetadata>
<RequestId>f355d788-e67e-440f-b915-99e35254ffee</RequestId>
</ResponseMetadata>
</UpdateTagsForResourceResponse>
"""
EB_LIST_TAGS_FOR_RESOURCE = """
<ListTagsForResourceResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
<ListTagsForResourceResult>
<ResourceTags>
{% for key, value in tags.items() %}
<member>
<Key>{{ key }}</Key>
<Value>{{ value }}</Value>
</member>
{% endfor %}
</ResourceTags>
<ResourceArn>{{ arn }}</ResourceArn>
</ListTagsForResourceResult>
<ResponseMetadata>
<RequestId>178e410f-3b57-456f-a64c-a3b6a16da9ab</RequestId>
</ResponseMetadata>
</ListTagsForResourceResponse>
"""

View File

@ -72,6 +72,78 @@ def test_describe_environments():
envs[0]['EnvironmentName'].should.equal('myenv') envs[0]['EnvironmentName'].should.equal('myenv')
def tags_dict_to_list(tag_dict):
tag_list = []
for key, value in tag_dict.items():
tag_list.append({'Key': key, 'Value': value})
return tag_list
def tags_list_to_dict(tag_list):
tag_dict = {}
for tag in tag_list:
tag_dict[tag['Key']] = tag['Value']
return tag_dict
@mock_eb
def test_create_environment_tags():
conn = boto3.client('elasticbeanstalk', region_name='us-east-1')
conn.create_application(
ApplicationName="myapp",
)
env_tags = {'initial key': 'initial value'}
env = conn.create_environment(
ApplicationName="myapp",
EnvironmentName="myenv",
Tags=tags_dict_to_list(env_tags),
)
tags = conn.list_tags_for_resource(
ResourceArn=env['EnvironmentArn'],
)
tags['ResourceArn'].should.equal(env['EnvironmentArn'])
tags_list_to_dict(tags['ResourceTags']).should.equal(env_tags)
@mock_eb
def test_update_tags():
conn = boto3.client('elasticbeanstalk', region_name='us-east-1')
conn.create_application(
ApplicationName="myapp",
)
env_tags = {
'initial key': 'initial value',
'to remove': 'delete me',
'to update': 'original',
}
env = conn.create_environment(
ApplicationName="myapp",
EnvironmentName="myenv",
Tags=tags_dict_to_list(env_tags),
)
extra_env_tags = {
'to update': 'new',
'extra key': 'extra value',
}
conn.update_tags_for_resource(
ResourceArn=env['EnvironmentArn'],
TagsToAdd=tags_dict_to_list(extra_env_tags),
TagsToRemove=['to remove'],
)
total_env_tags = env_tags.copy()
total_env_tags.update(extra_env_tags)
del total_env_tags['to remove']
tags = conn.list_tags_for_resource(
ResourceArn=env['EnvironmentArn'],
)
tags['ResourceArn'].should.equal(env['EnvironmentArn'])
tags_list_to_dict(tags['ResourceTags']).should.equal(total_env_tags)
@mock_eb @mock_eb
def test_list_available_solution_stacks(): def test_list_available_solution_stacks():
conn = boto3.client('elasticbeanstalk', region_name='us-east-1') conn = boto3.client('elasticbeanstalk', region_name='us-east-1')