[英]ConflictException: Stage already exist from aws_api_gateway_deployment with stage_name
首次使用階段名稱創建 API 網關部署,並創建階段以配置 X-RAY 或 CloudWatch 日志記錄時,將導致“階段已存在”。
resource "aws_api_gateway_deployment" "this" {
rest_api_id = aws_api_gateway_rest_api.mysfit.id
stage_name = "${var.ENV}"
variables = {
deployed_at = timestamp()
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_api_gateway_stage" "this" {
stage_name = var.ENV
rest_api_id = aws_api_gateway_rest_api.mysfit.id
deployment_id = aws_api_gateway_deployment.this.id
dynamic "access_log_settings" {
for_each = var.enable_apigw_stage_cloudwatch_access_log ? [1] : []
content {
destination_arn = module.cloudwatch.cloudwatch_loggroup_arn
format = file("${path.module}/apigw_access_log_format.json")
}
}
xray_tracing_enabled = var.xray_tracing_enabled
tags = {
Project = var.PROJECT
Environment = var.ENV
}
}
解決方法是省略 aws_api_gateway_deployment 中的 stage_name,因為 stage 對於 API 網關部署是可選的。 但是,部署的invoke_url在URL路徑中沒有stage部分。
這是 Terraform 特定問題還是 API 網關問題?
據我了解,API網關部署設計存在邏輯缺陷。
API 部署是部署到一個階段,如在 Amazon ZDB974238714CA8DE634ACE 網關中部署 REST API 中一樣。
要部署 API,請創建 API 部署並將其與階段相關聯。 階段是對 API 的生命周期 state 的邏輯引用(例如,dev、prod、beta、v2)。
但是,創建階段 API 需要現有部署。 我相信,部署和階段之間的這種循環依賴是問題的根源。
創建階段 API 需要使用 --deployment-id 參數進行部署。 因此,我們需要首先創建一個 API 部署。
這是第一個問題。 如果我們為創建部署指定一個階段,它會創建該階段。 那我們就不能自己創造舞台。
當我們使用 CloudFormation 或 Terraform 等配置管理工具時,這會導致“Stage already exists”異常,因為我們將嘗試自己創建階段資源。
因此,當我們第一次創建 API 部署時,我們無法指定階段。
由於問題 1,我們要管理階段資源的創建,我們需要首先創建一個虛擬部署,以便我們可以使用虛擬創建一個階段。 這一步浪費部署創建是第二個問題。 雖然stage指向deployment,但deployment並不能完全識別stage,因為如果嘗試從deployment中獲取invoke URL,它不包括stage。
創建階段后,最后我們可以創建另一個指定階段的 API 部署。 由於階段已經存在,部署將引用階段並且調用 URL 將包含階段。
#--------------------------------------------------------------------------------
# Dummy API Deployment
#--------------------------------------------------------------------------------
resource "aws_api_gateway_deployment" "dummy" {
rest_api_id = "${aws_api_gateway_rest_api.this.id}"
#--------------------------------------------------------------------------------
# To avoid State already exists
# https://github.com/terraform-providers/terraform-provider-aws/issues/2918
#--------------------------------------------------------------------------------
#stage_name = "${var.ENV}"
#--------------------------------------------------------------------------------
# Force re-deployment at each run. Alternative is to verify MD5 of API GW files.
#--------------------------------------------------------------------------------
# https://medium.com/coryodaniel/til-forcing-terraform-to-deploy-a-aws-api-gateway-deployment-ed36a9f60c1a
# https://github.com/hashicorp/terraform/issues/6613
# Terraform’s aws_api_gateway_deployment won’t deploy subsequent releases in the event
# that something has changed in an integration, method, etc
#--------------------------------------------------------------------------------
stage_description = "Deployment at ${timestamp()}"
lifecycle {
create_before_destroy = true
}
depends_on = [
#--------------------------------------------------------------------------------
# [aws_api_gateway_account.this]
# To avoid the error: Updating API Gateway Stage failed:
# BadRequestException: CloudWatch Logs role ARN must be set in account settings to enable logging.
#--------------------------------------------------------------------------------
"aws_api_gateway_account.this",
#--------------------------------------------------------------------------------
# To avoid NotFoundException: Invalid Integration identifier specified
#--------------------------------------------------------------------------------
"aws_api_gateway_integration.ping_put",
]
#--------------------------------------------------------------------------------
}
#--------------------------------------------------------------------------------
# Create a stage refering to the dummy.
# The 2nd/true deployment will later refer to this stage
#--------------------------------------------------------------------------------
resource "aws_api_gateway_stage" "this" {
stage_name = var.ENV
rest_api_id = aws_api_gateway_rest_api.this.id
deployment_id = aws_api_gateway_deployment.dummy.id
xray_tracing_enabled = var.apigw_xray_tracing_enabled
tags = {
Project = var.PROJECT
Environment = var.ENV
}
depends_on = [
aws_api_gateway_deployment.dummy
]
}
#--------------------------------------------------------------------------------
# Legitimate API Deployment
#--------------------------------------------------------------------------------
resource "aws_api_gateway_deployment" "this" {
rest_api_id = aws_api_gateway_rest_api.this.id
stage_name = aws_api_gateway_stage.this.stage_name
lifecycle {
create_before_destroy = true
}
}
AWSTemplateFormatVersion: "2010-09-09"
Description: "My API Gateway and Lambda function"
Parameters:
apiGatewayStageName:
Type: "String"
Default: "devStage"
lambdaFunctionName:
Type: "String"
Default: "my-lambda-function"
Resources:
apiGateway:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "test-api"
Description: "My Test API"
apiGatewayRootMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
HttpMethod: "GET"
Integration:
IntegrationHttpMethod: "POST"
Type: "AWS_PROXY"
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "lambdaFunction.Arn"
ResourceId: !GetAtt "apiGateway.RootResourceId"
RestApiId: !Ref "apiGateway"
apiGatewayDeployment:
Type: "AWS::ApiGateway::Deployment"
DependsOn:
- "apiGatewayRootMethod"
Properties:
RestApiId: !Ref "apiGateway"
StageName: ""
apiGatewayStage:
Type: "AWS::ApiGateway::Stage"
Properties:
StageName: !Ref "apiGatewayStageName"
RestApiId: !Ref "apiGateway"
DeploymentId: !Ref "apiGatewayDeployment"
MethodSettings:
- ResourcePath: /
HttpMethod: "GET"
MetricsEnabled: 'true'
DataTraceEnabled: 'true'
lambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Code:
ZipFile: |
def handler(event,context):
return {
'body': 'Hello World from Lambda',
'headers': {
'Content-Type': 'text/plain'
},
'statusCode': 200
}
Description: "My function"
FunctionName: !Ref "lambdaFunctionName"
Handler: "index.handler"
MemorySize: 256
Role: !GetAtt "lambdaIAMRole.Arn"
Runtime: "python3.7"
Timeout: 30
lambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "lambdaFunction.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/GET/"
lambdaIAMRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Effect: "Allow"
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${lambdaFunctionName}:*"
PolicyName: "lambda"
lambdaLogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: !Sub "/aws/lambda/${lambdaFunctionName}"
RetentionInDays: 90
Outputs:
apiGatewayInvokeURL:
Value: !Sub "https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGatewayStageName}"
lambdaArn:
Value: !GetAtt "lambdaFunction.Arn"
引用文檔
AWS::ApiGateway::Deployment 資源將 API 網關 RestApi 資源部署到
stage
這意味着,如果您正在創建一個 AWS::ApiGateway::Stage 資源,其階段名稱與您傳遞給 AWS::ApiGateway::Deployment 的階段名稱相同,那么 Cloudformation 腳本將嘗試創建兩個具有相同名稱的階段 - 因此提示錯誤。
因此,解決方案是not
將階段名稱傳遞到部署資源中。 這將起作用:
Parameters:
ENVIRONMENT:
Type: String
Resources:
ContactsStage:
Type: AWS::ApiGateway::Stage
Properties:
StageName: !Ref ENVIRONMENT
RestApiId: !Ref MyApiGateway
DeploymentId: !Ref MyDeployment
MethodSettings:
- ResourcePath: /
HttpMethod: POST
MetricsEnabled: 'true'
DataTraceEnabled: 'false'
MyDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn: MyMethod
Properties:
RestApiId: !Ref MyApiGateway
# StageName: !Ref ENVIRONMENT # uncommenting this will cause the error
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.