UNPKG

@aws-cdk-testing/cli-integ

Version:

Integration tests for the AWS CDK CLI

663 lines 97.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestFixture = exports.EXTENDED_TEST_TIMEOUT_S = exports.DEFAULT_TEST_TIMEOUT_S = void 0; exports.withSpecificCdkApp = withSpecificCdkApp; exports.withCdkApp = withCdkApp; exports.withCdkMigrateApp = withCdkMigrateApp; exports.withDefaultFixture = withDefaultFixture; exports.withSpecificFixture = withSpecificFixture; exports.withExtendedTimeoutFixture = withExtendedTimeoutFixture; exports.withCDKMigrateFixture = withCDKMigrateFixture; exports.withRetry = withRetry; exports.withoutBootstrap = withoutBootstrap; exports.cloneDirectory = cloneDirectory; exports.ensureBootstrapped = ensureBootstrapped; exports.installNpmPackages = installNpmPackages; /* eslint-disable no-console */ const assert = require("assert"); const fs = require("fs"); const os = require("os"); const path = require("path"); const client_cloudformation_1 = require("@aws-sdk/client-cloudformation"); const client_ecr_public_1 = require("@aws-sdk/client-ecr-public"); const aws_1 = require("./aws"); const subprocess_1 = require("./package-sources/subprocess"); const resources_1 = require("./resources"); const shell_1 = require("./shell"); const with_aws_1 = require("./with-aws"); const with_timeout_1 = require("./with-timeout"); const yarn_1 = require("./yarn"); exports.DEFAULT_TEST_TIMEOUT_S = 20 * 60; exports.EXTENDED_TEST_TIMEOUT_S = 30 * 60; /** * Higher order function to execute a block with a CDK app fixture * * Requires an AWS client to be passed in. * * For backwards compatibility with existing tests (so we don't have to change * too much) the inner block is expected to take a `TestFixture` object. */ function withSpecificCdkApp(appName, block) { return async (context) => { const randy = context.randomString; const stackNamePrefix = `cdktest-${randy}`; const integTestDir = path.join(os.tmpdir(), `cdk-integ-${randy}`); context.output.write(` Stack prefix: ${stackNamePrefix}\n`); context.output.write(` Test directory: ${integTestDir}\n`); context.output.write(` Region: ${context.aws.region}\n`); await cloneDirectory(path.join(resources_1.RESOURCES_DIR, 'cdk-apps', appName), integTestDir, context.output); const fixture = new TestFixture(integTestDir, stackNamePrefix, context.output, context.aws, context.randomString); await fixture.ecrPublicLogin(); let success = true; try { const installationVersion = fixture.library.requestedVersion(); await installNpmPackages(fixture, { 'aws-cdk-lib': installationVersion, 'constructs': '^10', }); if (!context.disableBootstrap) { await ensureBootstrapped(fixture); } await block(fixture); } catch (e) { 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); } } }; } /** * Like `withSpecificCdkApp`, but uses the default integration testing app with a million stacks in it */ function withCdkApp(block) { // 'app' is the name of the default integration app in the `cdk-apps` directory return withSpecificCdkApp('app', block); } function withCdkMigrateApp(language, block) { return async (context) => { const stackName = `cdk-migrate-${language}-integ-${context.randomString}`; const integTestDir = path.join(os.tmpdir(), `cdk-migrate-${language}-integ-${context.randomString}`); context.output.write(` Stack name: ${stackName}\n`); context.output.write(` Test directory: ${integTestDir}\n`); fs.mkdirSync(integTestDir); const fixture = new TestFixture(integTestDir, stackName, context.output, context.aws, context.randomString); await fixture.ecrPublicLogin(); await ensureBootstrapped(fixture); await fixture.cdkMigrate(language, stackName); const testFixture = new TestFixture(path.join(integTestDir, stackName), stackName, context.output, context.aws, context.randomString); let success = true; try { await block(testFixture); } catch (e) { success = false; throw e; } finally { if (process.env.INTEG_NO_CLEAN) { context.log(`Left test directory in '${integTestDir}' ($INTEG_NO_CLEAN)`); } else { await fixture.dispose(success); } } }; } /** * Default test fixture for most (all?) integ tests * * It's a composition of withAws/withCdkApp, expecting the test block to take a `TestFixture` * object. * * We could have put `withAws(withCdkApp(fixture => { /... actual test here.../ }))` in every * test declaration but centralizing it is going to make it convenient to modify in the future. */ function withDefaultFixture(block, options = {}) { return (0, with_aws_1.withAws)((0, with_timeout_1.withTimeout)(exports.DEFAULT_TEST_TIMEOUT_S, withCdkApp(block)), options.aws); } function withSpecificFixture(appName, block, options = {}) { return (0, with_aws_1.withAws)((0, with_timeout_1.withTimeout)(exports.DEFAULT_TEST_TIMEOUT_S, withSpecificCdkApp(appName, block)), options.aws); } function withExtendedTimeoutFixture(block, options = {}) { return (0, with_aws_1.withAws)((0, with_timeout_1.withTimeout)(exports.EXTENDED_TEST_TIMEOUT_S, withCdkApp(block)), options.aws); } function withCDKMigrateFixture(language, block, options = {}) { return (0, with_aws_1.withAws)((0, with_timeout_1.withTimeout)(exports.DEFAULT_TEST_TIMEOUT_S, withCdkMigrateApp(language, block)), options.aws); } /** * Retry wrapper that executes a test callback up to maxAttempts times * * If any attempt succeeds, it returns immediately. If all attempts fail, * it throws the last error encountered. */ function withRetry(callback, maxAttempts = 2) { return async (context) => { let lastError; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { await callback(context); return; } catch (error) { lastError = error; if (attempt < maxAttempts) { context.log(`Attempt ${attempt}/${maxAttempts} failed: ${error}. Retrying...`); } } } throw lastError; }; } /** * To be used in place of `withDefaultFixture` when the test * should not create the default bootstrap stack */ function withoutBootstrap(block, options = {}) { return (0, with_aws_1.withAws)(withCdkApp(block), { ...options.aws, disableBootstrap: true, }); } /** * Prepare a target dir byreplicating a source directory */ async function cloneDirectory(source, target, output) { await (0, shell_1.shell)(['rm', '-rf', target], { outputs: output ? [output] : [] }); await (0, shell_1.shell)(['mkdir', '-p', target], { outputs: output ? [output] : [] }); await (0, shell_1.shell)(['cp', '-R', source + '/*', target], { outputs: output ? [output] : [] }); } class TestFixture extends shell_1.ShellHelper { integTestDir; stackNamePrefix; output; aws; randomString; qualifier; bucketsToDelete = new Array(); cli; cdkAssets; library; constructor(integTestDir, stackNamePrefix, output, aws, randomString) { super(integTestDir, output); this.integTestDir = integTestDir; this.stackNamePrefix = stackNamePrefix; this.output = output; this.aws = aws; this.randomString = randomString; this.qualifier = this.randomString.slice(0, 10); this.cli = (0, subprocess_1.testSource)('cli'); this.cdkAssets = (0, subprocess_1.testSource)('cdkAssets'); this.library = (0, subprocess_1.testSource)('library'); } log(s) { this.output.write(`${s}\n`); } /** * Login to the public ECR gallery using the current AWS credentials. * Use this if your test needs to directly pull images outside of a `cdk` or `cdk-assets` command. */ async ecrPublicLogin() { const tokenResponse = await this.aws.ecrPublic.send(new client_ecr_public_1.GetAuthorizationTokenCommand({})); const authData = tokenResponse.authorizationData?.authorizationToken; const docker = process.env.CDK_DOCKER ?? 'docker'; if (!authData) { throw new Error('Could not retrieve ECR public auth token.'); } const decoded = Buffer.from(authData, 'base64').toString('utf-8'); const [username, password] = decoded.split(':'); await this.shell([docker, 'login', '--username', username, '--password', '${ECR_PASSWORD}', 'public.ecr.aws'], { shell: true, modEnv: { ECR_PASSWORD: password, }, }); } /** * @returns the captured output of the deploy command. * !!! DO NOT assume this is the stack's ARN. It will contain other output. !!! */ async cdkDeploy(stackNames, options = {}, skipStackRename) { return this.cdk(this.cdkDeployCommandLine(stackNames, options, skipStackRename), options); } cdkDeployCommandLine(stackNames, options = {}, skipStackRename) { stackNames = typeof stackNames === 'string' ? [stackNames] : stackNames; const neverRequireApproval = options.neverRequireApproval ?? true; return [ 'deploy', ...(neverRequireApproval ? ['--require-approval=never'] : []), // Default to no approval in an unattended test ...(options.options ?? []), // use events because bar renders bad in tests '--progress', 'events', ...(skipStackRename ? stackNames : this.fullStackName(stackNames)), ...(options.telemetryFile ? [`--telemetry-file=${options.telemetryFile}`] : []), ]; } async cdkSynth(options = {}) { return this.cdk([ 'synth', ...(options.telemetryFile ? [`--telemetry-file=${options.telemetryFile}`] : []), ...(options.options ?? []), ], options); } async cdkRefactor(options = {}) { return this.cdk([ 'refactor', ...(options.options ?? []), ], options); } async cdkDestroy(stackNames, options = {}) { stackNames = typeof stackNames === 'string' ? [stackNames] : stackNames; // default to true because most tests don't test user interaction const force = options.force ?? true; return this.cdk(['destroy', ...(force ? ['-f'] : []), // pass -f if user interaction is not desired ...(options.options ?? []), ...this.fullStackName(stackNames)], options); } async cdkBootstrapLegacy(options) { const args = ['bootstrap']; if (options.verbose) { args.push('-v'); } args.push('--toolkit-stack-name', options.toolkitStackName); if (options.bootstrapBucketName) { args.push('--bootstrap-bucket-name', options.bootstrapBucketName); } if (options.noExecute) { args.push('--no-execute'); } if (options.publicAccessBlockConfiguration !== undefined) { args.push('--public-access-block-configuration', options.publicAccessBlockConfiguration.toString()); } if (options.tags) { args.push('--tags', options.tags); } return this.cdk(args, { ...options.cliOptions, modEnv: { ...options.cliOptions?.modEnv, // so that this works for V2, // where the "new" bootstrap is the default CDK_LEGACY_BOOTSTRAP: '1', }, }); } async cdkBootstrapModern(options) { const args = ['bootstrap']; if (options.verbose) { args.push('-v'); } if (options.showTemplate) { args.push('--show-template'); } if (options.template) { args.push('--template', options.template); } args.push('--toolkit-stack-name', options.toolkitStackName); if (options.bootstrapBucketName) { args.push('--bootstrap-bucket-name', options.bootstrapBucketName); } args.push('--qualifier', options.qualifier ?? this.qualifier); if (options.cfnExecutionPolicy) { args.push('--cloudformation-execution-policies', options.cfnExecutionPolicy); } if (options.terminationProtection !== undefined) { args.push('--termination-protection', options.terminationProtection.toString()); } if (options.force) { args.push('--force'); } if (options.tags) { args.push('--tags', options.tags); } if (options.customPermissionsBoundary !== undefined) { args.push('--custom-permissions-boundary', options.customPermissionsBoundary); } else if (options.examplePermissionsBoundary !== undefined) { args.push('--example-permissions-boundary'); } if (options.denyExternalId !== undefined) { args.push(options.denyExternalId ? '--deny-external-id' : '--no-deny-external-id'); } if (options.usePreviousParameters === false) { args.push('--no-previous-parameters'); } if (options.bootstrapTemplate) { args.push('--template', options.bootstrapTemplate); } if (options.trust != null) { args.push('--trust', options.trust.join(',')); } if (options.untrust != null) { args.push('--untrust', options.untrust.join(',')); } return this.cdk(args, { ...options.cliOptions, modEnv: { ...options.cliOptions?.modEnv, // so that this works for V1, // where the "old" bootstrap is the default CDK_NEW_BOOTSTRAP: '1', }, }); } async cdkGarbageCollect(options) { const args = [ 'gc', '--unstable=gc', // TODO: remove when stabilizing '--confirm=false', '--created-buffer-days=0', // Otherwise all assets created during integ tests are too young ]; if (options.rollbackBufferDays) { args.push('--rollback-buffer-days', String(options.rollbackBufferDays)); } if (options.type) { args.push('--type', options.type); } if (options.bootstrapStackName) { args.push('--bootstrapStackName', options.bootstrapStackName); } return this.cdk(args); } async cdkMigrate(language, stackName, inputPath, options) { return this.cdk([ 'migrate', '--language', language, '--stack-name', stackName, '--from-path', inputPath ?? path.join(__dirname, '..', 'resources', 'templates', 'sqs-template.json').toString(), ...(options?.options ?? []), ], options); } async cdk(args, options = {}) { const verbose = options.verbose ?? true; if (!verbose && options.verboseLevel) { throw new Error(`Invalid verbose state: verbose is false and verboseLevel is ${options.verboseLevel}`); } const verboseLevel = options.verboseLevel ?? 1; await this.cli.makeCliAvailable(); return this.shell([ 'cdk', ...(verbose ? [`-${'v'.repeat(verboseLevel)}`] : []), ...args, ], { ...options, modEnv: { ...this.cdkShellEnv(), ...options.modEnv, }, }); } /** * Return the environment variables with which to execute CDK */ cdkShellEnv() { // if tests are using an explicit aws identity already (i.e creds) // force every cdk command to use the same identity. const awsCreds = this.aws.identityEnv() ?? {}; return { AWS_REGION: this.aws.region, AWS_DEFAULT_REGION: this.aws.region, STACK_NAME_PREFIX: this.stackNamePrefix, PACKAGE_LAYOUT_VERSION: '2', TESTING_CDK: 'true', // In these tests we want to make a distinction between stdout and sterr CI: 'false', ...awsCreds, }; } template(stackName) { const fullStackName = this.fullStackName(stackName); const templatePath = path.join(this.integTestDir, 'cdk.out', `${fullStackName}.template.json`); return JSON.parse(fs.readFileSync(templatePath, { encoding: 'utf-8' }).toString()); } async bootstrapRepoName() { await ensureBootstrapped(this); const response = await this.aws.cloudFormation.send(new client_cloudformation_1.DescribeStacksCommand({})); const stack = (response.Stacks ?? []) .filter((s) => s.StackName && s.StackName == this.bootstrapStackName); assert(stack.length == 1); return (0, aws_1.outputFromStack)('ImageRepositoryName', stack[0]) ?? ''; } get bootstrapStackName() { return this.fullStackName('bootstrap-stack'); } fullStackName(stackNames) { if (typeof stackNames === 'string') { return `${this.stackNamePrefix}-${stackNames}`; } else { return stackNames.map(s => `${this.stackNamePrefix}-${s}`); } } /** * Append this to the list of buckets to potentially delete * * At the end of a test, we clean up buckets that may not have gotten destroyed * (for whatever reason). */ rememberToDeleteBucket(bucketName) { this.bucketsToDelete.push(bucketName); } /** * Cleanup leftover stacks and bootstrapped resources */ async dispose(success) { // when using the atmosphere service, it does resource cleanup on our behalf // so we don't have to wait for it. if (!(0, with_aws_1.atmosphereEnabled)()) { const stacksToDelete = await this.deleteableStacks(this.stackNamePrefix); this.sortBootstrapStacksToTheEnd(stacksToDelete); // Bootstrap stacks have buckets that need to be cleaned const bucketNames = stacksToDelete.map(stack => (0, aws_1.outputFromStack)('BucketName', stack)).filter(defined); // Parallelism will be reasonable // eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism await Promise.all(bucketNames.map(b => this.aws.emptyBucket(b))); // The bootstrap bucket has a removal policy of RETAIN by default, so add it to the buckets to be cleaned up. this.bucketsToDelete.push(...bucketNames); // Bootstrap stacks have ECR repositories with images which should be deleted const imageRepositoryNames = stacksToDelete.map(stack => (0, aws_1.outputFromStack)('ImageRepositoryName', stack)).filter(defined); // Parallelism will be reasonable // eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism await Promise.all(imageRepositoryNames.map(r => this.aws.deleteImageRepository(r))); await this.aws.deleteStacks(...stacksToDelete.map((s) => { if (!s.StackName) { throw new Error('Stack name is required to delete a stack.'); } return s.StackName; })); // We might have leaked some buckets by upgrading the bootstrap stack. Be // sure to clean everything. for (const bucket of this.bucketsToDelete) { await this.aws.deleteBucket(bucket); } } // 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) { console.error(`Failed to clean up ${this.integTestDir} due to permissions issues (Docker running as root?)`); } } } /** * Return the stacks starting with our testing prefix that should be deleted */ async deleteableStacks(prefix) { const statusFilter = [ 'CREATE_IN_PROGRESS', 'CREATE_FAILED', 'CREATE_COMPLETE', 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_FAILED', 'ROLLBACK_COMPLETE', 'DELETE_FAILED', 'UPDATE_IN_PROGRESS', 'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS', 'UPDATE_COMPLETE', 'UPDATE_ROLLBACK_IN_PROGRESS', 'UPDATE_ROLLBACK_FAILED', 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS', 'UPDATE_ROLLBACK_COMPLETE', 'REVIEW_IN_PROGRESS', 'IMPORT_IN_PROGRESS', 'IMPORT_COMPLETE', 'IMPORT_ROLLBACK_IN_PROGRESS', 'IMPORT_ROLLBACK_FAILED', 'IMPORT_ROLLBACK_COMPLETE', ]; const response = await this.aws.cloudFormation.send(new client_cloudformation_1.DescribeStacksCommand({})); return (response.Stacks ?? []) .filter((s) => s.StackName && s.StackName.startsWith(prefix)) .filter((s) => s.StackStatus && statusFilter.includes(s.StackStatus)) .filter((s) => s.RootId === undefined); // Only delete parent stacks. Nested stacks are deleted in the process } sortBootstrapStacksToTheEnd(stacks) { stacks.sort((a, b) => { if (!a.StackName || !b.StackName) { throw new Error('Stack names do not exists. These are required for sorting the bootstrap stacks.'); } const aBs = a.StackName.startsWith(this.bootstrapStackName); const bBs = b.StackName.startsWith(this.bootstrapStackName); return aBs != bBs // '+' converts a boolean to 0 or 1 ? (+aBs) - (+bBs) : a.StackName.localeCompare(b.StackName); }); } } exports.TestFixture = TestFixture; /** * Make sure that the given environment is bootstrapped * * Since we go striping across regions, it's going to suck doing this * by hand so let's just mass-automate it. */ async function ensureBootstrapped(fixture) { // Always use the modern bootstrap stack, otherwise we may get the error // "refusing to downgrade from version 7 to version 0" when bootstrapping with default // settings using a v1 CLI. // // It doesn't matter for tests: when they want to test something about an actual legacy // bootstrap stack, they'll create a bootstrap stack with a non-default name to test that exact property. const envSpecifier = `aws://${await fixture.aws.account()}/${fixture.aws.region}`; if (ALREADY_BOOTSTRAPPED_IN_THIS_RUN.has(envSpecifier)) { return; } if ((0, with_aws_1.atmosphereEnabled)()) { // when atmosphere is enabled, each test starts with an empty environment // and needs to deploy the bootstrap stack. in case environments are recylced too quickly, // cloudformation may think the bootstrap bucket still exists even though it doesnt (because of s3 eventual consistency). // so we retry on the specific error for a while. await bootstrapWithRetryOnBucketExists(envSpecifier, fixture); } else { await doBootstrap(envSpecifier, fixture, false); } // when using the atmosphere service, every test needs to bootstrap // its own environment. if (!(0, with_aws_1.atmosphereEnabled)()) { ALREADY_BOOTSTRAPPED_IN_THIS_RUN.add(envSpecifier); } } async function doBootstrap(envSpecifier, fixture, allowErrExit) { return fixture.cdk(['bootstrap', '--bootstrap-kms-key-id', 'AWS_MANAGED_KEY', envSpecifier], { modEnv: { // Even for v1, use new bootstrap CDK_NEW_BOOTSTRAP: '1', // when allowing error exit, we probably want to inspect // and compare output, which is better done without color characters. ...(allowErrExit ? { FORCE_COLOR: '0' } : {}), }, allowErrExit, }); } async function bootstrapWithRetryOnBucketExists(envSpecifier, fixture) { const account = await fixture.aws.account(); const retryAfterSeconds = 30; const bootstrapBucket = `cdk-hnb659fds-assets-${account}-${fixture.aws.region}`; // s3 says that a bucket deletion can take up to an hour to be fully visible. // empirically we see that a few minutes is enough though. lets give 10 to be on the safe(r) side. const timeoutMinutes = 10; const timeoutDate = new Date(Date.now() + timeoutMinutes * 60 * 1000); while (true) { const out = await doBootstrap(envSpecifier, fixture, true); if (out.includes(`Environment ${envSpecifier} bootstrapped`)) { break; } if (out.includes(`${bootstrapBucket} already exists`)) { // might be an s3 eventualy consistency issue due to recycled environments. if (Date.now() < timeoutDate.getTime()) { fixture.log(`Bootstrap of ${envSpecifier} failed due to bucket existence check. Retrying in ${retryAfterSeconds} seconds...`); await (0, aws_1.sleep)(retryAfterSeconds * 1000); continue; } } throw new Error(`Failed bootstrapping ${envSpecifier}`); } } function defined(x) { return x !== undefined; } /** * Install the given NPM packages, identified by their names and versions * * Works by writing the packages to a `package.json` file, and * then running NPM7's "install" on it. The use of NPM7 will automatically * install required peerDependencies. * * If we're running in REPO mode and we find the package in the set of local * packages in the repository, we'll write the directory name to `package.json` * so that NPM will create a symlink (this allows running tests against * built-but-unpackaged modules, and saves dev cycle time). * * Be aware you MUST install all the packages you directly depend upon! In the case * of a repo/symlinking install, transitive dependencies WILL NOT be installed in the * current directory's `node_modules` directory, because they will already have been * symlinked from the TARGET directory's `node_modules` directory (which is sufficient * for Node's dependency lookup mechanism). */ async function installNpmPackages(fixture, packages) { if (process.env.REPO_ROOT) { const monoRepo = await (0, yarn_1.findYarnPackages)(process.env.REPO_ROOT); // Replace the install target with the physical location of this package for (const key of Object.keys(packages)) { if (key in monoRepo) { packages[key] = monoRepo[key]; } } } fs.writeFileSync(path.join(fixture.integTestDir, 'package.json'), JSON.stringify({ name: 'cdk-integ-tests', private: true, version: '0.0.1', devDependencies: packages, }, undefined, 2), { encoding: 'utf-8' }); // we often ECONNRESET from NPM so lets retry. this might be because of high concurrency // which overwhelmes system resources. const timeoutMinutes = 10; const timeoutDate = new Date(Date.now() + timeoutMinutes * 60 * 1000); const retryAfterSeconds = 30; while (true) { try { // Now install that `package.json` using NPM7 await fixture.shell(['node', require.resolve('npm'), 'install']); break; } catch (e) { if (Date.now() < timeoutDate.getTime() && fixture.output.toString().includes('ECONNRESET')) { fixture.log(`npm install failed due to ECONNRESET. Retrying in ${retryAfterSeconds} seconds...`); await (0, aws_1.sleep)(retryAfterSeconds * 1000); continue; } throw e; } } } const ALREADY_BOOTSTRAPPED_IN_THIS_RUN = new Set(); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2l0aC1jZGstYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsid2l0aC1jZGstYXBwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQXVDQSxnREErQ0M7QUFLRCxnQ0FLQztBQUVELDhDQStDQztBQVdELGdEQUVDO0FBRUQsa0RBRUM7QUFFRCxnRUFFQztBQUVELHNEQUVDO0FBU0QsOEJBcUJDO0FBbUJELDRDQUtDO0FBaUJELHdDQUlDO0FBc2hCRCxnREEyQkM7QUFnRUQsZ0RBdUNDO0FBNzRCRCwrQkFBK0I7QUFDL0IsaUNBQWlDO0FBQ2pDLHlCQUF5QjtBQUN6Qix5QkFBeUI7QUFDekIsNkJBQTZCO0FBRTdCLDBFQUF1RTtBQUN2RSxrRUFBMEU7QUFFMUUsK0JBQStDO0FBRy9DLDZEQUEwRDtBQUMxRCwyQ0FBNEM7QUFFNUMsbUNBQXFEO0FBRXJELHlDQUF3RDtBQUN4RCxpREFBNkM7QUFDN0MsaUNBQTBDO0FBRTdCLFFBQUEsc0JBQXNCLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUNqQyxRQUFBLHVCQUF1QixHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFTL0M7Ozs7Ozs7R0FPRztBQUNILFNBQWdCLGtCQUFrQixDQUNoQyxPQUFlLEVBQ2YsS0FBOEM7SUFFOUMsT0FBTyxLQUFLLEVBQUUsT0FBMkQsRUFBRSxFQUFFO1FBQzNFLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxZQUFZLENBQUM7UUFDbkMsTUFBTSxlQUFlLEdBQUcsV0FBVyxLQUFLLEVBQUUsQ0FBQztRQUMzQyxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsRUFBRSxhQUFhLEtBQUssRUFBRSxDQUFDLENBQUM7UUFFbEUsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsb0JBQW9CLGVBQWUsSUFBSSxDQUFDLENBQUM7UUFDOUQsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsb0JBQW9CLFlBQVks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