Files
AI-coodex-rekog-image-labeling/label/infra/lambda_api_gateway/api_gw.py
2026-05-14 14:07:04 -03:00

408 lines
15 KiB
Python

import pulumi
import pulumi_aws as aws
import json
# import time
def create_api_gatewayv2(api_config, l_api, fn, aws_region, project_name):
# Create VPC endpoint for PRIVATE API Gateway
# if api_config["type"] == "PRIVATE":
# vpce = aws.ec2.VpcEndpoint(f"vpce-{l_api['name']}",
# vpc_id=l_api["network_config"]["vpc_id"],
# service_name=f"com.amazonaws.{aws_region}.execute-api",
# subnet_ids=l_api["network_config"]["private_subnet_ids"],
# private_dns_enabled=True,
# vpc_endpoint_type="Interface"
# )
# HTTP API apigwv2
# Create API Gateway V2 HTTP API
api = aws.apigatewayv2.Api(f"api-{l_api['name']}",
name=l_api['name'],
protocol_type="HTTP",
)
sg_vpc_link = aws.ec2.SecurityGroup(f"secgroup-{l_api['name']}",
vpc_id=l_api["network_config"]["vpc_id"],
ingress=[
aws.ec2.SecurityGroupIngressArgs(
protocol="tcp",
from_port=0,
to_port=0,
cidr_blocks=["3.14.44.224/32"]
)
],
egress=[
aws.ec2.SecurityGroupEgressArgs(
protocol="-1",
from_port=0,
to_port=0,
cidr_blocks=["0.0.0.0/0"]
)
]
)
# Create a VPC Link
vpc_link = aws.apigatewayv2.VpcLink(
f"VpcLink-{project_name}",
subnet_ids=l_api["network_config"]["private_subnet_ids"],
security_group_ids=sg_vpc_link,
)
# Add IAM resource policy to restrict access by VPC (for PRIVATE type)
# if api_config["type"] == "PRIVATE":
# api_policy = aws.apigatewayv2.ApiPolicy(f"policy-{l_api['name']}",
# api_id=api.id,
# policy=pulumi.Output.all(api.arn, l_api["network_config"]["vpc_id"]).apply(
# lambda args: json.dumps({
# "Version": "2012-10-17",
# "Statement": [{
# "Effect": "Allow",
# "Principal": "*",
# "Action": "execute-api:Invoke",
# "Resource": f"{args[0]}/*",
# "Condition": {
# "StringEquals": {
# "aws:sourceVpc": args[1]
# }
# }
# }]
# })
# )
# )
integration_get = None
integration_post = None
# Create Lambda integrations
for route in api_config["routes"]:
if route["method"] == "GET" and not integration_get:
integration_get = aws.apigatewayv2.Integration(f"integration-{l_api['name']}-{route['path'].replace('/', '-')}",
api_id=api.id,
integration_type="AWS_PROXY",
integration_method="GET",
integration_uri=fn.invoke_arn,
# connection_type="INTERNET",
payload_format_version="2.0",
# connection_id=vpc_link.id
)
elif route["method"] == "POST" and not integration_post:
integration_post = aws.apigatewayv2.Integration(f"integration-{l_api['name']}-{route['path'].replace('/', '-')}",
api_id=api.id,
integration_type="AWS_PROXY",
integration_uri=fn.invoke_arn,
integration_method="POST",
payload_format_version="2.0"
)
# Create routes dynamically from config
routes = []
for route in api_config["routes"]:
if route['method'] == "GET":
integration = integration_get
elif route['method'] == "POST":
integration = integration_post
r = aws.apigatewayv2.Route(
f"route-{l_api['name']}-{route['method']}-{route['path'].replace('/', '-')}",
api_id=api.id,
route_key=f"{route['method']} {route['path']}",
target=integration.id.apply(lambda id: f"integrations/{id}")
)
routes.append(r)
# Lambda permission for API Gateway
permission = aws.lambda_.Permission(
f"permission-{l_api['name']}",
action="lambda:InvokeFunction",
function=fn.name,
principal="apigateway.amazonaws.com",
source_arn=api.execution_arn.apply(lambda arn: f"{arn}/*/*")
)
# Create stage
stage = aws.apigatewayv2.Stage(f"stage-{l_api['name']}",
api_id=api.id,
name="$default",
auto_deploy=True
)
# Export the API URL
pulumi.export(f"{l_api['name']}-url", api.api_endpoint)
def create_api_gateway(api_config, l_api, fn, account_id, aws_region, project_name):
vpce_id = None
# Create API Gateway
if api_config["type"] == "PRIVATE":
if api_config["create_and_allow_vpce"]:
# Cria uma nova Security Group para o VPC Endpoint
vpc_endpoint_sg = aws.ec2.SecurityGroup(f"api-gateway-vpce-sg-{project_name}",
vpc_id=l_api["network_config"]["vpc_id"],
description=f"Security Group for API Gateway VPC Endpoint - {project_name}",
ingress=[
aws.ec2.SecurityGroupIngressArgs(
protocol="tcp",
from_port=0,
to_port=0,
cidr_blocks=["0.0.0.0/0"],
description="Allow HTTPS traffic to API Gateway Endpoint"
),
],
egress=[
# Permite todo o tráfego de saída. Pode ser restringido se necessário.
aws.ec2.SecurityGroupEgressArgs(
protocol="-1", # "-1" significa todos os protocolos
from_port=0,
to_port=0,
cidr_blocks=["0.0.0.0/0"],
),
]
)
# Create VPC endpoint for PRIVATE API Gateway
vpce = aws.ec2.VpcEndpoint(f"vpce-{l_api['name']}",
vpc_id=l_api["network_config"]["vpc_id"],
service_name=f"com.amazonaws.{aws_region}.execute-api",
subnet_ids=l_api["network_config"]["private_subnet_ids"],
private_dns_enabled=False,
vpc_endpoint_type="Interface",
security_group_ids=[vpc_endpoint_sg]
)
vpce_id = vpce.id
# api = aws.apigateway.RestApi(f"api-{l_api["name"]}",
# description=l_api["name"],
# fail_on_warnings=False,
# put_rest_api_mode='merge',
# endpoint_configuration={
# "types": api_config["type"],
# "vpc_endpoint_ids": [vpce.id] if api_config["type"] == "PRIVATE" and api_config["create_and_allow_vpce"] else None
# }
# )
api = aws.apigateway.RestApi(f"api-{l_api["name"]}",
description=l_api["name"],
put_rest_api_mode='merge' if api_config["type"] == "PRIVATE" else 'overwrite',
fail_on_warnings=False,
endpoint_configuration={
"types": api_config["type"],
"vpc_endpoint_ids": [vpce_id] if api_config["type"] == "PRIVATE" and api_config["create_and_allow_vpce"] else None
}
)
# Build policy statements
policy_outputs = []
if api_config["allow_inbound_any"]:
policy_outputs.append(
pulumi.Output.all(aws_region, account_id, api.id, vpce_id).apply(
lambda args: {
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": f"arn:aws:execute-api:{args[0]}:{args[1]}:{args[2]}/*"
}
)
)
else:
if api_config["type"] == "PRIVATE" and api_config["create_and_allow_vpce"]:
policy_outputs.append(
pulumi.Output.all(aws_region, account_id, api.id, vpce_id).apply(
lambda args: {
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": f"arn:aws:execute-api:{args[0]}:{args[1]}:{args[2]}/*",
"Condition": {
"StringEquals": {
"aws:sourceVpce": args[3]
}
}
}
)
)
if api_config.get("allow_inbound_cidrs"):
policy_outputs.append(
pulumi.Output.all(aws_region, account_id, api.id).apply(
lambda args: {
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": f"arn:aws:execute-api:{args[0]}:{args[1]}:{args[2]}/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": api_config.get("allow_inbound_cidrs", [])
}
}
}
)
)
if len(policy_outputs) > 0:
# Resource policy for private API
resource_policy = aws.apigateway.RestApiPolicy(f"policy-{l_api['name']}",
rest_api_id=api.id,
policy=pulumi.Output.all(*policy_outputs).apply(
lambda statements: json.dumps({
"Version": "2012-10-17",
"Statement": statements
})
)
)
# Create resources and methods dynamically
resources = {}
api_dependencies = []
# routes = []
for route in api_config["routes"]:
path = route["path"].strip("/")
path_parts = path.split("/")
# Build nested resources
parent_id = api.root_resource_id
resource_path = ""
for part in path_parts:
resource_path += f"/{part}"
resource_key = resource_path
if resource_key not in resources:
resources[resource_key] = aws.apigateway.Resource(
f"resource-{l_api['name']}-{part}",
rest_api=api.id,
parent_id=parent_id,
path_part=part
)
parent_id = resources[resource_key].id
# Create method for this route
method = aws.apigateway.Method(
f"method-{l_api['name']}-{route['method']}-{path.replace('/', '-')}",
rest_api=api.id,
resource_id=parent_id,
http_method=route["method"],
authorization=api_config["authorization"]
)
# Create integration
integration = aws.apigateway.Integration(
f"integration-{l_api['name']}-{route['method']}-{path.replace('/', '-')}",
rest_api=api.id,
resource_id=parent_id,
http_method=method.http_method,
integration_http_method="POST", # for Lambda integration, it's always POST
type="AWS_PROXY",
uri=fn.invoke_arn
)
method_response = aws.apigateway.MethodResponse(f"methodResponse-{path.replace('/', '-')}",
rest_api=api.id,
resource_id=parent_id,
http_method=method.http_method,
status_code="200",
response_models={"application/json": "Empty"}
)
integration_response = aws.apigateway.IntegrationResponse(f"integrationResponse-{path.replace('/', '-')}",
rest_api=api.id,
resource_id=parent_id,
http_method=method.http_method,
status_code="200",
selection_pattern="",
response_templates={"application/json": ""},
opts=pulumi.ResourceOptions(depends_on=[integration])
)
# # Lambda permission for API Gateway
# permission = aws.lambda_.Permission(
# f"permission-{l_api['name']}-{route['method']}-{path.replace('/', '-')}",
# action="lambda:InvokeFunction",
# function=fn.name,
# principal="apigateway.amazonaws.com",
# source_arn=api.execution_arn.apply(
# lambda arn, m=route['method'], r=resource_path: f"{arn}/*/{m}{r}"
# )
# )
api_dependencies.append(method)
api_dependencies.append(integration)
api_dependencies.append(method_response)
api_dependencies.append(integration_response)
# Lambda permission for API Gateway
permission = aws.lambda_.Permission(
f"permission-{l_api['name']}-general",
action="lambda:InvokeFunction",
function=fn.name,
principal="apigateway.amazonaws.com",
source_arn=api.execution_arn.apply(
lambda arn: f"{arn}/*/*"
)
)
api_dependencies.append(permission)
# method = getattr(apigateway.Method, route["method"])
# routes.append(apigateway.RouteArgs(
# path=route["path"],
# method=method,
# event_handler=fn
# ))
# api = apigateway.RestAPI("api",
# routes=routes,
# # type=api_config["type"],
# # put_rest_api_mode="merge" if api_config["type"] == "PRIVATE" else "overwrite"
# )
# # Create a VPC link to integrate API Gateway with the VPC
# vpc_link = aws.apigateway.VpcLink(f"VpcLink-{l_api['name']}",
# name=f"VpcLink-{l_api['name']}",
# target_arn=l_api["network_config"]["vpc_id"],
# tags={
# "Name": f'VpcLink-{l_api["name"]}',
# })
# Create a deployment for the API Gateway
deployment = aws.apigateway.Deployment(f"deployment-{l_api['name']}",
rest_api=api.id,
# triggers={"redeployment": str(int(time.time()))},
opts=pulumi.ResourceOptions(depends_on=list(resources.values())+api_dependencies)
)
# Create a stage
stage = aws.apigateway.Stage(f"stage-{l_api['name']}",
deployment=deployment.id,
rest_api=api.id,
stage_name=api_config["stage_name"],
opts=pulumi.ResourceOptions(depends_on=[deployment])
)
if False: #use_api_key: #TODO
api_key = aws.apigateway.ApiKey(api_gateway_config["name"],
name=api_gateway_config["name"],
description=api_gateway_config["description"],
enabled=True
)
# API Key to Stage
usage_plan = aws.apigateway.UsagePlan("api-usage-plan",
name=api_gateway_config["usage_plan_name"],
description="Usage plan for API Gateway associated with API Key",
api_stages=[aws.apigateway.UsagePlanApiStageArgs(
api_id=api.id,
stage=deployment.stage_name
)]
)
# API Key to Usage Plan
aws.apigateway.UsagePlanKey("api-key-usage-plan-association",
key_id=api_key.id,
key_type="API_KEY",
usage_plan_id=usage_plan.id
)
# Export the stage URL
pulumi.export(f"{l_api['name']}-url", stage.invoke_url)