From cf5007b97dba86dbce2fabd0ff313e9a78da1185 Mon Sep 17 00:00:00 2001 From: Alexandre Blanchet Date: Wed, 7 Jul 2021 11:11:19 +0100 Subject: [PATCH] Add support for remove_flow_output and remove_flow_vpc_interface (#4058) Co-authored-by: Alexandre Blanchet --- IMPLEMENTATION_COVERAGE.md | 4 +- moto/mediaconnect/models.py | 28 ++++++ moto/mediaconnect/responses.py | 21 +++++ moto/mediaconnect/urls.py | 2 + tests/test_mediaconnect/test_mediaconnect.py | 91 +++++++++++++++++++- 5 files changed, 143 insertions(+), 3 deletions(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index dc27017d2..9ba8e804c 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -6755,9 +6755,9 @@ - [ ] list_reservations - [X] list_tags_for_resource - [ ] purchase_offering -- [ ] remove_flow_output +- [X] remove_flow_output - [ ] remove_flow_source -- [ ] remove_flow_vpc_interface +- [X] remove_flow_vpc_interface - [ ] revoke_flow_entitlement - [X] start_flow - [X] stop_flow diff --git a/moto/mediaconnect/models.py b/moto/mediaconnect/models.py index a48f42e8d..907faac5e 100644 --- a/moto/mediaconnect/models.py +++ b/moto/mediaconnect/models.py @@ -205,6 +205,34 @@ class MediaConnectBackend(BaseBackend): ) return flow_arn, flow.outputs + def remove_flow_vpc_interface(self, flow_arn, vpc_interface_name): + if flow_arn in self._flows: + flow = self._flows[flow_arn] + flow.vpc_interfaces = [ + vpc_interface + for vpc_interface in self._flows[flow_arn].vpc_interfaces + if vpc_interface["name"] != vpc_interface_name + ] + else: + raise NotFoundException( + message="flow with arn={} not found".format(flow_arn) + ) + return flow_arn, vpc_interface_name + + def remove_flow_output(self, flow_arn, output_name): + if flow_arn in self._flows: + flow = self._flows[flow_arn] + flow.outputs = [ + output + for output in self._flows[flow_arn].outputs + if output["name"] != output_name + ] + else: + raise NotFoundException( + message="flow with arn={} not found".format(flow_arn) + ) + return flow_arn, output_name + # add methods from here diff --git a/moto/mediaconnect/responses.py b/moto/mediaconnect/responses.py index 495463f98..b6c0bf3ed 100644 --- a/moto/mediaconnect/responses.py +++ b/moto/mediaconnect/responses.py @@ -90,6 +90,19 @@ class MediaConnectResponse(BaseResponse): ) return json.dumps(dict(flow_arn=flow_arn, vpc_interfaces=vpc_interfaces)) + def remove_flow_vpc_interface(self): + flow_arn = unquote(self._get_param("flowArn")) + vpc_interface_name = unquote(self._get_param("vpcInterfaceName")) + ( + flow_arn, + vpc_interface_name, + ) = self.mediaconnect_backend.remove_flow_vpc_interface( + flow_arn=flow_arn, vpc_interface_name=vpc_interface_name + ) + return json.dumps( + dict(flow_arn=flow_arn, vpc_interface_name=vpc_interface_name) + ) + def add_flow_outputs(self): flow_arn = unquote(self._get_param("flowArn")) outputs = self._get_param("outputs") @@ -98,4 +111,12 @@ class MediaConnectResponse(BaseResponse): ) return json.dumps(dict(flow_arn=flow_arn, outputs=outputs)) + def remove_flow_output(self): + flow_arn = unquote(self._get_param("flowArn")) + output_name = unquote(self._get_param("outputArn")) + flow_arn, output_name = self.mediaconnect_backend.remove_flow_output( + flow_arn=flow_arn, output_name=output_name + ) + return json.dumps(dict(flow_arn=flow_arn, output_name=output_name)) + # add methods from here diff --git a/moto/mediaconnect/urls.py b/moto/mediaconnect/urls.py index c5b5dfc1a..f83ccc4b8 100644 --- a/moto/mediaconnect/urls.py +++ b/moto/mediaconnect/urls.py @@ -13,7 +13,9 @@ url_paths = { "{0}/v1/flows": response.dispatch, "{0}/v1/flows/(?P[^/.]+)": response.dispatch, "{0}/v1/flows/(?P[^/.]+)/vpcInterfaces": response.dispatch, + "{0}/v1/flows/(?P[^/.]+)/vpcInterfaces/(?P[^/.]+)": response.dispatch, "{0}/v1/flows/(?P[^/.]+)/outputs": response.dispatch, + "{0}/v1/flows/(?P[^/.]+)/outputs/(?P[^/.]+)": response.dispatch, "{0}/v1/flows/start/(?P[^/.]+)": response.dispatch, "{0}/v1/flows/stop/(?P[^/.]+)": response.dispatch, "{0}/tags/(?P[^/.]+)": response.dispatch, diff --git a/tests/test_mediaconnect/test_mediaconnect.py b/tests/test_mediaconnect/test_mediaconnect.py index 07a5d8209..55c85462b 100644 --- a/tests/test_mediaconnect/test_mediaconnect.py +++ b/tests/test_mediaconnect/test_mediaconnect.py @@ -1,7 +1,6 @@ from __future__ import unicode_literals import boto3 -import botocore import pytest import sure # noqa from botocore.exceptions import ClientError @@ -209,6 +208,53 @@ def test_add_flow_vpc_interfaces_fails(): ) +@mock_mediaconnect +def test_remove_flow_vpc_interface_succeeds(): + client = boto3.client("mediaconnect", region_name=region) + channel_config = _create_flow_config("test Flow 1") + + create_response = client.create_flow(**channel_config) + create_response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + create_response["Flow"]["Status"].should.equal("STANDBY") + flow_arn = create_response["Flow"]["FlowArn"] + + client.add_flow_vpc_interfaces( + FlowArn=flow_arn, + VpcInterfaces=[ + { + "Name": "VPCInterface", + "SubnetId": "", + "SecurityGroupIds": [], + "RoleArn": "", + } + ], + ) + + describe_response = client.describe_flow(FlowArn=flow_arn) + describe_response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + len(describe_response["Flow"]["VpcInterfaces"]).should.equal(1) + + client.remove_flow_vpc_interface(FlowArn=flow_arn, VpcInterfaceName="VPCInterface") + + describe_response = client.describe_flow(FlowArn=flow_arn) + len(describe_response["Flow"]["VpcInterfaces"]).should.equal(0) + + +@mock_mediaconnect +def test_remove_flow_vpc_interface_fails(): + client = boto3.client("mediaconnect", region_name=region) + flow_arn = "unknown-flow" + with pytest.raises(ClientError) as err: + client.remove_flow_vpc_interface( + FlowArn=flow_arn, VpcInterfaceName="VPCInterface" + ) + err = err.value.response["Error"] + err["Code"].should.equal("NotFoundException") + err["Message"].should.equal( + "flow with arn=unknown-flow not found".format(str(flow_arn)) + ) + + @mock_mediaconnect def test_add_flow_outputs_succeeds(): client = boto3.client("mediaconnect", region_name=region) @@ -246,3 +292,46 @@ def test_add_flow_outputs_fails(): err["Message"].should.equal( "flow with arn=unknown-flow not found".format(str(flow_arn)) ) + + +@mock_mediaconnect +def test_remove_flow_output_fails(): + client = boto3.client("mediaconnect", region_name=region) + flow_arn = "unknown-flow" + output_arn = "unknown-arn" + with pytest.raises(ClientError) as err: + client.remove_flow_output( + FlowArn=flow_arn, OutputArn=output_arn, + ) + err = err.value.response["Error"] + err["Code"].should.equal("NotFoundException") + err["Message"].should.equal( + "flow with arn=unknown-flow not found".format(str(flow_arn)) + ) + + +@mock_mediaconnect +def test_remove_flow_output_succeeds(): + client = boto3.client("mediaconnect", region_name=region) + channel_config = _create_flow_config("test Flow 1") + + create_response = client.create_flow(**channel_config) + create_response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + create_response["Flow"]["Status"].should.equal("STANDBY") + flow_arn = create_response["Flow"]["FlowArn"] + + client.add_flow_outputs( + FlowArn=flow_arn, + Outputs=[ + {"Description": "string", "Name": "string", "Port": 123, "Protocol": "rist"} + ], + ) + + describe_response = client.describe_flow(FlowArn=flow_arn) + describe_response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200) + len(describe_response["Flow"]["Outputs"]).should.equal(1) + + client.remove_flow_output(FlowArn=flow_arn, OutputArn="string") + + describe_response = client.describe_flow(FlowArn=flow_arn) + len(describe_response["Flow"]["Outputs"]).should.equal(0)