UNPKG

cdk-construct-simple-nat

Version:

A CDK construct to build Simple NAT instance on AWS.

309 lines 47.6 kB
"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