@aws-cdk-testing/cli-integ
Version:
Integration tests for the AWS CDK CLI
249 lines • 39 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SamIntegrationTestFixture = void 0;
exports.withSamIntegrationCdkApp = withSamIntegrationCdkApp;
exports.withSamIntegrationFixture = withSamIntegrationFixture;
exports.randomInteger = randomInteger;
exports.shellWithAction = shellWithAction;
const child_process = require("child_process");
const os = require("os");
const path = require("path");
const axios = require("axios");
const resources_1 = require("./resources");
const shell_1 = require("./shell");
const with_aws_1 = require("./with-aws");
const with_cdk_app_1 = require("./with-cdk-app");
const with_timeout_1 = require("./with-timeout");
/**
* Higher order function to execute a block with a SAM Integration CDK app fixture
*/
function withSamIntegrationCdkApp(block) {
return async (context) => {
const randy = context.randomString;
const stackNamePrefix = `cdktest-${randy}`;
const integTestDir = path.join(os.tmpdir(), `cdk-integ-${randy}`);
context.log(` Stack prefix: ${stackNamePrefix}\n`);
context.log(` Test directory: ${integTestDir}\n`);
context.log(` Region: ${context.aws.region}\n`);
await (0, with_cdk_app_1.cloneDirectory)(path.join(resources_1.RESOURCES_DIR, 'cdk-apps', 'sam_cdk_integ_app'), integTestDir, context.output);
const fixture = new SamIntegrationTestFixture(integTestDir, stackNamePrefix, context.output, context.aws, context.randomString);
await fixture.ecrPublicLogin();
let success = true;
try {
const installationVersion = fixture.library.requestedVersion();
const alphaInstallationVersion = fixture.library.requestedAlphaVersion();
await (0, with_cdk_app_1.installNpmPackages)(fixture, {
'aws-cdk-lib': installationVersion,
'@aws-cdk/aws-lambda-go-alpha': alphaInstallationVersion,
'@aws-cdk/aws-lambda-python-alpha': alphaInstallationVersion,
'constructs': '^10',
});
await block(fixture);
}
catch (e) {
// We survive certain cases involving gopkg.in
if (errorCausedByGoPkg(e.message)) {
return;
}
success = false;
throw e;
}
finally {
if (process.env.INTEG_NO_CLEAN) {
context.log(`Left test directory in '${integTestDir}' ($INTEG_NO_CLEAN)\n`);
}
else {
await fixture.dispose(success);
}
}
};
}
/**
* Return whether or not the error is being caused by gopkg.in being down
*
* Our Go build depends on https://gopkg.in/, which has errors pretty often
* (every couple of days). It is run by a single volunteer.
*/
function errorCausedByGoPkg(error) {
// The error is different depending on what request fails. Messages recognized:
////////////////////////////////////////////////////////////////////
// go: github.com/aws/aws-lambda-go@v1.28.0 requires
// gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: invalid version: git ls-remote -q origin in /go/pkg/mod/cache/vcs/0901dc1ef67fcce1c9b3ae51078740de4a0e2dc673e720584ac302973af82f36: exit status 128:
// remote: Cannot obtain refs from GitHub: cannot talk to GitHub: Get https://github.com/go-yaml/yaml.git/info/refs?service=git-upload-pack: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
// fatal: unable to access 'https://gopkg.in/yaml.v3/': The requested URL returned error: 502
////////////////////////////////////////////////////////////////////
// go: downloading github.com/aws/aws-lambda-go v1.28.0
// go: github.com/aws/aws-lambda-go@v1.28.0 requires
// gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: unrecognized import path "gopkg.in/yaml.v3": reading https://gopkg.in/yaml.v3?go-get=1: 502 Bad Gateway
// server response: Cannot obtain refs from GitHub: cannot talk to GitHub: Get https://github.com/go-yaml/yaml.git/info/refs?service=git-upload-pack: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
////////////////////////////////////////////////////////////////////
// go: github.com/aws/aws-lambda-go@v1.28.0 requires
// gopkg.in/yaml.v3@v3.0.0-20200615113413-eeeca48fe776: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /go/pkg/mod/cache/vcs/0901dc1ef67fcce1c9b3ae51078740de4a0e2dc673e720584ac302973af82f36: exit status 128:
// error: RPC failed; HTTP 502 curl 22 The requested URL returned error: 502
// fatal: the remote end hung up unexpectedly
////////////////////////////////////////////////////////////////////
return (error.includes('gopkg\.in.*invalid version.*exit status 128')
|| error.match(/unrecognized import path[^\n]gopkg\.in/));
}
/**
* SAM Integration test fixture for CDK - SAM integration test cases
*/
function withSamIntegrationFixture(block) {
return (0, with_aws_1.withAws)((0, with_timeout_1.withTimeout)(with_cdk_app_1.DEFAULT_TEST_TIMEOUT_S, withSamIntegrationCdkApp(block)));
}
class SamIntegrationTestFixture extends with_cdk_app_1.TestFixture {
async samShell(command, filter, action, options = {}) {
return shellWithAction(command, filter, action, {
outputs: [this.output],
cwd: path.join(this.integTestDir, 'cdk.out').toString(),
...options,
});
}
async samBuild(stackName) {
const fullStackName = this.fullStackName(stackName);
const templatePath = path.join(this.integTestDir, 'cdk.out', `${fullStackName}.template.json`);
const args = ['--template', templatePath.toString()];
return this.samShell(['sam', 'build', ...args]);
}
async samLocalStartApi(stackName, isBuilt, port, apiPath) {
const fullStackName = this.fullStackName(stackName);
const templatePath = path.join(this.integTestDir, 'cdk.out', `${fullStackName}.template.json`);
const args = isBuilt ? [] : ['--template', templatePath.toString()];
args.push('--port');
args.push(port.toString());
// https://github.com/aws/aws-sam-cli/pull/7892
args.push('--no-memory-limit');
// "Press Ctrl+C to quit" looks to be printed by a Flask server built into SAM CLI.
return this.samShell(['sam', 'local', 'start-api', ...args], 'Press CTRL+C to quit', () => {
return new Promise((resolve, reject) => {
axios.get(`http://127.0.0.1:${port}${apiPath}`).then(resp => {
resolve(resp.data);
}).catch(error => {
reject(new Error(`Failed to invoke api path ${apiPath} on port ${port} with error ${error}`));
});
});
});
}
/**
* Cleanup leftover stacks and buckets
*/
async dispose(success) {
// If the tests completed successfully, happily delete the fixture
// (otherwise leave it for humans to inspect)
if (success) {
const cleaned = (0, shell_1.rimraf)(this.integTestDir);
if (!cleaned) {
// eslint-disable-next-line no-console
console.error(`Failed to clean up ${this.integTestDir} due to permissions issues (Docker running as root?)`);
}
}
}
}
exports.SamIntegrationTestFixture = SamIntegrationTestFixture;
function randomInteger(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
/**
* A shell command that does what you want
*
* Is platform-aware, handles errors nicely.
*/
async function shellWithAction(command, filter, action, options = {}, actionTimeoutSeconds = 600) {
if (options.modEnv && options.env) {
throw new Error('Use either env or modEnv but not both');
}
const writeToOutputs = (x) => {
for (const output of options.outputs ?? []) {
output.write(x);
}
};
writeToOutputs(`💻 ${command.join(' ')}\n`);
const env = options.env ?? (options.modEnv ? { ...process.env, ...options.modEnv } : undefined);
const child = child_process.spawn(command[0], command.slice(1), {
...options,
env,
// Need this for Windows where we want .cmd and .bat to be found as well.
shell: true,
stdio: ['ignore', 'pipe', 'pipe'],
});
return new Promise((resolve, reject) => {
const out = new Array();
const stdout = new Array();
const stderr = new Array();
let actionSucceeded = false;
let actionOutput;
let actionExecuted = false;
async function maybeExecuteAction(chunk) {
out.push(Buffer.from(chunk));
if (!actionExecuted && typeof filter === 'string' && Buffer.concat(out).toString('utf-8').includes(filter) && typeof action === 'function') {
actionExecuted = true;
writeToOutputs('before executing action\n');
try {
const output = await action();
writeToOutputs(`action output is ${JSON.stringify(output)}\n`);
actionOutput = output;
actionSucceeded = true;
}
catch (error) {
writeToOutputs(`action error is ${error}\n`);
actionSucceeded = false;
actionOutput = error;
}
finally {
writeToOutputs('terminate sam sub process\n');
killSubProcess(child, command.join(' '));
}
}
}
if (typeof filter === 'string' && typeof action === 'function') {
// Reject with an error if an action is configured, but the filter failed
// to show up in the output before the timeout occurred.
setTimeout(() => {
if (!actionExecuted) {
reject(new Error(`Timed out waiting for filter ${JSON.stringify(filter)} to appear in command output after ${actionTimeoutSeconds} seconds\nOutput so far:\n${Buffer.concat(out).toString('utf-8')}`));
killSubProcess(child, command.join(' '));
}
}, actionTimeoutSeconds * 1_000).unref();
}
child.stdout.on('data', chunk => {
writeToOutputs(chunk);
stdout.push(chunk);
void maybeExecuteAction(chunk);
});
child.stderr.on('data', chunk => {
writeToOutputs(chunk);
if (options.captureStderr ?? true) {
stderr.push(chunk);
}
void maybeExecuteAction(chunk);
});
child.once('error', reject);
// Wait for 'exit' instead of close, don't care about reading the streams all the way to the end
child.once('exit', (code, signal) => {
writeToOutputs(`Subprocess has exited with code ${code}, signal ${signal}\n`);
const output = (Buffer.concat(stdout).toString('utf-8') + Buffer.concat(stderr).toString('utf-8')).trim();
if (code == null || code === 0 || options.allowErrExit) {
let result = new Array();
result.push(actionOutput);
result.push(output);
resolve({
actionSucceeded: actionSucceeded,
actionOutput: actionOutput,
shellOutput: output,
});
}
else {
reject(new Error(`'${command.join(' ')}' exited with error code ${code}. Output: \n${output}`));
}
});
});
}
function killSubProcess(child, command) {
/**
* Check if the sub process is running in container, so child_process.spawn will
* create multiple processes, and to kill all of them we need to run different logic
*/
child.kill('SIGINT');
child_process.exec(`for pid in $(ps -ef | grep "${command}" | awk '{print $2}'); do kill -2 $pid; done`);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aC1zYW0uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ3aXRoLXNhbS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFzQkEsNERBK0NDO0FBa0NELDhEQUVDO0FBd0RELHNDQUVDO0FBT0QsMENBd0dDO0FBbFJELCtDQUErQztBQUMvQyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLCtCQUErQjtBQUUvQiwyQ0FBNEM7QUFFNUMsbUNBQWlDO0FBRWpDLHlDQUFxQztBQUNyQyxpREFBeUc7QUFDekcsaURBQTZDO0FBUTdDOztHQUVHO0FBQ0gsU0FBZ0Isd0JBQXdCLENBQXFDLEtBQTREO0lBQ3ZJLE9BQU8sS0FBSyxFQUFFLE9BQVUsRUFBRSxFQUFFO1FBQzFCLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUM7UUFDbkMsTUFBTSxlQUFlLEdBQUcsV0FBVyxLQUFLLEVBQUUsQ0FBQztRQUMzQyxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsRUFBRSxhQUFhLEtBQUssRUFBRSxDQUFDLENBQUM7UUFFbEUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQkFBb0IsZUFBZSxJQUFJLENBQUMsQ0FBQztRQUNyRCxPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixZQUFZLElBQUksQ0FBQyxDQUFDO1FBQ2xELE9BQU8sQ0FBQyxHQUFHLENBQUMsb0JBQW9CLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxJQUFJLENBQUMsQ0FBQztRQUV4RCxNQUFNLElBQUEsNkJBQWMsRUFBQyxJQUFJLENBQUMsSUFBSSxDQUFDLHlCQUFhLEVBQUUsVUFBVSxFQUFFLG1CQUFtQixDQUFDLEVBQUUsWUFBWSxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUM5RyxNQUFNLE9BQU8sR0FBRyxJQUFJLHlCQUF5QixDQUMzQyxZQUFZLEVBQ1osZUFBZSxFQUNmLE9BQU8sQ0FBQyxNQUFNLEVBQ2QsT0FB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QStCLE9BQU8sOENBQThDLENBQUMsQ0FBQztBQUMzRyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgY2hpbGRfcHJvY2VzcyBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCAqIGFzIG9zIGZyb20gJ29zJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgKiBhcyBheGlvcyBmcm9tICdheGlvcyc7XG5pbXBvcnQgdHlwZSB7IFRlc3RDb250ZXh0IH0gZnJvbSAnLi9pbnRlZy10ZXN0JztcbmltcG9ydCB7IFJFU09VUkNFU19ESVIgfSBmcm9tICcuL3Jlc291cmNlcyc7XG5pbXBvcnQgdHlwZSB7IFNoZWxsT3B0aW9ucyB9IGZyb20gJy4vc2hlbGwnO1xuaW1wb3J0IHsgcmltcmFmIH0gZnJvbSAnLi9zaGVsbCc7XG5pbXBvcnQgdHlwZSB7IEF3c0NvbnRleHQgfSBmcm9tICcuL3dpdGgtYXdzJztcbmltcG9ydCB7IHdpdGhBd3MgfSBmcm9tICcuL3dpdGgtYXdzJztcbmltcG9ydCB7IGNsb25lRGlyZWN0b3J5LCBpbnN0YWxsTnBtUGFja2FnZXMsIFRlc3RGaXh0dXJlLCBERUZBVUxUX1RFU1RfVElNRU9VVF9TIH0gZnJvbSAnLi93aXRoLWNkay1hcHAnO1xuaW1wb3J0IHsgd2l0aFRpbWVvdXQgfSBmcm9tICcuL3dpdGgtdGltZW91dCc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQWN0aW9uT3V0cHV0IHtcbiAgYWN0aW9uU3VjY2VlZGVkPzogYm9vbGVhbjtcbiAgYWN0aW9uT3V0cHV0PzogYW55O1xuICBzaGVsbE91dHB1dD86IHN0cmluZztcbn1cblxuLyoqXG4gKiBIaWdoZXIgb3JkZXIgZnVuY3Rpb24gdG8gZXhlY3V0ZSBhIGJsb2NrIHdpdGggYSBTQU0gSW50ZWdyYXRpb24gQ0RLIGFwcCBmaXh0dXJlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB3aXRoU2FtSW50ZWdyYXRpb25DZGtBcHA8QSBleHRlbmRzIFRlc3RDb250ZXh0ICYgQXdzQ29udGV4dD4oYmxvY2s6IChjb250ZXh0OiBTYW1JbnRlZ3JhdGlvblRlc3RGaXh0dXJlKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIHJldHVybiBhc3luYyAoY29udGV4dDogQSkgPT4ge1xuICAgIGNvbnN0IHJhbmR5ID0gY29udGV4dC5yYW5kb21TdHJpbmc7XG4gICAgY29uc3Qgc3RhY2tOYW1lUHJlZml4ID0gYGNka3Rlc3QtJHtyYW5keX1gO1xuICAgIGNvbnN0IGludGVnVGVzdERpciA9IHBhdGguam9pbihvcy50bXBkaXIoKSwgYGNkay1pbnRlZy0ke3JhbmR5fWApO1xuXG4gICAgY29udGV4dC5sb2coYCBTdGFjayBwcmVmaXg6ICAgJHtzdGFja05hbWVQcmVmaXh9XFxuYCk7XG4gICAgY29udGV4dC5sb2coYCBUZXN0IGRpcmVjdG9yeTogJHtpbnRlZ1Rlc3REaXJ9XFxuYCk7XG4gICAgY29udGV4dC5sb2coYCBSZWdpb246ICAgICAgICAgJHtjb250ZXh0LmF3cy5yZWdpb259XFxuYCk7XG5cbiAgICBhd2FpdCBjbG9uZURpcmVjdG9yeShwYXRoLmpvaW4oUkVTT1VSQ0VTX0RJUiwgJ2Nkay1hcHBzJywgJ3NhbV9jZGtfaW50ZWdfYXBwJyksIGludGVnVGVzdERpciwgY29udGV4dC5vdXRwdXQpO1xuICAgIGNvbnN0IGZpeHR1cmUgPSBuZXcgU2FtSW50ZWdyYXRpb25UZXN0Rml4dHVyZShcbiAgICAgIGludGVnVGVzdERpcixcbiAgICAgIHN0YWNrTmFtZVByZWZpeCxcbiAgICAgIGNvbnRleHQub3V0cHV0LFxuICAgICAgY29udGV4dC5hd3MsXG4gICAgICBjb250ZXh0LnJhbmRvbVN0cmluZyk7XG4gICAgYXdhaXQgZml4dHVyZS5lY3JQdWJsaWNMb2dpbigpO1xuXG4gICAgbGV0IHN1Y2Nlc3MgPSB0cnVlO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBpbnN0YWxsYXRpb25WZXJzaW9uID0gZml4dHVyZS5saWJyYXJ5LnJlcXVlc3RlZFZlcnNpb24oKTtcblxuICAgICAgY29uc3QgYWxwaGFJbnN0YWxsYXRpb25WZXJzaW9uID0gZml4dHVyZS5saWJyYXJ5LnJlcXVlc3RlZEFscGhhVmVyc2lvbigpO1xuICAgICAgYXdhaXQgaW5zdGFsbE5wbVBhY2thZ2VzKGZpeHR1cmUsIHtcbiAgICAgICAgJ2F3cy1jZGstbGliJzogaW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgJ0Bhd3MtY2RrL2F3cy1sYW1iZGEtZ28tYWxwaGEnOiBhbHBoYUluc3RhbGxhdGlvblZlcnNpb24sXG4gICAgICAgICdAYXdzLWNkay9hd3MtbGFtYmRhLXB5dGhvbi1hbHBoYSc6IGFscGhhSW5zdGFsbGF0aW9uVmVyc2lvbixcbiAgICAgICAgJ2NvbnN0cnVjdHMnOiAnXjEwJyxcbiAgICAgIH0pO1xuXG4gICAgICBhd2FpdCBibG9jayhmaXh0dXJlKTtcbiAgICB9IGNhdGNoIChlOiBhbnkpIHtcbiAgICAgIC8vIFdlIHN1cnZpdmUgY2VydGFpbiBjYXNlcyBpbnZvbHZpbmcgZ29wa2cuaW5cbiAgICAgIGlmIChlcnJvckNhdXNlZEJ5R29Qa2coZS5tZXNzYWdlKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBzdWNjZXNzID0gZmFsc2U7XG4gICAgICB0aHJvdyBlO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBpZiAocHJvY2Vzcy5lbnYuSU5URUdfTk9fQ0xFQU4pIHtcbiAgICAgICAgY29udGV4dC5sb2coYExlZnQgdGVzdCBkaXJlY3RvcnkgaW4gJyR7aW50ZWdUZXN0RGlyfScgKCRJTlRFR19OT19DTEVBTilcXG5gKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGF3YWl0IGZpeHR1cmUuZGlzcG9zZShzdWNjZXNzKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG59XG5cbi8qKlxuICogUmV0dXJuIHdoZXRoZXIgb3Igbm90IHRoZSBlcnJvciBpcyBiZWluZyBjYXVzZWQgYnkgZ29wa2cuaW4gYmVpbmcgZG93blxuICpcbiAqIE91ciBHbyBidWlsZCBkZXBlbmRzIG9uIGh0dHBzOi8vZ29wa2cuaW4vLCB3aGljaCBoYXMgZXJyb3JzIHByZXR0eSBvZnRlblxuICogKGV2ZXJ5IGNvdXBsZSBvZiBkYXlzKS4gSXQgaXMgcnVuIGJ5IGEgc2luZ2xlIHZvbHVudGVlci5cbiAqL1xuZnVuY3Rpb24gZXJyb3JDYXVzZWRCeUdvUGtnKGVycm9yOiBzdHJpbmcpIHtcbiAgLy8gVGhlIGVycm9yIGlzIGRpZmZlcmVudCBkZXBlbmRpbmcgb24gd2hhdCByZXF1ZXN0IGZhaWxzLiBNZXNzYWdlcyByZWNvZ25pemVkOlxuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyAgICBnbzogZ2l0aHViLmNvbS9hd3MvYXdzLWxhbWJkYS1nb0B2MS4yOC4wIHJlcXVpcmVzXG4gIC8vICAgICAgICBnb3BrZy5pbi95YW1sLnYzQHYzLjAuMC0yMDIwMDYxNTExMzQxMy1lZWVjYTQ4ZmU3NzY6IGludmFsaWQgdmVyc2lvbjogZ2l0IGxzLXJlbW90ZSAtcSBvcmlnaW4gaW4gL2dvL3BrZy9tb2QvY2FjaGUvdmNzLzA5MDFkYzFlZjY3ZmNjZTFjOWIzYWU1MTA3ODc0MGRlNGEwZTJkYzY3M2U3MjA1ODRhYzMwMjk3M2FmODJmMzY6IGV4aXQgc3RhdHVzIDEyODpcbiAgLy8gICAgICAgIHJlbW90ZTogQ2Fubm90IG9idGFpbiByZWZzIGZyb20gR2l0SHViOiBjYW5ub3QgdGFsayB0byBHaXRIdWI6IEdldCBodHRwczovL2dpdGh1Yi5jb20vZ28teWFtbC95YW1sLmdpdC9pbmZvL3JlZnM/c2VydmljZT1naXQtdXBsb2FkLXBhY2s6IG5ldC9odHRwOiByZXF1ZXN0IGNhbmNlbGVkIChDbGllbnQuVGltZW91dCBleGNlZWRlZCB3aGlsZSBhd2FpdGluZyBoZWFkZXJzKVxuICAvLyAgICAgICAgZmF0YWw6IHVuYWJsZSB0byBhY2Nlc3MgJ2h0dHBzOi8vZ29wa2cuaW4veWFtbC52My8nOiBUaGUgcmVxdWVzdGVkIFVSTCByZXR1cm5lZCBlcnJvcjogNTAyXG4gIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gIC8vICAgIGdvOiBkb3dubG9hZGluZyBnaXRodWIuY29tL2F3cy9hd3MtbGFtYmRhLWdvIHYxLjI4LjBcbiAgLy8gICAgZ286IGdpdGh1Yi5jb20vYXdzL2F3cy1sYW1iZGEtZ29AdjEuMjguMCByZXF1aXJlc1xuICAvLyAgICAgICAgZ29wa2cuaW4veWFtbC52M0B2My4wLjAtMjAyMDA2MTUxMTM0MTMtZWVlY2E0OGZlNzc2OiB1bnJlY29nbml6ZWQgaW1wb3J0IHBhdGggXCJnb3BrZy5pbi95YW1sLnYzXCI6IHJlYWRpbmcgaHR0cHM6Ly9nb3BrZy5pbi95YW1sLnYzP2dvLWdldD0xOiA1MDIgQmFkIEdhdGV3YXlcbiAgLy8gICAgICAgIHNlcnZlciByZXNwb25zZTogQ2Fubm90IG9idGFpbiByZWZzIGZyb20gR2l0SHViOiBjYW5ub3QgdGFsayB0byBHaXRIdWI6IEdldCBodHRwczovL2dpdGh1Yi5jb20vZ28teWFtbC95YW1sLmdpdC9pbmZvL3JlZnM/c2VydmljZT1naXQtdXBsb2FkLXBhY2s6IG5ldC9odHRwOiByZXF1ZXN0IGNhbmNlbGVkIChDbGllbnQuVGltZW91dCBleGNlZWRlZCB3aGlsZSBhd2FpdGluZyBoZWFkZXJzKVxuICAvLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAvLyAgICBnbzogZ2l0aHViLmNvbS9hd3MvYXdzLWxhbWJkYS1nb0B2MS4yOC4wIHJlcXVpcmVzXG4gIC8vICAgICAgICBnb3BrZy5pbi95YW1sLnYzQHYzLjAuMC0yMDIwMDYxNTExMzQxMy1lZWVjYTQ4ZmU3NzY6IGludmFsaWQgdmVyc2lvbjogZ2l0IGZldGNoIC1mIG9yaWdpbiByZWZzL2hlYWRzLyo6cmVmcy9oZWFkcy8qIHJlZnMvdGFncy8qOnJlZnMvdGFncy8qIGluIC9nby9wa2cvbW9kL2NhY2hlL3Zjcy8wOTAxZGMxZWY2N2ZjY2UxYzliM2FlNTEwNzg3NDBkZTRhMGUyZGM2NzNlNzIwNTg0YWMzMDI5NzNhZjgyZjM2OiBleGl0IHN0YXR1cyAxMjg6XG4gIC8vICAgICAgICBlcnJvcjogUlBDIGZhaWxlZDsgSFRUUCA1MDIgY3VybCAyMiBUaGUgcmVxdWVzdGVkIFVSTCByZXR1cm5lZCBlcnJvcjogNTAyXG4gIC8vICAgICAgICBmYXRhbDogdGhlIHJlbW90ZSBlbmQgaHVuZyB1cCB1bmV4cGVjdGVkbHlcbiAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICByZXR1cm4gKGVycm9yLmluY2x1ZGVzKCdnb3BrZ1xcLmluLippbnZhbGlkIHZlcnNpb24uKmV4aXQgc3RhdHVzIDEyOCcpXG4gICAgfHwgZXJyb3IubWF0Y2goL3VucmVjb2duaXplZCBpbXBvcnQgcGF0aFteXFxuXWdvcGtnXFwuaW4vKSk7XG59XG5cbi8qKlxuICogU0FNIEludGVncmF0aW9uIHRlc3QgZml4dHVyZSBmb3IgQ0RLIC0gU0FNIGludGVncmF0aW9uIHRlc3QgY2FzZXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdpdGhTYW1JbnRlZ3JhdGlvbkZpeHR1cmUoYmxvY2s6IChjb250ZXh0OiBTYW1JbnRlZ3JhdGlvblRlc3RGaXh0dXJlKSA9PiBQcm9taXNlPHZvaWQ+KSB7XG4gIHJldHVybiB3aXRoQXdzKHdpdGhUaW1lb3V0KERFRkFVTFRfVEVTVF9USU1FT1VUX1MsIHdpdGhTYW1JbnRlZ3JhdGlvbkNka0FwcChibG9jaykpKTtcbn1cblxuZXhwb3J0IGNsYXNzIFNhbUludGVncmF0aW9uVGVzdEZpeHR1cmUgZXh0ZW5kcyBUZXN0Rml4dHVyZSB7XG4gIHB1YmxpYyBhc3luYyBzYW1TaGVsbChjb21tYW5kOiBzdHJpbmdbXSwgZmlsdGVyPzogc3RyaW5nLCBhY3Rpb24/OiAoKSA9PiBhbnksIG9wdGlvbnM6IE9taXQ8U2hlbGxPcHRpb25zLCAnY3dkJyB8ICdvdXRwdXQnPiA9IHt9KTogUHJvbWlzZTxBY3Rpb25PdXRwdXQ+IHtcbiAgICByZXR1cm4gc2hlbGxXaXRoQWN0aW9uKGNvbW1hbmQsIGZpbHRlciwgYWN0aW9uLCB7XG4gICAgICBvdXRwdXRzOiBbdGhpcy5vdXRwdXRdLFxuICAgICAgY3dkOiBwYXRoLmpvaW4odGhpcy5pbnRlZ1Rlc3REaXIsICdjZGsub3V0JykudG9TdHJpbmcoKSxcbiAgICAgIC4uLm9wdGlvbnMsXG4gICAgfSk7XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgc2FtQnVpbGQoc3RhY2tOYW1lOiBzdHJpbmcpIHtcbiAgICBjb25zdCBmdWxsU3RhY2tOYW1lID0gdGhpcy5mdWxsU3RhY2tOYW1lKHN0YWNrTmFtZSk7XG4gICAgY29uc3QgdGVtcGxhdGVQYXRoID0gcGF0aC5qb2luKHRoaXMuaW50ZWdUZXN0RGlyLCAnY2RrLm91dCcsIGAke2Z1bGxTdGFja05hbWV9LnRlbXBsYXRlLmpzb25gKTtcbiAgICBjb25zdCBhcmdzID0gWyctLXRlbXBsYXRlJywgdGVtcGxhdGVQYXRoLnRvU3RyaW5nKCldO1xuICAgIHJldHVybiB0aGlzLnNhbVNoZWxsKFsnc2FtJywgJ2J1aWxkJywgLi4uYXJnc10pO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIHNhbUxvY2FsU3RhcnRBcGkoc3RhY2tOYW1lOiBzdHJpbmcsIGlzQnVpbHQ6IGJvb2xlYW4sIHBvcnQ6IG51bWJlciwgYXBpUGF0aDogc3RyaW5nKTogUHJvbWlzZTxBY3Rpb25PdXRwdXQ+IHtcbiAgICBjb25zdCBmdWxsU3RhY2tOYW1lID0gdGhpcy5mdWxsU3RhY2tOYW1lKHN0YWNrTmFtZSk7XG4gICAgY29uc3QgdGVtcGxhdGVQYXRoID0gcGF0aC5qb2luKHRoaXMuaW50ZWdUZXN0RGlyLCAnY2RrLm91dCcsIGAke2Z1bGxTdGFja05hbWV9LnRlbXBsYXRlLmpzb25gKTtcbiAgICBjb25zdCBhcmdzID0gaXNCdWlsdD8gW10gOiBbJy0tdGVtcGxhdGUnLCB0ZW1wbGF0ZVBhdGgudG9TdHJpbmcoKV07XG4gICAgYXJncy5wdXNoKCctLXBvcnQnKTtcbiAgICBhcmdzLnB1c2gocG9ydC50b1N0cmluZygpKTtcblxuICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9hd3MvYXdzLXNhbS1jbGkvcHVsbC83ODkyXG4gICAgYXJncy5wdXNoKCctLW5vLW1lbW9yeS1saW1pdCcpO1xuXG4gICAgLy8gXCJQcmVzcyBDdHJsK0MgdG8gcXVpdFwiIGxvb2tzIHRvIGJlIHByaW50ZWQgYnkgYSBGbGFzayBzZXJ2ZXIgYnVpbHQgaW50byBTQU0gQ0xJLlxuICAgIHJldHVybiB0aGlzLnNhbVNoZWxsKFsnc2FtJywgJ2xvY2FsJywgJ3N0YXJ0LWFwaScsIC4uLmFyZ3NdLCAnUHJlc3MgQ1RSTCtDIHRvIHF1aXQnLCAoKT0+e1xuICAgICAgcmV0dXJuIG5ldyBQcm9taXNlPEFjdGlvbk91dHB1dD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICBheGlvcy5nZXQoYGh0dHA6Ly8xMjcuMC4wLjE6JHtwb3J0fSR7YXBpUGF0aH1gKS50aGVuKCByZXNwID0+IHtcbiAgICAgICAgICByZXNvbHZlKHJlc3AuZGF0YSk7XG4gICAgICAgIH0pLmNhdGNoKCBlcnJvciA9PiB7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIGludm9rZSBhcGkgcGF0aCAke2FwaVBhdGh9IG9uIHBvcnQgJHtwb3J0fSB3aXRoIGVycm9yICR7ZXJyb3J9YCkpO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFudXAgbGVmdG92ZXIgc3RhY2tzIGFuZCBidWNrZXRzXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgZGlzcG9zZShzdWNjZXNzOiBib29sZWFuKSB7XG4gICAgLy8gSWYgdGhlIHRlc3RzIGNvbXBsZXRlZCBzdWNjZXNzZnVsbHksIGhhcHBpbHkgZGVsZXRlIHRoZSBmaXh0dXJlXG4gICAgLy8gKG90aGVyd2lzZSBsZWF2ZSBpdCBmb3IgaHVtYW5zIHRvIGluc3BlY3QpXG4gICAgaWYgKHN1Y2Nlc3MpIHtcbiAgICAgIGNvbnN0IGNsZWFuZWQgPSByaW1yYWYodGhpcy5pbnRlZ1Rlc3REaXIpO1xuICAgICAgaWYgKCFjbGVhbmVkKSB7XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlXG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYEZhaWxlZCB0byBjbGVhbiB1cCAke3RoaXMuaW50ZWdUZXN0RGlyfSBkdWUgdG8gcGVybWlzc2lvbnMgaXNzdWVzIChEb2NrZXIgcnVubmluZyBhcyByb290PylgKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJhbmRvbUludGVnZXIobWluOiBudW1iZXIsIG1heDogbnVtYmVyKSB7XG4gIHJldHVybiBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiAobWF4IC0gbWluKSArIG1pbik7XG59XG5cbi8qKlxuICogQSBzaGVsbCBjb21tYW5kIHRoYXQgZG9lcyB3aGF0IHlvdSB3YW50XG4gKlxuICogSXMgcGxhdGZvcm0tYXdhcmUsIGhhbmRsZXMgZXJyb3JzIG5pY2VseS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNoZWxsV2l0aEFjdGlvbihcbiAgY29tbWFuZDogc3RyaW5nW10sXG4gIGZpbHRlcj86IHN0cmluZyxcbiAgYWN0aW9uPzogKCkgPT4gUHJvbWlzZTxhbnk+LFxuICBvcHRpb25zOiBTaGVsbE9wdGlvbnMgPSB7fSxcbiAgYWN0aW9uVGltZW91dFNlY29uZHM6IG51bWJlciA9IDYwMCxcbik6IFByb21pc2U8QWN0aW9uT3V0cHV0PiB7XG4gIGlmIChvcHRpb25zLm1vZEVudiAmJiBvcHRpb25zLmVudikge1xuICAgIHRocm93IG5ldyBFcnJvcignVXNlIGVpdGhlciBlbnYgb3IgbW9kRW52IGJ1dCBub3QgYm90aCcpO1xuICB9XG5cbiAgY29uc3Qgd3JpdGVUb091dHB1dHMgPSAoeDogc3RyaW5nKSA9PiB7XG4gICAgZm9yIChjb25zdCBvdXRwdXQgb2Ygb3B0aW9ucy5vdXRwdXRzID8/IFtdKSB7XG4gICAgICBvdXRwdXQud3JpdGUoeCk7XG4gICAgfVxuICB9O1xuICB3cml0ZVRvT3V0cHV0cyhg8J+SuyAke2NvbW1hbmQuam9pbignICcpfVxcbmApO1xuXG4gIGNvbnN0IGVudiA9IG9wdGlvbnMuZW52ID8/IChvcHRpb25zLm1vZEVudiA/IHsgLi4ucHJvY2Vzcy5lbnYsIC4uLm9wdGlvbnMubW9kRW52IH0gOiB1bmRlZmluZWQpO1xuXG4gIGNvbnN0IGNoaWxkID0gY2hpbGRfcHJvY2Vzcy5zcGF3bihjb21tYW5kWzBdLCBjb21tYW5kLnNsaWNlKDEpLCB7XG4gICAgLi4ub3B0aW9ucyxcbiAgICBlbnYsXG4gICAgLy8gTmVlZCB0aGlzIGZvciBXaW5kb3dzIHdoZXJlIHdlIHdhbnQgLmNtZCBhbmQgLmJhdCB0byBiZSBmb3VuZCBhcyB3ZWxsLlxuICAgIHNoZWxsOiB0cnVlLFxuICAgIHN0ZGlvOiBbJ2lnbm9yZScsICdwaXBlJywgJ3BpcGUnXSxcbiAgfSk7XG5cbiAgcmV0dXJuIG5ldyBQcm9taXNlPEFjdGlvbk91dHB1dD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGNvbnN0IG91dCA9IG5ldyBBcnJheTxCdWZmZXI+KCk7XG4gICAgY29uc3Qgc3Rkb3V0ID0gbmV3IEFycmF5PEJ1ZmZlcj4oKTtcbiAgICBjb25zdCBzdGRlcnIgPSBuZXcgQXJyYXk8QnVmZmVyPigpO1xuICAgIGxldCBhY3Rpb25TdWNjZWVkZWQgPSBmYWxzZTtcbiAgICBsZXQgYWN0aW9uT3V0cHV0OiBhbnk7XG4gICAgbGV0IGFjdGlvbkV4ZWN1dGVkID0gZmFsc2U7XG5cbiAgICBhc3luYyBmdW5jdGlvbiBtYXliZUV4ZWN1dGVBY3Rpb24oY2h1bms6IGFueSkge1xuICAgICAgb3V0LnB1c2goQnVmZmVyLmZyb20oY2h1bmspKTtcbiAgICAgIGlmICghYWN0aW9uRXhlY3V0ZWQgJiYgdHlwZW9mIGZpbHRlciA9PT0gJ3N0cmluZycgJiYgQnVmZmVyLmNvbmNhdChvdXQpLnRvU3RyaW5nKCd1dGYtOCcpLmluY2x1ZGVzKGZpbHRlcikgJiYgdHlwZW9mIGFjdGlvbiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICBhY3Rpb25FeGVjdXRlZCA9IHRydWU7XG4gICAgICAgIHdyaXRlVG9PdXRwdXRzKCdiZWZvcmUgZXhlY3V0aW5nIGFjdGlvblxcbicpO1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IG91dHB1dCA9IGF3YWl0IGFjdGlvbigpO1xuICAgICAgICAgIHdyaXRlVG9PdXRwdXRzKGBhY3Rpb24gb3V0cHV0IGlzICR7SlNPTi5zdHJpbmdpZnkob3V0cHV0KX1cXG5gKTtcbiAgICAgICAgICBhY3Rpb25PdXRwdXQgPSBvdXRwdXQ7XG4gICAgICAgICAgYWN0aW9uU3VjY2VlZGVkID0gdHJ1ZTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgICAgICAgIHdyaXRlVG9PdXRwdXRzKGBhY3Rpb24gZXJyb3IgaXMgJHtlcnJvcn1cXG5gKTtcbiAgICAgICAgICBhY3Rpb25TdWNjZWVkZWQgPSBmYWxzZTtcbiAgICAgICAgICBhY3Rpb25PdXRwdXQgPSBlcnJvcjtcbiAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICB3cml0ZVRvT3V0cHV0cygndGVybWluYXRlIHNhbSBzdWIgcHJvY2Vzc1xcbicpO1xuICAgICAgICAgIGtpbGxTdWJQcm9jZXNzKGNoaWxkLCBjb21tYW5kLmpvaW4oJyAnKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGZpbHRlciA9PT0gJ3N0cmluZycgJiYgdHlwZW9mIGFjdGlvbiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgLy8gUmVqZWN0IHdpdGggYW4gZXJyb3IgaWYgYW4gYWN0aW9uIGlzIGNvbmZpZ3VyZWQsIGJ1dCB0aGUgZmlsdGVyIGZhaWxlZFxuICAgICAgLy8gdG8gc2hvdyB1cCBpbiB0aGUgb3V0cHV0IGJlZm9yZSB0aGUgdGltZW91dCBvY2N1cnJlZC5cbiAgICAgIHNldFRpbWVvdXQoXG4gICAgICAgICgpID0+IHtcbiAgICAgICAgICBpZiAoIWFjdGlvbkV4ZWN1dGVkKSB7XG4gICAgICAgICAgICByZWplY3QobmV3IEVycm9yKGBUaW1lZCBvdXQgd2FpdGluZyBmb3IgZmlsdGVyICR7SlNPTi5zdHJpbmdpZnkoZmlsdGVyKX0gdG8gYXBwZWFyIGluIGNvbW1hbmQgb3V0cHV0IGFmdGVyICR7YWN0aW9uVGltZW91dFNlY29uZHN9IHNlY29uZHNcXG5PdXRwdXQgc28gZmFyOlxcbiR7QnVmZmVyLmNvbmNhdChvdXQpLnRvU3RyaW5nKCd1dGYtOCcpfWApKTtcbiAgICAgICAgICAgIGtpbGxTdWJQcm9jZXNzKGNoaWxkLCBjb21tYW5kLmpvaW4oJyAnKSk7XG4gICAgICAgICAgfVxuICAgICAgICB9LCBhY3Rpb25UaW1lb3V0U2Vjb25kcyAqIDFfMDAwLFxuICAgICAgKS51bnJlZigpO1xuICAgIH1cblxuICAgIGNoaWxkLnN0ZG91dCEub24oJ2RhdGEnLCBjaHVuayA9PiB7XG4gICAgICB3cml0ZVRvT3V0cHV0cyhjaHVuayk7XG4gICAgICBzdGRvdXQucHVzaChjaHVuayk7XG4gICAgICB2b2lkIG1heWJlRXhlY3V0ZUFjdGlvbihjaHVuayk7XG4gICAgfSk7XG5cbiAgICBjaGlsZC5zdGRlcnIhLm9uKCdkYXRhJywgY2h1bmsgPT4ge1xuICAgICAgd3JpdGVUb091dHB1dHMoY2h1bmspO1xuICAgICAgaWYgKG9wdGlvbnMuY2FwdHVyZVN0ZGVyciA/PyB0cnVlKSB7XG4gICAgICAgIHN0ZGVyci5wdXNoKGNodW5rKTtcbiAgICAgIH1cbiAgICAgIHZvaWQgbWF5YmVFeGVjdXRlQWN0aW9uKGNodW5rKTtcbiAgICB9KTtcblxuICAgIGNoaWxkLm9uY2UoJ2Vycm9yJywgcmVqZWN0KTtcblxuICAgIC8vIFdhaXQgZm9yICdleGl0JyBpbnN0ZWFkIG9mIGNsb3NlLCBkb24ndCBjYXJlIGFib3V0IHJlYWRpbmcgdGhlIHN0cmVhbXMgYWxsIHRoZSB3YXkgdG8gdGhlIGVuZFxuICAgIGNoaWxkLm9uY2UoJ2V4aXQnLCAoY29kZSwgc2lnbmFsKSA9PiB7XG4gICAgICB3cml0ZVRvT3V0cHV0cyhgU3VicHJvY2VzcyBoYXMgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LCBzaWduYWwgJHtzaWduYWx9XFxuYCk7XG4gICAgICBjb25zdCBvdXRwdXQgPSAoQnVmZmVyLmNvbmNhdChzdGRvdXQpLnRvU3RyaW5nKCd1dGYtOCcpICsgQnVmZmVyLmNvbmNhdChzdGRlcnIpLnRvU3RyaW5nKCd1dGYtOCcpKS50cmltKCk7XG4gICAgICBpZiAoY29kZSA9PSBudWxsIHx8IGNvZGUgPT09IDAgfHwgb3B0aW9ucy5hbGxvd0VyckV4aXQpIHtcbiAgICAgICAgbGV0IHJlc3VsdCA9IG5ldyBBcnJheTxzdHJpbmc+KCk7XG4gICAgICAgIHJlc3VsdC5wdXNoKGFjdGlvbk91dHB1dCk7XG4gICAgICAgIHJlc3VsdC5wdXNoKG91dHB1dCk7XG4gICAgICAgIHJlc29sdmUoe1xuICAgICAgICAgIGFjdGlvblN1Y2NlZWRlZDogYWN0aW9uU3VjY2VlZGVkLFxuICAgICAgICAgIGFjdGlvbk91dHB1dDogYWN0aW9uT3V0cHV0LFxuICAgICAgICAgIHNoZWxsT3V0cHV0OiBvdXRwdXQsXG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgJyR7Y29tbWFuZC5qb2luKCcgJyl9JyBleGl0ZWQgd2l0aCBlcnJvciBjb2RlICR7Y29kZX0uIE91dHB1dDogXFxuJHtvdXRwdXR9YCkpO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcbn1cblxuZnVuY3Rpb24ga2lsbFN1YlByb2Nlc3MoY2hpbGQ6IGNoaWxkX3Byb2Nlc3MuQ2hpbGRQcm9jZXNzLCBjb21tYW5kOiBzdHJpbmcpIHtcbiAgLyoqXG4gICAqIENoZWNrIGlmIHRoZSBzdWIgcHJvY2VzcyBpcyBydW5uaW5nIGluIGNvbnRhaW5lciwgc28gY2hpbGRfcHJvY2Vzcy5zcGF3biB3aWxsXG4gICAqIGNyZWF0ZSBtdWx0aXBsZSBwcm9jZXNzZXMsIGFuZCB0byBraWxsIGFsbCBvZiB0aGVtIHdlIG5lZWQgdG8gcnVuIGRpZmZlcmVudCBsb2dpY1xuICAgKi9cbiAgY2hpbGQua2lsbCgnU0lHSU5UJyk7XG4gIGNoaWxkX3Byb2Nlc3MuZXhlYyhgZm9yIHBpZCBpbiAkKHBzIC1lZiB8IGdyZXAgXCIke2NvbW1hbmR9XCIgfCBhd2sgJ3twcmludCAkMn0nKTsgZG8ga2lsbCAtMiAkcGlkOyBkb25lYCk7XG59XG4iXX0=