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{{ request_id_tag }}>
+"""
+
+
+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)