aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
257 lines • 35.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.describeListenersByLoadBalancerArn = exports.tagsMatch = exports.describeTags = exports.describeLoadBalancers = exports.LoadBalancerListenerContextProviderPlugin = exports.LoadBalancerContextProviderPlugin = void 0;
const cxapi = require("@aws-cdk/cx-api");
const credentials_1 = require("../api/aws-auth/credentials");
/**
* Provides load balancer context information.
*/
class LoadBalancerContextProviderPlugin {
constructor(aws) {
this.aws = aws;
}
async getValue(query) {
const options = { assumeRoleArn: query.lookupRoleArn };
const elbv2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(query.account, query.region), credentials_1.Mode.ForReading, options)).sdk.elbv2();
if (!query.loadBalancerArn && !query.loadBalancerTags) {
throw new Error('The load balancer lookup query must specify either `loadBalancerArn` or `loadBalancerTags`');
}
const loadBalancers = await findLoadBalancers(elbv2, query);
if (loadBalancers.length === 0) {
throw new Error(`No load balancers found matching ${JSON.stringify(query)}`);
}
if (loadBalancers.length > 1) {
throw new Error(`Multiple load balancers found matching ${JSON.stringify(query)} - please provide more specific criteria`);
}
const loadBalancer = loadBalancers[0];
const ipAddressType = loadBalancer.IpAddressType === 'ipv4'
? cxapi.LoadBalancerIpAddressType.IPV4
: cxapi.LoadBalancerIpAddressType.DUAL_STACK;
return {
loadBalancerArn: loadBalancer.LoadBalancerArn,
loadBalancerCanonicalHostedZoneId: loadBalancer.CanonicalHostedZoneId,
loadBalancerDnsName: loadBalancer.DNSName,
vpcId: loadBalancer.VpcId,
securityGroupIds: loadBalancer.SecurityGroups ?? [],
ipAddressType: ipAddressType,
};
}
}
exports.LoadBalancerContextProviderPlugin = LoadBalancerContextProviderPlugin;
/**
* Provides load balancer listener context information
*/
class LoadBalancerListenerContextProviderPlugin {
constructor(aws) {
this.aws = aws;
}
async getValue(query) {
const options = { assumeRoleArn: query.lookupRoleArn };
const elbv2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(query.account, query.region), credentials_1.Mode.ForReading, options)).sdk.elbv2();
if (!query.listenerArn && !query.loadBalancerArn && !query.loadBalancerTags) {
throw new Error('The load balancer listener query must specify at least one of: `listenerArn`, `loadBalancerArn` or `loadBalancerTags`');
}
return query.listenerArn ? this.getListenerByArn(elbv2, query) : this.getListenerByFilteringLoadBalancers(elbv2, query);
}
/**
* Look up a listener by querying listeners for query's listener arn and then
* resolve its load balancer for the security group information.
*/
async getListenerByArn(elbv2, query) {
const listenerArn = query.listenerArn;
const listenerResults = await elbv2.describeListeners({ ListenerArns: [listenerArn] }).promise();
const listeners = (listenerResults.Listeners ?? []);
if (listeners.length === 0) {
throw new Error(`No load balancer listeners found matching arn ${listenerArn}`);
}
const listener = listeners[0];
const loadBalancers = await findLoadBalancers(elbv2, {
...query,
loadBalancerArn: listener.LoadBalancerArn,
});
if (loadBalancers.length === 0) {
throw new Error(`No associated load balancer found for listener arn ${listenerArn}`);
}
const loadBalancer = loadBalancers[0];
return {
listenerArn: listener.ListenerArn,
listenerPort: listener.Port,
securityGroupIds: loadBalancer.SecurityGroups ?? [],
};
}
/**
* Look up a listener by starting from load balancers, filtering out
* unmatching load balancers, and then by querying the listeners of each load
* balancer and filtering out unmatching listeners.
*/
async getListenerByFilteringLoadBalancers(elbv2, args) {
// Find matching load balancers
const loadBalancers = await findLoadBalancers(elbv2, args);
if (loadBalancers.length === 0) {
throw new Error(`No associated load balancers found for load balancer listener query ${JSON.stringify(args)}`);
}
return this.findMatchingListener(elbv2, loadBalancers, args);
}
/**
* Finds the matching listener from the list of load balancers. This will
* error unless there is exactly one match so that the user is prompted to
* provide more specific criteria rather than us providing a nondeterministic
* result.
*/
async findMatchingListener(elbv2, loadBalancers, query) {
const loadBalancersByArn = indexLoadBalancersByArn(loadBalancers);
const loadBalancerArns = Object.keys(loadBalancersByArn);
const matches = Array();
for await (const listener of describeListenersByLoadBalancerArn(elbv2, loadBalancerArns)) {
const loadBalancer = loadBalancersByArn[listener.LoadBalancerArn];
if (listenerMatchesQueryFilter(listener, query) && loadBalancer) {
matches.push({
listenerArn: listener.ListenerArn,
listenerPort: listener.Port,
securityGroupIds: loadBalancer.SecurityGroups ?? [],
});
}
}
if (matches.length === 0) {
throw new Error(`No load balancer listeners found matching ${JSON.stringify(query)}`);
}
if (matches.length > 1) {
throw new Error(`Multiple load balancer listeners found matching ${JSON.stringify(query)} - please provide more specific criteria`);
}
return matches[0];
}
}
exports.LoadBalancerListenerContextProviderPlugin = LoadBalancerListenerContextProviderPlugin;
/**
* Find load balancers by the given filter args.
*/
async function findLoadBalancers(elbv2, args) {
// List load balancers
let loadBalancers = await describeLoadBalancers(elbv2, {
LoadBalancerArns: args.loadBalancerArn ? [args.loadBalancerArn] : undefined,
});
// Filter by load balancer type
loadBalancers = loadBalancers.filter(lb => lb.Type === args.loadBalancerType);
// Filter by load balancer tags
if (args.loadBalancerTags) {
loadBalancers = await filterLoadBalancersByTags(elbv2, loadBalancers, args.loadBalancerTags);
}
return loadBalancers;
}
/**
* Helper to paginate over describeLoadBalancers
* @internal
*/
async function describeLoadBalancers(elbv2, request) {
const loadBalancers = Array();
let page;
do {
page = await elbv2.describeLoadBalancers({
...request,
Marker: page?.NextMarker,
}).promise();
loadBalancers.push(...Array.from(page.LoadBalancers ?? []));
} while (page.NextMarker);
return loadBalancers;
}
exports.describeLoadBalancers = describeLoadBalancers;
/**
* Describes the tags of each load balancer and returns the load balancers that
* match the given tags.
*/
async function filterLoadBalancersByTags(elbv2, loadBalancers, loadBalancerTags) {
const loadBalancersByArn = indexLoadBalancersByArn(loadBalancers);
const loadBalancerArns = Object.keys(loadBalancersByArn);
const matchingLoadBalancers = Array();
// Consume the items of async generator.
for await (const tags of describeTags(elbv2, loadBalancerArns)) {
if (tagsMatch(tags, loadBalancerTags) && loadBalancersByArn[tags.ResourceArn]) {
matchingLoadBalancers.push(loadBalancersByArn[tags.ResourceArn]);
}
}
return matchingLoadBalancers;
}
/**
* Generator function that yields `TagDescriptions`. The API doesn't support
* pagination, so this generator breaks the resource list into chunks and issues
* the appropriate requests, yielding each tag description as it receives it.
* @internal
*/
async function* describeTags(elbv2, resourceArns) {
// Max of 20 resource arns per request.
const chunkSize = 20;
for (let i = 0; i < resourceArns.length; i += chunkSize) {
const chunk = resourceArns.slice(i, Math.min(i + chunkSize, resourceArns.length));
const chunkTags = await elbv2.describeTags({
ResourceArns: chunk,
}).promise();
for (const tag of chunkTags.TagDescriptions ?? []) {
yield tag;
}
}
}
exports.describeTags = describeTags;
/**
* Determines if the given TagDescription matches the required tags.
* @internal
*/
function tagsMatch(tagDescription, requiredTags) {
const tagsByName = {};
for (const tag of tagDescription.Tags ?? []) {
tagsByName[tag.Key] = tag.Value;
}
for (const tag of requiredTags) {
if (tagsByName[tag.key] !== tag.value) {
return false;
}
}
return true;
}
exports.tagsMatch = tagsMatch;
/**
* Async generator that produces listener descriptions by traversing the
* pagination. Because describeListeners only lets you search by one load
* balancer arn at a time, we request them individually and yield the listeners
* as they come in.
* @internal
*/
async function* describeListenersByLoadBalancerArn(elbv2, loadBalancerArns) {
for (const loadBalancerArn of loadBalancerArns) {
let page;
do {
page = await elbv2.describeListeners({
LoadBalancerArn: loadBalancerArn,
Marker: page?.NextMarker,
}).promise();
for (const listener of page.Listeners ?? []) {
yield listener;
}
} while (page.NextMarker);
}
}
exports.describeListenersByLoadBalancerArn = describeListenersByLoadBalancerArn;
/**
* Determines if a listener matches the query filters.
*/
function listenerMatchesQueryFilter(listener, args) {
if (args.listenerPort && listener.Port !== args.listenerPort) {
// No match.
return false;
}
if (args.listenerProtocol && listener.Protocol !== args.listenerProtocol) {
// No match.
return false;
}
return true;
}
/**
* Returns a record of load balancers indexed by their arns
*/
function indexLoadBalancersByArn(loadBalancers) {
const loadBalancersByArn = {};
for (const loadBalancer of loadBalancers) {
loadBalancersByArn[loadBalancer.LoadBalancerArn] = loadBalancer;
}
return loadBalancersByArn;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9hZC1iYWxhbmNlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJsb2FkLWJhbGFuY2Vycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSx5Q0FBeUM7QUFFekMsNkRBQW1EO0FBSW5EOztHQUVHO0FBQ0gsTUFBYSxpQ0FBaUM7SUFDNUMsWUFBNkIsR0FBZ0I7UUFBaEIsUUFBRyxHQUFILEdBQUcsQ0FBYTtJQUM3QyxDQUFDO0lBRUQsS0FBSyxDQUFDLFFBQVEsQ0FBQyxLQUF3QztRQUNyRCxNQUFNLE9BQU8sR0FBRyxFQUFFLGFBQWEsRUFBRSxLQUFLLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDdkQsTUFBTSxLQUFLLEdBQUcsQ0FBQyxNQUFNLElBQUksQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUUsa0JBQUksQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7UUFFOUksSUFBSSxDQUFDLEtBQUssQ0FBQyxlQUFlLElBQUksQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLEVBQUU7WUFDckQsTUFBTSxJQUFJLEtBQUssQ0FBQyw0RkFBNEYsQ0FBQyxDQUFDO1NBQy9HO1FBRUQsTUFBTSxhQUFhLEdBQUcsTUFBTSxpQkFBaUIsQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFNUQsSUFBSSxhQUFhLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtZQUM5QixNQUFNLElBQUksS0FBSyxDQUFDLG9DQUFvQyxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztTQUM5RTtRQUVELElBQUksYUFBYSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7WUFDNUIsTUFBTSxJQUFJLEtBQUssQ0FBQywwQ0FBMEMsSUFBSSxDQUFD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a0IsQ0FBQyxZQUFZLENBQUMsZUFBZ0IsQ0FBQyxHQUFHLFlBQVksQ0FBQztLQUNsRTtJQUVELE9BQU8sa0JBQWtCLENBQUM7QUFDNUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGN4c2NoZW1hIGZyb20gJ0Bhd3MtY2RrL2Nsb3VkLWFzc2VtYmx5LXNjaGVtYSc7XG5pbXBvcnQgKiBhcyBjeGFwaSBmcm9tICdAYXdzLWNkay9jeC1hcGknO1xuaW1wb3J0ICogYXMgQVdTIGZyb20gJ2F3cy1zZGsnO1xuaW1wb3J0IHsgTW9kZSB9IGZyb20gJy4uL2FwaS9hd3MtYXV0aC9jcmVkZW50aWFscyc7XG5pbXBvcnQgeyBTZGtQcm92aWRlciB9IGZyb20gJy4uL2FwaS9hd3MtYXV0aC9zZGstcHJvdmlkZXInO1xuaW1wb3J0IHsgQ29udGV4dFByb3ZpZGVyUGx1Z2luIH0gZnJvbSAnLi4vYXBpL3BsdWdpbic7XG5cbi8qKlxuICogUHJvdmlkZXMgbG9hZCBiYWxhbmNlciBjb250ZXh0IGluZm9ybWF0aW9uLlxuICovXG5leHBvcnQgY2xhc3MgTG9hZEJhbGFuY2VyQ29udGV4dFByb3ZpZGVyUGx1Z2luIGltcGxlbWVudHMgQ29udGV4dFByb3ZpZGVyUGx1Z2luIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBhd3M6IFNka1Byb3ZpZGVyKSB7XG4gIH1cblxuICBhc3luYyBnZXRWYWx1ZShxdWVyeTogY3hzY2hlbWEuTG9hZEJhbGFuY2VyQ29udGV4dFF1ZXJ5KTogUHJvbWlzZTxjeGFwaS5Mb2FkQmFsYW5jZXJDb250ZXh0UmVzcG9uc2U+IHtcbiAgICBjb25zdCBvcHRpb25zID0geyBhc3N1bWVSb2xlQXJuOiBxdWVyeS5sb29rdXBSb2xlQXJuIH07XG4gICAgY29uc3QgZWxidjIgPSAoYXdhaXQgdGhpcy5hd3MuZm9yRW52aXJvbm1lbnQoY3hhcGkuRW52aXJvbm1lbnRVdGlscy5tYWtlKHF1ZXJ5LmFjY291bnQsIHF1ZXJ5LnJlZ2lvbiksIE1vZGUuRm9yUmVhZGluZywgb3B0aW9ucykpLnNkay5lbGJ2MigpO1xuXG4gICAgaWYgKCFxdWVyeS5sb2FkQmFsYW5jZXJBcm4gJiYgIXF1ZXJ5LmxvYWRCYWxhbmNlclRhZ3MpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVGhlIGxvYWQgYmFsYW5jZXIgbG9va3VwIHF1ZXJ5IG11c3Qgc3BlY2lmeSBlaXRoZXIgYGxvYWRCYWxhbmNlckFybmAgb3IgYGxvYWRCYWxhbmNlclRhZ3NgJyk7XG4gICAgfVxuXG4gICAgY29uc3QgbG9hZEJhbGFuY2VycyA9IGF3YWl0IGZpbmRMb2FkQmFsYW5jZXJzKGVsYnYyLCBxdWVyeSk7XG5cbiAgICBpZiAobG9hZEJhbGFuY2Vycy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gbG9hZCBiYWxhbmNlcnMgZm91bmQgbWF0Y2hpbmcgJHtKU09OLnN0cmluZ2lmeShxdWVyeSl9YCk7XG4gICAgfVxuXG4gICAgaWYgKGxvYWRCYWxhbmNlcnMubGVuZ3RoID4gMSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBNdWx0aXBsZSBsb2FkIGJhbGFuY2VycyBmb3VuZCBtYXRjaGluZyAke0pTT04uc3RyaW5naWZ5KHF1ZXJ5KX0gLSBwbGVhc2UgcHJvdmlkZSBtb3JlIHNwZWNpZmljIGNyaXRlcmlhYCk7XG4gICAgfVxuXG4gICAgY29uc3QgbG9hZEJhbGFuY2VyID0gbG9hZEJhbGFuY2Vyc1swXTtcblxuICAgIGNvbnN0IGlwQWRkcmVzc1R5cGUgPSBsb2FkQmFsYW5jZXIuSXBBZGRyZXNzVHlwZSA9PT0gJ2lwdjQnXG4gICAgICA/IGN4YXBpLkxvYWRCYWxhbmNlcklwQWRkcmVzc1R5cGUuSVBWNFxuICAgICAgOiBjeGFwaS5Mb2FkQmFsYW5jZXJJcEFkZHJlc3NUeXBlLkRVQUxfU1RBQ0s7XG5cbiAgICByZXR1cm4ge1xuICAgICAgbG9hZEJhbGFuY2VyQXJuOiBsb2FkQmFsYW5jZXIuTG9hZEJhbGFuY2VyQXJuISxcbiAgICAgIGxvYWRCYWxhbmNlckNhbm9uaWNhbEhvc3RlZFpvbmVJZDogbG9hZEJhbGFuY2VyLkNhbm9uaWNhbEhvc3RlZFpvbmVJZCEsXG4gICAgICBsb2FkQmFsYW5jZXJEbnNOYW1lOiBsb2FkQmFsYW5jZXIuRE5TTmFtZSEsXG4gICAgICB2cGNJZDogbG9hZEJhbGFuY2VyLlZwY0lkISxcbiAgICAgIHNlY3VyaXR5R3JvdXBJZHM6IGxvYWRCYWxhbmNlci5TZWN1cml0eUdyb3VwcyA/PyBbXSxcbiAgICAgIGlwQWRkcmVzc1R5cGU6IGlwQWRkcmVzc1R5cGUsXG4gICAgfTtcbiAgfVxufVxuXG4vLyBEZWNyZWFzZXMgbGluZSBsZW5ndGhcbnR5cGUgTG9hZEJhbGFuY2VyTGlzdGVuZXJRdWVyeSA9IGN4c2NoZW1hLkxvYWRCYWxhbmNlckxpc3RlbmVyQ29udGV4dFF1ZXJ5O1xudHlwZSBMb2FkQmFsYW5jZXJMaXN0ZW5lclJlc3BvbnNlID0gY3hhcGkuTG9hZEJhbGFuY2VyTGlzdGVuZXJDb250ZXh0UmVzcG9uc2U7XG5cbi8qKlxuICogUHJvdmlkZXMgbG9hZCBiYWxhbmNlciBsaXN0ZW5lciBjb250ZXh0IGluZm9ybWF0aW9uXG4gKi9cbmV4cG9ydCBjbGFzcyBMb2FkQmFsYW5jZXJMaXN0ZW5lckNvbnRleHRQcm92aWRlclBsdWdpbiBpbXBsZW1lbnRzIENvbnRleHRQcm92aWRlclBsdWdpbiB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVhZG9ubHkgYXdzOiBTZGtQcm92aWRlcikge1xuICB9XG5cbiAgYXN5bmMgZ2V0VmFsdWUocXVlcnk6IExvYWRCYWxhbmNlckxpc3RlbmVyUXVlcnkpOiBQcm9taXNlPExvYWRCYWxhbmNlckxpc3RlbmVyUmVzcG9uc2U+IHtcbiAgICBjb25zdCBvcHRpb25zID0geyBhc3N1bWVSb2xlQXJuOiBxdWVyeS5sb29rdXBSb2xlQXJuIH07XG4gICAgY29uc3QgZWxidjIgPSAoYXdhaXQgdGhpcy5hd3MuZm9yRW52aXJvbm1lbnQoY3hhcGkuRW52aXJvbm1lbnRVdGlscy5tYWtlKHF1ZXJ5LmFjY291bnQsIHF1ZXJ5LnJlZ2lvbiksIE1vZGUuRm9yUmVhZGluZywgb3B0aW9ucykpLnNkay5lbGJ2MigpO1xuXG4gICAgaWYgKCFxdWVyeS5saXN0ZW5lckFybiAmJiAhcXVlcnkubG9hZEJhbGFuY2VyQXJuICYmICFxdWVyeS5sb2FkQmFsYW5jZXJUYWdzKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1RoZSBsb2FkIGJhbGFuY2VyIGxpc3RlbmVyIHF1ZXJ5IG11c3Qgc3BlY2lmeSBhdCBsZWFzdCBvbmUgb2Y6IGBsaXN0ZW5lckFybmAsIGBsb2FkQmFsYW5jZXJBcm5gIG9yIGBsb2FkQmFsYW5jZXJUYWdzYCcpO1xuICAgIH1cblxuICAgIHJldHVybiBxdWVyeS5saXN0ZW5lckFybiA/IHRoaXMuZ2V0TGlzdGVuZXJCeUFybihlbGJ2MiwgcXVlcnkpIDogdGhpcy5nZXRMaXN0ZW5lckJ5RmlsdGVyaW5nTG9hZEJhbGFuY2VycyhlbGJ2MiwgcXVlcnkpO1xuICB9XG5cbiAgLyoqXG4gICAqIExvb2sgdXAgYSBsaXN0ZW5lciBieSBxdWVyeWluZyBsaXN0ZW5lcnMgZm9yIHF1ZXJ5J3MgbGlzdGVuZXIgYXJuIGFuZCB0aGVuXG4gICAqIHJlc29sdmUgaXRzIGxvYWQgYmFsYW5jZXIgZm9yIHRoZSBzZWN1cml0eSBncm91cCBpbmZvcm1hdGlvbi5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZ2V0TGlzdGVuZXJCeUFybihlbGJ2MjogQVdTLkVMQnYyLCBxdWVyeTogTG9hZEJhbGFuY2VyTGlzdGVuZXJRdWVyeSkge1xuICAgIGNvbnN0IGxpc3RlbmVyQXJuID0gcXVlcnkubGlzdGVuZXJBcm4hO1xuICAgIGNvbnN0IGxpc3RlbmVyUmVzdWx0cyA9IGF3YWl0IGVsYnYyLmRlc2NyaWJlTGlzdGVuZXJzKHsgTGlzdGVuZXJBcm5zOiBbbGlzdGVuZXJBcm5dIH0pLnByb21pc2UoKTtcbiAgICBjb25zdCBsaXN0ZW5lcnMgPSAobGlzdGVuZXJSZXN1bHRzLkxpc3RlbmVycyA/PyBbXSk7XG5cbiAgICBpZiAobGlzdGVuZXJzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBsb2FkIGJhbGFuY2VyIGxpc3RlbmVycyBmb3VuZCBtYXRjaGluZyBhcm4gJHtsaXN0ZW5lckFybn1gKTtcbiAgICB9XG5cbiAgICBjb25zdCBsaXN0ZW5lciA9IGxpc3RlbmVyc1swXTtcblxuICAgIGNvbnN0IGxvYWRCYWxhbmNlcnMgPSBhd2FpdCBmaW5kTG9hZEJhbGFuY2VycyhlbGJ2Miwge1xuICAgICAgLi4ucXVlcnksXG4gICAgICBsb2FkQmFsYW5jZXJBcm46IGxpc3RlbmVyLkxvYWRCYWxhbmNlckFybiEsXG4gICAgfSk7XG5cbiAgICBpZiAobG9hZEJhbGFuY2Vycy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gYXNzb2NpYXRlZCBsb2FkIGJhbGFuY2VyIGZvdW5kIGZvciBsaXN0ZW5lciBhcm4gJHtsaXN0ZW5lckFybn1gKTtcbiAgICB9XG5cbiAgICBjb25zdCBsb2FkQmFsYW5jZXIgPSBsb2FkQmFsYW5jZXJzWzBdO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIGxpc3RlbmVyQXJuOiBsaXN0ZW5lci5MaXN0ZW5lckFybiEsXG4gICAgICBsaXN0ZW5lclBvcnQ6IGxpc3RlbmVyLlBvcnQhLFxuICAgICAgc2VjdXJpdHlHcm91cElkczogbG9hZEJhbGFuY2VyLlNlY3VyaXR5R3JvdXBzID8/IFtdLFxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogTG9vayB1cCBhIGxpc3RlbmVyIGJ5IHN0YXJ0aW5nIGZyb20gbG9hZCBiYWxhbmNlcnMsIGZpbHRlcmluZyBvdXRcbiAgICogdW5tYXRjaGluZyBsb2FkIGJhbGFuY2VycywgYW5kIHRoZW4gYnkgcXVlcnlpbmcgdGhlIGxpc3RlbmVycyBvZiBlYWNoIGxvYWRcbiAgICogYmFsYW5jZXIgYW5kIGZpbHRlcmluZyBvdXQgdW5tYXRjaGluZyBsaXN0ZW5lcnMuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdldExpc3RlbmVyQnlGaWx0ZXJpbmdMb2FkQmFsYW5jZXJzKGVsYnYyOiBBV1MuRUxCdjIsIGFyZ3M6IExvYWRCYWxhbmNlckxpc3RlbmVyUXVlcnkpIHtcbiAgICAvLyBGaW5kIG1hdGNoaW5nIGxvYWQgYmFsYW5jZXJzXG4gICAgY29uc3QgbG9hZEJhbGFuY2VycyA9IGF3YWl0IGZpbmRMb2FkQmFsYW5jZXJzKGVsYnYyLCBhcmdzKTtcblxuICAgIGlmIChsb2FkQmFsYW5jZXJzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBhc3NvY2lhdGVkIGxvYWQgYmFsYW5jZXJzIGZvdW5kIGZvciBsb2FkIGJhbGFuY2VyIGxpc3RlbmVyIHF1ZXJ5ICR7SlNPTi5zdHJpbmdpZnkoYXJncyl9YCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuZmluZE1hdGNoaW5nTGlzdGVuZXIoZWxidjIsIGxvYWRCYWxhbmNlcnMsIGFyZ3MpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZpbmRzIHRoZSBtYXRjaGluZyBsaXN0ZW5lciBmcm9tIHRoZSBsaXN0IG9mIGxvYWQgYmFsYW5jZXJzLiBUaGlzIHdpbGxcbiAgICogZXJyb3IgdW5sZXNzIHRoZXJlIGlzIGV4YWN0bHkgb25lIG1hdGNoIHNvIHRoYXQgdGhlIHVzZXIgaXMgcHJvbXB0ZWQgdG9cbiAgICogcHJvdmlkZSBtb3JlIHNwZWNpZmljIGNyaXRlcmlhIHJhdGhlciB0aGFuIHVzIHByb3ZpZGluZyBhIG5vbmRldGVybWluaXN0aWNcbiAgICogcmVzdWx0LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBmaW5kTWF0Y2hpbmdMaXN0ZW5lcihlbGJ2MjogQVdTLkVMQnYyLCBsb2FkQmFsYW5jZXJzOiBBV1MuRUxCdjIuTG9hZEJhbGFuY2VycywgcXVlcnk6IExvYWRCYWxhbmNlckxpc3RlbmVyUXVlcnkpIHtcbiAgICBjb25zdCBsb2FkQmFsYW5jZXJzQnlBcm4gPSBpbmRleExvYWRCYWxhbmNlcnNCeUFybihsb2FkQmFsYW5jZXJzKTtcbiAgICBjb25zdCBsb2FkQmFsYW5jZXJBcm5zID0gT2JqZWN0LmtleXMobG9hZEJhbGFuY2Vyc0J5QXJuKTtcblxuICAgIGNvbnN0IG1hdGNoZXMgPSBBcnJheTxjeGFwaS5Mb2FkQmFsYW5jZXJMaXN0ZW5lckNvbnRleHRSZXNwb25zZT4oKTtcblxuICAgIGZvciBhd2FpdCAoY29uc3QgbGlzdGVuZXIgb2YgZGVzY3JpYmVMaXN0ZW5lcnNCeUxvYWRCYWxhbmNlckFybihlbGJ2MiwgbG9hZEJhbGFuY2VyQXJucykpIHtcbiAgICAgIGNvbnN0IGxvYWRCYWxhbmNlciA9IGxvYWRCYWxhbmNlcnNCeUFybltsaXN0ZW5lci5Mb2FkQmFsYW5jZXJBcm4hXTtcbiAgICAgIGlmIChsaXN0ZW5lck1hdGNoZXNRdWVyeUZpbHRlcihsaXN0ZW5lciwgcXVlcnkpICYmIGxvYWRCYWxhbmNlcikge1xuICAgICAgICBtYXRjaGVzLnB1c2goe1xuICAgICAgICAgIGxpc3RlbmVyQXJuOiBsaXN0ZW5lci5MaXN0ZW5lckFybiEsXG4gICAgICAgICAgbGlzdGVuZXJQb3J0OiBsaXN0ZW5lci5Qb3J0ISxcbiAgICAgICAgICBzZWN1cml0eUdyb3VwSWRzOiBsb2FkQmFsYW5jZXIuU2VjdXJpdHlHcm91cHMgPz8gW10sXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChtYXRjaGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBsb2FkIGJhbGFuY2VyIGxpc3RlbmVycyBmb3VuZCBtYXRjaGluZyAke0pTT04uc3RyaW5naWZ5KHF1ZXJ5KX1gKTtcbiAgICB9XG5cbiAgICBpZiAobWF0Y2hlcy5sZW5ndGggPiAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYE11bHRpcGxlIGxvYWQgYmFsYW5jZXIgbGlzdGVuZXJzIGZvdW5kIG1hdGNoaW5nICR7SlNPTi5zdHJpbmdpZnkocXVlcnkpfSAtIHBsZWFzZSBwcm92aWRlIG1vcmUgc3BlY2lmaWMgY3JpdGVyaWFgKTtcbiAgICB9XG5cbiAgICByZXR1cm4gbWF0Y2hlc1swXTtcbiAgfVxufVxuXG4vKipcbiAqIEZpbmQgbG9hZCBiYWxhbmNlcnMgYnkgdGhlIGdpdmVuIGZpbHRlciBhcmdzLlxuICovXG5hc3luYyBmdW5jdGlvbiBmaW5kTG9hZEJhbGFuY2VycyhlbGJ2MjogQVdTLkVMQnYyLCBhcmdzOiBjeHNjaGVtYS5Mb2FkQmFsYW5jZXJGaWx0ZXIpIHtcbiAgLy8gTGlzdCBsb2FkIGJhbGFuY2Vyc1xuICBsZXQgbG9hZEJhbGFuY2VycyA9IGF3YWl0IGRlc2NyaWJlTG9hZEJhbGFuY2VycyhlbGJ2Miwge1xuICAgIExvYWRCYWxhbmNlckFybnM6IGFyZ3MubG9hZEJhbGFuY2VyQXJuID8gW2FyZ3MubG9hZEJhbGFuY2VyQXJuXSA6IHVuZGVmaW5lZCxcbiAgfSk7XG5cbiAgLy8gRmlsdGVyIGJ5IGxvYWQgYmFsYW5jZXIgdHlwZVxuICBsb2FkQmFsYW5jZXJzID0gbG9hZEJhbGFuY2Vycy5maWx0ZXIobGIgPT4gbGIuVHlwZSA9PT0gYXJncy5sb2FkQmFsYW5jZXJUeXBlKTtcblxuICAvLyBGaWx0ZXIgYnkgbG9hZCBiYWxhbmNlciB0YWdzXG4gIGlmIChhcmdzLmxvYWRCYWxhbmNlclRhZ3MpIHtcbiAgICBsb2FkQmFsYW5jZXJzID0gYXdhaXQgZmlsdGVyTG9hZEJhbGFuY2Vyc0J5VGFncyhlbGJ2MiwgbG9hZEJhbGFuY2VycywgYXJncy5sb2FkQmFsYW5jZXJUYWdzKTtcbiAgfVxuXG4gIHJldHVybiBsb2FkQmFsYW5jZXJzO1xufVxuXG4vKipcbiAqIEhlbHBlciB0byBwYWdpbmF0ZSBvdmVyIGRlc2NyaWJlTG9hZEJhbGFuY2Vyc1xuICogQGludGVybmFsXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBkZXNjcmliZUxvYWRCYWxhbmNlcnMoZWxidjI6IEFXUy5FTEJ2MiwgcmVxdWVzdDogQVdTLkVMQnYyLkRlc2NyaWJlTG9hZEJhbGFuY2Vyc0lucHV0KSB7XG4gIGNvbnN0IGxvYWRCYWxhbmNlcnMgPSBBcnJheTxBV1MuRUxCdjIuTG9hZEJhbGFuY2VyPigpO1xuICBsZXQgcGFnZTogQVdTLkVMQnYyLkRlc2NyaWJlTG9hZEJhbGFuY2Vyc091dHB1dCB8IHVuZGVmaW5lZDtcbiAgZG8ge1xuICAgIHBhZ2UgPSBhd2FpdCBlbGJ2Mi5kZXNjcmliZUxvYWRCYWxhbmNlcnMoe1xuICAgICAgLi4ucmVxdWVzdCxcbiAgICAgIE1hcmtlcjogcGFnZT8uTmV4dE1hcmtlcixcbiAgICB9KS5wcm9taXNlKCk7XG5cbiAgICBsb2FkQmFsYW5jZXJzLnB1c2goLi4uQXJyYXkuZnJvbShwYWdlLkxvYWRCYWxhbmNlcnMgPz8gW10pKTtcbiAgfSB3aGlsZSAocGFnZS5OZXh0TWFya2VyKTtcblxuICByZXR1cm4gbG9hZEJhbGFuY2Vycztcbn1cblxuLyoqXG4gKiBEZXNjcmliZXMgdGhlIHRhZ3Mgb2YgZWFjaCBsb2FkIGJhbGFuY2VyIGFuZCByZXR1cm5zIHRoZSBsb2FkIGJhbGFuY2VycyB0aGF0XG4gKiBtYXRjaCB0aGUgZ2l2ZW4gdGFncy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZmlsdGVyTG9hZEJhbGFuY2Vyc0J5VGFncyhlbGJ2MjogQVdTLkVMQnYyLCBsb2FkQmFsYW5jZXJzOiBBV1MuRUxCdjIuTG9hZEJhbGFuY2VycywgbG9hZEJhbGFuY2VyVGFnczogY3hzY2hlbWEuVGFnW10pIHtcbiAgY29uc3QgbG9hZEJhbGFuY2Vyc0J5QXJuID0gaW5kZXhMb2FkQmFsYW5jZXJzQnlBcm4obG9hZEJhbGFuY2Vycyk7XG4gIGNvbnN0IGxvYWRCYWxhbmNlckFybnMgPSBPYmplY3Qua2V5cyhsb2FkQmFsYW5jZXJzQnlBcm4pO1xuICBjb25zdCBtYXRjaGluZ0xvYWRCYWxhbmNlcnMgPSBBcnJheTxBV1MuRUxCdjIuTG9hZEJhbGFuY2VyPigpO1xuXG4gIC8vIENvbnN1bWUgdGhlIGl0ZW1zIG9mIGFzeW5jIGdlbmVyYXRvci5cbiAgZm9yIGF3YWl0IChjb25zdCB0YWdzIG9mIGRlc2NyaWJlVGFncyhlbGJ2MiwgbG9hZEJhbGFuY2VyQXJucykpIHtcbiAgICBpZiAodGFnc01hdGNoKHRhZ3MsIGxvYWRCYWxhbmNlclRhZ3MpICYmIGxvYWRCYWxhbmNlcnNCeUFyblt0YWdzLlJlc291cmNlQXJuIV0pIHtcbiAgICAgIG1hdGNoaW5nTG9hZEJhbGFuY2Vycy5wdXNoKGxvYWRCYWxhbmNlcnNCeUFyblt0YWdzLlJlc291cmNlQXJuIV0pO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBtYXRjaGluZ0xvYWRCYWxhbmNlcnM7XG59XG5cbi8qKlxuICogR2VuZXJhdG9yIGZ1bmN0aW9uIHRoYXQgeWllbGRzIGBUYWdEZXNjcmlwdGlvbnNgLiBUaGUgQVBJIGRvZXNuJ3Qgc3VwcG9ydFxuICogcGFnaW5hdGlvbiwgc28gdGhpcyBnZW5lcmF0b3IgYnJlYWtzIHRoZSByZXNvdXJjZSBsaXN0IGludG8gY2h1bmtzIGFuZCBpc3N1ZXNcbiAqIHRoZSBhcHByb3ByaWF0ZSByZXF1ZXN0cywgeWllbGRpbmcgZWFjaCB0YWcgZGVzY3JpcHRpb24gYXMgaXQgcmVjZWl2ZXMgaXQuXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uKiBkZXNjcmliZVRhZ3MoZWxidjI6IEFXUy5FTEJ2MiwgcmVzb3VyY2VBcm5zOiBzdHJpbmdbXSkge1xuICAvLyBNYXggb2YgMjAgcmVzb3VyY2UgYXJucyBwZXIgcmVxdWVzdC5cbiAgY29uc3QgY2h1bmtTaXplID0gMjA7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgcmVzb3VyY2VBcm5zLmxlbmd0aDsgaSArPSBjaHVua1NpemUpIHtcbiAgICBjb25zdCBjaHVuayA9IHJlc291cmNlQXJucy5zbGljZShpLCBNYXRoLm1pbihpICsgY2h1bmtTaXplLCByZXNvdXJjZUFybnMubGVuZ3RoKSk7XG4gICAgY29uc3QgY2h1bmtUYWdzID0gYXdhaXQgZWxidjIuZGVzY3JpYmVUYWdzKHtcbiAgICAgIFJlc291cmNlQXJuczogY2h1bmssXG4gICAgfSkucHJvbWlzZSgpO1xuXG4gICAgZm9yIChjb25zdCB0YWcgb2YgY2h1bmtUYWdzLlRhZ0Rlc2NyaXB0aW9ucyA/PyBbXSkge1xuICAgICAgeWllbGQgdGFnO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIERldGVybWluZXMgaWYgdGhlIGdpdmVuIFRhZ0Rlc2NyaXB0aW9uIG1hdGNoZXMgdGhlIHJlcXVpcmVkIHRhZ3MuXG4gKiBAaW50ZXJuYWxcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRhZ3NNYXRjaCh0YWdEZXNjcmlwdGlvbjogQVdTLkVMQnYyLlRhZ0Rlc2NyaXB0aW9uLCByZXF1aXJlZFRhZ3M6IGN4c2NoZW1hLlRhZ1tdKSB7XG4gIGNvbnN0IHRhZ3NCeU5hbWU6IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IHVuZGVmaW5lZD4gPSB7fTtcbiAgZm9yIChjb25zdCB0YWcgb2YgdGFnRGVzY3JpcHRpb24uVGFncyA/PyBbXSkge1xuICAgIHRhZ3NCeU5hbWVbdGFnLktleSFdID0gdGFnLlZhbHVlO1xuICB9XG5cbiAgZm9yIChjb25zdCB0YWcgb2YgcmVxdWlyZWRUYWdzKSB7XG4gICAgaWYgKHRhZ3NCeU5hbWVbdGFnLmtleV0gIT09IHRhZy52YWx1ZSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB0cnVlO1xufVxuXG4vKipcbiAqIEFzeW5jIGdlbmVyYXRvciB0aGF0IHByb2R1Y2VzIGxpc3RlbmVyIGRlc2NyaXB0aW9ucyBieSB0cmF2ZXJzaW5nIHRoZVxuICogcGFnaW5hdGlvbi4gQmVjYXVzZSBkZXNjcmliZUxpc3RlbmVycyBvbmx5IGxldHMgeW91IHNlYXJjaCBieSBvbmUgbG9hZFxuICogYmFsYW5jZXIgYXJuIGF0IGEgdGltZSwgd2UgcmVxdWVzdCB0aGVtIGluZGl2aWR1YWxseSBhbmQgeWllbGQgdGhlIGxpc3RlbmVyc1xuICogYXMgdGhleSBjb21lIGluLlxuICogQGludGVybmFsXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiogZGVzY3JpYmVMaXN0ZW5lcnNCeUxvYWRCYWxhbmNlckFybihlbGJ2MjogQVdTLkVMQnYyLCBsb2FkQmFsYW5jZXJBcm5zOiBzdHJpbmdbXSkge1xuICBmb3IgKGNvbnN0IGxvYWRCYWxhbmNlckFybiBvZiBsb2FkQmFsYW5jZXJBcm5zKSB7XG4gICAgbGV0IHBhZ2U6IEFXUy5FTEJ2Mi5EZXNjcmliZUxpc3RlbmVyc091dHB1dCB8IHVuZGVmaW5lZDtcbiAgICBkbyB7XG4gICAgICBwYWdlID0gYXdhaXQgZWxidjIuZGVzY3JpYmVMaXN0ZW5lcnMoe1xuICAgICAgICBMb2FkQmFsYW5jZXJBcm46IGxvYWRCYWxhbmNlckFybixcbiAgICAgICAgTWFya2VyOiBwYWdlPy5OZXh0TWFya2VyLFxuICAgICAgfSkucHJvbWlzZSgpO1xuXG4gICAgICBmb3IgKGNvbnN0IGxpc3RlbmVyIG9mIHBhZ2UuTGlzdGVuZXJzID8/IFtdKSB7XG4gICAgICAgIHlpZWxkIGxpc3RlbmVyO1xuICAgICAgfVxuICAgIH0gd2hpbGUgKHBhZ2UuTmV4dE1hcmtlcik7XG4gIH1cbn1cblxuLyoqXG4gKiBEZXRlcm1pbmVzIGlmIGEgbGlzdGVuZXIgbWF0Y2hlcyB0aGUgcXVlcnkgZmlsdGVycy5cbiAqL1xuZnVuY3Rpb24gbGlzdGVuZXJNYXRjaGVzUXVlcnlGaWx0ZXIobGlzdGVuZXI6IEFXUy5FTEJ2Mi5MaXN0ZW5lciwgYXJnczogY3hzY2hlbWEuTG9hZEJhbGFuY2VyTGlzdGVuZXJDb250ZXh0UXVlcnkpOiBib29sZWFuIHtcbiAgaWYgKGFyZ3MubGlzdGVuZXJQb3J0ICYmIGxpc3RlbmVyLlBvcnQgIT09IGFyZ3MubGlzdGVuZXJQb3J0KSB7XG4gICAgLy8gTm8gbWF0Y2guXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgaWYgKGFyZ3MubGlzdGVuZXJQcm90b2NvbCAmJiBsaXN0ZW5lci5Qcm90b2NvbCAhPT0gYXJncy5saXN0ZW5lclByb3RvY29sKSB7XG4gICAgLy8gTm8gbWF0Y2guXG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgcmV0dXJuIHRydWU7XG59XG5cbi8qKlxuICogUmV0dXJucyBhIHJlY29yZCBvZiBsb2FkIGJhbGFuY2VycyBpbmRleGVkIGJ5IHRoZWlyIGFybnNcbiAqL1xuZnVuY3Rpb24gaW5kZXhMb2FkQmFsYW5jZXJzQnlBcm4obG9hZEJhbGFuY2VyczogQVdTLkVMQnYyLkxvYWRCYWxhbmNlcltdKTogUmVjb3JkPHN0cmluZywgQVdTLkVMQnYyLkxvYWRCYWxhbmNlcj4ge1xuICBjb25zdCBsb2FkQmFsYW5jZXJzQnlBcm46IFJlY29yZDxzdHJpbmcsIEFXUy5FTEJ2Mi5Mb2FkQmFsYW5jZXI+ID0ge307XG5cbiAgZm9yIChjb25zdCBsb2FkQmFsYW5jZXIgb2YgbG9hZEJhbGFuY2Vycykge1xuICAgIGxvYWRCYWxhbmNlcnNCeUFybltsb2FkQmFsYW5jZXIuTG9hZEJhbGFuY2VyQXJuIV0gPSBsb2FkQmFsYW5jZXI7XG4gIH1cblxuICByZXR1cm4gbG9hZEJhbGFuY2Vyc0J5QXJuO1xufVxuIl19