diff --git a/moto/greengrass/exceptions.py b/moto/greengrass/exceptions.py index 02ba702c113..8c639f8c871 100644 --- a/moto/greengrass/exceptions.py +++ b/moto/greengrass/exceptions.py @@ -21,3 +21,9 @@ class VersionNotFoundException(GreengrassClientError): def __init__(self, msg): self.code = 404 super().__init__("VersionNotFoundException", msg) + + +class InvalidInputException(GreengrassClientError): + def __init__(self, msg): + self.code = 400 + super().__init__("InvalidInputException", msg) diff --git a/moto/greengrass/models.py b/moto/greengrass/models.py index 787121619ea..4bc0f9d119d 100644 --- a/moto/greengrass/models.py +++ b/moto/greengrass/models.py @@ -7,6 +7,7 @@ from .exceptions import ( GreengrassClientError, IdNotFoundException, + InvalidInputException, InvalidContainerDefinitionException, VersionNotFoundException, ) @@ -354,6 +355,29 @@ def create_resource_definition(self, name, initial_version): return resource_def + def list_resource_definitions(self): + return self.resource_definitions + + def get_resource_definition(self, resource_definition_id): + + if resource_definition_id not in self.resource_definitions: + raise IdNotFoundException("That Resource List Definition does not exist.") + return self.resource_definitions[resource_definition_id] + + def delete_resource_definition(self, resource_definition_id): + if resource_definition_id not in self.resource_definitions: + raise IdNotFoundException("That resources definition does not exist.") + del self.resource_definitions[resource_definition_id] + del self.resource_definition_versions[resource_definition_id] + + def update_resource_definition(self, resource_definition_id, name): + + if name == "": + raise InvalidInputException("Invalid resource name.") + if resource_definition_id not in self.resource_definitions: + raise IdNotFoundException("That resources definition does not exist.") + self.resource_definitions[resource_definition_id].name = name + def create_resource_definition_version(self, resource_definition_id, resources): if resource_definition_id not in self.resource_definitions: diff --git a/moto/greengrass/responses.py b/moto/greengrass/responses.py index 49d6ae4a717..161ec353cb8 100644 --- a/moto/greengrass/responses.py +++ b/moto/greengrass/responses.py @@ -236,6 +236,9 @@ def resource_definitions(self, request, full_url, headers): if self.method == "POST": return self.create_resource_definition() + if self.method == "GET": + return self.list_resource_definitions() + def create_resource_definition(self): initial_version = self._get_param("InitialVersion") @@ -245,6 +248,51 @@ def create_resource_definition(self): ) return 201, {"status": 201}, json.dumps(res.to_dict()) + def list_resource_definitions(self): + + res = self.greengrass_backend.list_resource_definitions() + return ( + 200, + {"status": 200}, + json.dumps({"Definitions": [i.to_dict() for i in res.values()]}), + ) + + def resource_definition(self, request, full_url, headers): + self.setup_class(request, full_url, headers) + + if self.method == "GET": + return self.get_resource_definition() + + if self.method == "DELETE": + return self.delete_resource_definition() + + if self.method == "PUT": + return self.update_resource_definition() + + def get_resource_definition(self): + resource_definition_id = self.path.split("/")[-1] + res = self.greengrass_backend.get_resource_definition( + resource_definition_id=resource_definition_id + ) + return 200, {"status": 200}, json.dumps(res.to_dict()) + + def delete_resource_definition(self): + + resource_definition_id = self.path.split("/")[-1] + self.greengrass_backend.delete_resource_definition( + resource_definition_id=resource_definition_id + ) + return 200, {"status": 200}, json.dumps({}) + + def update_resource_definition(self): + + resource_definition_id = self.path.split("/")[-1] + name = self._get_param("Name") + self.greengrass_backend.update_resource_definition( + resource_definition_id=resource_definition_id, name=name + ) + return 200, {"status": 200}, json.dumps({}) + def resource_definition_versions(self, request, full_url, headers): self.setup_class(request, full_url, headers) diff --git a/moto/greengrass/urls.py b/moto/greengrass/urls.py index 5a237da0a4d..0ba7a078831 100644 --- a/moto/greengrass/urls.py +++ b/moto/greengrass/urls.py @@ -18,5 +18,6 @@ "{0}/greengrass/definition/devices/(?P[^/]+)/versions$": response.device_definition_versions, "{0}/greengrass/definition/devices/(?P[^/]+)/versions/(?P[^/]+)/?$": response.device_definition_version, "{0}/greengrass/definition/resources$": response.resource_definitions, + "{0}/greengrass/definition/resources/(?P[^/]+)/?$": response.resource_definition, "{0}/greengrass/definition/resources/(?P[^/]+)/versions$": response.resource_definition_versions, } diff --git a/tests/test_greengrass/test_greengrass_resource.py b/tests/test_greengrass/test_greengrass_resource.py index 272b96d4689..193dce4040d 100644 --- a/tests/test_greengrass/test_greengrass_resource.py +++ b/tests/test_greengrass/test_greengrass_resource.py @@ -270,3 +270,231 @@ def test_create_resources_definition_version_with_invalid_local_device_resource( f", but got: {source_path}])" ) ex.value.response["Error"]["Code"].should.equal("400") + + +@freezegun.freeze_time("2022-06-01 12:00:00") +@mock_greengrass +def test_list_resources(): + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Resources": [ + { + "Id": "123", + "Name": "test_directory", + "ResourceDataContainer": { + "LocalVolumeResourceData": { + "DestinationPath": "/test_dir", + "GroupOwnerSetting": {"AutoAddGroupOwner": True}, + "SourcePath": "/home/ggc_user/test_dir", + } + }, + } + ] + } + resource_name = "MotoTestResource" + create_res = client.create_resource_definition( + InitialVersion=init_ver, Name=resource_name + ) + + res_def_id = create_res["Id"] + res_def_arn = create_res["Arn"] + res_ver = create_res["LatestVersion"] + res_ver_arn = create_res["LatestVersionArn"] + + res = client.list_resource_definitions() + res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + res.should.have.key("Definitions") + res["Definitions"].should.have.length_of(1) + definition = res["Definitions"][0] + definition.should.have.key("Id").equals(res_def_id) + definition.should.have.key("Arn").equals(res_def_arn) + definition.should.have.key("LatestVersion").equals(res_ver) + definition.should.have.key("LatestVersionArn").equals(res_ver_arn) + + if not TEST_SERVER_MODE: + definition.should.have.key("CreationTimestamp").equals( + "2022-06-01T12:00:00.000Z" + ) + definition.should.have.key("LastUpdatedTimestamp").equals( + "2022-06-01T12:00:00.000Z" + ) + + +@freezegun.freeze_time("2022-06-01 12:00:00") +@mock_greengrass +def test_get_resource_definition(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Resources": [ + { + "Id": "123", + "Name": "test_directory", + "ResourceDataContainer": { + "LocalVolumeResourceData": { + "DestinationPath": "/test_dir", + "GroupOwnerSetting": {"AutoAddGroupOwner": True}, + "SourcePath": "/home/ggc_user/test_dir", + } + }, + } + ] + } + resource_name = "MotoTestResource" + create_res = client.create_resource_definition( + InitialVersion=init_ver, Name=resource_name + ) + + resource_def_id = create_res["Id"] + arn = create_res["Arn"] + latest_version = create_res["LatestVersion"] + latest_version_arn = create_res["LatestVersionArn"] + + get_res = client.get_resource_definition(ResourceDefinitionId=resource_def_id) + + get_res.should.have.key("Name").equals(resource_name) + get_res.should.have.key("Arn").equals(arn) + get_res.should.have.key("Id").equals(resource_def_id) + get_res.should.have.key("LatestVersion").equals(latest_version) + get_res.should.have.key("LatestVersionArn").equals(latest_version_arn) + + if not TEST_SERVER_MODE: + get_res.should.have.key("CreationTimestamp").equal("2022-06-01T12:00:00.000Z") + get_res.should.have.key("LastUpdatedTimestamp").equals( + "2022-06-01T12:00:00.000Z" + ) + + +@mock_greengrass +def test_get_resource_definition_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + with pytest.raises(ClientError) as ex: + client.get_resource_definition( + ResourceDefinitionId="76f22a66-176a-4474-b450-04099dc4b069" + ) + ex.value.response["Error"]["Message"].should.equal( + "That Resource List Definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@mock_greengrass +def test_delete_resource_definition(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Resources": [ + { + "Id": "123", + "Name": "test_directory", + "ResourceDataContainer": { + "LocalVolumeResourceData": { + "DestinationPath": "/test_dir", + "GroupOwnerSetting": {"AutoAddGroupOwner": True}, + "SourcePath": "/home/ggc_user/test_dir", + } + }, + } + ] + } + create_res = client.create_resource_definition( + InitialVersion=init_ver, Name="TestResource" + ) + + resource_def_id = create_res["Id"] + del_res = client.delete_resource_definition(ResourceDefinitionId=resource_def_id) + del_res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + +@mock_greengrass +def test_delete_resource_definition_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + + with pytest.raises(ClientError) as ex: + client.delete_resource_definition( + ResourceDefinitionId="6fbffc21-989e-4d29-a793-a42f450a78c6" + ) + ex.value.response["Error"]["Message"].should.equal( + "That resources definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException") + + +@mock_greengrass +def test_update_resource_definition(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Resources": [ + { + "Id": "123", + "Name": "test_directory", + "ResourceDataContainer": { + "LocalVolumeResourceData": { + "DestinationPath": "/test_dir", + "GroupOwnerSetting": {"AutoAddGroupOwner": True}, + "SourcePath": "/home/ggc_user/test_dir", + } + }, + } + ] + } + create_res = client.create_resource_definition( + InitialVersion=init_ver, Name="TestResource" + ) + resource_def_id = create_res["Id"] + updated_resource_name = "UpdatedResource" + update_res = client.update_resource_definition( + ResourceDefinitionId=resource_def_id, Name=updated_resource_name + ) + update_res["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + + get_res = client.get_resource_definition(ResourceDefinitionId=resource_def_id) + get_res.should.have.key("Name").equals(updated_resource_name) + + +@mock_greengrass +def test_update_device_definition_with_empty_name(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + init_ver = { + "Resources": [ + { + "Id": "123", + "Name": "test_directory", + "ResourceDataContainer": { + "LocalVolumeResourceData": { + "DestinationPath": "/test_dir", + "GroupOwnerSetting": {"AutoAddGroupOwner": True}, + "SourcePath": "/home/ggc_user/test_dir", + } + }, + } + ] + } + create_res = client.create_resource_definition( + InitialVersion=init_ver, Name="TestResource" + ) + resource_def_id = create_res["Id"] + + with pytest.raises(ClientError) as ex: + client.update_resource_definition(ResourceDefinitionId=resource_def_id, Name="") + ex.value.response["Error"]["Message"].should.equal("Invalid resource name.") + ex.value.response["Error"]["Code"].should.equal("InvalidInputException") + + +@mock_greengrass +def test_update_resource_definition_with_invalid_id(): + + client = boto3.client("greengrass", region_name="ap-northeast-1") + + with pytest.raises(ClientError) as ex: + client.update_resource_definition( + ResourceDefinitionId="6fbffc21-989e-4d29-a793-a42f450a78c6", Name="123" + ) + ex.value.response["Error"]["Message"].should.equal( + "That resources definition does not exist." + ) + ex.value.response["Error"]["Code"].should.equal("IdNotFoundException")