diff --git a/docs/docs/configuration/index.rst b/docs/docs/configuration/index.rst index c2e8b7ad1..3a2e59d06 100644 --- a/docs/docs/configuration/index.rst +++ b/docs/docs/configuration/index.rst @@ -20,7 +20,8 @@ If you are using the decorators, some options are configurable within the decora "services": ["dynamodb"] }, "reset_boto3_session": True, - } + }, + "iam": {"load_aws_managed_policies": False}, }) @@ -49,6 +50,36 @@ That is why Moto resets the `boto3-Session`, to make sure that it is recreated w If all of your tests use Moto, and you never want to reach out to AWS, you can choose to _not_ reset the `boto3-session`. New boto3-clients that are created will reuse the `boto3-Session` (with fake credentials), making Moto much more performant. +AWS Managed Policies +-------------------- +Moto comes bundled with all Managed Policies that AWS exposes, which are updated regularly. However, they are not loaded unless specifically requested for performance reasons. + +Set `"iam": {"load_aws_managed_policies": True}` to load these policies for a single test. + +Configuring MotoServer +---------------------- +The following options can also be configured when running the MotoServer: + +.. sourcecode:: + + options = { + "batch": {"use_docker": True}, + "lambda": {"use_docker": True} + } + requests.post(f"http://localhost:5000/moto-api/config", json=options) + +Send a GET request to see the current status of this configuration: + +.. sourcecode:: + + requests.get(f"http://localhost:5000/moto-api/config").json() + +The IAM Managed Policies should be loaded with an environment variable: + +.. sourcecode:: + + MOTO_IAM_LOAD_MANAGED_POLICIES=true + Other configuration options --------------------------- diff --git a/moto/moto_api/_internal/models.py b/moto/moto_api/_internal/models.py index 4f6a8ab7f..7c5b98b68 100644 --- a/moto/moto_api/_internal/models.py +++ b/moto/moto_api/_internal/models.py @@ -2,6 +2,7 @@ from typing import Any, Dict, List, Optional, Set, Tuple from moto.core import DEFAULT_ACCOUNT_ID from moto.core.base_backend import BackendDict, BaseBackend +from moto.core.config import default_user_config from moto.core.model_instances import reset_model_data @@ -116,5 +117,17 @@ class MotoAPIBackend(BaseBackend): self.proxy_urls_to_passthrough.clear() self.proxy_hosts_to_passthrough.clear() + def get_config(self) -> Dict[str, Any]: + return { + "batch": default_user_config["batch"], + "lambda": default_user_config["lambda"], + } + + def set_config(self, config: Dict[str, Any]) -> None: + if "batch" in config: + default_user_config["batch"] = config["batch"] + if "lambda" in config: + default_user_config["lambda"] = config["lambda"] + moto_api_backend = MotoAPIBackend(region_name="global", account_id=DEFAULT_ACCOUNT_ID) diff --git a/moto/moto_api/_internal/responses.py b/moto/moto_api/_internal/responses.py index f4649195c..665b28997 100644 --- a/moto/moto_api/_internal/responses.py +++ b/moto/moto_api/_internal/responses.py @@ -277,6 +277,23 @@ class MotoAPIResponse(BaseResponse): resp = {"http_urls": list(urls), "https_hosts": list(hosts)} return 201, res_headers, json.dumps(resp).encode("utf-8") + def config( + self, + request: Any, + full_url: str, # pylint: disable=unused-argument + headers: Any, + ) -> TYPE_RESPONSE: + from .models import moto_api_backend + + res_headers = {"Content-Type": "application/json"} + + if request.method == "POST": + config = self._get_body(headers, request) + moto_api_backend.set_config(config) + + config = moto_api_backend.get_config() + return 201, res_headers, json.dumps(config).encode("utf-8") + def _get_body(self, headers: Any, request: Any) -> Any: if isinstance(request, AWSPreparedRequest): return json.loads(request.body) # type: ignore[arg-type] diff --git a/moto/moto_api/_internal/urls.py b/moto/moto_api/_internal/urls.py index 6c32a2398..e0b53b794 100644 --- a/moto/moto_api/_internal/urls.py +++ b/moto/moto_api/_internal/urls.py @@ -13,6 +13,7 @@ url_paths = { "{0}/moto-api/reset-auth": response_instance.reset_auth_response, "{0}/moto-api/seed": response_instance.seed, "{0}/moto-api/proxy/passthrough": response_instance.set_proxy_passthrough, + "{0}/moto-api/config": response_instance.config, "{0}/moto-api/static/athena/query-results": response_instance.set_athena_result, "{0}/moto-api/static/ce/cost-and-usage-results": response_instance.set_ce_cost_usage_result, "{0}/moto-api/static/inspector2/findings-results": response_instance.set_inspector2_findings_result, diff --git a/tests/test_core/test_config.py b/tests/test_core/test_config.py new file mode 100644 index 000000000..63ba387ff --- /dev/null +++ b/tests/test_core/test_config.py @@ -0,0 +1,52 @@ +import requests + +from moto import mock_aws, settings +from moto.awslambda.models import LambdaBackend +from moto.awslambda.utils import get_backend +from moto.awslambda_simple.models import LambdaSimpleBackend +from moto.core import DEFAULT_ACCOUNT_ID +from moto.core.config import default_user_config + + +@mock_aws +def test_change_configuration_using_api() -> None: + assert default_user_config["batch"] == {"use_docker": True} + assert default_user_config["lambda"] == {"use_docker": True} + + base_url = ( + "localhost:5000" if settings.TEST_SERVER_MODE else "motoapi.amazonaws.com" + ) + resp = requests.get(f"http://{base_url}/moto-api/config") + assert resp.json()["batch"] == {"use_docker": True} + assert resp.json()["lambda"] == {"use_docker": True} + + # Update a single configuration item + requests.post( + f"http://{base_url}/moto-api/config", json={"batch": {"use_docker": False}} + ) + + resp = requests.get(f"http://{base_url}/moto-api/config") + assert resp.json()["batch"] == {"use_docker": False} + assert resp.json()["lambda"] == {"use_docker": True} + + if settings.TEST_DECORATOR_MODE: + isinstance(get_backend(DEFAULT_ACCOUNT_ID, "us-east-1"), LambdaBackend) + + # Update multiple configuration items + requests.post( + f"http://{base_url}/moto-api/config", + json={"batch": {"use_docker": True}, "lambda": {"use_docker": False}}, + ) + + resp = requests.get(f"http://{base_url}/moto-api/config") + assert resp.json()["batch"] == {"use_docker": True} + assert resp.json()["lambda"] == {"use_docker": False} + + if settings.TEST_DECORATOR_MODE: + isinstance(get_backend(DEFAULT_ACCOUNT_ID, "us-east-1"), LambdaSimpleBackend) + + # reset + requests.post( + f"http://{base_url}/moto-api/config", + json={"batch": {"use_docker": True}, "lambda": {"use_docker": True}}, + )