ConflictException: этап уже существует из aws_api_gateway_deployment с stage_name - PullRequest
0 голосов
/ 04 апреля 2020

Проблема

Если сначала создать развертывание шлюза 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, так как этап необязателен для развертывания шлюза API. Однако invoke_url развертывания не содержит часть сцены в пути URL.

Это проблема, связанная со спецификацией Terraform c или проблемой шлюза API?

Ссылка

1 Ответ

0 голосов
/ 09 апреля 2020

В моем понимании есть логический дефект в дизайне развертывания шлюза API.

Развертывание API - это развертывание на этапе, как в Развертывание API REST в Amazon API Gateway .

Чтобы развернуть API, вы создаете развертывание API и связываете его со стадией. Этап - это логическая ссылка на состояние жизненного цикла вашего API (например, dev, prod, beta, v2).

Однако API этапа создания требует существующего развертывания. Я полагаю, что эта круговая зависимость между развертыванием и этапом является источником проблем.

Задача 1

API этапа создания требует развертывания с аргументом --deployment-id. Следовательно, нам нужно сначала создать развертывание API.

Вот первая проблема. Если мы указываем этап для create-развертывания, он создает этап. Тогда мы не можем сами создать сцену.

Когда мы используем инструмент управления конфигурацией, например CloudFormation или Terraform, это вызывает исключение «Этап уже существует», так как мы попытаемся создать ресурс этапа самостоятельно.

Следовательно, мы не можем указать этап, когда мы впервые создаем развертывание API.

Задача 2

Чтобы мы могли управлять созданием ресурса этапа из-за проблемы 1, нам нужно сначала создать фиктивное развертывание, чтобы мы можем создать сцену с помощью манекена. Этот шаг создания расточительного развертывания является второй проблемой. Хотя этап указывает на развертывание, развертывание не полностью распознает этап, потому что если попытаться получить URL-адрес вызова из развертывания, он не включает этап.

После создания этапа, наконец, мы может создать другое развертывание API, которое определяет этап. Поскольку этап уже существует, развертывание будет ссылаться на этап, а URL-адрес вызова будет включать этап.

Пример

Terraform

#--------------------------------------------------------------------------------
# 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
  }
}

CloudFormation

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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...