UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

354 lines 59.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.tryHotswapDeployment = tryHotswapDeployment; const cfn_diff = require("@aws-cdk/cloudformation-diff"); const chalk = require("chalk"); const nested_stack_helpers_1 = require("./nested-stack-helpers"); const logging_1 = require("../../logging"); const error_1 = require("../../toolkit/error"); const error_2 = require("../../util/error"); const evaluate_cloudformation_template_1 = require("../evaluate-cloudformation-template"); const appsync_mapping_templates_1 = require("../hotswap/appsync-mapping-templates"); const code_build_projects_1 = require("../hotswap/code-build-projects"); const common_1 = require("../hotswap/common"); const ecs_services_1 = require("../hotswap/ecs-services"); const lambda_functions_1 = require("../hotswap/lambda-functions"); const s3_bucket_deployments_1 = require("../hotswap/s3-bucket-deployments"); const stepfunctions_state_machines_1 = require("../hotswap/stepfunctions-state-machines"); const plugin_1 = require("../plugin"); // Must use a require() otherwise esbuild complains about calling a namespace // eslint-disable-next-line @typescript-eslint/no-require-imports const pLimit = require('p-limit'); const RESOURCE_DETECTORS = { // Lambda 'AWS::Lambda::Function': lambda_functions_1.isHotswappableLambdaFunctionChange, 'AWS::Lambda::Version': lambda_functions_1.isHotswappableLambdaFunctionChange, 'AWS::Lambda::Alias': lambda_functions_1.isHotswappableLambdaFunctionChange, // AppSync 'AWS::AppSync::Resolver': appsync_mapping_templates_1.isHotswappableAppSyncChange, 'AWS::AppSync::FunctionConfiguration': appsync_mapping_templates_1.isHotswappableAppSyncChange, 'AWS::AppSync::GraphQLSchema': appsync_mapping_templates_1.isHotswappableAppSyncChange, 'AWS::AppSync::ApiKey': appsync_mapping_templates_1.isHotswappableAppSyncChange, 'AWS::ECS::TaskDefinition': ecs_services_1.isHotswappableEcsServiceChange, 'AWS::CodeBuild::Project': code_build_projects_1.isHotswappableCodeBuildProjectChange, 'AWS::StepFunctions::StateMachine': stepfunctions_state_machines_1.isHotswappableStateMachineChange, 'Custom::CDKBucketDeployment': s3_bucket_deployments_1.isHotswappableS3BucketDeploymentChange, 'AWS::IAM::Policy': async (logicalId, change, evaluateCfnTemplate) => { // If the policy is for a S3BucketDeploymentChange, we can ignore the change if (await (0, s3_bucket_deployments_1.skipChangeForS3DeployCustomResourcePolicy)(logicalId, change, evaluateCfnTemplate)) { return []; } return (0, common_1.reportNonHotswappableResource)(change, 'This resource type is not supported for hotswap deployments'); }, 'AWS::CDK::Metadata': async () => [], }; /** * Perform a hotswap deployment, short-circuiting CloudFormation if possible. * If it's not possible to short-circuit the deployment * (because the CDK Stack contains changes that cannot be deployed without CloudFormation), * returns `undefined`. */ async function tryHotswapDeployment(sdkProvider, assetParams, cloudFormationStack, stackArtifact, hotswapMode, hotswapPropertyOverrides) { // resolve the environment, so we can substitute things like AWS::Region in CFN expressions const resolvedEnv = await sdkProvider.resolveEnvironment(stackArtifact.environment); // create a new SDK using the CLI credentials, because the default one will not work for new-style synthesis - // it assumes the bootstrap deploy Role, which doesn't have permissions to update Lambda functions const sdk = (await sdkProvider.forEnvironment(resolvedEnv, plugin_1.Mode.ForWriting)).sdk; const currentTemplate = await (0, nested_stack_helpers_1.loadCurrentTemplateWithNestedStacks)(stackArtifact, sdk); const evaluateCfnTemplate = new evaluate_cloudformation_template_1.EvaluateCloudFormationTemplate({ stackName: stackArtifact.stackName, template: stackArtifact.template, parameters: assetParams, account: resolvedEnv.account, region: resolvedEnv.region, partition: (await sdk.currentAccount()).partition, sdk, nestedStacks: currentTemplate.nestedStacks, }); const stackChanges = cfn_diff.fullDiff(currentTemplate.deployedRootTemplate, stackArtifact.template); const { hotswappableChanges, nonHotswappableChanges } = await classifyResourceChanges(stackChanges, evaluateCfnTemplate, sdk, currentTemplate.nestedStacks, hotswapPropertyOverrides); logNonHotswappableChanges(nonHotswappableChanges, hotswapMode); // preserve classic hotswap behavior if (hotswapMode === common_1.HotswapMode.FALL_BACK) { if (nonHotswappableChanges.length > 0) { return undefined; } } // apply the short-circuitable changes await applyAllHotswappableChanges(sdk, hotswappableChanges); return { type: 'did-deploy-stack', noOp: hotswappableChanges.length === 0, stackArn: cloudFormationStack.stackId, outputs: cloudFormationStack.outputs, }; } /** * Classifies all changes to all resources as either hotswappable or not. * Metadata changes are excluded from the list of (non)hotswappable resources. */ async function classifyResourceChanges(stackChanges, evaluateCfnTemplate, sdk, nestedStackNames, hotswapPropertyOverrides) { const resourceDifferences = getStackResourceDifferences(stackChanges); const promises = []; const hotswappableResources = new Array(); const nonHotswappableResources = new Array(); for (const logicalId of Object.keys(stackChanges.outputs.changes)) { nonHotswappableResources.push({ hotswappable: false, reason: 'output was changed', logicalId, rejectedChanges: [], resourceType: 'Stack Output', }); } // gather the results of the detector functions for (const [logicalId, change] of Object.entries(resourceDifferences)) { if (change.newValue?.Type === 'AWS::CloudFormation::Stack' && change.oldValue?.Type === 'AWS::CloudFormation::Stack') { const nestedHotswappableResources = await findNestedHotswappableChanges(logicalId, change, nestedStackNames, evaluateCfnTemplate, sdk, hotswapPropertyOverrides); hotswappableResources.push(...nestedHotswappableResources.hotswappableChanges); nonHotswappableResources.push(...nestedHotswappableResources.nonHotswappableChanges); continue; } const hotswappableChangeCandidate = isCandidateForHotswapping(change, logicalId); // we don't need to run this through the detector functions, we can already judge this if ('hotswappable' in hotswappableChangeCandidate) { if (!hotswappableChangeCandidate.hotswappable) { nonHotswappableResources.push(hotswappableChangeCandidate); } continue; } const resourceType = hotswappableChangeCandidate.newValue.Type; if (resourceType in RESOURCE_DETECTORS) { // run detector functions lazily to prevent unhandled promise rejections promises.push(() => RESOURCE_DETECTORS[resourceType](logicalId, hotswappableChangeCandidate, evaluateCfnTemplate, hotswapPropertyOverrides)); } else { (0, common_1.reportNonHotswappableChange)(nonHotswappableResources, hotswappableChangeCandidate, undefined, 'This resource type is not supported for hotswap deployments'); } } // resolve all detector results const changesDetectionResults = []; for (const detectorResultPromises of promises) { // Constant set of promises per resource // eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism const hotswapDetectionResults = await Promise.all(await detectorResultPromises()); changesDetectionResults.push(hotswapDetectionResults); } for (const resourceDetectionResults of changesDetectionResults) { for (const propertyResult of resourceDetectionResults) { propertyResult.hotswappable ? hotswappableResources.push(propertyResult) : nonHotswappableResources.push(propertyResult); } } return { hotswappableChanges: hotswappableResources, nonHotswappableChanges: nonHotswappableResources, }; } /** * Returns all changes to resources in the given Stack. * * @param stackChanges the collection of all changes to a given Stack */ function getStackResourceDifferences(stackChanges) { // we need to collapse logical ID rename changes into one change, // as they are represented in stackChanges as a pair of two changes: one addition and one removal const allResourceChanges = stackChanges.resources.changes; const allRemovalChanges = filterDict(allResourceChanges, (resChange) => resChange.isRemoval); const allNonRemovalChanges = filterDict(allResourceChanges, (resChange) => !resChange.isRemoval); for (const [logId, nonRemovalChange] of Object.entries(allNonRemovalChanges)) { if (nonRemovalChange.isAddition) { const addChange = nonRemovalChange; // search for an identical removal change const identicalRemovalChange = Object.entries(allRemovalChanges).find(([_, remChange]) => { return changesAreForSameResource(remChange, addChange); }); // if we found one, then this means this is a rename change if (identicalRemovalChange) { const [removedLogId, removedResourceChange] = identicalRemovalChange; allNonRemovalChanges[logId] = makeRenameDifference(removedResourceChange, addChange); // delete the removal change that forms the rename pair delete allRemovalChanges[removedLogId]; } } } // the final result are all of the remaining removal changes, // plus all of the non-removal changes // (we saved the rename changes in that object already) return { ...allRemovalChanges, ...allNonRemovalChanges, }; } /** Filters an object with string keys based on whether the callback returns 'true' for the given value in the object. */ function filterDict(dict, func) { return Object.entries(dict).reduce((acc, [key, t]) => { if (func(t)) { acc[key] = t; } return acc; }, {}); } /** Finds any hotswappable changes in all nested stacks. */ async function findNestedHotswappableChanges(logicalId, change, nestedStackTemplates, evaluateCfnTemplate, sdk, hotswapPropertyOverrides) { const nestedStack = nestedStackTemplates[logicalId]; if (!nestedStack.physicalName) { return { hotswappableChanges: [], nonHotswappableChanges: [ { hotswappable: false, logicalId, reason: `physical name for AWS::CloudFormation::Stack '${logicalId}' could not be found in CloudFormation, so this is a newly created nested stack and cannot be hotswapped`, rejectedChanges: [], resourceType: 'AWS::CloudFormation::Stack', }, ], }; } const evaluateNestedCfnTemplate = await evaluateCfnTemplate.createNestedEvaluateCloudFormationTemplate(nestedStack.physicalName, nestedStack.generatedTemplate, change.newValue?.Properties?.Parameters); const nestedDiff = cfn_diff.fullDiff(nestedStackTemplates[logicalId].deployedTemplate, nestedStackTemplates[logicalId].generatedTemplate); return classifyResourceChanges(nestedDiff, evaluateNestedCfnTemplate, sdk, nestedStackTemplates[logicalId].nestedStackTemplates, hotswapPropertyOverrides); } /** Returns 'true' if a pair of changes is for the same resource. */ function changesAreForSameResource(oldChange, newChange) { return (oldChange.oldResourceType === newChange.newResourceType && // this isn't great, but I don't want to bring in something like underscore just for this comparison JSON.stringify(oldChange.oldProperties) === JSON.stringify(newChange.newProperties)); } function makeRenameDifference(remChange, addChange) { return new cfn_diff.ResourceDifference( // we have to fill in the old value, because otherwise this will be classified as a non-hotswappable change remChange.oldValue, addChange.newValue, { resourceType: { oldType: remChange.oldResourceType, newType: addChange.newResourceType, }, propertyDiffs: addChange.propertyDiffs, otherDiffs: addChange.otherDiffs, }); } /** * Returns a `HotswappableChangeCandidate` if the change is hotswappable * Returns an empty `HotswappableChange` if the change is to CDK::Metadata * Returns a `NonHotswappableChange` if the change is not hotswappable */ function isCandidateForHotswapping(change, logicalId) { // a resource has been removed OR a resource has been added; we can't short-circuit that change if (!change.oldValue) { return { hotswappable: false, resourceType: change.newValue.Type, logicalId, rejectedChanges: [], reason: `resource '${logicalId}' was created by this deployment`, }; } else if (!change.newValue) { return { hotswappable: false, resourceType: change.oldValue.Type, logicalId, rejectedChanges: [], reason: `resource '${logicalId}' was destroyed by this deployment`, }; } // a resource has had its type changed if (change.newValue?.Type !== change.oldValue?.Type) { return { hotswappable: false, resourceType: change.newValue?.Type, logicalId, rejectedChanges: [], reason: `resource '${logicalId}' had its type changed from '${change.oldValue?.Type}' to '${change.newValue?.Type}'`, }; } return { logicalId, oldValue: change.oldValue, newValue: change.newValue, propertyUpdates: change.propertyUpdates, }; } async function applyAllHotswappableChanges(sdk, hotswappableChanges) { if (hotswappableChanges.length > 0) { (0, logging_1.info)(`\n${common_1.ICON} hotswapping resources:`); } const limit = pLimit(10); // eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism return Promise.all(hotswappableChanges.map(hotswapOperation => limit(() => { return applyHotswappableChange(sdk, hotswapOperation); }))); } async function applyHotswappableChange(sdk, hotswapOperation) { // note the type of service that was successfully hotswapped in the User-Agent const customUserAgent = `cdk-hotswap/success-${hotswapOperation.service}`; sdk.appendCustomUserAgent(customUserAgent); for (const name of hotswapOperation.resourceNames) { (0, logging_1.info)(` ${common_1.ICON} %s`, chalk.bold(name)); } // if the SDK call fails, an error will be thrown by the SDK // and will prevent the green 'hotswapped!' text from being displayed try { await hotswapOperation.apply(sdk); } catch (e) { if (e.name === 'TimeoutError' || e.name === 'AbortError') { const result = JSON.parse((0, error_2.formatErrorMessage)(e)); const error = new error_1.ToolkitError(formatWaiterErrorResult(result)); error.name = e.name; throw error; } throw e; } for (const name of hotswapOperation.resourceNames) { (0, logging_1.info)(`${common_1.ICON} %s %s`, chalk.bold(name), chalk.green('hotswapped!')); } sdk.removeCustomUserAgent(customUserAgent); } function formatWaiterErrorResult(result) { const main = [ `Resource is not in the expected state due to waiter status: ${result.state}`, result.reason ? `${result.reason}.` : '', ].join('. '); if (result.observedResponses != null) { const observedResponses = Object .entries(result.observedResponses) .map(([msg, count]) => ` - ${msg} (${count})`) .join('\n'); return `${main} Observed responses:\n${observedResponses}`; } return main; } function logNonHotswappableChanges(nonHotswappableChanges, hotswapMode) { if (nonHotswappableChanges.length === 0) { return; } /** * EKS Services can have a task definition that doesn't refer to the task definition being updated. * We have to log this as a non-hotswappable change to the task definition, but when we do, * we wind up hotswapping the task definition and logging it as a non-hotswappable change. * * This logic prevents us from logging that change as non-hotswappable when we hotswap it. */ if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) { nonHotswappableChanges = nonHotswappableChanges.filter((change) => change.hotswapOnlyVisible === true); if (nonHotswappableChanges.length === 0) { return; } } if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) { (0, logging_1.info)('\n%s %s', chalk.red('⚠️'), chalk.red('The following non-hotswappable changes were found. To reconcile these using CloudFormation, specify --hotswap-fallback')); } else { (0, logging_1.info)('\n%s %s', chalk.red('⚠️'), chalk.red('The following non-hotswappable changes were found:')); } for (const change of nonHotswappableChanges) { change.rejectedChanges.length > 0 ? (0, logging_1.info)(' logicalID: %s, type: %s, rejected changes: %s, reason: %s', chalk.bold(change.logicalId), chalk.bold(change.resourceType), chalk.bold(change.rejectedChanges), chalk.red(change.reason)) : (0, logging_1.info)(' logicalID: %s, type: %s, reason: %s', chalk.bold(change.logicalId), chalk.bold(change.resourceType), chalk.red(change.reason)); } (0, logging_1.info)(''); // newline } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaG90c3dhcC1kZXBsb3ltZW50cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImhvdHN3YXAtZGVwbG95bWVudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFtRkEsb0RBb0RDO0FBdklELHlEQUF5RDtBQUd6RCwrQkFBK0I7QUFHL0IsaUVBQW1HO0FBQ25HLDJDQUFxQztBQUNyQywrQ0FBbUQ7QUFDbkQsNENBQXNEO0FBQ3RELDBGQUFxRjtBQUNyRixvRkFBbUY7QUFDbkYsd0VBQXNGO0FBQ3RGLDhDQVUyQjtBQUMzQiwwREFBeUU7QUFDekUsa0VBQWlGO0FBQ2pGLDRFQUcwQztBQUMxQywwRkFBMkY7QUFDM0Ysc0NBQWlDO0FBR2pDLDZFQUE2RTtBQUM3RSxpRUFBaUU7QUFDakUsTUFBTSxNQUFNLEdBQTZCLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQztBQVM1RCxNQUFNLGtCQUFrQixHQUF1QztJQUM3RCxTQUFTO0lBQ1QsdUJBQXVCLEVBQUUscURBQWtDO0lBQzNELHNCQUFzQixFQUFFLHFEQUFrQztJQUMxRCxvQkFBb0IsRUFBRSxxREFBa0M7SUFFeEQsVUFBVTtJQUNWLHdCQUF3QixFQUFFLHVEQUEyQjtJQUNyRCxxQ0FBcUMsRUFBRSx1REFBMkI7SUFDbEUsNkJBQTZCLEVBQUUsdURBQTJCO0lBQzFELHNCQUFzQixFQUFFLHVEQUEyQjtJQUVuRCwwQkFBMEIsRUFBRSw2Q0FBOEI7SUFDMUQseUJBQXlCLEVBQUUsMERBQW9DO0lBQy9ELGtDQUFrQyxFQUFFLCtEQUFnQztJQUNwRSw2QkFBNkIsRUFBRSw4REFBc0M7SUFDckUsa0JBQWtCLEVBQUUsS0FBSyxFQUN2QixTQUFpQixFQUNqQixNQUFtQyxFQUNuQyxtQkFBbUQsRUFDckIsRUFBRTtRQUNoQyw0RUFBNEU7UUFDNUUsSUFBSSxNQUFNLElBQUEsaUVBQXlDLEVBQUMsU0FBUyxFQUFFLE1BQU0sRUFBRSxtQkFBbUIsQ0FBQyxFQUFFLENBQUM7WUFDNUYsT0FBTyxFQUFFLENB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QUksQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLEVBQ2xDLEtBQUssQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUN6QjtZQUNELENBQUMsQ0FBQyxJQUFBLGNBQUksRUFDSix5Q0FBeUMsRUFDekMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLEVBQzVCLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUMvQixLQUFLLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FDekIsQ0FBQztJQUNOLENBQUM7SUFFRCxJQUFBLGNBQUksRUFBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVU7QUFDdEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNmbl9kaWZmIGZyb20gJ0Bhd3MtY2RrL2Nsb3VkZm9ybWF0aW9uLWRpZmYnO1xuaW1wb3J0ICogYXMgY3hhcGkgZnJvbSAnQGF3cy1jZGsvY3gtYXBpJztcbmltcG9ydCB7IFdhaXRlclJlc3VsdCB9IGZyb20gJ0BzbWl0aHkvdXRpbC13YWl0ZXInO1xuaW1wb3J0ICogYXMgY2hhbGsgZnJvbSAnY2hhbGsnO1xuaW1wb3J0IHR5cGUgeyBTREssIFNka1Byb3ZpZGVyIH0gZnJvbSAnLi4vYXdzLWF1dGgnO1xuaW1wb3J0IHR5cGUgeyBDbG91ZEZvcm1hdGlvblN0YWNrIH0gZnJvbSAnLi9jbG91ZGZvcm1hdGlvbic7XG5pbXBvcnQgeyBOZXN0ZWRTdGFja1RlbXBsYXRlcywgbG9hZEN1cnJlbnRUZW1wbGF0ZVdpdGhOZXN0ZWRTdGFja3MgfSBmcm9tICcuL25lc3RlZC1zdGFjay1oZWxwZXJzJztcbmltcG9ydCB7IGluZm8gfSBmcm9tICcuLi8uLi9sb2dnaW5nJztcbmltcG9ydCB7IFRvb2xraXRFcnJvciB9IGZyb20gJy4uLy4uL3Rvb2xraXQvZXJyb3InO1xuaW1wb3J0IHsgZm9ybWF0RXJyb3JNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdXRpbC9lcnJvcic7XG5pbXBvcnQgeyBFdmFsdWF0ZUNsb3VkRm9ybWF0aW9uVGVtcGxhdGUgfSBmcm9tICcuLi9ldmFsdWF0ZS1jbG91ZGZvcm1hdGlvbi10ZW1wbGF0ZSc7XG5pbXBvcnQgeyBpc0hvdHN3YXBwYWJsZUFwcFN5bmNDaGFuZ2UgfSBmcm9tICcuLi9ob3Rzd2FwL2FwcHN5bmMtbWFwcGluZy10ZW1wbGF0ZXMnO1xuaW1wb3J0IHsgaXNIb3Rzd2FwcGFibGVDb2RlQnVpbGRQcm9qZWN0Q2hhbmdlIH0gZnJvbSAnLi4vaG90c3dhcC9jb2RlLWJ1aWxkLXByb2plY3RzJztcbmltcG9ydCB7XG4gIElDT04sXG4gIENoYW5nZUhvdHN3YXBSZXN1bHQsXG4gIEhvdHN3YXBNb2RlLFxuICBIb3Rzd2FwcGFibGVDaGFuZ2UsXG4gIE5vbkhvdHN3YXBwYWJsZUNoYW5nZSxcbiAgSG90c3dhcHBhYmxlQ2hhbmdlQ2FuZGlkYXRlLFxuICBIb3Rzd2FwUHJvcGVydHlPdmVycmlkZXMsIENsYXNzaWZpZWRSZXNvdXJjZUNoYW5nZXMsXG4gIHJlcG9ydE5vbkhvdHN3YXBwYWJsZUNoYW5nZSxcbiAgcmVwb3J0Tm9uSG90c3dhcHBhYmxlUmVzb3VyY2UsXG59IGZyb20gJy4uL2hvdHN3YXAvY29tbW9uJztcbmltcG9ydCB7IGlzSG90c3dhcHBhYmxlRWNzU2VydmljZUNoYW5nZSB9IGZyb20gJy4uL2hvdHN3YXAvZWNzLXNlcnZpY2VzJztcbmltcG9ydCB7IGlzSG90c3dhcHBhYmxlTGFtYmRhRnVuY3Rpb25DaGFuZ2UgfSBmcm9tICcuLi9ob3Rzd2FwL2xhbWJkYS1mdW5jdGlvbnMnO1xuaW1wb3J0IHtcbiAgc2tpcENoYW5nZUZvclMzRGVwbG95Q3VzdG9tUmVzb3VyY2VQb2xpY3ksXG4gIGlzSG90c3dhcHBhYmxlUzNCdWNrZXREZXBsb3ltZW50Q2hhbmdlLFxufSBmcm9tICcuLi9ob3Rzd2FwL3MzLWJ1Y2tldC1kZXBsb3ltZW50cyc7XG5pbXBvcnQgeyBpc0hvdHN3YXBwYWJsZVN0YXRlTWFjaGluZUNoYW5nZSB9IGZyb20gJy4uL2hvdHN3YXAvc3RlcGZ1bmN0aW9ucy1zdGF0ZS1tYWNoaW5lcyc7XG5pbXBvcnQgeyBNb2RlIH0gZnJvbSAnLi4vcGx1Z2luJztcbmltcG9ydCB7IFN1Y2Nlc3NmdWxEZXBsb3lTdGFja1Jlc3VsdCB9IGZyb20gJy4vZGVwbG95bWVudC1yZXN1bHQnO1xuXG4vLyBNdXN0IHVzZSBhIHJlcXVpcmUoKSBvdGhlcndpc2UgZXNidWlsZCBjb21wbGFpbnMgYWJvdXQgY2FsbGluZyBhIG5hbWVzcGFjZVxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbmNvbnN0IHBMaW1pdDogdHlwZW9mIGltcG9ydCgncC1saW1pdCcpID0gcmVxdWlyZSgncC1saW1pdCcpO1xuXG50eXBlIEhvdHN3YXBEZXRlY3RvciA9IChcbiAgbG9naWNhbElkOiBzdHJpbmcsXG4gIGNoYW5nZTogSG90c3dhcHBhYmxlQ2hhbmdlQ2FuZGlkYXRlLFxuICBldmFsdWF0ZUNmblRlbXBsYXRlOiBFdmFsdWF0ZUNsb3VkRm9ybWF0aW9uVGVtcGxhdGUsXG4gIGhvdHN3YXBQcm9wZXJ0eU92ZXJyaWRlczogSG90c3dhcFByb3BlcnR5T3ZlcnJpZGVzLFxuKSA9PiBQcm9taXNlPENoYW5nZUhvdHN3YXBSZXN1bHQ+O1xuXG5jb25zdCBSRVNPVVJDRV9ERVRFQ1RPUlM6IHsgW2tleTogc3RyaW5nXTogSG90c3dhcERldGVjdG9yIH0gPSB7XG4gIC8vIExhbWJkYVxuICAnQVdTOjpMYW1iZGE6OkZ1bmN0aW9uJzogaXNIb3Rzd2FwcGFibGVMYW1iZGFGdW5jdGlvbkNoYW5nZSxcbiAgJ0FXUzo6TGFtYmRhOjpWZXJzaW9uJzogaXNIb3Rzd2FwcGFibGVMYW1iZGFGdW5jdGlvbkNoYW5nZSxcbiAgJ0FXUzo6TGFtYmRhOjpBbGlhcyc6IGlzSG90c3dhcHBhYmxlTGFtYmRhRnVuY3Rpb25DaGFuZ2UsXG5cbiAgLy8gQXBwU3luY1xuICAnQVdTOjpBcHBTeW5jOjpSZXNvbHZlcic6IGlzSG90c3dhcHBhYmxlQXBwU3luY0NoYW5nZSxcbiAgJ0FXUzo6QXBwU3luYzo6RnVuY3Rpb25Db25maWd1cmF0aW9uJzogaXNIb3Rzd2FwcGFibGVBcHBTeW5jQ2hhbmdlLFxuICAnQVdTOjpBcHBTeW5jOjpHcmFwaFFMU2NoZW1hJzogaXNIb3Rzd2FwcGFibGVBcHBTeW5jQ2hhbmdlLFxuICAnQVdTOjpBcHBTeW5jOjpBcGlLZXknOiBpc0hvdHN3YXBwYWJsZUFwcFN5bmNDaGFuZ2UsXG5cbiAgJ0FXUzo6RUNTOjpUYXNrRGVmaW5pdGlvbic6IGlzSG90c3dhcHBhYmxlRWNzU2VydmljZUNoYW5nZSxcbiAgJ0FXUzo6Q29kZUJ1aWxkOjpQcm9qZWN0JzogaXNIb3Rzd2FwcGFibGVDb2RlQnVpbGRQcm9qZWN0Q2hhbmdlLFxuICAnQVdTOjpTdGVwRnVuY3Rpb25zOjpTdGF0ZU1hY2hpbmUnOiBpc0hvdHN3YXBwYWJsZVN0YXRlTWFjaGluZUNoYW5nZSxcbiAgJ0N1c3RvbTo6Q0RLQnVja2V0RGVwbG95bWVudCc6IGlzSG90c3dhcHBhYmxlUzNCdWNrZXREZXBsb3ltZW50Q2hhbmdlLFxuICAnQVdTOjpJQU06OlBvbGljeSc6IGFzeW5jIChcbiAgICBsb2dpY2FsSWQ6IHN0cmluZyxcbiAgICBjaGFuZ2U6IEhvdHN3YXBwYWJsZUNoYW5nZUNhbmRpZGF0ZSxcbiAgICBldmFsdWF0ZUNmblRlbXBsYXRlOiBFdmFsdWF0ZUNsb3VkRm9ybWF0aW9uVGVtcGxhdGUsXG4gICk6IFByb21pc2U8Q2hhbmdlSG90c3dhcFJlc3VsdD4gPT4ge1xuICAgIC8vIElmIHRoZSBwb2xpY3kgaXMgZm9yIGEgUzNCdWNrZXREZXBsb3ltZW50Q2hhbmdlLCB3ZSBjYW4gaWdub3JlIHRoZSBjaGFuZ2VcbiAgICBpZiAoYXdhaXQgc2tpcENoYW5nZUZvclMzRGVwbG95Q3VzdG9tUmVzb3VyY2VQb2xpY3kobG9naWNhbElkLCBjaGFuZ2UsIGV2YWx1YXRlQ2ZuVGVtcGxhdGUpKSB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuXG4gICAgcmV0dXJuIHJlcG9ydE5vbkhvdHN3YXBwYWJsZVJlc291cmNlKGNoYW5nZSwgJ1RoaXMgcmVzb3VyY2UgdHlwZSBpcyBub3Qgc3VwcG9ydGVkIGZvciBob3Rzd2FwIGRlcGxveW1lbnRzJyk7XG4gIH0sXG5cbiAgJ0FXUzo6Q0RLOjpNZXRhZGF0YSc6IGFzeW5jICgpID0+IFtdLFxufTtcblxuLyoqXG4gKiBQZXJmb3JtIGEgaG90c3dhcCBkZXBsb3ltZW50LCBzaG9ydC1jaXJjdWl0aW5nIENsb3VkRm9ybWF0aW9uIGlmIHBvc3NpYmxlLlxuICogSWYgaXQncyBub3QgcG9zc2libGUgdG8gc2hvcnQtY2lyY3VpdCB0aGUgZGVwbG95bWVudFxuICogKGJlY2F1c2UgdGhlIENESyBTdGFjayBjb250YWlucyBjaGFuZ2VzIHRoYXQgY2Fubm90IGJlIGRlcGxveWVkIHdpdGhvdXQgQ2xvdWRGb3JtYXRpb24pLFxuICogcmV0dXJucyBgdW5kZWZpbmVkYC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHRyeUhvdHN3YXBEZXBsb3ltZW50KFxuICBzZGtQcm92aWRlcjogU2RrUHJvdmlkZXIsXG4gIGFzc2V0UGFyYW1zOiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB9LFxuICBjbG91ZEZvcm1hdGlvblN0YWNrOiBDbG91ZEZvcm1hdGlvblN0YWNrLFxuICBzdGFja0FydGlmYWN0OiBjeGFwaS5DbG91ZEZvcm1hdGlvblN0YWNrQXJ0aWZhY3QsXG4gIGhvdHN3YXBNb2RlOiBIb3Rzd2FwTW9kZSwgaG90c3dhcFByb3BlcnR5T3ZlcnJpZGVzOiBIb3Rzd2FwUHJvcGVydHlPdmVycmlkZXMsXG4pOiBQcm9taXNlPFN1Y2Nlc3NmdWxEZXBsb3lTdGFja1Jlc3VsdCB8IHVuZGVmaW5lZD4ge1xuICAvLyByZXNvbHZlIHRoZSBlbnZpcm9ubWVudCwgc28gd2UgY2FuIHN1YnN0aXR1dGUgdGhpbmdzIGxpa2UgQVdTOjpSZWdpb24gaW4gQ0ZOIGV4cHJlc3Npb25zXG4gIGNvbnN0IHJlc29sdmVkRW52ID0gYXdhaXQgc2RrUHJvdmlkZXIucmVzb2x2ZUVudmlyb25tZW50KHN0YWNrQXJ0aWZhY3QuZW52aXJvbm1lbnQpO1xuICAvLyBjcmVhdGUgYSBuZXcgU0RLIHVzaW5nIHRoZSBDTEkgY3JlZGVudGlhbHMsIGJlY2F1c2UgdGhlIGRlZmF1bHQgb25lIHdpbGwgbm90IHdvcmsgZm9yIG5ldy1zdHlsZSBzeW50aGVzaXMgLVxuICAvLyBpdCBhc3N1bWVzIHRoZSBib290c3RyYXAgZGVwbG95IFJvbGUsIHdoaWNoIGRvZXNuJ3QgaGF2ZSBwZXJtaXNzaW9ucyB0byB1cGRhdGUgTGFtYmRhIGZ1bmN0aW9uc1xuICBjb25zdCBzZGsgPSAoYXdhaXQgc2RrUHJvdmlkZXIuZm9yRW52aXJvbm1lbnQocmVzb2x2ZWRFbnYsIE1vZGUuRm9yV3JpdGluZykpLnNkaztcblxuICBjb25zdCBjdXJyZW50VGVtcGxhdGUgPSBhd2FpdCBsb2FkQ3VycmVudFRlbXBsYXRlV2l0aE5lc3RlZFN0YWNrcyhzdGFja0FydGlmYWN0LCBzZGspO1xuXG4gIGNvbnN0IGV2YWx1YXRlQ2ZuVGVtcGxhdGUgPSBuZXcgRXZhbHVhdGVDbG91ZEZvcm1hdGlvblRlbXBsYXRlKHtcbiAgICBzdGFja05hbWU6IHN0YWNrQXJ0aWZhY3Quc3RhY2tOYW1lLFxuICAgIHRlbXBsYXRlOiBzdGFja0FydGlmYWN0LnRlbXBsYXRlLFxuICAgIHBhcmFtZXRlcnM6IGFzc2V0UGFyYW1zLFxuICAgIGFjY291bnQ6IHJlc29sdmVkRW52LmFjY291bnQsXG4gICAgcmVnaW9uOiByZXNvbHZlZEVudi5yZWdpb24sXG4gICAgcGFydGl0aW9uOiAoYXdhaXQgc2RrLmN1cnJlbnRBY2NvdW50KCkpLnBhcnRpdGlvbixcbiAgICBzZGssXG4gICAgbmVzdGVkU3RhY2tzOiBjdXJyZW50VGVtcGxhdGUubmVzdGVkU3RhY2tzLFxuICB9KTtcblxuICBjb25zdCBzdGFja0NoYW5nZXMgPSBjZm5fZGlmZi5mdWxsRGlmZihjdXJyZW50VGVtcGxhdGUuZGVwbG95ZWRSb290VGVtcGxhdGUsIHN0YWNrQXJ0aWZhY3QudGVtcGxhdGUpO1xuICBjb25zdCB7IGhvdHN3YXBwYWJsZUNoYW5nZXMsIG5vbkhvdHN3YXBwYWJsZUNoYW5nZXMgfSA9IGF3YWl0IGNsYXNzaWZ5UmVzb3VyY2VDaGFuZ2VzKFxuICAgIHN0YWNrQ2hhbmdlcyxcbiAgICBldmFsdWF0ZUNmblRlbXBsYXRlLFxuICAgIHNkayxcbiAgICBjdXJyZW50VGVtcGxhdGUubmVzdGVkU3RhY2tzLCBob3Rzd2FwUHJvcGVydHlPdmVycmlkZXMsXG4gICk7XG5cbiAgbG9nTm9uSG90c3dhcHBhYmxlQ2hhbmdlcyhub25Ib3Rzd2FwcGFibGVDaGFuZ2VzLCBob3Rzd2FwTW9kZSk7XG5cbiAgLy8gcHJlc2VydmUgY2xhc3NpYyBob3Rzd2FwIGJlaGF2aW9yXG4gIGlmIChob3Rzd2FwTW9kZSA9PT0gSG90c3dhcE1vZGUuRkFMTF9CQUNLKSB7XG4gICAgaWYgKG5vbkhvdHN3YXBwYWJsZUNoYW5nZXMubGVuZ3RoID4gMCkge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICAvLyBhcHBseSB0aGUgc2hvcnQtY2lyY3VpdGFibGUgY2hhbmdlc1xuICBhd2FpdCBhcHBseUFsbEhvdHN3YXBwYWJsZUNoYW5nZXMoc2RrLCBob3Rzd2FwcGFibGVDaGFuZ2VzKTtcblxuICByZXR1cm4ge1xuICAgIHR5cGU6ICdkaWQtZGVwbG95LXN0YWNrJyxcbiAgICBub09wOiBob3Rzd2FwcGFibGVDaGFuZ2VzLmxlbmd0aCA9PT0gMCxcbiAgICBzdGFja0FybjogY2xvdWRGb3JtYXRpb25TdGFjay5zdGFja0lkLFxuICAgIG91dHB1dHM6IGNsb3VkRm9ybWF0aW9uU3RhY2sub3V0cHV0cyxcbiAgfTtcbn1cblxuLyoqXG4gKiBDbGFzc2lmaWVzIGFsbCBjaGFuZ2VzIHRvIGFsbCByZXNvdXJjZXMgYXMgZWl0aGVyIGhvdHN3YXBwYWJsZSBvciBub3QuXG4gKiBNZXRhZGF0YSBjaGFuZ2VzIGFyZSBleGNsdWRlZCBmcm9tIHRoZSBsaXN0IG9mIChub24paG90c3dhcHBhYmxlIHJlc291cmNlcy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY2xhc3NpZnlSZXNvdXJjZUNoYW5nZXMoXG4gIHN0YWNrQ2hhbmdlczogY2ZuX2RpZmYuVGVtcGxhdGVEaWZmLFxuICBldmFsdWF0ZUNmblRlbXBsYXRlOiBFdmFsdWF0ZUNsb3VkRm9ybWF0aW9uVGVtcGxhdGUsXG4gIHNkazogU0RLLFxuICBuZXN0ZWRTdGFja05hbWVzOiB7IFtuZXN0ZWRTdGFja05hbWU6IHN0cmluZ106IE5lc3RlZFN0YWNrVGVtcGxhdGVzIH0sXG4gIGhvdHN3YXBQcm9wZXJ0eU92ZXJyaWRlczogSG90c3dhcFByb3BlcnR5T3ZlcnJpZGVzLFxuKTogUHJvbWlzZTxDbGFzc2lmaWVkUmVzb3VyY2VDaGFuZ2VzPiB7XG4gIGNvbnN0IHJlc291cmNlRGlmZmVyZW5jZXMgPSBnZXRTdGFja1Jlc291cmNlRGlmZmVyZW5jZXMoc3RhY2tDaGFuZ2VzKTtcblxuICBjb25zdCBwcm9taXNlczogQXJyYXk8KCkgPT4gUHJvbWlzZTxDaGFuZ2VIb3Rzd2FwUmVzdWx0Pj4gPSBbXTtcbiAgY29uc3QgaG90c3dhcHBhYmxlUmVzb3VyY2VzID0gbmV3IEFycmF5PEhvdHN3YXBwYWJsZUNoYW5nZT4oKTtcbiAgY29uc3Qgbm9uSG90c3dhcHBhYmxlUmVzb3VyY2VzID0gbmV3IEFycmF5PE5vbkhvdHN3YXBwYWJsZUNoYW5nZT4oKTtcbiAgZm9yIChjb25zdCBsb2dpY2FsSWQgb2YgT2JqZWN0LmtleXMoc3RhY2tDaGFuZ2VzLm91dHB1dHMuY2hhbmdlcykpIHtcbiAgICBub25Ib3Rzd2FwcGFibGVSZXNvdXJjZXMucHVzaCh7XG4gICAgICBob3Rzd2FwcGFibGU6IGZhbHNlLFxuICAgICAgcmVhc29uOiAnb3V0cHV0IHdhcyBjaGFuZ2VkJyxcbiAgICAgIGxvZ2ljYWxJZCxcbiAgICAgIHJlamVjdGVkQ2hhbmdlczogW10sXG4gICAgICByZXNvdXJjZVR5cGU6ICdTdGFjayBPdXRwdXQnLFxuICAgIH0pO1xuICB9XG4gIC8vIGdhdGhlciB0aGUgcmVzdWx0cyBvZiB0aGUgZGV0ZWN0b3IgZnVuY3Rpb25zXG4gIGZvciAoY29uc3QgW2xvZ2ljYWxJZCwgY2hhbmdlXSBvZiBPYmplY3QuZW50cmllcyhyZXNvdXJjZURpZmZlcmVuY2VzKSkge1xuICAgIGlmIChjaGFuZ2UubmV3VmFsdWU/LlR5cGUgPT09ICdBV1M6OkNsb3VkRm9ybWF0aW9uOjpTdGFjaycgJiYgY2hhbmdlLm9sZFZhbHVlPy5UeXBlID09PSAnQVdTOjpDbG91ZEZvcm1hdGlvbjo6U3RhY2snKSB7XG4gICAgICBjb25zdCBuZXN0ZWRIb3Rzd2FwcGFibGVSZXNvdXJjZXMgPSBhd2FpdCBmaW5kTmVzdGVkSG90c3dhcHBhYmxlQ2hhbmdlcyhcbiAgICAgICAgbG9naWNhbElkLFxuICAgICAgICBjaGFuZ2UsXG4gICAgICAgIG5lc3RlZFN0YWNrTmFtZXMsXG4gICAgICAgIGV2YWx1YXRlQ2ZuVGVtcGxhdGUsXG4gICAgICAgIHNkayxcbiAgICAgICAgaG90c3dhcFByb3BlcnR5T3ZlcnJpZGVzLFxuICAgICAgKTtcbiAgICAgIGhvdHN3YXBwYWJsZVJlc291cmNlcy5wdXNoKC4uLm5lc3RlZEhvdHN3YXBwYWJsZVJlc291cmNlcy5ob3Rzd2FwcGFibGVDaGFuZ2VzKTtcbiAgICAgIG5vbkhvdHN3YXBwYWJsZVJlc291cmNlcy5wdXNoKC4uLm5lc3RlZEhvdHN3YXBwYWJsZVJlc291cmNlcy5ub25Ib3Rzd2FwcGFibGVDaGFuZ2VzKTtcblxuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgY29uc3QgaG90c3dhcHBhYmxlQ2hhbmdlQ2FuZGlkYXRlID0gaXNDYW5kaWRhdGVGb3JIb3Rzd2FwcGluZyhjaGFuZ2UsIGxvZ2ljYWxJZCk7XG4gICAgLy8gd2UgZG9uJ3QgbmVlZCB0byBydW4gdGhpcyB0aHJvdWdoIHRoZSBkZXRlY3RvciBmdW5jdGlvbnMsIHdlIGNhbiBhbHJlYWR5IGp1ZGdlIHRoaXNcbiAgICBpZiAoJ2hvdHN3YXBwYWJsZScgaW4gaG90c3dhcHBhYmxlQ2hhbmdlQ2FuZGlkYXRlKSB7XG4gICAgICBpZiAoIWhvdHN3YXBwYWJsZUNoYW5nZUNhbmRpZGF0ZS5ob3Rzd2FwcGFibGUpIHtcbiAgICAgICAgbm9uSG90c3dhcHBhYmxlUmVzb3VyY2VzLnB1c2goaG90c3dhcHBhYmxlQ2hhbmdlQ2FuZGlkYXRlKTtcbiAgICAgIH1cblxuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzb3VyY2VUeXBlOiBzdHJpbmcgPSBob3Rzd2FwcGFibGVDaGFuZ2VDYW5kaWRhdGUubmV3VmFsdWUuVHlwZTtcbiAgICBpZiAocmVzb3VyY2VUeXBlIGluIFJFU09VUkNFX0RFVEVDVE9SUykge1xuICAgICAgLy8gcnVuIGRldGVjdG9yIGZ1bmN0aW9ucyBsYXppbHkgdG8gcHJldmVudCB1bmhhbmRsZWQgcHJvbWlzZSByZWplY3Rpb25zXG4gICAgICBwcm9taXNlcy5wdXNoKCgpID0+XG4gICAgICAgIFJFU09VUkNFX0RFVEVDVE9SU1tyZXNvdXJjZVR5cGVdKGxvZ2ljYWxJZCwgaG90c3dhcHBhYmxlQ2hhbmdlQ2FuZGlkYXRlLCBldmFsdWF0ZUNmblRlbXBsYXRlLCBob3Rzd2FwUHJvcGVydHlPdmVycmlkZXMpLFxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmVwb3J0Tm9uSG90c3dhcHBhYmxlQ2hhbmdlKFxuICAgICAgICBub25Ib3Rzd2FwcGFibGVSZXNvdXJjZXMsXG4gICAgICAgIGhvdHN3YXBwYWJsZUNoYW5nZUNhbmRpZGF0ZSxcbiAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgICAnVGhpcyByZXNvdXJjZSB0eXBlIGlzIG5vdCBzdXBwb3J0ZWQgZm9yIGhvdHN3YXAgZGVwbG95bWVudHMnLFxuICAgICAgKTtcbiAgICB9XG4gIH1cblxuICAvLyByZXNvbHZlIGFsbCBkZXRlY3RvciByZXN1bHRzXG4gIGNvbnN0IGNoYW5nZXNEZXRlY3Rpb25SZXN1bHRzOiBBcnJheTxDaGFuZ2VIb3Rzd2FwUmVzdWx0PiA9IFtdO1xuICBmb3IgKGNvbnN0IGRldGVjdG9yUmVzdWx0UHJvbWlzZXMgb2YgcHJvbWlzZXMpIHtcbiAgICAvLyBDb25zdGFudCBzZXQgb2YgcHJvbWlzZXMgcGVyIHJlc291cmNlXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEBjZGtsYWJzL3Byb21pc2VhbGwtbm8tdW5ib3VuZGVkLXBhcmFsbGVsaXNtXG4gICAgY29uc3QgaG90c3dhcERldGVjdGlvblJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChhd2FpdCBkZXRlY3RvclJlc3VsdFByb21pc2VzKCkpO1xuICAgIGNoYW5nZXNEZXRlY3Rpb25SZXN1bHRzLnB1c2goaG90c3dhcERldGVjdGlvblJlc3VsdHMpO1xuICB9XG5cbiAgZm9yIChjb25zdCByZXNvdXJjZURldGVjdGlvblJlc3VsdHMgb2YgY2hhbmdlc0RldGVjdGlvblJlc3VsdHMpIHtcbiAgICBmb3IgKGNvbnN0IHByb3BlcnR5UmVzdWx0IG9mIHJlc291cmNlRGV0ZWN0aW9uUmVzdWx0cykge1xuICAgICAgcHJvcGVydHlSZXN1bHQuaG90c3dhcHBhYmxlXG4gICAgICAgID8gaG90c3dhcHBhYmxlUmVzb3VyY2VzLnB1c2gocHJvcGVydHlSZXN1bHQpXG4gICAgICAgIDogbm9uSG90c3dhcHBhYmxlUmVzb3VyY2VzLnB1c2gocHJvcGVydHlSZXN1bHQpO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgaG90c3dhcHBhYmxlQ2hhbmdlczogaG90c3dhcHBhYmxlUmVzb3VyY2VzLFxuICAgIG5vbkhvdHN3YXBwYWJsZUNoYW5nZXM6IG5vbkhvdHN3YXBwYWJsZVJlc291cmNlcyxcbiAgfTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGFsbCBjaGFuZ2VzIHRvIHJlc291cmNlcyBpbiB0aGUgZ2l2ZW4gU3RhY2suXG4gKlxuICogQHBhcmFtIHN0YWNrQ2hhbmdlcyB0aGUgY29sbGVjdGlvbiBvZiBhbGwgY2hhbmdlcyB0byBhIGdpdmVuIFN0YWNrXG4gKi9cbmZ1bmN0aW9uIGdldFN0YWNrUmVzb3VyY2VEaWZmZXJlbmNlcyhzdGFja0NoYW5nZXM6IGNmbl9kaWZmLlRlbXBsYXRlRGlmZik6IHtcbiAgW2xvZ2ljYWxJZDogc3RyaW5nXTogY2ZuX2RpZmYuUmVzb3VyY2VEaWZmZXJlbmNlO1xufSB7XG4gIC8vIHdlIG5lZWQgdG8gY29sbGFwc2UgbG9naWNhbCBJRCByZW5hbWUgY2hhbmdlcyBpbnRvIG9uZSBjaGFuZ2UsXG4gIC8vIGFzIHRoZXkgYXJlIHJlcHJlc2VudGVkIGluIHN0YWNrQ2hhbmdlcyBhcyBhIHBhaXIgb2YgdHdvIGNoYW5nZXM6IG9uZSBhZGRpdGlvbiBhbmQgb25lIHJlbW92YWxcbiAgY29uc3QgYWxsUmVzb3VyY2VDaGFuZ2VzOiB7IFtsb2dJZDogc3RyaW5nXTogY2ZuX2RpZmYuUmVzb3VyY2VEaWZmZXJlbmNlIH0gPSBzdGFja0NoYW5nZXMucmVzb3VyY2VzLmNoYW5nZXM7XG4gIGNvbnN0IGFsbFJlbW92YWxDaGFuZ2VzID0gZmlsdGVyRGljdChhbGxSZXNvdXJjZUNoYW5nZXMsIChyZXNDaGFuZ2UpID0+IHJlc0NoYW5nZS5pc1JlbW92YWwpO1xuICBjb25zdCBhbGxOb25SZW1vdmFsQ2hhbmdlcyA9IGZpbHRlckRpY3QoYWxsUmVzb3VyY2VDaGFuZ2VzLCAocmVzQ2hhbmdlKSA9PiAhcmVzQ2hhbmdlLmlzUmVtb3ZhbCk7XG4gIGZvciAoY29uc3QgW2xvZ0lkLCBub25SZW1vdmFsQ2hhbmdlXSBvZiBPYmplY3QuZW50cmllcyhhbGxOb25SZW1vdmFsQ2hhbmdlcykpIHtcbiAgICBpZiAobm9uUmVtb3ZhbENoYW5nZS5pc0FkZGl0aW9uKSB7XG4gICAgICBjb25zdCBhZGRDaGFuZ2UgPSBub25SZW1vdmFsQ2hhbmdlO1xuICAgICAgLy8gc2VhcmNoIGZvciBhbiBpZGVudGljYWwgcmVtb3ZhbCBjaGFuZ2VcbiAgICAgIGNvbnN0IGlkZW50aWNhbFJlbW92YWxDaGFuZ2UgPSBPYmplY3QuZW50cmllcyhhbGxSZW1vdmFsQ2hhbmdlcykuZmluZCgoW18sIHJlbUNoYW5nZV0pID0+IHtcbiAgICAgICAgcmV0dXJuIGNoYW5nZXNBcmVGb3JTYW1lUmVzb3VyY2UocmVtQ2hhbmdlLCBhZGRDaGFuZ2UpO1xuICAgICAgfSk7XG4gICAgICAvLyBpZiB3ZSBmb3VuZCBvbmUsIHRoZW4gdGhpcyBtZWFucyB0aGlzIGlzIGEgcmVuYW1lIGNoYW5nZVxuICAgICAgaWYgKGlkZW50aWNhbFJlbW92YWxDaGFuZ2UpIHtcbiAgICAgICAgY29uc3QgW3JlbW92ZWRMb2dJZCwgcmVtb3ZlZFJlc291cmNlQ2hhbmdlXSA9IGlkZW50aWNhbFJlbW92YWxDaGFuZ2U7XG4gICAgICAgIGFsbE5vblJlbW92YWxDaGFuZ2VzW2xvZ0lkXSA9IG1ha2VSZW5hbWVEaWZmZXJlbmNlKHJlbW92ZWRSZXNvdXJjZUNoYW5nZSwgYWRkQ2hhbmdlKTtcbiAgICAgICAgLy8gZGVsZXRlIHRoZSByZW1vdmFsIGNoYW5nZSB0aGF0IGZvcm1zIHRoZSByZW5hbWUgcGFpclxuICAgICAgICBkZWxldGUgYWxsUmVtb3ZhbENoYW5nZXNbcmVtb3ZlZExvZ0lkXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgLy8gdGhlIGZpbmFsIHJlc3VsdCBhcmUgYWxsIG9mIHRoZSByZW1haW5pbmcgcmVtb3ZhbCBjaGFuZ2VzLFxuICAvLyBwbHVzIGFsbCBvZiB0aGUgbm9uLXJlbW92YWwgY2hhbmdlc1xuICAvLyAod2Ugc2F2ZWQgdGhlIHJlbmFtZSBjaGFuZ2VzIGluIHRoYXQgb2JqZWN0IGFscmVhZHkpXG4gIHJldHVybiB7XG4gICAgLi4uYWxsUmVtb3ZhbENoYW5nZXMsXG4gICAgLi4uYWxsTm9uUmVtb3ZhbENoYW5nZXMsXG4gIH07XG59XG5cbi8qKiBGaWx0ZXJzIGFuIG9iamVjdCB3aXRoIHN0cmluZyBrZXlzIGJhc2VkIG9uIHdoZXRoZXIgdGhlIGNhbGxiYWNrIHJldHVybnMgJ3RydWUnIGZvciB0aGUgZ2l2ZW4gdmFsdWUgaW4gdGhlIG9iamVjdC4gKi9cbmZ1bmN0aW9uIGZpbHRlckRpY3Q8VD4oZGljdDogeyBba2V5OiBzdHJpbmddOiBUIH0sIGZ1bmM6ICh0OiBUKSA9PiBib29sZWFuKTogeyBba2V5OiBzdHJpbmddOiBUIH0ge1xuICByZXR1cm4gT2JqZWN0LmVudHJpZXMoZGljdCkucmVkdWNlKFxuICAgIChhY2MsIFtrZXksIHRdKSA9PiB7XG4gICAgICBpZiAoZnVuYyh0KSkge1xuICAgICAgICBhY2Nba2V5XSA9IHQ7XG4gICAgICB9XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sXG4gICAge30gYXMgeyBba2V5OiBzdHJpbmddOiBUIH0sXG4gICk7XG59XG5cbi8qKiBGaW5kcyBhbnkgaG90c3dhcHBhYmxlIGNoYW5nZXMgaW4gYWxsIG5lc3RlZCBzdGFja3MuICovXG5hc3luYyBmdW5jdGlvbiBmaW5kTmVzdGVkSG90c3dhcHBhYmxlQ2hhbmdlcyhcbiAgbG9naWNhbElkOiBzdHJpbmcsXG4gIGNoYW5nZTogY2ZuX2RpZmYuUmVzb3VyY2VEaWZmZXJlbmNlLFxuICBuZXN0ZWRTdGFja1RlbXBsYXRlczogeyBbbmVzdGVkU3RhY2tOYW1lOiBzdHJpbmddOiBOZXN0ZWRTdGFja1RlbXBsYXRlcyB9LFxuICBldmFsdWF0ZUNmblRlbXBsYXRlOiBFdmFsdWF0ZUNsb3VkRm9ybWF0aW9uVGVtcGxhdGUsXG4gIHNkazogU0RLLFxuICBob3Rzd2FwUHJvcGVydHlPdmVycmlkZXM6IEhvdHN3YXBQcm9wZXJ0eU92ZXJyaWRlcyxcbik6IFByb21pc2U8Q2xhc3NpZmllZFJlc291cmNlQ2hhbmdlcz4ge1xuICBjb25zdCBuZXN0ZWRTdGFjayA9IG5lc3RlZFN0YWNrVGVtcGxhdGVzW2xvZ2ljYWxJZF07XG4gIGlmICghbmVzdGVkU3RhY2sucGh5c2ljYWxOYW1lKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGhvdHN3YXBwYWJsZUNoYW5nZXM6IFtdLFxuICAgICAgbm9uSG90c3dhcHBhYmxlQ2hhbmdlczogW1xuICAgICAgICB7XG4gICAgICAgICAgaG90c3dhcHBhYmxlOiBmYWxzZSxcbiAgICAgICAgICBsb2dpY2FsSWQsXG4gICAgICAgICAgcmVhc29uOiBgcGh5c2ljYWwgbmFtZSBmb3IgQVdTOjpDbG91ZEZvcm1hdGlvbjo6U3RhY2sgJyR7bG9naWNhbElkfScgY291bGQgbm90IGJlIGZvdW5kIGluIENsb3VkRm9ybWF0aW9uLCBzbyB0aGlzIGlzIGEgbmV3bHkgY3JlYXRlZCBuZXN0ZWQgc3RhY2sgYW5kIGNhbm5vdCBiZSBob3Rzd2FwcGVkYCxcbiAgICAgICAgICByZWplY3RlZENoYW5nZXM6IFtdLFxuICAgICAgICAgIHJlc291cmNlVHlwZTogJ0FXUzo6Q2xvdWRGb3JtYXRpb246OlN0YWNrJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfTtcbiAgfVxuXG4gIGNvbnN0IGV2YWx1YXRlTmVzdGVkQ2ZuVGVtcGxhdGUgPSBhd2FpdCBldmFsdWF0ZUNmblRlbXBsYXRlLmNyZWF0ZU5lc3RlZEV2YWx1YXRlQ2xvdWRGb3JtYXRpb25UZW1wbGF0ZShcbiAgICBuZXN