Ошибка Serverless Shared API Gateway при развертывании на разных этапах

Я использую бессерверную версию 1.29.2.

Я создал начальный сценарий формирования облака, который создает API REST API GateWay, который будет использоваться другими службами. Итак, вот скрипт cloudformation, отвечающий за это.

{
   "AWSTemplateFormatVersion":"2010-09-09",
   "Description":"API",
   "Resources":{
      "APIGw":{
         "Type":"AWS::ApiGateway::RestApi",
         "Properties":{
            "Name":"API-GW"
         }
      }
   },
   "Outputs":{
      "ApiGwRestApiId":{
         "Value":{
            "Ref":"APIGw"
         },
         "Export":{
            "Name":"apigw-restApiId"
         }
      },
      "eyesApiGwRestApiRootResourceId":{
         "Value":{
            "Fn::GetAtt":[
               "APIGw",
               "RootResourceId"
            ]
         },
         "Export":{
            "Name":"apigw-rootResourceId"
         }
      }
   }
}

Вот serverless.yml для приложения, которое я пытался развернуть.

service: template-test-service

provider:
  name: aws
  runtime: python3.6
  region: eu-central-1
  stage: ${self:custom.environment.stage}
  environment:
    stage: ${self:custom.environment.stage}
  apiGateway:
    restApiId:
      'Fn::ImportValue': apigw-restApiId
    restApiRootResourceId:
      'Fn::ImportValue': apigw-rootResourceId

Когда я выполняю sls deploy --stage dev, все работает нормально, однако, когда я выполняю еще одно развертывание на sls deploy --stage prod

Эта ошибка появляется.

Another resource with the same parent already has this name

person KyelJmD    schedule 03.10.2018    source источник


Ответы (2)


Я боролся с этим в течение недели, и проблема связана с тем, как шлюз API построен из ресурсов и методов. Из документации

В Amazon API Gateway вы создаете REST API как набор программируемых объектов, известных как ресурсы API Gateway. Например, вы используете ресурс RestApi для представления API, который может содержать набор сущностей Resource. Каждая сущность Resource может, в свою очередь, иметь один или несколько ресурсов Method. Метод, выраженный в параметрах и теле запроса, определяет интерфейс прикладного программирования для доступа клиента к открытому ресурсу и представляет собой входящий запрос, отправленный клиентом.

Бессерверный интерфейс командной строки создает для вас все ресурсы/методы, когда у вас есть функция, запускаемая событием http.

functions:
  GetScenesInGame:
    handler: handler.hello
    layers: arn:aws:lambda:eu-west-1:xxxxxxxxx:layer:pynamodb-layer:1
    events:
      - http:
          method: GET
          path: api/v1/game/{gameId}/scene

В приведенном выше примере создается пять ресурсов (api, v1, game, gameIdParam, scene) и, наконец, добавляется метод GET для конечного ресурса.

К сожалению, если у вас есть два отдельных стека (как это может быть в настройке микросервиса), если они являются какой-либо частью вышеуказанных методов, возникает ошибка Другой ресурс с тем же родителем уже имеет это имя.

Решение выделено в этой статье из бессерверного развертывания бессерверного микросервиса на aws, хотя это не очень ясно и легко пропустить.

Во-первых, есть шаблон облачного формирования верхнего уровня, который настраивает необходимые ресурсы.

Для ресурса, к которому вы хотите добавить бессерверную микрослужбу, вы экспортируете идентификатор ресурса в качестве выходной переменной в свой стек.

Затем в файле serverless.yml вы импортируете ссылку на шлюз API и идентификатор ресурса APIGateway.

Затем вы можете развернуть каждую службу без конфликта имен ресурсов в структуре API.

В приведенных ниже шаблонах показано наличие набора ресурсов верхнего уровня.

api/v1/game/{gameId}/page

api/v1/game/{gameId}/scene

Затем прикрепите PageService к ресурсу page и SceneService к ресурсу scene.

api-шлюз.yml

    AWSTemplateFormatVersion: "2010-09-09"
Description: "S3 template for deploying S3 to be used by ACS s3 connector."

Resources:
    TestApiGw:
            Type: AWS::ApiGateway::RestApi
            Properties:
                Name: !Sub 'test-apigw-throwaway'

    ApiResource:
        Type: AWS::ApiGateway::Resource
        Properties:
            RestApiId: !Ref TestApiGw
            ParentId: !GetAtt 
                - TestApiGw
                - RootResourceId
            PathPart: "api"

    VersionResource:
        Type: AWS::ApiGateway::Resource
        Properties:
            RestApiId: !Ref TestApiGw
            ParentId: !Ref ApiResource
            PathPart: "v1"

    GameResource:
        Type: AWS::ApiGateway::Resource
        Properties:
            RestApiId: !Ref TestApiGw
            ParentId: !Ref VersionResource
            PathPart: "game"

    GameParamResource:
        Type: AWS::ApiGateway::Resource
        Properties:
            RestApiId: !Ref TestApiGw
            ParentId: !Ref GameResource
            PathPart: "{gameId}"

    SceneResource:
        Type: AWS::ApiGateway::Resource
        Properties:
            RestApiId: !Ref TestApiGw
            ParentId: !Ref GameParamResource
            PathPart: "scene"

    PageResource:
            Type: AWS::ApiGateway::Resource
            Properties:
                RestApiId: !Ref TestApiGw
                ParentId: !Ref GameParamResource
                PathPart: "page"

Outputs:
    ApiRestApiId:
        Value: !Ref TestApiGw
        Export:
            Name: !Sub ${AWS::StackName}-TestApiId

    ApiRootResourceId:
        Value:
            Fn::GetAtt:
                - TestApiGw
                - RootResourceId
        Export:
            Name: !Sub ${AWS::StackName}-ApiRootResourceVar

    ApiSceneResourceVar:
        Value: !Ref SceneResource
        Export:
        # variable names are global so this will need more work to make it unique across stages.
            Name: !Sub ${AWS::StackName}-ApiSceneResourceVar

    ApiPageResourceVar:
        Value: !Ref PageResource
        Export:
        # variable names are global so this will need more work to make it unique across stages.
            Name: !Sub ${AWS::StackName}-ApiPageResourceVar

serverless.yml (файл Serverless Cli для службы страниц)

service: scrap-page-service

provider:
  name: aws
  runtime: python2.7
  apiGateway:
    restApiId: 
      "Fn::ImportValue": throw-stack-1-TestApiId
    restApiRootResourceId: 
      "Fn::ImportValue": throw-stack-1-ApiPageResourceVar

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: ""
          method: get

serverless.yml (файл Serverless Cli для службы сцены)

service: scrap-scene-service

provider:
  name: aws
  runtime: python2.7
  apiGateway:
    restApiId: 
      "Fn::ImportValue": throw-stack-1-TestApiId
    restApiRootResourceId: 
      "Fn::ImportValue": throw-stack-1-ApiSceneResourceVar

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: ""
          method: get

Надеюсь, это поможет другим решить эту проблему, и если у кого-то есть лучший способ сделать это, мне было бы интересно узнать :-)

person Alex Ward    schedule 15.11.2019
comment
Привет, @Alex Levitt, ты вообще пробовал это с помощью serverless-domain-manager? - person billydh; 06.01.2020
comment
Прошло некоторое время с тех пор, как я в последний раз смотрел на него, но я помню, что serverless-domain-manager был одним из решений, которые я пытался использовать, но, похоже, у него не было необходимого мне контроля, чтобы избежать конфликта маршрутов. - person Alex Ward; 07.01.2020

Взгляните на Бессерверная документация по совместному использованию шлюза API ?

Кажется, что вам нужно создать общие компоненты пути к ресурсам, которые являются объектами CloudFormed.

person SebM    schedule 22.03.2019
comment
Привет и добро пожаловать в Stack Overflow! Спасибо за редактирование. Не могли бы вы добавить информацию о событии? Если эта ссылка не работает, этого ответа все равно будет недостаточно. Объясните, как создавать общие компоненты путей к ресурсам и как общие компоненты путей к ресурсам являются объектами CloudFormed. - person Jonathan; 22.03.2019
comment
Я был бы признателен за пример того, как это настроить. - person Omar Dulaimi; 24.03.2021