Dokumentacja i skutki ślepej wiary w nią

21 kwietnia 2021 roku, 00:40, minęła właśnie chyba druga godzina jak ja probuję zmusić funkcję lambda do wykonania się przez notyfikację topica SNSa. Niby trywialne zadanie i dokumentacja Serverlessa ma opisany ten use case:

functions:
  dispatcher:
    handler: dispatcher.dispatch
    events:
      - sns:
          arn: !Ref SuperTopic
          topicName: MyCustomTopic

resources:
  Resources:
    SuperTopic:
      Type: AWS::SNS::Topic
      Properties:
        TopicName: MyCustomTopic

Ale, niestety to nie dzialą. Metodą prób i błędów doszedłem do wniosku, że owszem ten events - sns działa, ale tylko jeśli zdefiniujemy tam nowy topic SNS. Jak wskazujemy jeszcze nieistniejący topic, to Serverless wypluwa do CloudFormation następujące instrukcje:

  send-email:
    handler: src/handler.sendEmail
    memorySize: 128
    timeout: 60
    events:
      - sns: asd # nowy topic do stworzenia przez CloudFormation

a to jest część szablonu CloudFormation wygenerowany przez Serverless dla konfiguracji wyżej:

    "SNSTopicAsd": {
      "Type": "AWS::SNS::Topic",
      "Properties": {
        "TopicName": "asd",
        "Subscription": [
          {
            "Endpoint": {
              "Fn::GetAtt": ["SendDashemailLambdaFunction", "Arn"]
            },
            "Protocol": "lambda"
          }
        ]
      }
    },
    "SendDashemailLambdaPermissionAsdSNS": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": ["SendDashemailLambdaFunction", "Arn"]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": "sns.amazonaws.com",
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:",
              { "Ref": "AWS::Partition" },
              ":sns:",
              { "Ref": "AWS::Region" },
              ":",
              { "Ref": "AWS::AccountId" },
              ":",
              "asd"
            ]
          ]
        }
      }
    },

Natomiast, gdy chcemy wskazać już istniejący topic, to takich instrukcji w skrypcie CloudFormaiton brak:

  send-email:
    handler: src/handler.sendEmail
    memorySize: 128
    timeout: 60
    events:
      - sns:
        arn: !Ref "DocumentGeneratedTopic" # ref do SNS topic zdefiniowanego w resources
        topicName: edoc-generated
po prawiej stronie widać różnicę pomiędzy tworzeniem przez Serverless topica SNS i subskrypcji do niego przez funkcję Lambda i wskazaniem przez referencję już istniejącego topica

Niestety, ślepo uwierzyłem dokumentacji Serverlessa i przez godzinę lub dwie próbowałem rozkminić dlaczego to nie działa mimo że powinno, i rozwiązanie problemu powstało dopiero po analizie szablonów CloudFormation generowanych przez Serverless:

functions:
  send-email:
    handler: src/handler.sendEmail
    memorySize: 128
    timeout: 60

resources:
  Resources:
    DocumentGeneratedTopicSendEmailFunctionSubscription:
      Type: AWS::SNS::Subscription
      Properties:
        Endpoint: "arn:aws:lambda:${self:provider.region}:#{AWS::AccountId}:function:${self:service}-${self:provider.stage}-send-email"
        Protocol: lambda
        TopicArn: !Ref "DocumentGeneratedTopic"

    DocumentGeneratedTopicSendEmailFunctionInvokePermission:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        Principal: sns.amazonaws.com
        SourceArn: !Ref "DocumentGeneratedTopic"
        FunctionName: "arn:aws:lambda:${self:provider.region}:#{AWS::AccountId}:function:${self:service}-${self:provider.stage}-send-email"

Zasadę ograniczonego zaufania, oprócz dróg publicznych, warto też stosować do dokumentacji technicznej.

Vladyslav Chyzhevskyi, 21 kwietnia 2021 roku