From 870f0ad22dd6b31aba0dd942c8b6995a2ae52975 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Thu, 7 Sep 2023 13:59:25 +0000 Subject: [PATCH] Feature: RoboMaker: *_robot_applications() (#6780) --- IMPLEMENTATION_COVERAGE.md | 64 ++++++++++++++++- docs/docs/services/robomaker.rst | 95 ++++++++++++++++++++++++++ moto/__init__.py | 1 + moto/backend_index.py | 1 + moto/robomaker/__init__.py | 5 ++ moto/robomaker/models.py | 73 ++++++++++++++++++++ moto/robomaker/responses.py | 46 +++++++++++++ moto/robomaker/urls.py | 16 +++++ tests/test_robomaker/__init__.py | 0 tests/test_robomaker/test_robomaker.py | 36 ++++++++++ 10 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 docs/docs/services/robomaker.rst create mode 100644 moto/robomaker/__init__.py create mode 100644 moto/robomaker/models.py create mode 100644 moto/robomaker/responses.py create mode 100644 moto/robomaker/urls.py create mode 100644 tests/test_robomaker/__init__.py create mode 100644 tests/test_robomaker/test_robomaker.py diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 27a3fe290..9c9ede371 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -5804,6 +5804,69 @@ - [ ] untag_resources +## robomaker +
+7% implemented + +- [ ] batch_delete_worlds +- [ ] batch_describe_simulation_job +- [ ] cancel_deployment_job +- [ ] cancel_simulation_job +- [ ] cancel_simulation_job_batch +- [ ] cancel_world_export_job +- [ ] cancel_world_generation_job +- [ ] create_deployment_job +- [ ] create_fleet +- [ ] create_robot +- [X] create_robot_application +- [ ] create_robot_application_version +- [ ] create_simulation_application +- [ ] create_simulation_application_version +- [ ] create_simulation_job +- [ ] create_world_export_job +- [ ] create_world_generation_job +- [ ] create_world_template +- [ ] delete_fleet +- [ ] delete_robot +- [X] delete_robot_application +- [ ] delete_simulation_application +- [ ] delete_world_template +- [ ] deregister_robot +- [ ] describe_deployment_job +- [ ] describe_fleet +- [ ] describe_robot +- [X] describe_robot_application +- [ ] describe_simulation_application +- [ ] describe_simulation_job +- [ ] describe_simulation_job_batch +- [ ] describe_world +- [ ] describe_world_export_job +- [ ] describe_world_generation_job +- [ ] describe_world_template +- [ ] get_world_template_body +- [ ] list_deployment_jobs +- [ ] list_fleets +- [X] list_robot_applications +- [ ] list_robots +- [ ] list_simulation_applications +- [ ] list_simulation_job_batches +- [ ] list_simulation_jobs +- [ ] list_tags_for_resource +- [ ] list_world_export_jobs +- [ ] list_world_generation_jobs +- [ ] list_world_templates +- [ ] list_worlds +- [ ] register_robot +- [ ] restart_simulation_job +- [ ] start_simulation_job_batch +- [ ] sync_deployment_job +- [ ] tag_resource +- [ ] untag_resource +- [ ] update_robot_application +- [ ] update_simulation_application +- [ ] update_world_template +
+ ## route53
41% implemented @@ -7485,7 +7548,6 @@ - redshift-serverless - resiliencehub - resource-explorer-2 -- robomaker - rolesanywhere - route53-recovery-cluster - route53-recovery-control-config diff --git a/docs/docs/services/robomaker.rst b/docs/docs/services/robomaker.rst new file mode 100644 index 000000000..70df30899 --- /dev/null +++ b/docs/docs/services/robomaker.rst @@ -0,0 +1,95 @@ +.. _implementedservice_robomaker: + +.. |start-h3| raw:: html + +

+ +.. |end-h3| raw:: html + +

+ +========= +robomaker +========= + +.. autoclass:: moto.robomaker.models.RoboMakerBackend + +|start-h3| Example usage |end-h3| + +.. sourcecode:: python + + @mock_robomaker + def test_robomaker_behaviour: + boto3.client("robomaker") + ... + + + +|start-h3| Implemented features for this service |end-h3| + +- [ ] batch_delete_worlds +- [ ] batch_describe_simulation_job +- [ ] cancel_deployment_job +- [ ] cancel_simulation_job +- [ ] cancel_simulation_job_batch +- [ ] cancel_world_export_job +- [ ] cancel_world_generation_job +- [ ] create_deployment_job +- [ ] create_fleet +- [ ] create_robot +- [X] create_robot_application + + The tags and environment parameters are not yet implemented + + +- [ ] create_robot_application_version +- [ ] create_simulation_application +- [ ] create_simulation_application_version +- [ ] create_simulation_job +- [ ] create_world_export_job +- [ ] create_world_generation_job +- [ ] create_world_template +- [ ] delete_fleet +- [ ] delete_robot +- [X] delete_robot_application +- [ ] delete_simulation_application +- [ ] delete_world_template +- [ ] deregister_robot +- [ ] describe_deployment_job +- [ ] describe_fleet +- [ ] describe_robot +- [X] describe_robot_application +- [ ] describe_simulation_application +- [ ] describe_simulation_job +- [ ] describe_simulation_job_batch +- [ ] describe_world +- [ ] describe_world_export_job +- [ ] describe_world_generation_job +- [ ] describe_world_template +- [ ] get_world_template_body +- [ ] list_deployment_jobs +- [ ] list_fleets +- [X] list_robot_applications + + Currently returns all applications - none of the parameters are taken into account + + +- [ ] list_robots +- [ ] list_simulation_applications +- [ ] list_simulation_job_batches +- [ ] list_simulation_jobs +- [ ] list_tags_for_resource +- [ ] list_world_export_jobs +- [ ] list_world_generation_jobs +- [ ] list_world_templates +- [ ] list_worlds +- [ ] register_robot +- [ ] restart_simulation_job +- [ ] start_simulation_job_batch +- [ ] sync_deployment_job +- [ ] tag_resource +- [ ] untag_resource +- [ ] update_robot_application +- [ ] update_simulation_application +- [ ] update_world_template + diff --git a/moto/__init__.py b/moto/__init__.py index 1e80fd257..4be50ec86 100644 --- a/moto/__init__.py +++ b/moto/__init__.py @@ -148,6 +148,7 @@ mock_resourcegroups = lazy_load( mock_resourcegroupstaggingapi = lazy_load( ".resourcegroupstaggingapi", "mock_resourcegroupstaggingapi" ) +mock_robomaker = lazy_load(".robomaker", "mock_robomaker") mock_route53 = lazy_load(".route53", "mock_route53") mock_route53resolver = lazy_load( ".route53resolver", "mock_route53resolver", boto3_name="route53resolver" diff --git a/moto/backend_index.py b/moto/backend_index.py index fdc603b74..faf763d1d 100644 --- a/moto/backend_index.py +++ b/moto/backend_index.py @@ -136,6 +136,7 @@ backend_url_patterns = [ re.compile("https?://resource-groups(-fips)?\\.(.+)\\.amazonaws.com"), ), ("resourcegroupstaggingapi", re.compile("https?://tagging\\.(.+)\\.amazonaws.com")), + ("robomaker", re.compile("https?://robomaker\\.(.+)\\.amazonaws\\.com")), ("route53", re.compile("https?://route53(\\..+)?\\.amazonaws.com")), ( "route53resolver", diff --git a/moto/robomaker/__init__.py b/moto/robomaker/__init__.py new file mode 100644 index 000000000..99dc70c6f --- /dev/null +++ b/moto/robomaker/__init__.py @@ -0,0 +1,5 @@ +"""robomaker module initialization; sets value for base decorator.""" +from .models import robomaker_backends +from ..core.models import base_decorator + +mock_robomaker = base_decorator(robomaker_backends) diff --git a/moto/robomaker/models.py b/moto/robomaker/models.py new file mode 100644 index 000000000..a03234d4e --- /dev/null +++ b/moto/robomaker/models.py @@ -0,0 +1,73 @@ +from moto.core import BaseBackend, BackendDict, BaseModel +from moto.core.utils import unix_time + +from typing import Any, Dict, List, Iterable + + +class RobotApplication(BaseModel): + def __init__( + self, + account_id: str, + region: str, + name: str, + sources: List[Dict[str, str]], + robot_software_suite: Dict[str, str], + ): + self.account_id = account_id + self.region = region + self.name = name + self.sources = sources + self.robot_software_suite = robot_software_suite + self.created_on = unix_time() + + @property + def arn(self) -> str: + return f"arn:aws:robomaker:{self.region}:{self.account_id}:robot-application/{self.name}/{self.created_on}" + + def to_dict(self) -> Dict[str, Any]: + return { + "arn": self.arn, + "name": self.name, + "sources": self.sources, + "robotSoftwareSuite": self.robot_software_suite, + } + + +class RoboMakerBackend(BaseBackend): + def __init__(self, region_name: str, account_id: str): + super().__init__(region_name, account_id) + self.robot_applications: Dict[str, RobotApplication] = {} + + def create_robot_application( + self, + name: str, + sources: List[Dict[str, str]], + robot_software_suite: Dict[str, str], + ) -> RobotApplication: + """ + The tags and environment parameters are not yet implemented + """ + app = RobotApplication( + account_id=self.account_id, + region=self.region_name, + name=name, + sources=sources, + robot_software_suite=robot_software_suite, + ) + self.robot_applications[name] = app + return app + + def describe_robot_application(self, application: str) -> RobotApplication: + return self.robot_applications[application] + + def delete_robot_application(self, application: str) -> None: + self.robot_applications.pop(application) + + def list_robot_applications(self) -> Iterable[RobotApplication]: + """ + Currently returns all applications - none of the parameters are taken into account + """ + return self.robot_applications.values() + + +robomaker_backends = BackendDict(RoboMakerBackend, "robomaker") diff --git a/moto/robomaker/responses.py b/moto/robomaker/responses.py new file mode 100644 index 000000000..59d44e76b --- /dev/null +++ b/moto/robomaker/responses.py @@ -0,0 +1,46 @@ +import json + +from moto.core.responses import BaseResponse +from .models import robomaker_backends, RoboMakerBackend + + +class RoboMakerResponse(BaseResponse): + def __init__(self) -> None: + super().__init__(service_name="robomaker") + + @property + def robomaker_backend(self) -> RoboMakerBackend: + return robomaker_backends[self.current_account][self.region] + + def create_robot_application(self) -> str: + name = self._get_param("name") + sources = self._get_param("sources") + robot_software_suite = self._get_param("robotSoftwareSuite") + app = self.robomaker_backend.create_robot_application( + name=name, + sources=sources, + robot_software_suite=robot_software_suite, + ) + return json.dumps(app.to_dict()) + + def describe_robot_application(self) -> str: + application = self._get_param("application") + app = self.robomaker_backend.describe_robot_application( + application=application, + ) + return json.dumps(app.to_dict()) + + def delete_robot_application(self) -> str: + application = self._get_param("application") + self.robomaker_backend.delete_robot_application( + application=application, + ) + return "{}" + + def list_robot_applications(self) -> str: + robot_applications = self.robomaker_backend.list_robot_applications() + return json.dumps( + dict( + robotApplicationSummaries=[app.to_dict() for app in robot_applications] + ) + ) diff --git a/moto/robomaker/urls.py b/moto/robomaker/urls.py new file mode 100644 index 000000000..ac8656616 --- /dev/null +++ b/moto/robomaker/urls.py @@ -0,0 +1,16 @@ +from .responses import RoboMakerResponse + +url_bases = [ + r"https?://robomaker\.(.+)\.amazonaws\.com", +] + + +response = RoboMakerResponse() + + +url_paths = { + "{0}/createRobotApplication$": response.dispatch, + "{0}/deleteRobotApplication$": response.dispatch, + "{0}/describeRobotApplication$": response.dispatch, + "{0}/listRobotApplications$": response.dispatch, +} diff --git a/tests/test_robomaker/__init__.py b/tests/test_robomaker/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_robomaker/test_robomaker.py b/tests/test_robomaker/test_robomaker.py new file mode 100644 index 000000000..48c952a54 --- /dev/null +++ b/tests/test_robomaker/test_robomaker.py @@ -0,0 +1,36 @@ +"""Unit tests for robomaker-supported APIs.""" +import boto3 + +from moto import mock_robomaker + +# See our Development Tips on writing tests for hints on how to write good tests: +# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html + + +@mock_robomaker +def test_robot_application(): + client = boto3.client("robomaker", region_name="eu-west-1") + app = client.create_robot_application( + name="viki", + sources=[{"s3Bucket": "sth", "s3Key": "else"}], + robotSoftwareSuite={"name": "ROS", "version": "Kinetic"}, + ) + assert "robot-application/viki" in app["arn"] + assert app["name"] == "viki" + assert app["sources"] == [{"s3Bucket": "sth", "s3Key": "else"}] + assert app["robotSoftwareSuite"] == {"name": "ROS", "version": "Kinetic"} + + app = client.describe_robot_application(application="viki") + assert "robot-application/viki" in app["arn"] + assert app["name"] == "viki" + assert app["sources"] == [{"s3Bucket": "sth", "s3Key": "else"}] + assert app["robotSoftwareSuite"] == {"name": "ROS", "version": "Kinetic"} + + apps = client.list_robot_applications()["robotApplicationSummaries"] + assert len(apps) == 1 + assert apps[0]["name"] == "viki" + + client.delete_robot_application(application="viki") + + apps = client.list_robot_applications()["robotApplicationSummaries"] + assert len(apps) == 0