Blog

motchi0214

[AWS Dev Day Online Japan フォローアップ] ShifterにおけるCloudFront Functions / Lambda@edge運用について

9月30日 15:10 – 15:50(Track H) ブレイクアウトセッションにて、「Lambda@Edge から CloudFront Functions への移行と CI / CDパイプラインの整備」をテーマに登壇いたしました。

当日の資料

CI / CDパイプライン付きCloudFront Functionsテンプレート

今回のセッションで紹介したCI / CDパイプライン付きのAWS CDKスタックテンプレートをGitHubにて公開しています。

lib/配下にAWS CDKやJestのE2Eテストで利用するインフラ定義を保存しています。

https://github.com/digitalcube/cloudfront-functions-template/blob/main/lib/cloudfront-functions-stack.ts

functions/配下にCloudFront Functionsの実体コードを保存しています。

https://github.com/digitalcube/cloudfront-functions-template/tree/main/functions

let / const / exportなどの手癖で書きがちなコードについては、AWS CDKのUnit Testで存在チェックを行なっています。


import { SynthUtils } from '@aws-cdk/assert';
import '@aws-cdk/assert/jest';
import * as cdk from '@aws-cdk/core';
import * as CloudfrontFunctions from '../../lib/cloudfront-functions-stack';

describe('unit test for the stack', () => {
  let app: cdk.App
  let stack: cdk.Stack
  beforeEach(() => {
    app = new cdk.App();
    stack = new CloudfrontFunctions.CloudfrontFunctionsStack(app, 'MyTestStack');
  })
  it('should not contain module.export on the functionCode', () => {
    const resources = Object.values(SynthUtils.toCloudFormation(stack).Resources)
      .filter((resource: any) => resource.Type === "AWS::CloudFront::Function" )
    resources.forEach((resource: any) => {
      expect(resource.Properties.FunctionCode).not.toContain('module.exports')
    })
  })
  it('should not contain const on the functionCode', () => {
    const resources = Object.values(SynthUtils.toCloudFormation(stack).Resources)
      .filter((resource: any) => resource.Type === "AWS::CloudFront::Function" )
    resources.forEach((resource: any) => {
      expect(resource.Properties.FunctionCode).not.toContain('const')
    })
  })
  it('should not contain let on the functionCode', () => {
    const resources = Object.values(SynthUtils.toCloudFormation(stack).Resources)
      .filter((resource: any) => resource.Type === "AWS::CloudFront::Function" )
    resources.forEach((resource: any) => {
      expect(resource.Properties.FunctionCode).not.toContain('let')
    })
  })
})

AWS CDK / Jest / CIタスクでのリソース定義再利用用ツール: cff-tools

Function名や実装などのインフラ・サービス定義を1元管理するために、cff-toolsというライブラリを公開しています。

リソース定義を以下のように行います。

import { Function } from 'cff-tools';
import { join } from 'path';

const stage = process.env.STAGE || 'development'
export const ViewerRequestFunction = new Function({
  name: 'ViewerRequestFunction-' + stage,
  runtime: 'cloudfront-js-1.0'
}, {
    functionFilePath: join(__dirname, '../functions/viewer_reqeust.js')
})

あとは、exportしたFunction定義を、AWS CDKやJestなどで利用するだけです。

AWS CDK

    new CfnFunction(this, 'ViewerRequestFunction' + stage, {
      functionCode: ViewerRequestFunction.getFunctionCode().toString(),
      autoPublish: false,
      functionConfig: {
        comment: "empty function (boiler plate)",
        runtime: ViewerRequestFunction.runtime
      },
      name: ViewerRequestFunction.name
    })

Jest

Jestの場合、CloudFront FunctionsでTestFunctionする場合のPayload Eventを作るためのビルダークラスも用意しています。

    describe('ViewerRequestFunction', () => {
        it('should return request with no updated', async () => {
            const eventBuilder = TestRequestEventFactory.create()
            const task = new FunctionTask(ViewerRequestFunction)
            const result = await task.runTestToGetFunctionOutput(eventBuilder, 'DEVELOPMENT')
            expect(result)
                .toEqual({
                    request: eventBuilder.getEvent().request
                })
        })
    })

CIタスク


const publishFunction = async (targetFunction: Function) => {
    const task = new FunctionTask(targetFunction)
    const result = await task.publish()
    return result
}

const functions = [ViewerRequestFunction, ViewerResponseFunction];


Promise.all(functions.map(publishFunction))
.then(result => {
    console.log(result)
})
.catch(e => {
    console.error(e)
})

Publish前のE2Eテスト

E2EテストをPublish前に挟むことで、「必要なレスポンスヘッダーが漏れていること」や「そもそもFunctionが実行できない(制限に引っかかったなど)」を検知できます。

おわりに

OSSツールを作ってまで環境を整備するのはToo muchだったかもしれないとは思います。

が、年1〜2回あるかないかの更新作業になりがちなリソースですので、ワークフローを固めておくことで、「久々に触るから、思い出すところからやらないと・・・」や「もう覚えていないから触りたくない・・・」となりがちな部分をつぶしておくことは重要です。

ShifterやAMIMOTOをより快適にご利用いただくためにも、デジタルキューブではさまざまなAWSサービス・新機能をテストし、導入にチャレンジしてまいります。

Share on facebook
Share on twitter