From 5e8b457bc967d0d074fc1f2fda77ac7817608b3e Mon Sep 17 00:00:00 2001 From: Jean-Frederic Mainville <35815402+jfmainville@users.noreply.github.com> Date: Mon, 11 Sep 2023 04:57:27 -0400 Subject: [PATCH] Elastic Beanstalk: Add the `delete_application` method support (#6797) --- docs/docs/services/elasticbeanstalk.rst | 2 +- moto/elasticbeanstalk/exceptions.py | 33 ++++++++++++ moto/elasticbeanstalk/models.py | 23 ++++++++- moto/elasticbeanstalk/responses.py | 50 +++++++++++++------ tests/test_elasticbeanstalk/__init__.py | 0 .../{test_eb.py => test_elasticbeanstalk.py} | 31 ++++++++++++ tests/test_elasticbeanstalk/test_server.py | 15 ++++++ 7 files changed, 135 insertions(+), 19 deletions(-) create mode 100644 tests/test_elasticbeanstalk/__init__.py rename tests/test_elasticbeanstalk/{test_eb.py => test_elasticbeanstalk.py} (82%) create mode 100644 tests/test_elasticbeanstalk/test_server.py diff --git a/docs/docs/services/elasticbeanstalk.rst b/docs/docs/services/elasticbeanstalk.rst index 36343d7b3..45efbb313 100644 --- a/docs/docs/services/elasticbeanstalk.rst +++ b/docs/docs/services/elasticbeanstalk.rst @@ -36,7 +36,7 @@ elasticbeanstalk - [X] create_environment - [ ] create_platform_version - [ ] create_storage_location -- [ ] delete_application +- [x] delete_application - [ ] delete_application_version - [ ] delete_configuration_template - [ ] delete_environment_configuration diff --git a/moto/elasticbeanstalk/exceptions.py b/moto/elasticbeanstalk/exceptions.py index 22e70cb29..632080fb5 100644 --- a/moto/elasticbeanstalk/exceptions.py +++ b/moto/elasticbeanstalk/exceptions.py @@ -1,5 +1,27 @@ +from typing import Any + from moto.core.exceptions import RESTError +EXCEPTION_RESPONSE = """ + + + Sender + {{ error_type }} + {{ message }} + + <{{ request_id_tag }}>30c0dedb-92b1-4e2b-9be4-1188e3ed86ab +""" + + +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): def __init__(self, message: str): @@ -9,3 +31,14 @@ class InvalidParameterValueError(RESTError): class ResourceNotFoundException(RESTError): def __init__(self, message: str): 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.", + ) diff --git a/moto/elasticbeanstalk/models.py b/moto/elasticbeanstalk/models.py index e809c8cf6..e90887770 100644 --- a/moto/elasticbeanstalk/models.py +++ b/moto/elasticbeanstalk/models.py @@ -2,7 +2,12 @@ import weakref from typing import Dict, List from moto.core import BaseBackend, BackendDict, BaseModel -from .exceptions import InvalidParameterValueError, ResourceNotFoundException + +from .exceptions import ( + InvalidParameterValueError, + ResourceNotFoundException, + ApplicationNotFound, +) from .utils import make_arn @@ -42,7 +47,11 @@ class FakeEnvironment(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.application_name = application_name self.environments: Dict[str, FakeEnvironment] = dict() @@ -148,5 +157,15 @@ class EBBackend(BaseBackend): return env 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") diff --git a/moto/elasticbeanstalk/responses.py b/moto/elasticbeanstalk/responses.py index 568ed4c16..e1e24741f 100644 --- a/moto/elasticbeanstalk/responses.py +++ b/moto/elasticbeanstalk/responses.py @@ -1,7 +1,8 @@ from moto.core.responses import BaseResponse from moto.core.utils import tags_from_query_string -from .models import eb_backends, EBBackend + from .exceptions import InvalidParameterValueError +from .models import eb_backends, EBBackend class EBResponse(BaseResponse): @@ -9,35 +10,39 @@ class EBResponse(BaseResponse): super().__init__(service_name="elasticbeanstalk") @property - def backend(self) -> EBBackend: + def elasticbeanstalk_backend(self) -> EBBackend: """ :rtype: EBBackend """ return eb_backends[self.current_account][self.region] def create_application(self) -> str: - app = self.backend.create_application( + app = self.elasticbeanstalk_backend.create_application( application_name=self._get_param("ApplicationName") ) 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: 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: application_name = self._get_param("ApplicationName") try: - app = self.backend.applications[application_name] + app = self.elasticbeanstalk_backend.applications[application_name] except KeyError: raise InvalidParameterValueError( f"No Application named '{application_name}' found." ) tags = tags_from_query_string(self.querystring, prefix="Tags.member") - env = self.backend.create_environment( + env = self.elasticbeanstalk_backend.create_environment( app, environment_name=self._get_param("EnvironmentName"), stack_name=self._get_param("SolutionStackName"), @@ -45,10 +50,12 @@ class EBResponse(BaseResponse): ) 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: - envs = self.backend.describe_environments() + envs = self.elasticbeanstalk_backend.describe_environments() template = self.response_template(EB_DESCRIBE_ENVIRONMENTS) return template.render(environments=envs) @@ -62,17 +69,26 @@ class EBResponse(BaseResponse): self.querystring, prefix="TagsToAdd.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 def list_tags_for_resource(self) -> str: 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) 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 = """ @@ -105,7 +121,6 @@ EB_CREATE_APPLICATION = """ """ - EB_DESCRIBE_APPLICATIONS = """ @@ -141,6 +156,13 @@ EB_DESCRIBE_APPLICATIONS = """ """ +DELETE_APPLICATION_TEMPLATE = """ + + + 015a05eb-282e-4b76-bd18-663fdfaf42e4 + + +""" EB_CREATE_ENVIRONMENT = """ @@ -167,7 +189,6 @@ EB_CREATE_ENVIRONMENT = """ """ - EB_DESCRIBE_ENVIRONMENTS = """ @@ -207,7 +228,6 @@ EB_DESCRIBE_ENVIRONMENTS = """ """ - # Current list as of 2019-09-04 EB_LIST_AVAILABLE_SOLUTION_STACKS = """ @@ -1359,7 +1379,6 @@ EB_LIST_AVAILABLE_SOLUTION_STACKS = """ """ - EB_UPDATE_TAGS_FOR_RESOURCE = """ @@ -1368,7 +1387,6 @@ EB_UPDATE_TAGS_FOR_RESOURCE = """ """ - EB_LIST_TAGS_FOR_RESOURCE = """ diff --git a/tests/test_elasticbeanstalk/__init__.py b/tests/test_elasticbeanstalk/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_elasticbeanstalk/test_eb.py b/tests/test_elasticbeanstalk/test_elasticbeanstalk.py similarity index 82% rename from tests/test_elasticbeanstalk/test_eb.py rename to tests/test_elasticbeanstalk/test_elasticbeanstalk.py index 93d84d581..28c01b5a9 100644 --- a/tests/test_elasticbeanstalk/test_eb.py +++ b/tests/test_elasticbeanstalk/test_elasticbeanstalk.py @@ -34,6 +34,37 @@ def test_describe_applications(): 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 def test_create_environment(): # Create Elastic Beanstalk Environment diff --git a/tests/test_elasticbeanstalk/test_server.py b/tests/test_elasticbeanstalk/test_server.py new file mode 100644 index 000000000..315276fc8 --- /dev/null +++ b/tests/test_elasticbeanstalk/test_server.py @@ -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 "" in str(resp.data)