2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS CDKをDenoで動かす2 experimentalモジュール編

Last updated at Posted at 2022-06-01

の続きで、DenoでAWS CDKを動かしていきます。

要約

  • CDKモジュールを複数esm.shからimportする場合constructsのバージョン合わせが必要

experimentalモジュールを使う

AWS CDK v2はパッケージが1つにまとまりましたが、
依然experimentalモジュールは別モジュールとして配布されています。

experimentalモジュールとして一番使いそうなAmazon Kinesis Data Firehoseモジュールを使ってみます。

素朴なインポート

// app.ts
import cdk from "https://esm.sh/aws-cdk-lib@2.26.0";
import logs from "https://esm.sh/aws-cdk-lib@2.26.0/aws-logs";
import s3 from "https://esm.sh/aws-cdk-lib@2.26.0/aws-s3";
import firehose from "https://esm.sh/@aws-cdk/aws-kinesisfirehose-alpha@2.26.0-alpha.0";
import firehoseDestinations from "https://esm.sh/@aws-cdk/aws-kinesisfirehose-destinations-alpha@2.26.0-alpha.0";

const app = new cdk.App();
export const stack = new cdk.Stack(app, "MyStack");
new logs.LogGroup(stack, "MyLogGroup");

const firehoseDestBucket = new s3.Bucket(stack, "FirehoseDestBucket", {});
new firehose.DeliveryStream(stack, "FirehoseStream", {
  destinations: [
    new firehoseDestinations.S3Bucket(firehoseDestBucket),
  ],
});

app.synth();

前回のコードに追加してこんな感じですかね。
Firehose Streamと宛先のS3 Bucketを作ります。

$ cdk synth
Check file:///workspaces/cdk-deno/app.ts
error: TS2345 [ERROR]: Argument of type 'Stack' is not assignable to parameter of type 'Construct'.
  Types of property 'node' are incompatible.
    Type 'import("https://esm.sh/v83/constructs@10.1.22/lib/construct.d.ts").Node' is not assignable to type 'import("https://esm.sh/v84/constructs@10.1.23/lib/construct.d.ts").Node'.
      Types have separate declarations of a private property 'host'.
const firehoseDestBucket = new s3.Bucket(stack, "FirehoseDestBucket", {});
                                         ~~~~~
    at file:///workspaces/cdk-deno/app.ts:11:42

TS2345 [ERROR]: Argument of type 'Stack' is not assignable to parameter of type 'Construct'.
new firehose.DeliveryStream(stack, "FirehoseStream", {
                            ~~~~~
    at file:///workspaces/cdk-deno/app.ts:12:29

Found 2 errors.

ありゃ、エラーになってしまいました。
どうもaws-cdkが依存するconstructsモジュールの型が合わないようです。

(esm.shの推移的依存の解決とかどうなってるんだ…!?)

と前回思っていましたが、
初回リクエスト時の最新の依存で解決されてしまい、
推移的依存であるconstructsのバージョンがズレてしまっているようです。

esm.shでの依存関係の指定

https://esm.sh/ のドキュメントを読むと
以下のようにdepsパラメータで依存の指定ができるようです。良く出来てる。

import React from "https://esm.sh/react@16.14.0"
import useSWR from "https://esm.sh/swr?deps=react@16.14.0"

constructsのバージョンを指定したインポート

CDKコード

先程のソースのインポート文を修正します。

// app.ts
import cdk from "https://esm.sh/aws-cdk-lib@2.26.0?deps=constructs@10.1.23";
import logs from "https://esm.sh/aws-cdk-lib@2.26.0/aws-logs?deps=constructs@10.1.23";
import s3 from "https://esm.sh/aws-cdk-lib@2.26.0/aws-s3?deps=constructs@10.1.23";
import firehose from "https://esm.sh/@aws-cdk/aws-kinesisfirehose-alpha@2.26.0-alpha.0?deps=constructs@10.1.23";
import firehoseDestinations from "https://esm.sh/@aws-cdk/aws-kinesisfirehose-destinations-alpha@2.26.0-alpha.0?deps=constructs@10.1.23";

const app = new cdk.App();
export const stack = new cdk.Stack(app, "MyStack");
new logs.LogGroup(stack, "MyLogGroup");

const firehoseDestBucket = new s3.Bucket(stack, "FirehoseDestBucket", {});
new firehose.DeliveryStream(stack, "FirehoseStream", {
  destinations: [
    new firehoseDestinations.S3Bucket(firehoseDestBucket),
  ],
});

app.synth();
$ cdk synth
Check file:///workspaces/cdk-deno/app.ts
Resources:
  MyLogGroup5C0DAD85:
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays: 731
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Metadata:
      aws:cdk:path: MyStack/MyLogGroup/Resource
  FirehoseDestBucket5161CA67:
    Type: AWS::S3::Bucket
<...略...>

動きました!

テストコード

テストコードも修正します

// app.test.ts
import { assertSnapshot } from "https://deno.land/std@0.141.0/testing/snapshot.ts";
import assertions from "https://esm.sh/aws-cdk-lib@2.26.0/assertions?deps=constructs@10.1.23";
import { stack } from "./app.ts";

Deno.test("snapshot", async (t) => {
  await assertSnapshot(t, assertions.Template.fromStack(stack).toJSON());
});

そしてテスト実行します。

$ deno test --allow-all
running 1 test from ./app.test.ts
snapshot ... FAILED (18ms)

以下の差分があると失敗しました。
Stackのリソースを追加したのでこれは想定通りの失敗です。
素晴らしい。

 ERRORS 

snapshot => ./app.test.ts:5:6
error: AssertionError: Snapshot does not match:


    [Diff] Actual / Expected


    {
-     Mappings: {
-       awscdkawskinesisfirehoseCidrBlocks: {
-         "af-south-1": {
-           FirehoseCidrBlock: "13.244.121.224/27",
-         },
-         "ap-east-1": {
-           FirehoseCidrBlock: "18.162.221.32/27",
-         },
-         "ap-northeast-1": {
-           FirehoseCidrBlock: "13.113.196.224/27",
-         },
-         "ap-northeast-2": {
-           FirehoseCidrBlock: "13.209.1.64/27",
-         },
-         "ap-northeast-3": {
-           FirehoseCidrBlock: "13.208.177.192/27",
-         },
-         "ap-south-1": {
-           FirehoseCidrBlock: "13.232.67.32/27",
-         },
-         "ap-southeast-1": {
-           FirehoseCidrBlock: "13.228.64.192/27",
-         },
-         "ap-southeast-2": {
-           FirehoseCidrBlock: "13.210.67.224/27",
-         },
-         "ca-central-1": {
-           FirehoseCidrBlock: "35.183.92.128/27",
-         },
-         "cn-north-1": {
-           FirehoseCidrBlock: "52.81.151.32/27",
-         },
-         "cn-northwest-1": {
-           FirehoseCidrBlock: "161.189.23.64/27",
-         },
-         "eu-central-1": {
-           FirehoseCidrBlock: "35.158.127.160/27",
-         },
-         "eu-north-1": {
-           FirehoseCidrBlock: "13.53.63.224/27",
-         },
-         "eu-south-1": {
-           FirehoseCidrBlock: "15.161.135.128/27",
-         },
-         "eu-west-1": {
-           FirehoseCidrBlock: "52.19.239.192/27",
-         },
-         "eu-west-2": {
-           FirehoseCidrBlock: "18.130.1.96/27",
-         },
-         "eu-west-3": {
-           FirehoseCidrBlock: "35.180.1.96/27",
-         },
-         "me-south-1": {
-           FirehoseCidrBlock: "15.185.91.0/27",
-         },
-         "sa-east-1": {
-           FirehoseCidrBlock: "18.228.1.128/27",
-         },
-         "us-east-1": {
-           FirehoseCidrBlock: "52.70.63.192/27",
-         },
-         "us-east-2": {
-           FirehoseCidrBlock: "13.58.135.96/27",
-         },
-         "us-gov-east-1": {
-           FirehoseCidrBlock: "18.253.138.96/27",
-         },
-         "us-gov-west-1": {
-           FirehoseCidrBlock: "52.61.204.160/27",
-         },
-         "us-west-1": {
-           FirehoseCidrBlock: "13.57.135.192/27",
-         },
-         "us-west-2": {
-           FirehoseCidrBlock: "52.89.255.224/27",
-         },
-       },
-     },
      Parameters: {
        BootstrapVersion: {
          Default: "/cdk-bootstrap/hnb659fds/version",
          Description: "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]",
          Type: "AWS::SSM::Parameter::Value<String>",
        },
      },
      Resources: {
-       FirehoseDestBucket5161CA67: {
-         DeletionPolicy: "Retain",
-         Type: "AWS::S3::Bucket",
-         UpdateReplacePolicy: "Retain",
-       },
-       FirehoseStream85DFF016: {
-         DependsOn: [
-           "FirehoseStreamS3DestinationRoleDefaultPolicyD12DE613",
-         ],
-         Properties: {
-           DeliveryStreamType: "DirectPut",
-           ExtendedS3DestinationConfiguration: {
-             BucketARN: {
-               "Fn::GetAtt": [
-                 "FirehoseDestBucket5161CA67",
-                 "Arn",
-               ],
-             },
-             CloudWatchLoggingOptions: {
-               Enabled: true,
-               LogGroupName: {
-                 Ref: "FirehoseStreamLogGroup9325AD6F",
-               },
-               LogStreamName: {
-                 Ref: "FirehoseStreamLogGroupS3Destination159E9820",
-               },
-             },
-             RoleARN: {
-               "Fn::GetAtt": [
-                 "FirehoseStreamS3DestinationRole5FDC3E58",
-                 "Arn",
-               ],
-             },
-           },
-         },
-         Type: "AWS::KinesisFirehose::DeliveryStream",
-       },
-       FirehoseStreamLogGroup9325AD6F: {
-         DeletionPolicy: "Retain",
-         Properties: {
-           RetentionInDays: 731,
-         },
-         Type: "AWS::Logs::LogGroup",
-         UpdateReplacePolicy: "Retain",
-       },
-       FirehoseStreamLogGroupS3Destination159E9820: {
-         DeletionPolicy: "Retain",
-         Properties: {
-           LogGroupName: {
-             Ref: "FirehoseStreamLogGroup9325AD6F",
-           },
-         },
-         Type: "AWS::Logs::LogStream",
-         UpdateReplacePolicy: "Retain",
-       },
-       FirehoseStreamS3DestinationRole5FDC3E58: {
-         Properties: {
-           AssumeRolePolicyDocument: {
-             Statement: [
-               {
-                 Action: "sts:AssumeRole",
-                 Effect: "Allow",
-                 Principal: {
-                   Service: "firehose.amazonaws.com",
-                 },
-               },
-             ],
-             Version: "2012-10-17",
-           },
-         },
-         Type: "AWS::IAM::Role",
-       },
-       FirehoseStreamS3DestinationRoleDefaultPolicyD12DE613: {
-         Properties: {
-           PolicyDocument: {
-             Statement: [
-               {
-                 Action: [
-                   "s3:GetObject*",
-                   "s3:GetBucket*",
-                   "s3:List*",
-                   "s3:DeleteObject*",
-                   "s3:PutObject",
-                   "s3:PutObjectLegalHold",
-                   "s3:PutObjectRetention",
-                   "s3:PutObjectTagging",
-                   "s3:PutObjectVersionTagging",
-                   "s3:Abort*",
-                 ],
-                 Effect: "Allow",
-                 Resource: [
-                   {
-                     "Fn::GetAtt": [
-                       "FirehoseDestBucket5161CA67",
-                       "Arn",
-                     ],
-                   },
-                   {
-                     "Fn::Join": [
-                       "",
-                       [
-                         {
-                           "Fn::GetAtt": [
-                             "FirehoseDestBucket5161CA67",
-                             "Arn",
-                           ],
-                         },
-                         "/*",
-                       ],
-                     ],
-                   },
-                 ],
-               },
-               {
-                 Action: [
-                   "logs:CreateLogStream",
-                   "logs:PutLogEvents",
-                 ],
-                 Effect: "Allow",
-                 Resource: {
-                   "Fn::GetAtt": [
-                     "FirehoseStreamLogGroup9325AD6F",
-                     "Arn",
-                   ],
-                 },
-               },
-             ],
-             Version: "2012-10-17",
-           },
-           PolicyName: "FirehoseStreamS3DestinationRoleDefaultPolicyD12DE613",
-           Roles: [
-             {
-               Ref: "FirehoseStreamS3DestinationRole5FDC3E58",
-             },
-           ],
-         },
-         Type: "AWS::IAM::Policy",
-       },
-       FirehoseStreamServiceRole98797615: {
-         Properties: {
-           AssumeRolePolicyDocument: {
-             Statement: [
-               {
-                 Action: "sts:AssumeRole",
-                 Effect: "Allow",
-                 Principal: {
-                   Service: "firehose.amazonaws.com",
-                 },
-               },
-             ],
-             Version: "2012-10-17",
-           },
-         },
-         Type: "AWS::IAM::Role",
-       },
        MyLogGroup5C0DAD85: {
          DeletionPolicy: "Retain",
          Properties: {
            RetentionInDays: 731,
          },
          Type: "AWS::Logs::LogGroup",
          UpdateReplacePolicy: "Retain",
        },
      },
      Rules: {
        CheckBootstrapVersion: {
          Assertions: [
            {
              Assert: {
                "Fn::Not": [
                  {
                    "Fn::Contains": [
                      [
                        "1",
                        "2",
                        "3",
                        "4",
                        "5",
                      ],
                      {
                        Ref: "BootstrapVersion",
                      },
                    ],
                  },
                ],
              },
              AssertDescription: "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.",
            },
          ],
        },
      },
    }

    throw new AssertionError(
          ^
    at assertSnapshot (https://deno.land/std@0.141.0/testing/snapshot.ts:407:11)
    at async file:///workspaces/cdk-deno/app.test.ts:6:3

 FAILURES 

snapshot => ./app.test.ts:5:6

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (2s)

error: Test failed

スナップショットを更新するオプション(-- --update)をつけて再実行します。

$ deno test --allow-all -- --update
running 1 test from ./app.test.ts
snapshot ... ok (14ms)

------- output -------

 > 1 snapshots updated.

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (2s)

これでスナップショットが更新されました。

インポートの整理

動くようにはなりましたがこれだとあちこちにCDKのバージョンや
constructsのバージョンが散らばって保守性が低い上に
インポート行が膨れて非常にノイズが大きいので非常に気持ち悪いです。

Denoのドキュメントによるとこういう場合は
deps.tsというファイルでまとめて再エクスポートするのがお決まりのようです。

// deps.ts
import awsCdkLib from "https://esm.sh/aws-cdk-lib@2.26.0?deps=constructs@10.1.23";
export const cdk = awsCdkLib;
export const { aws_s3: s3, aws_logs: logs } = awsCdkLib;

export { default as firehose } from "https://esm.sh/@aws-cdk/aws-kinesisfirehose-alpha@2.26.0-alpha.0?deps=constructs@10.1.23";
export { default as firehoseTo } from "https://esm.sh/@aws-cdk/aws-kinesisfirehose-destinations-alpha@2.26.0-alpha.0?deps=constructs@10.1.23";

// test
export { assertSnapshot } from "https://deno.land/std@0.141.0/testing/snapshot.ts";
export const { Template } = awsCdkLib.assertions;

とりあえずこんな感じですかね。
CDKとconstructsのバージョンを文字列定数に切り出したかったのですが
fromは文字列リテラルしか受け付けずエラーとなってしまいました。
まぁファイルが1つにまとまっただけマシか…

他に良い書き方があれば教えて下さい…

呼び出し元

// app.ts
import { cdk, firehose, firehoseTo, logs, s3 } from "./deps.ts";

const app = new cdk.App();
export const stack = new cdk.Stack(app, "MyStack");
new logs.LogGroup(stack, "MyLogGroup");

const firehoseDestBucket = new s3.Bucket(stack, "FirehoseDestBucket", {});
new firehose.DeliveryStream(stack, "FirehoseStream", {
  destinations: [
    new firehoseTo.S3Bucket(firehoseDestBucket),
  ],
});

app.synth();
// app.test.ts
import { assertSnapshot, Template } from "./deps.ts";
import { stack } from "./app.ts";

Deno.test("snapshot", async (t) => {
  await assertSnapshot(t, Template.fromStack(stack).toJSON());
});

利用側は非常にスッキリしましたね!これなら満足です:relaxed:

全体のソースはこちら

今回の感想

今のところいい感じに動いてる。CDK + Denoマジでなかなかいいんじゃないの?

続き

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?