AWS API GatewayとSAMとSwaggerとGolang構成での基本設定

調べる効率が悪いのか、タイトルの構成で基本的なところ動くまでも結構時間かかってしまいました。イチから調べるよりこういったスタート地点のがあると良いと思いますので残しておきます。

主にSAMとSwaggerの関連性の部分が厳しかった。。。

コード

ディレクトリ構成は以下の通りです。

.
├── Makefile
├── README.md
├── bin
│   ├── hello
│   └── root
├── src
│   ├── api
│   │   ├── swagger.dev.yml
│   │   └── swagger.prod.yml
│   └── handler
│       ├── hello
│       │   └── main.go
│       └── root
│           └── main.go
└── template.yml

github.com

GolangとAPIGatewayとのやりとり

こちらを使います。

aws-lambda-go/README_ApiGatewayEvent.md at v1.2.0 · aws/aws-lambda-go · GitHub

package main

import (
    "context"
    "fmt"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
)

func HandleRequest(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    msg := fmt.Sprintf("Processing request data for request %s.\n", req.RequestContext.RequestID)
    msg += fmt.Sprintf("Body size = %d.\n", len(req.Body))
    msg += fmt.Sprintln("Headers:")
    for key, value := range req.Headers {
        msg += fmt.Sprintf("    %s: %s\n", key, value)
    }
    return events.APIGatewayProxyResponse{
        IsBase64Encoded: false,
        Headers:         map[string]string{"myheader": "myheader"},
        Body:            msg,
        StatusCode:      200,
    }, nil
}

func main() {
    lambda.Start(HandleRequest)

外部のSwaggerファイルを参照

APIGatewaiの定義部分で外部のSwaggerファイルを参照しています。この部分かなり悩んだのですが、こちらのブログにたどりついたおかげで、AWS::Include Transform 使うことで着地しました。

  Api:
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Sub ${Stage}
      DefinitionBody:
        Fn::Transform:
          Name: AWS::Include
          Parameters:
            Location: !Sub s3://${ArtifactBucket}/${SwaggerFile}

AWS::Include TransformLocation ではS3しか参照できないため、デプロイ直前にS3にSwaggerファイルをアップロードしています。

deploy: package
  aws s3 cp ./src/api/$(SWAGGER_FILE) s3://$(S3_STAGING_BUCKET)/
  sam deploy --template-file ./.packaged.yml --stack-name $(STACK_NAME) --capabilities CAPABILITY_IAM \
  --parameter-overrides \
  Stage=$(STAGE) \
  ArtifactBucket=$(S3_STAGING_BUCKET) \
  SwaggerFile=$(SWAGGER_FILE)
  aws cloudformation describe-stacks --stack-name $(STACK_NAME) --query "Stacks[0].Outputs" --output table

Malformed Lambda proxy response

直前まで動いていたのに、突然繋がらなくなり、調べるとこのエラーが出るようになっていました。スタック作り直しても全く解決せずで困り果てていました。動きとしては何かのタイムアウトになているようで、Lambda管理画面から直接も実行できない状態でした。

解決策・・書きたいのですが、しばらく時間経ってから何も設定変更せずにDeployし直したら動くようになりました。

Wed Jun 13 11:29:12 UTC 2018 : Received response. Integration latency: 10700 ms
Wed Jun 13 11:29:12 UTC 2018 : Endpoint response body before transformations: {"Message":"An error occurred and the request cannot be processed.","Type":"Service"}

Wed Jun 13 11:29:12 UTC 2018 : Endpoint response headers: {Connection=keep-alive, x-amzn-RequestId=f897c580-6efc-11e8-92cb-79be3563d414, x-amzn-ErrorType=ServiceException, Content-Length=86, Date=Wed, 13 Jun 2018 11:29:12 GMT, Content-Type=application/json}
Wed Jun 13 11:29:12 UTC 2018 : Execution failed due to configuration error: Malformed Lambda proxy response
Wed Jun 13 11:29:12 UTC 2018 : Method completed with status: 502

参考資料

Swagger周りは結構ひっかかりました。

いつも助けてもらってますが、今回もクラメソさんのブログに助けてもらいました。

dev.classmethod.jp

AWSのSwagger拡張のドキュメントも、どこ読めばよいかわかりにくかったのですが、ここをエントリポイントに探すと必要な情報にたどり着きやすかったです。

docs.aws.amazon.com

その他

ステージを細かく管理できない

現状ステージごとにスタックを作らなければなりません。そのうち修正が出てくるかも。

Mutliple API stages but in a single document - best practice · Issue #198 · awslabs/serverless-application-model · GitHub

Stageというステージが勝手に作成されてしまう

最初は自分の設定ミスかと思いましたが、バグ?、仕様?のようです。 f:id:yomon8:20180613170952p:plain SAM Creates stage named "Stage" by default? · Issue #191 · awslabs/serverless-application-model · GitHub

こちらの方法で解決できる場合もありそうで、ステージの管理もある程度柔軟にできそうですが、複雑になりすぎて辛そう・・