cdk-nuxt 2.0.0 → 2.1.0
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/README.md +45 -19
- package/index.d.ts +2 -1
- package/index.js +3 -3
- package/lib/cli/deploy-server.js +33 -5
- package/lib/cli/destroy-server.js +13 -0
- package/lib/functions/access-logs-analysis/group-by-date/.gitignore +2 -0
- package/lib/functions/access-logs-analysis/group-by-date/build/app/index.d.ts +2 -0
- package/lib/functions/access-logs-analysis/group-by-date/build/app/index.js +57 -0
- package/lib/functions/access-logs-analysis/group-by-date/build/app/index.js.map +1 -0
- package/lib/functions/access-logs-analysis/group-by-date/index.d.ts +2 -0
- package/lib/functions/access-logs-analysis/group-by-date/index.js +57 -0
- package/lib/functions/access-logs-analysis/group-by-date/index.ts +64 -0
- package/lib/functions/access-logs-analysis/group-by-date/package.json +20 -0
- package/lib/functions/access-logs-analysis/group-by-date/tsconfig.json +33 -0
- package/lib/functions/access-logs-analysis/group-by-date/yarn.lock +1203 -0
- package/lib/functions/access-logs-analysis/partitioning/.gitignore +2 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/create-partition.d.ts +1 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/create-partition.js +57 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/create-partition.js.map +1 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/transform-partition.d.ts +2 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/transform-partition.js +72 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/transform-partition.js.map +1 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/types.d.ts +7 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/types.js +3 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/types.js.map +1 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/util.d.ts +9 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/util.js +44 -0
- package/lib/functions/access-logs-analysis/partitioning/build/app/util.js.map +1 -0
- package/lib/functions/access-logs-analysis/partitioning/create-partition.d.ts +1 -0
- package/lib/functions/access-logs-analysis/partitioning/create-partition.js +57 -0
- package/lib/functions/access-logs-analysis/partitioning/create-partition.ts +58 -0
- package/lib/functions/access-logs-analysis/partitioning/package.json +20 -0
- package/lib/functions/access-logs-analysis/partitioning/transform-partition.d.ts +2 -0
- package/lib/functions/access-logs-analysis/partitioning/transform-partition.js +72 -0
- package/lib/functions/access-logs-analysis/partitioning/transform-partition.ts +83 -0
- package/lib/functions/access-logs-analysis/partitioning/tsconfig.json +33 -0
- package/lib/functions/access-logs-analysis/partitioning/types.d.ts +7 -0
- package/lib/functions/access-logs-analysis/partitioning/types.js +3 -0
- package/lib/functions/access-logs-analysis/partitioning/types.ts +8 -0
- package/lib/functions/access-logs-analysis/partitioning/util.d.ts +9 -0
- package/lib/functions/access-logs-analysis/partitioning/util.js +44 -0
- package/lib/functions/access-logs-analysis/partitioning/util.ts +52 -0
- package/lib/functions/access-logs-analysis/partitioning/yarn.lock +951 -0
- package/lib/functions/assets-cleanup/build/app/index.js +1 -1
- package/lib/functions/assets-cleanup/build/app/index.js.map +1 -1
- package/lib/functions/assets-cleanup/index.js +2 -2
- package/lib/functions/assets-cleanup/index.ts +2 -2
- package/lib/stack/NuxtAppStackProps.js +3 -0
- package/lib/stack/{nuxt-app-static-assets.d.ts → NuxtAppStaticAssets.d.ts} +9 -4
- package/lib/stack/NuxtAppStaticAssets.js +71 -0
- package/lib/stack/{nuxt-app-static-assets.ts → NuxtAppStaticAssets.ts} +31 -24
- package/lib/stack/access-logs-analysis/AccessLogsAnalysis.d.ts +66 -0
- package/lib/stack/access-logs-analysis/AccessLogsAnalysis.js +270 -0
- package/lib/stack/access-logs-analysis/AccessLogsAnalysis.ts +330 -0
- package/lib/stack/access-logs-analysis/AccessLogsAnalysisProps.d.ts +21 -0
- package/lib/stack/access-logs-analysis/AccessLogsAnalysisProps.js +3 -0
- package/lib/stack/access-logs-analysis/AccessLogsAnalysisProps.ts +26 -0
- package/lib/stack/access-logs-analysis/AccessLogsParquetTable.d.ts +9 -0
- package/lib/stack/access-logs-analysis/AccessLogsParquetTable.js +25 -0
- package/lib/stack/access-logs-analysis/AccessLogsParquetTable.ts +23 -0
- package/lib/stack/access-logs-analysis/AccessLogsTableConfig.d.ts +8 -0
- package/lib/stack/access-logs-analysis/AccessLogsTableConfig.js +167 -0
- package/lib/stack/access-logs-analysis/AccessLogsTableConfig.ts +164 -0
- package/lib/stack/access-logs-analysis/AccessLogsTableProps.d.ts +7 -0
- package/lib/stack/access-logs-analysis/AccessLogsTableProps.js +3 -0
- package/lib/stack/access-logs-analysis/AccessLogsTableProps.ts +8 -0
- package/lib/stack/access-logs-analysis/CloudFrontAccessLogsAnalysis.d.ts +36 -0
- package/lib/stack/access-logs-analysis/CloudFrontAccessLogsAnalysis.js +60 -0
- package/lib/stack/access-logs-analysis/CloudFrontAccessLogsAnalysis.ts +73 -0
- package/lib/stack/access-logs-analysis/CloudFrontAccessLogsByDateTable.d.ts +9 -0
- package/lib/stack/access-logs-analysis/CloudFrontAccessLogsByDateTable.js +37 -0
- package/lib/stack/access-logs-analysis/CloudFrontAccessLogsByDateTable.ts +44 -0
- package/lib/stack/server/{nuxt-server-app-stack.d.ts → NuxtServerAppStack.d.ts} +13 -75
- package/lib/stack/server/NuxtServerAppStack.js +483 -0
- package/lib/stack/server/{nuxt-server-app-stack.ts → NuxtServerAppStack.ts} +53 -91
- package/lib/stack/server/NuxtServerAppStackProps.d.ts +80 -0
- package/lib/stack/server/NuxtServerAppStackProps.js +3 -0
- package/lib/stack/server/NuxtServerAppStackProps.ts +94 -0
- package/lib/templates/stack-index-server.ts +108 -15
- package/package.json +3 -1
- package/lib/stack/nuxt-app-stack-props.js +0 -3
- package/lib/stack/nuxt-app-static-assets.js +0 -72
- package/lib/stack/server/nuxt-server-app-stack.js +0 -447
- /package/lib/stack/{nuxt-app-stack-props.d.ts → NuxtAppStackProps.d.ts} +0 -0
- /package/lib/stack/{nuxt-app-stack-props.ts → NuxtAppStackProps.ts} +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AccessLogsAnalysis = void 0;
|
|
4
|
+
const aws_s3_1 = require("aws-cdk-lib/aws-s3");
|
|
5
|
+
const constructs_1 = require("constructs");
|
|
6
|
+
const aws_athena_1 = require("aws-cdk-lib/aws-athena");
|
|
7
|
+
const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
|
|
8
|
+
const aws_events_1 = require("aws-cdk-lib/aws-events");
|
|
9
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
10
|
+
const aws_glue_alpha_1 = require("@aws-cdk/aws-glue-alpha");
|
|
11
|
+
const aws_lambda_event_sources_1 = require("aws-cdk-lib/aws-lambda-event-sources");
|
|
12
|
+
const aws_logs_1 = require("aws-cdk-lib/aws-logs");
|
|
13
|
+
const aws_iam_1 = require("aws-cdk-lib/aws-iam");
|
|
14
|
+
const aws_events_targets_1 = require("aws-cdk-lib/aws-events-targets");
|
|
15
|
+
const path = require("path");
|
|
16
|
+
/**
|
|
17
|
+
* Provides the AWS resources to analyze access logs. This construct is derived from the official AWS sample
|
|
18
|
+
* CloudFormation stack:
|
|
19
|
+
* {@link https://github.com/aws-samples/amazon-cloudfront-access-logs-queries/blob/mainline/template.yaml}
|
|
20
|
+
*/
|
|
21
|
+
class AccessLogsAnalysis extends constructs_1.Construct {
|
|
22
|
+
constructor(scope, id, props) {
|
|
23
|
+
super(scope, id);
|
|
24
|
+
this.resourceIdPrefix = props.resourcePrefix;
|
|
25
|
+
this.bucket = props.bucket;
|
|
26
|
+
this.setupLifecycleRules(props);
|
|
27
|
+
this.workgroup = this.createWorkgroup();
|
|
28
|
+
this.database = this.createGlueDatabase();
|
|
29
|
+
this.accessLogsByDateTable = this.createAccessLogsByDateTable();
|
|
30
|
+
this.accessLogsParquetTable = this.createAccessLogsParquetTable();
|
|
31
|
+
this.groupByDateLayer = this.createGroupByDateLayer();
|
|
32
|
+
this.groupByDateLambda = this.createGroupByDateLambda();
|
|
33
|
+
this.partitioningLayer = this.createPartitioningLayer();
|
|
34
|
+
this.createPartitionLambda = this.createCreatePartitionLambda();
|
|
35
|
+
this.transformPartitionLambda = this.createTransformPartitionLambda();
|
|
36
|
+
this.createPartitionsScheduler = this.createCreatePartitionsScheduler();
|
|
37
|
+
this.transformPartitionsScheduler = this.createTransformPartitionsScheduler(props);
|
|
38
|
+
}
|
|
39
|
+
setupLifecycleRules(props) {
|
|
40
|
+
// cleanup raw and partitioned access logs after a configurable time range
|
|
41
|
+
var _a, _b, _c;
|
|
42
|
+
this.bucket.addLifecycleRule({
|
|
43
|
+
id: `${this.resourceIdPrefix}-cleanup-unprocessed`,
|
|
44
|
+
prefix: `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_UNPROCESSED}/*`,
|
|
45
|
+
expiration: (_a = props.expireRawLogsAfter) !== null && _a !== void 0 ? _a : aws_cdk_lib_1.Duration.days(7),
|
|
46
|
+
});
|
|
47
|
+
this.bucket.addLifecycleRule({
|
|
48
|
+
id: `${this.resourceIdPrefix}-cleanup-grouped`,
|
|
49
|
+
prefix: `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_GROUPED_BY_DATE}/*`,
|
|
50
|
+
expiration: (_b = props.expireIntermediateLogsAfter) !== null && _b !== void 0 ? _b : aws_cdk_lib_1.Duration.days(7),
|
|
51
|
+
});
|
|
52
|
+
this.bucket.addLifecycleRule({
|
|
53
|
+
id: `${this.resourceIdPrefix}-cleanup-transformed`,
|
|
54
|
+
prefix: `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_TRANSFORMED}/*`,
|
|
55
|
+
expiration: (_c = props.expireTransformedLogsAfter) !== null && _c !== void 0 ? _c : aws_cdk_lib_1.Duration.days(180),
|
|
56
|
+
});
|
|
57
|
+
/* CHECKME: delete query results after 1 week
|
|
58
|
+
this.bucket.addLifecycleRule({
|
|
59
|
+
id: `${this.resourceIdPrefix}-cleanup-query-results`,
|
|
60
|
+
prefix: `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_ATHENA_RESULTS}/*`,
|
|
61
|
+
expiration: Duration.days(7),
|
|
62
|
+
});
|
|
63
|
+
*/
|
|
64
|
+
}
|
|
65
|
+
createWorkgroup() {
|
|
66
|
+
const workgroupName = `${this.resourceIdPrefix}-workgroup`;
|
|
67
|
+
// due to a current bug, the tags are not automatically assigned to the workgroup
|
|
68
|
+
// note, that the keys must be converted to lower case
|
|
69
|
+
const tags = aws_cdk_lib_1.Stack.of(this)
|
|
70
|
+
.tags.renderTags()
|
|
71
|
+
.map((tag) => ({ key: tag.Key, value: tag.Value }));
|
|
72
|
+
return new aws_athena_1.CfnWorkGroup(this, workgroupName, {
|
|
73
|
+
name: workgroupName,
|
|
74
|
+
workGroupConfiguration: {
|
|
75
|
+
publishCloudWatchMetricsEnabled: false,
|
|
76
|
+
resultConfiguration: {
|
|
77
|
+
outputLocation: `s3://${this.bucket.bucketName}/${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_ATHENA_RESULTS}`,
|
|
78
|
+
},
|
|
79
|
+
enforceWorkGroupConfiguration: true
|
|
80
|
+
},
|
|
81
|
+
tags,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
createGlueDatabase() {
|
|
85
|
+
const databaseName = `${this.resourceIdPrefix}-database`;
|
|
86
|
+
return new aws_glue_alpha_1.Database(this, databaseName, {
|
|
87
|
+
// Athena doesn't support dashes in database/table names
|
|
88
|
+
databaseName: databaseName.replace(/-/g, '_'),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
createGroupByDateLayer() {
|
|
92
|
+
const layerVersionName = `${this.resourceIdPrefix}-group-by-date-dependencies`;
|
|
93
|
+
return new aws_lambda_1.LayerVersion(this, layerVersionName, {
|
|
94
|
+
layerVersionName,
|
|
95
|
+
compatibleArchitectures: [aws_lambda_1.Architecture.ARM_64, aws_lambda_1.Architecture.X86_64],
|
|
96
|
+
compatibleRuntimes: [aws_lambda_1.Runtime.NODEJS_14_X, aws_lambda_1.Runtime.NODEJS_16_X],
|
|
97
|
+
code: aws_lambda_1.Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/group-by-date/build/layer')),
|
|
98
|
+
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
createPartitioningLayer() {
|
|
102
|
+
const layerVersionName = `${this.resourceIdPrefix}-partitioning-dependencies`;
|
|
103
|
+
return new aws_lambda_1.LayerVersion(this, layerVersionName, {
|
|
104
|
+
layerVersionName,
|
|
105
|
+
compatibleArchitectures: [aws_lambda_1.Architecture.ARM_64, aws_lambda_1.Architecture.X86_64],
|
|
106
|
+
compatibleRuntimes: [aws_lambda_1.Runtime.NODEJS_14_X, aws_lambda_1.Runtime.NODEJS_16_X],
|
|
107
|
+
code: aws_lambda_1.Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/partitioning/build/layer')),
|
|
108
|
+
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A Lambda function to be triggered whenever a new access log is created.
|
|
113
|
+
* It moves the raw access logs into a sub folder hierarchy by year, month, day and hour.
|
|
114
|
+
*/
|
|
115
|
+
createGroupByDateLambda() {
|
|
116
|
+
const functionName = `${this.resourceIdPrefix}-group-by-date`;
|
|
117
|
+
const lambda = new aws_lambda_1.Function(this, functionName, {
|
|
118
|
+
functionName,
|
|
119
|
+
architecture: aws_lambda_1.Architecture.ARM_64,
|
|
120
|
+
runtime: aws_lambda_1.Runtime.NODEJS_16_X,
|
|
121
|
+
code: aws_lambda_1.Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/group-by-date/build/app')),
|
|
122
|
+
memorySize: 512,
|
|
123
|
+
timeout: aws_cdk_lib_1.Duration.seconds(20),
|
|
124
|
+
handler: 'index.handler',
|
|
125
|
+
layers: [this.groupByDateLayer],
|
|
126
|
+
events: [
|
|
127
|
+
new aws_lambda_event_sources_1.S3EventSource(this.bucket, {
|
|
128
|
+
events: [aws_s3_1.EventType.OBJECT_CREATED],
|
|
129
|
+
filters: [this.getUnprocessedObjectsFilter()],
|
|
130
|
+
}),
|
|
131
|
+
],
|
|
132
|
+
logRetention: aws_logs_1.RetentionDays.TWO_WEEKS,
|
|
133
|
+
environment: {
|
|
134
|
+
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
|
|
135
|
+
NODE_OPTIONS: '--enable-source-maps',
|
|
136
|
+
TARGET_FOLDER: AccessLogsAnalysis.ACCESS_LOGS_FOLDER_GROUPED_BY_DATE,
|
|
137
|
+
RAW_ACCESS_LOG_FILE_PATTERN: this.getRawAccessLogFilePattern().source,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
this.bucket.grantReadWrite(lambda);
|
|
141
|
+
this.bucket.grantDelete(lambda, `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_UNPROCESSED}/*`);
|
|
142
|
+
return lambda;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Creates a new partition for the upcoming hour in the access log database.
|
|
146
|
+
*/
|
|
147
|
+
createCreatePartitionLambda() {
|
|
148
|
+
const functionName = `${this.resourceIdPrefix}-create-part`;
|
|
149
|
+
const lambda = new aws_lambda_1.Function(this, functionName, {
|
|
150
|
+
functionName,
|
|
151
|
+
architecture: aws_lambda_1.Architecture.ARM_64,
|
|
152
|
+
runtime: aws_lambda_1.Runtime.NODEJS_16_X,
|
|
153
|
+
code: aws_lambda_1.Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/partitioning/build/app'), {
|
|
154
|
+
exclude: ['transform-partition*', '*.d.ts'],
|
|
155
|
+
}),
|
|
156
|
+
memorySize: 128,
|
|
157
|
+
timeout: aws_cdk_lib_1.Duration.seconds(20),
|
|
158
|
+
handler: 'create-partition.handler',
|
|
159
|
+
layers: [this.partitioningLayer],
|
|
160
|
+
logRetention: aws_logs_1.RetentionDays.TWO_WEEKS,
|
|
161
|
+
environment: {
|
|
162
|
+
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
|
|
163
|
+
NODE_OPTIONS: '--enable-source-maps',
|
|
164
|
+
WORKGROUP: this.workgroup.name,
|
|
165
|
+
DATABASE: this.database.databaseName,
|
|
166
|
+
TABLE: this.accessLogsByDateTable.tableName,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
// grant access to S3 bucket and to execute Athena queries which create new partitions
|
|
170
|
+
this.bucket.grantReadWrite(lambda);
|
|
171
|
+
lambda.addToRolePolicy(new aws_iam_1.PolicyStatement({
|
|
172
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
173
|
+
actions: [
|
|
174
|
+
'athena:StartQueryExecution',
|
|
175
|
+
'athena:GetQueryExecution',
|
|
176
|
+
'glue:CreateDatabase',
|
|
177
|
+
'glue:CreatePartition',
|
|
178
|
+
'glue:GetDatabase',
|
|
179
|
+
'glue:GetTable',
|
|
180
|
+
'glue:BatchCreatePartition',
|
|
181
|
+
],
|
|
182
|
+
resources: ['*'],
|
|
183
|
+
}));
|
|
184
|
+
return lambda;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Transforms partitions from the Hive format to Parquet.
|
|
188
|
+
*/
|
|
189
|
+
createTransformPartitionLambda() {
|
|
190
|
+
const functionName = `${this.resourceIdPrefix}-transform-part`;
|
|
191
|
+
const lambda = new aws_lambda_1.Function(this, functionName, {
|
|
192
|
+
functionName,
|
|
193
|
+
architecture: aws_lambda_1.Architecture.ARM_64,
|
|
194
|
+
runtime: aws_lambda_1.Runtime.NODEJS_16_X,
|
|
195
|
+
code: aws_lambda_1.Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/partitioning/build/app'), {
|
|
196
|
+
exclude: ['create-partition*', '*.d.ts'],
|
|
197
|
+
}),
|
|
198
|
+
memorySize: 128,
|
|
199
|
+
timeout: aws_cdk_lib_1.Duration.seconds(20),
|
|
200
|
+
handler: 'transform-partition.handler',
|
|
201
|
+
layers: [this.partitioningLayer],
|
|
202
|
+
logRetention: aws_logs_1.RetentionDays.TWO_WEEKS,
|
|
203
|
+
environment: {
|
|
204
|
+
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
|
|
205
|
+
NODE_OPTIONS: '--enable-source-maps',
|
|
206
|
+
WORKGROUP: this.workgroup.name,
|
|
207
|
+
DATABASE: this.database.databaseName,
|
|
208
|
+
SOURCE_TABLE: this.accessLogsByDateTable.tableName,
|
|
209
|
+
TARGET_TABLE: this.accessLogsParquetTable.tableName,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
// grant access to S3 bucket and to execute Athena queries which create new partitions
|
|
213
|
+
this.bucket.grantReadWrite(lambda);
|
|
214
|
+
lambda.addToRolePolicy(new aws_iam_1.PolicyStatement({
|
|
215
|
+
effect: aws_iam_1.Effect.ALLOW,
|
|
216
|
+
actions: [
|
|
217
|
+
'athena:StartQueryExecution',
|
|
218
|
+
'athena:GetQueryExecution',
|
|
219
|
+
'glue:CreateDatabase',
|
|
220
|
+
'glue:CreatePartition',
|
|
221
|
+
'glue:GetDatabase',
|
|
222
|
+
'glue:GetTable',
|
|
223
|
+
'glue:BatchCreatePartition',
|
|
224
|
+
'glue:GetPartition',
|
|
225
|
+
'glue:GetPartitions',
|
|
226
|
+
'glue:CreateTable',
|
|
227
|
+
'glue:DeleteTable',
|
|
228
|
+
'glue:DeletePartition',
|
|
229
|
+
],
|
|
230
|
+
resources: ['*'],
|
|
231
|
+
}));
|
|
232
|
+
return lambda;
|
|
233
|
+
}
|
|
234
|
+
createCreatePartitionsScheduler() {
|
|
235
|
+
const ruleName = `${this.resourceIdPrefix}-create-part-sched`;
|
|
236
|
+
return new aws_events_1.Rule(this, ruleName, {
|
|
237
|
+
ruleName,
|
|
238
|
+
schedule: aws_events_1.Schedule.cron({ minute: '55' }),
|
|
239
|
+
targets: [new aws_events_targets_1.LambdaFunction(this.createPartitionLambda, { retryAttempts: 10 })],
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
createTransformPartitionsScheduler(props) {
|
|
243
|
+
const ruleName = `${this.resourceIdPrefix}-transform-part-sched`;
|
|
244
|
+
return new aws_events_1.Rule(this, ruleName, {
|
|
245
|
+
ruleName,
|
|
246
|
+
schedule: aws_events_1.Schedule.cron({ minute: '1' }),
|
|
247
|
+
targets: [
|
|
248
|
+
new aws_events_targets_1.LambdaFunction(this.transformPartitionLambda, {
|
|
249
|
+
event: this.getTransformPartitionInvocationProps(props),
|
|
250
|
+
retryAttempts: 10,
|
|
251
|
+
}),
|
|
252
|
+
],
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
getTransformPartitionInvocationProps(props) {
|
|
256
|
+
return aws_events_1.RuleTargetInput.fromObject({
|
|
257
|
+
columnNames: [
|
|
258
|
+
...this.accessLogsParquetTable.columns,
|
|
259
|
+
...this.accessLogsParquetTable.partitionKeys,
|
|
260
|
+
].map(column => column.name),
|
|
261
|
+
columnTransformations: this.getColumnTransformationRules(props),
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
exports.AccessLogsAnalysis = AccessLogsAnalysis;
|
|
266
|
+
AccessLogsAnalysis.ACCESS_LOGS_FOLDER_UNPROCESSED = 'unprocessed';
|
|
267
|
+
AccessLogsAnalysis.ACCESS_LOGS_FOLDER_GROUPED_BY_DATE = 'by-date';
|
|
268
|
+
AccessLogsAnalysis.ACCESS_LOGS_FOLDER_TRANSFORMED = 'transformed';
|
|
269
|
+
AccessLogsAnalysis.ACCESS_LOGS_FOLDER_ATHENA_RESULTS = 'athena-query-results';
|
|
270
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWNjZXNzTG9nc0FuYWx5c2lzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiQWNjZXNzTG9nc0FuYWx5c2lzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLCtDQUE0RTtBQUM1RSwyQ0FBcUM7QUFDckMsdURBQW9EO0FBR3BELHVEQUEyRjtBQUMzRix1REFBdUU7QUFDdkUsNkNBQW1FO0FBQ25FLDREQUF5RDtBQUN6RCxtRkFBbUU7QUFDbkUsbURBQW1EO0FBQ25ELGlEQUE0RDtBQUM1RCx1RUFBOEQ7QUFDOUQsNkJBQTZCO0FBSTdCOzs7O0dBSUc7QUFDSCxNQUFzQixrQkFBbUIsU0FBUSxzQkFBUztJQXFCdEQsWUFBc0IsS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBOEI7UUFDOUUsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNqQixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsS0FBSyxDQUFDLGNBQWMsQ0FBQztRQUM3QyxJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFDM0IsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ3hDLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7UUFDMUMsSUFBSSxDQUFDLHFCQUFxQixHQUFHLElBQUksQ0FBQywyQkFBMkIsRUFBRSxDQUFDO1FBQ2hFLElBQUksQ0FBQyxzQkFBc0IsR0FBRyxJQUFJLENBQUMsNEJBQTRCLEVBQUUsQ0FBQztRQUNsRSxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLHNCQUFzQixFQUFFLENBQUM7UUFDdEQsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1FBQ3hELElBQUksQ0FBQyxpQkFBaUIsR0FBRyxJQUFJLENBQUMs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QyxJQUFJLENBQUMsd0JBQXdCLEVBQUU7b0JBQzlDLEtBQUssRUFBRSxJQUFJLENBQUMsb0NBQW9DLENBQUMsS0FBSyxDQUFDO29CQUN2RCxhQUFhLEVBQUUsRUFBRTtpQkFDcEIsQ0FBQzthQUNMO1NBQ0osQ0FBQyxDQUFDO0lBQ1AsQ0FBQztJQUVPLG9DQUFvQyxDQUFDLEtBQThCO1FBQ3ZFLE9BQU8sNEJBQWUsQ0FBQyxVQUFVLENBQUM7WUFDOUIsV0FBVyxFQUFFO2dCQUNULEdBQUcsSUFBSSxDQUFDLHNCQUFzQixDQUFDLE9BQU87Z0JBQ3RDLEdBQUksSUFBSSxDQUFDLHNCQUFzQixDQUFDLGFBQTBCO2FBQzdELENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztZQUM1QixxQkFBcUIsRUFBRSxJQUFJLENBQUMsNEJBQTRCLENBQUMsS0FBSyxDQUFDO1NBQ2xFLENBQUMsQ0FBQztJQUNQLENBQUM7O0FBNVNMLGdEQW1UQztBQWpUNkIsaURBQThCLEdBQUcsYUFBYSxDQUFDO0FBQy9DLHFEQUFrQyxHQUFHLFNBQVMsQ0FBQztBQUMvQyxpREFBOEIsR0FBRyxhQUFhLENBQUM7QUFDL0Msb0RBQWlDLEdBQUcsc0JBQXNCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge0J1Y2tldCwgRXZlbnRUeXBlLCBOb3RpZmljYXRpb25LZXlGaWx0ZXJ9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zMyc7XG5pbXBvcnQge0NvbnN0cnVjdH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQge0NmbldvcmtHcm91cH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWF0aGVuYSc7XG5pbXBvcnQge0Nsb3VkRnJvbnRBY2Nlc3NMb2dzQnlEYXRlVGFibGV9IGZyb20gJy4vQ2xvdWRGcm9udEFjY2Vzc0xvZ3NCeURhdGVUYWJsZSc7XG5pbXBvcnQge0FjY2Vzc0xvZ3NQYXJxdWV0VGFibGV9IGZyb20gJy4vQWNjZXNzTG9nc1BhcnF1ZXRUYWJsZSc7XG5pbXBvcnQge0FyY2hpdGVjdHVyZSwgQ29kZSwgRnVuY3Rpb24sIExheWVyVmVyc2lvbiwgUnVudGltZX0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG5pbXBvcnQge1J1bGUsIFJ1bGVUYXJnZXRJbnB1dCwgU2NoZWR1bGV9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1ldmVudHMnO1xuaW1wb3J0IHtDZm5UYWcsIER1cmF0aW9uLCBSZW1vdmFsUG9saWN5LCBTdGFja30gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHtDb2x1bW4sIERhdGFiYXNlfSBmcm9tICdAYXdzLWNkay9hd3MtZ2x1ZS1hbHBoYSc7XG5pbXBvcnQge1MzRXZlbnRTb3VyY2V9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1sYW1iZGEtZXZlbnQtc291cmNlcyc7XG5pbXBvcnQge1JldGVudGlvbkRheXN9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1sb2dzJztcbmltcG9ydCB7RWZmZWN0LCBQb2xpY3lTdGF0ZW1lbnR9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0IHtMYW1iZGFGdW5jdGlvbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWV2ZW50cy10YXJnZXRzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQge0FjY2Vzc0xvZ3NBbmFseXNpc1Byb3BzfSBmcm9tIFwiLi9BY2Nlc3NMb2dzQW5hbHlzaXNQcm9wc1wiO1xuaW1wb3J0IHtDb2x1bW5UcmFuc2Zvcm1hdGlvblJ1bGVzfSBmcm9tIFwiLi4vLi4vZnVuY3Rpb25zL2FjY2Vzcy1sb2dzLWFuYWx5c2lzL3BhcnRpdGlvbmluZy90eXBlc1wiO1xuXG4vKipcbiAqIFByb3ZpZGVzIHRoZSBBV1MgcmVzb3VyY2VzIHRvIGFuYWx5emUgYWNjZXNzIGxvZ3MuIFRoaXMgY29uc3RydWN0IGlzIGRlcml2ZWQgZnJvbSB0aGUgb2ZmaWNpYWwgQVdTIHNhbXBsZVxuICogQ2xvdWRGb3JtYXRpb24gc3RhY2s6XG4gKiB7QGxpbmsgaHR0cHM6Ly9naXRodWIuY29tL2F3cy1zYW1wbGVzL2FtYXpvbi1jbG91ZGZyb250LWFjY2Vzcy1sb2dzLXF1ZXJpZXMvYmxvYi9tYWlubGluZS90ZW1wbGF0ZS55YW1sfVxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQWNjZXNzTG9nc0FuYWx5c2lzIGV4dGVuZHMgQ29uc3RydWN0IHtcblxuICAgIHByb3RlY3RlZCBzdGF0aWMgcmVhZG9ubHkgQUNDRVNTX0xPR1NfRk9MREVSX1VOUFJPQ0VTU0VEID0gJ3VucHJvY2Vzc2VkJztcbiAgICBwcm90ZWN0ZWQgc3RhdGljIHJlYWRvbmx5IEFDQ0VTU19MT0dTX0ZPTERFUl9HUk9VUEVEX0JZX0RBVEUgPSAnYnktZGF0ZSc7XG4gICAgcHJvdGVjdGVkIHN0YXRpYyByZWFkb25seSBBQ0NFU1NfTE9HU19GT0xERVJfVFJBTlNGT1JNRUQgPSAndHJhbnNmb3JtZWQnO1xuICAgIHByb3RlY3RlZCBzdGF0aWMgcmVhZG9ubHkgQUNDRVNTX0xPR1NfRk9MREVSX0FUSEVOQV9SRVNVTFRTID0gJ2F0aGVuYS1xdWVyeS1yZXN1bHRzJztcblxuICAgIHByb3RlY3RlZCByZWFkb25seSByZXNvdXJjZUlkUHJlZml4OiBzdHJpbmc7XG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IGJ1Y2tldDogQnVja2V0O1xuICAgIHByb3RlY3RlZCByZWFkb25seSB3b3JrZ3JvdXA6IENmbldvcmtHcm91cDtcbiAgICBwcm90ZWN0ZWQgcmVhZG9ubHkgZGF0YWJhc2U6IERhdGFiYXNlO1xuICAgIHByb3RlY3RlZCByZWFkb25seSBhY2Nlc3NMb2dzQnlEYXRlVGFibGU6IENsb3VkRnJvbnRBY2Nlc3NMb2dzQnlEYXRlVGFibGU7XG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IGFjY2Vzc0xvZ3NQYXJxdWV0VGFibGU6IEFjY2Vzc0xvZ3NQYXJxdWV0VGFibGU7XG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IGdyb3VwQnlEYXRlTGF5ZXI6IExheWVyVmVyc2lvbjtcbiAgICBwcm90ZWN0ZWQgcmVhZG9ubHkgZ3JvdXBCeURhdGVMYW1iZGE6IEZ1bmN0aW9uO1xuICAgIHByb3RlY3RlZCByZWFkb25seSBwYXJ0aXRpb25pbmdMYXllcjogTGF5ZXJWZXJzaW9uO1xuICAgIHByb3RlY3RlZCByZWFkb25seSBjcmVhdGVQYXJ0aXRpb25MYW1iZGE6IEZ1bmN0aW9uO1xuICAgIHByb3RlY3RlZCByZWFkb25seSB0cmFuc2Zvcm1QYXJ0aXRpb25MYW1iZGE6IEZ1bmN0aW9uO1xuICAgIHByb3RlY3RlZCByZWFkb25seSBjcmVhdGVQYXJ0aXRpb25zU2NoZWR1bGVyOiBSdWxlO1xuICAgIHByb3RlY3RlZCByZWFkb25seSB0cmFuc2Zvcm1QYXJ0aXRpb25zU2NoZWR1bGVyOiBSdWxlO1xuXG4gICAgcHJvdGVjdGVkIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBBY2Nlc3NMb2dzQW5hbHlzaXNQcm9wcykge1xuICAgICAgICBzdXBlcihzY29wZSwgaWQpO1xuICAgICAgICB0aGlzLnJlc291cmNlSWRQcmVmaXggPSBwcm9wcy5yZXNvdXJjZVByZWZpeDtcbiAgICAgICAgdGhpcy5idWNrZXQgPSBwcm9wcy5idWNrZXQ7XG4gICAgICAgIHRoaXMuc2V0dXBMaWZlY3ljbGVSdWxlcyhwcm9wcyk7XG4gICAgICAgIHRoaXMud29ya2dyb3VwID0gdGhpcy5jcmVhdGVXb3JrZ3JvdXAoKTtcbiAgICAgICAgdGhpcy5kYXRhYmFzZSA9IHRoaXMuY3JlYXRlR2x1ZURhdGFiYXNlKCk7XG4gICAgICAgIHRoaXMuYWNjZXNzTG9nc0J5RGF0ZVRhYmxlID0gdGhpcy5jcmVhdGVBY2Nlc3NMb2dzQnlEYXRlVGFibGUoKTtcbiAgICAgICAgdGhpcy5hY2Nlc3NMb2dzUGFycXVldFRhYmxlID0gdGhpcy5jcmVhdGVBY2Nlc3NMb2dzUGFycXVldFRhYmxlKCk7XG4gICAgICAgIHRoaXMuZ3JvdXBCeURhdGVMYXllciA9IHRoaXMuY3JlYXRlR3JvdXBCeURhdGVMYXllcigpO1xuICAgICAgICB0aGlzLmdyb3VwQnlEYXRlTGFtYmRhID0gdGhpcy5jcmVhdGVHcm91cEJ5RGF0ZUxhbWJkYSgpO1xuICAgICAgICB0aGlzLnBhcnRpdGlvbmluZ0xheWVyID0gdGhpcy5jcmVhdGVQYXJ0aXRpb25pbmdMYXllcigpO1xuICAgICAgICB0aGlzLmNyZWF0ZVBhcnRpdGlvbkxhbWJkYSA9IHRoaXMuY3JlYXRlQ3JlYXRlUGFydGl0aW9uTGFtYmRhKCk7XG4gICAgICAgIHRoaXMudHJhbnNmb3JtUGFydGl0aW9uTGFtYmRhID0gdGhpcy5jcmVhdGVUcmFuc2Zvcm1QYXJ0aXRpb25MYW1iZGEoKTtcbiAgICAgICAgdGhpcy5jcmVhdGVQYXJ0aXRpb25zU2NoZWR1bGVyID0gdGhpcy5jcmVhdGVDcmVhdGVQYXJ0aXRpb25zU2NoZWR1bGVyKCk7XG4gICAgICAgIHRoaXMudHJhbnNmb3JtUGFydGl0aW9uc1NjaGVkdWxlciA9IHRoaXMuY3JlYXRlVHJhbnNmb3JtUGFydGl0aW9uc1NjaGVkdWxlcihwcm9wcyk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzZXR1cExpZmVjeWNsZVJ1bGVzKHByb3BzOiBBY2Nlc3NMb2dzQW5hbHlzaXNQcm9wcyk6IHZvaWQge1xuICAgICAgICAvLyBjbGVhbnVwIHJhdyBhbmQgcGFydGl0aW9uZWQgYWNjZXNzIGxvZ3MgYWZ0ZXIgYSBjb25maWd1cmFibGUgdGltZSByYW5nZVxuXG4gICAgICAgIHRoaXMuYnVja2V0LmFkZExpZmVjeWNsZVJ1bGUoe1xuICAgICAgICAgICAgaWQ6IGAke3RoaXMucmVzb3VyY2VJZFByZWZpeH0tY2xlYW51cC11bnByb2Nlc3NlZGAsXG4gICAgICAgICAgICBwcmVmaXg6IGAke0FjY2Vzc0xvZ3NBbmFseXNpcy5BQ0NFU1NfTE9HU19GT0xERVJfVU5QUk9DRVNTRUR9LypgLFxuICAgICAgICAgICAgZXhwaXJhdGlvbjogcHJvcHMuZXhwaXJlUmF3TG9nc0FmdGVyID8/IER1cmF0aW9uLmRheXMoNyksXG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMuYnVja2V0LmFkZExpZmVjeWNsZVJ1bGUoe1xuICAgICAgICAgICAgaWQ6IGAke3RoaXMucmVzb3VyY2VJZFByZWZpeH0tY2xlYW51cC1ncm91cGVkYCxcbiAgICAgICAgICAgIHByZWZpeDogYCR7QWNjZXNzTG9nc0FuYWx5c2lzLkFDQ0VTU19MT0dTX0ZPTERFUl9HUk9VUEVEX0JZX0RBVEV9LypgLFxuICAgICAgICAgICAgZXhwaXJhdGlvbjogcHJvcHMuZXhwaXJlSW50ZXJtZWRpYXRlTG9nc0FmdGVyID8/IER1cmF0aW9uLmRheXMoNyksXG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMuYnVja2V0LmFkZExpZmVjeWNsZVJ1bGUoe1xuICAgICAgICAgICAgaWQ6IGAke3RoaXMucmVzb3VyY2VJZFByZWZpeH0tY2xlYW51cC10cmFuc2Zvcm1lZGAsXG4gICAgICAgICAgICBwcmVmaXg6IGAke0FjY2Vzc0xvZ3NBbmFseXNpcy5BQ0NFU1NfTE9HU19GT0xERVJfVFJBTlNGT1JNRUR9LypgLFxuICAgICAgICAgICAgZXhwaXJhdGlvbjogcHJvcHMuZXhwaXJlVHJhbnNmb3JtZWRMb2dzQWZ0ZXIgPz8gRHVyYXRpb24uZGF5cygxODApLFxuICAgICAgICB9KTtcblxuICAgICAgICAvKiBDSEVDS01FOiBkZWxldGUgcXVlcnkgcmVzdWx0cyBhZnRlciAxIHdlZWtcbiAgICAgICAgdGhpcy5idWNrZXQuYWRkTGlmZWN5Y2xlUnVsZSh7XG4gICAgICAgICAgaWQ6IGAke3RoaXMucmVzb3VyY2VJZFByZWZpeH0tY2xlYW51cC1xdWVyeS1yZXN1bHRzYCxcbiAgICAgICAgICBwcmVmaXg6IGAke0FjY2Vzc0xvZ3NBbmFseXNpcy5BQ0NFU1NfTE9HU19GT0xERVJfQVRIRU5BX1JFU1VMVFN9LypgLFxuICAgICAgICAgIGV4cGlyYXRpb246IER1cmF0aW9uLmRheXMoNyksXG4gICAgICAgIH0pO1xuICAgICAgICAqL1xuICAgIH1cblxuICAgIHByaXZhdGUgY3JlYXRlV29ya2dyb3VwKCk6IENmbldvcmtHcm91cCB7XG4gICAgICAgIGNvbnN0IHdvcmtncm91cE5hbWUgPSBgJHt0aGlzLnJlc291cmNlSWRQcmVmaXh9LXdvcmtncm91cGA7XG5cbiAgICAgICAgLy8gZHVlIHRvIGEgY3VycmVudCBidWcsIHRoZSB0YWdzIGFyZSBub3QgYXV0b21hdGljYWxseSBhc3NpZ25lZCB0byB0aGUgd29ya2dyb3VwXG4gICAgICAgIC8vIG5vdGUsIHRoYXQgdGhlIGtleXMgbXVzdCBiZSBjb252ZXJ0ZWQgdG8gbG93ZXIgY2FzZVxuICAgICAgICBjb25zdCB0YWdzOiBDZm5UYWdbXSA9IFN0YWNrLm9mKHRoaXMpXG4gICAgICAgICAgICAudGFncy5yZW5kZXJUYWdzKClcbiAgICAgICAgICAgIC5tYXAoKHRhZzogeyBLZXk6IHN0cmluZzsgVmFsdWU6IHN0cmluZyB9KSA9PiAoe2tleTogdGFnLktleSwgdmFsdWU6IHRhZy5WYWx1ZX0pKTtcblxuICAgICAgICByZXR1cm4gbmV3IENmbldvcmtHcm91cCh0aGlzLCB3b3JrZ3JvdXBOYW1lLCB7XG4gICAgICAgICAgICBuYW1lOiB3b3JrZ3JvdXBOYW1lLFxuICAgICAgICAgICAgd29ya0dyb3VwQ29uZmlndXJhdGlvbjoge1xuICAgICAgICAgICAgICAgIHB1Ymxpc2hDbG91ZFdhdGNoTWV0cmljc0VuYWJsZWQ6IGZhbHNlLFxuICAgICAgICAgICAgICAgIHJlc3VsdENvbmZpZ3VyYXRpb246IHtcbiAgICAgICAgICAgICAgICAgICAgb3V0cHV0TG9jYXRpb246IGBzMzovLyR7dGhpcy5idWNrZXQuYnVja2V0TmFtZX0vJHtBY2Nlc3NMb2dzQW5hbHlzaXMuQUNDRVNTX0xPR1NfRk9MREVSX0FUSEVOQV9SRVNVTFRTfWAsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICBlbmZvcmNlV29ya0dyb3VwQ29uZmlndXJhdGlvbjogdHJ1ZVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHRhZ3MsXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgY3JlYXRlR2x1ZURhdGFiYXNlKCk6IERhdGFiYXNlIHtcbiAgICAgICAgY29uc3QgZGF0YWJhc2VOYW1lID0gYCR7dGhpcy5yZXNvdXJjZUlkUHJlZml4fS1kYXRhYmFzZWA7XG4gICAgICAgIHJldHVybiBuZXcgRGF0YWJhc2UodGhpcywgZGF0YWJhc2VOYW1lLCB7XG4gICAgICAgICAgICAvLyBBdGhlbmEgZG9lc24ndCBzdXBwb3J0IGRhc2hlcyBpbiBkYXRhYmFzZS90YWJsZSBuYW1lc1xuICAgICAgICAgICAgZGF0YWJhc2VOYW1lOiBkYXRhYmFzZU5hbWUucmVwbGFjZSgvLS9nLCAnXycpLFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgYWJzdHJhY3QgY3JlYXRlQWNjZXNzTG9nc0J5RGF0ZVRhYmxlKCk6IENsb3VkRnJvbnRBY2Nlc3NMb2dzQnlEYXRlVGFibGU7XG5cbiAgICBwcm90ZWN0ZWQgYWJzdHJhY3QgY3JlYXRlQWNjZXNzTG9nc1BhcnF1ZXRUYWJsZSgpOiBDbG91ZEZyb250QWNjZXNzTG9nc0J5RGF0ZVRhYmxlO1xuXG4gICAgcHJpdmF0ZSBjcmVhdGVHcm91cEJ5RGF0ZUxheWVyKCk6IExheWVyVmVyc2lvbiB7XG4gICAgICAgIGNvbnN0IGxheWVyVmVyc2lvbk5hbWUgPSBgJHt0aGlzLnJlc291cmNlSWRQcmVmaXh9LWdyb3VwLWJ5LWRhdGUtZGVwZW5kZW5jaWVzYDtcbiAgICAgICAgcmV0dXJuIG5ldyBMYXllclZlcnNpb24odGhpcywgbGF5ZXJWZXJzaW9uTmFtZSwge1xuICAgICAgICAgICAgbGF5ZXJWZXJzaW9uTmFtZSxcbiAgICAgICAgICAgIGNvbXBhdGlibGVBcmNoaXRlY3R1cmVzOiBbQXJjaGl0ZWN0dXJlLkFSTV82NCwgQXJjaGl0ZWN0dXJlLlg4Nl82NF0sXG4gICAgICAgICAgICBjb21wYXRpYmxlUnVudGltZXM6IFtSdW50aW1lLk5PREVKU18xNF9YLCBSdW50aW1lLk5PREVKU18xNl9YXSxcbiAgICAgICAgICAgIGNvZGU6IENvZGUuZnJvbUFzc2V0KHBhdGguam9pbihfX2Rpcm5hbWUsICcuLi8uLi9mdW5jdGlvbnMvYWNjZXNzLWxvZ3MtYW5hbHlzaXMvZ3JvdXAtYnktZGF0ZS9idWlsZC9sYXllcicpKSxcbiAgICAgICAgICAgIHJlbW92YWxQb2xpY3k6IFJlbW92YWxQb2xpY3kuREVTVFJPWSxcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBjcmVhdGVQYXJ0aXRpb25pbmdMYXllcigpOiBMYXllclZlcnNpb24ge1xuICAgICAgICBjb25zdCBsYXllclZlcnNpb25OYW1lID0gYCR7dGhpcy5yZXNvdXJjZUlkUHJlZml4fS1wYXJ0aXRpb25pbmctZGVwZW5kZW5jaWVzYDtcbiAgICAgICAgcmV0dXJuIG5ldyBMYXllclZlcnNpb24odGhpcywgbGF5ZXJWZXJzaW9uTmFtZSwge1xuICAgICAgICAgICAgbGF5ZXJWZXJzaW9uTmFtZSxcbiAgICAgICAgICAgIGNvbXBhdGlibGVBcmNoaXRlY3R1cmVzOiBbQXJjaGl0ZWN0dXJlLkFSTV82NCwgQXJjaGl0ZWN0dXJlLlg4Nl82NF0sXG4gICAgICAgICAgICBjb21wYXRpYmxlUnVudGltZXM6IFtSdW50aW1lLk5PREVKU18xNF9YLCBSdW50aW1lLk5PREVKU18xNl9YXSxcbiAgICAgICAgICAgIGNvZGU6IENvZGUuZnJvbUFzc2V0KHBhdGguam9pbihfX2Rpcm5hbWUsICcuLi8uLi9mdW5jdGlvbnMvYWNjZXNzLWxvZ3MtYW5hbHlzaXMvcGFydGl0aW9uaW5nL2J1aWxkL2xheWVyJykpLFxuICAgICAgICAgICAgcmVtb3ZhbFBvbGljeTogUmVtb3ZhbFBvbGljeS5ERVNUUk9ZLFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBIExhbWJkYSBmdW5jdGlvbiB0byBiZSB0cmlnZ2VyZWQgd2hlbmV2ZXIgYSBuZXcgYWNjZXNzIGxvZyBpcyBjcmVhdGVkLlxuICAgICAqIEl0IG1vdmVzIHRoZSByYXcgYWNjZXNzIGxvZ3MgaW50byBhIHN1YiBmb2xkZXIgaGllcmFyY2h5IGJ5IHllYXIsIG1vbnRoLCBkYXkgYW5kIGhvdXIuXG4gICAgICovXG4gICAgcHJpdmF0ZSBjcmVhdGVHcm91cEJ5RGF0ZUxhbWJkYSgpOiBGdW5jdGlvbiB7XG4gICAgICAgIGNvbnN0IGZ1bmN0aW9uTmFtZSA9IGAke3RoaXMucmVzb3VyY2VJZFByZWZpeH0tZ3JvdXAtYnktZGF0ZWA7XG5cbiAgICAgICAgY29uc3QgbGFtYmRhID0gbmV3IEZ1bmN0aW9uKHRoaXMsIGZ1bmN0aW9uTmFtZSwge1xuICAgICAgICAgICAgZnVuY3Rpb25OYW1lLFxuICAgICAgICAgICAgYXJjaGl0ZWN0dXJlOiBBcmNoaXRlY3R1cmUuQVJNXzY0LFxuICAgICAgICAgICAgcnVudGltZTogUnVudGltZS5OT0RFSlNfMTZfWCxcbiAgICAgICAgICAgIGNvZGU6IENvZGUuZnJvbUFzc2V0KHBhdGguam9pbihfX2Rpcm5hbWUsICcuLi8uLi9mdW5jdGlvbnMvYWNjZXNzLWxvZ3MtYW5hbHlzaXMvZ3JvdXAtYnktZGF0ZS9idWlsZC9hcHAnKSksXG4gICAgICAgICAgICBtZW1vcnlTaXplOiA1MTIsXG4gICAgICAgICAgICB0aW1lb3V0OiBEdXJhdGlvbi5zZWNvbmRzKDIwKSxcbiAgICAgICAgICAgIGhhbmRsZXI6ICdpbmRleC5oYW5kbGVyJyxcbiAgICAgICAgICAgIGxheWVyczogW3RoaXMuZ3JvdXBCeURhdGVMYXllcl0sXG4gICAgICAgICAgICBldmVudHM6IFtcbiAgICAgICAgICAgICAgICBuZXcgUzNFdmVudFNvdXJjZSh0aGlzLmJ1Y2tldCwge1xuICAgICAgICAgICAgICAgICAgICBldmVudHM6IFtFdmVudFR5cGUuT0JKRUNUX0NSRUFURURdLFxuICAgICAgICAgICAgICAgICAgICBmaWx0ZXJzOiBbdGhpcy5nZXRVbnByb2Nlc3NlZE9iamVjdHNGaWx0ZXIoKV0sXG4gICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICBdLFxuICAgICAgICAgICAgbG9nUmV0ZW50aW9uOiBSZXRlbnRpb25EYXlzLlRXT19XRUVLUyxcbiAgICAgICAgICAgIGVudmlyb25tZW50OiB7XG4gICAgICAgICAgICAgICAgQVdTX05PREVKU19DT05ORUNUSU9OX1JFVVNFX0VOQUJMRUQ6ICcxJyxcbiAgICAgICAgICAgICAgICBOT0RFX09QVElPTlM6ICctLWVuYWJsZS1zb3VyY2UtbWFwcycsXG4gICAgICAgICAgICAgICAgVEFSR0VUX0ZPTERFUjogQWNjZXNzTG9nc0FuYWx5c2lzLkFDQ0VTU19MT0dTX0ZPTERFUl9HUk9VUEVEX0JZX0RBVEUsXG4gICAgICAgICAgICAgICAgUkFXX0FDQ0VTU19MT0dfRklMRV9QQVRURVJOOiB0aGlzLmdldFJhd0FjY2Vzc0xvZ0ZpbGVQYXR0ZXJuKCkuc291cmNlLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5idWNrZXQuZ3JhbnRSZWFkV3JpdGUobGFtYmRhKTtcbiAgICAgICAgdGhpcy5idWNrZXQuZ3JhbnREZWxldGUobGFtYmRhLCBgJHtBY2Nlc3NMb2dzQW5hbHlzaXMuQUNDRVNTX0xPR1NfRk9MREVSX1VOUFJPQ0VTU0VEfS8qYCk7XG5cbiAgICAgICAgcmV0dXJuIGxhbWJkYTtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgYWJzdHJhY3QgZ2V0VW5wcm9jZXNzZWRPYmplY3RzRmlsdGVyKCk6IE5vdGlmaWNhdGlvbktleUZpbHRlcjtcblxuICAgIC8qKiBUaGUgcGF0dGVybiB0byBpZGVudGlmeSB1bnByb2Nlc3NlZCBhY2Nlc3MgbG9ncyBkZXBlbmRzIG9uIHRoZSBwcm9kdWNpbmcgc291cmNlICovXG4gICAgcHJvdGVjdGVkIGFic3RyYWN0IGdldFJhd0FjY2Vzc0xvZ0ZpbGVQYXR0ZXJuKCk6IFJlZ0V4cDtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSBuZXcgcGFydGl0aW9uIGZvciB0aGUgdXBjb21pbmcgaG91ciBpbiB0aGUgYWNjZXNzIGxvZyBkYXRhYmFzZS5cbiAgICAgKi9cbiAgICBwcml2YXRlIGNyZWF0ZUNyZWF0ZVBhcnRpdGlvbkxhbWJkYSgpOiBGdW5jdGlvbiB7XG4gICAgICAgIGNvbnN0IGZ1bmN0aW9uTmFtZSA9IGAke3RoaXMucmVzb3VyY2VJZFByZWZpeH0tY3JlYXRlLXBhcnRgO1xuXG4gICAgICAgIGNvbnN0IGxhbWJkYSA9IG5ldyBGdW5jdGlvbih0aGlzLCBmdW5jdGlvbk5hbWUsIHtcbiAgICAgICAgICAgIGZ1bmN0aW9uTmFtZSxcbiAgICAgICAgICAgIGFyY2hpdGVjdHVyZTogQXJjaGl0ZWN0dXJlLkFSTV82NCxcbiAgICAgICAgICAgIHJ1bnRpbWU6IFJ1bnRpbWUuTk9ERUpTXzE2X1gsXG4gICAgICAgICAgICBjb2RlOiBDb2RlLmZyb21Bc3NldChwYXRoLmpvaW4oX19kaXJuYW1lLCAnLi4vLi4vZnVuY3Rpb25zL2FjY2Vzcy1sb2dzLWFuYWx5c2lzL3BhcnRpdGlvbmluZy9idWlsZC9hcHAnKSwge1xuICAgICAgICAgICAgICAgIGV4Y2x1ZGU6IFsndHJhbnNmb3JtLXBhcnRpdGlvbionLCAnKi5kLnRzJ10sXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIG1lbW9yeVNpemU6IDEyOCxcbiAgICAgICAgICAgIHRpbWVvdXQ6IER1cmF0aW9uLnNlY29uZHMoMjApLFxuICAgICAgICAgICAgaGFuZGxlcjogJ2NyZWF0ZS1wYXJ0aXRpb24uaGFuZGxlcicsXG4gICAgICAgICAgICBsYXllcnM6IFt0aGlzLnBhcnRpdGlvbmluZ0xheWVyXSxcbiAgICAgICAgICAgIGxvZ1JldGVudGlvbjogUmV0ZW50aW9uRGF5cy5UV09fV0VFS1MsXG4gICAgICAgICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICAgICAgICAgIEFXU19OT0RFSlNfQ09OTkVDVElPTl9SRVVTRV9FTkFCTEVEOiAnMScsXG4gICAgICAgICAgICAgICAgTk9ERV9PUFRJT05TOiAnLS1lbmFibGUtc291cmNlLW1hcHMnLFxuICAgICAgICAgICAgICAgIFdPUktHUk9VUDogdGhpcy53b3JrZ3JvdXAubmFtZSxcbiAgICAgICAgICAgICAgICBEQVRBQkFTRTogdGhpcy5kYXRhYmFzZS5kYXRhYmFzZU5hbWUsXG4gICAgICAgICAgICAgICAgVEFCTEU6IHRoaXMuYWNjZXNzTG9nc0J5RGF0ZVRhYmxlLnRhYmxlTmFtZSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIGdyYW50IGFjY2VzcyB0byBTMyBidWNrZXQgYW5kIHRvIGV4ZWN1dGUgQXRoZW5hIHF1ZXJpZXMgd2hpY2ggY3JlYXRlIG5ldyBwYXJ0aXRpb25zXG4gICAgICAgIHRoaXMuYnVja2V0LmdyYW50UmVhZFdyaXRlKGxhbWJkYSk7XG4gICAgICAgIGxhbWJkYS5hZGRUb1JvbGVQb2xpY3koXG4gICAgICAgICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgICAgICAgICBlZmZlY3Q6IEVmZmVjdC5BTExPVyxcbiAgICAgICAgICAgICAgICBhY3Rpb25zOiBbXG4gICAgICAgICAgICAgICAgICAgICdhdGhlbmE6U3RhcnRRdWVyeUV4ZWN1dGlvbicsXG4gICAgICAgICAgICAgICAgICAgICdhdGhlbmE6R2V0UXVlcnlFeGVjdXRpb24nLFxuICAgICAgICAgICAgICAgICAgICAnZ2x1ZTpDcmVhdGVEYXRhYmFzZScsXG4gICAgICAgICAgICAgICAgICAgICdnbHVlOkNyZWF0ZVBhcnRpdGlvbicsXG4gICAgICAgICAgICAgICAgICAgICdnbHVlOkdldERhdGFiYXNlJyxcbiAgICAgICAgICAgICAgICAgICAgJ2dsdWU6R2V0VGFibGUnLFxuICAgICAgICAgICAgICAgICAgICAnZ2x1ZTpCYXRjaENyZWF0ZVBhcnRpdGlvbicsXG4gICAgICAgICAgICAgICAgXSxcbiAgICAgICAgICAgICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgKTtcblxuICAgICAgICByZXR1cm4gbGFtYmRhO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFRyYW5zZm9ybXMgcGFydGl0aW9ucyBmcm9tIHRoZSBIaXZlIGZvcm1hdCB0byBQYXJxdWV0LlxuICAgICAqL1xuICAgIHByaXZhdGUgY3JlYXRlVHJhbnNmb3JtUGFydGl0aW9uTGFtYmRhKCk6IEZ1bmN0aW9uIHtcbiAgICAgICAgY29uc3QgZnVuY3Rpb25OYW1lID0gYCR7dGhpcy5yZXNvdXJjZUlkUHJlZml4fS10cmFuc2Zvcm0tcGFydGA7XG5cbiAgICAgICAgY29uc3QgbGFtYmRhID0gbmV3IEZ1bmN0aW9uKHRoaXMsIGZ1bmN0aW9uTmFtZSwge1xuICAgICAgICAgICAgZnVuY3Rpb25OYW1lLFxuICAgICAgICAgICAgYXJjaGl0ZWN0dXJlOiBBcmNoaXRlY3R1cmUuQVJNXzY0LFxuICAgICAgICAgICAgcnVudGltZTogUnVudGltZS5OT0RFSlNfMTZfWCxcbiAgICAgICAgICAgIGNvZGU6IENvZGUuZnJvbUFzc2V0KHBhdGguam9pbihfX2Rpcm5hbWUsICcuLi8uLi9mdW5jdGlvbnMvYWNjZXNzLWxvZ3MtYW5hbHlzaXMvcGFydGl0aW9uaW5nL2J1aWxkL2FwcCcpLCB7XG4gICAgICAgICAgICAgICAgZXhjbHVkZTogWydjcmVhdGUtcGFydGl0aW9uKicsICcqLmQudHMnXSxcbiAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgbWVtb3J5U2l6ZTogMTI4LFxuICAgICAgICAgICAgdGltZW91dDogRHVyYXRpb24uc2Vjb25kcygyMCksXG4gICAgICAgICAgICBoYW5kbGVyOiAndHJhbnNmb3JtLXBhcnRpdGlvbi5oYW5kbGVyJyxcbiAgICAgICAgICAgIGxheWVyczogW3RoaXMucGFydGl0aW9uaW5nTGF5ZXJdLFxuICAgICAgICAgICAgbG9nUmV0ZW50aW9uOiBSZXRlbnRpb25EYXlzLlRXT19XRUVLUyxcbiAgICAgICAgICAgIGVudmlyb25tZW50OiB7XG4gICAgICAgICAgICAgICAgQVdTX05PREVKU19DT05ORUNUSU9OX1JFVVNFX0VOQUJMRUQ6ICcxJyxcbiAgICAgICAgICAgICAgICBOT0RFX09QVElPTlM6ICctLWVuYWJsZS1zb3VyY2UtbWFwcycsXG4gICAgICAgICAgICAgICAgV09SS0dST1VQOiB0aGlzLndvcmtncm91cC5uYW1lLFxuICAgICAgICAgICAgICAgIERBVEFCQVNFOiB0aGlzLmRhdGFiYXNlLmRhdGFiYXNlTmFtZSxcbiAgICAgICAgICAgICAgICBTT1VSQ0VfVEFCTEU6IHRoaXMuYWNjZXNzTG9nc0J5RGF0ZVRhYmxlLnRhYmxlTmFtZSxcbiAgICAgICAgICAgICAgICBUQVJHRVRfVEFCTEU6IHRoaXMuYWNjZXNzTG9nc1BhcnF1ZXRUYWJsZS50YWJsZU5hbWUsXG4gICAgICAgICAgICB9LFxuICAgICAgICB9KTtcblxuICAgICAgICAvLyBncmFudCBhY2Nlc3MgdG8gUzMgYnVja2V0IGFuZCB0byBleGVjdXRlIEF0aGVuYSBxdWVyaWVzIHdoaWNoIGNyZWF0ZSBuZXcgcGFydGl0aW9uc1xuICAgICAgICB0aGlzLmJ1Y2tldC5ncmFudFJlYWRXcml0ZShsYW1iZGEpO1xuICAgICAgICBsYW1iZGEuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICAgICAgbmV3IFBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgICAgICAgICAgZWZmZWN0OiBFZmZlY3QuQUxMT1csXG4gICAgICAgICAgICAgICAgYWN0aW9uczogW1xuICAgICAgICAgICAgICAgICAgICAnYXRoZW5hOlN0YXJ0UXVlcnlFeGVjdXRpb24nLFxuICAgICAgICAgICAgICAgICAgICAnYXRoZW5hOkdldFF1ZXJ5RXhlY3V0aW9uJyxcbiAgICAgICAgICAgICAgICAgICAgJ2dsdWU6Q3JlYXRlRGF0YWJhc2UnLFxuICAgICAgICAgICAgICAgICAgICAnZ2x1ZTpDcmVhdGVQYXJ0aXRpb24nLFxuICAgICAgICAgICAgICAgICAgICAnZ2x1ZTpHZXREYXRhYmFzZScsXG4gICAgICAgICAgICAgICAgICAgICdnbHVlOkdldFRhYmxlJyxcbiAgICAgICAgICAgICAgICAgICAgJ2dsdWU6QmF0Y2hDcmVhdGVQYXJ0aXRpb24nLFxuICAgICAgICAgICAgICAgICAgICAnZ2x1ZTpHZXRQYXJ0aXRpb24nLFxuICAgICAgICAgICAgICAgICAgICAnZ2x1ZTpHZXRQYXJ0aXRpb25zJyxcbiAgICAgICAgICAgICAgICAgICAgJ2dsdWU6Q3JlYXRlVGFibGUnLFxuICAgICAgICAgICAgICAgICAgICAnZ2x1ZTpEZWxldGVUYWJsZScsXG4gICAgICAgICAgICAgICAgICAgICdnbHVlOkRlbGV0ZVBhcnRpdGlvbicsXG4gICAgICAgICAgICAgICAgXSxcbiAgICAgICAgICAgICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgKTtcblxuICAgICAgICByZXR1cm4gbGFtYmRhO1xuICAgIH1cblxuICAgIHByaXZhdGUgY3JlYXRlQ3JlYXRlUGFydGl0aW9uc1NjaGVkdWxlcigpOiBSdWxlIHtcbiAgICAgICAgY29uc3QgcnVsZU5hbWUgPSBgJHt0aGlzLnJlc291cmNlSWRQcmVmaXh9LWNyZWF0ZS1wYXJ0LXNjaGVkYDtcbiAgICAgICAgcmV0dXJuIG5ldyBSdWxlKHRoaXMsIHJ1bGVOYW1lLCB7XG4gICAgICAgICAgICBydWxlTmFtZSxcbiAgICAgICAgICAgIHNjaGVkdWxlOiBTY2hlZHVsZS5jcm9uKHttaW51dGU6ICc1NSd9KSxcbiAgICAgICAgICAgIHRhcmdldHM6IFtuZXcgTGFtYmRhRnVuY3Rpb24odGhpcy5jcmVhdGVQYXJ0aXRpb25MYW1iZGEsIHtyZXRyeUF0dGVtcHRzOiAxMH0pXSxcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBjcmVhdGVUcmFuc2Zvcm1QYXJ0aXRpb25zU2NoZWR1bGVyKHByb3BzOiBBY2Nlc3NMb2dzQW5hbHlzaXNQcm9wcyk6IFJ1bGUge1xuICAgICAgICBjb25zdCBydWxlTmFtZSA9IGAke3RoaXMucmVzb3VyY2VJZFByZWZpeH0tdHJhbnNmb3JtLXBhcnQtc2NoZWRgO1xuICAgICAgICByZXR1cm4gbmV3IFJ1bGUodGhpcywgcnVsZU5hbWUsIHtcbiAgICAgICAgICAgIHJ1bGVOYW1lLFxuICAgICAgICAgICAgc2NoZWR1bGU6IFNjaGVkdWxlLmNyb24oe21pbnV0ZTogJzEnfSksXG4gICAgICAgICAgICB0YXJnZXRzOiBbXG4gICAgICAgICAgICAgICAgbmV3IExhbWJkYUZ1bmN0aW9uKHRoaXMudHJhbnNmb3JtUGFydGl0aW9uTGFtYmRhLCB7XG4gICAgICAgICAgICAgICAgICAgIGV2ZW50OiB0aGlzLmdldFRyYW5zZm9ybVBhcnRpdGlvbkludm9jYXRpb25Qcm9wcyhwcm9wcyksXG4gICAgICAgICAgICAgICAgICAgIHJldHJ5QXR0ZW1wdHM6IDEwLFxuICAgICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgXSxcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBnZXRUcmFuc2Zvcm1QYXJ0aXRpb25JbnZvY2F0aW9uUHJvcHMocHJvcHM6IEFjY2Vzc0xvZ3NBbmFseXNpc1Byb3BzKTogUnVsZVRhcmdldElucHV0IHtcbiAgICAgICAgcmV0dXJuIFJ1bGVUYXJnZXRJbnB1dC5mcm9tT2JqZWN0KHtcbiAgICAgICAgICAgIGNvbHVtbk5hbWVzOiBbXG4gICAgICAgICAgICAgICAgLi4udGhpcy5hY2Nlc3NMb2dzUGFycXVldFRhYmxlLmNvbHVtbnMsXG4gICAgICAgICAgICAgICAgLi4uKHRoaXMuYWNjZXNzTG9nc1BhcnF1ZXRUYWJsZS5wYXJ0aXRpb25LZXlzIGFzIENvbHVtbltdKSxcbiAgICAgICAgICAgIF0ubWFwKGNvbHVtbiA9PiBjb2x1bW4ubmFtZSksXG4gICAgICAgICAgICBjb2x1bW5UcmFuc2Zvcm1hdGlvbnM6IHRoaXMuZ2V0Q29sdW1uVHJhbnNmb3JtYXRpb25SdWxlcyhwcm9wcyksXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIE5lZWRzIHRvIHJldHVybiB0aGUgbG9nIHNvdXJjZSBzcGVjaWZpYyBjb2x1bW4gdHJhbnNmb3JtYXRpb24gcnVsZXNcbiAgICAgKiAoZS5nLiB0byBhbm9ueW1pemUgSVAgYWRkcmVzc2VzKS5cbiAgICAgKi9cbiAgICBwcm90ZWN0ZWQgYWJzdHJhY3QgZ2V0Q29sdW1uVHJhbnNmb3JtYXRpb25SdWxlcyhwcm9wczogQWNjZXNzTG9nc0FuYWx5c2lzUHJvcHMpOiBDb2x1bW5UcmFuc2Zvcm1hdGlvblJ1bGVzO1xufVxuIl19
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import {Bucket, EventType, NotificationKeyFilter} from 'aws-cdk-lib/aws-s3';
|
|
2
|
+
import {Construct} from 'constructs';
|
|
3
|
+
import {CfnWorkGroup} from 'aws-cdk-lib/aws-athena';
|
|
4
|
+
import {CloudFrontAccessLogsByDateTable} from './CloudFrontAccessLogsByDateTable';
|
|
5
|
+
import {AccessLogsParquetTable} from './AccessLogsParquetTable';
|
|
6
|
+
import {Architecture, Code, Function, LayerVersion, Runtime} from 'aws-cdk-lib/aws-lambda';
|
|
7
|
+
import {Rule, RuleTargetInput, Schedule} from 'aws-cdk-lib/aws-events';
|
|
8
|
+
import {CfnTag, Duration, RemovalPolicy, Stack} from 'aws-cdk-lib';
|
|
9
|
+
import {Column, Database} from '@aws-cdk/aws-glue-alpha';
|
|
10
|
+
import {S3EventSource} from 'aws-cdk-lib/aws-lambda-event-sources';
|
|
11
|
+
import {RetentionDays} from 'aws-cdk-lib/aws-logs';
|
|
12
|
+
import {Effect, PolicyStatement} from 'aws-cdk-lib/aws-iam';
|
|
13
|
+
import {LambdaFunction} from 'aws-cdk-lib/aws-events-targets';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import {AccessLogsAnalysisProps} from "./AccessLogsAnalysisProps";
|
|
16
|
+
import {ColumnTransformationRules} from "../../functions/access-logs-analysis/partitioning/types";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Provides the AWS resources to analyze access logs. This construct is derived from the official AWS sample
|
|
20
|
+
* CloudFormation stack:
|
|
21
|
+
* {@link https://github.com/aws-samples/amazon-cloudfront-access-logs-queries/blob/mainline/template.yaml}
|
|
22
|
+
*/
|
|
23
|
+
export abstract class AccessLogsAnalysis extends Construct {
|
|
24
|
+
|
|
25
|
+
protected static readonly ACCESS_LOGS_FOLDER_UNPROCESSED = 'unprocessed';
|
|
26
|
+
protected static readonly ACCESS_LOGS_FOLDER_GROUPED_BY_DATE = 'by-date';
|
|
27
|
+
protected static readonly ACCESS_LOGS_FOLDER_TRANSFORMED = 'transformed';
|
|
28
|
+
protected static readonly ACCESS_LOGS_FOLDER_ATHENA_RESULTS = 'athena-query-results';
|
|
29
|
+
|
|
30
|
+
protected readonly resourceIdPrefix: string;
|
|
31
|
+
protected readonly bucket: Bucket;
|
|
32
|
+
protected readonly workgroup: CfnWorkGroup;
|
|
33
|
+
protected readonly database: Database;
|
|
34
|
+
protected readonly accessLogsByDateTable: CloudFrontAccessLogsByDateTable;
|
|
35
|
+
protected readonly accessLogsParquetTable: AccessLogsParquetTable;
|
|
36
|
+
protected readonly groupByDateLayer: LayerVersion;
|
|
37
|
+
protected readonly groupByDateLambda: Function;
|
|
38
|
+
protected readonly partitioningLayer: LayerVersion;
|
|
39
|
+
protected readonly createPartitionLambda: Function;
|
|
40
|
+
protected readonly transformPartitionLambda: Function;
|
|
41
|
+
protected readonly createPartitionsScheduler: Rule;
|
|
42
|
+
protected readonly transformPartitionsScheduler: Rule;
|
|
43
|
+
|
|
44
|
+
protected constructor(scope: Construct, id: string, props: AccessLogsAnalysisProps) {
|
|
45
|
+
super(scope, id);
|
|
46
|
+
this.resourceIdPrefix = props.resourcePrefix;
|
|
47
|
+
this.bucket = props.bucket;
|
|
48
|
+
this.setupLifecycleRules(props);
|
|
49
|
+
this.workgroup = this.createWorkgroup();
|
|
50
|
+
this.database = this.createGlueDatabase();
|
|
51
|
+
this.accessLogsByDateTable = this.createAccessLogsByDateTable();
|
|
52
|
+
this.accessLogsParquetTable = this.createAccessLogsParquetTable();
|
|
53
|
+
this.groupByDateLayer = this.createGroupByDateLayer();
|
|
54
|
+
this.groupByDateLambda = this.createGroupByDateLambda();
|
|
55
|
+
this.partitioningLayer = this.createPartitioningLayer();
|
|
56
|
+
this.createPartitionLambda = this.createCreatePartitionLambda();
|
|
57
|
+
this.transformPartitionLambda = this.createTransformPartitionLambda();
|
|
58
|
+
this.createPartitionsScheduler = this.createCreatePartitionsScheduler();
|
|
59
|
+
this.transformPartitionsScheduler = this.createTransformPartitionsScheduler(props);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private setupLifecycleRules(props: AccessLogsAnalysisProps): void {
|
|
63
|
+
// cleanup raw and partitioned access logs after a configurable time range
|
|
64
|
+
|
|
65
|
+
this.bucket.addLifecycleRule({
|
|
66
|
+
id: `${this.resourceIdPrefix}-cleanup-unprocessed`,
|
|
67
|
+
prefix: `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_UNPROCESSED}/*`,
|
|
68
|
+
expiration: props.expireRawLogsAfter ?? Duration.days(7),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
this.bucket.addLifecycleRule({
|
|
72
|
+
id: `${this.resourceIdPrefix}-cleanup-grouped`,
|
|
73
|
+
prefix: `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_GROUPED_BY_DATE}/*`,
|
|
74
|
+
expiration: props.expireIntermediateLogsAfter ?? Duration.days(7),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
this.bucket.addLifecycleRule({
|
|
78
|
+
id: `${this.resourceIdPrefix}-cleanup-transformed`,
|
|
79
|
+
prefix: `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_TRANSFORMED}/*`,
|
|
80
|
+
expiration: props.expireTransformedLogsAfter ?? Duration.days(180),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/* CHECKME: delete query results after 1 week
|
|
84
|
+
this.bucket.addLifecycleRule({
|
|
85
|
+
id: `${this.resourceIdPrefix}-cleanup-query-results`,
|
|
86
|
+
prefix: `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_ATHENA_RESULTS}/*`,
|
|
87
|
+
expiration: Duration.days(7),
|
|
88
|
+
});
|
|
89
|
+
*/
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private createWorkgroup(): CfnWorkGroup {
|
|
93
|
+
const workgroupName = `${this.resourceIdPrefix}-workgroup`;
|
|
94
|
+
|
|
95
|
+
// due to a current bug, the tags are not automatically assigned to the workgroup
|
|
96
|
+
// note, that the keys must be converted to lower case
|
|
97
|
+
const tags: CfnTag[] = Stack.of(this)
|
|
98
|
+
.tags.renderTags()
|
|
99
|
+
.map((tag: { Key: string; Value: string }) => ({key: tag.Key, value: tag.Value}));
|
|
100
|
+
|
|
101
|
+
return new CfnWorkGroup(this, workgroupName, {
|
|
102
|
+
name: workgroupName,
|
|
103
|
+
workGroupConfiguration: {
|
|
104
|
+
publishCloudWatchMetricsEnabled: false,
|
|
105
|
+
resultConfiguration: {
|
|
106
|
+
outputLocation: `s3://${this.bucket.bucketName}/${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_ATHENA_RESULTS}`,
|
|
107
|
+
},
|
|
108
|
+
enforceWorkGroupConfiguration: true
|
|
109
|
+
},
|
|
110
|
+
tags,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private createGlueDatabase(): Database {
|
|
115
|
+
const databaseName = `${this.resourceIdPrefix}-database`;
|
|
116
|
+
return new Database(this, databaseName, {
|
|
117
|
+
// Athena doesn't support dashes in database/table names
|
|
118
|
+
databaseName: databaseName.replace(/-/g, '_'),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
protected abstract createAccessLogsByDateTable(): CloudFrontAccessLogsByDateTable;
|
|
123
|
+
|
|
124
|
+
protected abstract createAccessLogsParquetTable(): CloudFrontAccessLogsByDateTable;
|
|
125
|
+
|
|
126
|
+
private createGroupByDateLayer(): LayerVersion {
|
|
127
|
+
const layerVersionName = `${this.resourceIdPrefix}-group-by-date-dependencies`;
|
|
128
|
+
return new LayerVersion(this, layerVersionName, {
|
|
129
|
+
layerVersionName,
|
|
130
|
+
compatibleArchitectures: [Architecture.ARM_64, Architecture.X86_64],
|
|
131
|
+
compatibleRuntimes: [Runtime.NODEJS_14_X, Runtime.NODEJS_16_X],
|
|
132
|
+
code: Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/group-by-date/build/layer')),
|
|
133
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private createPartitioningLayer(): LayerVersion {
|
|
138
|
+
const layerVersionName = `${this.resourceIdPrefix}-partitioning-dependencies`;
|
|
139
|
+
return new LayerVersion(this, layerVersionName, {
|
|
140
|
+
layerVersionName,
|
|
141
|
+
compatibleArchitectures: [Architecture.ARM_64, Architecture.X86_64],
|
|
142
|
+
compatibleRuntimes: [Runtime.NODEJS_14_X, Runtime.NODEJS_16_X],
|
|
143
|
+
code: Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/partitioning/build/layer')),
|
|
144
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* A Lambda function to be triggered whenever a new access log is created.
|
|
150
|
+
* It moves the raw access logs into a sub folder hierarchy by year, month, day and hour.
|
|
151
|
+
*/
|
|
152
|
+
private createGroupByDateLambda(): Function {
|
|
153
|
+
const functionName = `${this.resourceIdPrefix}-group-by-date`;
|
|
154
|
+
|
|
155
|
+
const lambda = new Function(this, functionName, {
|
|
156
|
+
functionName,
|
|
157
|
+
architecture: Architecture.ARM_64,
|
|
158
|
+
runtime: Runtime.NODEJS_16_X,
|
|
159
|
+
code: Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/group-by-date/build/app')),
|
|
160
|
+
memorySize: 512,
|
|
161
|
+
timeout: Duration.seconds(20),
|
|
162
|
+
handler: 'index.handler',
|
|
163
|
+
layers: [this.groupByDateLayer],
|
|
164
|
+
events: [
|
|
165
|
+
new S3EventSource(this.bucket, {
|
|
166
|
+
events: [EventType.OBJECT_CREATED],
|
|
167
|
+
filters: [this.getUnprocessedObjectsFilter()],
|
|
168
|
+
}),
|
|
169
|
+
],
|
|
170
|
+
logRetention: RetentionDays.TWO_WEEKS,
|
|
171
|
+
environment: {
|
|
172
|
+
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
|
|
173
|
+
NODE_OPTIONS: '--enable-source-maps',
|
|
174
|
+
TARGET_FOLDER: AccessLogsAnalysis.ACCESS_LOGS_FOLDER_GROUPED_BY_DATE,
|
|
175
|
+
RAW_ACCESS_LOG_FILE_PATTERN: this.getRawAccessLogFilePattern().source,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
this.bucket.grantReadWrite(lambda);
|
|
180
|
+
this.bucket.grantDelete(lambda, `${AccessLogsAnalysis.ACCESS_LOGS_FOLDER_UNPROCESSED}/*`);
|
|
181
|
+
|
|
182
|
+
return lambda;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
protected abstract getUnprocessedObjectsFilter(): NotificationKeyFilter;
|
|
186
|
+
|
|
187
|
+
/** The pattern to identify unprocessed access logs depends on the producing source */
|
|
188
|
+
protected abstract getRawAccessLogFilePattern(): RegExp;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Creates a new partition for the upcoming hour in the access log database.
|
|
192
|
+
*/
|
|
193
|
+
private createCreatePartitionLambda(): Function {
|
|
194
|
+
const functionName = `${this.resourceIdPrefix}-create-part`;
|
|
195
|
+
|
|
196
|
+
const lambda = new Function(this, functionName, {
|
|
197
|
+
functionName,
|
|
198
|
+
architecture: Architecture.ARM_64,
|
|
199
|
+
runtime: Runtime.NODEJS_16_X,
|
|
200
|
+
code: Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/partitioning/build/app'), {
|
|
201
|
+
exclude: ['transform-partition*', '*.d.ts'],
|
|
202
|
+
}),
|
|
203
|
+
memorySize: 128,
|
|
204
|
+
timeout: Duration.seconds(20),
|
|
205
|
+
handler: 'create-partition.handler',
|
|
206
|
+
layers: [this.partitioningLayer],
|
|
207
|
+
logRetention: RetentionDays.TWO_WEEKS,
|
|
208
|
+
environment: {
|
|
209
|
+
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
|
|
210
|
+
NODE_OPTIONS: '--enable-source-maps',
|
|
211
|
+
WORKGROUP: this.workgroup.name,
|
|
212
|
+
DATABASE: this.database.databaseName,
|
|
213
|
+
TABLE: this.accessLogsByDateTable.tableName,
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// grant access to S3 bucket and to execute Athena queries which create new partitions
|
|
218
|
+
this.bucket.grantReadWrite(lambda);
|
|
219
|
+
lambda.addToRolePolicy(
|
|
220
|
+
new PolicyStatement({
|
|
221
|
+
effect: Effect.ALLOW,
|
|
222
|
+
actions: [
|
|
223
|
+
'athena:StartQueryExecution',
|
|
224
|
+
'athena:GetQueryExecution',
|
|
225
|
+
'glue:CreateDatabase',
|
|
226
|
+
'glue:CreatePartition',
|
|
227
|
+
'glue:GetDatabase',
|
|
228
|
+
'glue:GetTable',
|
|
229
|
+
'glue:BatchCreatePartition',
|
|
230
|
+
],
|
|
231
|
+
resources: ['*'],
|
|
232
|
+
})
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
return lambda;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Transforms partitions from the Hive format to Parquet.
|
|
240
|
+
*/
|
|
241
|
+
private createTransformPartitionLambda(): Function {
|
|
242
|
+
const functionName = `${this.resourceIdPrefix}-transform-part`;
|
|
243
|
+
|
|
244
|
+
const lambda = new Function(this, functionName, {
|
|
245
|
+
functionName,
|
|
246
|
+
architecture: Architecture.ARM_64,
|
|
247
|
+
runtime: Runtime.NODEJS_16_X,
|
|
248
|
+
code: Code.fromAsset(path.join(__dirname, '../../functions/access-logs-analysis/partitioning/build/app'), {
|
|
249
|
+
exclude: ['create-partition*', '*.d.ts'],
|
|
250
|
+
}),
|
|
251
|
+
memorySize: 128,
|
|
252
|
+
timeout: Duration.seconds(20),
|
|
253
|
+
handler: 'transform-partition.handler',
|
|
254
|
+
layers: [this.partitioningLayer],
|
|
255
|
+
logRetention: RetentionDays.TWO_WEEKS,
|
|
256
|
+
environment: {
|
|
257
|
+
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
|
|
258
|
+
NODE_OPTIONS: '--enable-source-maps',
|
|
259
|
+
WORKGROUP: this.workgroup.name,
|
|
260
|
+
DATABASE: this.database.databaseName,
|
|
261
|
+
SOURCE_TABLE: this.accessLogsByDateTable.tableName,
|
|
262
|
+
TARGET_TABLE: this.accessLogsParquetTable.tableName,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// grant access to S3 bucket and to execute Athena queries which create new partitions
|
|
267
|
+
this.bucket.grantReadWrite(lambda);
|
|
268
|
+
lambda.addToRolePolicy(
|
|
269
|
+
new PolicyStatement({
|
|
270
|
+
effect: Effect.ALLOW,
|
|
271
|
+
actions: [
|
|
272
|
+
'athena:StartQueryExecution',
|
|
273
|
+
'athena:GetQueryExecution',
|
|
274
|
+
'glue:CreateDatabase',
|
|
275
|
+
'glue:CreatePartition',
|
|
276
|
+
'glue:GetDatabase',
|
|
277
|
+
'glue:GetTable',
|
|
278
|
+
'glue:BatchCreatePartition',
|
|
279
|
+
'glue:GetPartition',
|
|
280
|
+
'glue:GetPartitions',
|
|
281
|
+
'glue:CreateTable',
|
|
282
|
+
'glue:DeleteTable',
|
|
283
|
+
'glue:DeletePartition',
|
|
284
|
+
],
|
|
285
|
+
resources: ['*'],
|
|
286
|
+
})
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
return lambda;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
private createCreatePartitionsScheduler(): Rule {
|
|
293
|
+
const ruleName = `${this.resourceIdPrefix}-create-part-sched`;
|
|
294
|
+
return new Rule(this, ruleName, {
|
|
295
|
+
ruleName,
|
|
296
|
+
schedule: Schedule.cron({minute: '55'}),
|
|
297
|
+
targets: [new LambdaFunction(this.createPartitionLambda, {retryAttempts: 10})],
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private createTransformPartitionsScheduler(props: AccessLogsAnalysisProps): Rule {
|
|
302
|
+
const ruleName = `${this.resourceIdPrefix}-transform-part-sched`;
|
|
303
|
+
return new Rule(this, ruleName, {
|
|
304
|
+
ruleName,
|
|
305
|
+
schedule: Schedule.cron({minute: '1'}),
|
|
306
|
+
targets: [
|
|
307
|
+
new LambdaFunction(this.transformPartitionLambda, {
|
|
308
|
+
event: this.getTransformPartitionInvocationProps(props),
|
|
309
|
+
retryAttempts: 10,
|
|
310
|
+
}),
|
|
311
|
+
],
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private getTransformPartitionInvocationProps(props: AccessLogsAnalysisProps): RuleTargetInput {
|
|
316
|
+
return RuleTargetInput.fromObject({
|
|
317
|
+
columnNames: [
|
|
318
|
+
...this.accessLogsParquetTable.columns,
|
|
319
|
+
...(this.accessLogsParquetTable.partitionKeys as Column[]),
|
|
320
|
+
].map(column => column.name),
|
|
321
|
+
columnTransformations: this.getColumnTransformationRules(props),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Needs to return the log source specific column transformation rules
|
|
327
|
+
* (e.g. to anonymize IP addresses).
|
|
328
|
+
*/
|
|
329
|
+
protected abstract getColumnTransformationRules(props: AccessLogsAnalysisProps): ColumnTransformationRules;
|
|
330
|
+
}
|