rds-database-auto-start-preventer 0.4.4 → 0.5.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.
@@ -0,0 +1,241 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.RDSDatabaseAutoStartPreventer = void 0;
37
+ const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
38
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
39
+ const events = __importStar(require("aws-cdk-lib/aws-events"));
40
+ const targets = __importStar(require("aws-cdk-lib/aws-events-targets"));
41
+ const iam = __importStar(require("aws-cdk-lib/aws-iam"));
42
+ const lambda = __importStar(require("aws-cdk-lib/aws-lambda"));
43
+ const logs = __importStar(require("aws-cdk-lib/aws-logs"));
44
+ const aws_secretsmanager_1 = require("aws-cdk-lib/aws-secretsmanager");
45
+ const constructs_1 = require("constructs");
46
+ const auto_start_prevent_function_1 = require("./funcs/auto-start-prevent-function");
47
+ /**
48
+ * Returns true when a concrete string is empty or whitespace-only.
49
+ * Unresolved CDK tokens are treated as present because their value is unknown at synthesis.
50
+ *
51
+ * @param value - Prop string to check.
52
+ * @returns Whether the value is blank.
53
+ */
54
+ const isBlankText = (value) => !aws_cdk_lib_1.Token.isUnresolved(value) && value.trim().length === 0;
55
+ /**
56
+ * Rejects empty tag filters and Slack secret names before resources are created.
57
+ *
58
+ * @param props - Construct props to validate.
59
+ * @throws When tagKey or a tag value is blank, tagValues is empty, or slackSecretName is present but blank.
60
+ */
61
+ const validateProps = (props) => {
62
+ if (isBlankText(props.targetResource.tagKey)) {
63
+ throw new Error('targetResource.tagKey must be a non-empty string.');
64
+ }
65
+ if (props.targetResource.tagValues.length === 0) {
66
+ throw new Error('targetResource.tagValues must contain at least one value.');
67
+ }
68
+ for (const value of props.targetResource.tagValues) {
69
+ if (isBlankText(value)) {
70
+ throw new Error('targetResource.tagValues must not contain empty strings.');
71
+ }
72
+ }
73
+ const slackSecretName = props.secrets.slackSecretName;
74
+ if (slackSecretName !== undefined && isBlankText(slackSecretName)) {
75
+ throw new Error('secrets.slackSecretName must be a non-empty string.');
76
+ }
77
+ };
78
+ /**
79
+ * Deploys EventBridge rules and a Durable Lambda to stop matching RDS resources
80
+ * after auto-start events (RDS-EVENT-0154 / RDS-EVENT-0153).
81
+ *
82
+ * EventBridge rules match all auto-start events; tag filtering runs in the handler
83
+ * using `TagList` from `rds:DescribeDBInstances` / `rds:DescribeDBClusters`.
84
+ * IAM grants account/region-scoped describe on `db` / `cluster` ARNs, and stop only
85
+ * when `aws:ResourceTag` matches {@link TargetResource} (no Resource Groups Tagging API).
86
+ */
87
+ class RDSDatabaseAutoStartPreventer extends constructs_1.Construct {
88
+ static [JSII_RTTI_SYMBOL_1] = { fqn: "rds-database-auto-start-preventer.RDSDatabaseAutoStartPreventer", version: "0.5.0" };
89
+ /**
90
+ * Creates the Durable Lambda, EventBridge rules, and IAM policies.
91
+ *
92
+ * The Lambda receives `{ event, params }` where `params` carries `tagKey` and `tagValues`
93
+ * from {@link RDSDatabaseAutoStartPreventerProps.targetResource}. Tag filters are passed
94
+ * only via that payload (not via `TAG_KEY` / `TAG_VALUES` environment variables).
95
+ *
96
+ * @param scope - Parent construct.
97
+ * @param id - Construct id.
98
+ * @param props - Target resource (tag key/values), enable rule flag, and secrets.
99
+ * @throws When tagKey or a tag value is blank, tagValues is empty, or slackSecretName is present but blank.
100
+ */
101
+ constructor(scope, id, props) {
102
+ super(scope, id);
103
+ validateProps(props);
104
+ const slackSecretName = props.secrets.slackSecretName;
105
+ const slackSecret = slackSecretName === undefined
106
+ ? undefined
107
+ : aws_secretsmanager_1.Secret.fromSecretNameV2(this, 'SlackSecret', slackSecretName);
108
+ // Durable Functions-based Running Scheduler (previous Step Functions logic implemented in Lambda).
109
+ // Durable Execution requires Node.js 22+.
110
+ const autoStartPreventFunction = new auto_start_prevent_function_1.AutoStartPreventFunction(this, 'AutoStartPreventFunction', {
111
+ description: 'A function to prevent the RDS Database or Cluster from starting automatically.',
112
+ architecture: lambda.Architecture.ARM_64,
113
+ timeout: aws_cdk_lib_1.Duration.minutes(15),
114
+ memorySize: 512,
115
+ retryAttempts: 2,
116
+ durableConfig: {
117
+ executionTimeout: aws_cdk_lib_1.Duration.hours(2),
118
+ retentionPeriod: aws_cdk_lib_1.Duration.days(1),
119
+ },
120
+ environment: slackSecret === undefined
121
+ ? undefined
122
+ : {
123
+ SLACK_SECRET_NAME: slackSecret.secretName,
124
+ },
125
+ // Required by aws-lambda-secret-fetcher (^0.6+) when Slack is configured.
126
+ paramsAndSecrets: slackSecret === undefined
127
+ ? undefined
128
+ : lambda.ParamsAndSecretsLayerVersion.fromVersion(lambda.ParamsAndSecretsVersions.V1_0_103, {
129
+ cacheSize: 500,
130
+ logLevel: lambda.ParamsAndSecretsLogLevel.INFO,
131
+ }),
132
+ role: new iam.Role(this, 'AutoStartPreventFunctionRole', {
133
+ description: 'A role to control the RDS Database or Cluster.',
134
+ assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
135
+ managedPolicies: [
136
+ iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
137
+ iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicDurableExecutionRolePolicy'),
138
+ ],
139
+ }),
140
+ logGroup: new logs.LogGroup(this, 'AutoStartPreventFunctionLogGroup', {
141
+ retention: logs.RetentionDays.THREE_MONTHS,
142
+ // Delete on failed create/rollback; retain after successful create (stack delete/replace).
143
+ removalPolicy: aws_cdk_lib_1.RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE,
144
+ }),
145
+ loggingFormat: lambda.LoggingFormat.JSON,
146
+ systemLogLevelV2: lambda.SystemLogLevel.INFO,
147
+ applicationLogLevelV2: lambda.ApplicationLogLevel.INFO,
148
+ });
149
+ const rdsDbAndClusterArns = [
150
+ aws_cdk_lib_1.Stack.of(this).formatArn({
151
+ service: 'rds',
152
+ resource: 'db',
153
+ resourceName: '*',
154
+ arnFormat: aws_cdk_lib_1.ArnFormat.COLON_RESOURCE_NAME,
155
+ }),
156
+ aws_cdk_lib_1.Stack.of(this).formatArn({
157
+ service: 'rds',
158
+ resource: 'cluster',
159
+ resourceName: '*',
160
+ arnFormat: aws_cdk_lib_1.ArnFormat.COLON_RESOURCE_NAME,
161
+ }),
162
+ ];
163
+ // Describe stays without ResourceTag so TagList can be read for non-matching resources.
164
+ autoStartPreventFunction.addToRolePolicy(new iam.PolicyStatement({
165
+ sid: 'RdsDescribeForTagFilter',
166
+ effect: iam.Effect.ALLOW,
167
+ actions: [
168
+ 'rds:DescribeDBInstances',
169
+ 'rds:DescribeDBClusters',
170
+ ],
171
+ resources: rdsDbAndClusterArns,
172
+ }));
173
+ // Stop is limited to db/cluster ARNs whose tag matches targetResource.
174
+ autoStartPreventFunction.addToRolePolicy(new iam.PolicyStatement({
175
+ sid: 'RdsStopTaggedTargets',
176
+ effect: iam.Effect.ALLOW,
177
+ actions: [
178
+ 'rds:StopDBInstance',
179
+ 'rds:StopDBCluster',
180
+ ],
181
+ resources: rdsDbAndClusterArns,
182
+ conditions: {
183
+ StringEquals: {
184
+ [`aws:ResourceTag/${props.targetResource.tagKey}`]: props.targetResource.tagValues,
185
+ },
186
+ },
187
+ }));
188
+ if (slackSecret !== undefined) {
189
+ slackSecret.grantRead(autoStartPreventFunction);
190
+ }
191
+ // See: https://docs.aws.amazon.com/lambda/latest/dg/durable-getting-started-iac.html
192
+ const autoStartPreventFunctionAlias = autoStartPreventFunction.addAlias('live');
193
+ const enableRule = props.enableRule !== false;
194
+ // Pass the EventBridge event and tag filter params to the Lambda target input.
195
+ const lambdaInput = events.RuleTargetInput.fromObject({
196
+ event: events.EventField.fromPath('$'),
197
+ params: {
198
+ tagKey: props.targetResource.tagKey,
199
+ tagValues: props.targetResource.tagValues,
200
+ },
201
+ });
202
+ // 👇 EventBridge by RDS DB Instance auto start event
203
+ new events.Rule(this, 'DBInstanceEvent', {
204
+ description: 'rds db instance auto start event catch rule.',
205
+ enabled: enableRule,
206
+ eventPattern: {
207
+ source: ['aws.rds'],
208
+ detailType: ['RDS DB Instance Event'],
209
+ detail: {
210
+ EventID: ['RDS-EVENT-0154'],
211
+ },
212
+ },
213
+ targets: [
214
+ new targets.LambdaFunction(autoStartPreventFunctionAlias, {
215
+ retryAttempts: 2,
216
+ event: lambdaInput,
217
+ }),
218
+ ],
219
+ });
220
+ // 👇 EventBridge by RDS DB Cluster auto start event
221
+ new events.Rule(this, 'DBClusterEvent', {
222
+ description: 'db cluster auto start event catch rule',
223
+ enabled: enableRule,
224
+ eventPattern: {
225
+ source: ['aws.rds'],
226
+ detailType: ['RDS DB Cluster Event'],
227
+ detail: {
228
+ EventID: ['RDS-EVENT-0153'],
229
+ },
230
+ },
231
+ targets: [
232
+ new targets.LambdaFunction(autoStartPreventFunctionAlias, {
233
+ retryAttempts: 2,
234
+ event: lambdaInput,
235
+ }),
236
+ ],
237
+ });
238
+ }
239
+ }
240
+ exports.RDSDatabaseAutoStartPreventer = RDSDatabaseAutoStartPreventer;
241
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmRzLWRhdGFiYXNlLWF1dG8tc3RhcnQtcHJldmVudGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3Jkcy1kYXRhYmFzZS1hdXRvLXN0YXJ0LXByZXZlbnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsNkNBQStFO0FBQy9FLCtEQUFpRDtBQUNqRCx3RUFBMEQ7QUFDMUQseURBQTJDO0FBQzNDLCtEQUFpRDtBQUNqRCwyREFBNkM7QUFDN0MsdUVBQXdEO0FBQ3hELDJDQUF1QztBQUN2QyxxRkFBK0U7QUFzQy9FOzs7Ozs7R0FNRztBQUNILE1BQU0sV0FBVyxHQUFHLENBQUMsS0FBYSxFQUFXLEVBQUUsQ0FDN0MsQ0FBQyxtQkFBSyxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsSUFBSSxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsTUFBTSxLQUFLLENBQUMsQ0FBQztBQUUxRDs7Ozs7R0FLRztBQUNILE1BQU0sYUFBYSxHQUFHLENBQUMsS0FBeUMsRUFBUSxFQUFFO0lBQ3hFLElBQUksV0FBVyxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztRQUM3QyxNQUFNLElBQUksS0FBSyxDQUFDLG1EQUFtRCxDQUFDLENBQUM7SUFDdkUsQ0FBQztJQUNELElBQUksS0FBSyxDQUFDLGNBQWMsQ0FBQyxTQUFTLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ2hELE1BQU0sSUFBSSxLQUFLLENBQUMsMkRBQTJELENBQUMsQ0FBQztJQUMvRSxDQUFDO0lBQ0QsS0FBSyxNQUFNLEtBQUssSUFBSSxLQUFLLENBQUMsY0FBYyxDQUFDLFNBQVMsRUFBRSxDQUFDO1FBQ25ELElBQUksV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDdkIsTUFBTSxJQUFJLEtBQUssQ0FBQywwREFBMEQsQ0FBQyxDQUFDO1FBQzlFLENBQUM7SUFDSCxDQUFDO0lBQ0QsTUFBTSxlQUFlLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUM7SUFDdEQsSUFBSSxlQUFlLEtBQUssU0FBUyxJQUFJLFdBQVcsQ0FBQyxlQUFlLENBQUMsRUFBRSxDQUFDO1FBQ2xFLE1BQU0sSUFBSSxLQUFLLENBQUMscURBQXFELENBQUMsQ0FBQztJQUN6RSxDQUFDO0FBQ0gsQ0FBQyxDQUFDO0FBRUY7Ozs7Ozs7O0dBUUc7QUFDSCxNQUFhLDZCQUE4QixTQUFRLHNCQUFTOztJQUUxRDs7Ozs7Ozs7Ozs7T0FXRztJQUNILFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBeUM7UUFDakYsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNqQixhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFckIsTUFBTSxlQUFlLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUM7UUFDdEQsTUFBTSxXQUFXLEdBQUcsZUFBZSxLQUFLLFNBQVM7WUFDL0MsQ0FBQyxDQUFDLFNBQVM7WUFDWCxDQUFDLENBQUMsMkJBQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLEVBQUUsYUFBYSxFQUFFLGVBQWUsQ0FBQyxDQUFDO1FBRWxFLG1HQUFtRztRQUNuRywwQ0FBMEM7UUFDMUMsTUFBTSx3QkFBd0IsR0FBRyxJQUFJLHNEQUF3QixDQUFDLElBQUksRUFBRSwwQkFBMEIsRUFBRTtZQUM5RixXQUFXLEVBQUUsZ0ZBQWdGO1lBQzdGLFlBQVksRUFBRSxNQUFNLENBQUMsWUFBWSxDQUFDLE1BQU07WUFDeEMsT0FBTyxFQUFFLHNCQUFRLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUM3QixVQUFVLEVBQUUsR0FBRztZQUNmLGFBQWEsRUFBRSxDQUFDO1lBQ2hCLGFBQWEsRUFBRTtnQkFDYixnQkFBZ0IsRUFBRSxzQkFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0JBQ25DLGVBQWUsRUFBRSxzQkFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7YUFDbEM7WUFDRCxXQUFXLEVBQUUsV0FBVyxLQUFLLFNBQVM7Z0JBQ3BDLENBQUMsQ0FBQyxTQUFTO2dCQUNYLENBQUMsQ0FBQztvQkFDQSxpQkFBaUIsRUFBRSxXQUFXLENBQUMsVUFBVTtpQkFDMUM7WUFDSCwwRUFBMEU7WUFDMUUsZ0JBQWdCLEVBQUUsV0FBVyxLQUFLLFNBQVM7Z0JBQ3pDLENBQUMsQ0FBQyxTQUFTO2dCQUNYLENBQUMsQ0FBQyxNQUFNLENBQUMsNEJBQTRCLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxRQUFRLEVBQUU7b0JBQzFGLFNBQVMsRUFBRSxHQUFHO29CQUNkLFFBQVEsRUFBRSxNQUFNLENBQUMsd0JBQXdCLENBQUMsSUFBSTtpQkFDL0MsQ0FBQztZQUNKLElBQUksRUFBRSxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLDhCQUE4QixFQUFFO2dCQUN2RCxXQUFXLEVBQUUsZ0RBQWdEO2dCQUM3RCxTQUFTLEVBQUUsSUFBSSxHQUFHLENBQUMsZ0JBQWdCLENBQUMsc0JBQXNCLENBQUM7Z0JBQzNELGVBQWUsRUFBRTtvQkFDZixHQUFHLENBQUMsYUFBYSxDQUFDLHdCQUF3QixDQUFDLDBDQUEwQyxDQUFDO29CQUN0RixHQUFHLENBQUMsYUFBYSxDQUFDLHdCQUF3QixDQUFDLHVEQUF1RCxDQUFDO2lCQUNwRzthQUNGLENBQUM7WUFDRixRQUFRLEVBQUUsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxrQ0FBa0MsRUFBRTtnQkFDcEUsU0FBUyxFQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsWUFBWTtnQkFDMUMsMkZBQTJGO2dCQUMzRixhQUFhLEVBQUUsMkJBQWEsQ0FBQywwQkFBMEI7YUFDeEQsQ0FBQztZQUNGLGFBQWEsRUFBRSxNQUFNLENBQUMsYUFBYSxDQUFDLElBQUk7WUFDeEMsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLGNBQWMsQ0FBQyxJQUFJO1lBQzVDLHFCQUFxQixFQUFFLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJO1NBQ3ZELENBQUMsQ0FBQztRQUNILE1BQU0sbUJBQW1CLEdBQUc7WUFDMUIsbUJBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsU0FBUyxDQUFDO2dCQUN2QixPQUFPLEVBQUUsS0FBSztnQkFDZCxRQUFRLEVBQUUsSUFBSTtnQkFDZCxZQUFZLEVBQUUsR0FBRztnQkFDakIsU0FBUyxFQUFFLHVCQUFTLENBQUMsbUJBQW1CO2FBQ3pDLENBQUM7WUFDRixtQkFBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxTQUFTLENBQUM7Z0JBQ3ZCLE9BQU8sRUFBRSxLQUFLO2dCQUNkLFFBQVEsRUFBRSxTQUFTO2dCQUNuQixZQUFZLEVBQUUsR0FBRztnQkFDakIsU0FBUyxFQUFFLHVCQUFTLENBQUMsbUJBQW1CO2FBQ3pDLENBQUM7U0FDSCxDQUFDO1FBQ0Ysd0ZBQXdGO1FBQ3hGLHdCQUF3QixDQUFDLGVBQWUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxlQUFlLENBQUM7WUFDL0QsR0FBRyxFQUFFLHlCQUF5QjtZQUM5QixNQUFNLEVBQUUsR0FBRyxDQUFDLE1BQU0sQ0FBQyxLQUFLO1lBQ3hCLE9BQU8sRUFBRTtnQkFDUCx5QkFBeUI7Z0JBQ3pCLHdCQUF3QjthQUN6QjtZQUNELFNBQVMsRUFBRSxtQkFBbUI7U0FDL0IsQ0FBQyxDQUFDLENBQUM7UUFDSix1RUFBdUU7UUFDdkUsd0JBQXdCLENBQUMsZUFBZSxDQUFDLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztZQUMvRCxHQUFHLEVBQUUsc0JBQXNCO1lBQzNCLE1BQU0sRUFBRSxHQUFHLENBQUMsTUFBTSxDQUFDLEtBQUs7WUFDeEIsT0FBTyxFQUFFO2dCQUNQLG9CQUFvQjtnQkFDcEIsbUJBQW1CO2FBQ3BCO1lBQ0QsU0FBUyxFQUFFLG1CQUFtQjtZQUM5QixVQUFVLEVBQUU7Z0JBQ1YsWUFBWSxFQUFFO29CQUNaLENBQUMsbUJBQW1CLEtBQUssQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxLQUFLLENBQUMsY0FBYyxDQUFDLFNBQVM7aUJBQ25GO2FBQ0Y7U0FDRixDQUFDLENBQUMsQ0FBQztRQUNKLElBQUksV0FBVyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzlCLFdBQVcsQ0FBQyxTQUFTLENBQUMsd0JBQXdCLENBQUMsQ0FBQztRQUNsRCxDQUFDO1FBRUQscUZBQXFGO1FBQ3JGLE1BQU0sNkJBQTZCLEdBQUcsd0JBQXdCLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRWhGLE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxVQUFVLEtBQUssS0FBSyxDQUFDO1FBRTlDLCtFQUErRTtRQUMvRSxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsZUFBZSxDQUFDLFVBQVUsQ0FBQztZQUNwRCxLQUFLLEVBQUUsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDO1lBQ3RDLE1BQU0sRUFBRTtnQkFDTixNQUFNLEVBQUUsS0FBSyxDQUFDLGNBQWMsQ0FBQyxNQUFNO2dCQUNuQyxTQUFTLEVBQUUsS0FBSyxDQUFDLGNBQWMsQ0FBQyxTQUFTO2FBQzFDO1NBQ0YsQ0FBQyxDQUFDO1FBRUgscURBQXFEO1FBQ3JELElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsaUJBQWlCLEVBQUU7WUFDdkMsV0FBVyxFQUFFLDhDQUE4QztZQUMzRCxPQUFPLEVBQUUsVUFBVTtZQUNuQixZQUFZLEVBQUU7Z0JBQ1osTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDO2dCQUNuQixVQUFVLEVBQUUsQ0FBQyx1QkFBdUIsQ0FBQztnQkFDckMsTUFBTSxFQUFFO29CQUNOLE9BQU8sRUFBRSxDQUFDLGdCQUFnQixDQUFDO2lCQUM1QjthQUNGO1lBQ0QsT0FBTyxFQUFFO2dCQUNQLElBQUksT0FBTyxDQUFDLGNBQWMsQ0FBQyw2QkFBNkIsRUFBRTtvQkFDeEQsYUFBYSxFQUFFLENBQUM7b0JBQ2hCLEtBQUssRUFBRSxXQUFXO2lCQUNuQixDQUFDO2FBQ0g7U0FDRixDQUFDLENBQUM7UUFFSCxvREFBb0Q7UUFDcEQsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxnQkFBZ0IsRUFBRTtZQUN0QyxXQUFXLEVBQUUsd0NBQXdDO1lBQ3JELE9BQU8sRUFBRSxVQUFVO1lBQ25CLFlBQVksRUFBRTtnQkFDWixNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUM7Z0JBQ25CLFVBQVUsRUFBRSxDQUFDLHNCQUFzQixDQUFDO2dCQUNwQyxNQUFNLEVBQUU7b0JBQ04sT0FBTyxFQUFFLENBQUMsZ0JBQWdCLENBQUM7aUJBQzVCO2FBQ0Y7WUFDRCxPQUFPLEVBQUU7Z0JBQ1AsSUFBSSxPQUFPLENBQUMsY0FBYyxDQUFDLDZCQUE2QixFQUFFO29CQUN4RCxhQUFhLEVBQUUsQ0FBQztvQkFDaEIsS0FBSyxFQUFFLFdBQVc7aUJBQ25CLENBQUM7YUFDSDtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7O0FBOUpILHNFQStKQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEFybkZvcm1hdCwgRHVyYXRpb24sIFJlbW92YWxQb2xpY3ksIFN0YWNrLCBUb2tlbiB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCAqIGFzIGV2ZW50cyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZXZlbnRzJztcbmltcG9ydCAqIGFzIHRhcmdldHMgZnJvbSAnYXdzLWNkay1saWIvYXdzLWV2ZW50cy10YXJnZXRzJztcbmltcG9ydCAqIGFzIGlhbSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcbmltcG9ydCAqIGFzIGxhbWJkYSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCAqIGFzIGxvZ3MgZnJvbSAnYXdzLWNkay1saWIvYXdzLWxvZ3MnO1xuaW1wb3J0IHsgU2VjcmV0IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXNlY3JldHNtYW5hZ2VyJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0IHsgQXV0b1N0YXJ0UHJldmVudEZ1bmN0aW9uIH0gZnJvbSAnLi9mdW5jcy9hdXRvLXN0YXJ0LXByZXZlbnQtZnVuY3Rpb24nO1xuXG4vKipcbiAqIFRhZy1iYXNlZCBjcml0ZXJpYSBldmFsdWF0ZWQgaW4gdGhlIExhbWJkYSBoYW5kbGVyIChub3Qgb24gdGhlIEV2ZW50QnJpZGdlIHJ1bGUpLlxuICogUmVzb3VyY2VzIHdob3NlIHRhZyB2YWx1ZSBmb3Ige0BsaW5rIFRhcmdldFJlc291cmNlLnRhZ0tleX0gaXMgbm90IGluXG4gKiB7QGxpbmsgVGFyZ2V0UmVzb3VyY2UudGFnVmFsdWVzfSBhcmUgc2tpcHBlZCB3aXRob3V0IGNhbGxpbmcgU3RvcERCKi5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUYXJnZXRSZXNvdXJjZSB7XG4gIC8qKiBUYWcga2V5IHRvIG1hdGNoIChlLmcuIFwiRW52aXJvbm1lbnRcIiwgXCJBdXRvU3RhcnRQcmV2ZW50XCIpLiAqL1xuICByZWFkb25seSB0YWdLZXk6IHN0cmluZztcbiAgLyoqIFRhZyB2YWx1ZXMgdGhhdCBpbmRpY2F0ZSB0aGUgcmVzb3VyY2Ugc2hvdWxkIGJlIHByb3RlY3RlZCAoZS5nLiBbXCJwcm9kdWN0aW9uXCJdKS4gKi9cbiAgcmVhZG9ubHkgdGFnVmFsdWVzOiBzdHJpbmdbXTtcbn1cblxuLyoqXG4gKiBFeHRlcm5hbCBzZWNyZXRzIGZvciBub3RpZmljYXRpb25zIChlLmcuIFNsYWNrKS5cbiAqIE9taXQge0BsaW5rIFNlY3JldHMuc2xhY2tTZWNyZXROYW1lfSB0byBza2lwIFNsYWNrIG5vdGlmaWNhdGlvbnMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2VjcmV0cyB7XG4gIC8qKlxuICAgKiBOYW1lIG9mIHRoZSBTZWNyZXRzIE1hbmFnZXIgc2VjcmV0IGNvbnRhaW5pbmcgU2xhY2sgdG9rZW4gYW5kIGNoYW5uZWwuXG4gICAqIFdoZW4gb21pdHRlZCwgdGhlIGhhbmRsZXIgc3RvcHMgbWF0Y2hpbmcgUkRTIHJlc291cmNlcyBhbmQgZG9lcyBub3QgcG9zdCB0byBTbGFjay5cbiAgICovXG4gIHJlYWRvbmx5IHNsYWNrU2VjcmV0TmFtZT86IHN0cmluZztcbn1cblxuLyoqXG4gKiBQcm9wcyBmb3IgdGhlIFJEUyBkYXRhYmFzZSBhdXRvLXN0YXJ0IHByZXZlbnRlciBjb25zdHJ1Y3QuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUkRTRGF0YWJhc2VBdXRvU3RhcnRQcmV2ZW50ZXJQcm9wcyB7XG4gIC8qKiBUYWctYmFzZWQgdGFyZ2V0IHJlc291cmNlIGNyaXRlcmlhIGZvciBSRFMgaW5zdGFuY2VzL2NsdXN0ZXJzIHRvIHByb3RlY3QuICovXG4gIHJlYWRvbmx5IHRhcmdldFJlc291cmNlOiBUYXJnZXRSZXNvdXJjZTtcbiAgLyoqIFdoZXRoZXIgdGhlIEV2ZW50QnJpZGdlIHJ1bGVzIGFyZSBlbmFibGVkLiBEZWZhdWx0cyB0byB0cnVlIGlmIG9taXR0ZWQuICovXG4gIHJlYWRvbmx5IGVuYWJsZVJ1bGU/OiBib29sZWFuO1xuICAvKiogU2VjcmV0cyB1c2VkIGZvciBub3RpZmljYXRpb25zIChlLmcuIFNsYWNrKS4gKi9cbiAgcmVhZG9ubHkgc2VjcmV0czogU2VjcmV0cztcbn1cblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgd2hlbiBhIGNvbmNyZXRlIHN0cmluZyBpcyBlbXB0eSBvciB3aGl0ZXNwYWNlLW9ubHkuXG4gKiBVbnJlc29sdmVkIENESyB0b2tlbnMgYXJlIHRyZWF0ZWQgYXMgcHJlc2VudCBiZWNhdXNlIHRoZWlyIHZhbHVlIGlzIHVua25vd24gYXQgc3ludGhlc2lzLlxuICpcbiAqIEBwYXJhbSB2YWx1ZSAtIFByb3Agc3RyaW5nIHRvIGNoZWNrLlxuICogQHJldHVybnMgV2hldGhlciB0aGUgdmFsdWUgaXMgYmxhbmsuXG4gKi9cbmNvbnN0IGlzQmxhbmtUZXh0ID0gKHZhbHVlOiBzdHJpbmcpOiBib29sZWFuID0+XG4gICFUb2tlbi5pc1VucmVzb2x2ZWQodmFsdWUpICYmIHZhbHVlLnRyaW0oKS5sZW5ndGggPT09IDA7XG5cbi8qKlxuICogUmVqZWN0cyBlbXB0eSB0YWcgZmlsdGVycyBhbmQgU2xhY2sgc2VjcmV0IG5hbWVzIGJlZm9yZSByZXNvdXJjZXMgYXJlIGNyZWF0ZWQuXG4gKlxuICogQHBhcmFtIHByb3BzIC0gQ29uc3RydWN0IHByb3BzIHRvIHZhbGlkYXRlLlxuICogQHRocm93cyBXaGVuIHRhZ0tleSBvciBhIHRhZyB2YWx1ZSBpcyBibGFuaywgdGFnVmFsdWVzIGlzIGVtcHR5LCBvciBzbGFja1NlY3JldE5hbWUgaXMgcHJlc2VudCBidXQgYmxhbmsuXG4gKi9cbmNvbnN0IHZhbGlkYXRlUHJvcHMgPSAocHJvcHM6IFJEU0RhdGFiYXNlQXV0b1N0YXJ0UHJldmVudGVyUHJvcHMpOiB2b2lkID0+IHtcbiAgaWYgKGlzQmxhbmtUZXh0KHByb3BzLnRhcmdldFJlc291cmNlLnRhZ0tleSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3RhcmdldFJlc291cmNlLnRhZ0tleSBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZy4nKTtcbiAgfVxuICBpZiAocHJvcHMudGFyZ2V0UmVzb3VyY2UudGFnVmFsdWVzLmxlbmd0aCA9PT0gMCkge1xuICAgIHRocm93IG5ldyBFcnJvcigndGFyZ2V0UmVzb3VyY2UudGFnVmFsdWVzIG11c3QgY29udGFpbiBhdCBsZWFzdCBvbmUgdmFsdWUuJyk7XG4gIH1cbiAgZm9yIChjb25zdCB2YWx1ZSBvZiBwcm9wcy50YXJnZXRSZXNvdXJjZS50YWdWYWx1ZXMpIHtcbiAgICBpZiAoaXNCbGFua1RleHQodmFsdWUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3RhcmdldFJlc291cmNlLnRhZ1ZhbHVlcyBtdXN0IG5vdCBjb250YWluIGVtcHR5IHN0cmluZ3MuJyk7XG4gICAgfVxuICB9XG4gIGNvbnN0IHNsYWNrU2VjcmV0TmFtZSA9IHByb3BzLnNlY3JldHMuc2xhY2tTZWNyZXROYW1lO1xuICBpZiAoc2xhY2tTZWNyZXROYW1lICE9PSB1bmRlZmluZWQgJiYgaXNCbGFua1RleHQoc2xhY2tTZWNyZXROYW1lKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignc2VjcmV0cy5zbGFja1NlY3JldE5hbWUgbXVzdCBiZSBhIG5vbi1lbXB0eSBzdHJpbmcuJyk7XG4gIH1cbn07XG5cbi8qKlxuICogRGVwbG95cyBFdmVudEJyaWRnZSBydWxlcyBhbmQgYSBEdXJhYmxlIExhbWJkYSB0byBzdG9wIG1hdGNoaW5nIFJEUyByZXNvdXJjZXNcbiAqIGFmdGVyIGF1dG8tc3RhcnQgZXZlbnRzIChSRFMtRVZFTlQtMDE1NCAvIFJEUy1FVkVOVC0wMTUzKS5cbiAqXG4gKiBFdmVudEJyaWRnZSBydWxlcyBtYXRjaCBhbGwgYXV0by1zdGFydCBldmVudHM7IHRhZyBmaWx0ZXJpbmcgcnVucyBpbiB0aGUgaGFuZGxlclxuICogdXNpbmcgYFRhZ0xpc3RgIGZyb20gYHJkczpEZXNjcmliZURCSW5zdGFuY2VzYCAvIGByZHM6RGVzY3JpYmVEQkNsdXN0ZXJzYC5cbiAqIElBTSBncmFudHMgYWNjb3VudC9yZWdpb24tc2NvcGVkIGRlc2NyaWJlIG9uIGBkYmAgLyBgY2x1c3RlcmAgQVJOcywgYW5kIHN0b3Agb25seVxuICogd2hlbiBgYXdzOlJlc291cmNlVGFnYCBtYXRjaGVzIHtAbGluayBUYXJnZXRSZXNvdXJjZX0gKG5vIFJlc291cmNlIEdyb3VwcyBUYWdnaW5nIEFQSSkuXG4gKi9cbmV4cG9ydCBjbGFzcyBSRFNEYXRhYmFzZUF1dG9TdGFydFByZXZlbnRlciBleHRlbmRzIENvbnN0cnVjdCB7XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgdGhlIER1cmFibGUgTGFtYmRhLCBFdmVudEJyaWRnZSBydWxlcywgYW5kIElBTSBwb2xpY2llcy5cbiAgICpcbiAgICogVGhlIExhbWJkYSByZWNlaXZlcyBgeyBldmVudCwgcGFyYW1zIH1gIHdoZXJlIGBwYXJhbXNgIGNhcnJpZXMgYHRhZ0tleWAgYW5kIGB0YWdWYWx1ZXNgXG4gICAqIGZyb20ge0BsaW5rIFJEU0RhdGFiYXNlQXV0b1N0YXJ0UHJldmVudGVyUHJvcHMudGFyZ2V0UmVzb3VyY2V9LiBUYWcgZmlsdGVycyBhcmUgcGFzc2VkXG4gICAqIG9ubHkgdmlhIHRoYXQgcGF5bG9hZCAobm90IHZpYSBgVEFHX0tFWWAgLyBgVEFHX1ZBTFVFU2AgZW52aXJvbm1lbnQgdmFyaWFibGVzKS5cbiAgICpcbiAgICogQHBhcmFtIHNjb3BlIC0gUGFyZW50IGNvbnN0cnVjdC5cbiAgICogQHBhcmFtIGlkIC0gQ29uc3RydWN0IGlkLlxuICAgKiBAcGFyYW0gcHJvcHMgLSBUYXJnZXQgcmVzb3VyY2UgKHRhZyBrZXkvdmFsdWVzKSwgZW5hYmxlIHJ1bGUgZmxhZywgYW5kIHNlY3JldHMuXG4gICAqIEB0aHJvd3MgV2hlbiB0YWdLZXkgb3IgYSB0YWcgdmFsdWUgaXMgYmxhbmssIHRhZ1ZhbHVlcyBpcyBlbXB0eSwgb3Igc2xhY2tTZWNyZXROYW1lIGlzIHByZXNlbnQgYnV0IGJsYW5rLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IFJEU0RhdGFiYXNlQXV0b1N0YXJ0UHJldmVudGVyUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuICAgIHZhbGlkYXRlUHJvcHMocHJvcHMpO1xuXG4gICAgY29uc3Qgc2xhY2tTZWNyZXROYW1lID0gcHJvcHMuc2VjcmV0cy5zbGFja1NlY3JldE5hbWU7XG4gICAgY29uc3Qgc2xhY2tTZWNyZXQgPSBzbGFja1NlY3JldE5hbWUgPT09IHVuZGVmaW5lZFxuICAgICAgPyB1bmRlZmluZWRcbiAgICAgIDogU2VjcmV0LmZyb21TZWNyZXROYW1lVjIodGhpcywgJ1NsYWNrU2VjcmV0Jywgc2xhY2tTZWNyZXROYW1lKTtcblxuICAgIC8vIER1cmFibGUgRnVuY3Rpb25zLWJhc2VkIFJ1bm5pbmcgU2NoZWR1bGVyIChwcmV2aW91cyBTdGVwIEZ1bmN0aW9ucyBsb2dpYyBpbXBsZW1lbnRlZCBpbiBMYW1iZGEpLlxuICAgIC8vIER1cmFibGUgRXhlY3V0aW9uIHJlcXVpcmVzIE5vZGUuanMgMjIrLlxuICAgIGNvbnN0IGF1dG9TdGFydFByZXZlbnRGdW5jdGlvbiA9IG5ldyBBdXRvU3RhcnRQcmV2ZW50RnVuY3Rpb24odGhpcywgJ0F1dG9TdGFydFByZXZlbnRGdW5jdGlvbicsIHtcbiAgICAgIGRlc2NyaXB0aW9uOiAnQSBmdW5jdGlvbiB0byBwcmV2ZW50IHRoZSBSRFMgRGF0YWJhc2Ugb3IgQ2x1c3RlciBmcm9tIHN0YXJ0aW5nIGF1dG9tYXRpY2FsbHkuJyxcbiAgICAgIGFyY2hpdGVjdHVyZTogbGFtYmRhLkFyY2hpdGVjdHVyZS5BUk1fNjQsXG4gICAgICB0aW1lb3V0OiBEdXJhdGlvbi5taW51dGVzKDE1KSxcbiAgICAgIG1lbW9yeVNpemU6IDUxMixcbiAgICAgIHJldHJ5QXR0ZW1wdHM6IDIsXG4gICAgICBkdXJhYmxlQ29uZmlnOiB7XG4gICAgICAgIGV4ZWN1dGlvblRpbWVvdXQ6IER1cmF0aW9uLmhvdXJzKDIpLFxuICAgICAgICByZXRlbnRpb25QZXJpb2Q6IER1cmF0aW9uLmRheXMoMSksXG4gICAgICB9LFxuICAgICAgZW52aXJvbm1lbnQ6IHNsYWNrU2VjcmV0ID09PSB1bmRlZmluZWRcbiAgICAgICAgPyB1bmRlZmluZWRcbiAgICAgICAgOiB7XG4gICAgICAgICAgU0xBQ0tfU0VDUkVUX05BTUU6IHNsYWNrU2VjcmV0LnNlY3JldE5hbWUsXG4gICAgICAgIH0sXG4gICAgICAvLyBSZXF1aXJlZCBieSBhd3MtbGFtYmRhLXNlY3JldC1mZXRjaGVyICheMC42Kykgd2hlbiBTbGFjayBpcyBjb25maWd1cmVkLlxuICAgICAgcGFyYW1zQW5kU2VjcmV0czogc2xhY2tTZWNyZXQgPT09IHVuZGVmaW5lZFxuICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICA6IGxhbWJkYS5QYXJhbXNBbmRTZWNyZXRzTGF5ZXJWZXJzaW9uLmZyb21WZXJzaW9uKGxhbWJkYS5QYXJhbXNBbmRTZWNyZXRzVmVyc2lvbnMuVjFfMF8xMDMsIHtcbiAgICAgICAgICBjYWNoZVNpemU6IDUwMCxcbiAgICAgICAgICBsb2dMZXZlbDogbGFtYmRhLlBhcmFtc0FuZFNlY3JldHNMb2dMZXZlbC5JTkZPLFxuICAgICAgICB9KSxcbiAgICAgIHJvbGU6IG5ldyBpYW0uUm9sZSh0aGlzLCAnQXV0b1N0YXJ0UHJldmVudEZ1bmN0aW9uUm9sZScsIHtcbiAgICAgICAgZGVzY3JpcHRpb246ICdBIHJvbGUgdG8gY29udHJvbCB0aGUgUkRTIERhdGFiYXNlIG9yIENsdXN0ZXIuJyxcbiAgICAgICAgYXNzdW1lZEJ5OiBuZXcgaWFtLlNlcnZpY2VQcmluY2lwYWwoJ2xhbWJkYS5hbWF6b25hd3MuY29tJyksXG4gICAgICAgIG1hbmFnZWRQb2xpY2llczogW1xuICAgICAgICAgIGlhbS5NYW5hZ2VkUG9saWN5LmZyb21Bd3NNYW5hZ2VkUG9saWN5TmFtZSgnc2VydmljZS1yb2xlL0FXU0xhbWJkYUJhc2ljRXhlY3V0aW9uUm9sZScpLFxuICAgICAgICAgIGlhbS5NYW5hZ2VkUG9saWN5LmZyb21Bd3NNYW5hZ2VkUG9saWN5TmFtZSgnc2VydmljZS1yb2xlL0FXU0xhbWJkYUJhc2ljRHVyYWJsZUV4ZWN1dGlvblJvbGVQb2xpY3knKSxcbiAgICAgICAgXSxcbiAgICAgIH0pLFxuICAgICAgbG9nR3JvdXA6IG5ldyBsb2dzLkxvZ0dyb3VwKHRoaXMsICdBdXRvU3RhcnRQcmV2ZW50RnVuY3Rpb25Mb2dHcm91cCcsIHtcbiAgICAgICAgcmV0ZW50aW9uOiBsb2dzLlJldGVudGlvbkRheXMuVEhSRUVfTU9OVEhTLFxuICAgICAgICAvLyBEZWxldGUgb24gZmFpbGVkIGNyZWF0ZS9yb2xsYmFjazsgcmV0YWluIGFmdGVyIHN1Y2Nlc3NmdWwgY3JlYXRlIChzdGFjayBkZWxldGUvcmVwbGFjZSkuXG4gICAgICAgIHJlbW92YWxQb2xpY3k6IFJlbW92YWxQb2xpY3kuUkVUQUlOX09OX1VQREFURV9PUl9ERUxFVEUsXG4gICAgICB9KSxcbiAgICAgIGxvZ2dpbmdGb3JtYXQ6IGxhbWJkYS5Mb2dnaW5nRm9ybWF0LkpTT04sXG4gICAgICBzeXN0ZW1Mb2dMZXZlbFYyOiBsYW1iZGEuU3lzdGVtTG9nTGV2ZWwuSU5GTyxcbiAgICAgIGFwcGxpY2F0aW9uTG9nTGV2ZWxWMjogbGFtYmRhLkFwcGxpY2F0aW9uTG9nTGV2ZWwuSU5GTyxcbiAgICB9KTtcbiAgICBjb25zdCByZHNEYkFuZENsdXN0ZXJBcm5zID0gW1xuICAgICAgU3RhY2sub2YodGhpcykuZm9ybWF0QXJuKHtcbiAgICAgICAgc2VydmljZTogJ3JkcycsXG4gICAgICAgIHJlc291cmNlOiAnZGInLFxuICAgICAgICByZXNvdXJjZU5hbWU6ICcqJyxcbiAgICAgICAgYXJuRm9ybWF0OiBBcm5Gb3JtYXQuQ09MT05fUkVTT1VSQ0VfTkFNRSxcbiAgICAgIH0pLFxuICAgICAgU3RhY2sub2YodGhpcykuZm9ybWF0QXJuKHtcbiAgICAgICAgc2VydmljZTogJ3JkcycsXG4gICAgICAgIHJlc291cmNlOiAnY2x1c3RlcicsXG4gICAgICAgIHJlc291cmNlTmFtZTogJyonLFxuICAgICAgICBhcm5Gb3JtYXQ6IEFybkZvcm1hdC5DT0xPTl9SRVNPVVJDRV9OQU1FLFxuICAgICAgfSksXG4gICAgXTtcbiAgICAvLyBEZXNjcmliZSBzdGF5cyB3aXRob3V0IFJlc291cmNlVGFnIHNvIFRhZ0xpc3QgY2FuIGJlIHJlYWQgZm9yIG5vbi1tYXRjaGluZyByZXNvdXJjZXMuXG4gICAgYXV0b1N0YXJ0UHJldmVudEZ1bmN0aW9uLmFkZFRvUm9sZVBvbGljeShuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICBzaWQ6ICdSZHNEZXNjcmliZUZvclRhZ0ZpbHRlcicsXG4gICAgICBlZmZlY3Q6IGlhbS5FZmZlY3QuQUxMT1csXG4gICAgICBhY3Rpb25zOiBbXG4gICAgICAgICdyZHM6RGVzY3JpYmVEQkluc3RhbmNlcycsXG4gICAgICAgICdyZHM6RGVzY3JpYmVEQkNsdXN0ZXJzJyxcbiAgICAgIF0sXG4gICAgICByZXNvdXJjZXM6IHJkc0RiQW5kQ2x1c3RlckFybnMsXG4gICAgfSkpO1xuICAgIC8vIFN0b3AgaXMgbGltaXRlZCB0byBkYi9jbHVzdGVyIEFSTnMgd2hvc2UgdGFnIG1hdGNoZXMgdGFyZ2V0UmVzb3VyY2UuXG4gICAgYXV0b1N0YXJ0UHJldmVudEZ1bmN0aW9uLmFkZFRvUm9sZVBvbGljeShuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICBzaWQ6ICdSZHNTdG9wVGFnZ2VkVGFyZ2V0cycsXG4gICAgICBlZmZlY3Q6IGlhbS5FZmZlY3QuQUxMT1csXG4gICAgICBhY3Rpb25zOiBbXG4gICAgICAgICdyZHM6U3RvcERCSW5zdGFuY2UnLFxuICAgICAgICAncmRzOlN0b3BEQkNsdXN0ZXInLFxuICAgICAgXSxcbiAgICAgIHJlc291cmNlczogcmRzRGJBbmRDbHVzdGVyQXJucyxcbiAgICAgIGNvbmRpdGlvbnM6IHtcbiAgICAgICAgU3RyaW5nRXF1YWxzOiB7XG4gICAgICAgICAgW2Bhd3M6UmVzb3VyY2VUYWcvJHtwcm9wcy50YXJnZXRSZXNvdXJjZS50YWdLZXl9YF06IHByb3BzLnRhcmdldFJlc291cmNlLnRhZ1ZhbHVlcyxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSkpO1xuICAgIGlmIChzbGFja1NlY3JldCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBzbGFja1NlY3JldC5ncmFudFJlYWQoYXV0b1N0YXJ0UHJldmVudEZ1bmN0aW9uKTtcbiAgICB9XG5cbiAgICAvLyBTZWU6IGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9sYW1iZGEvbGF0ZXN0L2RnL2R1cmFibGUtZ2V0dGluZy1zdGFydGVkLWlhYy5odG1sXG4gICAgY29uc3QgYXV0b1N0YXJ0UHJldmVudEZ1bmN0aW9uQWxpYXMgPSBhdXRvU3RhcnRQcmV2ZW50RnVuY3Rpb24uYWRkQWxpYXMoJ2xpdmUnKTtcblxuICAgIGNvbnN0IGVuYWJsZVJ1bGUgPSBwcm9wcy5lbmFibGVSdWxlICE9PSBmYWxzZTtcblxuICAgIC8vIFBhc3MgdGhlIEV2ZW50QnJpZGdlIGV2ZW50IGFuZCB0YWcgZmlsdGVyIHBhcmFtcyB0byB0aGUgTGFtYmRhIHRhcmdldCBpbnB1dC5cbiAgICBjb25zdCBsYW1iZGFJbnB1dCA9IGV2ZW50cy5SdWxlVGFyZ2V0SW5wdXQuZnJvbU9iamVjdCh7XG4gICAgICBldmVudDogZXZlbnRzLkV2ZW50RmllbGQuZnJvbVBhdGgoJyQnKSxcbiAgICAgIHBhcmFtczoge1xuICAgICAgICB0YWdLZXk6IHByb3BzLnRhcmdldFJlc291cmNlLnRhZ0tleSxcbiAgICAgICAgdGFnVmFsdWVzOiBwcm9wcy50YXJnZXRSZXNvdXJjZS50YWdWYWx1ZXMsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8g8J+RhyBFdmVudEJyaWRnZSBieSBSRFMgREIgSW5zdGFuY2UgYXV0byBzdGFydCBldmVudFxuICAgIG5ldyBldmVudHMuUnVsZSh0aGlzLCAnREJJbnN0YW5jZUV2ZW50Jywge1xuICAgICAgZGVzY3JpcHRpb246ICdyZHMgZGIgaW5zdGFuY2UgYXV0byBzdGFydCBldmVudCBjYXRjaCBydWxlLicsXG4gICAgICBlbmFibGVkOiBlbmFibGVSdWxlLFxuICAgICAgZXZlbnRQYXR0ZXJuOiB7XG4gICAgICAgIHNvdXJjZTogWydhd3MucmRzJ10sXG4gICAgICAgIGRldGFpbFR5cGU6IFsnUkRTIERCIEluc3RhbmNlIEV2ZW50J10sXG4gICAgICAgIGRldGFpbDoge1xuICAgICAgICAgIEV2ZW50SUQ6IFsnUkRTLUVWRU5ULTAxNTQnXSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICB0YXJnZXRzOiBbXG4gICAgICAgIG5ldyB0YXJnZXRzLkxhbWJkYUZ1bmN0aW9uKGF1dG9TdGFydFByZXZlbnRGdW5jdGlvbkFsaWFzLCB7XG4gICAgICAgICAgcmV0cnlBdHRlbXB0czogMixcbiAgICAgICAgICBldmVudDogbGFtYmRhSW5wdXQsXG4gICAgICAgIH0pLFxuICAgICAgXSxcbiAgICB9KTtcblxuICAgIC8vIPCfkYcgRXZlbnRCcmlkZ2UgYnkgUkRTIERCIENsdXN0ZXIgYXV0byBzdGFydCBldmVudFxuICAgIG5ldyBldmVudHMuUnVsZSh0aGlzLCAnREJDbHVzdGVyRXZlbnQnLCB7XG4gICAgICBkZXNjcmlwdGlvbjogJ2RiIGNsdXN0ZXIgYXV0byBzdGFydCBldmVudCBjYXRjaCBydWxlJyxcbiAgICAgIGVuYWJsZWQ6IGVuYWJsZVJ1bGUsXG4gICAgICBldmVudFBhdHRlcm46IHtcbiAgICAgICAgc291cmNlOiBbJ2F3cy5yZHMnXSxcbiAgICAgICAgZGV0YWlsVHlwZTogWydSRFMgREIgQ2x1c3RlciBFdmVudCddLFxuICAgICAgICBkZXRhaWw6IHtcbiAgICAgICAgICBFdmVudElEOiBbJ1JEUy1FVkVOVC0wMTUzJ10sXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgICAgdGFyZ2V0czogW1xuICAgICAgICBuZXcgdGFyZ2V0cy5MYW1iZGFGdW5jdGlvbihhdXRvU3RhcnRQcmV2ZW50RnVuY3Rpb25BbGlhcywge1xuICAgICAgICAgIHJldHJ5QXR0ZW1wdHM6IDIsXG4gICAgICAgICAgZXZlbnQ6IGxhbWJkYUlucHV0LFxuICAgICAgICB9KSxcbiAgICAgIF0sXG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rds-database-auto-start-preventer",
3
- "description": "CDK stack that stops RDS DB instances and clusters after they are auto-started by AWS (RDS-EVENT-0154 / RDS-EVENT-0153). It uses EventBridge rules and a Durable Lambda to detect auto-start events, optionally filter by tags, stop the resource if it matches, and post a notification to Slack.",
3
+ "description": "CDK construct library that stops RDS DB instances and clusters after they are auto-started by AWS (RDS-EVENT-0154 / RDS-EVENT-0153), using EventBridge and a Durable Lambda to filter by tags in the handler, stop matching available resources, and post to Slack only after a successful stop when a Slack secret name is set.",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/gammarers-aws-cdk-constructs/rds-database-auto-start-preventer.git"
@@ -39,11 +39,9 @@
39
39
  "organization": false
40
40
  },
41
41
  "devDependencies": {
42
- "@aws-sdk/client-lambda": "^3.1063.0",
43
42
  "@aws-sdk/client-rds": "^3.1063.0",
44
- "@aws-sdk/client-resource-groups-tagging-api": "^3.1063.0",
45
43
  "@aws/durable-execution-sdk-js": "^1.1.7",
46
- "@gammarers/projen-projects": "^0.3.1",
44
+ "@gammarers/projen-projects": "^0.5.10",
47
45
  "@slack/web-api": "^6.13.0",
48
46
  "@stylistic/eslint-plugin": "^2",
49
47
  "@types/aws-lambda": "^8.10.162",
@@ -99,7 +97,7 @@
99
97
  "publishConfig": {
100
98
  "access": "public"
101
99
  },
102
- "version": "0.4.4",
100
+ "version": "0.5.0",
103
101
  "jest": {
104
102
  "coverageProvider": "v8",
105
103
  "testMatch": [
@@ -1,212 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.RDSDatabaseAutoStartPreventer = void 0;
37
- const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
38
- const aws_cdk_lib_1 = require("aws-cdk-lib");
39
- const events = __importStar(require("aws-cdk-lib/aws-events"));
40
- const targets = __importStar(require("aws-cdk-lib/aws-events-targets"));
41
- const iam = __importStar(require("aws-cdk-lib/aws-iam"));
42
- const lambda = __importStar(require("aws-cdk-lib/aws-lambda"));
43
- const logs = __importStar(require("aws-cdk-lib/aws-logs"));
44
- const aws_secretsmanager_1 = require("aws-cdk-lib/aws-secretsmanager");
45
- const constructs_1 = require("constructs");
46
- const auto_start_prevent_function_1 = require("../funcs/auto-start-prevent-function");
47
- /**
48
- * Returns true when a concrete string is empty or whitespace-only.
49
- * Unresolved CDK tokens are treated as present because their value is unknown at synthesis.
50
- *
51
- * @param value - Prop string to check.
52
- * @returns Whether the value is blank.
53
- */
54
- const isBlankText = (value) => !aws_cdk_lib_1.Token.isUnresolved(value) && value.trim().length === 0;
55
- /**
56
- * Rejects empty tag filters and Slack secret names before resources are created.
57
- *
58
- * @param props - Construct props to validate.
59
- * @throws When tagKey or a tag value is blank, tagValues is empty, or slackSecretName is present but blank.
60
- */
61
- const validateProps = (props) => {
62
- if (isBlankText(props.targetResource.tagKey)) {
63
- throw new Error('targetResource.tagKey must be a non-empty string.');
64
- }
65
- if (props.targetResource.tagValues.length === 0) {
66
- throw new Error('targetResource.tagValues must contain at least one value.');
67
- }
68
- for (const value of props.targetResource.tagValues) {
69
- if (isBlankText(value)) {
70
- throw new Error('targetResource.tagValues must not contain empty strings.');
71
- }
72
- }
73
- const slackSecretName = props.secrets.slackSecretName;
74
- if (slackSecretName !== undefined && isBlankText(slackSecretName)) {
75
- throw new Error('secrets.slackSecretName must be a non-empty string.');
76
- }
77
- };
78
- /**
79
- * Deploys EventBridge rules and a Durable Lambda to stop matching RDS resources
80
- * after auto-start events (RDS-EVENT-0154 / RDS-EVENT-0153).
81
- *
82
- * EventBridge rules match all auto-start events; tag filtering runs in the handler
83
- * using `TagList` from `rds:DescribeDBInstances` / `rds:DescribeDBClusters`.
84
- * IAM grants RDS describe and stop actions only (no Resource Groups Tagging API).
85
- */
86
- class RDSDatabaseAutoStartPreventer extends constructs_1.Construct {
87
- static [JSII_RTTI_SYMBOL_1] = { fqn: "rds-database-auto-start-preventer.RDSDatabaseAutoStartPreventer", version: "0.4.4" };
88
- /**
89
- * Creates the Durable Lambda, EventBridge rules, and IAM policies.
90
- *
91
- * The Lambda receives `{ event, params }` where `params` carries `tagKey` and `tagValues`
92
- * from {@link RDSDatabaseAutoStartPreventerProps.targetResource}. Tag filters are passed
93
- * only via that payload (not via `TAG_KEY` / `TAG_VALUES` environment variables).
94
- *
95
- * @param scope - Parent construct.
96
- * @param id - Construct id.
97
- * @param props - Target resource (tag key/values), enable rule flag, and secrets.
98
- * @throws When tagKey or a tag value is blank, tagValues is empty, or slackSecretName is present but blank.
99
- */
100
- constructor(scope, id, props) {
101
- super(scope, id);
102
- validateProps(props);
103
- const slackSecretName = props.secrets.slackSecretName;
104
- const slackSecret = slackSecretName === undefined
105
- ? undefined
106
- : aws_secretsmanager_1.Secret.fromSecretNameV2(this, 'SlackSecret', slackSecretName);
107
- // Durable Functions-based Running Scheduler (previous Step Functions logic implemented in Lambda).
108
- // Durable Execution requires Node.js 22+.
109
- const autoStartPreventFunction = new auto_start_prevent_function_1.AutoStartPreventFunction(this, 'AutoStartPreventFunction', {
110
- description: 'A function to prevent the RDS Database or Cluster from starting automatically.',
111
- architecture: lambda.Architecture.ARM_64,
112
- timeout: aws_cdk_lib_1.Duration.minutes(15),
113
- memorySize: 512,
114
- retryAttempts: 2,
115
- durableConfig: {
116
- executionTimeout: aws_cdk_lib_1.Duration.hours(2),
117
- retentionPeriod: aws_cdk_lib_1.Duration.days(1),
118
- },
119
- environment: slackSecret === undefined
120
- ? undefined
121
- : {
122
- SLACK_SECRET_NAME: slackSecret.secretName,
123
- },
124
- // Required by aws-lambda-secret-fetcher (^0.6+) when Slack is configured.
125
- paramsAndSecrets: slackSecret === undefined
126
- ? undefined
127
- : lambda.ParamsAndSecretsLayerVersion.fromVersion(lambda.ParamsAndSecretsVersions.V1_0_103, {
128
- cacheSize: 500,
129
- logLevel: lambda.ParamsAndSecretsLogLevel.INFO,
130
- }),
131
- role: new iam.Role(this, 'AutoStartPreventFunctionRole', {
132
- description: 'A role to control the RDS Database or Cluster.',
133
- assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
134
- managedPolicies: [
135
- iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
136
- iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicDurableExecutionRolePolicy'),
137
- ],
138
- }),
139
- logGroup: new logs.LogGroup(this, 'AutoStartPreventFunctionLogGroup', {
140
- retention: logs.RetentionDays.THREE_MONTHS,
141
- removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
142
- }),
143
- loggingFormat: lambda.LoggingFormat.JSON,
144
- systemLogLevelV2: lambda.SystemLogLevel.INFO,
145
- applicationLogLevelV2: lambda.ApplicationLogLevel.INFO,
146
- });
147
- // Grant RDS describe (status and TagList) and stop permissions to the handler role.
148
- autoStartPreventFunction.addToRolePolicy(new iam.PolicyStatement({
149
- sid: 'RdsAutoStartControl',
150
- effect: iam.Effect.ALLOW,
151
- actions: [
152
- 'rds:DescribeDBInstances',
153
- 'rds:DescribeDBClusters',
154
- 'rds:StopDBInstance',
155
- 'rds:StopDBCluster',
156
- ],
157
- resources: ['*'],
158
- }));
159
- if (slackSecret !== undefined) {
160
- slackSecret.grantRead(autoStartPreventFunction);
161
- }
162
- // See: https://docs.aws.amazon.com/lambda/latest/dg/durable-getting-started-iac.html
163
- const autoStartPreventFunctionAlias = autoStartPreventFunction.addAlias('live');
164
- const enableRule = props.enableRule !== false;
165
- // Pass the EventBridge event and tag filter params to the Lambda target input.
166
- const lambdaInput = events.RuleTargetInput.fromObject({
167
- event: events.EventField.fromPath('$'),
168
- params: {
169
- tagKey: props.targetResource.tagKey,
170
- tagValues: props.targetResource.tagValues,
171
- },
172
- });
173
- // 👇 EventBridge by RDS DB Instance auto start event
174
- new events.Rule(this, 'DBInstanceEvent', {
175
- description: 'rds db instance auto start event catch rule.',
176
- enabled: enableRule,
177
- eventPattern: {
178
- source: ['aws.rds'],
179
- detailType: ['RDS DB Instance Event'],
180
- detail: {
181
- EventID: ['RDS-EVENT-0154'],
182
- },
183
- },
184
- targets: [
185
- new targets.LambdaFunction(autoStartPreventFunctionAlias, {
186
- retryAttempts: 2,
187
- event: lambdaInput,
188
- }),
189
- ],
190
- });
191
- // 👇 EventBridge by RDS DB Cluster auto start event
192
- new events.Rule(this, 'DBClusterEvent', {
193
- description: 'db cluster auto start event catch rule',
194
- enabled: enableRule,
195
- eventPattern: {
196
- source: ['aws.rds'],
197
- detailType: ['RDS DB Cluster Event'],
198
- detail: {
199
- EventID: ['RDS-EVENT-0153'],
200
- },
201
- },
202
- targets: [
203
- new targets.LambdaFunction(autoStartPreventFunctionAlias, {
204
- retryAttempts: 2,
205
- event: lambdaInput,
206
- }),
207
- ],
208
- });
209
- }
210
- }
211
- exports.RDSDatabaseAutoStartPreventer = RDSDatabaseAutoStartPreventer;
212
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmRzLWRhdGFiYXNlLWF1dG8tc3RhcnQtcHJldmVudGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbnN0cnVjdHMvcmRzLWRhdGFiYXNlLWF1dG8tc3RhcnQtcHJldmVudGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSw2Q0FBNkQ7QUFDN0QsK0RBQWlEO0FBQ2pELHdFQUEwRDtBQUMxRCx5REFBMkM7QUFDM0MsK0RBQWlEO0FBQ2pELDJEQUE2QztBQUM3Qyx1RUFBd0Q7QUFDeEQsMkNBQXVDO0FBQ3ZDLHNGQUFnRjtBQXNDaEY7Ozs7OztHQU1HO0FBQ0gsTUFBTSxXQUFXLEdBQUcsQ0FBQyxLQUFhLEVBQVcsRUFBRSxDQUM3QyxDQUFDLG1CQUFLLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEtBQUssQ0FBQyxDQUFDO0FBRTFEOzs7OztHQUtHO0FBQ0gsTUFBTSxhQUFhLEdBQUcsQ0FBQyxLQUF5QyxFQUFRLEVBQUU7SUFDeEUsSUFBSSxXQUFXLENBQUMsS0FBSyxDQUFDLGNBQWMsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQzdDLE1BQU0sSUFBSSxLQUFLLENBQUMsbURBQW1ELENBQUMsQ0FBQztJQUN2RSxDQUFDO0lBQ0QsSUFBSSxLQUFLLENBQUMsY0FBYyxDQUFDLFNBQVMsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDaEQsTUFBTSxJQUFJLEtBQUssQ0FBQywyREFBMkQsQ0FBQyxDQUFDO0lBQy9FLENBQUM7SUFDRCxLQUFLLE1BQU0sS0FBSyxJQUFJLEtBQUssQ0FBQyxjQUFjLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDbkQsSUFBSSxXQUFXLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUN2QixNQUFNLElBQUksS0FBSyxDQUFDLDBEQUEwRCxDQUFDLENBQUM7UUFDOUUsQ0FBQztJQUNILENBQUM7SUFDRCxNQUFNLGVBQWUsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQztJQUN0RCxJQUFJLGVBQWUsS0FBSyxTQUFTLElBQUksV0FBVyxDQUFDLGVBQWUsQ0FBQyxFQUFFLENBQUM7UUFDbEUsTUFBTSxJQUFJLEtBQUssQ0FBQyxxREFBcUQsQ0FBQyxDQUFDO0lBQ3pFLENBQUM7QUFDSCxDQUFDLENBQUM7QUFFRjs7Ozs7OztHQU9HO0FBQ0gsTUFBYSw2QkFBOEIsU0FBUSxzQkFBUzs7SUFFMUQ7Ozs7Ozs7Ozs7O09BV0c7SUFDSCxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQXlDO1FBQ2pGLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDakIsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBRXJCLE1BQU0sZUFBZSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDO1FBQ3RELE1BQU0sV0FBVyxHQUFHLGVBQWUsS0FBSyxTQUFTO1lBQy9DLENBQUMsQ0FBQyxTQUFTO1lBQ1gsQ0FBQyxDQUFDLDJCQUFNLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLGFBQWEsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUVsRSxtR0FBbUc7UUFDbkcsMENBQTBDO1FBQzFDLE1BQU0sd0JBQXdCLEdBQUcsSUFBSSxzREFBd0IsQ0FBQyxJQUFJLEVBQUUsMEJBQTBCLEVBQUU7WUFDOUYsV0FBVyxFQUFFLGdGQUFnRjtZQUM3RixZQUFZLEVBQUUsTUFBTSxDQUFDLFlBQVksQ0FBQyxNQUFNO1lBQ3hDLE9BQU8sRUFBRSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDN0IsVUFBVSxFQUFFLEdBQUc7WUFDZixhQUFhLEVBQUUsQ0FBQztZQUNoQixhQUFhLEVBQUU7Z0JBQ2IsZ0JBQWdCLEVBQUUsc0JBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO2dCQUNuQyxlQUFlLEVBQUUsc0JBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2FBQ2xDO1lBQ0QsV0FBVyxFQUFFLFdBQVcsS0FBSyxTQUFTO2dCQUNwQyxDQUFDLENBQUMsU0FBUztnQkFDWCxDQUFDLENBQUM7b0JBQ0EsaUJBQWlCLEVBQUUsV0FBVyxDQUFDLFVBQVU7aUJBQzFDO1lBQ0gsMEVBQTBFO1lBQzFFLGdCQUFnQixFQUFFLFdBQVcsS0FBSyxTQUFTO2dCQUN6QyxDQUFDLENBQUMsU0FBUztnQkFDWCxDQUFDLENBQUMsTUFBTSxDQUFDLDRCQUE0QixDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsd0JBQXdCLENBQUMsUUFBUSxFQUFFO29CQUMxRixTQUFTLEVBQUUsR0FBRztvQkFDZCxRQUFRLEVBQUUsTUFBTSxDQUFDLHdCQUF3QixDQUFDLElBQUk7aUJBQy9DLENBQUM7WUFDSixJQUFJLEVBQUUsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSw4QkFBOEIsRUFBRTtnQkFDdkQsV0FBVyxFQUFFLGdEQUFnRDtnQkFDN0QsU0FBUyxFQUFFLElBQUksR0FBRyxDQUFDLGdCQUFnQixDQUFDLHNCQUFzQixDQUFDO2dCQUMzRCxlQUFlLEVBQUU7b0JBQ2YsR0FBRyxDQUFDLGFBQWEsQ0FBQyx3QkFBd0IsQ0FBQywwQ0FBMEMsQ0FBQztvQkFDdEYsR0FBRyxDQUFDLGFBQWEsQ0FBQyx3QkFBd0IsQ0FBQyx1REFBdUQsQ0FBQztpQkFDcEc7YUFDRixDQUFDO1lBQ0YsUUFBUSxFQUFFLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsa0NBQWtDLEVBQUU7Z0JBQ3BFLFNBQVMsRUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLFlBQVk7Z0JBQzFDLGFBQWEsRUFBRSwyQkFBYSxDQUFDLE9BQU87YUFDckMsQ0FBQztZQUNGLGFBQWEsRUFBRSxNQUFNLENBQUMsYUFBYSxDQUFDLElBQUk7WUFDeEMsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLGNBQWMsQ0FBQyxJQUFJO1lBQzVDLHFCQUFxQixFQUFFLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJO1NBQ3ZELENBQUMsQ0FBQztRQUNILG9GQUFvRjtRQUNwRix3QkFBd0IsQ0FBQyxlQUFlLENBQUMsSUFBSSxHQUFHLENBQUMsZUFBZSxDQUFDO1lBQy9ELEdBQUcsRUFBRSxxQkFBcUI7WUFDMUIsTUFBTSxFQUFFLEdBQUcsQ0FBQyxNQUFNLENBQUMsS0FBSztZQUN4QixPQUFPLEVBQUU7Z0JBQ1AseUJBQXlCO2dCQUN6Qix3QkFBd0I7Z0JBQ3hCLG9CQUFvQjtnQkFDcEIsbUJBQW1CO2FBQ3BCO1lBQ0QsU0FBUyxFQUFFLENBQUMsR0FBRyxDQUFDO1NBQ2pCLENBQUMsQ0FBQyxDQUFDO1FBQ0osSUFBSSxXQUFXLEtBQUssU0FBUyxFQUFFLENBQUM7WUFDOUIsV0FBVyxDQUFDLFNBQVMsQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO1FBQ2xELENBQUM7UUFFRCxxRkFBcUY7UUFDckYsTUFBTSw2QkFBNkIsR0FBRyx3QkFBd0IsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFaEYsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLFVBQVUsS0FBSyxLQUFLLENBQUM7UUFFOUMsK0VBQStFO1FBQy9FLE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxlQUFlLENBQUMsVUFBVSxDQUFDO1lBQ3BELEtBQUssRUFBRSxNQUFNLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUM7WUFDdEMsTUFBTSxFQUFFO2dCQUNOLE1BQU0sRUFBRSxLQUFLLENBQUMsY0FBYyxDQUFDLE1BQU07Z0JBQ25DLFNBQVMsRUFBRSxLQUFLLENBQUMsY0FBYyxDQUFDLFNBQVM7YUFDMUM7U0FDRixDQUFDLENBQUM7UUFFSCxxREFBcUQ7UUFDckQsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxpQkFBaUIsRUFBRTtZQUN2QyxXQUFXLEVBQUUsOENBQThDO1lBQzNELE9BQU8sRUFBRSxVQUFVO1lBQ25CLFlBQVksRUFBRTtnQkFDWixNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUM7Z0JBQ25CLFVBQVUsRUFBRSxDQUFDLHVCQUF1QixDQUFDO2dCQUNyQyxNQUFNLEVBQUU7b0JBQ04sT0FBTyxFQUFFLENBQUMsZ0JBQWdCLENBQUM7aUJBQzVCO2FBQ0Y7WUFDRCxPQUFPLEVBQUU7Z0JBQ1AsSUFBSSxPQUFPLENBQUMsY0FBYyxDQUFDLDZCQUE2QixFQUFFO29CQUN4RCxhQUFhLEVBQUUsQ0FBQztvQkFDaEIsS0FBSyxFQUFFLFdBQVc7aUJBQ25CLENBQUM7YUFDSDtTQUNGLENBQUMsQ0FBQztRQUVILG9EQUFvRDtRQUNwRCxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLGdCQUFnQixFQUFFO1lBQ3RDLFdBQVcsRUFBRSx3Q0FBd0M7WUFDckQsT0FBTyxFQUFFLFVBQVU7WUFDbkIsWUFBWSxFQUFFO2dCQUNaLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQztnQkFDbkIsVUFBVSxFQUFFLENBQUMsc0JBQXNCLENBQUM7Z0JBQ3BDLE1BQU0sRUFBRTtvQkFDTixPQUFPLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQztpQkFDNUI7YUFDRjtZQUNELE9BQU8sRUFBRTtnQkFDUCxJQUFJLE9BQU8sQ0FBQyxjQUFjLENBQUMsNkJBQTZCLEVBQUU7b0JBQ3hELGFBQWEsRUFBRSxDQUFDO29CQUNoQixLQUFLLEVBQUUsV0FBVztpQkFDbkIsQ0FBQzthQUNIO1NBQ0YsQ0FBQyxDQUFDO0lBQ0wsQ0FBQzs7QUFsSUgsc0VBbUlDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRHVyYXRpb24sIFJlbW92YWxQb2xpY3ksIFRva2VuIH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0ICogYXMgZXZlbnRzIGZyb20gJ2F3cy1jZGstbGliL2F3cy1ldmVudHMnO1xuaW1wb3J0ICogYXMgdGFyZ2V0cyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZXZlbnRzLXRhcmdldHMnO1xuaW1wb3J0ICogYXMgaWFtIGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0ICogYXMgbGFtYmRhIGZyb20gJ2F3cy1jZGstbGliL2F3cy1sYW1iZGEnO1xuaW1wb3J0ICogYXMgbG9ncyBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbG9ncyc7XG5pbXBvcnQgeyBTZWNyZXQgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc2VjcmV0c21hbmFnZXInO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgeyBBdXRvU3RhcnRQcmV2ZW50RnVuY3Rpb24gfSBmcm9tICcuLi9mdW5jcy9hdXRvLXN0YXJ0LXByZXZlbnQtZnVuY3Rpb24nO1xuXG4vKipcbiAqIFRhZy1iYXNlZCBjcml0ZXJpYSBldmFsdWF0ZWQgaW4gdGhlIExhbWJkYSBoYW5kbGVyIChub3Qgb24gdGhlIEV2ZW50QnJpZGdlIHJ1bGUpLlxuICogUmVzb3VyY2VzIHdob3NlIHRhZyB2YWx1ZSBmb3Ige0BsaW5rIFRhcmdldFJlc291cmNlLnRhZ0tleX0gaXMgbm90IGluXG4gKiB7QGxpbmsgVGFyZ2V0UmVzb3VyY2UudGFnVmFsdWVzfSBhcmUgc2tpcHBlZCB3aXRob3V0IGNhbGxpbmcgU3RvcERCKi5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUYXJnZXRSZXNvdXJjZSB7XG4gIC8qKiBUYWcga2V5IHRvIG1hdGNoIChlLmcuIFwiRW52aXJvbm1lbnRcIiwgXCJBdXRvU3RhcnRQcmV2ZW50XCIpLiAqL1xuICByZWFkb25seSB0YWdLZXk6IHN0cmluZztcbiAgLyoqIFRhZyB2YWx1ZXMgdGhhdCBpbmRpY2F0ZSB0aGUgcmVzb3VyY2Ugc2hvdWxkIGJlIHByb3RlY3RlZCAoZS5nLiBbXCJwcm9kdWN0aW9uXCJdKS4gKi9cbiAgcmVhZG9ubHkgdGFnVmFsdWVzOiBzdHJpbmdbXTtcbn1cblxuLyoqXG4gKiBFeHRlcm5hbCBzZWNyZXRzIGZvciBub3RpZmljYXRpb25zIChlLmcuIFNsYWNrKS5cbiAqIE9taXQge0BsaW5rIFNlY3JldHMuc2xhY2tTZWNyZXROYW1lfSB0byBza2lwIFNsYWNrIG5vdGlmaWNhdGlvbnMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2VjcmV0cyB7XG4gIC8qKlxuICAgKiBOYW1lIG9mIHRoZSBTZWNyZXRzIE1hbmFnZXIgc2VjcmV0IGNvbnRhaW5pbmcgU2xhY2sgdG9rZW4gYW5kIGNoYW5uZWwuXG4gICAqIFdoZW4gb21pdHRlZCwgdGhlIGhhbmRsZXIgc3RvcHMgbWF0Y2hpbmcgUkRTIHJlc291cmNlcyBhbmQgZG9lcyBub3QgcG9zdCB0byBTbGFjay5cbiAgICovXG4gIHJlYWRvbmx5IHNsYWNrU2VjcmV0TmFtZT86IHN0cmluZztcbn1cblxuLyoqXG4gKiBQcm9wcyBmb3IgdGhlIFJEUyBkYXRhYmFzZSBhdXRvLXN0YXJ0IHByZXZlbnRlciBjb25zdHJ1Y3QuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUkRTRGF0YWJhc2VBdXRvU3RhcnRQcmV2ZW50ZXJQcm9wcyB7XG4gIC8qKiBUYWctYmFzZWQgdGFyZ2V0IHJlc291cmNlIGNyaXRlcmlhIGZvciBSRFMgaW5zdGFuY2VzL2NsdXN0ZXJzIHRvIHByb3RlY3QuICovXG4gIHJlYWRvbmx5IHRhcmdldFJlc291cmNlOiBUYXJnZXRSZXNvdXJjZTtcbiAgLyoqIFdoZXRoZXIgdGhlIEV2ZW50QnJpZGdlIHJ1bGVzIGFyZSBlbmFibGVkLiBEZWZhdWx0cyB0byB0cnVlIGlmIG9taXR0ZWQuICovXG4gIHJlYWRvbmx5IGVuYWJsZVJ1bGU/OiBib29sZWFuO1xuICAvKiogU2VjcmV0cyB1c2VkIGZvciBub3RpZmljYXRpb25zIChlLmcuIFNsYWNrKS4gKi9cbiAgcmVhZG9ubHkgc2VjcmV0czogU2VjcmV0cztcbn1cblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgd2hlbiBhIGNvbmNyZXRlIHN0cmluZyBpcyBlbXB0eSBvciB3aGl0ZXNwYWNlLW9ubHkuXG4gKiBVbnJlc29sdmVkIENESyB0b2tlbnMgYXJlIHRyZWF0ZWQgYXMgcHJlc2VudCBiZWNhdXNlIHRoZWlyIHZhbHVlIGlzIHVua25vd24gYXQgc3ludGhlc2lzLlxuICpcbiAqIEBwYXJhbSB2YWx1ZSAtIFByb3Agc3RyaW5nIHRvIGNoZWNrLlxuICogQHJldHVybnMgV2hldGhlciB0aGUgdmFsdWUgaXMgYmxhbmsuXG4gKi9cbmNvbnN0IGlzQmxhbmtUZXh0ID0gKHZhbHVlOiBzdHJpbmcpOiBib29sZWFuID0+XG4gICFUb2tlbi5pc1VucmVzb2x2ZWQodmFsdWUpICYmIHZhbHVlLnRyaW0oKS5sZW5ndGggPT09IDA7XG5cbi8qKlxuICogUmVqZWN0cyBlbXB0eSB0YWcgZmlsdGVycyBhbmQgU2xhY2sgc2VjcmV0IG5hbWVzIGJlZm9yZSByZXNvdXJjZXMgYXJlIGNyZWF0ZWQuXG4gKlxuICogQHBhcmFtIHByb3BzIC0gQ29uc3RydWN0IHByb3BzIHRvIHZhbGlkYXRlLlxuICogQHRocm93cyBXaGVuIHRhZ0tleSBvciBhIHRhZyB2YWx1ZSBpcyBibGFuaywgdGFnVmFsdWVzIGlzIGVtcHR5LCBvciBzbGFja1NlY3JldE5hbWUgaXMgcHJlc2VudCBidXQgYmxhbmsuXG4gKi9cbmNvbnN0IHZhbGlkYXRlUHJvcHMgPSAocHJvcHM6IFJEU0RhdGFiYXNlQXV0b1N0YXJ0UHJldmVudGVyUHJvcHMpOiB2b2lkID0+IHtcbiAgaWYgKGlzQmxhbmtUZXh0KHByb3BzLnRhcmdldFJlc291cmNlLnRhZ0tleSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3RhcmdldFJlc291cmNlLnRhZ0tleSBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZy4nKTtcbiAgfVxuICBpZiAocHJvcHMudGFyZ2V0UmVzb3VyY2UudGFnVmFsdWVzLmxlbmd0aCA9PT0gMCkge1xuICAgIHRocm93IG5ldyBFcnJvcigndGFyZ2V0UmVzb3VyY2UudGFnVmFsdWVzIG11c3QgY29udGFpbiBhdCBsZWFzdCBvbmUgdmFsdWUuJyk7XG4gIH1cbiAgZm9yIChjb25zdCB2YWx1ZSBvZiBwcm9wcy50YXJnZXRSZXNvdXJjZS50YWdWYWx1ZXMpIHtcbiAgICBpZiAoaXNCbGFua1RleHQodmFsdWUpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3RhcmdldFJlc291cmNlLnRhZ1ZhbHVlcyBtdXN0IG5vdCBjb250YWluIGVtcHR5IHN0cmluZ3MuJyk7XG4gICAgfVxuICB9XG4gIGNvbnN0IHNsYWNrU2VjcmV0TmFtZSA9IHByb3BzLnNlY3JldHMuc2xhY2tTZWNyZXROYW1lO1xuICBpZiAoc2xhY2tTZWNyZXROYW1lICE9PSB1bmRlZmluZWQgJiYgaXNCbGFua1RleHQoc2xhY2tTZWNyZXROYW1lKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignc2VjcmV0cy5zbGFja1NlY3JldE5hbWUgbXVzdCBiZSBhIG5vbi1lbXB0eSBzdHJpbmcuJyk7XG4gIH1cbn07XG5cbi8qKlxuICogRGVwbG95cyBFdmVudEJyaWRnZSBydWxlcyBhbmQgYSBEdXJhYmxlIExhbWJkYSB0byBzdG9wIG1hdGNoaW5nIFJEUyByZXNvdXJjZXNcbiAqIGFmdGVyIGF1dG8tc3RhcnQgZXZlbnRzIChSRFMtRVZFTlQtMDE1NCAvIFJEUy1FVkVOVC0wMTUzKS5cbiAqXG4gKiBFdmVudEJyaWRnZSBydWxlcyBtYXRjaCBhbGwgYXV0by1zdGFydCBldmVudHM7IHRhZyBmaWx0ZXJpbmcgcnVucyBpbiB0aGUgaGFuZGxlclxuICogdXNpbmcgYFRhZ0xpc3RgIGZyb20gYHJkczpEZXNjcmliZURCSW5zdGFuY2VzYCAvIGByZHM6RGVzY3JpYmVEQkNsdXN0ZXJzYC5cbiAqIElBTSBncmFudHMgUkRTIGRlc2NyaWJlIGFuZCBzdG9wIGFjdGlvbnMgb25seSAobm8gUmVzb3VyY2UgR3JvdXBzIFRhZ2dpbmcgQVBJKS5cbiAqL1xuZXhwb3J0IGNsYXNzIFJEU0RhdGFiYXNlQXV0b1N0YXJ0UHJldmVudGVyIGV4dGVuZHMgQ29uc3RydWN0IHtcblxuICAvKipcbiAgICogQ3JlYXRlcyB0aGUgRHVyYWJsZSBMYW1iZGEsIEV2ZW50QnJpZGdlIHJ1bGVzLCBhbmQgSUFNIHBvbGljaWVzLlxuICAgKlxuICAgKiBUaGUgTGFtYmRhIHJlY2VpdmVzIGB7IGV2ZW50LCBwYXJhbXMgfWAgd2hlcmUgYHBhcmFtc2AgY2FycmllcyBgdGFnS2V5YCBhbmQgYHRhZ1ZhbHVlc2BcbiAgICogZnJvbSB7QGxpbmsgUkRTRGF0YWJhc2VBdXRvU3RhcnRQcmV2ZW50ZXJQcm9wcy50YXJnZXRSZXNvdXJjZX0uIFRhZyBmaWx0ZXJzIGFyZSBwYXNzZWRcbiAgICogb25seSB2aWEgdGhhdCBwYXlsb2FkIChub3QgdmlhIGBUQUdfS0VZYCAvIGBUQUdfVkFMVUVTYCBlbnZpcm9ubWVudCB2YXJpYWJsZXMpLlxuICAgKlxuICAgKiBAcGFyYW0gc2NvcGUgLSBQYXJlbnQgY29uc3RydWN0LlxuICAgKiBAcGFyYW0gaWQgLSBDb25zdHJ1Y3QgaWQuXG4gICAqIEBwYXJhbSBwcm9wcyAtIFRhcmdldCByZXNvdXJjZSAodGFnIGtleS92YWx1ZXMpLCBlbmFibGUgcnVsZSBmbGFnLCBhbmQgc2VjcmV0cy5cbiAgICogQHRocm93cyBXaGVuIHRhZ0tleSBvciBhIHRhZyB2YWx1ZSBpcyBibGFuaywgdGFnVmFsdWVzIGlzIGVtcHR5LCBvciBzbGFja1NlY3JldE5hbWUgaXMgcHJlc2VudCBidXQgYmxhbmsuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogUkRTRGF0YWJhc2VBdXRvU3RhcnRQcmV2ZW50ZXJQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG4gICAgdmFsaWRhdGVQcm9wcyhwcm9wcyk7XG5cbiAgICBjb25zdCBzbGFja1NlY3JldE5hbWUgPSBwcm9wcy5zZWNyZXRzLnNsYWNrU2VjcmV0TmFtZTtcbiAgICBjb25zdCBzbGFja1NlY3JldCA9IHNsYWNrU2VjcmV0TmFtZSA9PT0gdW5kZWZpbmVkXG4gICAgICA/IHVuZGVmaW5lZFxuICAgICAgOiBTZWNyZXQuZnJvbVNlY3JldE5hbWVWMih0aGlzLCAnU2xhY2tTZWNyZXQnLCBzbGFja1NlY3JldE5hbWUpO1xuXG4gICAgLy8gRHVyYWJsZSBGdW5jdGlvbnMtYmFzZWQgUnVubmluZyBTY2hlZHVsZXIgKHByZXZpb3VzIFN0ZXAgRnVuY3Rpb25zIGxvZ2ljIGltcGxlbWVudGVkIGluIExhbWJkYSkuXG4gICAgLy8gRHVyYWJsZSBFeGVjdXRpb24gcmVxdWlyZXMgTm9kZS5qcyAyMisuXG4gICAgY29uc3QgYXV0b1N0YXJ0UHJldmVudEZ1bmN0aW9uID0gbmV3IEF1dG9TdGFydFByZXZlbnRGdW5jdGlvbih0aGlzLCAnQXV0b1N0YXJ0UHJldmVudEZ1bmN0aW9uJywge1xuICAgICAgZGVzY3JpcHRpb246ICdBIGZ1bmN0aW9uIHRvIHByZXZlbnQgdGhlIFJEUyBEYXRhYmFzZSBvciBDbHVzdGVyIGZyb20gc3RhcnRpbmcgYXV0b21hdGljYWxseS4nLFxuICAgICAgYXJjaGl0ZWN0dXJlOiBsYW1iZGEuQXJjaGl0ZWN0dXJlLkFSTV82NCxcbiAgICAgIHRpbWVvdXQ6IER1cmF0aW9uLm1pbnV0ZXMoMTUpLFxuICAgICAgbWVtb3J5U2l6ZTogNTEyLFxuICAgICAgcmV0cnlBdHRlbXB0czogMixcbiAgICAgIGR1cmFibGVDb25maWc6IHtcbiAgICAgICAgZXhlY3V0aW9uVGltZW91dDogRHVyYXRpb24uaG91cnMoMiksXG4gICAgICAgIHJldGVudGlvblBlcmlvZDogRHVyYXRpb24uZGF5cygxKSxcbiAgICAgIH0sXG4gICAgICBlbnZpcm9ubWVudDogc2xhY2tTZWNyZXQgPT09IHVuZGVmaW5lZFxuICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICA6IHtcbiAgICAgICAgICBTTEFDS19TRUNSRVRfTkFNRTogc2xhY2tTZWNyZXQuc2VjcmV0TmFtZSxcbiAgICAgICAgfSxcbiAgICAgIC8vIFJlcXVpcmVkIGJ5IGF3cy1sYW1iZGEtc2VjcmV0LWZldGNoZXIgKF4wLjYrKSB3aGVuIFNsYWNrIGlzIGNvbmZpZ3VyZWQuXG4gICAgICBwYXJhbXNBbmRTZWNyZXRzOiBzbGFja1NlY3JldCA9PT0gdW5kZWZpbmVkXG4gICAgICAgID8gdW5kZWZpbmVkXG4gICAgICAgIDogbGFtYmRhLlBhcmFtc0FuZFNlY3JldHNMYXllclZlcnNpb24uZnJvbVZlcnNpb24obGFtYmRhLlBhcmFtc0FuZFNlY3JldHNWZXJzaW9ucy5WMV8wXzEwMywge1xuICAgICAgICAgIGNhY2hlU2l6ZTogNTAwLFxuICAgICAgICAgIGxvZ0xldmVsOiBsYW1iZGEuUGFyYW1zQW5kU2VjcmV0c0xvZ0xldmVsLklORk8sXG4gICAgICAgIH0pLFxuICAgICAgcm9sZTogbmV3IGlhbS5Sb2xlKHRoaXMsICdBdXRvU3RhcnRQcmV2ZW50RnVuY3Rpb25Sb2xlJywge1xuICAgICAgICBkZXNjcmlwdGlvbjogJ0Egcm9sZSB0byBjb250cm9sIHRoZSBSRFMgRGF0YWJhc2Ugb3IgQ2x1c3Rlci4nLFxuICAgICAgICBhc3N1bWVkQnk6IG5ldyBpYW0uU2VydmljZVByaW5jaXBhbCgnbGFtYmRhLmFtYXpvbmF3cy5jb20nKSxcbiAgICAgICAgbWFuYWdlZFBvbGljaWVzOiBbXG4gICAgICAgICAgaWFtLk1hbmFnZWRQb2xpY3kuZnJvbUF3c01hbmFnZWRQb2xpY3lOYW1lKCdzZXJ2aWNlLXJvbGUvQVdTTGFtYmRhQmFzaWNFeGVjdXRpb25Sb2xlJyksXG4gICAgICAgICAgaWFtLk1hbmFnZWRQb2xpY3kuZnJvbUF3c01hbmFnZWRQb2xpY3lOYW1lKCdzZXJ2aWNlLXJvbGUvQVdTTGFtYmRhQmFzaWNEdXJhYmxlRXhlY3V0aW9uUm9sZVBvbGljeScpLFxuICAgICAgICBdLFxuICAgICAgfSksXG4gICAgICBsb2dHcm91cDogbmV3IGxvZ3MuTG9nR3JvdXAodGhpcywgJ0F1dG9TdGFydFByZXZlbnRGdW5jdGlvbkxvZ0dyb3VwJywge1xuICAgICAgICByZXRlbnRpb246IGxvZ3MuUmV0ZW50aW9uRGF5cy5USFJFRV9NT05USFMsXG4gICAgICAgIHJlbW92YWxQb2xpY3k6IFJlbW92YWxQb2xpY3kuREVTVFJPWSxcbiAgICAgIH0pLFxuICAgICAgbG9nZ2luZ0Zvcm1hdDogbGFtYmRhLkxvZ2dpbmdGb3JtYXQuSlNPTixcbiAgICAgIHN5c3RlbUxvZ0xldmVsVjI6IGxhbWJkYS5TeXN0ZW1Mb2dMZXZlbC5JTkZPLFxuICAgICAgYXBwbGljYXRpb25Mb2dMZXZlbFYyOiBsYW1iZGEuQXBwbGljYXRpb25Mb2dMZXZlbC5JTkZPLFxuICAgIH0pO1xuICAgIC8vIEdyYW50IFJEUyBkZXNjcmliZSAoc3RhdHVzIGFuZCBUYWdMaXN0KSBhbmQgc3RvcCBwZXJtaXNzaW9ucyB0byB0aGUgaGFuZGxlciByb2xlLlxuICAgIGF1dG9TdGFydFByZXZlbnRGdW5jdGlvbi5hZGRUb1JvbGVQb2xpY3kobmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgc2lkOiAnUmRzQXV0b1N0YXJ0Q29udHJvbCcsXG4gICAgICBlZmZlY3Q6IGlhbS5FZmZlY3QuQUxMT1csXG4gICAgICBhY3Rpb25zOiBbXG4gICAgICAgICdyZHM6RGVzY3JpYmVEQkluc3RhbmNlcycsXG4gICAgICAgICdyZHM6RGVzY3JpYmVEQkNsdXN0ZXJzJyxcbiAgICAgICAgJ3JkczpTdG9wREJJbnN0YW5jZScsXG4gICAgICAgICdyZHM6U3RvcERCQ2x1c3RlcicsXG4gICAgICBdLFxuICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICB9KSk7XG4gICAgaWYgKHNsYWNrU2VjcmV0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHNsYWNrU2VjcmV0LmdyYW50UmVhZChhdXRvU3RhcnRQcmV2ZW50RnVuY3Rpb24pO1xuICAgIH1cblxuICAgIC8vIFNlZTogaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2xhbWJkYS9sYXRlc3QvZGcvZHVyYWJsZS1nZXR0aW5nLXN0YXJ0ZWQtaWFjLmh0bWxcbiAgICBjb25zdCBhdXRvU3RhcnRQcmV2ZW50RnVuY3Rpb25BbGlhcyA9IGF1dG9TdGFydFByZXZlbnRGdW5jdGlvbi5hZGRBbGlhcygnbGl2ZScpO1xuXG4gICAgY29uc3QgZW5hYmxlUnVsZSA9IHByb3BzLmVuYWJsZVJ1bGUgIT09IGZhbHNlO1xuXG4gICAgLy8gUGFzcyB0aGUgRXZlbnRCcmlkZ2UgZXZlbnQgYW5kIHRhZyBmaWx0ZXIgcGFyYW1zIHRvIHRoZSBMYW1iZGEgdGFyZ2V0IGlucHV0LlxuICAgIGNvbnN0IGxhbWJkYUlucHV0ID0gZXZlbnRzLlJ1bGVUYXJnZXRJbnB1dC5mcm9tT2JqZWN0KHtcbiAgICAgIGV2ZW50OiBldmVudHMuRXZlbnRGaWVsZC5mcm9tUGF0aCgnJCcpLFxuICAgICAgcGFyYW1zOiB7XG4gICAgICAgIHRhZ0tleTogcHJvcHMudGFyZ2V0UmVzb3VyY2UudGFnS2V5LFxuICAgICAgICB0YWdWYWx1ZXM6IHByb3BzLnRhcmdldFJlc291cmNlLnRhZ1ZhbHVlcyxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICAvLyDwn5GHIEV2ZW50QnJpZGdlIGJ5IFJEUyBEQiBJbnN0YW5jZSBhdXRvIHN0YXJ0IGV2ZW50XG4gICAgbmV3IGV2ZW50cy5SdWxlKHRoaXMsICdEQkluc3RhbmNlRXZlbnQnLCB7XG4gICAgICBkZXNjcmlwdGlvbjogJ3JkcyBkYiBpbnN0YW5jZSBhdXRvIHN0YXJ0IGV2ZW50IGNhdGNoIHJ1bGUuJyxcbiAgICAgIGVuYWJsZWQ6IGVuYWJsZVJ1bGUsXG4gICAgICBldmVudFBhdHRlcm46IHtcbiAgICAgICAgc291cmNlOiBbJ2F3cy5yZHMnXSxcbiAgICAgICAgZGV0YWlsVHlwZTogWydSRFMgREIgSW5zdGFuY2UgRXZlbnQnXSxcbiAgICAgICAgZGV0YWlsOiB7XG4gICAgICAgICAgRXZlbnRJRDogWydSRFMtRVZFTlQtMDE1NCddLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIHRhcmdldHM6IFtcbiAgICAgICAgbmV3IHRhcmdldHMuTGFtYmRhRnVuY3Rpb24oYXV0b1N0YXJ0UHJldmVudEZ1bmN0aW9uQWxpYXMsIHtcbiAgICAgICAgICByZXRyeUF0dGVtcHRzOiAyLFxuICAgICAgICAgIGV2ZW50OiBsYW1iZGFJbnB1dCxcbiAgICAgICAgfSksXG4gICAgICBdLFxuICAgIH0pO1xuXG4gICAgLy8g8J+RhyBFdmVudEJyaWRnZSBieSBSRFMgREIgQ2x1c3RlciBhdXRvIHN0YXJ0IGV2ZW50XG4gICAgbmV3IGV2ZW50cy5SdWxlKHRoaXMsICdEQkNsdXN0ZXJFdmVudCcsIHtcbiAgICAgIGRlc2NyaXB0aW9uOiAnZGIgY2x1c3RlciBhdXRvIHN0YXJ0IGV2ZW50IGNhdGNoIHJ1bGUnLFxuICAgICAgZW5hYmxlZDogZW5hYmxlUnVsZSxcbiAgICAgIGV2ZW50UGF0dGVybjoge1xuICAgICAgICBzb3VyY2U6IFsnYXdzLnJkcyddLFxuICAgICAgICBkZXRhaWxUeXBlOiBbJ1JEUyBEQiBDbHVzdGVyIEV2ZW50J10sXG4gICAgICAgIGRldGFpbDoge1xuICAgICAgICAgIEV2ZW50SUQ6IFsnUkRTLUVWRU5ULTAxNTMnXSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICB0YXJnZXRzOiBbXG4gICAgICAgIG5ldyB0YXJnZXRzLkxhbWJkYUZ1bmN0aW9uKGF1dG9TdGFydFByZXZlbnRGdW5jdGlvbkFsaWFzLCB7XG4gICAgICAgICAgcmV0cnlBdHRlbXB0czogMixcbiAgICAgICAgICBldmVudDogbGFtYmRhSW5wdXQsXG4gICAgICAgIH0pLFxuICAgICAgXSxcbiAgICB9KTtcbiAgfVxufVxuIl19