UNPKG

aws-cdk

Version:

AWS CDK CLI, the command line tool for CDK apps

599 lines 93.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GarbageCollector = exports.ObjectAsset = exports.ImageAsset = exports.ECR_ISOLATED_TAG = exports.S3_ISOLATED_TAG = void 0; const chalk = require("chalk"); const promptly = require("promptly"); const toolkit_info_1 = require("../toolkit-info"); const progress_printer_1 = require("./progress-printer"); const stack_refresh_1 = require("./stack-refresh"); const api_1 = require("../../../../@aws-cdk/tmp-toolkit-helpers/src/api"); const private_1 = require("../../../../@aws-cdk/tmp-toolkit-helpers/src/api/io/private"); const mode_1 = require("../plugin/mode"); // Must use a require() otherwise esbuild complains // eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/consistent-type-imports const pLimit = require('p-limit'); exports.S3_ISOLATED_TAG = 'aws-cdk:isolated'; exports.ECR_ISOLATED_TAG = 'aws-cdk.isolated'; // ':' is not valid in ECR tags const P_LIMIT = 50; const DAY = 24 * 60 * 60 * 1000; // Number of milliseconds in a day /** * An image asset that lives in the bootstrapped ECR Repository */ class ImageAsset { constructor(digest, size, tags, manifest) { this.digest = digest; this.size = size; this.tags = tags; this.manifest = manifest; } getTag(tag) { return this.tags.find(t => t.includes(tag)); } hasTag(tag) { return this.tags.some(t => t.includes(tag)); } hasIsolatedTag() { return this.hasTag(exports.ECR_ISOLATED_TAG); } getIsolatedTag() { return this.getTag(exports.ECR_ISOLATED_TAG); } isolatedTagBefore(date) { const dateIsolated = this.dateIsolated(); if (!dateIsolated || dateIsolated == '') { return false; } return new Date(dateIsolated) < date; } buildImageTag(inc) { // isolatedTag will look like "X-aws-cdk.isolated-YYYYY" return `${inc}-${exports.ECR_ISOLATED_TAG}-${String(Date.now())}`; } dateIsolated() { // isolatedTag will look like "X-aws-cdk.isolated-YYYYY" return this.getIsolatedTag()?.split('-')[3]; } } exports.ImageAsset = ImageAsset; /** * An object asset that lives in the bootstrapped S3 Bucket */ class ObjectAsset { constructor(bucket, key, size) { this.bucket = bucket; this.key = key; this.size = size; this.cached_tags = undefined; } fileName() { return this.key.split('.')[0]; } async allTags(s3) { if (this.cached_tags) { return this.cached_tags; } const response = await s3.getObjectTagging({ Bucket: this.bucket, Key: this.key }); this.cached_tags = response.TagSet; return this.cached_tags; } getTag(tag) { if (!this.cached_tags) { throw new api_1.ToolkitError('Cannot call getTag before allTags'); } return this.cached_tags.find((t) => t.Key === tag)?.Value; } hasTag(tag) { if (!this.cached_tags) { throw new api_1.ToolkitError('Cannot call hasTag before allTags'); } return this.cached_tags.some((t) => t.Key === tag); } hasIsolatedTag() { return this.hasTag(exports.S3_ISOLATED_TAG); } isolatedTagBefore(date) { const tagValue = this.getTag(exports.S3_ISOLATED_TAG); if (!tagValue || tagValue == '') { return false; } return new Date(tagValue) < date; } } exports.ObjectAsset = ObjectAsset; /** * A class to facilitate Garbage Collection of S3 and ECR assets */ class GarbageCollector { constructor(props) { this.props = props; this.ioHelper = props.ioHelper; this.garbageCollectS3Assets = ['s3', 'all'].includes(props.type); this.garbageCollectEcrAssets = ['ecr', 'all'].includes(props.type); this.permissionToDelete = ['delete-tagged', 'full'].includes(props.action); this.permissionToTag = ['tag', 'full'].includes(props.action); this.confirm = props.confirm ?? true; this.bootstrapStackName = props.bootstrapStackName ?? toolkit_info_1.DEFAULT_TOOLKIT_STACK_NAME; } /** * Perform garbage collection on the resolved environment. */ async garbageCollect() { await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${this.garbageCollectS3Assets} ${this.garbageCollectEcrAssets}`)); // SDKs const sdk = (await this.props.sdkProvider.forEnvironment(this.props.resolvedEnvironment, mode_1.Mode.ForWriting)).sdk; const cfn = sdk.cloudFormation(); const qualifier = await this.bootstrapQualifier(sdk, this.bootstrapStackName); const activeAssets = new stack_refresh_1.ActiveAssetCache(); // Grab stack templates first await (0, stack_refresh_1.refreshStacks)(cfn, this.ioHelper, activeAssets, qualifier); // Start the background refresh const backgroundStackRefresh = new stack_refresh_1.BackgroundStackRefresh({ cfn, ioHelper: this.ioHelper, activeAssets, qualifier, }); backgroundStackRefresh.start(); try { if (this.garbageCollectS3Assets) { await this.garbageCollectS3(sdk, activeAssets, backgroundStackRefresh); } if (this.garbageCollectEcrAssets) { await this.garbageCollectEcr(sdk, activeAssets, backgroundStackRefresh); } } catch (err) { throw new api_1.ToolkitError(err); } finally { backgroundStackRefresh.stop(); } } /** * Perform garbage collection on ECR assets */ async garbageCollectEcr(sdk, activeAssets, backgroundStackRefresh) { const ecr = sdk.ecr(); const repo = await this.bootstrapRepositoryName(sdk, this.bootstrapStackName); const numImages = await this.numImagesInRepo(ecr, repo); const printer = new progress_printer_1.ProgressPrinter(this.ioHelper, numImages, 1000); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Found bootstrap repo ${repo} with ${numImages} images`)); try { // const batches = 1; const batchSize = 1000; const currentTime = Date.now(); const graceDays = this.props.rollbackBufferDays; await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Parsing through ${numImages} images in batches`)); printer.start(); for await (const batch of this.readRepoInBatches(ecr, repo, batchSize, currentTime)) { await backgroundStackRefresh.noOlderThan(600000); // 10 mins const { included: isolated, excluded: notIsolated } = partition(batch, asset => !asset.tags.some(t => activeAssets.contains(t))); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${isolated.length} isolated images`)); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${notIsolated.length} not isolated images`)); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${batch.length} images total`)); let deletables = isolated; let taggables = []; let untaggables = []; if (graceDays > 0) { await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg('Filtering out images that are not old enough to delete')); // We delete images that are not referenced in ActiveAssets and have the Isolated Tag with a date // earlier than the current time - grace period. deletables = isolated.filter(img => img.isolatedTagBefore(new Date(currentTime - (graceDays * DAY)))); // We tag images that are not referenced in ActiveAssets and do not have the Isolated Tag. taggables = isolated.filter(img => !img.hasIsolatedTag()); // We untag images that are referenced in ActiveAssets and currently have the Isolated Tag. untaggables = notIsolated.filter(img => img.hasIsolatedTag()); } await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${deletables.length} deletable assets`)); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${taggables.length} taggable assets`)); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${untaggables.length} assets to untag`)); if (this.permissionToDelete && deletables.length > 0) { await this.confirmationPrompt(printer, deletables, 'image'); await this.parallelDeleteEcr(ecr, repo, deletables, printer); } if (this.permissionToTag && taggables.length > 0) { await this.parallelTagEcr(ecr, repo, taggables, printer); } if (this.permissionToTag && untaggables.length > 0) { await this.parallelUntagEcr(ecr, repo, untaggables); } printer.reportScannedAsset(batch.length); } } catch (err) { throw new api_1.ToolkitError(err); } finally { printer.stop(); } } /** * Perform garbage collection on S3 assets */ async garbageCollectS3(sdk, activeAssets, backgroundStackRefresh) { const s3 = sdk.s3(); const bucket = await this.bootstrapBucketName(sdk, this.bootstrapStackName); const numObjects = await this.numObjectsInBucket(s3, bucket); const printer = new progress_printer_1.ProgressPrinter(this.ioHelper, numObjects, 1000); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Found bootstrap bucket ${bucket} with ${numObjects} objects`)); try { const batchSize = 1000; const currentTime = Date.now(); const graceDays = this.props.rollbackBufferDays; await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Parsing through ${numObjects} objects in batches`)); printer.start(); // Process objects in batches of 1000 // This is the batch limit of s3.DeleteObject and we intend to optimize for the "worst case" scenario // where gc is run for the first time on a long-standing bucket where ~100% of objects are isolated. for await (const batch of this.readBucketInBatches(s3, bucket, batchSize, currentTime)) { await backgroundStackRefresh.noOlderThan(600000); // 10 mins const { included: isolated, excluded: notIsolated } = partition(batch, asset => !activeAssets.contains(asset.fileName())); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${isolated.length} isolated assets`)); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${notIsolated.length} not isolated assets`)); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${batch.length} objects total`)); let deletables = isolated; let taggables = []; let untaggables = []; if (graceDays > 0) { await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg('Filtering out assets that are not old enough to delete')); await this.parallelReadAllTags(s3, batch); // We delete objects that are not referenced in ActiveAssets and have the Isolated Tag with a date // earlier than the current time - grace period. deletables = isolated.filter(obj => obj.isolatedTagBefore(new Date(currentTime - (graceDays * DAY)))); // We tag objects that are not referenced in ActiveAssets and do not have the Isolated Tag. taggables = isolated.filter(obj => !obj.hasIsolatedTag()); // We untag objects that are referenced in ActiveAssets and currently have the Isolated Tag. untaggables = notIsolated.filter(obj => obj.hasIsolatedTag()); } await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${deletables.length} deletable assets`)); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${taggables.length} taggable assets`)); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`${untaggables.length} assets to untag`)); if (this.permissionToDelete && deletables.length > 0) { await this.confirmationPrompt(printer, deletables, 'object'); await this.parallelDeleteS3(s3, bucket, deletables, printer); } if (this.permissionToTag && taggables.length > 0) { await this.parallelTagS3(s3, bucket, taggables, currentTime, printer); } if (this.permissionToTag && untaggables.length > 0) { await this.parallelUntagS3(s3, bucket, untaggables); } printer.reportScannedAsset(batch.length); } } catch (err) { throw new api_1.ToolkitError(err); } finally { printer.stop(); } } async parallelReadAllTags(s3, objects) { const limit = pLimit(P_LIMIT); for (const obj of objects) { await limit(() => obj.allTags(s3)); } } /** * Untag assets that were previously tagged, but now currently referenced. * Since this is treated as an implementation detail, we do not print the results in the printer. */ async parallelUntagEcr(ecr, repo, untaggables) { const limit = pLimit(P_LIMIT); for (const img of untaggables) { const tag = img.getIsolatedTag(); await limit(() => ecr.batchDeleteImage({ repositoryName: repo, imageIds: [{ imageTag: tag, }], })); } await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Untagged ${untaggables.length} assets`)); } /** * Untag assets that were previously tagged, but now currently referenced. * Since this is treated as an implementation detail, we do not print the results in the printer. */ async parallelUntagS3(s3, bucket, untaggables) { const limit = pLimit(P_LIMIT); for (const obj of untaggables) { const tags = await obj.allTags(s3) ?? []; const updatedTags = tags.filter((tag) => tag.Key !== exports.S3_ISOLATED_TAG); await limit(() => s3.deleteObjectTagging({ Bucket: bucket, Key: obj.key, })); await limit(() => s3.putObjectTagging({ Bucket: bucket, Key: obj.key, Tagging: { TagSet: updatedTags, }, })); } await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Untagged ${untaggables.length} assets`)); } /** * Tag images in parallel using p-limit */ async parallelTagEcr(ecr, repo, taggables, printer) { const limit = pLimit(P_LIMIT); for (let i = 0; i < taggables.length; i++) { const img = taggables[i]; const tagEcr = async () => { try { await ecr.putImage({ repositoryName: repo, imageDigest: img.digest, imageManifest: img.manifest, imageTag: img.buildImageTag(i), }); } catch (error) { // This is a false negative -- an isolated asset is untagged // likely due to an imageTag collision. We can safely ignore, // and the isolated asset will be tagged next time. await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Warning: unable to tag image ${JSON.stringify(img.tags)} with ${img.buildImageTag(i)} due to the following error: ${error}`)); } }; await limit(() => tagEcr()); } printer.reportTaggedAsset(taggables); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Tagged ${taggables.length} assets`)); } /** * Tag objects in parallel using p-limit. The putObjectTagging API does not * support batch tagging so we must handle the parallelism client-side. */ async parallelTagS3(s3, bucket, taggables, date, printer) { const limit = pLimit(P_LIMIT); for (const obj of taggables) { await limit(() => s3.putObjectTagging({ Bucket: bucket, Key: obj.key, Tagging: { TagSet: [ { Key: exports.S3_ISOLATED_TAG, Value: String(date), }, ], }, })); } printer.reportTaggedAsset(taggables); await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Tagged ${taggables.length} assets`)); } /** * Delete images in parallel. The deleteImage API supports batches of 100. */ async parallelDeleteEcr(ecr, repo, deletables, printer) { const batchSize = 100; const imagesToDelete = deletables.map(img => ({ imageDigest: img.digest, })); try { const batches = []; for (let i = 0; i < imagesToDelete.length; i += batchSize) { batches.push(imagesToDelete.slice(i, i + batchSize)); } // Delete images in batches for (const batch of batches) { await ecr.batchDeleteImage({ imageIds: batch, repositoryName: repo, }); const deletedCount = batch.length; await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Deleted ${deletedCount} assets`)); printer.reportDeletedAsset(deletables.slice(0, deletedCount)); } } catch (err) { await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_ERROR.msg(`Error deleting images: ${err}`)); } } /** * Delete objects in parallel. The deleteObjects API supports batches of 1000. */ async parallelDeleteS3(s3, bucket, deletables, printer) { const batchSize = 1000; const objectsToDelete = deletables.map(asset => ({ Key: asset.key, })); try { const batches = []; for (let i = 0; i < objectsToDelete.length; i += batchSize) { batches.push(objectsToDelete.slice(i, i + batchSize)); } // Delete objects in batches for (const batch of batches) { await s3.deleteObjects({ Bucket: bucket, Delete: { Objects: batch, Quiet: true, }, }); const deletedCount = batch.length; await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(`Deleted ${deletedCount} assets`)); printer.reportDeletedAsset(deletables.slice(0, deletedCount)); } } catch (err) { await this.ioHelper.notify(private_1.IO.DEFAULT_TOOLKIT_DEBUG.msg(chalk.red(`Error deleting objects: ${err}`))); } } async bootstrapBucketName(sdk, bootstrapStackName) { const toolkitInfo = await toolkit_info_1.ToolkitInfo.lookup(this.props.resolvedEnvironment, sdk, this.ioHelper, bootstrapStackName); return toolkitInfo.bucketName; } async bootstrapRepositoryName(sdk, bootstrapStackName) { const toolkitInfo = await toolkit_info_1.ToolkitInfo.lookup(this.props.resolvedEnvironment, sdk, this.ioHelper, bootstrapStackName); return toolkitInfo.repositoryName; } async bootstrapQualifier(sdk, bootstrapStackName) { const toolkitInfo = await toolkit_info_1.ToolkitInfo.lookup(this.props.resolvedEnvironment, sdk, this.ioHelper, bootstrapStackName); return toolkitInfo.bootstrapStack.parameters.Qualifier; } async numObjectsInBucket(s3, bucket) { let totalCount = 0; let continuationToken; do { const response = await s3.listObjectsV2({ Bucket: bucket, ContinuationToken: continuationToken, }); totalCount += response.KeyCount ?? 0; continuationToken = response.NextContinuationToken; } while (continuationToken); return totalCount; } async numImagesInRepo(ecr, repo) { let totalCount = 0; let nextToken; do { const response = await ecr.listImages({ repositoryName: repo, nextToken: nextToken, }); totalCount += response.imageIds?.length ?? 0; nextToken = response.nextToken; } while (nextToken); return totalCount; } async *readRepoInBatches(ecr, repo, batchSize = 1000, currentTime) { let continuationToken; do { const batch = []; while (batch.length < batchSize) { const response = await ecr.listImages({ repositoryName: repo, nextToken: continuationToken, }); // No images in the repository if (!response.imageIds || response.imageIds.length === 0) { break; } // map unique image digest to (possibly multiple) tags const images = imageMap(response.imageIds ?? []); const imageIds = Object.keys(images).map(key => ({ imageDigest: key, })); const describeImageInfo = await ecr.describeImages({ repositoryName: repo, imageIds: imageIds, }); const getImageInfo = await ecr.batchGetImage({ repositoryName: repo, imageIds: imageIds, }); const combinedImageInfo = describeImageInfo.imageDetails?.map(imageDetail => { const matchingImage = getImageInfo.images?.find(img => img.imageId?.imageDigest === imageDetail.imageDigest); return { ...imageDetail, manifest: matchingImage?.imageManifest, }; }); for (const image of combinedImageInfo ?? []) { const lastModified = image.imagePushedAt ?? new Date(currentTime); // Store the image if it was pushed earlier than today - createdBufferDays if (image.imageDigest && lastModified < new Date(currentTime - (this.props.createdBufferDays * DAY))) { batch.push(new ImageAsset(image.imageDigest, image.imageSizeInBytes ?? 0, image.imageTags ?? [], image.manifest ?? '')); } } continuationToken = response.nextToken; if (!continuationToken) break; // No more images to fetch } if (batch.length > 0) { yield batch; } } while (continuationToken); } /** * Generator function that reads objects from the S3 Bucket in batches. */ async *readBucketInBatches(s3, bucket, batchSize = 1000, currentTime) { let continuationToken; do { const batch = []; while (batch.length < batchSize) { const response = await s3.listObjectsV2({ Bucket: bucket, ContinuationToken: continuationToken, }); response.Contents?.forEach((obj) => { const key = obj.Key ?? ''; const size = obj.Size ?? 0; const lastModified = obj.LastModified ?? new Date(currentTime); // Store the object if it has a Key and // if it has not been modified since today - createdBufferDays if (key && lastModified < new Date(currentTime - (this.props.createdBufferDays * DAY))) { batch.push(new ObjectAsset(bucket, key, size)); } }); continuationToken = response.NextContinuationToken; if (!continuationToken) break; // No more objects to fetch } if (batch.length > 0) { yield batch; } } while (continuationToken); } async confirmationPrompt(printer, deletables, type) { const pluralize = (name, count) => { return count === 1 ? name : `${name}s`; }; if (this.confirm) { const message = [ `Found ${deletables.length} ${pluralize(type, deletables.length)} to delete based off of the following criteria:`, `- ${type}s have been isolated for > ${this.props.rollbackBufferDays} days`, `- ${type}s were created > ${this.props.createdBufferDays} days ago`, '', 'Delete this batch (yes/no/delete-all)?', ].join('\n'); printer.pause(); const response = await promptly.prompt(message, { trim: true }); // Anything other than yes/y/delete-all is treated as no if (!response || !['yes', 'y', 'delete-all'].includes(response.toLowerCase())) { throw new api_1.ToolkitError('Deletion aborted by user'); } else if (response.toLowerCase() == 'delete-all') { this.confirm = false; } } printer.resume(); } } exports.GarbageCollector = GarbageCollector; function partition(xs, pred) { const result = { included: [], excluded: [], }; for (const x of xs) { if (pred(x)) { result.included.push(x); } else { result.excluded.push(x); } } return result; } function imageMap(imageIds) { const images = {}; for (const image of imageIds ?? []) { if (!image.imageDigest || !image.imageTag) { continue; } if (!images[image.imageDigest]) { images[image.imageDigest] = []; } images[image.imageDigest].push(image.imageTag); } return images; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2FyYmFnZS1jb2xsZWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJnYXJiYWdlLWNvbGxlY3Rvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFHQSwrQkFBK0I7QUFDL0IscUNBQXFDO0FBRXJDLGtEQUEwRTtBQUMxRSx5REFBcUQ7QUFDckQsbURBQTBGO0FBQzFGLDBFQUFnRjtBQUNoRix5RkFBZ0c7QUFDaEcseUNBQXNDO0FBRXRDLG1EQUFtRDtBQUNuRCw0R0FBNEc7QUFDNUcsTUFBTSxNQUFNLEdBQTZCLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUUvQyxRQUFBLGVBQWUsR0FBRyxrQkFBa0IsQ0FBQztBQUNyQyxRQUFBLGdCQUFnQixHQUFHLGtCQUFrQixDQUFDLENBQUMsK0JBQStCO0FBQ25GLE1BQU0sT0FBTyxHQUFHLEVBQUUsQ0FBQztBQUNuQixNQUFNLEdBQUcsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQyxrQ0FBa0M7QUFJbkU7O0dBRUc7QUFDSCxNQUFhLFVBQVU7SUFDckIsWUFDa0IsTUFBYyxFQUNkLElBQVksRUFDWixJQUFjLEVBQ2QsUUFBZ0I7UUFIaEIsV0FBTSxHQUFOLE1BQU0sQ0FBUTtRQUNkLFNBQUksR0FBSixJQUFJLENBQVE7UUFDWixTQUFJLEdBQUosSUFBSSxDQUFVO1FBQ2QsYUFBUSxHQUFSLFFBQVEsQ0FBUTtJQUVsQyxDQUFDO0lBRU8sTUFBTSxDQUFDLEdBQVc7UUFDeEIsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUM5QyxDQUFDO0lBRU8sTUFBTSxDQUFDLEdBQVc7UUFDeEIsT0FBTyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMs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