2021-02-15 10:31:33 +00:00
|
|
|
import json
|
|
|
|
|
2023-11-30 15:55:51 +00:00
|
|
|
import boto3
|
|
|
|
|
|
|
|
from moto import mock_apigateway, mock_cloudformation, mock_iam, mock_lambda, mock_logs
|
2021-02-15 10:31:33 +00:00
|
|
|
|
|
|
|
template = """{
|
|
|
|
"AWSTemplateFormatVersion": "2010-09-09",
|
|
|
|
"Description": "The AWS CloudFormation template for this Serverless application",
|
|
|
|
"Resources": {
|
|
|
|
"ServerlessDeploymentBucket": {
|
|
|
|
"Type": "AWS::S3::Bucket"
|
|
|
|
},
|
|
|
|
"HelloLogGroup": {
|
|
|
|
"Type": "AWS::Logs::LogGroup",
|
|
|
|
"Properties": {
|
|
|
|
"LogGroupName": "/aws/lambda/timeseries-service-dev-hello"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"IamRoleLambdaExecution": {
|
|
|
|
"Type": "AWS::IAM::Role",
|
|
|
|
"Properties": {
|
|
|
|
"AssumeRolePolicyDocument": {
|
|
|
|
"Version": "2012-10-17",
|
|
|
|
"Statement": [
|
|
|
|
{
|
|
|
|
"Effect": "Allow",
|
|
|
|
"Principal": {
|
|
|
|
"Service": [
|
|
|
|
"lambda.amazonaws.com"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"Action": [
|
|
|
|
"sts:AssumeRole"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"Policies": [
|
|
|
|
{
|
|
|
|
"PolicyName": {
|
|
|
|
"Fn::Join": [
|
|
|
|
"-",
|
|
|
|
[
|
|
|
|
"dev",
|
|
|
|
"timeseries-service",
|
|
|
|
"lambda"
|
|
|
|
]
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"PolicyDocument": {
|
|
|
|
"Version": "2012-10-17",
|
|
|
|
"Statement": [
|
|
|
|
{
|
|
|
|
"Effect": "Allow",
|
|
|
|
"Action": [
|
|
|
|
"logs:CreateLogStream"
|
|
|
|
],
|
|
|
|
"Resource": [
|
|
|
|
{
|
|
|
|
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/timeseries-service-dev-hello:*"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Effect": "Allow",
|
|
|
|
"Action": [
|
|
|
|
"logs:PutLogEvents"
|
|
|
|
],
|
|
|
|
"Resource": [
|
|
|
|
{
|
|
|
|
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/timeseries-service-dev-hello:*:*"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"Path": "/",
|
|
|
|
"RoleName": {
|
|
|
|
"Fn::Join": [
|
|
|
|
"-",
|
|
|
|
[
|
|
|
|
"timeseries-service",
|
|
|
|
"dev",
|
|
|
|
"us-east-1",
|
|
|
|
"lambdaRole"
|
|
|
|
]
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"HelloLambdaFunction": {
|
|
|
|
"Type": "AWS::Lambda::Function",
|
|
|
|
"Properties": {
|
|
|
|
"Code": {
|
|
|
|
"S3Bucket": {
|
|
|
|
"Ref": "ServerlessDeploymentBucket"
|
|
|
|
},
|
|
|
|
"S3Key": "serverless/timeseries-service/dev/1542744572309-2018-11-20T20:09:32.309Z/timeseries-service.zip"
|
|
|
|
},
|
|
|
|
"FunctionName": "timeseries-service-dev-hello",
|
|
|
|
"Handler": "handler.hello",
|
|
|
|
"MemorySize": 1024,
|
|
|
|
"Role": {
|
|
|
|
"Fn::GetAtt": [
|
|
|
|
"IamRoleLambdaExecution",
|
|
|
|
"Arn"
|
|
|
|
]
|
|
|
|
},
|
2023-08-31 06:47:49 +00:00
|
|
|
"Runtime": "python3.11",
|
2021-02-15 10:31:33 +00:00
|
|
|
"Timeout": 6
|
|
|
|
},
|
|
|
|
"DependsOn": [
|
|
|
|
"HelloLogGroup",
|
|
|
|
"IamRoleLambdaExecution"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"HelloLambdaVersionU88Ag36tX5K6Yuze3R8jedH2g7q2TTGuafWQxEnUmo": {
|
|
|
|
"Type": "AWS::Lambda::Version",
|
|
|
|
"DeletionPolicy": "Retain",
|
|
|
|
"Properties": {
|
|
|
|
"FunctionName": {
|
|
|
|
"Ref": "HelloLambdaFunction"
|
|
|
|
},
|
|
|
|
"CodeSha256": "+pq+8RveA979z1DNF8UKnFGZfgE07blNyJGust5VJnU="
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"ApiGatewayRestApi": {
|
|
|
|
"Type": "AWS::ApiGateway::RestApi",
|
|
|
|
"Properties": {
|
|
|
|
"Name": "dev-timeseries-service",
|
|
|
|
"EndpointConfiguration": {
|
|
|
|
"Types": [
|
|
|
|
"EDGE"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"ApiGatewayResourceHello": {
|
|
|
|
"Type": "AWS::ApiGateway::Resource",
|
|
|
|
"Properties": {
|
|
|
|
"ParentId": {
|
|
|
|
"Fn::GetAtt": [
|
|
|
|
"ApiGatewayRestApi",
|
|
|
|
"RootResourceId"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"PathPart": "hello",
|
|
|
|
"RestApiId": {
|
|
|
|
"Ref": "ApiGatewayRestApi"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"ApiGatewayMethodHelloGet": {
|
|
|
|
"Type": "AWS::ApiGateway::Method",
|
|
|
|
"Properties": {
|
|
|
|
"HttpMethod": "GET",
|
|
|
|
"RequestParameters": {},
|
|
|
|
"ResourceId": {
|
|
|
|
"Ref": "ApiGatewayResourceHello"
|
|
|
|
},
|
|
|
|
"RestApiId": {
|
|
|
|
"Ref": "ApiGatewayRestApi"
|
|
|
|
},
|
|
|
|
"ApiKeyRequired": false,
|
|
|
|
"AuthorizationType": "NONE",
|
|
|
|
"Integration": {
|
|
|
|
"IntegrationHttpMethod": "POST",
|
|
|
|
"Type": "AWS_PROXY",
|
|
|
|
"Uri": {
|
|
|
|
"Fn::Join": [
|
|
|
|
"",
|
|
|
|
[
|
|
|
|
"arn:",
|
|
|
|
{
|
|
|
|
"Ref": "AWS::Partition"
|
|
|
|
},
|
|
|
|
":apigateway:",
|
|
|
|
{
|
|
|
|
"Ref": "AWS::Region"
|
|
|
|
},
|
|
|
|
":lambda:path/2015-03-31/functions/",
|
|
|
|
{
|
|
|
|
"Fn::GetAtt": [
|
|
|
|
"HelloLambdaFunction",
|
|
|
|
"Arn"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"/invocations"
|
|
|
|
]
|
|
|
|
]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"MethodResponses": []
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"ApiGatewayDeployment1542744572805": {
|
|
|
|
"Type": "AWS::ApiGateway::Deployment",
|
|
|
|
"Properties": {
|
|
|
|
"RestApiId": {
|
|
|
|
"Ref": "ApiGatewayRestApi"
|
|
|
|
},
|
|
|
|
"StageName": "dev"
|
|
|
|
},
|
|
|
|
"DependsOn": [
|
|
|
|
"ApiGatewayMethodHelloGet"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"HelloLambdaPermissionApiGateway": {
|
|
|
|
"Type": "AWS::Lambda::Permission",
|
|
|
|
"Properties": {
|
|
|
|
"FunctionName": {
|
|
|
|
"Fn::GetAtt": [
|
|
|
|
"HelloLambdaFunction",
|
|
|
|
"Arn"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"Action": "lambda:InvokeFunction",
|
|
|
|
"Principal": {
|
|
|
|
"Fn::Join": [
|
|
|
|
"",
|
|
|
|
[
|
|
|
|
"apigateway.",
|
|
|
|
{
|
|
|
|
"Ref": "AWS::URLSuffix"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"SourceArn": {
|
|
|
|
"Fn::Join": [
|
|
|
|
"",
|
|
|
|
[
|
|
|
|
"arn:",
|
|
|
|
{
|
|
|
|
"Ref": "AWS::Partition"
|
|
|
|
},
|
|
|
|
":execute-api:",
|
|
|
|
{
|
|
|
|
"Ref": "AWS::Region"
|
|
|
|
},
|
|
|
|
":",
|
|
|
|
{
|
|
|
|
"Ref": "AWS::AccountId"
|
|
|
|
},
|
|
|
|
":",
|
|
|
|
{
|
|
|
|
"Ref": "ApiGatewayRestApi"
|
|
|
|
},
|
|
|
|
"/*/*"
|
|
|
|
]
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"Outputs": {
|
|
|
|
"ServerlessDeploymentBucketName": {
|
|
|
|
"Value": {
|
|
|
|
"Ref": "ServerlessDeploymentBucket"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"HelloLambdaFunctionQualifiedArn": {
|
|
|
|
"Description": "Current Lambda function version",
|
|
|
|
"Value": {
|
|
|
|
"Ref": "HelloLambdaVersionU88Ag36tX5K6Yuze3R8jedH2g7q2TTGuafWQxEnUmo"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"ServiceEndpoint": {
|
|
|
|
"Description": "URL of the service endpoint",
|
|
|
|
"Value": {
|
|
|
|
"Fn::Join": [
|
|
|
|
"",
|
|
|
|
[
|
|
|
|
"https://",
|
|
|
|
{
|
|
|
|
"Ref": "ApiGatewayRestApi"
|
|
|
|
},
|
|
|
|
".execute-api.us-east-1.",
|
|
|
|
{
|
|
|
|
"Ref": "AWS::URLSuffix"
|
|
|
|
},
|
|
|
|
"/dev"
|
|
|
|
]
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}"""
|
|
|
|
|
2022-09-12 20:58:18 +00:00
|
|
|
template_with_missing_sub = """{
|
|
|
|
"AWSTemplateFormatVersion": "2010-09-09",
|
|
|
|
"Description": "AWS CloudFormation template",
|
|
|
|
"Resources": {
|
|
|
|
"UnknownResource": {"Type": "AWS::Cloud9::EnvironmentEC2", "Properties": {}},
|
|
|
|
"ApiGatewayRestApi": {
|
|
|
|
"Type": "AWS::ApiGateway::RestApi",
|
|
|
|
"Properties": {
|
|
|
|
"Name": "test-api",
|
|
|
|
"Description": {"Fn::Sub": "${UnknownResource}"},
|
|
|
|
"EndpointConfiguration": {
|
|
|
|
"Types": [
|
|
|
|
"EDGE"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"Outputs": {
|
|
|
|
}
|
|
|
|
}"""
|
|
|
|
|
2021-02-15 10:31:33 +00:00
|
|
|
|
|
|
|
@mock_cloudformation
|
|
|
|
@mock_lambda
|
|
|
|
@mock_iam
|
|
|
|
@mock_logs
|
|
|
|
@mock_apigateway
|
|
|
|
def test_simple_apigateway_with_lambda_proxy():
|
|
|
|
region = "us-east-1"
|
|
|
|
apigw = boto3.client("apigateway", region_name=region)
|
|
|
|
cf = boto3.client("cloudformation", region_name=region)
|
|
|
|
awslambda = boto3.client("lambda", region_name=region)
|
|
|
|
cf.create_stack(StackName="teststack", TemplateBody=template)
|
|
|
|
#
|
|
|
|
cf.describe_stacks(StackName="teststack")["Stacks"]
|
|
|
|
resources = cf.describe_stack_resources(StackName="teststack")["StackResources"]
|
|
|
|
api_id = [
|
|
|
|
r["PhysicalResourceId"]
|
|
|
|
for r in resources
|
|
|
|
if r["ResourceType"] == "AWS::ApiGateway::RestApi"
|
|
|
|
][0]
|
|
|
|
fn_name = [
|
|
|
|
r["PhysicalResourceId"]
|
|
|
|
for r in resources
|
|
|
|
if r["LogicalResourceId"] == "HelloLambdaFunction"
|
|
|
|
][0]
|
|
|
|
#
|
|
|
|
# Verify Rest API was created
|
|
|
|
api = apigw.get_rest_apis()["items"][0]
|
2023-06-07 09:46:18 +00:00
|
|
|
assert api["id"] == api_id
|
|
|
|
assert api["name"] == "dev-timeseries-service"
|
2021-02-15 10:31:33 +00:00
|
|
|
#
|
|
|
|
# Verify Gateway Resource was created
|
|
|
|
paths = apigw.get_resources(restApiId=api_id)["items"]
|
|
|
|
root_path = [p for p in paths if p["path"] == "/"][0]
|
|
|
|
hello_path = [p for p in paths if p["path"] == "/hello"][0]
|
2023-06-07 09:46:18 +00:00
|
|
|
assert hello_path["parentId"] == root_path["id"]
|
2021-02-15 10:31:33 +00:00
|
|
|
#
|
|
|
|
# Verify Gateway Method was created
|
|
|
|
m = apigw.get_method(
|
|
|
|
restApiId=api_id, resourceId=hello_path["id"], httpMethod="GET"
|
|
|
|
)
|
2023-06-07 09:46:18 +00:00
|
|
|
assert m["httpMethod"] == "GET"
|
2021-02-15 10:31:33 +00:00
|
|
|
#
|
|
|
|
# Verify a Gateway Deployment was created
|
|
|
|
d = apigw.get_deployments(restApiId=api_id)["items"]
|
2023-06-07 09:46:18 +00:00
|
|
|
assert len(d) == 1
|
2021-02-15 10:31:33 +00:00
|
|
|
#
|
|
|
|
# Verify Lambda function was created
|
|
|
|
awslambda.get_function(FunctionName=fn_name) # Will throw 404 if it doesn't exist
|
|
|
|
#
|
|
|
|
# Verify Lambda Permission was created
|
|
|
|
policy = json.loads(awslambda.get_policy(FunctionName=fn_name)["Policy"])
|
|
|
|
statement = policy["Statement"][0]
|
2023-06-07 09:46:18 +00:00
|
|
|
assert (
|
|
|
|
statement["FunctionName"]
|
|
|
|
== f"arn:aws:lambda:us-east-1:123456789012:function:{fn_name}"
|
|
|
|
)
|
|
|
|
assert (
|
|
|
|
statement["Condition"]["ArnLike"]["AWS:SourceArn"]
|
|
|
|
== f"arn:aws:execute-api:us-east-1:123456789012:{api_id}/*/*"
|
2021-02-15 10:31:33 +00:00
|
|
|
)
|
2022-09-12 20:58:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
@mock_apigateway
|
|
|
|
@mock_cloudformation
|
|
|
|
def test_apigateway_with_unknown_description():
|
|
|
|
region = "us-east-1"
|
|
|
|
apigw = boto3.client("apigateway", region_name=region)
|
|
|
|
cf = boto3.client("cloudformation", region_name=region)
|
|
|
|
cf.create_stack(StackName="teststack", TemplateBody=template_with_missing_sub)
|
|
|
|
|
|
|
|
api = apigw.get_rest_apis()["items"][0]
|
2023-06-07 09:46:18 +00:00
|
|
|
assert api["description"] == "${UnknownResource}"
|