Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Link v2 integration to lambda function #5481

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions samcli/hook_packages/terraform/hooks/prepare/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,20 @@ class GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationExcepti
"""


class OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException(OneResourceLinkingLimitationException):
"""
Exception specific for Gateway V2 Integration linking to more than one Lambda function
"""


class GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException(
LocalVariablesLinkingLimitationException
):
"""
Exception specific for Gateway V2 Integration linking to Lambda Function using locals.
"""


class InvalidSamMetadataPropertiesException(UserException):
pass

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
GatewayResourceToApiGatewayIntegrationResponseLocalVariablesLinkingLimitationException,
GatewayResourceToApiGatewayMethodLocalVariablesLinkingLimitationException,
GatewayResourceToGatewayRestApiLocalVariablesLinkingLimitationException,
GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException,
GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationException,
InvalidResourceLinkingException,
LambdaFunctionToApiGatewayIntegrationLocalVariablesLinkingLimitationException,
Expand All @@ -27,6 +28,7 @@
OneGatewayResourceToApiGatewayIntegrationResponseLinkingLimitationException,
OneGatewayResourceToApiGatewayMethodLinkingLimitationException,
OneGatewayResourceToRestApiLinkingLimitationException,
OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException,
OneGatewayV2RouteToGatewayV2IntegrationLinkingLimitationException,
OneLambdaFunctionResourceToApiGatewayIntegrationLinkingLimitationException,
OneLambdaLayerLinkingLimitationException,
Expand Down Expand Up @@ -1807,3 +1809,86 @@ def _link_gateway_v2_route_to_integration(
tf_destination_value_extractor_from_link_field_value_function=_extract_gateway_v2_integration_id_from_route_target_value,
)
ResourceLinker(resource_linking_pair).link_resources()


def _link_gateway_v2_integration_to_lambda_function_callback(
gateway_v2_integration_cfn_resource: Dict, lambda_function_resources: List[ReferenceType]
):
"""
Callback function that is used by the linking algorithm to update a CFN V2 Route Resource with a reference to
the Gateway V2 integration resource

Parameters
----------
gateway_v2_integration_cfn_resource: Dict
API Gateway V2 Integration CFN resource
lambda_function_resources: List[ReferenceType]
List of referenced lambda function either as the logical id of Integration resources
defined in the customer project, or the invocation ARN values for actual functions defined
in customer's account. This list should always contain one element only.
"""
if len(lambda_function_resources) > 1:
raise InvalidResourceLinkingException("Could not link multiple lambda functions to one Gateway V2 Integration")

if not lambda_function_resources:
LOG.info(
"Unable to find any references to Lambda functions, skip linking Gateway V2 Integration to Lambda Functions"
)
return

logical_id = lambda_function_resources[0]
gateway_v2_integration_cfn_resource["Properties"]["IntegrationUri"] = (
{
"Fn::Join": [
"",
[
"arn:",
{"Ref": "AWS::Partition"},
":apigateway:",
{"Ref": "AWS::Region"},
":lambda:path/2015-03-31/functions/",
{"Fn::GetAtt": [logical_id.value, "Arn"]},
"/invocations",
],
]
}
if isinstance(logical_id, LogicalIdReference)
else logical_id.value
)


def _link_gateway_v2_integration_to_lambda_function(
v2_gateway_integration_config_resources: Dict[str, TFResource],
v2_gateway_integration_config_address_cfn_resources_map: Dict[str, List],
lambda_functions_resources: Dict[str, Dict],
) -> None:
"""
Iterate through all the resources and link the corresponding
Gateway V2 integration resources to each lambda functions

Parameters
----------
v2_gateway_integration_config_resources: Dict[str, TFResource]
Dictionary of configuration Gateway Integrations
v2_gateway_integration_config_address_cfn_resources_map: Dict[str, List]
Dictionary containing resolved configuration addresses matched up to the cfn Gateway Integration
lambda_functions_resources: Dict[str, Dict]
Dictionary of all Terraform lambda functions resources (not configuration resources).
The dictionary's key is the calculated logical id for each resource.
"""
exceptions = ResourcePairExceptions(
multiple_resource_linking_exception=OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException,
local_variable_linking_exception=GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException,
)
resource_linking_pair = ResourceLinkingPair(
source_resource_cfn_resource=v2_gateway_integration_config_address_cfn_resources_map,
source_resource_tf_config=v2_gateway_integration_config_resources,
destination_resource_tf=lambda_functions_resources,
tf_destination_attribute_name="invoke_arn",
terraform_link_field_name="integration_uri",
cfn_link_field_name="IntegrationUri",
terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX,
cfn_resource_update_call_back_function=_link_gateway_v2_integration_to_lambda_function_callback,
linking_exceptions=exceptions,
)
ResourceLinker(resource_linking_pair).link_resources()
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
_link_gateway_methods_to_gateway_rest_apis,
_link_gateway_resources_to_gateway_rest_apis,
_link_gateway_stage_to_rest_api,
_link_gateway_v2_integration_to_lambda_function,
_link_gateway_v2_route_to_integration,
_link_lambda_functions_to_layers,
)
Expand Down Expand Up @@ -98,4 +99,9 @@
dest=TF_AWS_API_GATEWAY_V2_INTEGRATION,
linking_func=_link_gateway_v2_route_to_integration,
),
LinkingPairCaller(
source=TF_AWS_API_GATEWAY_V2_INTEGRATION,
dest=TF_AWS_LAMBDA_FUNCTION,
linking_func=_link_gateway_v2_integration_to_lambda_function,
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
GatewayResourceToApiGatewayIntegrationResponseLocalVariablesLinkingLimitationException,
OneGatewayV2RouteToGatewayV2IntegrationLinkingLimitationException,
GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationException,
OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException,
GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException,
)

from samcli.hook_packages.terraform.hooks.prepare.resource_linking import (
Expand Down Expand Up @@ -86,6 +88,8 @@
_link_gateway_v2_route_to_integration,
API_GATEWAY_V2_INTEGRATION_RESOURCE_ADDRESS_PREFIX,
_link_gateway_v2_route_to_integration_callback,
_link_gateway_v2_integration_to_lambda_function_callback,
_link_gateway_v2_integration_to_lambda_function,
_extract_gateway_v2_integration_id_from_route_target_value,
)
from samcli.hook_packages.terraform.hooks.prepare.utilities import get_configuration_address
Expand Down Expand Up @@ -2118,6 +2122,10 @@ def test_link_gateway_integration_to_function_call_back(
_link_gateway_v2_route_to_integration_callback,
"Could not link multiple Gateway V2 Integrations to one Gateway V2 Route",
),
(
_link_gateway_v2_integration_to_lambda_function_callback,
"Could not link multiple lambda functions to one Gateway V2 Integration",
),
]
)
def test_linking_callbacks_raises_multiple_reference_exception(self, linking_call_back_method, expected_message):
Expand All @@ -2133,6 +2141,7 @@ def test_linking_callbacks_raises_multiple_reference_exception(self, linking_cal
(_link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back,),
(_link_gateway_method_to_gateway_authorizer_call_back,),
(_link_gateway_v2_route_to_integration_callback,),
(_link_gateway_v2_integration_to_lambda_function_callback,),
]
)
def test_linking_callbacks_skips_empty_references(self, linking_call_back_method):
Expand Down Expand Up @@ -2474,6 +2483,87 @@ def test__link_gateway_v2_route_to_integration_callback(self, input_gateway_v2_r
input_gateway_v2_route["Properties"]["Target"] = expected_route
self.assertEqual(gateway_resource, input_gateway_v2_route)

@patch(
"samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_integration_to_lambda_function_callback"
)
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker")
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair")
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions")
def test_link_gateway_v2_integration_to_lambda_function(
self,
mock_resource_linking_exceptions,
mock_resource_linking_pair,
mock_resource_linker,
mock_link_gateway_v2_integration_to_lambda_function_callback,
):
integrations_v2_cfn_resources = Mock()
integrations_v2_config_resources = Mock()
lambda_function_tf_resources = Mock()

_link_gateway_v2_integration_to_lambda_function(
integrations_v2_config_resources, integrations_v2_cfn_resources, lambda_function_tf_resources
)

mock_resource_linking_exceptions.assert_called_once_with(
multiple_resource_linking_exception=OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException,
local_variable_linking_exception=GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException,
)

mock_resource_linking_pair.assert_called_once_with(
source_resource_cfn_resource=integrations_v2_cfn_resources,
source_resource_tf_config=integrations_v2_config_resources,
destination_resource_tf=lambda_function_tf_resources,
tf_destination_attribute_name="invoke_arn",
terraform_link_field_name="integration_uri",
cfn_link_field_name="IntegrationUri",
terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX,
cfn_resource_update_call_back_function=mock_link_gateway_v2_integration_to_lambda_function_callback,
linking_exceptions=mock_resource_linking_exceptions(),
)

mock_resource_linker.assert_called_once_with(mock_resource_linking_pair())

@parameterized.expand(
[
(
{
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {"IntegrationUri": "invoke_arn"},
},
[LogicalIdReference("FunctionA")],
{
"Fn::Join": [
"",
[
"arn:",
{"Ref": "AWS::Partition"},
":apigateway:",
{"Ref": "AWS::Region"},
":lambda:path/2015-03-31/functions/",
{"Fn::GetAtt": ["FunctionA", "Arn"]},
"/invocations",
],
]
},
),
(
{
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {"IntegrationUri": "invoke_arn"},
},
[ExistingResourceReference("invoke_arn")],
"invoke_arn",
),
]
)
def test_link_gateway_v2_integration_to_lambda_function_callback(
self, input_gateway_v2_integration, logical_ids, expected_route
):
gateway_resource = deepcopy(input_gateway_v2_integration)
_link_gateway_v2_integration_to_lambda_function_callback(gateway_resource, logical_ids)
input_gateway_v2_integration["Properties"]["IntegrationUri"] = expected_route
self.assertEqual(gateway_resource, input_gateway_v2_integration)

@parameterized.expand(
[
("integrations/invokeArn", "invokeArn"),
Expand Down