Open5

Lambda Function URLs に爆速で対応した Serverless Framework のコード差分を眺めてみる

hassaku63hassaku63

目的は Serverless Framework の内部実装を読んで理解を深めること。

もうちょっと言うと自分が身近に利用している OSS に commit できるようになりたいので、まずはコードリーディングから始めたい、漠然と眺めるにはコードベースが大きすぎるため目的がハッキリしているかつ自分にも理解できるトピック・規模の Pull Request を読み、変更箇所の感覚を持つ


元ネタとなる Pull Request はこちら。

serverless/serverless - pull/10936

https://github.com/serverless/serverless/pull/10936

CloudFormation の仕様はこちら。

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-url.html

コードベースは v3.12.0 を使用。

https://github.com/serverless/serverless/tree/v3.12.0

hassaku63hassaku63

sls のディレクトリ構造はこんな感じ

$ tree -d -L 3 -I docs -I scripts -I test
.
├── bin
├── commands
└── lib
    ├── aws
    ├── classes
    │   └── config-schema-handler
    ├── cli
    │   ├── commands-schema
    │   │   └── common-options
    │   ├── interactive-setup
    │   └── render-help
    ├── commands
    ├── configuration
    │   └── variables
    │       └── sources
    ├── plugins
    │   ├── aws
    │   │   ├── common
    │   │   ├── custom-resources
    │   │   ├── deploy
    │   │   ├── info
    │   │   ├── invoke-local
    │   │   ├── lib
    │   │   ├── package
    │   │   ├── remove
    │   │   └── utils
    │   ├── create
    │   ├── package
    │   │   └── lib
    │   └── plugin
    │       └── lib
    ├── templates
    │   └── recommended-list
    └── utils
        ├── fs
        ├── npm-package
        └── telemetry
hassaku63hassaku63

files を見ていくと、差分が出ているコード(テスト除く)は案外少ない

  • lib/plugins/aws/info/display.js
  • lib/plugins/aws/pacakge/compile/functions.js
  • lib/plugins/aws/info/get-stack-info.js
  • lib/plugins/aws/lib/naming.js
  • lib/plugins/aws/provider.js
  • lib/utils/telemetry/generate-payload.js

lib/utils/telemetry/* はおそらく Serverless Framework がサービス提供してる "Monitoring" 関係の機能。

https://github.com/serverless/serverless/blob/v3.12.0/lib/utils/telemetry/index.js#L180-L188

↑ここで外部のエンドポイントにリクエストを投げており、投げる先の URL である telemetryUrl は @serverless/utils で sls がマネージしているっぽいものが定義されている

https://github.com/serverless/utils/blob/main/analytics-and-notfications-url.js#L10-L17

ということなので、 sls がホストしているテレメトリ系の機能にデータを送信できるという感じ。

(私自身は普段 serverless のツールしか使わないので、あんまり関係がない...。実際に改修する立場になれば考慮する必要がありそうな場所だが、今回読みたい場所からすると本質的な場所じゃないのでスルーしていく)


lib/plugins/aws/* を見ていく

...が、その前に大枠を把握したいので sls の構文と cfn の対応を見ておく

https://github.com/serverless/serverless/blob/v3.12.0/docs/providers/aws/guide/serverless.yml.md#functions

https://github.com/serverless/serverless/blob/v3.12.0/lib/plugins/aws/lib/naming.js#L153-L155

serverless.yml の $.functions.* の下がこんな感じ

    url:
      authorizer: 'aws_iam' # Authorizer used for calls to Lambda URL
      cors:  # CORS configuration for Lambda URL, can also be defined as `true` with default CORS   configuration
        allowedOrigins:
          - *
        allowedHeaders:
          - Authorization
        allowedMethods:
          - GET
        allowCredentials: true
        exposedResponseHeaders:
          - SomeHeader
        maxAge: 3600

対して CloudFormation の AWS::Lambda::Url

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-url.html

Type: AWS::Lambda::Url
Properties: 
  AuthType: String
  Cors: 
    Cors
  Qualifier: String
  TargetFunctionArn: String

Qualifier 以外のプロパティは素直にマップしている感じがある。必要な仕事は package (あるいは deploy) 時に行われる CFn 生成時のデータ変換の仕事だけ考えればよい、と理解して良さそう

hassaku63hassaku63

plugin/aws/provider.js

基本的には AWS Provider に関する実装

例えば yaml の $.provider 以下の仕様を規定したり、AWS Provider としての機能(AWS Provider 適用下での $.functions 以下で使える構文とか?他は思いつかない)を宣言したり、といったことをしているように見える

PR では url に対応するスキーマを追加している。

initiallize() hook の中で ConfigSchemaHandler とかいうよくわからんやつに対して AWS Provider が持つべきスキーマを宣言している感じ。

ConfigSchemaHandler.defineProvider() に渡している第二引数 (option) の中身は、 $.definitions 以外は yaml の中で使えるデータ構造を規定しているように見える。

今回追加されているのは $.function.url なので、yaml の functions の下で使えるプロパティをここで定義した、という見方で良さそう。このメソッドを持つクラスも "ConfigSchemaHandler" なので、 yaml のスキーマを定義するクラスと見ていいのではないか。

まとめると、

AWS Provider を使う上で、yaml 上で使える構文を拡張したかったら lib/plugins/aws/provider.js の configSchemaHandler.defineProvider() の中身を拡張仕様に準拠するように書き換えることが必要。

hassaku63hassaku63

lib/plugins/aws/package/compile/function.js

この中で lib/plugins/aws/lib/naming.js の差分も使っているので一緒に取り上げる。

naming の差分を見ると getLambdaFunctionUrlLogicalId という機能が追加されており、これは CFn の AWS::Lambda::Url の CFn 上の論理リソースID に変換する役目のように見受けられる。横の関数を見ていても基本は AWS Provider において、 sls が用意する CFn 論理リソースID の導出のためのモジュールであることがわかる。ここで言えることは、

lib/plugins/aws/lib/naming.js は CFn 定義に変換する際の論理リソースID となる名前を自動計算するモジュールである ということ。なので、sls の方でよしなに CFn リソース定義を吐き出してくれる機能を作ろうと思ったらここを見ればよい

functions.js を見ていく

ここでは Compile の仕事をしているらしい(パッケージ階層的にも)。class AwsCompileFunctions のメソッドに compileFunctionUrl を追加しているほか、compileFunction にこのメソッドの呼び出しを追加している。

yaml 的には functions の下で書かれた定義であり、これを CFn 対応の構造に展開する作業が compile である。よって、functions における本質的な仕事+差分はこうなる。

  • yaml 定義に書かれた function の定義から CFn 準拠のデータ構造を生成すること
  • Lambda Function の compile 処理を URL に対応させること