diff --git a/moto/__init__.py b/moto/__init__.py index 920a05a5f..c458f6254 100644 --- a/moto/__init__.py +++ b/moto/__init__.py @@ -122,6 +122,7 @@ mock_kinesisvideoarchivedmedia = lazy_load( mock_medialive = lazy_load(".medialive", "mock_medialive") mock_support = lazy_load(".support", "mock_support") mock_mediaconnect = lazy_load(".mediaconnect", "mock_mediaconnect") +mock_mediapackage = lazy_load(".mediapackage", "mock_mediapackage") # import logging # logging.getLogger('boto').setLevel(logging.CRITICAL) diff --git a/moto/backends.py b/moto/backends.py index b25c45cf3..15c894eb9 100644 --- a/moto/backends.py +++ b/moto/backends.py @@ -79,6 +79,7 @@ BACKENDS = { "forecast": ("forecast", "forecast_backends"), "support": ("support", "support_backends"), "mediaconnect": ("mediaconnect", "mediaconnect_backends"), + "mediapackage": ("mediapackage", "mediapackage_backends"), } diff --git a/moto/mediapackage/__init__.py b/moto/mediapackage/__init__.py new file mode 100644 index 000000000..7e2ed7d52 --- /dev/null +++ b/moto/mediapackage/__init__.py @@ -0,0 +1,6 @@ +from __future__ import unicode_literals +from .models import mediapackage_backends +from ..core.models import base_decorator + +mediapackage_backend = mediapackage_backends["us-east-1"] +mock_mediapackage = base_decorator(mediapackage_backends) diff --git a/moto/mediapackage/exceptions.py b/moto/mediapackage/exceptions.py new file mode 100644 index 000000000..baffc4882 --- /dev/null +++ b/moto/mediapackage/exceptions.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/moto/mediapackage/models.py b/moto/mediapackage/models.py new file mode 100644 index 000000000..207857fd1 --- /dev/null +++ b/moto/mediapackage/models.py @@ -0,0 +1,202 @@ +from __future__ import unicode_literals +from boto3 import Session +from moto.core import BaseBackend, BaseModel +from collections import OrderedDict + + +class Channel(BaseModel): + def __init__(self, *args, **kwargs): + self.arn = kwargs.get("arn") + self.channel_id = kwargs.get("channel_id") + self.description = kwargs.get("description") + self.tags = kwargs.get("tags") + + def to_dict(self, exclude=None): + data = { + "arn": self.arn, + "id": self.channel_id, + "description": self.description, + "tags": self.tags, + } + if exclude: + for key in exclude: + del data[key] + return data + + +class OriginEndpoint(BaseModel): + def __init__(self, *args, **kwargs): + self.arn = kwargs.get("arn") + self.authorization = kwargs.get("authorization") + self.channel_id = kwargs.get("channel_id") + self.cmaf_package = kwargs.get("cmaf_package") + self.dash_package = kwargs.get("dash_package") + self.description = kwargs.get("description") + self.hls_package = kwargs.get("hls_package") + self.id = kwargs.get("id") + self.manifest_name = kwargs.get("manifest_name") + self.mss_package = kwargs.get("mss_package") + self.origination = kwargs.get("origination") + self.startover_window_seconds = kwargs.get("startover_window_seconds") + self.tags = kwargs.get("tags") + self.time_delay_seconds = kwargs.get("time_delay_seconds") + self.url = kwargs.get("url") + self.whitelist = kwargs.get("whitelist") + + def to_dict(self): + data = { + "arn": self.arn, + "authorization": self.authorization, + "channelId": self.channel_id, + "cmafPackage": self.cmaf_package, + "dashPackage": self.dash_package, + "description": self.description, + "hlsPackage": self.hls_package, + "id": self.id, + "manifestName": self.manifest_name, + "mssPackage": self.mss_package, + "origination": self.origination, + "startoverWindowSeconds": self.startover_window_seconds, + "tags": self.tags, + "timeDelaySeconds": self.time_delay_seconds, + "url": self.url, + "whitelist": self.whitelist, + } + return data + + +class MediaPackageBackend(BaseBackend): + def __init__(self, region_name=None): + super(MediaPackageBackend, self).__init__() + self.region_name = region_name + + def reset(self): + region_name = self.region_name + self.__dict__ = {} + self.__init__(region_name) + self._channels = OrderedDict() + self._origin_endpoints = OrderedDict() + + def create_channel(self, description, id, tags): + arn = "arn:aws:mediapackage:channel:{}".format(id) + channel = Channel( + arn=arn, + description=description, + egress_access_logs={}, + hls_ingest={}, + channel_id=id, + ingress_access_logs={}, + tags=tags, + ) + self._channels[id] = channel + return channel + + def list_channels(self): + channels = list(self._channels.values()) + response_channels = [c.to_dict() for c in channels] + return response_channels + + def describe_channel(self, id): + channel = self._channels[id] + return channel.to_dict() + + def delete_channel(self, id): + channel = self._channels[id] + del self._channels[id] + return channel.to_dict() + + def create_origin_endpoint( + self, + authorization, + channel_id, + cmaf_package, + dash_package, + description, + hls_package, + id, + manifest_name, + mss_package, + origination, + startover_window_seconds, + tags, + time_delay_seconds, + whitelist, + ): + arn = "arn:aws:mediapackage:origin_endpoint:{}".format(id) + url = "https://origin-endpoint.mediapackage.{}.amazonaws.com/{}".format( + self.region_name, id + ) + origin_endpoint = OriginEndpoint( + arn=arn, + authorization=authorization, + channel_id=channel_id, + cmaf_package=cmaf_package, + dash_package=dash_package, + description=description, + hls_package=hls_package, + id=id, + manifest_name=manifest_name, + mss_package=mss_package, + origination=origination, + startover_window_seconds=startover_window_seconds, + tags=tags, + time_delay_seconds=time_delay_seconds, + url=url, + whitelist=whitelist, + ) + self._origin_endpoints[id] = origin_endpoint + return origin_endpoint + + def describe_origin_endpoint(self, id): + origin_endpoint = self._origin_endpoints[id] + return origin_endpoint.to_dict() + + def list_origin_endpoints(self): + origin_endpoints = list(self._origin_endpoints.values()) + response_origin_endpoints = [o.to_dict() for o in origin_endpoints] + return response_origin_endpoints + + def delete_origin_endpoint(self, id): + origin_endpoint = self._origin_endpoints[id] + del self._origin_endpoints[id] + return origin_endpoint.to_dict() + + def update_origin_endpoint( + self, + authorization, + cmaf_package, + dash_package, + description, + hls_package, + id, + manifest_name, + mss_package, + origination, + startover_window_seconds, + time_delay_seconds, + whitelist, + ): + origin_endpoint = self._origin_endpoints[id] + origin_endpoint.authorization = authorization + origin_endpoint.cmaf_package = cmaf_package + origin_endpoint.dash_package = dash_package + origin_endpoint.description = description + origin_endpoint.hls_package = hls_package + origin_endpoint.manifest_name = manifest_name + origin_endpoint.mss_package = mss_package + origin_endpoint.origination = origination + origin_endpoint.startover_window_seconds = startover_window_seconds + origin_endpoint.time_delay_seconds = time_delay_seconds + origin_endpoint.whitelist = whitelist + return origin_endpoint + + +mediapackage_backends = {} +for region in Session().get_available_regions("mediapackage"): + mediapackage_backends[region] = MediaPackageBackend(region) +for region in Session().get_available_regions( + "mediapackage", partition_name="aws-us-gov" +): + mediapackage_backends[region] = MediaPackageBackend(region) +for region in Session().get_available_regions("mediapackage", partition_name="aws-cn"): + mediapackage_backends[region] = MediaPackageBackend(region) diff --git a/moto/mediapackage/responses.py b/moto/mediapackage/responses.py new file mode 100644 index 000000000..633abef67 --- /dev/null +++ b/moto/mediapackage/responses.py @@ -0,0 +1,107 @@ +from __future__ import unicode_literals +from moto.core.responses import BaseResponse +from .models import mediapackage_backends +import json + + +class MediaPackageResponse(BaseResponse): + SERVICE_NAME = "mediapackage" + + @property + def mediapackage_backend(self): + return mediapackage_backends[self.region] + + def create_channel(self): + description = self._get_param("description") + id = self._get_param("id") + tags = self._get_param("tags") + channel = self.mediapackage_backend.create_channel( + description=description, id=id, tags=tags, + ) + return json.dumps(channel.to_dict()) + + def list_channels(self): + channels = self.mediapackage_backend.list_channels() + return json.dumps(dict(channels=channels)) + + def describe_channel(self): + id = self._get_param("id") + return json.dumps(self.mediapackage_backend.describe_channel(id=id)) + + def delete_channel(self): + channel_id = self._get_param("id") + return json.dumps(self.mediapackage_backend.delete_channel(id=channel_id)) + + def create_origin_endpoint(self): + authorization = self._get_param("authorization") + channel_id = self._get_param("channelId") + cmaf_package = self._get_param("cmafPackage") + dash_package = self._get_param("dashPackage") + description = self._get_param("description") + hls_package = self._get_param("hlsPackage") + id = self._get_param("id") + manifest_name = self._get_param("manifestName") + mss_package = self._get_param("mssPackage") + origination = self._get_param("origination") + startover_window_seconds = self._get_int_param("startoverWindowSeconds") + tags = self._get_param("tags") + time_delay_seconds = self._get_int_param("timeDelaySeconds.member") + whitelist = self._get_list_prefix("whitelist.member") + origin_endpoint = self.mediapackage_backend.create_origin_endpoint( + authorization=authorization, + channel_id=channel_id, + cmaf_package=cmaf_package, + dash_package=dash_package, + description=description, + hls_package=hls_package, + id=id, + manifest_name=manifest_name, + mss_package=mss_package, + origination=origination, + startover_window_seconds=startover_window_seconds, + tags=tags, + time_delay_seconds=time_delay_seconds, + whitelist=whitelist, + ) + return json.dumps(origin_endpoint.to_dict()) + + def list_origin_endpoints(self): + origin_endpoints = self.mediapackage_backend.list_origin_endpoints() + return json.dumps(dict(originEndpoints=origin_endpoints)) + + def describe_origin_endpoint(self): + id = self._get_param("id") + return json.dumps(self.mediapackage_backend.describe_origin_endpoint(id=id)) + + def delete_origin_endpoint(self): + id = self._get_param("id") + return json.dumps(self.mediapackage_backend.delete_origin_endpoint(id=id)) + + def update_origin_endpoint(self): + authorization = self._get_param("authorization") + cmaf_package = self._get_param("cmafPackage") + dash_package = self._get_param("dashPackage") + description = self._get_param("description") + hls_package = self._get_param("hlsPackage") + id = self._get_param("id") + manifest_name = self._get_param("manifestName") + mss_package = self._get_param("mssPackage") + origination = self._get_param("origination") + startover_window_seconds = self._get_int_param("startoverWindowSeconds") + time_delay_seconds = self._get_int_param("timeDelaySeconds") + whitelist = self._get_list_prefix("whitelist.member") + origin_endpoint = self.mediapackage_backend.update_origin_endpoint( + authorization=authorization, + cmaf_package=cmaf_package, + dash_package=dash_package, + description=description, + hls_package=hls_package, + id=id, + manifest_name=manifest_name, + mss_package=mss_package, + origination=origination, + startover_window_seconds=startover_window_seconds, + time_delay_seconds=time_delay_seconds, + whitelist=whitelist, + ) + return json.dumps(origin_endpoint.to_dict()) diff --git a/moto/mediapackage/urls.py b/moto/mediapackage/urls.py new file mode 100644 index 000000000..2253e4a2d --- /dev/null +++ b/moto/mediapackage/urls.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals +from .responses import MediaPackageResponse + +url_bases = [ + "https?://mediapackage.(.+).amazonaws.com", +] + + +response = MediaPackageResponse() + + +url_paths = { + "{0}/channels": response.dispatch, + "{0}/channels/(?P[^/.]+)": response.dispatch, + "{0}/origin_endpoints": response.dispatch, + "{0}/origin_endpoints/(?P[^/.]+)": response.dispatch, +} diff --git a/tests/test_medialive/test_server.py b/tests/test_medialive/test_server.py index df51acf8e..0e4c649b1 100644 --- a/tests/test_medialive/test_server.py +++ b/tests/test_medialive/test_server.py @@ -16,7 +16,6 @@ def test_medialive_list_channels(): test_client = backend.test_client() res = test_client.get("/prod/channels") - result = res.data.decode("utf-8") result.should.contain('"channels": []') diff --git a/tests/test_mediapackage/test_mediapackage.py b/tests/test_mediapackage/test_mediapackage.py new file mode 100644 index 000000000..9e883ddf3 --- /dev/null +++ b/tests/test_mediapackage/test_mediapackage.py @@ -0,0 +1,207 @@ +from __future__ import unicode_literals + +import boto3 +import sure # noqa +from moto import mock_mediapackage + +region = "eu-west-1" + + +def _create_channel_config(**kwargs): + id = kwargs.get("id", "channel-id") + description = kwargs.get("description", "Awesome channel!") + tags = kwargs.get("tags", {"Customer": "moto"}) + channel_config = dict(Description=description, Id=id, Tags=tags,) + return channel_config + + +def _create_origin_endpoint_config(**kwargs): + authorization = kwargs.get( + "authorization", + {"CdnIdentifierSecret": "cdn-id-secret", "SecretsRoleArn": "secrets-arn"}, + ) + channel_id = kwargs.get("channel_id", "channel-id") + cmaf_package = kwargs.get("cmafpackage", {"HlsManifests": []}) + dash_package = kwargs.get("dash_package", {"AdTriggers": []}) + description = kwargs.get("description", "channel-description") + hls_package = kwargs.get("hls_package", {"AdMarkers": "NONE"}) + id = kwargs.get("id", "origin-endpoint-id") + manifest_name = kwargs.get("manifest_name", "manifest-name") + mss_package = kwargs.get("mss_package", {"ManifestWindowSeconds": 1}) + origination = kwargs.get("origination", "ALLOW") + startover_window_seconds = kwargs.get("startover_window_seconds", 1) + tags = kwargs.get("tags", {"Customer": "moto"}) + time_delay_seconds = kwargs.get("time_delay_seconds", 1) + whitelist = kwargs.get("whitelist", ["whitelist"]) + origin_endpoint_config = dict( + Authorization=authorization, + ChannelId=channel_id, + CmafPackage=cmaf_package, + DashPackage=dash_package, + Description=description, + HlsPackage=hls_package, + Id=id, + ManifestName=manifest_name, + MssPackage=mss_package, + Origination=origination, + StartoverWindowSeconds=startover_window_seconds, + Tags=tags, + TimeDelaySeconds=time_delay_seconds, + Whitelist=whitelist, + ) + return origin_endpoint_config + + +@mock_mediapackage +def test_create_channel_succeeds(): + client = boto3.client("mediapackage", region_name=region) + channel_config = _create_channel_config() + + response = client.create_channel(**channel_config) + response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + response["Arn"].should.equal( + "arn:aws:mediapackage:channel:{}".format(response["Id"]) + ) + response["Description"].should.equal("Awesome channel!") + response["Id"].should.equal("channel-id") + response["Tags"]["Customer"].should.equal("moto") + + +@mock_mediapackage +def test_describe_channel_succeeds(): + client = boto3.client("mediapackage", region_name=region) + channel_config = _create_channel_config() + + create_response = client.create_channel(**channel_config) + describe_response = client.describe_channel(Id=create_response["Id"]) + describe_response["Arn"].should.equal( + "arn:aws:mediapackage:channel:{}".format(describe_response["Id"]) + ) + describe_response["Description"].should.equal(channel_config["Description"]) + describe_response["Tags"]["Customer"].should.equal("moto") + + +@mock_mediapackage +def test_delete_channel_successfully_deletes(): + client = boto3.client("mediapackage", region_name=region) + channel_config = _create_channel_config() + create_response = client.create_channel(**channel_config) + # Before deletion + list_response = client.list_channels() + channels_list = list_response["Channels"] + client.delete_channel(Id=create_response["Id"]) + # After deletion + post_deletion_list_response = client.list_channels() + post_deletion_channels_list = post_deletion_list_response["Channels"] + len(post_deletion_channels_list).should.equal(len(channels_list) - 1) + + +@mock_mediapackage +def test_list_channels_succeds(): + channels_list = [] + client = boto3.client("mediapackage", region_name=region) + channel_config = _create_channel_config() + len(channels_list).should.equal(0) + client.create_channel(**channel_config) + list_response = client.list_channels() + channels_list = list_response["Channels"] + len(channels_list).should.equal(1) + first_channel = channels_list[0] + first_channel["Arn"].should.equal( + "arn:aws:mediapackage:channel:{}".format(first_channel["Id"]) + ) + first_channel["Description"].should.equal(channel_config["Description"]) + first_channel["Tags"]["Customer"].should.equal("moto") + + +@mock_mediapackage +def test_create_origin_endpoint_succeeds(): + client = boto3.client("mediapackage", region_name=region) + origin_endpoint_config = _create_origin_endpoint_config() + + response = client.create_origin_endpoint(**origin_endpoint_config) + response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + response["Arn"].should.equal( + "arn:aws:mediapackage:origin_endpoint:{}".format(response["Id"]) + ) + response["ChannelId"].should.equal(origin_endpoint_config["ChannelId"]) + response["Description"].should.equal(origin_endpoint_config["Description"]) + response["HlsPackage"].should.equal(origin_endpoint_config["HlsPackage"]) + response["Origination"].should.equal("ALLOW") + + +@mock_mediapackage +def test_describe_origin_endpoint_succeeds(): + client = boto3.client("mediapackage", region_name=region) + origin_endpoint_config = _create_origin_endpoint_config() + + create_response = client.create_origin_endpoint(**origin_endpoint_config) + describe_response = client.describe_origin_endpoint(Id=create_response["Id"]) + describe_response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + describe_response["Arn"].should.equal( + "arn:aws:mediapackage:origin_endpoint:{}".format(describe_response["Id"]) + ) + describe_response["ChannelId"].should.equal(origin_endpoint_config["ChannelId"]) + describe_response["Description"].should.equal(origin_endpoint_config["Description"]) + describe_response["HlsPackage"].should.equal(origin_endpoint_config["HlsPackage"]) + describe_response["Origination"].should.equal("ALLOW") + describe_response["Url"].should.equal( + "https://origin-endpoint.mediapackage.{}.amazonaws.com/{}".format( + region, describe_response["Id"] + ) + ) + + +@mock_mediapackage +def test_delete_origin_endpoint_succeeds(): + client = boto3.client("mediapackage", region_name=region) + origin_endpoint_config = _create_origin_endpoint_config() + create_response = client.create_origin_endpoint(**origin_endpoint_config) + list_response = client.list_origin_endpoints() + # Before deletion + origin_endpoints_list = list_response["OriginEndpoints"] + client.delete_origin_endpoint(Id=create_response["Id"]) + # After deletion + post_deletion_list_response = client.list_origin_endpoints() + post_deletion_origin_endpoints_list = post_deletion_list_response["OriginEndpoints"] + len(post_deletion_origin_endpoints_list).should.equal( + len(origin_endpoints_list) - 1 + ) + + +@mock_mediapackage +def test_update_origin_endpoint_succeeds(): + client = boto3.client("mediapackage", region_name=region) + origin_endpoint_config = _create_origin_endpoint_config() + create_response = client.create_origin_endpoint(**origin_endpoint_config) + update_response = client.update_origin_endpoint( + Id=create_response["Id"], + Description="updated-channel-description", + ManifestName="updated-manifest-name", + ) + update_response["Description"].should.equal("updated-channel-description") + update_response["ManifestName"].should.equal("updated-manifest-name") + + +@mock_mediapackage +def test_list_origin_endpoint_succeeds(): + origin_endpoints_list = [] + client = boto3.client("mediapackage", region_name=region) + origin_endpoint_config = _create_origin_endpoint_config() + len(origin_endpoints_list).should.equal(0) + client.create_origin_endpoint(**origin_endpoint_config) + list_response = client.list_origin_endpoints() + origin_endpoints_list = list_response["OriginEndpoints"] + len(origin_endpoints_list).should.equal(1) + first_origin_endpoint = origin_endpoints_list[0] + first_origin_endpoint["Arn"].should.equal( + "arn:aws:mediapackage:origin_endpoint:{}".format(first_origin_endpoint["Id"]) + ) + first_origin_endpoint["ChannelId"].should.equal(origin_endpoint_config["ChannelId"]) + first_origin_endpoint["Description"].should.equal( + origin_endpoint_config["Description"] + ) + first_origin_endpoint["HlsPackage"].should.equal( + origin_endpoint_config["HlsPackage"] + ) + first_origin_endpoint["Origination"].should.equal("ALLOW") diff --git a/tests/test_mediapackage/test_server.py b/tests/test_mediapackage/test_server.py new file mode 100644 index 000000000..05adfb66e --- /dev/null +++ b/tests/test_mediapackage/test_server.py @@ -0,0 +1,30 @@ +from __future__ import unicode_literals + +import sure # noqa + +import moto.server as server +from moto import mock_mediapackage + +""" +Test the different server responses +""" + + +@mock_mediapackage +def test_mediapackage_list_channels(): + backend = server.create_backend_app("mediapackage") + test_client = backend.test_client() + + res = test_client.get("/channels") + result = res.data.decode("utf-8") + result.should.contain('"channels": []') + + +@mock_mediapackage +def test_mediapackage_list_origin_endpoints(): + backend = server.create_backend_app("mediapackage") + test_client = backend.test_client() + + res = test_client.get("/origin_endpoints") + result = res.data.decode("utf-8") + result.should.contain('"originEndpoints": []')