UNPKG

@aws-cdk-testing/cli-integ

Version:

Integration tests for the AWS CDK CLI

146 lines 19.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = require("fs"); const os = require("os"); const path = require("path"); const integ_test_1 = require("../../../lib/integ-test"); const proxy_1 = require("../../../lib/proxy"); const with_cdk_app_1 = require("../../../lib/with-cdk-app"); const docker = process.env.CDK_DOCKER ?? 'docker'; (0, integ_test_1.integTest)('all calls from isolated container go through proxy', (0, with_cdk_app_1.withDefaultFixture)(async (fixture) => { // Find the 'cdk' command and make sure it is mounted into the container const cdkFullpath = (await fixture.shell(['which', 'cdk'])).trim(); let cdkTop = findMountableParent(cdkFullpath); // Run a 'cdk deploy' inside the container const commands = [ `env ${renderEnv(fixture.cdkShellEnv())} ${cdkFullpath} ${fixture.cdkDeployCommandLine('test-2').join(' ')} -v`, ]; await runInIsolatedContainer(fixture, [cdkTop], commands); })); async function runInIsolatedContainer(fixture, pathsToMount, testCommands) { pathsToMount.push(`${process.env.HOME}`, fixture.integTestDir); // Resolve credential provider to an access key that we can pass into the container const credentials = await fixture.aws.credentials(); const proxy = await (0, proxy_1.startProxyServer)(fixture.integTestDir); try { const proxyPort = proxy.port; const setupCommands = [ 'apt-get update -qq', 'apt-get install -qqy nodejs > /dev/null', ...isolatedDockerCommands(proxyPort, proxy.certPath), ]; const scriptName = path.join(fixture.integTestDir, 'script.sh'); // Write a script file await fs_1.promises.writeFile(scriptName, [ '#!/bin/bash', 'set -x', 'set -eu', ...setupCommands, ...testCommands, ].join('\n'), 'utf-8'); await fs_1.promises.chmod(scriptName, 0o755); await fixture.ecrPublicLogin(); // Run commands in a Docker shell await fixture.shell([ docker, 'run', '--net=bridge', '--rm', ...pathsToMount.flatMap(p => ['-v', `${p}:${p}`]), ...['HOME', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN'].flatMap(e => ['-e', e]), '-w', fixture.integTestDir, '--cap-add=NET_ADMIN', 'public.ecr.aws/ubuntu/ubuntu:24.04_stable', `${scriptName}`, ], { modEnv: { AWS_ACCESS_KEY_ID: credentials.accessKeyId, AWS_SECRET_ACCESS_KEY: credentials.secretAccessKey, AWS_SESSION_TOKEN: credentials.sessionToken, }, }); } finally { await proxy.stop(); } } /** * Return the commands necessary to isolate the inside of the container from the internet, * except by going through the proxy */ function isolatedDockerCommands(proxyPort, caBundlePath) { let defaultBridgeIp = '172.17.0.1'; // The host's default IP is different on CodeBuild than it is on other Docker systems. if (process.env.CODEBUILD_BUILD_ARN) { defaultBridgeIp = '172.18.0.1'; } return [ 'echo Working...', 'apt-get install -qqy curl net-tools iputils-ping dnsutils iptables > /dev/null', '', // Looking up `host.docker.internal` is necessary on MacOS Finch and Docker Desktop // on Mac. The magic IP address is necessary on CodeBuild and GitHub Actions. // // I tried `--add-host=host.docker.internal:host-gateway` on the Linuxes but // it doesn't change anything on either GHA or CodeBuild, so we're left with this // backup solution. 'gateway=$(dig +short host.docker.internal)', 'if [[ -z "$gateway" ]]; then', ` gateway=${defaultBridgeIp}`, 'fi', '', '# Some iptables manipulation; there might be unnecessary commands in here, not an expert', 'iptables -F', 'iptables -X', 'iptables -P INPUT DROP', 'iptables -P OUTPUT DROP', 'iptables -P FORWARD DROP', 'iptables -A INPUT -i lo -j ACCEPT', 'iptables -A OUTPUT -o lo -j ACCEPT', 'iptables -A OUTPUT -d $gateway -j ACCEPT', 'iptables -A INPUT -s $gateway -j ACCEPT', '', '', `if [[ ! -f ${caBundlePath} ]]; then`, ` echo "Could not find ${caBundlePath}, this will probably not go well. Exiting." >&2`, ' exit 1', 'fi', '', '# Configure a bunch of tools to work with the proxy', 'echo "+-------------------------------------------------------------------------------------+"', 'echo "| Direct network traffic has been blocked, everything must go through the proxy. |"', 'echo "+-------------------------------------------------------------------------------------+"', `export HTTP_PROXY=http://$gateway:${proxyPort}/`, `export HTTPS_PROXY=http://$gateway:${proxyPort}/`, `export NODE_EXTRA_CA_CERTS=${caBundlePath}`, `export AWS_CA_BUNDLE=${caBundlePath}`, `export SSL_CERT_FILE=${caBundlePath}`, 'echo "Acquire::http::proxy \"$HTTP_PROXY\";" >> /etc/apt/apt.conf.d/95proxies', 'echo "Acquire::https::proxy \"$HTTPS_PROXY\";" >> /etc/apt/apt.conf.d/95proxies', ]; } function renderEnv(env) { return Object.entries(env).filter(([_, v]) => v).map(([k, v]) => `${k}='${v}'`).join(' '); } /** * Find a broadly mountable parent of the given directory * * We don't want to just mount the top-level directory, because * it could end up being `/var` (top-level dir of tmpdir on Mac). */ function findMountableParent(dir) { const candidates = [ os.tmpdir(), process.env.TMPDIR, process.env.HOME, ]; for (const cand of candidates) { if (cand === undefined) { continue; } if (dir.startsWith(cand)) { const suffix = dir.substring(cand.length); const firstChildDir = suffix.split('/')[0]; return path.join(cand, firstChildDir); } } return dir; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJveHkuaW50ZWd0ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicHJveHkuaW50ZWd0ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsMkJBQW9DO0FBQ3BDLHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFDN0Isd0RBQW9EO0FBQ3BELDhDQUFzRDtBQUV0RCw0REFBK0Q7QUFFL0QsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLElBQUksUUFBUSxDQUFDO0FBRWxELElBQUEsc0JBQVMsRUFDUCxvREFBb0QsRUFDcEQsSUFBQSxpQ0FBa0IsRUFBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7SUFDbkMsd0VBQXdFO0lBQ3hFLE1BQU0sV0FBVyxHQUFHLENBQUMsTUFBTSxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNuRSxJQUFJLE1BQU0sR0FBRyxtQkFBbUIsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUU5QywwQ0FBMEM7SUFDMUMsTUFBTSxRQUFRLEdBQUc7UUFDZixPQUFPLFNBQVMsQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUMsSUFBSSxXQUFXLElBQUksT0FBTyxDQUFDLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSztLQUNoSCxDQUFDO0lBRUYsTUFBTSxzQkFBc0IsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUMsRUFBRSxRQUFRLENBQUMsQ0FBQztBQUM1RCxDQUFDLENBQUMsQ0FDSCxDQUFDO0FBRUYsS0FBSyxVQUFVLHNCQUFzQixDQUFDLE9BQW9CLEVBQUUsWUFBc0IsRUFBRSxZQUFzQjtJQUN4RyxZQUFZLENBQUMsSUFBSSxDQUNmLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsRUFDckIsT0FBTyxDQUFDLFlBQVksQ0FDckIs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QUFDO1lBQzFDLE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFFM0MsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxhQUFhLENBQUMsQ0FBQztRQUN4QyxDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sR0FBRyxDQUFDO0FBQ2IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHByb21pc2VzIGFzIGZzIH0gZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IGludGVnVGVzdCB9IGZyb20gJy4uLy4uLy4uL2xpYi9pbnRlZy10ZXN0JztcbmltcG9ydCB7IHN0YXJ0UHJveHlTZXJ2ZXIgfSBmcm9tICcuLi8uLi8uLi9saWIvcHJveHknO1xuaW1wb3J0IHR5cGUgeyBUZXN0Rml4dHVyZSB9IGZyb20gJy4uLy4uLy4uL2xpYi93aXRoLWNkay1hcHAnO1xuaW1wb3J0IHsgd2l0aERlZmF1bHRGaXh0dXJlIH0gZnJvbSAnLi4vLi4vLi4vbGliL3dpdGgtY2RrLWFwcCc7XG5cbmNvbnN0IGRvY2tlciA9IHByb2Nlc3MuZW52LkNES19ET0NLRVIgPz8gJ2RvY2tlcic7XG5cbmludGVnVGVzdChcbiAgJ2FsbCBjYWxscyBmcm9tIGlzb2xhdGVkIGNvbnRhaW5lciBnbyB0aHJvdWdoIHByb3h5JyxcbiAgd2l0aERlZmF1bHRGaXh0dXJlKGFzeW5jIChmaXh0dXJlKSA9PiB7XG4gICAgLy8gRmluZCB0aGUgJ2NkaycgY29tbWFuZCBhbmQgbWFrZSBzdXJlIGl0IGlzIG1vdW50ZWQgaW50byB0aGUgY29udGFpbmVyXG4gICAgY29uc3QgY2RrRnVsbHBhdGggPSAoYXdhaXQgZml4dHVyZS5zaGVsbChbJ3doaWNoJywgJ2NkayddKSkudHJpbSgpO1xuICAgIGxldCBjZGtUb3AgPSBmaW5kTW91bnRhYmxlUGFyZW50KGNka0Z1bGxwYXRoKTtcblxuICAgIC8vIFJ1biBhICdjZGsgZGVwbG95JyBpbnNpZGUgdGhlIGNvbnRhaW5lclxuICAgIGNvbnN0IGNvbW1hbmRzID0gW1xuICAgICAgYGVudiAke3JlbmRlckVudihmaXh0dXJlLmNka1NoZWxsRW52KCkpfSAke2Nka0Z1bGxwYXRofSAke2ZpeHR1cmUuY2RrRGVwbG95Q29tbWFuZExpbmUoJ3Rlc3QtMicpLmpvaW4oJyAnKX0gLXZgLFxuICAgIF07XG5cbiAgICBhd2FpdCBydW5Jbklzb2xhdGVkQ29udGFpbmVyKGZpeHR1cmUsIFtjZGtUb3BdLCBjb21tYW5kcyk7XG4gIH0pLFxuKTtcblxuYXN5bmMgZnVuY3Rpb24gcnVuSW5Jc29sYXRlZENvbnRhaW5lcihmaXh0dXJlOiBUZXN0Rml4dHVyZSwgcGF0aHNUb01vdW50OiBzdHJpbmdbXSwgdGVzdENvbW1hbmRzOiBzdHJpbmdbXSkge1xuICBwYXRoc1RvTW91bnQucHVzaChcbiAgICBgJHtwcm9jZXNzLmVudi5IT01FfWAsXG4gICAgZml4dHVyZS5pbnRlZ1Rlc3REaXIsXG4gICk7XG5cbiAgLy8gUmVzb2x2ZSBjcmVkZW50aWFsIHByb3ZpZGVyIHRvIGFuIGFjY2VzcyBrZXkgdGhhdCB3ZSBjYW4gcGFzcyBpbnRvIHRoZSBjb250YWluZXJcbiAgY29uc3QgY3JlZGVudGlhbHMgPSBhd2FpdCBmaXh0dXJlLmF3cy5jcmVkZW50aWFscygpO1xuXG4gIGNvbnN0IHByb3h5ID0gYXdhaXQgc3RhcnRQcm94eVNlcnZlcihmaXh0dXJlLmludGVnVGVzdERpcik7XG4gIHRyeSB7XG4gICAgY29uc3QgcHJveHlQb3J0ID0gcHJveHkucG9ydDtcblxuICAgIGNvbnN0IHNldHVwQ29tbWFuZHMgPSBbXG4gICAgICAnYXB0LWdldCB1cGRhdGUgLXFxJyxcbiAgICAgICdhcHQtZ2V0IGluc3RhbGwgLXFxeSBub2RlanMgPiAvZGV2L251bGwnLFxuICAgICAgLi4uaXNvbGF0ZWREb2NrZXJDb21tYW5kcyhwcm94eVBvcnQsIHByb3h5LmNlcnRQYXRoKSxcbiAgICBdO1xuXG4gICAgY29uc3Qgc2NyaXB0TmFtZSA9IHBhdGguam9pbihmaXh0dXJlLmludGVnVGVzdERpciwgJ3NjcmlwdC5zaCcpO1xuXG4gICAgLy8gV3JpdGUgYSBzY3JpcHQgZmlsZVxuICAgIGF3YWl0IGZzLndyaXRlRmlsZShzY3JpcHROYW1lLCBbXG4gICAgICAnIyEvYmluL2Jhc2gnLFxuICAgICAgJ3NldCAteCcsXG4gICAgICAnc2V0IC1ldScsXG4gICAgICAuLi5zZXR1cENvbW1hbmRzLFxuICAgICAgLi4udGVzdENvbW1hbmRzLFxuICAgIF0uam9pbignXFxuJyksICd1dGYtOCcpO1xuXG4gICAgYXdhaXQgZnMuY2htb2Qoc2NyaXB0TmFtZSwgMG83NTUpO1xuXG4gICAgYXdhaXQgZml4dHVyZS5lY3JQdWJsaWNMb2dpbigpO1xuXG4gICAgLy8gUnVuIGNvbW1hbmRzIGluIGEgRG9ja2VyIHNoZWxsXG4gICAgYXdhaXQgZml4dHVyZS5zaGVsbChbXG4gICAgICBkb2NrZXIsICdydW4nLCAnLS1uZXQ9YnJpZGdlJywgJy0tcm0nLFxuICAgICAgLi4ucGF0aHNUb01vdW50LmZsYXRNYXAocCA9PiBbJy12JywgYCR7cH06JHtwfWBdKSxcbiAgICAgIC4uLlsnSE9NRScsICdBV1NfQUNDRVNTX0tFWV9JRCcsICdBV1NfU0VDUkVUX0FDQ0VTU19LRVknLCAnQVdTX1NFU1NJT05fVE9LRU4nXS5mbGF0TWFwKGUgPT4gWyctZScsIGVdKSxcbiAgICAgICctdycsIGZpeHR1cmUuaW50ZWdUZXN0RGlyLFxuICAgICAgJy0tY2FwLWFkZD1ORVRfQURNSU4nLFxuICAgICAgJ3B1YmxpYy5lY3IuYXdzL3VidW50dS91YnVudHU6MjQuMDRfc3RhYmxlJyxcbiAgICAgIGAke3NjcmlwdE5hbWV9YCxcbiAgICBdLCB7XG4gICAgICBtb2RFbnY6IHtcbiAgICAgICAgQVdTX0FDQ0VTU19LRVlfSUQ6IGNyZWRlbnRpYWxzLmFjY2Vzc0tleUlkLFxuICAgICAgICBBV1NfU0VDUkVUX0FDQ0VTU19LRVk6IGNyZWRlbnRpYWxzLnNlY3JldEFjY2Vzc0tleSxcbiAgICAgICAgQVdTX1NFU1NJT05fVE9LRU46IGNyZWRlbnRpYWxzLnNlc3Npb25Ub2tlbixcbiAgICAgIH0sXG4gICAgfSk7XG4gIH0gZmluYWxseSB7XG4gICAgYXdhaXQgcHJveHkuc3RvcCgpO1xuICB9XG59XG5cbi8qKlxuICogUmV0dXJuIHRoZSBjb21tYW5kcyBuZWNlc3NhcnkgdG8gaXNvbGF0ZSB0aGUgaW5zaWRlIG9mIHRoZSBjb250YWluZXIgZnJvbSB0aGUgaW50ZXJuZXQsXG4gKiBleGNlcHQgYnkgZ29pbmcgdGhyb3VnaCB0aGUgcHJveHlcbiAqL1xuZnVuY3Rpb24gaXNvbGF0ZWREb2NrZXJDb21tYW5kcyhwcm94eVBvcnQ6IG51bWJlciwgY2FCdW5kbGVQYXRoOiBzdHJpbmcpIHtcbiAgbGV0IGRlZmF1bHRCcmlkZ2VJcCA9ICcxNzIuMTcuMC4xJztcblxuICAvLyBUaGUgaG9zdCdzIGRlZmF1bHQgSVAgaXMgZGlmZmVyZW50IG9uIENvZGVCdWlsZCB0aGFuIGl0IGlzIG9uIG90aGVyIERvY2tlciBzeXN0ZW1zLlxuICBpZiAocHJvY2Vzcy5lbnYuQ09ERUJVSUxEX0JVSUxEX0FSTikge1xuICAgIGRlZmF1bHRCcmlkZ2VJcCA9ICcxNzIuMTguMC4xJztcbiAgfVxuXG4gIHJldHVybiBbXG4gICAgJ2VjaG8gV29ya2luZy4uLicsXG4gICAgJ2FwdC1nZXQgaW5zdGFsbCAtcXF5IGN1cmwgbmV0LXRvb2xzIGlwdXRpbHMtcGluZyBkbnN1dGlscyBpcHRhYmxlcyA+IC9kZXYvbnVsbCcsXG4gICAgJycsXG4gICAgLy8gTG9va2luZyB1cCBgaG9zdC5kb2NrZXIuaW50ZXJuYWxgIGlzIG5lY2Vzc2FyeSBvbiBNYWNPUyBGaW5jaCBhbmQgRG9ja2VyIERlc2t0b3BcbiAgICAvLyBvbiBNYWMuIFRoZSBtYWdpYyBJUCBhZGRyZXNzIGlzIG5lY2Vzc2FyeSBvbiBDb2RlQnVpbGQgYW5kIEdpdEh1YiBBY3Rpb25zLlxuICAgIC8vXG4gICAgLy8gSSB0cmllZCBgLS1hZGQtaG9zdD1ob3N0LmRvY2tlci5pbnRlcm5hbDpob3N0LWdhdGV3YXlgIG9uIHRoZSBMaW51eGVzIGJ1dFxuICAgIC8vIGl0IGRvZXNuJ3QgY2hhbmdlIGFueXRoaW5nIG9uIGVpdGhlciBHSEEgb3IgQ29kZUJ1aWxkLCBzbyB3ZSdyZSBsZWZ0IHdpdGggdGhpc1xuICAgIC8vIGJhY2t1cCBzb2x1dGlvbi5cbiAgICAnZ2F0ZXdheT0kKGRpZyArc2hvcnQgaG9zdC5kb2NrZXIuaW50ZXJuYWwpJyxcbiAgICAnaWYgW1sgLXogXCIkZ2F0ZXdheVwiIF1dOyB0aGVuJyxcbiAgICBgICBnYXRld2F5PSR7ZGVmYXVsdEJyaWRnZUlwfWAsXG4gICAgJ2ZpJyxcbiAgICAnJyxcbiAgICAnIyBTb21lIGlwdGFibGVzIG1hbmlwdWxhdGlvbjsgdGhlcmUgbWlnaHQgYmUgdW5uZWNlc3NhcnkgY29tbWFuZHMgaW4gaGVyZSwgbm90IGFuIGV4cGVydCcsXG4gICAgJ2lwdGFibGVzIC1GJyxcbiAgICAnaXB0YWJsZXMgLVgnLFxuICAgICdpcHRhYmxlcyAtUCBJTlBVVCBEUk9QJyxcbiAgICAnaXB0YWJsZXMgLVAgT1VUUFVUIERST1AnLFxuICAgICdpcHRhYmxlcyAtUCBGT1JXQVJEIERST1AnLFxuICAgICdpcHRhYmxlcyAtQSBJTlBVVCAtaSBsbyAtaiBBQ0NFUFQnLFxuICAgICdpcHRhYmxlcyAtQSBPVVRQVVQgLW8gbG8gLWogQUNDRVBUJyxcbiAgICAnaXB0YWJsZXMgLUEgT1VUUFVUIC1kICRnYXRld2F5IC1qIEFDQ0VQVCcsXG4gICAgJ2lwdGFibGVzIC1BIElOUFVUIC1zICRnYXRld2F5IC1qIEFDQ0VQVCcsXG4gICAgJycsXG4gICAgJycsXG4gICAgYGlmIFtbICEgLWYgJHtjYUJ1bmRsZVBhdGh9IF1dOyB0aGVuYCxcbiAgICBgICAgIGVjaG8gXCJDb3VsZCBub3QgZmluZCAke2NhQnVuZGxlUGF0aH0sIHRoaXMgd2lsbCBwcm9iYWJseSBub3QgZ28gd2VsbC4gRXhpdGluZy5cIiA+JjJgLFxuICAgICcgICAgZXhpdCAxJyxcbiAgICAnZmknLFxuICAgICcnLFxuICAgICcjIENvbmZpZ3VyZSBhIGJ1bmNoIG9mIHRvb2xzIHRvIHdvcmsgd2l0aCB0aGUgcHJveHknLFxuICAgICdlY2hvIFwiKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rXCInLFxuICAgICdlY2hvIFwifCAgRGlyZWN0IG5ldHdvcmsgdHJhZmZpYyBoYXMgYmVlbiBibG9ja2VkLCBldmVyeXRoaW5nIG11c3QgZ28gdGhyb3VnaCB0aGUgcHJveHkuICAgICB8XCInLFxuICAgICdlY2hvIFwiKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rXCInLFxuICAgIGBleHBvcnQgSFRUUF9QUk9YWT1odHRwOi8vJGdhdGV3YXk6JHtwcm94eVBvcnR9L2AsXG4gICAgYGV4cG9ydCBIVFRQU19QUk9YWT1odHRwOi8vJGdhdGV3YXk6JHtwcm94eVBvcnR9L2AsXG4gICAgYGV4cG9ydCBOT0RFX0VYVFJBX0NBX0NFUlRTPSR7Y2FCdW5kbGVQYXRofWAsXG4gICAgYGV4cG9ydCBBV1NfQ0FfQlVORExFPSR7Y2FCdW5kbGVQYXRofWAsXG4gICAgYGV4cG9ydCBTU0xfQ0VSVF9GSUxFPSR7Y2FCdW5kbGVQYXRofWAsXG4gICAgJ2VjaG8gXCJBY3F1aXJlOjpodHRwOjpwcm94eSBcXFwiJEhUVFBfUFJPWFlcXFwiO1wiID4+IC9ldGMvYXB0L2FwdC5jb25mLmQvOTVwcm94aWVzJyxcbiAgICAnZWNobyBcIkFjcXVpcmU6Omh0dHBzOjpwcm94eSBcXFwiJEhUVFBTX1BST1hZXFxcIjtcIiA+PiAvZXRjL2FwdC9hcHQuY29uZi5kLzk1cHJveGllcycsXG4gIF07XG59XG5cbmZ1bmN0aW9uIHJlbmRlckVudihlbnY6IFJlY29yZDxzdHJpbmcsIHN0cmluZyB8IHVuZGVmaW5lZD4pIHtcbiAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKGVudikuZmlsdGVyKChbXywgdl0pID0+IHYpLm1hcCgoW2ssIHZdKSA9PiBgJHtrfT0nJHt2fSdgKS5qb2luKCcgJyk7XG59XG5cbi8qKlxuICogRmluZCBhIGJyb2FkbHkgbW91bnRhYmxlIHBhcmVudCBvZiB0aGUgZ2l2ZW4gZGlyZWN0b3J5XG4gKlxuICogV2UgZG9uJ3Qgd2FudCB0byBqdXN0IG1vdW50IHRoZSB0b3AtbGV2ZWwgZGlyZWN0b3J5LCBiZWNhdXNlXG4gKiBpdCBjb3VsZCBlbmQgdXAgYmVpbmcgYC92YXJgICh0b3AtbGV2ZWwgZGlyIG9mIHRtcGRpciBvbiBNYWMpLlxuICovXG5mdW5jdGlvbiBmaW5kTW91bnRhYmxlUGFyZW50KGRpcjogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgY2FuZGlkYXRlcyA9IFtcbiAgICBvcy50bXBkaXIoKSxcbiAgICBwcm9jZXNzLmVudi5UTVBESVIsXG4gICAgcHJvY2Vzcy5lbnYuSE9NRSxcbiAgXTtcbiAgZm9yIChjb25zdCBjYW5kIG9mIGNhbmRpZGF0ZXMpIHtcbiAgICBpZiAoY2FuZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBpZiAoZGlyLnN0YXJ0c1dpdGgoY2FuZCkpIHtcbiAgICAgIGNvbnN0IHN1ZmZpeCA9IGRpci5zdWJzdHJpbmcoY2FuZC5sZW5ndGgpO1xuICAgICAgY29uc3QgZmlyc3RDaGlsZERpciA9IHN1ZmZpeC5zcGxpdCgnLycpWzBdO1xuXG4gICAgICByZXR1cm4gcGF0aC5qb2luKGNhbmQsIGZpcnN0Q2hpbGREaXIpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZGlyO1xufVxuIl19