skuba 9.0.0-renovate-eslint-9.x-20240923103202 → 9.0.1-upgrade-cdk-template-20241002233314
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/github/issueComment.js.map +2 -2
- package/lib/cli/adapter/eslint.js +4 -4
- package/lib/cli/adapter/eslint.js.map +2 -2
- package/lib/cli/lint/autofix.js +0 -15
- package/lib/cli/lint/autofix.js.map +2 -2
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/collapseDuplicateMergeKeys.js +15 -6
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/collapseDuplicateMergeKeys.js.map +2 -2
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/index.js +10 -0
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/index.js.map +2 -2
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/moveNpmrcMounts.d.ts +2 -0
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/moveNpmrcMounts.js +82 -0
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/moveNpmrcMounts.js.map +7 -0
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/patchDockerCompose.js +1 -3
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/patchDockerCompose.js.map +2 -2
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/patchDockerImages.d.ts +2 -0
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/patchDockerImages.js +141 -0
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/patchDockerImages.js.map +7 -0
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/upgradeESLint.js +5 -3
- package/lib/cli/lint/internalLints/upgrade/patches/8.2.1/upgradeESLint.js.map +2 -2
- package/lib/wrapper/requestListener.js.map +2 -2
- package/package.json +9 -9
- package/template/express-rest-api/.buildkite/pipeline.yml +2 -2
- package/template/express-rest-api/Dockerfile +1 -1
- package/template/express-rest-api/Dockerfile.dev-deps +1 -1
- package/template/express-rest-api/package.json +1 -1
- package/template/greeter/.buildkite/pipeline.yml +2 -2
- package/template/greeter/Dockerfile +1 -1
- package/template/greeter/package.json +1 -1
- package/template/koa-rest-api/.buildkite/pipeline.yml +2 -2
- package/template/koa-rest-api/Dockerfile +1 -1
- package/template/koa-rest-api/Dockerfile.dev-deps +1 -1
- package/template/koa-rest-api/package.json +4 -4
- package/template/koa-rest-api/src/framework/bodyParser.ts +1 -1
- package/template/koa-rest-api/src/framework/server.test.ts +0 -1
- package/template/lambda-sqs-worker/.buildkite/pipeline.yml +2 -2
- package/template/lambda-sqs-worker/Dockerfile +1 -1
- package/template/lambda-sqs-worker/package.json +2 -2
- package/template/lambda-sqs-worker/serverless.yml +1 -1
- package/template/lambda-sqs-worker-cdk/.buildkite/pipeline.yml +2 -2
- package/template/lambda-sqs-worker-cdk/.env +1 -0
- package/template/lambda-sqs-worker-cdk/Dockerfile +1 -1
- package/template/lambda-sqs-worker-cdk/README.md +145 -0
- package/template/lambda-sqs-worker-cdk/infra/__snapshots__/appStack.test.ts.snap +102 -134
- package/template/lambda-sqs-worker-cdk/infra/appStack.test.ts +13 -2
- package/template/lambda-sqs-worker-cdk/infra/appStack.ts +52 -6
- package/template/lambda-sqs-worker-cdk/infra/config.ts +3 -0
- package/template/lambda-sqs-worker-cdk/package.json +9 -2
- package/template/lambda-sqs-worker-cdk/src/app.test.ts +116 -0
- package/template/lambda-sqs-worker-cdk/src/app.ts +43 -21
- package/template/lambda-sqs-worker-cdk/src/config.ts +15 -0
- package/template/lambda-sqs-worker-cdk/src/framework/handler.test.ts +61 -0
- package/template/lambda-sqs-worker-cdk/src/framework/handler.ts +43 -0
- package/template/lambda-sqs-worker-cdk/src/framework/logging.ts +27 -0
- package/template/lambda-sqs-worker-cdk/src/framework/metrics.ts +14 -0
- package/template/lambda-sqs-worker-cdk/src/framework/validation.test.ts +84 -0
- package/template/lambda-sqs-worker-cdk/src/framework/validation.ts +10 -0
- package/template/lambda-sqs-worker-cdk/src/mapping/jobScorer.ts +22 -0
- package/template/lambda-sqs-worker-cdk/src/services/aws.ts +5 -0
- package/template/lambda-sqs-worker-cdk/src/services/jobScorer.test.ts +44 -0
- package/template/lambda-sqs-worker-cdk/src/services/jobScorer.ts +59 -0
- package/template/lambda-sqs-worker-cdk/src/services/pipelineEventSender.test.ts +40 -0
- package/template/lambda-sqs-worker-cdk/src/services/pipelineEventSender.ts +33 -0
- package/template/lambda-sqs-worker-cdk/src/testing/handler.ts +13 -0
- package/template/lambda-sqs-worker-cdk/src/testing/logging.ts +19 -0
- package/template/lambda-sqs-worker-cdk/src/testing/services.ts +28 -0
- package/template/lambda-sqs-worker-cdk/src/testing/types.ts +33 -0
- package/template/lambda-sqs-worker-cdk/src/types/jobScorer.ts +15 -0
- package/template/lambda-sqs-worker-cdk/src/types/pipelineEvents.ts +21 -0
|
@@ -10,6 +10,40 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
|
|
|
10
10
|
},
|
|
11
11
|
},
|
|
12
12
|
"Resources": {
|
|
13
|
+
"datadogapikeysecret046FEF06": {
|
|
14
|
+
"DeletionPolicy": "Delete",
|
|
15
|
+
"Properties": {
|
|
16
|
+
"GenerateSecretString": {},
|
|
17
|
+
},
|
|
18
|
+
"Type": "AWS::SecretsManager::Secret",
|
|
19
|
+
"UpdateReplacePolicy": "Delete",
|
|
20
|
+
},
|
|
21
|
+
"destinationtopicDCE2E0B8": {
|
|
22
|
+
"Properties": {
|
|
23
|
+
"KmsMasterKeyId": {
|
|
24
|
+
"Fn::Join": [
|
|
25
|
+
"",
|
|
26
|
+
[
|
|
27
|
+
"arn:",
|
|
28
|
+
{
|
|
29
|
+
"Ref": "AWS::Partition",
|
|
30
|
+
},
|
|
31
|
+
":kms:",
|
|
32
|
+
{
|
|
33
|
+
"Ref": "AWS::Region",
|
|
34
|
+
},
|
|
35
|
+
":",
|
|
36
|
+
{
|
|
37
|
+
"Ref": "AWS::AccountId",
|
|
38
|
+
},
|
|
39
|
+
":alias/aws/sns",
|
|
40
|
+
],
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
"TopicName": "serviceName",
|
|
44
|
+
},
|
|
45
|
+
"Type": "AWS::SNS::Topic",
|
|
46
|
+
},
|
|
13
47
|
"kmskey49FBC3B3": {
|
|
14
48
|
"DeletionPolicy": "Retain",
|
|
15
49
|
"Properties": {
|
|
@@ -105,17 +139,6 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
|
|
|
105
139
|
},
|
|
106
140
|
"Resource": "*",
|
|
107
141
|
},
|
|
108
|
-
{
|
|
109
|
-
"Action": [
|
|
110
|
-
"kms:Decrypt",
|
|
111
|
-
"kms:GenerateDataKey",
|
|
112
|
-
],
|
|
113
|
-
"Effect": "Allow",
|
|
114
|
-
"Principal": {
|
|
115
|
-
"Service": "sns.amazonaws.com",
|
|
116
|
-
},
|
|
117
|
-
"Resource": "*",
|
|
118
|
-
},
|
|
119
142
|
],
|
|
120
143
|
"Version": "2012-10-17",
|
|
121
144
|
},
|
|
@@ -135,9 +158,6 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
|
|
|
135
158
|
},
|
|
136
159
|
"Type": "AWS::KMS::Alias",
|
|
137
160
|
},
|
|
138
|
-
"sourcetopic7C3DC892": {
|
|
139
|
-
"Type": "AWS::SNS::Topic",
|
|
140
|
-
},
|
|
141
161
|
"worker28EA3E30": {
|
|
142
162
|
"DependsOn": [
|
|
143
163
|
"workerServiceRoleDefaultPolicyBA498553",
|
|
@@ -156,6 +176,9 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
|
|
|
156
176
|
"Description": "Updated at 1212-12-12T12:12:12.121Z",
|
|
157
177
|
"Environment": {
|
|
158
178
|
"Variables": {
|
|
179
|
+
"DESTINATION_SNS_TOPIC_ARN": {
|
|
180
|
+
"Ref": "destinationtopicDCE2E0B8",
|
|
181
|
+
},
|
|
159
182
|
"ENVIRONMENT": "dev",
|
|
160
183
|
"NODE_ENV": "production",
|
|
161
184
|
"NODE_OPTIONS": "--enable-source-maps",
|
|
@@ -171,6 +194,20 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
|
|
|
171
194
|
"Arn",
|
|
172
195
|
],
|
|
173
196
|
},
|
|
197
|
+
"Layers": [
|
|
198
|
+
{
|
|
199
|
+
"Fn::Join": [
|
|
200
|
+
"",
|
|
201
|
+
[
|
|
202
|
+
"arn:aws:lambda:",
|
|
203
|
+
{
|
|
204
|
+
"Ref": "AWS::Region",
|
|
205
|
+
},
|
|
206
|
+
":464622532012:layer:Datadog-Extension-ARM:58",
|
|
207
|
+
],
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
],
|
|
174
211
|
"ReservedConcurrentExecutions": 2,
|
|
175
212
|
"Role": {
|
|
176
213
|
"Fn::GetAtt": [
|
|
@@ -611,59 +648,6 @@ exports[`returns expected CloudFormation stack for dev 1`] = `
|
|
|
611
648
|
"Type": "AWS::SQS::Queue",
|
|
612
649
|
"UpdateReplacePolicy": "Delete",
|
|
613
650
|
},
|
|
614
|
-
"workerqueuePolicy97054CB4": {
|
|
615
|
-
"Properties": {
|
|
616
|
-
"PolicyDocument": {
|
|
617
|
-
"Statement": [
|
|
618
|
-
{
|
|
619
|
-
"Action": "sqs:SendMessage",
|
|
620
|
-
"Condition": {
|
|
621
|
-
"ArnEquals": {
|
|
622
|
-
"aws:SourceArn": {
|
|
623
|
-
"Ref": "sourcetopic7C3DC892",
|
|
624
|
-
},
|
|
625
|
-
},
|
|
626
|
-
},
|
|
627
|
-
"Effect": "Allow",
|
|
628
|
-
"Principal": {
|
|
629
|
-
"Service": "sns.amazonaws.com",
|
|
630
|
-
},
|
|
631
|
-
"Resource": {
|
|
632
|
-
"Fn::GetAtt": [
|
|
633
|
-
"workerqueueA05CE5C6",
|
|
634
|
-
"Arn",
|
|
635
|
-
],
|
|
636
|
-
},
|
|
637
|
-
},
|
|
638
|
-
],
|
|
639
|
-
"Version": "2012-10-17",
|
|
640
|
-
},
|
|
641
|
-
"Queues": [
|
|
642
|
-
{
|
|
643
|
-
"Ref": "workerqueueA05CE5C6",
|
|
644
|
-
},
|
|
645
|
-
],
|
|
646
|
-
},
|
|
647
|
-
"Type": "AWS::SQS::QueuePolicy",
|
|
648
|
-
},
|
|
649
|
-
"workerqueueappStacksourcetopic613C6BDBD2F224F5": {
|
|
650
|
-
"DependsOn": [
|
|
651
|
-
"workerqueuePolicy97054CB4",
|
|
652
|
-
],
|
|
653
|
-
"Properties": {
|
|
654
|
-
"Endpoint": {
|
|
655
|
-
"Fn::GetAtt": [
|
|
656
|
-
"workerqueueA05CE5C6",
|
|
657
|
-
"Arn",
|
|
658
|
-
],
|
|
659
|
-
},
|
|
660
|
-
"Protocol": "sqs",
|
|
661
|
-
"TopicArn": {
|
|
662
|
-
"Ref": "sourcetopic7C3DC892",
|
|
663
|
-
},
|
|
664
|
-
},
|
|
665
|
-
"Type": "AWS::SNS::Subscription",
|
|
666
|
-
},
|
|
667
651
|
"workerqueuedeadletters83F3505C": {
|
|
668
652
|
"DeletionPolicy": "Delete",
|
|
669
653
|
"Properties": {
|
|
@@ -719,6 +703,40 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
|
|
|
719
703
|
},
|
|
720
704
|
},
|
|
721
705
|
"Resources": {
|
|
706
|
+
"datadogapikeysecret046FEF06": {
|
|
707
|
+
"DeletionPolicy": "Delete",
|
|
708
|
+
"Properties": {
|
|
709
|
+
"GenerateSecretString": {},
|
|
710
|
+
},
|
|
711
|
+
"Type": "AWS::SecretsManager::Secret",
|
|
712
|
+
"UpdateReplacePolicy": "Delete",
|
|
713
|
+
},
|
|
714
|
+
"destinationtopicDCE2E0B8": {
|
|
715
|
+
"Properties": {
|
|
716
|
+
"KmsMasterKeyId": {
|
|
717
|
+
"Fn::Join": [
|
|
718
|
+
"",
|
|
719
|
+
[
|
|
720
|
+
"arn:",
|
|
721
|
+
{
|
|
722
|
+
"Ref": "AWS::Partition",
|
|
723
|
+
},
|
|
724
|
+
":kms:",
|
|
725
|
+
{
|
|
726
|
+
"Ref": "AWS::Region",
|
|
727
|
+
},
|
|
728
|
+
":",
|
|
729
|
+
{
|
|
730
|
+
"Ref": "AWS::AccountId",
|
|
731
|
+
},
|
|
732
|
+
":alias/aws/sns",
|
|
733
|
+
],
|
|
734
|
+
],
|
|
735
|
+
},
|
|
736
|
+
"TopicName": "serviceName",
|
|
737
|
+
},
|
|
738
|
+
"Type": "AWS::SNS::Topic",
|
|
739
|
+
},
|
|
722
740
|
"kmskey49FBC3B3": {
|
|
723
741
|
"DeletionPolicy": "Retain",
|
|
724
742
|
"Properties": {
|
|
@@ -814,17 +832,6 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
|
|
|
814
832
|
},
|
|
815
833
|
"Resource": "*",
|
|
816
834
|
},
|
|
817
|
-
{
|
|
818
|
-
"Action": [
|
|
819
|
-
"kms:Decrypt",
|
|
820
|
-
"kms:GenerateDataKey",
|
|
821
|
-
],
|
|
822
|
-
"Effect": "Allow",
|
|
823
|
-
"Principal": {
|
|
824
|
-
"Service": "sns.amazonaws.com",
|
|
825
|
-
},
|
|
826
|
-
"Resource": "*",
|
|
827
|
-
},
|
|
828
835
|
],
|
|
829
836
|
"Version": "2012-10-17",
|
|
830
837
|
},
|
|
@@ -844,9 +851,6 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
|
|
|
844
851
|
},
|
|
845
852
|
"Type": "AWS::KMS::Alias",
|
|
846
853
|
},
|
|
847
|
-
"sourcetopic7C3DC892": {
|
|
848
|
-
"Type": "AWS::SNS::Topic",
|
|
849
|
-
},
|
|
850
854
|
"worker28EA3E30": {
|
|
851
855
|
"DependsOn": [
|
|
852
856
|
"workerServiceRoleDefaultPolicyBA498553",
|
|
@@ -865,6 +869,9 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
|
|
|
865
869
|
"Description": "Updated at 1212-12-12T12:12:12.121Z",
|
|
866
870
|
"Environment": {
|
|
867
871
|
"Variables": {
|
|
872
|
+
"DESTINATION_SNS_TOPIC_ARN": {
|
|
873
|
+
"Ref": "destinationtopicDCE2E0B8",
|
|
874
|
+
},
|
|
868
875
|
"ENVIRONMENT": "prod",
|
|
869
876
|
"NODE_ENV": "production",
|
|
870
877
|
"NODE_OPTIONS": "--enable-source-maps",
|
|
@@ -880,6 +887,20 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
|
|
|
880
887
|
"Arn",
|
|
881
888
|
],
|
|
882
889
|
},
|
|
890
|
+
"Layers": [
|
|
891
|
+
{
|
|
892
|
+
"Fn::Join": [
|
|
893
|
+
"",
|
|
894
|
+
[
|
|
895
|
+
"arn:aws:lambda:",
|
|
896
|
+
{
|
|
897
|
+
"Ref": "AWS::Region",
|
|
898
|
+
},
|
|
899
|
+
":464622532012:layer:Datadog-Extension-ARM:58",
|
|
900
|
+
],
|
|
901
|
+
],
|
|
902
|
+
},
|
|
903
|
+
],
|
|
883
904
|
"ReservedConcurrentExecutions": 20,
|
|
884
905
|
"Role": {
|
|
885
906
|
"Fn::GetAtt": [
|
|
@@ -1320,59 +1341,6 @@ exports[`returns expected CloudFormation stack for prod 1`] = `
|
|
|
1320
1341
|
"Type": "AWS::SQS::Queue",
|
|
1321
1342
|
"UpdateReplacePolicy": "Delete",
|
|
1322
1343
|
},
|
|
1323
|
-
"workerqueuePolicy97054CB4": {
|
|
1324
|
-
"Properties": {
|
|
1325
|
-
"PolicyDocument": {
|
|
1326
|
-
"Statement": [
|
|
1327
|
-
{
|
|
1328
|
-
"Action": "sqs:SendMessage",
|
|
1329
|
-
"Condition": {
|
|
1330
|
-
"ArnEquals": {
|
|
1331
|
-
"aws:SourceArn": {
|
|
1332
|
-
"Ref": "sourcetopic7C3DC892",
|
|
1333
|
-
},
|
|
1334
|
-
},
|
|
1335
|
-
},
|
|
1336
|
-
"Effect": "Allow",
|
|
1337
|
-
"Principal": {
|
|
1338
|
-
"Service": "sns.amazonaws.com",
|
|
1339
|
-
},
|
|
1340
|
-
"Resource": {
|
|
1341
|
-
"Fn::GetAtt": [
|
|
1342
|
-
"workerqueueA05CE5C6",
|
|
1343
|
-
"Arn",
|
|
1344
|
-
],
|
|
1345
|
-
},
|
|
1346
|
-
},
|
|
1347
|
-
],
|
|
1348
|
-
"Version": "2012-10-17",
|
|
1349
|
-
},
|
|
1350
|
-
"Queues": [
|
|
1351
|
-
{
|
|
1352
|
-
"Ref": "workerqueueA05CE5C6",
|
|
1353
|
-
},
|
|
1354
|
-
],
|
|
1355
|
-
},
|
|
1356
|
-
"Type": "AWS::SQS::QueuePolicy",
|
|
1357
|
-
},
|
|
1358
|
-
"workerqueueappStacksourcetopic613C6BDBD2F224F5": {
|
|
1359
|
-
"DependsOn": [
|
|
1360
|
-
"workerqueuePolicy97054CB4",
|
|
1361
|
-
],
|
|
1362
|
-
"Properties": {
|
|
1363
|
-
"Endpoint": {
|
|
1364
|
-
"Fn::GetAtt": [
|
|
1365
|
-
"workerqueueA05CE5C6",
|
|
1366
|
-
"Arn",
|
|
1367
|
-
],
|
|
1368
|
-
},
|
|
1369
|
-
"Protocol": "sqs",
|
|
1370
|
-
"TopicArn": {
|
|
1371
|
-
"Ref": "sourcetopic7C3DC892",
|
|
1372
|
-
},
|
|
1373
|
-
},
|
|
1374
|
-
"Type": "AWS::SNS::Subscription",
|
|
1375
|
-
},
|
|
1376
1344
|
"workerqueuedeadletters83F3505C": {
|
|
1377
1345
|
"DeletionPolicy": "Delete",
|
|
1378
1346
|
"Properties": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { App, aws_sns } from 'aws-cdk-lib';
|
|
1
|
+
import { App, aws_secretsmanager, aws_sns } from 'aws-cdk-lib';
|
|
2
2
|
import { Template } from 'aws-cdk-lib/assertions';
|
|
3
3
|
|
|
4
4
|
const currentDate = '1212-12-12T12:12:12.121Z';
|
|
@@ -36,6 +36,12 @@ it.each(['dev', 'prod'])(
|
|
|
36
36
|
.spyOn(aws_sns.Topic, 'fromTopicArn')
|
|
37
37
|
.mockImplementation((scope, id) => new aws_sns.Topic(scope, id));
|
|
38
38
|
|
|
39
|
+
jest
|
|
40
|
+
.spyOn(aws_secretsmanager.Secret, 'fromSecretPartialArn')
|
|
41
|
+
.mockImplementation(
|
|
42
|
+
(scope, id) => new aws_secretsmanager.Secret(scope, id),
|
|
43
|
+
);
|
|
44
|
+
|
|
39
45
|
const app = new App();
|
|
40
46
|
|
|
41
47
|
const stack = new AppStack(app, 'appStack');
|
|
@@ -47,13 +53,18 @@ it.each(['dev', 'prod'])(
|
|
|
47
53
|
/"S3Key":"([0-9a-f]+)\.zip"/g,
|
|
48
54
|
(_, hash) => `"S3Key":"${'x'.repeat(hash.length)}.zip"`,
|
|
49
55
|
)
|
|
50
|
-
.
|
|
56
|
+
.replace(
|
|
51
57
|
/workerCurrentVersion([0-9a-zA-Z]+)"/g,
|
|
52
58
|
(_, hash) => `workerCurrentVersion${'x'.repeat(hash.length)}"`,
|
|
53
59
|
)
|
|
54
60
|
.replaceAll(
|
|
55
61
|
/"Value":"\d+\.\d+\.\d+-([^"]+)"/g,
|
|
56
62
|
(_, hash) => `"Value": "x.x.x-${'x'.repeat(hash.length)}"`,
|
|
63
|
+
)
|
|
64
|
+
.replace(
|
|
65
|
+
/"DD_TAGS":"git.commit.sha:([0-9a-f]+),git.repository_url:([^\"]+)"/g,
|
|
66
|
+
(_, sha, url) =>
|
|
67
|
+
`"DD_TAGS":"git.commit.sha:${'x'.repeat(sha.length)},git.repository_url:${'x'.repeat(url.length)}"`,
|
|
57
68
|
);
|
|
58
69
|
expect(JSON.parse(json)).toMatchSnapshot();
|
|
59
70
|
},
|
|
@@ -8,11 +8,12 @@ import {
|
|
|
8
8
|
aws_lambda,
|
|
9
9
|
aws_lambda_event_sources,
|
|
10
10
|
aws_lambda_nodejs,
|
|
11
|
+
aws_secretsmanager,
|
|
11
12
|
aws_sns,
|
|
12
|
-
aws_sns_subscriptions,
|
|
13
13
|
aws_sqs,
|
|
14
14
|
} from 'aws-cdk-lib';
|
|
15
15
|
import type { Construct } from 'constructs';
|
|
16
|
+
import { Datadog, getExtensionLayerArn } from 'datadog-cdk-constructs-v2';
|
|
16
17
|
|
|
17
18
|
import { config } from './config';
|
|
18
19
|
|
|
@@ -49,13 +50,42 @@ export class AppStack extends Stack {
|
|
|
49
50
|
encryptionMasterKey: kmsKey,
|
|
50
51
|
});
|
|
51
52
|
|
|
52
|
-
const topic = aws_sns.Topic.fromTopicArn(
|
|
53
|
+
// const topic = aws_sns.Topic.fromTopicArn(
|
|
54
|
+
// this,
|
|
55
|
+
// 'source-topic',
|
|
56
|
+
// config.sourceSnsTopicArn,
|
|
57
|
+
// );
|
|
58
|
+
|
|
59
|
+
// topic.addSubscription(
|
|
60
|
+
// new aws_sns_subscriptions.SqsSubscription(queue, {
|
|
61
|
+
// rawMessageDelivery: true, // Remove this property if you require end to end datadog tracing
|
|
62
|
+
// }),
|
|
63
|
+
// );
|
|
64
|
+
|
|
65
|
+
const snsKey = aws_kms.Alias.fromAliasName(
|
|
53
66
|
this,
|
|
54
|
-
'
|
|
55
|
-
|
|
67
|
+
'alias-aws-sns',
|
|
68
|
+
'alias/aws/sns',
|
|
56
69
|
);
|
|
57
70
|
|
|
58
|
-
|
|
71
|
+
const destinationTopic = new aws_sns.Topic(this, 'destination-topic', {
|
|
72
|
+
masterKey: snsKey,
|
|
73
|
+
topicName: '<%- serviceName %>',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const datadogSecret = aws_secretsmanager.Secret.fromSecretPartialArn(
|
|
77
|
+
this,
|
|
78
|
+
'datadog-api-key-secret',
|
|
79
|
+
config.datadogApiKeySecretArn,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const datadog = new Datadog(this, 'datadog', {
|
|
83
|
+
apiKeySecret: datadogSecret,
|
|
84
|
+
addLayers: false,
|
|
85
|
+
enableDatadogLogs: false,
|
|
86
|
+
flushMetricsToLogs: false,
|
|
87
|
+
extensionLayerVersion: 58,
|
|
88
|
+
});
|
|
59
89
|
|
|
60
90
|
const architecture = '<%- lambdaCdkArchitecture %>';
|
|
61
91
|
|
|
@@ -85,17 +115,33 @@ export class AppStack extends Stack {
|
|
|
85
115
|
...defaultWorkerConfig,
|
|
86
116
|
entry: './src/app.ts',
|
|
87
117
|
timeout: Duration.seconds(30),
|
|
88
|
-
bundling:
|
|
118
|
+
bundling: {
|
|
119
|
+
...defaultWorkerBundlingConfig,
|
|
120
|
+
nodeModules: ['datadog-lambda-js', 'dd-trace'],
|
|
121
|
+
},
|
|
89
122
|
functionName: '<%- serviceName %>',
|
|
90
123
|
environment: {
|
|
91
124
|
...defaultWorkerEnvironment,
|
|
92
125
|
...config.workerLambda.environment,
|
|
126
|
+
DESTINATION_SNS_TOPIC_ARN: destinationTopic.topicArn,
|
|
93
127
|
},
|
|
94
128
|
// https://github.com/aws/aws-cdk/issues/28237
|
|
95
129
|
// This forces the lambda to be updated on every deployment
|
|
96
130
|
// If you do not wish to use hotswap, you can remove the new Date().toISOString() from the description
|
|
97
131
|
description: `Updated at ${new Date().toISOString()}`,
|
|
98
132
|
reservedConcurrentExecutions: config.workerLambda.reservedConcurrency,
|
|
133
|
+
layers: [
|
|
134
|
+
// Workaround for https://github.com/DataDog/datadog-cdk-constructs/issues/201
|
|
135
|
+
aws_lambda.LayerVersion.fromLayerVersionArn(
|
|
136
|
+
this,
|
|
137
|
+
'datadog-layer',
|
|
138
|
+
getExtensionLayerArn(
|
|
139
|
+
this.region,
|
|
140
|
+
datadog.props.extensionLayerVersion as number,
|
|
141
|
+
defaultWorkerConfig.architecture === aws_lambda.Architecture.ARM_64,
|
|
142
|
+
),
|
|
143
|
+
),
|
|
144
|
+
],
|
|
99
145
|
});
|
|
100
146
|
|
|
101
147
|
const workerDeployment = new LambdaDeployment(this, 'workerDeployment', {
|
|
@@ -16,6 +16,7 @@ interface Config {
|
|
|
16
16
|
VERSION: string;
|
|
17
17
|
};
|
|
18
18
|
};
|
|
19
|
+
datadogApiKeySecretArn: string;
|
|
19
20
|
sourceSnsTopicArn: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -30,6 +31,7 @@ const configs: Record<Environment, Config> = {
|
|
|
30
31
|
VERSION: Env.string('VERSION', { default: 'local' }),
|
|
31
32
|
},
|
|
32
33
|
},
|
|
34
|
+
datadogApiKeySecretArn: 'TODO: datadogApiKeySecretArn',
|
|
33
35
|
sourceSnsTopicArn: 'TODO: sourceSnsTopicArn',
|
|
34
36
|
},
|
|
35
37
|
prod: {
|
|
@@ -42,6 +44,7 @@ const configs: Record<Environment, Config> = {
|
|
|
42
44
|
VERSION: Env.string('VERSION', { default: 'local' }),
|
|
43
45
|
},
|
|
44
46
|
},
|
|
47
|
+
datadogApiKeySecretArn: 'TODO: datadogApiKeySecretArn',
|
|
45
48
|
sourceSnsTopicArn: 'TODO: sourceSnsTopicArn',
|
|
46
49
|
},
|
|
47
50
|
};
|
|
@@ -16,19 +16,26 @@
|
|
|
16
16
|
"@aws-sdk/client-codedeploy": "^3.363.0",
|
|
17
17
|
"@aws-sdk/client-lambda": "^3.363.0",
|
|
18
18
|
"@aws-sdk/client-sns": "^3.363.0",
|
|
19
|
-
"@seek/logger": "^
|
|
19
|
+
"@seek/logger": "^9.0.0",
|
|
20
|
+
"datadog-lambda-js": "^8.0.0",
|
|
21
|
+
"dd-trace": "^5.0.0",
|
|
20
22
|
"skuba-dive": "^2.0.0",
|
|
21
23
|
"zod": "^3.19.1"
|
|
22
24
|
},
|
|
23
25
|
"devDependencies": {
|
|
24
26
|
"@seek/aws-codedeploy-infra": "^2.1.0",
|
|
25
27
|
"@types/aws-lambda": "^8.10.82",
|
|
28
|
+
"@types/chance": "^1.1.3",
|
|
26
29
|
"@types/node": "^20.16.5",
|
|
27
30
|
"aws-cdk": "^2.109.0",
|
|
28
31
|
"aws-cdk-lib": "^2.109.0",
|
|
32
|
+
"aws-sdk-client-mock": "^4.0.0",
|
|
33
|
+
"aws-sdk-client-mock-jest": "^4.0.0",
|
|
34
|
+
"chance": "^1.1.8",
|
|
29
35
|
"constructs": "^10.0.17",
|
|
36
|
+
"datadog-cdk-constructs-v2": "^1.13.0",
|
|
30
37
|
"pino-pretty": "^11.0.0",
|
|
31
|
-
"skuba": "9.0.
|
|
38
|
+
"skuba": "9.0.1-upgrade-cdk-template-20241002233314"
|
|
32
39
|
},
|
|
33
40
|
"packageManager": "pnpm@9.11.0",
|
|
34
41
|
"engines": {
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { PublishCommand } from '@aws-sdk/client-sns';
|
|
2
|
+
|
|
3
|
+
import { metricsClient } from 'src/framework/metrics';
|
|
4
|
+
import { createCtx, createSqsEvent } from 'src/testing/handler';
|
|
5
|
+
import { logger } from 'src/testing/logging';
|
|
6
|
+
import { scoringService, sns } from 'src/testing/services';
|
|
7
|
+
import { chance, mockJobPublishedEvent } from 'src/testing/types';
|
|
8
|
+
|
|
9
|
+
import * as app from './app';
|
|
10
|
+
|
|
11
|
+
describe('app', () => {
|
|
12
|
+
it('exports a handler', () => expect(app).toHaveProperty('handler'));
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('handler', () => {
|
|
16
|
+
const ctx = createCtx();
|
|
17
|
+
|
|
18
|
+
const jobPublished = mockJobPublishedEvent({ entityId: chance.name() });
|
|
19
|
+
|
|
20
|
+
const score = chance.floating({ max: 1, min: 0 });
|
|
21
|
+
|
|
22
|
+
const distribution = jest
|
|
23
|
+
.spyOn(metricsClient, 'distribution')
|
|
24
|
+
.mockReturnValue();
|
|
25
|
+
|
|
26
|
+
beforeAll(logger.spy);
|
|
27
|
+
beforeAll(scoringService.spy);
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
scoringService.request.mockResolvedValue(score);
|
|
31
|
+
sns.publish.resolves({ MessageId: chance.guid({ version: 4 }) });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
logger.clear();
|
|
36
|
+
distribution.mockClear();
|
|
37
|
+
scoringService.clear();
|
|
38
|
+
sns.clear();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('handles one record', async () => {
|
|
42
|
+
const event = createSqsEvent([JSON.stringify(jobPublished)]);
|
|
43
|
+
|
|
44
|
+
await expect(app.handler(event, ctx)).resolves.toBeUndefined();
|
|
45
|
+
|
|
46
|
+
expect(scoringService.request).toHaveBeenCalledTimes(1);
|
|
47
|
+
|
|
48
|
+
expect(logger.error).not.toHaveBeenCalled();
|
|
49
|
+
|
|
50
|
+
expect(logger.debug.mock.calls).toEqual([
|
|
51
|
+
[{ count: 1 }, 'Received jobs'],
|
|
52
|
+
[{ snsMessageId: expect.any(String) }, 'Scored job'],
|
|
53
|
+
['Function succeeded'],
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
expect(distribution.mock.calls).toEqual([
|
|
57
|
+
['job.received', 1],
|
|
58
|
+
['job.scored', 1],
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
expect(sns.client).toReceiveCommandTimes(PublishCommand, 1);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('throws on invalid input', () => {
|
|
65
|
+
const event = createSqsEvent(['}']);
|
|
66
|
+
|
|
67
|
+
return expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('bubbles up scoring service error', async () => {
|
|
71
|
+
const err = Error(chance.sentence());
|
|
72
|
+
|
|
73
|
+
scoringService.request.mockRejectedValue(err);
|
|
74
|
+
|
|
75
|
+
const event = createSqsEvent([JSON.stringify(jobPublished)]);
|
|
76
|
+
|
|
77
|
+
await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
|
|
78
|
+
|
|
79
|
+
expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('bubbles up SNS error', async () => {
|
|
83
|
+
const err = Error(chance.sentence());
|
|
84
|
+
|
|
85
|
+
sns.publish.rejects(err);
|
|
86
|
+
|
|
87
|
+
const event = createSqsEvent([JSON.stringify(jobPublished)]);
|
|
88
|
+
|
|
89
|
+
await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
|
|
90
|
+
|
|
91
|
+
expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('throws on zero records', async () => {
|
|
95
|
+
const err = new Error('Received 0 records');
|
|
96
|
+
|
|
97
|
+
const event = createSqsEvent([]);
|
|
98
|
+
|
|
99
|
+
await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
|
|
100
|
+
|
|
101
|
+
expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('throws on multiple records', async () => {
|
|
105
|
+
const err = new Error('Received 2 records');
|
|
106
|
+
|
|
107
|
+
const event = createSqsEvent([
|
|
108
|
+
JSON.stringify(jobPublished),
|
|
109
|
+
JSON.stringify(jobPublished),
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
await expect(app.handler(event, ctx)).rejects.toThrow('Function failed');
|
|
113
|
+
|
|
114
|
+
expect(logger.error).toHaveBeenCalledWith({ err }, 'Function failed');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -1,35 +1,57 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { SQSEvent, SQSHandler } from 'aws-lambda';
|
|
1
|
+
import 'skuba-dive/register';
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import type { SQSEvent } from 'aws-lambda';
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
name: config.name,
|
|
15
|
-
|
|
16
|
-
transport:
|
|
17
|
-
config.environment === 'local' ? { target: 'pino-pretty' } : undefined,
|
|
18
|
-
});
|
|
5
|
+
import { createHandler } from 'src/framework/handler';
|
|
6
|
+
import { logger } from 'src/framework/logging';
|
|
7
|
+
import { metricsClient } from 'src/framework/metrics';
|
|
8
|
+
import { validateJson } from 'src/framework/validation';
|
|
9
|
+
import { scoreJobPublishedEvent, scoringService } from 'src/services/jobScorer';
|
|
10
|
+
import { sendPipelineEvent } from 'src/services/pipelineEventSender';
|
|
11
|
+
import { JobPublishedEventSchema } from 'src/types/pipelineEvents';
|
|
19
12
|
|
|
20
13
|
/**
|
|
21
14
|
* Tests connectivity to ensure appropriate access and network configuration.
|
|
22
15
|
*/
|
|
23
|
-
const smokeTest = async () =>
|
|
16
|
+
const smokeTest = async () => {
|
|
17
|
+
await Promise.all([scoringService.smokeTest(), sendPipelineEvent({}, true)]);
|
|
18
|
+
};
|
|
24
19
|
|
|
25
|
-
export const handler
|
|
20
|
+
export const handler = createHandler<SQSEvent>(async (event) => {
|
|
26
21
|
// Treat an empty object as our smoke test event.
|
|
27
22
|
if (!Object.keys(event).length) {
|
|
28
23
|
logger.debug('Received smoke test request');
|
|
29
24
|
return smokeTest();
|
|
30
25
|
}
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
const count = event.Records.length;
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
};
|
|
29
|
+
if (count !== 1) {
|
|
30
|
+
throw Error(`Received ${count} records`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
logger.debug({ count }, 'Received jobs');
|
|
34
|
+
|
|
35
|
+
metricsClient.distribution('job.received', event.Records.length);
|
|
36
|
+
|
|
37
|
+
const record = event.Records[0];
|
|
38
|
+
if (!record) {
|
|
39
|
+
throw new Error('Malformed SQS event with no records');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { body } = record;
|
|
43
|
+
|
|
44
|
+
// TODO: this throws an error, which will cause the Lambda function to retry
|
|
45
|
+
// the event and eventually send it to your dead-letter queue. If you don't
|
|
46
|
+
// trust your source to provide consistently well-formed input, consider
|
|
47
|
+
// catching and handling this error in code.
|
|
48
|
+
const publishedJob = validateJson(body, JobPublishedEventSchema);
|
|
49
|
+
|
|
50
|
+
const scoredJob = await scoreJobPublishedEvent(publishedJob);
|
|
51
|
+
|
|
52
|
+
const snsMessageId = await sendPipelineEvent(scoredJob);
|
|
53
|
+
|
|
54
|
+
logger.debug({ snsMessageId }, 'Scored job');
|
|
55
|
+
|
|
56
|
+
metricsClient.distribution('job.scored', 1);
|
|
57
|
+
});
|