cdk-construct-simple-nat
Version:
A CDK construct to build Simple NAT instance on AWS.
309 lines • 47.6 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SimpleNAT = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
/* eslint @typescript-eslint/no-require-imports: "off" */
const fs = require("fs");
const path = require("path");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const aws_ec2_1 = require("aws-cdk-lib/aws-ec2");
const aws_iam_1 = require("aws-cdk-lib/aws-iam");
const Mustache = require("mustache");
const fetch = require('sync-fetch');
/**
* Simple NAT instances construct.
*/
class SimpleNAT extends aws_cdk_lib_1.Resource {
constructor(scope, id, props) {
super(scope, id);
this.gateways = new PrefSet();
this._routeTablesLimit = new Map();
this._defaultRoutesPerTable = 50;
this._ipV6Regex = new RegExp(SimpleNAT.Ipv6Regex);
let subnets;
try {
subnets = props.vpc.selectSubnets(props.natSubnetsSelection ?? {
subnetType: aws_ec2_1.SubnetType.PUBLIC,
onePerAz: true,
});
}
catch (e) {
throw new Error('NAT instances must reside in public subnets.');
}
if (!subnets.hasPublic) {
throw new Error('The custom NAT subnet selection MUST select PUBLIC subnets.');
}
const machineImage = props.machineImage ?? aws_ec2_1.MachineImage.latestAmazonLinux2({
storage: aws_ec2_1.AmazonLinuxStorage.GENERAL_PURPOSE,
cpuType: aws_ec2_1.AmazonLinuxCpuType.X86_64,
});
if (machineImage.getImage(this).osType != aws_ec2_1.OperatingSystemType.LINUX) {
throw new Error('The OS of custom AMI must be Linux.');
}
this._securityGroup = new aws_ec2_1.SecurityGroup(scope, 'NatSecurityGroup', {
vpc: props.vpc,
description: 'Security Group for NAT instances',
allowAllOutbound: true,
});
this._securityGroup.addIngressRule(aws_ec2_1.Peer.ipv4(props.vpc.vpcCidrBlock), aws_ec2_1.Port.allTraffic());
const role = props.role ?? new aws_iam_1.Role(scope, 'NatRole', {
assumedBy: new aws_iam_1.ServicePrincipal('ec2.amazonaws.com'),
});
role.addManagedPolicy(aws_iam_1.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'));
role.addToPrincipalPolicy(new aws_iam_1.PolicyStatement({
actions: [
'ec2:AttachNetworkInterface',
'ec2:DetachNetworkInterface',
'ec2:DescribeNetworkInterfaces',
],
resources: ['*'],
}));
for (const sub of subnets.subnets) {
const eni = new aws_ec2_1.CfnNetworkInterface(sub, 'ENI1', {
subnetId: sub.subnetId,
sourceDestCheck: false,
description: 'ENI for binding EIP',
groupSet: [this._securityGroup.securityGroupId],
});
const eip = new aws_ec2_1.CfnEIP(sub, 'NatInstanceEIP', {
tags: [
{
key: 'Name',
value: `EIP for NAT instance in subnet '${sub.subnetId}'.`,
},
],
});
eip.applyRemovalPolicy(aws_cdk_lib_1.RemovalPolicy.RETAIN);
new aws_ec2_1.CfnEIPAssociation(sub, 'EIPAssociation', {
allocationId: eip.attrAllocationId,
networkInterfaceId: eni.ref,
});
const configs = [
aws_ec2_1.InitFile.fromFileInline('/opt/nat/snat.sh', path.join(__dirname, 'snat.sh'), {
mode: '000755',
}),
aws_ec2_1.InitFile.fromFileInline('/etc/systemd/system/snat.service', path.join(__dirname, 'snat.service')),
aws_ec2_1.InitFile.fromString('/opt/nat/runonce.sh', Mustache.render(fs.readFileSync(path.join(__dirname, 'runonce.sh'), 'utf-8'), {
eniId: eni.ref,
}), {
mode: '000755',
}),
aws_ec2_1.InitCommand.shellCommand('/opt/nat/runonce.sh'),
];
if (props.customScripts) {
configs.push(aws_ec2_1.InitFile.fromString('/opt/nat/custom.sh', props.customScripts, {
mode: '000755',
}));
configs.push(aws_ec2_1.InitCommand.shellCommand('/opt/nat/custom.sh'));
}
const natInstance = new aws_ec2_1.Instance(sub, 'NatInstance', {
instanceType: props.instanceType ?? aws_ec2_1.InstanceType.of(aws_ec2_1.InstanceClass.T3, aws_ec2_1.InstanceSize.MICRO),
machineImage,
vpc: props.vpc,
vpcSubnets: { subnets: [sub] },
securityGroup: this._securityGroup,
role,
keyPair: props.keyName ? aws_ec2_1.KeyPair.fromKeyPairName(this, 'KeyPair', props.keyName) : undefined,
init: aws_ec2_1.CloudFormationInit.fromConfigSets({
configSets: {
default: ['yumPreinstall', 'config'],
},
configs: {
yumPreinstall: new aws_ec2_1.InitConfig([
// Install an Amazon Linux package using yum
aws_ec2_1.InitPackage.yum('jq'),
]),
config: new aws_ec2_1.InitConfig(configs),
},
}),
initOptions: {
// Optional, which configsets to activate (['default'] by default)
configSets: ['default'],
timeout: aws_cdk_lib_1.Duration.minutes(5 + (props.customScripts ? 10 : 0)),
},
});
// NAT instance routes all traffic, both ways
this.gateways.add(sub.availabilityZone, {
instance: natInstance,
eni,
});
}
this._routeMappingSubnets = props.vpc.selectSubnets(props.privateSubnetsSelection ?? {
subnetType: aws_ec2_1.SubnetType.PRIVATE_WITH_EGRESS,
}).subnets.reduce((routeMapping, sub) => {
if (routeMapping.has(sub.routeTable.routeTableId)) {
routeMapping.get(sub.routeTable.routeTableId).push(sub);
}
else {
routeMapping.set(sub.routeTable.routeTableId, [sub]);
}
return routeMapping;
}, new Map());
aws_cdk_lib_1.Tags.of(this).add('construct', 'simple-nat');
}
addV4Route(v4CIDR) {
// Add routes to them in the private subnets
for (const [routeId, subnets] of this._routeMappingSubnets) {
this._configureSubnet(routeId, subnets, v4CIDR);
}
return this;
}
addV6Route(v6CIDR) {
// Add routes to them in the private subnets
for (const [routeId, subnets] of this._routeMappingSubnets) {
this._configureSubnet(routeId, subnets, undefined, v6CIDR);
}
return this;
}
/**
* Add Github IPs to route table
*/
withGithubRoute(props) {
const githubMeta = fetch('https://api.github.com/meta').json();
for (const cidr of githubMeta.git) {
for (const [routeId, subnets] of this._routeMappingSubnets) {
if (this._ipV6Regex.test(cidr)) {
const excludeIPv6 = props?.excludeIPv6 ?? false;
if (!excludeIPv6) {
this._configureSubnet(routeId, subnets, undefined, cidr);
}
}
else {
this._configureSubnet(routeId, subnets, cidr);
}
}
}
return this;
}
/**
* Add Google IPs to route table
*/
withGoogleRoute(props) {
const googleMeta = fetch('https://www.gstatic.com/ipranges/goog.json').json();
const excludeIPv6 = props?.excludeIPv6 ?? false;
for (const cidr of googleMeta.prefixes) {
for (const [routeId, subnets] of this._routeMappingSubnets) {
if (cidr.ipv4Prefix) {
this._configureSubnet(routeId, subnets, cidr.ipv4Prefix);
}
if (cidr.ipv6Prefix && !excludeIPv6) {
this._configureSubnet(routeId, subnets, undefined, cidr.ipv6Prefix);
}
}
}
return this;
}
/**
* Add Cloudflare IPs to route table
*
* See https://www.cloudflare.com/ips/ for details
*/
withCloudflareRoute(props) {
const ipV4 = fetch('https://www.cloudflare.com/ips-v4').text().split(/\r?\n/);
for (const cidr of ipV4) {
for (const [routeId, subnets] of this._routeMappingSubnets) {
this._configureSubnet(routeId, subnets, cidr);
}
}
const excludeIPv6 = props?.excludeIPv6 ?? false;
if (!excludeIPv6) {
const ipV6 = fetch('https://www.cloudflare.com/ips-v6').text().split(/\r?\n/);
for (const cidr of ipV6) {
for (const [routeId, subnets] of this._routeMappingSubnets) {
this._configureSubnet(routeId, subnets, undefined, cidr);
}
}
}
return this;
}
_configureSubnet(_routeId, subnets, v4CIDR, v6CIDR) {
const az = subnets[0].availabilityZone;
const natInstance = this.gateways.pick(az);
this._addRoute(`Route-${v4CIDR ? 'v4-' + v4CIDR?.replace(/[./]/gi, '-') : 'v6-' + v6CIDR?.replace(/[:/]/gi, '-')}`, subnets[0], {
destinationCidrBlock: v4CIDR,
destinationIpv6CidrBlock: v6CIDR,
routerType: aws_ec2_1.RouterType.NETWORK_INTERFACE,
routerId: natInstance.eni.ref,
enablesInternetConnectivity: true,
});
return this;
}
_addRoute(id, subnet, options) {
if (options.destinationCidrBlock && options.destinationIpv6CidrBlock) {
throw new Error('Cannot specify both \'destinationCidrBlock\' and \'destinationIpv6CidrBlock\'');
}
new aws_ec2_1.CfnRoute(subnet, id, {
routeTableId: subnet.routeTable.routeTableId,
destinationCidrBlock: options.destinationCidrBlock ?? (options.destinationIpv6CidrBlock === undefined ? '0.0.0.0/0' : undefined),
destinationIpv6CidrBlock: options.destinationIpv6CidrBlock,
[routerTypeToPropName(options.routerType)]: options.routerId,
});
const isIpv4 = (options.destinationCidrBlock != undefined);
if (this._routeTablesLimit.has(subnet.routeTable.routeTableId)) {
const stats = this._routeTablesLimit.get(subnet.routeTable.routeTableId);
if (isIpv4) {
stats.ipv4 += 1;
}
else {
stats.ipv6 += 1;
}
this._routeTablesLimit.set(subnet.routeTable.routeTableId, stats);
const totalRoutes = (isIpv4 ? stats.ipv4 : stats.ipv6);
if (totalRoutes > this._defaultRoutesPerTable) {
aws_cdk_lib_1.Annotations.of(this).addWarning(`The current routes in route table '${subnet.routeTable.routeTableId}' is ${totalRoutes} which exceeds the default limit ${this._defaultRoutesPerTable}. You can open ticket to increase it.`);
}
}
else {
this._routeTablesLimit.set(subnet.routeTable.routeTableId, {
ipv4: isIpv4 ? 1 : 0,
ipv6: isIpv4 ? 0 : 1,
});
}
}
}
exports.SimpleNAT = SimpleNAT;
_a = JSII_RTTI_SYMBOL_1;
SimpleNAT[_a] = { fqn: "cdk-construct-simple-nat.SimpleNAT", version: "0.2.742" };
SimpleNAT.Ipv6Regex = '^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$';
function routerTypeToPropName(routerType) {
switch (routerType) {
case aws_ec2_1.RouterType.EGRESS_ONLY_INTERNET_GATEWAY: return 'egressOnlyInternetGatewayId';
case aws_ec2_1.RouterType.GATEWAY: return 'gatewayId';
case aws_ec2_1.RouterType.INSTANCE: return 'instanceId';
case aws_ec2_1.RouterType.NAT_GATEWAY: return 'natGatewayId';
case aws_ec2_1.RouterType.NETWORK_INTERFACE: return 'networkInterfaceId';
case aws_ec2_1.RouterType.VPC_PEERING_CONNECTION: return 'vpcPeeringConnectionId';
default: throw new Error('Unsupported router type');
}
}
/**
* Preferential set
*
* Picks the value with the given key if available, otherwise distributes
* evenly among the available options.
*/
class PrefSet {
constructor() {
this.map = {};
this.vals = new Array();
this.next = 0;
}
add(pref, value) {
this.map[pref] = value;
this.vals.push([pref, value]);
}
pick(pref) {
if (this.vals.length === 0) {
throw new Error('Cannot pick, set is empty');
}
if (pref in this.map) {
return this.map[pref];
}
return this.vals[this.next++ % this.vals.length][1];
}
values() {
return this.vals;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSx5REFBeUQ7QUFDekQseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUM3Qiw2Q0FBbUY7QUFDbkYsaURBUTZCO0FBQzdCLGlEQUFvRztBQUVwRyxxQ0FBcUM7QUFDckMsTUFBTSxLQUFLLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO0FBOEVwQzs7R0FFRztBQUNILE1BQWEsU0FBVSxTQUFRLHNCQUFRO0lBWXJDLFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBcUI7UUFDN0QsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQVRYLGFBQVEsR0FBeUIsSUFBSSxPQUFPLEVBQWUsQ0FBQztRQUc1RCxzQkFBaUIsR0FBNEIsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUU5QywyQkFBc0IsR0FBRyxFQUFFLENBQUM7UUFDNUIsZUFBVSxHQUFHLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUs1RCxJQUFJLE9BQU8sQ0FBQztRQUNaLElBQUk7WUFDRixPQUFPLEdBQUcsS0FBSyxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLG1CQUFtQixJQUFJO2dCQUM3RCxVQUFVLEVBQUUsb0JBQVUsQ0FBQyxNQUFNO2dCQUM3QixRQUFRLEVBQUUsSUFBSTthQUNmLENBQUMsQ0FBQztTQUNKO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDVixNQUFNLElBQUksS0FBSyxDQUFDLDhDQUE4QyxDQUFDLENBQUM7U0FDakU7UUFFRCxJQUFJLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRTtZQUFDLE1BQU0sSUFBSSxLQUFLLENBQUMsNkRBQTZELENBQUMsQ0FBQztTQUFDO1FBRXpHLE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxZQUFZLElBQUksc0JBQVksQ0FBQyxrQkFBa0Is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RUFBRSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUVNLE1BQU07UUFDWCxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUM7SUFDbkIsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50IEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHM6IFwib2ZmXCIgKi9cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBSZXNvdXJjZSwgUmVtb3ZhbFBvbGljeSwgRHVyYXRpb24sIFRhZ3MsIEFubm90YXRpb25zIH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHtcbiAgSVZwYywgU3VibmV0U2VsZWN0aW9uLCBJbnN0YW5jZSwgSW5zdGFuY2VDbGFzcywgSW5zdGFuY2VTaXplLFxuICBJbnN0YW5jZVR5cGUsIFN1Ym5ldFR5cGUsIFBlZXIsIFNlY3VyaXR5R3JvdXAsIElTZWN1cml0eUdyb3VwLCBQb3J0LFxuICBDZm5Sb3V0ZSwgSVN1Ym5ldCwgU3VibmV0LCBSb3V0ZXJUeXBlLCBBZGRSb3V0ZU9wdGlvbnMsXG4gIE1hY2hpbmVJbWFnZSwgQW1hem9uTGludXhTdG9yYWdlLCBBbWF6b25MaW51eENwdVR5cGUsXG4gIENmbk5ldHdvcmtJbnRlcmZhY2UsIENmbkVJUCwgQ2ZuRUlQQXNzb2NpYXRpb24sXG4gIENsb3VkRm9ybWF0aW9uSW5pdCwgSW5pdENvbmZpZywgSW5pdEZpbGUsIEluaXRQYWNrYWdlLCBJbml0Q29tbWFuZCwgSU1hY2hpbmVJbWFnZSwgT3BlcmF0aW5nU3lzdGVtVHlwZSxcbiAgS2V5UGFpcixcbn0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWVjMic7XG5pbXBvcnQgeyBNYW5hZ2VkUG9saWN5LCBSb2xlLCBTZXJ2aWNlUHJpbmNpcGFsLCBQb2xpY3lTdGF0ZW1lbnQsIElSb2xlIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWlhbSc7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCAqIGFzIE11c3RhY2hlIGZyb20gJ211c3RhY2hlJztcbmNvbnN0IGZldGNoID0gcmVxdWlyZSgnc3luYy1mZXRjaCcpO1xuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIE5BVCBpbnN0YW5jZXNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTaW1wbGVOQVRQcm9wcyB7XG4gIC8qKlxuICAgKiBUaGUgVlBDIHRoZSBOQVQgaW5zdGFuY2VzIHdpbGwgcmVzaWRlXG4gICAqL1xuICByZWFkb25seSB2cGM6IElWcGM7XG4gIC8qKlxuICAgKiBUaGUgc3VibmV0IHNlbGVjdGlvbiBmb3IgTkFUIGluc3RhbmNlcywgb25lIE5BVCBpbnN0YW5jZSB3aWxsIGJlIHBsYWNlZCBpbiB0aGUgc2VsZWN0ZWQgc3VibmV0cy5cbiAgICpcbiAgICogTk9URTogbXVzdCBzZWxlY3QgdGhlIHB1YmxpYyBzdWJuZXRcbiAgICpcbiAgICogQGRlZmF1bHQgLSBzdWJuZXRUeXBlIGlzIFN1Ym5ldFR5cGUuUFVCTElDIGFuZCBvbmVQZXJBWiBpcyB0cnVlLlxuICAgKi9cbiAgcmVhZG9ubHkgbmF0U3VibmV0c1NlbGVjdGlvbj86IFN1Ym5ldFNlbGVjdGlvbjtcbiAgLyoqXG4gICAqIFRoZSBzdWJuZXQgc2VsZWN0aW9uIGZvciB1cGRhdGluZyByb3V0ZSB0YWJsZXMgZm9yIHNlbGVjdGVkIHN1Ym5ldHMuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gc3VibmV0VHlwZSBpcyBTdWJuZXRUeXBlLlBSSVZBVEVfV0lUSF9OQVQuXG4gICAqL1xuICByZWFkb25seSBwcml2YXRlU3VibmV0c1NlbGVjdGlvbj86IFN1Ym5ldFNlbGVjdGlvbjtcbiAgLyoqXG4gICAqIFRoZSBpbnN0YW5jZSB0eXBlIG9mIE5BVCBpbnN0YW5jZXNcbiAgICpcbiAgICogQGRlZmF1bHQgLSB0My5NSUNSTy5cbiAgICovXG4gIHJlYWRvbmx5IGluc3RhbmNlVHlwZT86IEluc3RhbmNlVHlwZTtcbiAgLyoqXG4gICAqIFRoZSBBTUkgb2YgTkFUIGluc3RhbmNlc1xuICAgKlxuICAgKiBAZGVmYXVsdCAtIEFtYXpvbiBMaW51eCAyIGZvciB4ODZfNjQuXG4gICAqL1xuICByZWFkb25seSBtYWNoaW5lSW1hZ2U/OiBJTWFjaGluZUltYWdlO1xuICAvKipcbiAgICogVGhlIGtleSBuYW1lIG9mIHNzaCBrZXkgb2YgTkFUIGluc3RhbmNlcy5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBObyBTU0ggYWNjZXNzIHdpbGwgYmUgcG9zc2libGUuXG4gICAqL1xuICByZWFkb25seSBrZXlOYW1lPzogc3RyaW5nO1xuICAvKipcbiAgICogVGhlIGN1c3RvbSBzY3JpcHQgd2hlbiBwcm92aXNpb25pbmcgdGhlIE5BVCBpbnN0YW5jZXMuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gbm8gY3VzdG9tIHNjcmlwdC5cbiAgICovXG4gIHJlYWRvbmx5IGN1c3RvbVNjcmlwdHM/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBUaGUgSUFNIHJvbGUgYXR0YWNoZWQgdG8gTkFUIGluc3RhbmNlcy5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBhbiBJQU0gcm9sZSBpcyBjcmVhdGVkLlxuICAgKi9cbiAgcmVhZG9ubHkgcm9sZT86IElSb2xlO1xufVxuXG5pbnRlcmZhY2UgTkFUSW5zdGFuY2Uge1xuICByZWFkb25seSBpbnN0YW5jZTogSW5zdGFuY2U7XG4gIHJlYWRvbmx5IGVuaTogQ2ZuTmV0d29ya0ludGVyZmFjZTtcbn1cblxuaW50ZXJmYWNlIFJvdXRlU3RhdHMge1xuICBpcHY0OiBudW1iZXI7XG4gIGlwdjY6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBQcm9wZXJ0aWVzIGZvciBob3cgYWRkaW5nIElQcyB0byByb3V0ZVxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJvdXRlUHJvcHMge1xuICAvKipcbiAgICogSWYgZXhjbHVkaW5nIElQdjYgd2hlbiBjcmVhdGluZyByb3V0ZVxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGZhbHNlXG4gICAqL1xuICByZWFkb25seSBleGNsdWRlSVB2Nj86IGJvb2xlYW47XG59XG5cbi8qKlxuICogU2ltcGxlIE5BVCBpbnN0YW5jZXMgY29uc3RydWN0LlxuICovXG5leHBvcnQgY2xhc3MgU2ltcGxlTkFUIGV4dGVuZHMgUmVzb3VyY2Uge1xuXG4gIHN0YXRpYyByZWFkb25seSBJcHY2UmVnZXggPSAnXnMqKCgoWzAtOUEtRmEtZl17MSw0fTopezd9KFswLTlBLUZhLWZdezEsNH18OikpfCgoWzAtOUEtRmEtZl17MSw0fTopezZ9KDpbMC05QS1GYS1mXXsxLDR9fCgoMjVbMC01XXwyWzAtNF1kfDFkZHxbMS05XT9kKSguKDI1WzAtNV18MlswLTRdZHwxZGR8WzEtOV0/ZCkpezN9KXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7NX0oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSwyfSl8OigoMjVbMC01XXwyWzAtNF1kfDFkZHxbMS05XT9kKSguKDI1WzAtNV18MlswLTRdZHwxZGR8WzEtOV0/ZCkpezN9KXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7NH0oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSwzfSl8KCg6WzAtOUEtRmEtZl17MSw0fSk/OigoMjVbMC01XXwyWzAtNF1kfDFkZHxbMS05XT9kKSguKDI1WzAtNV18MlswLTRdZHwxZGR8WzEtOV0/ZCkpezN9KSl8OikpfCgoWzAtOUEtRmEtZl17MSw0fTopezN9KCgoOlswLTlBLUZhLWZdezEsNH0pezEsNH0pfCgoOlswLTlBLUZhLWZdezEsNH0pezAsMn06KCgyNVswLTVdfDJbMC00XWR8MWRkfFsxLTldP2QpKC4oMjVbMC01XXwyWzAtNF1kfDFkZHxbMS05XT9kKSl7M30pKXw6KSl8KChbMC05QS1GYS1mXXsxLDR9Oil7Mn0oKCg6WzAtOUEtRmEtZl17MSw0fSl7MSw1fSl8KCg6WzAtOUEtRmEtZl17MSw0fSl7MCwzfTooKDI1WzAtNV18MlswLTRdZHwxZGR8WzEtOV0/ZCkoLigyNVswLTVdfDJbMC00XWR8MWRkfFsxLTldP2QpKXszfSkpfDopKXwoKFswLTlBLUZhLWZdezEsNH06KXsxfSgoKDpbMC05QS1GYS1mXXsxLDR9KXsxLDZ9KXwoKDpbMC05QS1GYS1mXXsxLDR9KXswLDR9OigoMjVbMC01XXwyWzAtNF1kfDFkZHxbMS05XT9kKSguKDI1WzAtNV18MlswLTRdZHwxZGR8WzEtOV0/ZCkpezN9KSl8OikpfCg6KCgoOlswLTlBLUZhLWZdezEsNH0pezEsN30pfCgoOlswLTlBLUZhLWZdezEsNH0pezAsNX06KCgyNVswLTVdfDJbMC00XWR8MWRkfFsxLTldP2QpKC4oMjVbMC01XXwyWzAtNF1kfDFkZHxbMS05XT9kKSl7M30pKXw6KSkpKCUuKyk/cyooLyhbMC05XXxbMS05XVswLTldfDFbMC0xXVswLTldfDEyWzAtOF0pKT8kJztcblxuICBwcml2YXRlIGdhdGV3YXlzOiBQcmVmU2V0PE5BVEluc3RhbmNlPiA9IG5ldyBQcmVmU2V0PE5BVEluc3RhbmNlPigpO1xuICBwcml2YXRlIF9zZWN1cml0eUdyb3VwOiBJU2VjdXJpdHlHcm91cDtcbiAgcHJpdmF0ZSBfcm91dGVNYXBwaW5nU3VibmV0czogTWFwPHN0cmluZywgU3VibmV0W10+O1xuICBwcml2YXRlIF9yb3V0ZVRhYmxlc0xpbWl0OiBNYXA8c3RyaW5nLCBSb3V0ZVN0YXRzPiA9IG5ldyBNYXAoKTtcblxuICBwcml2YXRlIHJlYWRvbmx5IF9kZWZhdWx0Um91dGVzUGVyVGFibGUgPSA1MDtcbiAgcHJpdmF0ZSByZWFkb25seSBfaXBWNlJlZ2V4ID0gbmV3IFJlZ0V4cChTaW1wbGVOQVQuSXB2NlJlZ2V4KTtcblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogU2ltcGxlTkFUUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuXG4gICAgbGV0IHN1Ym5ldHM7XG4gICAgdHJ5IHtcbiAgICAgIHN1Ym5ldHMgPSBwcm9wcy52cGMuc2VsZWN0U3VibmV0cyhwcm9wcy5uYXRTdWJuZXRzU2VsZWN0aW9uID8/IHtcbiAgICAgICAgc3VibmV0VHlwZTogU3VibmV0VHlwZS5QVUJMSUMsXG4gICAgICAgIG9uZVBlckF6OiB0cnVlLFxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdOQVQgaW5zdGFuY2VzIG11c3QgcmVzaWRlIGluIHB1YmxpYyBzdWJuZXRzLicpO1xuICAgIH1cblxuICAgIGlmICghc3VibmV0cy5oYXNQdWJsaWMpIHt0aHJvdyBuZXcgRXJyb3IoJ1RoZSBjdXN0b20gTkFUIHN1Ym5ldCBzZWxlY3Rpb24gTVVTVCBzZWxlY3QgUFVCTElDIHN1Ym5ldHMuJyk7fVxuXG4gICAgY29uc3QgbWFjaGluZUltYWdlID0gcHJvcHMubWFjaGluZUltYWdlID8/IE1hY2hpbmVJbWFnZS5sYXRlc3RBbWF6b25MaW51eDIoe1xuICAgICAgc3RvcmFnZTogQW1hem9uTGludXhTdG9yYWdlLkdFTkVSQUxfUFVSUE9TRSxcbiAgICAgIGNwdVR5cGU6IEFtYXpvbkxpbnV4Q3B1VHlwZS5YODZfNjQsXG4gICAgfSk7XG5cbiAgICBpZiAobWFjaGluZUltYWdlLmdldEltYWdlKHRoaXMpLm9zVHlwZSAhPSBPcGVyYXRpbmdTeXN0ZW1UeXBlLkxJTlVYKSB7dGhyb3cgbmV3IEVycm9yKCdUaGUgT1Mgb2YgY3VzdG9tIEFNSSBtdXN0IGJlIExpbnV4LicpO31cblxuICAgIHRoaXMuX3NlY3VyaXR5R3JvdXAgPSBuZXcgU2VjdXJpdHlHcm91cChzY29wZSwgJ05hdFNlY3VyaXR5R3JvdXAnLCB7XG4gICAgICB2cGM6IHByb3BzLnZwYyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnU2VjdXJpdHkgR3JvdXAgZm9yIE5BVCBpbnN0YW5jZXMnLFxuICAgICAgYWxsb3dBbGxPdXRib3VuZDogdHJ1ZSxcbiAgICB9KTtcbiAgICB0aGlzLl9zZWN1cml0eUdyb3VwLmFkZEluZ3Jlc3NSdWxlKFBlZXIuaXB2NChwcm9wcy52cGMudnBjQ2lkckJsb2NrKSwgUG9ydC5hbGxUcmFmZmljKCkpO1xuXG4gICAgY29uc3Qgcm9sZSA9IHByb3BzLnJvbGUgPz8gbmV3IFJvbGUoc2NvcGUsICdOYXRSb2xlJywge1xuICAgICAgYXNzdW1lZEJ5OiBuZXcgU2VydmljZVByaW5jaXBhbCgnZWMyLmFtYXpvbmF3cy5jb20nKSxcbiAgICB9KTtcbiAgICByb2xlLmFkZE1hbmFnZWRQb2xpY3koTWFuYWdlZFBvbGljeS5mcm9tQXdzTWFuYWdlZFBvbGljeU5hbWUoJ0FtYXpvblNTTU1hbmFnZWRJbnN0YW5jZUNvcmUnKSk7XG4gICAgcm9sZS5hZGRUb1ByaW5jaXBhbFBvbGljeShuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgIGFjdGlvbnM6IFtcbiAgICAgICAgJ2VjMjpBdHRhY2hOZXR3b3JrSW50ZXJmYWNlJyxcbiAgICAgICAgJ2VjMjpEZXRhY2hOZXR3b3JrSW50ZXJmYWNlJyxcbiAgICAgICAgJ2VjMjpEZXNjcmliZU5ldHdvcmtJbnRlcmZhY2VzJyxcbiAgICAgIF0sXG4gICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgIH0pKTtcblxuICAgIGZvciAoY29uc3Qgc3ViIG9mIHN1Ym5ldHMuc3VibmV0cykge1xuXG4gICAgICBjb25zdCBlbmkgPSBuZXcgQ2ZuTmV0d29ya0ludGVyZmFjZShzdWIgYXMgU3VibmV0LCAnRU5JMScsIHtcbiAgICAgICAgc3VibmV0SWQ6IHN1Yi5zdWJuZXRJZCxcbiAgICAgICAgc291cmNlRGVzdENoZWNrOiBmYWxzZSxcbiAgICAgICAgZGVzY3JpcHRpb246ICdFTkkgZm9yIGJpbmRpbmcgRUlQJyxcbiAgICAgICAgZ3JvdXBTZXQ6IFt0aGlzLl9zZWN1cml0eUdyb3VwLnNlY3VyaXR5R3JvdXBJZF0sXG4gICAgICB9KTtcbiAgICAgIGNvbnN0IGVpcCA9IG5ldyBDZm5FSVAoc3ViIGFzIFN1Ym5ldCwgJ05hdEluc3RhbmNlRUlQJywge1xuICAgICAgICB0YWdzOiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAga2V5OiAnTmFtZScsXG4gICAgICAgICAgICB2YWx1ZTogYEVJUCBmb3IgTkFUIGluc3RhbmNlIGluIHN1Ym5ldCAnJHtzdWIuc3VibmV0SWR9Jy5gLFxuICAgICAgICAgIH0sXG4gICAgICAgIF0sXG4gICAgICB9KTtcbiAgICAgIGVpcC5hcHBseVJlbW92YWxQb2xpY3koUmVtb3ZhbFBvbGljeS5SRVRBSU4pO1xuICAgICAgbmV3IENmbkVJUEFzc29jaWF0aW9uKHN1YiBhcyBTdWJuZXQsICdFSVBBc3NvY2lhdGlvbicsIHtcbiAgICAgICAgYWxsb2NhdGlvbklkOiBlaXAuYXR0ckFsbG9jYXRpb25JZCxcbiAgICAgICAgbmV0d29ya0ludGVyZmFjZUlkOiBlbmkucmVmLFxuICAgICAgfSk7XG5cbiAgICAgIGNvbnN0IGNvbmZpZ3MgPSBbXG4gICAgICAgIEluaXRGaWxlLmZyb21GaWxlSW5saW5lKCcvb3B0L25hdC9zbmF0LnNoJywgcGF0aC5qb2luKF9fZGlybmFtZSwgJ3NuYXQuc2gnKSwge1xuICAgICAgICAgIG1vZGU6ICcwMDA3NTUnLFxuICAgICAgICB9KSxcbiAgICAgICAgSW5pdEZpbGUuZnJvbUZpbGVJbmxpbmUoJy9ldGMvc3lzdGVtZC9zeXN0ZW0vc25hdC5zZXJ2aWNlJywgcGF0aC5qb2luKF9fZGlybmFtZSwgJ3NuYXQuc2VydmljZScpKSxcbiAgICAgICAgSW5pdEZpbGUuZnJvbVN0cmluZygnL29wdC9uYXQvcnVub25jZS5zaCcsXG4gICAgICAgICAgTXVzdGFjaGUucmVuZGVyKGZzLnJlYWRGaWxlU3luYyhwYXRoLmpvaW4oX19kaXJuYW1lLCAncnVub25jZS5zaCcpLCAndXRmLTgnKSwge1xuICAgICAgICAgICAgZW5pSWQ6IGVuaS5yZWYsXG4gICAgICAgICAgfSksIHtcbiAgICAgICAgICAgIG1vZGU6ICcwMDA3NTUnLFxuICAgICAgICAgIH0pLFxuICAgICAgICBJbml0Q29tbWFuZC5zaGVsbENvbW1hbmQoJy9vcHQvbmF0L3J1bm9uY2Uuc2gnKSxcbiAgICAgIF07XG4gICAgICBpZiAocHJvcHMuY3VzdG9tU2NyaXB0cykge1xuICAgICAgICBjb25maWdzLnB1c2goSW5pdEZpbGUuZnJvbVN0cmluZygnL29wdC9uYXQvY3VzdG9tLnNoJywgcHJvcHMuY3VzdG9tU2NyaXB0cywge1xuICAgICAgICAgIG1vZGU6ICcwMDA3NTUnLFxuICAgICAgICB9KSk7XG4gICAgICAgIGNvbmZpZ3MucHVzaChJbml0Q29tbWFuZC5zaGVsbENvbW1hbmQoJy9vcHQvbmF0L2N1c3RvbS5zaCcpKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IG5hdEluc3RhbmNlID0gbmV3IEluc3RhbmNlKHN1YiBhcyBTdWJuZXQsICdOYXRJbnN0YW5jZScsIHtcbiAgICAgICAgaW5zdGFuY2VUeXBlOiBwcm9wcy5pbnN0YW5jZVR5cGUgPz8gSW5zdGFuY2VUeXBlLm9mKEluc3RhbmNlQ2xhc3MuVDMsIEluc3RhbmNlU2l6ZS5NSUNSTyksXG4gICAgICAgIG1hY2hpbmVJbWFnZSxcbiAgICAgICAgdnBjOiBwcm9wcy52cGMsXG4gICAgICAgIHZwY1N1Ym5ldHM6IHsgc3VibmV0czogW3N1Yl0gfSxcbiAgICAgICAgc2VjdXJpdHlHcm91cDogdGhpcy5fc2VjdXJpdHlHcm91cCxcbiAgICAgICAgcm9sZSxcbiAgICAgICAga2V5UGFpcjogcHJvcHMua2V5TmFtZSA/IEtleVBhaXIuZnJvbUtleVBhaXJOYW1lKHRoaXMsICdLZXlQYWlyJywgcHJvcHMua2V5TmFtZSkgOiB1bmRlZmluZWQsXG4gICAgICAgIGluaXQ6IENsb3VkRm9ybWF0aW9uSW5pdC5mcm9tQ29uZmlnU2V0cyh7XG4gICAgICAgICAgY29uZmlnU2V0czoge1xuICAgICAgICAgICAgZGVmYXVsdDogWyd5dW1QcmVpbnN0YWxsJywgJ2NvbmZpZyddLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgY29uZmlnczoge1xuICAgICAgICAgICAgeXVtUHJlaW5zdGFsbDogbmV3IEluaXRDb25maWcoW1xuICAgICAgICAgICAgICAvLyBJbnN0YWxsIGFuIEFtYXpvbiBMaW51eCBwYWNrYWdlIHVzaW5nIHl1bVxuICAgICAgICAgICAgICBJbml0UGFja2FnZS55dW0oJ2pxJyksXG4gICAgICAgICAgICBdKSxcbiAgICAgICAgICAgIGNvbmZpZzogbmV3IEluaXRDb25maWcoY29uZmlncyksXG4gICAgICAgICAgfSxcbiAgICAgICAgfSksXG4gICAgICAgIGluaXRPcHRpb25zOiB7XG4gICAgICAgICAgLy8gT3B0aW9uYWwsIHdoaWNoIGNvbmZpZ3NldHMgdG8gYWN0aXZhdGUgKFsnZGVmYXVsdCddIGJ5IGRlZmF1bHQpXG4gICAgICAgICAgY29uZmlnU2V0czogWydkZWZhdWx0J10sXG4gICAgICAgICAgdGltZW91dDogRHVyYXRpb24ubWludXRlcyg1ICsgKHByb3BzLmN1c3RvbVNjcmlwdHMgPyAxMCA6IDApKSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuXG4gICAgICAvLyBOQVQgaW5zdGFuY2Ugcm91dGVzIGFsbCB0cmFmZmljLCBib3RoIHdheXNcbiAgICAgIHRoaXMuZ2F0ZXdheXMuYWRkKHN1Yi5hdmFpbGFiaWxpdHlab25lLCB7XG4gICAgICAgIGluc3RhbmNlOiBuYXRJbnN0YW5jZSxcbiAgICAgICAgZW5pLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgdGhpcy5fcm91dGVNYXBwaW5nU3VibmV0cyA9IHByb3BzLnZwYy5zZWxlY3RTdWJuZXRzKHByb3BzLnByaXZhdGVTdWJuZXRzU2VsZWN0aW9uID8/IHtcbiAgICAgIHN1Ym5ldFR5cGU6IFN1Ym5ldFR5cGUuUFJJVkFURV9XSVRIX0VHUkVTUyxcbiAgICB9KS5zdWJuZXRzLnJlZHVjZSgocm91dGVNYXBwaW5nLCBzdWIpID0+IHtcbiAgICAgIGlmIChyb3V0ZU1hcHBpbmcuaGFzKHN1Yi5yb3V0ZVRhYmxlLnJvdXRlVGFibGVJZCkpIHtcbiAgICAgICAgcm91dGVNYXBwaW5nLmdldChzdWIucm91dGVUYWJsZS5yb3V0ZVRhYmxlSWQpLnB1c2goc3ViKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJvdXRlTWFwcGluZy5zZXQoc3ViLnJvdXRlVGFibGUucm91dGVUYWJsZUlkLCBbc3ViXSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gcm91dGVNYXBwaW5nO1xuICAgIH0sIG5ldyBNYXAoKSk7XG5cbiAgICBUYWdzLm9mKHRoaXMpLmFkZCgnY29uc3RydWN0JywgJ3NpbXBsZS1uYXQnKTtcbiAgfVxuXG4gIHB1YmxpYyBhZGRWNFJvdXRlKHY0Q0lEUjogc3RyaW5nKTogdGhpcyB7XG4gICAgLy8gQWRkIHJvdXRlcyB0byB0aGVtIGluIHRoZSBwcml2YXRlIHN1Ym5ldHNcbiAgICBmb3IgKGNvbnN0IFtyb3V0ZUlkLCBzdWJuZXRzXSBvZiB0aGlzLl9yb3V0ZU1hcHBpbmdTdWJuZXRzKSB7XG4gICAgICB0aGlzLl9jb25maWd1cmVTdWJuZXQocm91dGVJZCwgc3VibmV0cywgdjRDSURSKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICBwdWJsaWMgYWRkVjZSb3V0ZSh2NkNJRFI6IHN0cmluZyk6IHRoaXMge1xuICAgIC8vIEFkZCByb3V0ZXMgdG8gdGhlbSBpbiB0aGUgcHJpdmF0ZSBzdWJuZXRzXG4gICAgZm9yIChjb25zdCBbcm91dGVJZCwgc3VibmV0c10gb2YgdGhpcy5fcm91dGVNYXBwaW5nU3VibmV0cykge1xuICAgICAgdGhpcy5fY29uZmlndXJlU3VibmV0KHJvdXRlSWQsIHN1Ym5ldHMsIHVuZGVmaW5lZCwgdjZDSURSKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICAvKipcbiAgICogQWRkIEdpdGh1YiBJUHMgdG8gcm91dGUgdGFibGVcbiAgICovXG4gIHB1YmxpYyB3aXRoR2l0aHViUm91dGUocHJvcHM/OiBSb3V0ZVByb3BzKTogdGhpcyB7XG4gICAgY29uc3QgZ2l0aHViTWV0YSA9IGZldGNoKCdodHRwczovL2FwaS5naXRodWIuY29tL21ldGEnKS5qc29uKCk7XG4gICAgZm9yIChjb25zdCBjaWRyIG9mIGdpdGh1Yk1ldGEuZ2l0KSB7XG4gICAgICBmb3IgKGNvbnN0IFtyb3V0ZUlkLCBzdWJuZXRzXSBvZiB0aGlzLl9yb3V0ZU1hcHBpbmdTdWJuZXRzKSB7XG4gICAgICAgIGlmICh0aGlzLl9pcFY2UmVnZXgudGVzdChjaWRyKSkge1xuICAgICAgICAgIGNvbnN0IGV4Y2x1ZGVJUHY2ID0gcHJvcHM/LmV4Y2x1ZGVJUHY2ID8/IGZhbHNlO1xuICAgICAgICAgIGlmICghZXhjbHVkZUlQdjYpIHt0aGlzLl9jb25maWd1cmVTdWJuZXQocm91dGVJZCwgc3VibmV0cywgdW5kZWZpbmVkLCBjaWRyKTt9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy5fY29uZmlndXJlU3VibmV0KHJvdXRlSWQsIHN1Ym5ldHMsIGNpZHIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBHb29nbGUgSVBzIHRvIHJvdXRlIHRhYmxlXG4gICAqL1xuICBwdWJsaWMgd2l0aEdvb2dsZVJvdXRlKHByb3BzPzogUm91dGVQcm9wcyk6IHRoaXMge1xuICAgIGNvbnN0IGdvb2dsZU1ldGEgPSBmZXRjaCgnaHR0cHM6Ly93d3cuZ3N0YXRpYy5jb20vaXByYW5nZXMvZ29vZy5qc29uJykuanNvbigpO1xuICAgIGNvbnN0IGV4Y2x1ZGVJUHY2ID0gcHJvcHM/LmV4Y2x1ZGVJUHY2ID8/IGZhbHNlO1xuICAgIGZvciAoY29uc3QgY2lkciBvZiBnb29nbGVNZXRhLnByZWZpeGVzKSB7XG4gICAgICBmb3IgKGNvbnN0IFtyb3V0ZUlkLCBzdWJuZXRzXSBvZiB0aGlzLl9yb3V0ZU1hcHBpbmdTdWJuZXRzKSB7XG4gICAgICAgIGlmIChjaWRyLmlwdjRQcmVmaXgpIHt0aGlzLl9jb25maWd1cmVTdWJuZXQocm91dGVJZCwgc3VibmV0cywgY2lkci5pcHY0UHJlZml4KTt9XG4gICAgICAgIGlmIChjaWRyLmlwdjZQcmVmaXggJiYgIWV4Y2x1ZGVJUHY2KSB7dGhpcy5fY29uZmlndXJlU3VibmV0KHJvdXRlSWQsIHN1Ym5ldHMsIHVuZGVmaW5lZCwgY2lkci5pcHY2UHJlZml4KTt9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZCBDbG91ZGZsYXJlIElQcyB0byByb3V0ZSB0YWJsZVxuICAgKlxuICAgKiBTZWUgaHR0cHM6Ly93d3cuY2xvdWRmbGFyZS5jb20vaXBzLyBmb3IgZGV0YWlsc1xuICAgKi9cbiAgcHVibGljIHdpdGhDbG91ZGZsYXJlUm91dGUocHJvcHM/OiBSb3V0ZVByb3BzKTogdGhpcyB7XG4gICAgY29uc3QgaXBWNCA9IGZldGNoKCdodHRwczovL3d3dy5jbG91ZGZsYXJlLmNvbS9pcHMtdjQnKS50ZXh0KCkuc3BsaXQoL1xccj9cXG4vKTtcbiAgICBmb3IgKGNvbnN0IGNpZHIgb2YgaXBWNCkge1xuICAgICAgZm9yIChjb25zdCBbcm91dGVJZCwgc3VibmV0c10gb2YgdGhpcy5fcm91dGVNYXBwaW5nU3VibmV0cykge1xuICAgICAgICB0aGlzLl9jb25maWd1cmVTdWJuZXQocm91dGVJZCwgc3VibmV0cywgY2lkcik7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGV4Y2x1ZGVJUHY2ID0gcHJvcHM/LmV4Y2x1ZGVJUHY2ID8/IGZhbHNlO1xuICAgIGlmICghZXhjbHVkZUlQdjYpIHtcbiAgICAgIGNvbnN0IGlwVjYgPSBmZXRjaCgnaHR0cHM6Ly93d3cuY2xvdWRmbGFyZS5jb20vaXBzLXY2JykudGV4dCgpLnNwbGl0KC9cXHI/XFxuLyk7XG4gICAgICBmb3IgKGNvbnN0IGNpZHIgb2YgaXBWNikge1xuICAgICAgICBmb3IgKGNvbnN0IFtyb3V0ZUlkLCBzdWJuZXRzXSBvZiB0aGlzLl9yb3V0ZU1hcHBpbmdTdWJuZXRzKSB7XG4gICAgICAgICAgdGhpcy5fY29uZmlndXJlU3VibmV0KHJvdXRlSWQsIHN1Ym5ldHMsIHVuZGVmaW5lZCwgY2lkcik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICBwcml2YXRlIF9jb25maWd1cmVTdWJuZXQoX3JvdXRlSWQ6IHN0cmluZywgc3VibmV0czogU3VibmV0W10sIHY0Q0lEUj86IHN0cmluZywgdjZDSURSPzogc3RyaW5nKSA6IHRoaXMge1xuICAgIGNvbnN0IGF6ID0gc3VibmV0c1swXS5hdmFpbGFiaWxpdHlab25lO1xuICAgIGNvbnN0IG5hdEluc3RhbmNlID0gdGhpcy5nYXRld2F5cy5waWNrKGF6KTtcbiAgICB0aGlzLl9hZGRSb3V0ZShgUm91dGUtJHt2NENJRFIgPyAndjQtJyArIHY0Q0lEUj8ucmVwbGFjZSgvWy4vXS9naSwgJy0nKSA6ICd2Ni0nICsgdjZDSURSPy5yZXBsYWNlKC9bOi9dL2dpLCAnLScpfWAsIHN1Ym5ldHNbMF0sIHtcbiAgICAgIGRlc3RpbmF0aW9uQ2lkckJsb2NrOiB2NENJRFIsXG4gICAgICBkZXN0aW5hdGlvbklwdjZDaWRyQmxvY2s6IHY2Q0lEUixcbiAgICAgIHJvdXRlclR5cGU6IFJvdXRlclR5cGUuTkVUV09SS19JTlRFUkZBQ0UsXG4gICAgICByb3V0ZXJJZDogbmF0SW5zdGFuY2UuZW5pLnJlZixcbiAgICAgIGVuYWJsZXNJbnRlcm5ldENvbm5lY3Rpdml0eTogdHJ1ZSxcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIHByaXZhdGUgX2FkZFJvdXRlKGlkOiBzdHJpbmcsIHN1Ym5ldDogSVN1Ym5ldCwgb3B0aW9uczogQWRkUm91dGVPcHRpb25zKSB7XG4gICAgaWYgKG9wdGlvbnMuZGVzdGluYXRpb25DaWRyQmxvY2sgJiYgb3B0aW9ucy5kZXN0aW5hdGlvbklwdjZDaWRyQmxvY2spIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IHNwZWNpZnkgYm90aCBcXCdkZXN0aW5hdGlvbkNpZHJCbG9ja1xcJyBhbmQgXFwnZGVzdGluYXRpb25JcHY2Q2lkckJsb2NrXFwnJyk7XG4gICAgfVxuXG4gICAgbmV3IENmblJvdXRlKHN1Ym5ldCBhcyBTdWJuZXQsIGlkLCB7XG4gICAgICByb3V0ZVRhYmxlSWQ6IHN1Ym5ldC5yb3V0ZVRhYmxlLnJvdXRlVGFibGVJZCxcbiAgICAgIGRlc3RpbmF0aW9uQ2lkckJsb2NrOiBvcHRpb25zLmRlc3RpbmF0aW9uQ2lkckJsb2NrID8/IChvcHRpb25zLmRlc3RpbmF0aW9uSXB2NkNpZHJCbG9jayA9PT0gdW5kZWZpbmVkID8gJzAuMC4wLjAvMCcgOiB1bmRlZmluZWQpLFxuICAgICAgZGVzdGluYXRpb25JcHY2Q2lkckJsb2NrOiBvcHRpb25zLmRlc3RpbmF0aW9uSXB2NkNpZHJCbG9jayxcbiAgICAgIFtyb3V0ZXJUeXBlVG9Qcm9wTmFtZShvcHRpb25zLnJvdXRlclR5cGUpXTogb3B0aW9ucy5yb3V0ZXJJZCxcbiAgICB9KTtcblxuICAgIGNvbnN0IGlzSXB2NCA9IChvcHRpb25zLmRlc3RpbmF0aW9uQ2lkckJsb2NrICE9IHVuZGVmaW5lZCk7XG4gICAgaWYgKHRoaXMuX3JvdXRlVGFibGVzTGltaXQuaGFzKHN1Ym5ldC5yb3V0ZVRhYmxlLnJvdXRlVGFibGVJZCkpIHtcbiAgICAgIGNvbnN0IHN0YXRzID0gdGhpcy5fcm91dGVUYWJsZXNMaW1pdC5nZXQoc3VibmV0LnJvdXRlVGFibGUucm91dGVUYWJsZUlkKSE7XG4gICAgICBpZiAoaXNJcHY0KSB7c3RhdHMuaXB2NCArPSAxO30gZWxzZSB7c3RhdHMuaXB2NiArPSAxO31cblxuICAgICAgdGhpcy5fcm91dGVUYWJsZXNMaW1pdC5zZXQoc3VibmV0LnJvdXRlVGFibGUucm91dGVUYWJsZUlkLCBzdGF0cyk7XG4gICAgICBjb25zdCB0b3RhbFJvdXRlcyA9IChpc0lwdjQgPyBzdGF0cy5pcHY0IDogc3RhdHMuaXB2Nik7XG4gICAgICBpZiAodG90YWxSb3V0ZXMgPiB0aGlzLl9kZWZhdWx0Um91dGVzUGVyVGFibGUpIHtBbm5vdGF0aW9ucy5vZih0aGlzKS5hZGRXYXJuaW5nKGBUaGUgY3VycmVudCByb3V0ZXMgaW4gcm91dGUgdGFibGUgJyR7c3VibmV0LnJvdXRlVGFibGUucm91dGVUYWJsZUlkfScgaXMgJHt0b3RhbFJvdXRlc30gd2hpY2ggZXhjZWVkcyB0aGUgZGVmYXVsdCBsaW1pdCAke3RoaXMuX2RlZmF1bHRSb3V0ZXNQZXJUYWJsZX0uIFlvdSBjYW4gb3BlbiB0aWNrZXQgdG8gaW5jcmVhc2UgaXQuYCk7fVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLl9yb3V0ZVRhYmxlc0xpbWl0LnNldChzdWJuZXQucm91dGVUYWJsZS5yb3V0ZVRhYmxlSWQsIHtcbiAgICAgICAgaXB2NDogaXNJcHY0ID8gMSA6IDAsXG4gICAgICAgIGlwdjY6IGlzSXB2NCA/IDAgOiAxLFxuICAgICAgfSk7XG4gICAgfVxuICB9XG59XG5cbmZ1bmN0aW9uIHJvdXRlclR5cGVUb1Byb3BOYW1lKHJvdXRlclR5cGU6IFJvdXRlclR5cGUpIHtcbiAgc3dpdGNoIChyb3V0ZXJUeXBlKSB7XG4gICAgY2FzZSBSb3V0ZXJUeXBlLkVHUkVTU19PTkxZX0lOVEVSTkVUX0dBVEVXQVk6IHJldHVybiAnZWdyZXNzT25seUludGVybmV0R2F0ZXdheUlkJztcbiAgICBjYXNlIFJvdXRlclR5cGUuR0FURVdBWTogcmV0dXJuICdnYXRld2F5SWQnO1xuICAgIGNhc2UgUm91dGVyVHlwZS5JTlNUQU5DRTogcmV0dXJuICdpbnN0YW5jZUlkJztcbiAgICBjYXNlIFJvdXRlclR5cGUuTkFUX0dBVEVXQVk6IHJldHVybiAnbmF0R2F0ZXdheUlkJztcbiAgICBjYXNlIFJvdXRlclR5cGUuTkVUV09SS19JTlRFUkZBQ0U6IHJldHVybiAnbmV0d29ya0ludGVyZmFjZUlkJztcbiAgICBjYXNlIFJvdXRlclR5cGUuVlBDX1BFRVJJTkdfQ09OTkVDVElPTjogcmV0dXJuICd2cGNQZWVyaW5nQ29ubmVjdGlvbklkJztcbiAgICBkZWZhdWx0OiB0aHJvdyBuZXcgRXJyb3IoJ1Vuc3VwcG9ydGVkIHJvdXRlciB0eXBlJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBQcmVmZXJlbnRpYWwgc2V0XG4gKlxuICogUGlja3MgdGhlIHZhbHVlIHdpdGggdGhlIGdpdmVuIGtleSBpZiBhdmFpbGFibGUsIG90aGVyd2lzZSBkaXN0cmlidXRlc1xuICogZXZlbmx5IGFtb25nIHRoZSBhdmFpbGFibGUgb3B0aW9ucy5cbiAqL1xuY2xhc3MgUHJlZlNldDxBPiB7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWFwOiBSZWNvcmQ8c3RyaW5nLCBBPiA9IHt9O1xuICBwcml2YXRlIHJlYWRvbmx5IHZhbHMgPSBuZXcgQXJyYXk8W3N0cmluZywgQV0+KCk7XG4gIHByaXZhdGUgbmV4dDogbnVtYmVyID0gMDtcblxuICBwdWJsaWMgYWRkKHByZWY6IHN0cmluZywgdmFsdWU6IEEpIHtcbiAgICB0aGlzLm1hcFtwcmVmXSA9IHZhbHVlO1xuICAgIHRoaXMudmFscy5wdXNoKFtwcmVmLCB2YWx1ZV0pO1xuICB9XG5cbiAgcHVibGljIHBpY2socHJlZjogc3RyaW5nKTogQSB7XG4gICAgaWYgKHRoaXMudmFscy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IHBpY2ssIHNldCBpcyBlbXB0eScpO1xuICAgIH1cblxuICAgIGlmIChwcmVmIGluIHRoaXMubWFwKSB7IHJldHVybiB0aGlzLm1hcFtwcmVmXTsgfVxuICAgIHJldHVybiB0aGlzLnZhbHNbdGhpcy5uZXh0KysgJSB0aGlzLnZhbHMubGVuZ3RoXVsxXTtcbiAgfVxuXG4gIHB1YmxpYyB2YWx1ZXMoKTogQXJyYXk8W3N0cmluZywgQV0+IHtcbiAgICByZXR1cm4gdGhpcy52YWxzO1xuICB9XG59Il19