の続きで、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());
});
利用側は非常にスッキリしましたね!これなら満足です
全体のソースはこちら
今回の感想
今のところいい感じに動いてる。CDK + Denoマジでなかなかいいんじゃないの?
続き