UNPKG

13.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.InstanceDrainHook = void 0;
4const fs = require("fs");
5const path = require("path");
6const autoscaling = require("@aws-cdk/aws-autoscaling");
7const hooks = require("@aws-cdk/aws-autoscaling-hooktargets");
8const iam = require("@aws-cdk/aws-iam");
9const lambda = require("@aws-cdk/aws-lambda");
10const cdk = require("@aws-cdk/core");
11// keep this import separate from other imports to reduce chance for merge conflicts with v2-main
12// eslint-disable-next-line no-duplicate-imports, import/order
13const core_1 = require("@aws-cdk/core");
14/**
15 * A hook to drain instances from ECS traffic before they're terminated
16 */
17class InstanceDrainHook extends core_1.Construct {
18 /**
19 * Constructs a new instance of the InstanceDrainHook class.
20 */
21 constructor(scope, id, props) {
22 super(scope, id);
23 const drainTime = props.drainTime || cdk.Duration.minutes(5);
24 // Invoke Lambda via SNS Topic
25 const fn = new lambda.Function(this, 'Function', {
26 code: lambda.Code.fromInline(fs.readFileSync(path.join(__dirname, 'lambda-source', 'index.py'), { encoding: 'utf-8' })),
27 handler: 'index.lambda_handler',
28 runtime: lambda.Runtime.PYTHON_3_6,
29 // Timeout: some extra margin for additional API calls made by the Lambda,
30 // up to a maximum of 15 minutes.
31 timeout: cdk.Duration.seconds(Math.min(drainTime.toSeconds() + 10, 900)),
32 environment: {
33 CLUSTER: props.cluster.clusterName,
34 },
35 });
36 // Hook everything up: ASG -> Topic, Topic -> Lambda
37 props.autoScalingGroup.addLifecycleHook('DrainHook', {
38 lifecycleTransition: autoscaling.LifecycleTransition.INSTANCE_TERMINATING,
39 defaultResult: autoscaling.DefaultResult.CONTINUE,
40 notificationTarget: new hooks.FunctionHook(fn, props.topicEncryptionKey),
41 heartbeatTimeout: drainTime,
42 });
43 // Describe actions cannot be restricted and restrict the CompleteLifecycleAction to the ASG arn
44 // https://docs.aws.amazon.com/autoscaling/ec2/userguide/control-access-using-iam.html
45 fn.addToRolePolicy(new iam.PolicyStatement({
46 actions: [
47 'ec2:DescribeInstances',
48 'ec2:DescribeInstanceAttribute',
49 'ec2:DescribeInstanceStatus',
50 'ec2:DescribeHosts',
51 ],
52 resources: ['*'],
53 }));
54 // Restrict to the ASG
55 fn.addToRolePolicy(new iam.PolicyStatement({
56 actions: ['autoscaling:CompleteLifecycleAction'],
57 resources: [props.autoScalingGroup.autoScalingGroupArn],
58 }));
59 fn.addToRolePolicy(new iam.PolicyStatement({
60 actions: ['ecs:DescribeContainerInstances', 'ecs:DescribeTasks'],
61 resources: ['*'],
62 conditions: {
63 ArnEquals: { 'ecs:cluster': props.cluster.clusterArn },
64 },
65 }));
66 // Restrict to the ECS Cluster
67 fn.addToRolePolicy(new iam.PolicyStatement({
68 actions: [
69 'ecs:ListContainerInstances',
70 'ecs:SubmitContainerStateChange',
71 'ecs:SubmitTaskStateChange',
72 ],
73 resources: [props.cluster.clusterArn],
74 }));
75 // Restrict the container-instance operations to the ECS Cluster
76 fn.addToRolePolicy(new iam.PolicyStatement({
77 actions: [
78 'ecs:UpdateContainerInstancesState',
79 'ecs:ListTasks',
80 ],
81 conditions: {
82 ArnEquals: { 'ecs:cluster': props.cluster.clusterArn },
83 },
84 resources: ['*'],
85 }));
86 }
87}
88exports.InstanceDrainHook = InstanceDrainHook;
89//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdGFuY2UtZHJhaW4taG9vay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImluc3RhbmNlLWRyYWluLWhvb2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qix3REFBd0Q7QUFDeEQsOERBQThEO0FBQzlELHdDQUF3QztBQUV4Qyw4Q0FBOEM7QUFDOUMscUNBQXFDO0FBSXJDLGlHQUFpRztBQUNqRyw4REFBOEQ7QUFDOUQsd0NBQTJEO0FBdUMzRDs7R0FFRztBQUNILE1BQWEsaUJBQWtCLFNBQVEsZ0JBQWE7SUFFbEQ7O09BRUc7SUFDSCxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQTZCO1FBQ3JFLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakIsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLFNBQVMsSUFBSSxHQUFHLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUU3RCw4QkFBOEI7UUFDOUIsTUFBTSxFQUFFLEdBQUcsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxVQUFVLEVBQUU7WUFDL0MsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsZUFBZSxFQUFFLFVBQVUsQ0FBQyxFQUFFLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDdkgsT0FBTyxFQUFFLHNCQUFzQjtZQUMvQixPQUFPLEVBQUUsTUFBTSxDQUFDLE9BQU8sQ0FBQyxVQUFVO1lBQ2xDLDBFQUEwRTtZQUMxRSxpQ0FBaUM7WUFDakMsT0FBTyxFQUFFLEdBQUcsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxHQUFHLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUN4RSxXQUFXLEVBQUU7Z0JBQ1gsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLENBQUMsV0FBVzthQUNuQztTQUNGLENBQUMsQ0FBQztRQUVILG9EQUFvRDtRQUNwRCxLQUFLLENBQUMsZ0JBQWdCLENBQUMsZ0JBQWdCLENBQUMsV0FBVyxFQUFFO1lBQ25ELG1CQUFtQixFQUFFLFdBQVcsQ0FBQyxtQkFBbUIsQ0FBQyxvQkFBb0I7WUFDekUsYUFBYSxFQUFFLFdBQVcsQ0FBQyxhQUFhLENBQUMsUUFBUTtZQUNqRCxrQkFBa0IsRUFBRSxJQUFJLEtBQUssQ0FBQyxZQUFZLENBQUMsRUFBRSxFQUFFLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQztZQUN4RSxnQkFBZ0IsRUFBRSxTQUFTO1NBQzVCLENBQUMsQ0FBQztRQUVILGdHQUFnRztRQUNoRyxzRkFBc0Y7UUFDdEYsRUFBRSxDQUFDLGVBQWUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxlQUFlLENBQUM7WUFDekMsT0FBTyxFQUFFO2dCQUNQLHVCQUF1QjtnQkFDdkIsK0JBQStCO2dCQUMvQiw0QkFBNEI7Z0JBQzVCLG1CQUFtQjthQUNwQjtZQUNELFNBQVMsRUFBRSxDQUFDLEdBQUcsQ0FBQztTQUNqQixDQUFDLENBQUMsQ0FBQztRQUVKLHNCQUFzQjtRQUN0QixFQUFFLENBQUMsZUFBZSxDQUFDLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztZQUN6QyxPQUFPLEVBQUUsQ0FBQyxxQ0FBcUMsQ0FBQztZQUNoRCxTQUFTLEVBQUUsQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLENBQUMsbUJBQW1CLENBQUM7U0FDeEQsQ0FBQyxDQUFDLENBQUM7UUFFSixFQUFFLENBQUMsZUFBZSxDQUFDLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztZQUN6QyxPQUFPLEVBQUUsQ0FBQyxnQ0FBZ0MsRUFBRSxtQkFBbUIsQ0FBQztZQUNoRSxTQUFTLEVBQUUsQ0FBQyxHQUFHLENBQUM7WUFDaEIsVUFBVSxFQUFFO2dCQUNWLFNBQVMsRUFBRSxFQUFFLGFBQWEsRUFBRSxLQUFLLENBQUMsT0FBTyxDQUFDLFVBQVUsRUFBRTthQUN2RDtTQUNGLENBQUMsQ0FBQyxDQUFDO1FBRUosOEJBQThCO1FBQzlCLEVBQUUsQ0FBQyxlQUFlLENBQUMsSUFBSSxHQUFHLENBQUMsZUFBZSxDQUFDO1lBQ3pDLE9BQU8sRUFBRTtnQkFDUCw0QkFBNEI7Z0JBQzVCLGdDQUFnQztnQkFDaEMsMkJBQTJCO2FBQzVCO1lBQ0QsU0FBUyxFQUFFLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUM7U0FDdEMsQ0FBQyxDQUFDLENBQUM7UUFFSixnRUFBZ0U7UUFDaEUsRUFBRSxDQUFDLGVBQWUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxlQUFlLENBQUM7WUFDekMsT0FBTyxFQUFFO2dCQUNQLG1DQUFtQztnQkFDbkMsZUFBZTthQUNoQjtZQUNELFVBQVUsRUFBRTtnQkFDVixTQUFTLEVBQUUsRUFBRSxhQUFhLEVBQUUsS0FBSyxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUU7YUFDdkQ7WUFDRCxTQUFTLEVBQUUsQ0FBQyxHQUFHLENBQUM7U0FDakIsQ0FBQyxDQUFDLENBQUM7S0FDTDtDQUNGO0FBL0VELDhDQStFQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgKiBhcyBhdXRvc2NhbGluZyBmcm9tICdAYXdzLWNkay9hd3MtYXV0b3NjYWxpbmcnO1xuaW1wb3J0ICogYXMgaG9va3MgZnJvbSAnQGF3cy1jZGsvYXdzLWF1dG9zY2FsaW5nLWhvb2t0YXJnZXRzJztcbmltcG9ydCAqIGFzIGlhbSBmcm9tICdAYXdzLWNkay9hd3MtaWFtJztcbmltcG9ydCAqIGFzIGttcyBmcm9tICdAYXdzLWNkay9hd3Mta21zJztcbmltcG9ydCAqIGFzIGxhbWJkYSBmcm9tICdAYXdzLWNkay9hd3MtbGFtYmRhJztcbmltcG9ydCAqIGFzIGNkayBmcm9tICdAYXdzLWNkay9jb3JlJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0IHsgSUNsdXN0ZXIgfSBmcm9tICcuLi9jbHVzdGVyJztcblxuLy8ga2VlcCB0aGlzIGltcG9ydCBzZXBhcmF0ZSBmcm9tIG90aGVyIGltcG9ydHMgdG8gcmVkdWNlIGNoYW5jZSBmb3IgbWVyZ2UgY29uZmxpY3RzIHdpdGggdjItbWFpblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWR1cGxpY2F0ZS1pbXBvcnRzLCBpbXBvcnQvb3JkZXJcbmltcG9ydCB7IENvbnN0cnVjdCBhcyBDb3JlQ29uc3RydWN0IH0gZnJvbSAnQGF3cy1jZGsvY29yZSc7XG5cbi8vIFJlZmVyZW5jZSBmb3IgdGhlIHNvdXJjZSBpbiB0aGlzIHBhY2thZ2U6XG4vL1xuLy8gaHR0cHM6Ly9naXRodWIuY29tL2F3cy1zYW1wbGVzL2Vjcy1yZWZhcmNoLWNsb3VkZm9ybWF0aW9uL2Jsb2IvbWFzdGVyL2luZnJhc3RydWN0dXJlL2xpZmVjeWNsZWhvb2sueWFtbFxuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIGluc3RhbmNlIGRyYWluaW5nIGhvb2tcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbnN0YW5jZURyYWluSG9va1Byb3BzIHtcbiAgLyoqXG4gICAqIFRoZSBBdXRvU2NhbGluZ0dyb3VwIHRvIGluc3RhbGwgdGhlIGluc3RhbmNlIGRyYWluaW5nIGhvb2sgZm9yXG4gICAqL1xuICBhdXRvU2NhbGluZ0dyb3VwOiBhdXRvc2NhbGluZy5JQXV0b1NjYWxpbmdHcm91cDtcblxuICAvKipcbiAgICogVGhlIGNsdXN0ZXIgb24gd2hpY2ggdGFza3MgaGF2ZSBiZWVuIHNjaGVkdWxlZFxuICAgKi9cbiAgY2x1c3RlcjogSUNsdXN0ZXI7XG5cbiAgLyoqXG4gICAqIEhvdyBtYW55IHNlY29uZHMgdG8gZ2l2ZSB0YXNrcyB0byBkcmFpbiBiZWZvcmUgdGhlIGluc3RhbmNlIGlzIHRlcm1pbmF0ZWQgYW55d2F5XG4gICAqXG4gICAqIE11c3QgYmUgYmV0d2VlbiAwIGFuZCAxNSBtaW51dGVzLlxuICAgKlxuICAgKiBAZGVmYXVsdCBEdXJhdGlvbi5taW51dGVzKDE1KVxuICAgKi9cbiAgZHJhaW5UaW1lPzogY2RrLkR1cmF0aW9uO1xuXG4gIC8qKlxuICAgKiBUaGUgSW5zdGFuY2VEcmFpbkhvb2sgY3JlYXRlcyBhbiBTTlMgdG9waWMgZm9yIHRoZSBsaWZlY3ljbGUgaG9vayBvZiB0aGUgQVNHLiBJZiBwcm92aWRlZCwgdGhlbiB0aGlzXG4gICAqIGtleSB3aWxsIGJlIHVzZWQgdG8gZW5jcnlwdCB0aGUgY29udGVudHMgb2YgdGhhdCBTTlMgVG9waWMuXG4gICAqIFNlZSBbU05TIERhdGEgRW5jcnlwdGlvbl0oaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL3Nucy9sYXRlc3QvZGcvc25zLWRhdGEtZW5jcnlwdGlvbi5odG1sKSBmb3IgbW9yZSBpbmZvcm1hdGlvbi5cbiAgICpcbiAgICogQGRlZmF1bHQgVGhlIFNOUyBUb3BpYyB3aWxsIG5vdCBiZSBlbmNyeXB0ZWQuXG4gICAqL1xuICB0b3BpY0VuY3J5cHRpb25LZXk/OiBrbXMuSUtleTtcbn1cblxuLyoqXG4gKiBBIGhvb2sgdG8gZHJhaW4gaW5zdGFuY2VzIGZyb20gRUNTIHRyYWZmaWMgYmVmb3JlIHRoZXkncmUgdGVybWluYXRlZFxuICovXG5leHBvcnQgY2xhc3MgSW5zdGFuY2VEcmFpbkhvb2sgZXh0ZW5kcyBDb3JlQ29uc3RydWN0IHtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBhIG5ldyBpbnN0YW5jZSBvZiB0aGUgSW5zdGFuY2VEcmFpbkhvb2sgY2xhc3MuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogSW5zdGFuY2VEcmFpbkhvb2tQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICBjb25zdCBkcmFpblRpbWUgPSBwcm9wcy5kcmFpblRpbWUgfHwgY2RrLkR1cmF0aW9uLm1pbnV0ZXMoNSk7XG5cbiAgICAvLyBJbnZva2UgTGFtYmRhIHZpYSBTTlMgVG9waWNcbiAgICBjb25zdCBmbiA9IG5ldyBsYW1iZGEuRnVuY3Rpb24odGhpcywgJ0Z1bmN0aW9uJywge1xuICAgICAgY29kZTogbGFtYmRhLkNvZGUuZnJvbUlubGluZShmcy5yZWFkRmlsZVN5bmMocGF0aC5qb2luKF9fZGlybmFtZSwgJ2xhbWJkYS1zb3VyY2UnLCAnaW5kZXgucHknKSwgeyBlbmNvZGluZzogJ3V0Zi04JyB9KSksXG4gICAgICBoYW5kbGVyOiAnaW5kZXgubGFtYmRhX2hhbmRsZXInLFxuICAgICAgcnVudGltZTogbGFtYmRhLlJ1bnRpbWUuUFlUSE9OXzNfNixcbiAgICAgIC8vIFRpbWVvdXQ6IHNvbWUgZXh0cmEgbWFyZ2luIGZvciBhZGRpdGlvbmFsIEFQSSBjYWxscyBtYWRlIGJ5IHRoZSBMYW1iZGEsXG4gICAgICAvLyB1cCB0byBhIG1heGltdW0gb2YgMTUgbWludXRlcy5cbiAgICAgIHRpbWVvdXQ6IGNkay5EdXJhdGlvbi5zZWNvbmRzKE1hdGgubWluKGRyYWluVGltZS50b1NlY29uZHMoKSArIDEwLCA5MDApKSxcbiAgICAgIGVudmlyb25tZW50OiB7XG4gICAgICAgIENMVVNURVI6IHByb3BzLmNsdXN0ZXIuY2x1c3Rlck5hbWUsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLy8gSG9vayBldmVyeXRoaW5nIHVwOiBBU0cgLT4gVG9waWMsIFRvcGljIC0+IExhbWJkYVxuICAgIHByb3BzLmF1dG9TY2FsaW5nR3JvdXAuYWRkTGlmZWN5Y2xlSG9vaygnRHJhaW5Ib29rJywge1xuICAgICAgbGlmZWN5Y2xlVHJhbnNpdGlvbjogYXV0b3NjYWxpbmcuTGlmZWN5Y2xlVHJhbnNpdGlvbi5JTlNUQU5DRV9URVJNSU5BVElORyxcbiAgICAgIGRlZmF1bHRSZXN1bHQ6IGF1dG9zY2FsaW5nLkRlZmF1bHRSZXN1bHQuQ09OVElOVUUsXG4gICAgICBub3RpZmljYXRpb25UYXJnZXQ6IG5ldyBob29rcy5GdW5jdGlvbkhvb2soZm4sIHByb3BzLnRvcGljRW5jcnlwdGlvbktleSksXG4gICAgICBoZWFydGJlYXRUaW1lb3V0OiBkcmFpblRpbWUsXG4gICAgfSk7XG5cbiAgICAvLyBEZXNjcmliZSBhY3Rpb25zIGNhbm5vdCBiZSByZXN0cmljdGVkIGFuZCByZXN0cmljdCB0aGUgQ29tcGxldGVMaWZlY3ljbGVBY3Rpb24gdG8gdGhlIEFTRyBhcm5cbiAgICAvLyBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vYXV0b3NjYWxpbmcvZWMyL3VzZXJndWlkZS9jb250cm9sLWFjY2Vzcy11c2luZy1pYW0uaHRtbFxuICAgIGZuLmFkZFRvUm9sZVBvbGljeShuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICBhY3Rpb25zOiBbXG4gICAgICAgICdlYzI6RGVzY3JpYmVJbnN0YW5jZXMnLFxuICAgICAgICAnZWMyOkRlc2NyaWJlSW5zdGFuY2VBdHRyaWJ1dGUnLFxuICAgICAgICAnZWMyOkRlc2NyaWJlSW5zdGFuY2VTdGF0dXMnLFxuICAgICAgICAnZWMyOkRlc2NyaWJlSG9zdHMnLFxuICAgICAgXSxcbiAgICAgIHJlc291cmNlczogWycqJ10sXG4gICAgfSkpO1xuXG4gICAgLy8gUmVzdHJpY3QgdG8gdGhlIEFTR1xuICAgIGZuLmFkZFRvUm9sZVBvbGljeShuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICBhY3Rpb25zOiBbJ2F1dG9zY2FsaW5nOkNvbXBsZXRlTGlmZWN5Y2xlQWN0aW9uJ10sXG4gICAgICByZXNvdXJjZXM6IFtwcm9wcy5hdXRvU2NhbGluZ0dyb3VwLmF1dG9TY2FsaW5nR3JvdXBBcm5dLFxuICAgIH0pKTtcblxuICAgIGZuLmFkZFRvUm9sZVBvbGljeShuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICBhY3Rpb25zOiBbJ2VjczpEZXNjcmliZUNvbnRhaW5lckluc3RhbmNlcycsICdlY3M6RGVzY3JpYmVUYXNrcyddLFxuICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgIGNvbmRpdGlvbnM6IHtcbiAgICAgICAgQXJuRXF1YWxzOiB7ICdlY3M6Y2x1c3Rlcic6IHByb3BzLmNsdXN0ZXIuY2x1c3RlckFybiB9LFxuICAgICAgfSxcbiAgICB9KSk7XG5cbiAgICAvLyBSZXN0cmljdCB0byB0aGUgRUNTIENsdXN0ZXJcbiAgICBmbi5hZGRUb1JvbGVQb2xpY3kobmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnZWNzOkxpc3RDb250YWluZXJJbnN0YW5jZXMnLFxuICAgICAgICAnZWNzOlN1Ym1pdENvbnRhaW5lclN0YXRlQ2hhbmdlJyxcbiAgICAgICAgJ2VjczpTdWJtaXRUYXNrU3RhdGVDaGFuZ2UnLFxuICAgICAgXSxcbiAgICAgIHJlc291cmNlczogW3Byb3BzLmNsdXN0ZXIuY2x1c3RlckFybl0sXG4gICAgfSkpO1xuXG4gICAgLy8gUmVzdHJpY3QgdGhlIGNvbnRhaW5lci1pbnN0YW5jZSBvcGVyYXRpb25zIHRvIHRoZSBFQ1MgQ2x1c3RlclxuICAgIGZuLmFkZFRvUm9sZVBvbGljeShuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICBhY3Rpb25zOiBbXG4gICAgICAgICdlY3M6VXBkYXRlQ29udGFpbmVySW5zdGFuY2VzU3RhdGUnLFxuICAgICAgICAnZWNzOkxpc3RUYXNrcycsXG4gICAgICBdLFxuICAgICAgY29uZGl0aW9uczoge1xuICAgICAgICBBcm5FcXVhbHM6IHsgJ2VjczpjbHVzdGVyJzogcHJvcHMuY2x1c3Rlci5jbHVzdGVyQXJuIH0sXG4gICAgICB9LFxuICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICB9KSk7XG4gIH1cbn1cbiJdfQ==
\No newline at end of file