Elastic Beanstalk: Add the delete_application method support (#6797)

This commit is contained in:
Jean-Frederic Mainville 2023-09-11 04:57:27 -04:00 committed by GitHub
parent 6c04b7b1ce
commit 5e8b457bc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 135 additions and 19 deletions

View File

@ -36,7 +36,7 @@ elasticbeanstalk
- [X] create_environment - [X] create_environment
- [ ] create_platform_version - [ ] create_platform_version
- [ ] create_storage_location - [ ] create_storage_location
- [ ] delete_application - [x] delete_application
- [ ] delete_application_version - [ ] delete_application_version
- [ ] delete_configuration_template - [ ] delete_configuration_template
- [ ] delete_environment_configuration - [ ] delete_environment_configuration

View File

@ -1,5 +1,27 @@
from typing import Any
from moto.core.exceptions import RESTError from moto.core.exceptions import RESTError
EXCEPTION_RESPONSE = """<?xml version="1.0"?>
<ErrorResponse xmlns="http://elasticache.amazonaws.com/doc/2015-02-02/">
<Error>
<Type>Sender</Type>
<Code>{{ error_type }}</Code>
<Message>{{ message }}</Message>
</Error>
<{{ request_id_tag }}>30c0dedb-92b1-4e2b-9be4-1188e3ed86ab</{{ request_id_tag }}>
</ErrorResponse>"""
class ElasticBeanstalkException(RESTError):
code = 400
def __init__(self, code: str, message: str, **kwargs: Any):
kwargs.setdefault("template", "ecerror")
self.templates["ecerror"] = EXCEPTION_RESPONSE
super().__init__(code, message)
class InvalidParameterValueError(RESTError): class InvalidParameterValueError(RESTError):
def __init__(self, message: str): def __init__(self, message: str):
@ -9,3 +31,14 @@ class InvalidParameterValueError(RESTError):
class ResourceNotFoundException(RESTError): class ResourceNotFoundException(RESTError):
def __init__(self, message: str): def __init__(self, message: str):
super().__init__("ResourceNotFoundException", message) super().__init__("ResourceNotFoundException", message)
class ApplicationNotFound(ElasticBeanstalkException):
code = 404
def __init__(self, application_name: str):
super().__init__(
"ApplicationNotFound",
message=f"Elastic Beanstalk application {application_name} not found.",
)

View File

@ -2,7 +2,12 @@ import weakref
from typing import Dict, List from typing import Dict, List
from moto.core import BaseBackend, BackendDict, BaseModel from moto.core import BaseBackend, BackendDict, BaseModel
from .exceptions import InvalidParameterValueError, ResourceNotFoundException
from .exceptions import (
InvalidParameterValueError,
ResourceNotFoundException,
ApplicationNotFound,
)
from .utils import make_arn from .utils import make_arn
@ -42,7 +47,11 @@ class FakeEnvironment(BaseModel):
class FakeApplication(BaseModel): class FakeApplication(BaseModel):
def __init__(self, backend: "EBBackend", application_name: str): def __init__(
self,
backend: "EBBackend",
application_name: str,
):
self.backend = weakref.proxy(backend) # weakref to break cycles self.backend = weakref.proxy(backend) # weakref to break cycles
self.application_name = application_name self.application_name = application_name
self.environments: Dict[str, FakeEnvironment] = dict() self.environments: Dict[str, FakeEnvironment] = dict()
@ -148,5 +157,15 @@ class EBBackend(BaseBackend):
return env return env
raise KeyError() raise KeyError()
def delete_application(
self,
application_name: str,
) -> None:
if application_name:
if application_name in self.applications:
self.applications.pop(application_name)
else:
raise ApplicationNotFound(application_name)
eb_backends = BackendDict(EBBackend, "elasticbeanstalk") eb_backends = BackendDict(EBBackend, "elasticbeanstalk")

View File

@ -1,7 +1,8 @@
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.core.utils import tags_from_query_string from moto.core.utils import tags_from_query_string
from .models import eb_backends, EBBackend
from .exceptions import InvalidParameterValueError from .exceptions import InvalidParameterValueError
from .models import eb_backends, EBBackend
class EBResponse(BaseResponse): class EBResponse(BaseResponse):
@ -9,35 +10,39 @@ class EBResponse(BaseResponse):
super().__init__(service_name="elasticbeanstalk") super().__init__(service_name="elasticbeanstalk")
@property @property
def backend(self) -> EBBackend: def elasticbeanstalk_backend(self) -> EBBackend:
""" """
:rtype: EBBackend :rtype: EBBackend
""" """
return eb_backends[self.current_account][self.region] return eb_backends[self.current_account][self.region]
def create_application(self) -> str: def create_application(self) -> str:
app = self.backend.create_application( app = self.elasticbeanstalk_backend.create_application(
application_name=self._get_param("ApplicationName") application_name=self._get_param("ApplicationName")
) )
template = self.response_template(EB_CREATE_APPLICATION) template = self.response_template(EB_CREATE_APPLICATION)
return template.render(region_name=self.backend.region_name, application=app) return template.render(
region_name=self.elasticbeanstalk_backend.region_name, application=app
)
def describe_applications(self) -> str: def describe_applications(self) -> str:
template = self.response_template(EB_DESCRIBE_APPLICATIONS) template = self.response_template(EB_DESCRIBE_APPLICATIONS)
return template.render(applications=self.backend.applications.values()) return template.render(
applications=self.elasticbeanstalk_backend.applications.values()
)
def create_environment(self) -> str: def create_environment(self) -> str:
application_name = self._get_param("ApplicationName") application_name = self._get_param("ApplicationName")
try: try:
app = self.backend.applications[application_name] app = self.elasticbeanstalk_backend.applications[application_name]
except KeyError: except KeyError:
raise InvalidParameterValueError( raise InvalidParameterValueError(
f"No Application named '{application_name}' found." f"No Application named '{application_name}' found."
) )
tags = tags_from_query_string(self.querystring, prefix="Tags.member") tags = tags_from_query_string(self.querystring, prefix="Tags.member")
env = self.backend.create_environment( env = self.elasticbeanstalk_backend.create_environment(
app, app,
environment_name=self._get_param("EnvironmentName"), environment_name=self._get_param("EnvironmentName"),
stack_name=self._get_param("SolutionStackName"), stack_name=self._get_param("SolutionStackName"),
@ -45,10 +50,12 @@ class EBResponse(BaseResponse):
) )
template = self.response_template(EB_CREATE_ENVIRONMENT) template = self.response_template(EB_CREATE_ENVIRONMENT)
return template.render(environment=env, region=self.backend.region_name) return template.render(
environment=env, region=self.elasticbeanstalk_backend.region_name
)
def describe_environments(self) -> str: def describe_environments(self) -> str:
envs = self.backend.describe_environments() envs = self.elasticbeanstalk_backend.describe_environments()
template = self.response_template(EB_DESCRIBE_ENVIRONMENTS) template = self.response_template(EB_DESCRIBE_ENVIRONMENTS)
return template.render(environments=envs) return template.render(environments=envs)
@ -62,17 +69,26 @@ class EBResponse(BaseResponse):
self.querystring, prefix="TagsToAdd.member" self.querystring, prefix="TagsToAdd.member"
) )
tags_to_remove = self._get_multi_param("TagsToRemove.member") tags_to_remove = self._get_multi_param("TagsToRemove.member")
self.backend.update_tags_for_resource(resource_arn, tags_to_add, tags_to_remove) self.elasticbeanstalk_backend.update_tags_for_resource(
resource_arn, tags_to_add, tags_to_remove
)
return EB_UPDATE_TAGS_FOR_RESOURCE return EB_UPDATE_TAGS_FOR_RESOURCE
def list_tags_for_resource(self) -> str: def list_tags_for_resource(self) -> str:
resource_arn = self._get_param("ResourceArn") resource_arn = self._get_param("ResourceArn")
tags = self.backend.list_tags_for_resource(resource_arn) tags = self.elasticbeanstalk_backend.list_tags_for_resource(resource_arn)
template = self.response_template(EB_LIST_TAGS_FOR_RESOURCE) template = self.response_template(EB_LIST_TAGS_FOR_RESOURCE)
return template.render(tags=tags, arn=resource_arn) return template.render(tags=tags, arn=resource_arn)
def delete_application(self) -> str:
application_name = self._get_param("ApplicationName")
self.elasticbeanstalk_backend.delete_application(
application_name=application_name,
)
return DELETE_APPLICATION_TEMPLATE
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/">
@ -105,7 +121,6 @@ EB_CREATE_APPLICATION = """
</CreateApplicationResponse> </CreateApplicationResponse>
""" """
EB_DESCRIBE_APPLICATIONS = """ EB_DESCRIBE_APPLICATIONS = """
<DescribeApplicationsResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/"> <DescribeApplicationsResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
<DescribeApplicationsResult> <DescribeApplicationsResult>
@ -141,6 +156,13 @@ EB_DESCRIBE_APPLICATIONS = """
</DescribeApplicationsResponse> </DescribeApplicationsResponse>
""" """
DELETE_APPLICATION_TEMPLATE = """
<DeleteApplicationResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
<ResponseMetadata>
<RequestId>015a05eb-282e-4b76-bd18-663fdfaf42e4</RequestId>
</ResponseMetadata>
</DeleteApplicationResponse>
"""
EB_CREATE_ENVIRONMENT = """ EB_CREATE_ENVIRONMENT = """
<CreateEnvironmentResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/"> <CreateEnvironmentResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
@ -167,7 +189,6 @@ EB_CREATE_ENVIRONMENT = """
</CreateEnvironmentResponse> </CreateEnvironmentResponse>
""" """
EB_DESCRIBE_ENVIRONMENTS = """ EB_DESCRIBE_ENVIRONMENTS = """
<DescribeEnvironmentsResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/"> <DescribeEnvironmentsResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
<DescribeEnvironmentsResult> <DescribeEnvironmentsResult>
@ -207,7 +228,6 @@ EB_DESCRIBE_ENVIRONMENTS = """
</DescribeEnvironmentsResponse> </DescribeEnvironmentsResponse>
""" """
# Current list as of 2019-09-04 # Current list as of 2019-09-04
EB_LIST_AVAILABLE_SOLUTION_STACKS = """ EB_LIST_AVAILABLE_SOLUTION_STACKS = """
<ListAvailableSolutionStacksResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/"> <ListAvailableSolutionStacksResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
@ -1359,7 +1379,6 @@ EB_LIST_AVAILABLE_SOLUTION_STACKS = """
</ListAvailableSolutionStacksResponse> </ListAvailableSolutionStacksResponse>
""" """
EB_UPDATE_TAGS_FOR_RESOURCE = """ EB_UPDATE_TAGS_FOR_RESOURCE = """
<UpdateTagsForResourceResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/"> <UpdateTagsForResourceResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
<ResponseMetadata> <ResponseMetadata>
@ -1368,7 +1387,6 @@ EB_UPDATE_TAGS_FOR_RESOURCE = """
</UpdateTagsForResourceResponse> </UpdateTagsForResourceResponse>
""" """
EB_LIST_TAGS_FOR_RESOURCE = """ EB_LIST_TAGS_FOR_RESOURCE = """
<ListTagsForResourceResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/"> <ListTagsForResourceResponse xmlns="http://elasticbeanstalk.amazonaws.com/docs/2010-12-01/">
<ListTagsForResourceResult> <ListTagsForResourceResult>

View File

View File

@ -34,6 +34,37 @@ def test_describe_applications():
assert "myapp" in apps["Applications"][0]["ApplicationArn"] assert "myapp" in apps["Applications"][0]["ApplicationArn"]
@mock_elasticbeanstalk
def test_delete_application():
conn = boto3.client("elasticbeanstalk", region_name="us-east-1")
application_name = "myapp"
conn.create_application(ApplicationName=application_name)
resp = conn.delete_application(ApplicationName=application_name)
assert resp["ResponseMetadata"]["HTTPStatusCode"] == 200
@mock_elasticbeanstalk
def test_delete_unknown_application():
conn = boto3.client("elasticbeanstalk", region_name="us-east-1")
application_name = "myapp"
unknown_application_name = "myapp1"
conn.create_application(ApplicationName=application_name)
with pytest.raises(ClientError) as exc:
conn.delete_application(ApplicationName=unknown_application_name)
err = exc.value.response["Error"]
assert err["Code"] == "ApplicationNotFound"
assert (
err["Message"]
== f"Elastic Beanstalk application {unknown_application_name} not found."
)
@mock_elasticbeanstalk @mock_elasticbeanstalk
def test_create_environment(): def test_create_environment():
# Create Elastic Beanstalk Environment # Create Elastic Beanstalk Environment

View File

@ -0,0 +1,15 @@
"""Test different server responses."""
import moto.server as server
def test_elasticbeanstalk_describe():
backend = server.create_backend_app("elasticbeanstalk")
test_client = backend.test_client()
data = "Action=DescribeApplications"
headers = {"Host": "elasticbeanstalk.us-east-1.amazonaws.com"}
resp = test_client.post("/", data=data, headers=headers)
assert resp.status_code == 200
assert "<Applications></Applications>" in str(resp.data)