UNPKG

token-injectable-docker-builder

Version:

The TokenInjectableDockerBuilder is a flexible AWS CDK construct that enables the usage of AWS CDK tokens in the building, pushing, and deployment of Docker images to Amazon Elastic Container Registry (ECR). It leverages AWS CodeBuild and Lambda custom re

230 lines 37.2 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.TokenInjectableDockerBuilder = void 0; const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); const crypto = require("crypto"); const fs = require("fs"); const path = require("path"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const aws_codebuild_1 = require("aws-cdk-lib/aws-codebuild"); const aws_ecr_1 = require("aws-cdk-lib/aws-ecr"); const aws_ecs_1 = require("aws-cdk-lib/aws-ecs"); const aws_iam_1 = require("aws-cdk-lib/aws-iam"); const aws_kms_1 = require("aws-cdk-lib/aws-kms"); const aws_lambda_1 = require("aws-cdk-lib/aws-lambda"); const aws_s3_assets_1 = require("aws-cdk-lib/aws-s3-assets"); const custom_resources_1 = require("aws-cdk-lib/custom-resources"); const constructs_1 = require("constructs"); /** * A CDK construct to build and push Docker images to an ECR repository using * CodeBuild and Lambda custom resources, **then** retrieve the final image tag * so that ECS/Lambda references use the exact digest. */ class TokenInjectableDockerBuilder extends constructs_1.Construct { /** * Creates a new `TokenInjectableDockerBuilder`. * * @param scope The scope in which to define this construct. * @param id The scoped construct ID. * @param props Configuration for building and pushing the Docker image. */ constructor(scope, id, props) { super(scope, id); const { path: sourcePath, buildArgs, dockerLoginSecretArn, vpc, securityGroups, subnetSelection, installCommands, preBuildCommands, kmsEncryption = false, completenessQueryInterval, exclude, } = props; // Generate an ephemeral tag for CodeBuild const imageTag = crypto.randomUUID(); // Optionally define a KMS key for ECR encryption if requested let encryptionKey; if (kmsEncryption) { encryptionKey = new aws_kms_1.Key(this, 'EcrEncryptionKey', { enableKeyRotation: true, }); } // Create an ECR repository (optionally with KMS encryption) this.ecrRepository = new aws_ecr_1.Repository(this, 'ECRRepository', { lifecycleRules: [ { rulePriority: 1, description: 'Remove untagged images after 1 day', tagStatus: aws_ecr_1.TagStatus.UNTAGGED, maxImageAge: aws_cdk_lib_1.Duration.days(1), }, ], encryption: kmsEncryption ? aws_ecr_1.RepositoryEncryption.KMS : aws_ecr_1.RepositoryEncryption.AES_256, encryptionKey: kmsEncryption ? encryptionKey : undefined, imageScanOnPush: true, }); let effectiveExclude = exclude; if (!effectiveExclude) { const dockerignorePath = path.join(sourcePath, '.dockerignore'); if (fs.existsSync(dockerignorePath)) { const fileContent = fs.readFileSync(dockerignorePath, 'utf8'); effectiveExclude = fileContent .split('\n') .map((line) => line.trim()) .filter((line) => line.length > 0 && !line.startsWith('#')); } } // Ensure Dockerfile is never excluded if (effectiveExclude) { effectiveExclude = effectiveExclude.filter((pattern) => pattern.toLowerCase() !== 'dockerfile'); } // Wrap the source folder as an S3 asset for CodeBuild to use const sourceAsset = new aws_s3_assets_1.Asset(this, 'SourceAsset', { path: sourcePath, exclude: effectiveExclude, }); // Convert buildArgs to a CLI-friendly string const buildArgsString = buildArgs ? Object.entries(buildArgs) .map(([k, v]) => `--build-arg ${k}=${v}`) .join(' ') : ''; // Optional DockerHub login, if a secret ARN is provided const dockerLoginCommands = dockerLoginSecretArn ? [ 'echo "Retrieving Docker credentials..."', 'apt-get update -y && apt-get install -y jq', `DOCKER_USERNAME=$(aws secretsmanager get-secret-value --secret-id ${dockerLoginSecretArn} --query SecretString --output text | jq -r .username)`, `DOCKER_PASSWORD=$(aws secretsmanager get-secret-value --secret-id ${dockerLoginSecretArn} --query SecretString --output text | jq -r .password)`, 'echo "Logging in to Docker Hub..."', 'echo $DOCKER_PASSWORD | docker login --username $DOCKER_USERNAME --password-stdin', ] : ['echo "No Docker credentials. Skipping Docker Hub login."']; const buildSpecObj = { version: '0.2', phases: { install: { commands: [ 'echo "Beginning install phase..."', ...(installCommands ?? []), ], }, pre_build: { commands: [ ...(preBuildCommands ?? []), ...dockerLoginCommands, 'echo "Retrieving AWS Account ID..."', 'export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)', 'echo "Logging into Amazon ECR..."', 'aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com', ], }, build: { commands: [ `echo "Building Docker image with tag ${imageTag}..."`, `docker build ${buildArgsString} -t $ECR_REPO_URI:${imageTag} $CODEBUILD_SRC_DIR`, ], }, post_build: { commands: [ `echo "Pushing Docker image with tag ${imageTag}..."`, `docker push $ECR_REPO_URI:${imageTag}`, ], }, }, }; // Create the CodeBuild project const codeBuildProject = new aws_codebuild_1.Project(this, 'CodeBuildProject', { source: aws_codebuild_1.Source.s3({ bucket: sourceAsset.bucket, path: sourceAsset.s3ObjectKey, }), environment: { buildImage: aws_codebuild_1.LinuxBuildImage.STANDARD_7_0, privileged: true, }, environmentVariables: { ECR_REPO_URI: { value: this.ecrRepository.repositoryUri }, }, buildSpec: aws_codebuild_1.BuildSpec.fromObject(buildSpecObj), vpc, securityGroups, subnetSelection, }); // Grant CodeBuild the ability to interact with ECR this.ecrRepository.grantPullPush(codeBuildProject); codeBuildProject.addToRolePolicy(new aws_iam_1.PolicyStatement({ actions: [ 'ecr:GetAuthorizationToken', 'ecr:GetDownloadUrlForLayer', 'ecr:BatchCheckLayerAvailability', ], resources: ['*'], })); if (dockerLoginSecretArn) { codeBuildProject.addToRolePolicy(new aws_iam_1.PolicyStatement({ actions: ['secretsmanager:GetSecretValue'], resources: [dockerLoginSecretArn], })); } // Conditionally grant KMS encrypt/decrypt if a key is used if (encryptionKey) { encryptionKey.grantEncryptDecrypt(codeBuildProject.role); } // Define Lambda functions for custom resource event and completion handling const onEventHandlerFunction = new aws_lambda_1.Function(this, 'OnEventHandlerFunction', { runtime: aws_lambda_1.Runtime.NODEJS_18_X, code: aws_lambda_1.Code.fromAsset(path.resolve(__dirname, '../onEvent')), handler: 'onEvent.handler', timeout: aws_cdk_lib_1.Duration.minutes(15), }); onEventHandlerFunction.addToRolePolicy(new aws_iam_1.PolicyStatement({ actions: ['codebuild:StartBuild'], resources: [codeBuildProject.projectArn], })); const isCompleteHandlerFunction = new aws_lambda_1.Function(this, 'IsCompleteHandlerFunction', { runtime: aws_lambda_1.Runtime.NODEJS_18_X, code: aws_lambda_1.Code.fromAsset(path.resolve(__dirname, '../isComplete')), environment: { IMAGE_TAG: imageTag, }, handler: 'isComplete.handler', timeout: aws_cdk_lib_1.Duration.minutes(15), }); isCompleteHandlerFunction.addToRolePolicy(new aws_iam_1.PolicyStatement({ actions: [ 'codebuild:BatchGetBuilds', 'codebuild:ListBuildsForProject', 'logs:GetLogEvents', 'logs:DescribeLogStreams', 'logs:DescribeLogGroups', ], resources: ['*'], })); // Conditionally allow encryption if a key is used if (encryptionKey) { encryptionKey.grantEncryptDecrypt(onEventHandlerFunction); encryptionKey.grantEncryptDecrypt(isCompleteHandlerFunction); } this.ecrRepository.grantPullPush(onEventHandlerFunction); this.ecrRepository.grantPullPush(isCompleteHandlerFunction); // Create a custom resource provider that uses the above Lambdas const provider = new custom_resources_1.Provider(this, 'CustomResourceProvider', { onEventHandler: onEventHandlerFunction, isCompleteHandler: isCompleteHandlerFunction, queryInterval: completenessQueryInterval ?? aws_cdk_lib_1.Duration.seconds(30), }); // Custom Resource that triggers the CodeBuild and waits for completion const buildTriggerResource = new aws_cdk_lib_1.CustomResource(this, 'BuildTriggerResource', { serviceToken: provider.serviceToken, properties: { ProjectName: codeBuildProject.projectName, ImageTag: imageTag, Trigger: sourceAsset.assetHash, }, }); buildTriggerResource.node.addDependency(codeBuildProject); // Retrieve the final Docker image tag from Data.ImageTag const imageTagRef = buildTriggerResource.getAttString('ImageTag'); this.containerImage = aws_ecs_1.ContainerImage.fromEcrRepository(this.ecrRepository, imageTagRef); this.dockerImageCode = aws_lambda_1.DockerImageCode.fromEcr(this.ecrRepository, { tagOrDigest: imageTagRef, }); } } exports.TokenInjectableDockerBuilder = TokenInjectableDockerBuilder; _a = JSII_RTTI_SYMBOL_1; TokenInjectableDockerBuilder[_a] = { fqn: "token-injectable-docker-builder.TokenInjectableDockerBuilder", version: "1.5.19" }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxpQ0FBaUM7QUFDakMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUU3Qiw2Q0FBdUQ7QUFDdkQsNkRBQXdGO0FBRXhGLGlEQUFrRjtBQUNsRixpREFBcUQ7QUFDckQsaURBQXNEO0FBQ3RELGlEQUEwQztBQUMxQyx1REFBa0Y7QUFDbEYsNkRBQWtEO0FBQ2xELG1FQUF3RDtBQUN4RCwyQ0FBdUM7QUF1SHZDOzs7O0dBSUc7QUFDSCxNQUFhLDRCQUE2QixTQUFRLHNCQUFTO0lBa0J6RDs7Ozs7O09BTUc7SUFDSCxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQXdDO1FBQ2hGLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakIsTUFBTSxFQUNKLElBQUksRUFBRSxVQUFVLEVBQ2hCLFNBQVMsRUFDVCxvQkFBb0IsRUFDcEIsR0FBRyxFQUNILGNBQWMsRUFDZCxlQUFlLEVBQ2YsZUFBZSxFQUNmLGdCQUFnQixFQUNoQixhQUFhLEdBQUcsS0FBSyxFQUNyQix5QkFBeUIsRUFDekIsT0FBTyxHQUNSLEdBQUcsS0FBSyxDQUFDO1FBRVYsMENBQTBDO1FBQzFDLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUVyQyw4REFBOEQ7UUFDOUQsSUFBSSxhQUE4QixDQUFDO1FBQ25DLElBQUksYUFBYSxFQUFFLENBQUM7WUFDbEIsYUFBYSxHQUFHLElBQUksYUFBRyxDQUFDLElBQUksRUFBRSxrQkFBa0IsRUFBRTtnQkFDaEQsaUJBQWlCLEVBQUUsSUFBSTthQUN4QixDQUFDLENBQUM7UUFDTCxDQUFDO1FBRUQsNERBQTREO1FBQzVELElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxvQkFBVSxDQUFDLElBQUksRUFBRSxlQUFlLEVBQUU7WUFDekQsY0FBYyxFQUFFO2dCQUNk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Q0FBQyxXQUFXO2dCQUN6QyxRQUFRLEVBQUUsUUFBUTtnQkFDbEIsT0FBTyxFQUFFLFdBQVcsQ0FBQyxTQUFTO2FBQy9CO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gsb0JBQW9CLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBRTFELHlEQUF5RDtRQUN6RCxNQUFNLFdBQVcsR0FBRyxvQkFBb0IsQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEUsSUFBSSxDQUFDLGNBQWMsR0FBRyx3QkFBYyxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDeEYsSUFBSSxDQUFDLGVBQWUsR0FBRyw0QkFBZSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFO1lBQ2pFLFdBQVcsRUFBRSxXQUFXO1NBQ3pCLENBQUMsQ0FBQztJQUNMLENBQUM7O0FBcFFILG9FQXFRQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNyeXB0byBmcm9tICdjcnlwdG8nO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcblxuaW1wb3J0IHsgQ3VzdG9tUmVzb3VyY2UsIER1cmF0aW9uIH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgUHJvamVjdCwgU291cmNlLCBMaW51eEJ1aWxkSW1hZ2UsIEJ1aWxkU3BlYyB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1jb2RlYnVpbGQnO1xuaW1wb3J0IHsgSVZwYywgSVNlY3VyaXR5R3JvdXAsIFN1Ym5ldFNlbGVjdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lYzInO1xuaW1wb3J0IHsgUmVwb3NpdG9yeSwgUmVwb3NpdG9yeUVuY3J5cHRpb24sIFRhZ1N0YXR1cyB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lY3InO1xuaW1wb3J0IHsgQ29udGFpbmVySW1hZ2UgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWNzJztcbmltcG9ydCB7IFBvbGljeVN0YXRlbWVudCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0IHsgS2V5IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWttcyc7XG5pbXBvcnQgeyBSdW50aW1lLCBDb2RlLCBEb2NrZXJJbWFnZUNvZGUsIEZ1bmN0aW9uIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBBc3NldCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1zMy1hc3NldHMnO1xuaW1wb3J0IHsgUHJvdmlkZXIgfSBmcm9tICdhd3MtY2RrLWxpYi9jdXN0b20tcmVzb3VyY2VzJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIHRoZSBgVG9rZW5JbmplY3RhYmxlRG9ja2VyQnVpbGRlcmAgY29uc3RydWN0LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFRva2VuSW5qZWN0YWJsZURvY2tlckJ1aWxkZXJQcm9wcyB7XG4gIC8qKlxuICAgKiBUaGUgcGF0aCB0byB0aGUgZGlyZWN0b3J5IGNvbnRhaW5pbmcgdGhlIERvY2tlcmZpbGUgb3Igc291cmNlIGNvZGUuXG4gICAqL1xuICByZWFkb25seSBwYXRoOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEJ1aWxkIGFyZ3VtZW50cyB0byBwYXNzIHRvIHRoZSBEb2NrZXIgYnVpbGQgcHJvY2Vzcy5cbiAgICogVGhlc2UgYXJlIHRyYW5zZm9ybWVkIGludG8gYC0tYnVpbGQtYXJnIEtFWT1WQUxVRWAgZmxhZ3MuXG4gICAqIEBleGFtcGxlXG4gICAqIHtcbiAgICogICBUT0tFTjogJ215LXNlY3JldC10b2tlbicsXG4gICAqICAgRU5WOiAncHJvZHVjdGlvbidcbiAgICogfVxuICAgKi9cbiAgcmVhZG9ubHkgYnVpbGRBcmdzPzogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfTtcblxuICAvKipcbiAgICogVGhlIEFSTiBvZiB0aGUgQVdTIFNlY3JldHMgTWFuYWdlciBzZWNyZXQgY29udGFpbmluZyBEb2NrZXIgbG9naW4gY3JlZGVudGlhbHMuXG4gICAqIFRoaXMgc2VjcmV0IHNob3VsZCBzdG9yZSBhIEpTT04gb2JqZWN0IHdpdGggdGhlIGZvbGxvd2luZyBzdHJ1Y3R1cmU6XG4gICAqIGBgYGpzb25cbiAgICoge1xuICAgKiAgIFwidXNlcm5hbWVcIjogXCJteS1kb2NrZXItdXNlcm5hbWVcIixcbiAgICogICBcInBhc3N3b3JkXCI6IFwibXktZG9ja2VyLXBhc3N3b3JkXCJcbiAgICogfVxuICAgKiBgYGBcbiAgICogSWYgbm90IHByb3ZpZGVkIChvciBub3QgbmVlZGVkKSwgdGhlIGNvbnN0cnVjdCB3aWxsIHNraXAgRG9ja2VyIEh1YiBsb2dpbi5cbiAgICpcbiAgICogKipOb3RlKio6IFRoZSBzZWNyZXQgbXVzdCBiZSBpbiB0aGUgc2FtZSByZWdpb24gYXMgdGhlIHN0YWNrLlxuICAgKlxuICAgKiBAZXhhbXBsZSAnYXJuOmF3czpzZWNyZXRzbWFuYWdlcjp1cy1lYXN0LTE6MTIzNDU2Nzg5MDEyOnNlY3JldDpEb2NrZXJMb2dpblNlY3JldCdcbiAgICovXG4gIHJlYWRvbmx5IGRvY2tlckxvZ2luU2VjcmV0QXJuPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgVlBDIGluIHdoaWNoIHRoZSBDb2RlQnVpbGQgcHJvamVjdCB3aWxsIGJlIGRlcGxveWVkLlxuICAgKiBJZiBwcm92aWRlZCwgdGhlIENvZGVCdWlsZCBwcm9qZWN0IHdpbGwgYmUgbGF1bmNoZWQgd2l0aGluIHRoZSBzcGVjaWZpZWQgVlBDLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIE5vIFZQQyBpcyBhdHRhY2hlZCwgYW5kIHRoZSBDb2RlQnVpbGQgcHJvamVjdCB3aWxsIHVzZSBwdWJsaWMgaW50ZXJuZXQuXG4gICAqL1xuICByZWFkb25seSB2cGM/OiBJVnBjO1xuXG4gIC8qKlxuICAgKiBUaGUgc2VjdXJpdHkgZ3JvdXBzIHRvIGF0dGFjaCB0byB0aGUgQ29kZUJ1aWxkIHByb2plY3QuXG4gICAqIFRoZXNlIGRlZmluZSB0aGUgbmV0d29yayBhY2Nlc3MgcnVsZXMgZm9yIHRoZSBDb2RlQnVpbGQgcHJvamVjdC5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBObyBzZWN1cml0eSBncm91cHMgYXJlIGF0dGFjaGVkLlxuICAgKi9cbiAgcmVhZG9ubHkgc2VjdXJpdHlHcm91cHM/OiBJU2VjdXJpdHlHcm91cFtdO1xuXG4gIC8qKlxuICAgKiBUaGUgc3VibmV0IHNlbGVjdGlvbiB0byBzcGVjaWZ5IHdoaWNoIHN1Ym5ldHMgdG8gdXNlIHdpdGhpbiB0aGUgVlBDLlxuICAgKiBBbGxvd3MgdGhlIHVzZXIgdG8gc2VsZWN0IHByaXZhdGUsIHB1YmxpYywgb3IgaXNvbGF0ZWQgc3VibmV0cy5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBBbGwgc3VibmV0cyBpbiB0aGUgVlBDIGFyZSB1c2VkLlxuICAgKi9cbiAgcmVhZG9ubHkgc3VibmV0U2VsZWN0aW9uPzogU3VibmV0U2VsZWN0aW9uO1xuXG4gIC8qKlxuICAgKiBDdXN0b20gY29tbWFuZHMgdG8gcnVuIGR1cmluZyB0aGUgaW5zdGFsbCBwaGFzZSBvZiBDb2RlQnVpbGQuXG4gICAqXG4gICAqICoqRXhhbXBsZSoqOlxuICAgKiBgYGB0c1xuICAgKiBpbnN0YWxsQ29tbWFuZHM6IFtcbiAgICogICAnZWNobyBcIlVwZGF0aW5nIHBhY2thZ2UgbGlzdHMuLi5cIicsXG4gICAqICAgJ2FwdC1nZXQgdXBkYXRlIC15JyxcbiAgICogICAnZWNobyBcIkluc3RhbGxpbmcgcmVxdWlyZWQgcGFja2FnZXMuLi5cIicsXG4gICAqICAgJ2FwdC1nZXQgaW5zdGFsbCAteSBjdXJsIGRuc3V0aWxzJyxcbiAgICogXSxcbiAgICogYGBgXG4gICAqIEBkZWZhdWx0IC0gTm8gYWRkaXRpb25hbCBpbnN0YWxsIGNvbW1hbmRzLlxuICAgKi9cbiAgcmVhZG9ubHkgaW5zdGFsbENvbW1hbmRzPzogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIEN1c3RvbSBjb21tYW5kcyB0byBydW4gZHVyaW5nIHRoZSBwcmVfYnVpbGQgcGhhc2Ugb2YgQ29kZUJ1aWxkLlxuICAgKlxuICAgKiAqKkV4YW1wbGUqKjpcbiAgICogYGBgdHNcbiAgICogcHJlQnVpbGRDb21tYW5kczogW1xuICAgKiAgICdlY2hvIFwiRmV0Y2hpbmcgY29uZmlndXJhdGlvbiBmcm9tIHByaXZhdGUgQVBJLi4uXCInLFxuICAgKiAgICdjdXJsIC1vIGNvbmZpZy5qc29uIGh0dHBzOi8vYXBpLmV4YW1wbGUuY29tL2NvbmZpZycsXG4gICAqIF0sXG4gICAqIGBgYFxuICAgKiBAZGVmYXVsdCAtIE5vIGFkZGl0aW9uYWwgcHJlLWJ1aWxkIGNvbW1hbmRzLlxuICAgKi9cbiAgcmVhZG9ubHkgcHJlQnVpbGRDb21tYW5kcz86IHN0cmluZ1tdO1xuXG4gIC8qKlxuICAgKiBXaGV0aGVyIHRvIGVuYWJsZSBLTVMgZW5jcnlwdGlvbiBmb3IgdGhlIEVDUiByZXBvc2l0b3J5LlxuICAgKiBJZiBgdHJ1ZWAsIGEgS01TIGtleSB3aWxsIGJlIGNyZWF0ZWQgZm9yIGVuY3J5cHRpbmcgRUNSIGltYWdlcy5cbiAgICogSWYgYGZhbHNlYCwgdGhlIHJlcG9zaXRvcnkgd2lsbCB1c2UgQUVTLTI1NiBlbmNyeXB0aW9uLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGZhbHNlXG4gICAqL1xuICByZWFkb25seSBrbXNFbmNyeXB0aW9uPzogYm9vbGVhbjtcblxuICAvKipcbiAgICogVGhlIHF1ZXJ5IGludGVydmFsIGZvciBjaGVja2luZyBpZiB0aGUgQ29kZUJ1aWxkIHByb2plY3QgaGFzIGNvbXBsZXRlZC5cbiAgICogVGhpcyBkZXRlcm1pbmVzIGhvdyBmcmVxdWVudGx5IHRoZSBjdXN0b20gcmVzb3VyY2UgcG9sbHMgZm9yIGJ1aWxkIGNvbXBsZXRpb24uXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gRHVyYXRpb24uc2Vjb25kcygzMClcbiAgICovXG4gIHJlYWRvbmx5IGNvbXBsZXRlbmVzc1F1ZXJ5SW50ZXJ2YWw/OiBEdXJhdGlvbjtcblxuICAvKipcbiAgICogQSBsaXN0IG9mIGZpbGUgcGF0aHMgaW4gdGhlIERvY2tlciBkaXJlY3RvcnkgdG8gZXhjbHVkZSBmcm9tIGJ1aWxkLlxuICAgKiBXaWxsIHVzZSBwYXRocyBpbiAuZG9ja2VyaWdub3JlIGZpbGUgaWYgcHJlc2VudC5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBObyBmaWxlIHBhdGggZXhjbHVzaW9uc1xuICAgKi9cbiAgcmVhZG9ubHkgZXhjbHVkZT86IHN0cmluZ1tdO1xufVxuXG4vKipcbiAqIEEgQ0RLIGNvbnN0cnVjdCB0byBidWlsZCBhbmQgcHVzaCBEb2NrZXIgaW1hZ2VzIHRvIGFuIEVDUiByZXBvc2l0b3J5IHVzaW5nXG4gKiBDb2RlQnVpbGQgYW5kIExhbWJkYSBjdXN0b20gcmVzb3VyY2VzLCAqKnRoZW4qKiByZXRyaWV2ZSB0aGUgZmluYWwgaW1hZ2UgdGFnXG4gKiBzbyB0aGF0IEVDUy9MYW1iZGEgcmVmZXJlbmNlcyB1c2UgdGhlIGV4YWN0IGRpZ2VzdC5cbiAqL1xuZXhwb3J0IGNsYXNzIFRva2VuSW5qZWN0YWJsZURvY2tlckJ1aWxkZXIgZXh0ZW5kcyBDb25zdHJ1Y3Qge1xuICAvKipcbiAgICogVGhlIEVDUiByZXBvc2l0b3J5IHRoYXQgc3RvcmVzIHRoZSByZXN1bHRpbmcgRG9ja2VyIGltYWdlLlxuICAgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBlY3JSZXBvc2l0b3J5OiBSZXBvc2l0b3J5O1xuXG4gIC8qKlxuICAgKiBBbiBFQ1MtY29tcGF0aWJsZSBjb250YWluZXIgaW1hZ2UgcmVmZXJlbmNpbmcgdGhlIHRhZ1xuICAgKiBvZiB0aGUgYnVpbHQgRG9ja2VyIGltYWdlLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGNvbnRhaW5lckltYWdlOiBDb250YWluZXJJbWFnZTtcblxuICAvKipcbiAgICogQSBMYW1iZGEtY29tcGF0aWJsZSBEb2NrZXJJbWFnZUNvZGUgcmVmZXJlbmNpbmcgdGhlIHRhZ1xuICAgKiBvZiB0aGUgYnVpbHQgRG9ja2VyIGltYWdlLlxuICAgKi9cbiAgcHVibGljIHJlYWRvbmx5IGRvY2tlckltYWdlQ29kZTogRG9ja2VySW1hZ2VDb2RlO1xuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbmV3IGBUb2tlbkluamVjdGFibGVEb2NrZXJCdWlsZGVyYC5cbiAgICpcbiAgICogQHBhcmFtIHNjb3BlIFRoZSBzY29wZSBpbiB3aGljaCB0byBkZWZpbmUgdGhpcyBjb25zdHJ1Y3QuXG4gICAqIEBwYXJhbSBpZCBUaGUgc2NvcGVkIGNvbnN0cnVjdCBJRC5cbiAgICogQHBhcmFtIHByb3BzIENvbmZpZ3VyYXRpb24gZm9yIGJ1aWxkaW5nIGFuZCBwdXNoaW5nIHRoZSBEb2NrZXIgaW1hZ2UuXG4gICAqL1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogVG9rZW5JbmplY3RhYmxlRG9ja2VyQnVpbGRlclByb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKTtcblxuICAgIGNvbnN0IHtcbiAgICAgIHBhdGg6IHNvdXJjZVBhdGgsXG4gICAgICBidWlsZEFyZ3MsXG4gICAgICBkb2NrZXJMb2dpblNlY3JldEFybixcbiAgICAgIHZwYyxcbiAgICAgIHNlY3VyaXR5R3JvdXBzLFxuICAgICAgc3VibmV0U2VsZWN0aW9uLFxuICAgICAgaW5zdGFsbENvbW1hbmRzLFxuICAgICAgcHJlQnVpbGRDb21tYW5kcyxcbiAgICAgIGttc0VuY3J5cHRpb24gPSBmYWxzZSxcbiAgICAgIGNvbXBsZXRlbmVzc1F1ZXJ5SW50ZXJ2YWwsXG4gICAgICBleGNsdWRlLFxuICAgIH0gPSBwcm9wcztcblxuICAgIC8vIEdlbmVyYXRlIGFuIGVwaGVtZXJhbCB0YWcgZm9yIENvZGVCdWlsZFxuICAgIGNvbnN0IGltYWdlVGFnID0gY3J5cHRvLnJhbmRvbVVVSUQoKTtcblxuICAgIC8vIE9wdGlvbmFsbHkgZGVmaW5lIGEgS01TIGtleSBmb3IgRUNSIGVuY3J5cHRpb24gaWYgcmVxdWVzdGVkXG4gICAgbGV0IGVuY3J5cHRpb25LZXk6IEtleSB8IHVuZGVmaW5lZDtcbiAgICBpZiAoa21zRW5jcnlwdGlvbikge1xuICAgICAgZW5jcnlwdGlvbktleSA9IG5ldyBLZXkodGhpcywgJ0VjckVuY3J5cHRpb25LZXknLCB7XG4gICAgICAgIGVuYWJsZUtleVJvdGF0aW9uOiB0cnVlLFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gQ3JlYXRlIGFuIEVDUiByZXBvc2l0b3J5IChvcHRpb25hbGx5IHdpdGggS01TIGVuY3J5cHRpb24pXG4gICAgdGhpcy5lY3JSZXBvc2l0b3J5ID0gbmV3IFJlcG9zaXRvcnkodGhpcywgJ0VDUlJlcG9zaXRvcnknLCB7XG4gICAgICBsaWZlY3ljbGVSdWxlczogW1xuICAgICAgICB7XG4gICAgICAgICAgcnVsZVByaW9yaXR5OiAxLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiAnUmVtb3ZlIHVudGFnZ2VkIGltYWdlcyBhZnRlciAxIGRheScsXG4gICAgICAgICAgdGFnU3RhdHVzOiBUYWdTdGF0dXMuVU5UQUdHRUQsXG4gICAgICAgICAgbWF4SW1hZ2VBZ2U6IER1cmF0aW9uLmRheXMoMSksXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgICAgZW5jcnlwdGlvbjoga21zRW5jcnlwdGlvbiA/IFJlcG9zaXRvcnlFbmNyeXB0aW9uLktNUyA6IFJlcG9zaXRvcnlFbmNyeXB0aW9uLkFFU18yNTYsXG4gICAgICBlbmNyeXB0aW9uS2V5OiBrbXNFbmNyeXB0aW9uID8gZW5jcnlwdGlvbktleSA6IHVuZGVmaW5lZCxcbiAgICAgIGltYWdlU2Nhbk9uUHVzaDogdHJ1ZSxcbiAgICB9KTtcblxuICAgIGxldCBlZmZlY3RpdmVFeGNsdWRlID0gZXhjbHVkZTtcbiAgICBpZiAoIWVmZmVjdGl2ZUV4Y2x1ZGUpIHtcbiAgICAgIGNvbnN0IGRvY2tlcmlnbm9yZVBhdGggPSBwYXRoLmpvaW4oc291cmNlUGF0aCwgJy5kb2NrZXJpZ25vcmUnKTtcbiAgICAgIGlmIChmcy5leGlzdHNTeW5jKGRvY2tlcmlnbm9yZVBhdGgpKSB7XG4gICAgICAgIGNvbnN0IGZpbGVDb250ZW50ID0gZnMucmVhZEZpbGVTeW5jKGRvY2tlcmlnbm9yZVBhdGgsICd1dGY4Jyk7XG4gICAgICAgIGVmZmVjdGl2ZUV4Y2x1ZGUgPSBmaWxlQ29udGVudFxuICAgICAgICAgIC5zcGxpdCgnXFxuJylcbiAgICAgICAgICAubWFwKChsaW5lOiBzdHJpbmcpID0+IGxpbmUudHJpbSgpKVxuICAgICAgICAgIC5maWx0ZXIoKGxpbmU6IHN0cmluZykgPT4gbGluZS5sZW5ndGggPiAwICYmICFsaW5lLnN0YXJ0c1dpdGgoJyMnKSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRW5zdXJlIERvY2tlcmZpbGUgaXMgbmV2ZXIgZXhjbHVkZWRcbiAgICBpZiAoZWZmZWN0aXZlRXhjbHVkZSkge1xuICAgICAgZWZmZWN0aXZlRXhjbHVkZSA9IGVmZmVjdGl2ZUV4Y2x1ZGUuZmlsdGVyKFxuICAgICAgICAocGF0dGVybjogc3RyaW5nKSA9PiBwYXR0ZXJuLnRvTG93ZXJDYXNlKCkgIT09ICdkb2NrZXJmaWxlJyxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gV3JhcCB0aGUgc291cmNlIGZvbGRlciBhcyBhbiBTMyBhc3NldCBmb3IgQ29kZUJ1aWxkIHRvIHVzZVxuICAgIGNvbnN0IHNvdXJjZUFzc2V0ID0gbmV3IEFzc2V0KHRoaXMsICdTb3VyY2VBc3NldCcsIHtcbiAgICAgIHBhdGg6IHNvdXJjZVBhdGgsXG4gICAgICBleGNsdWRlOiBlZmZlY3RpdmVFeGNsdWRlLFxuXG4gICAgfSk7XG5cbiAgICAvLyBDb252ZXJ0IGJ1aWxkQXJncyB0byBhIENMSS1mcmllbmRseSBzdHJpbmdcbiAgICBjb25zdCBidWlsZEFyZ3NTdHJpbmcgPSBidWlsZEFyZ3NcbiAgICAgID8gT2JqZWN0LmVudHJpZXMoYnVpbGRBcmdzKVxuICAgICAgICAubWFwKChbaywgdl0pID0+IGAtLWJ1aWxkLWFyZyAke2t9PSR7dn1gKVxuICAgICAgICAuam9pbignICcpXG4gICAgICA6ICcnO1xuXG4gICAgLy8gT3B0aW9uYWwgRG9ja2VySHViIGxvZ2luLCBpZiBhIHNlY3JldCBBUk4gaXMgcHJvdmlkZWRcbiAgICBjb25zdCBkb2NrZXJMb2dpbkNvbW1hbmRzID0gZG9ja2VyTG9naW5TZWNyZXRBcm5cbiAgICAgID8gW1xuICAgICAgICAnZWNobyBcIlJldHJpZXZpbmcgRG9ja2VyIGNyZWRlbnRpYWxzLi4uXCInLFxuICAgICAgICAnYXB0LWdldCB1cGRhdGUgLXkgJiYgYXB0LWdldCBpbnN0YWxsIC15IGpxJyxcbiAgICAgICAgYERPQ0tFUl9VU0VSTkFNRT0kKGF3cyBzZWNyZXRzbWFuYWdlciBnZXQtc2VjcmV0LXZhbHVlIC0tc2VjcmV0LWlkICR7ZG9ja2VyTG9naW5TZWNyZXRBcm59IC0tcXVlcnkgU2VjcmV0U3RyaW5nIC0tb3V0cHV0IHRleHQgfCBqcSAtciAudXNlcm5hbWUpYCxcbiAgICAgICAgYERPQ0tFUl9QQVNTV09SRD0kKGF3cyBzZWNyZXRzbWFuYWdlciBnZXQtc2VjcmV0LXZhbHVlIC0tc2VjcmV0LWlkICR7ZG9ja2VyTG9naW5TZWNyZXRBcm59IC0tcXVlcnkgU2VjcmV0U3RyaW5nIC0tb3V0cHV0IHRleHQgfCBqcSAtciAucGFzc3dvcmQpYCxcbiAgICAgICAgJ2VjaG8gXCJMb2dnaW5nIGluIHRvIERvY2tlciBIdWIuLi5cIicsXG4gICAgICAgICdlY2hvICRET0NLRVJfUEFTU1dPUkQgfCBkb2NrZXIgbG9naW4gLS11c2VybmFtZSAkRE9DS0VSX1VTRVJOQU1FIC0tcGFzc3dvcmQtc3RkaW4nLFxuICAgICAgXVxuICAgICAgOiBbJ2VjaG8gXCJObyBEb2NrZXIgY3JlZGVudGlhbHMuIFNraXBwaW5nIERvY2tlciBIdWIgbG9naW4uXCInXTtcblxuICAgIGNvbnN0IGJ1aWxkU3BlY09iaiA9IHtcbiAgICAgIHZlcnNpb246ICcwLjInLFxuICAgICAgcGhhc2VzOiB7XG4gICAgICAgIGluc3RhbGw6IHtcbiAgICAgICAgICBjb21tYW5kczogW1xuICAgICAgICAgICAgJ2VjaG8gXCJCZWdpbm5pbmcgaW5zdGFsbCBwaGFzZS4uLlwiJyxcbiAgICAgICAgICAgIC4uLihpbnN0YWxsQ29tbWFuZHMgPz8gW10pLFxuICAgICAgICAgIF0sXG4gICAgICAgIH0sXG4gICAgICAgIHByZV9idWlsZDoge1xuICAgICAgICAgIGNvbW1hbmRzOiBbXG4gICAgICAgICAgICAuLi4ocHJlQnVpbGRDb21tYW5kcyA/PyBbXSksXG4gICAgICAgICAgICAuLi5kb2NrZXJMb2dpbkNvbW1hbmRzLFxuICAgICAgICAgICAgJ2VjaG8gXCJSZXRyaWV2aW5nIEFXUyBBY2NvdW50IElELi4uXCInLFxuICAgICAgICAgICAgJ2V4cG9ydCBBQ0NPVU5UX0lEPSQoYXdzIHN0cyBnZXQtY2FsbGVyLWlkZW50aXR5IC0tcXVlcnkgQWNjb3VudCAtLW91dHB1dCB0ZXh0KScsXG4gICAgICAgICAgICAnZWNobyBcIkxvZ2dpbmcgaW50byBBbWF6b24gRUNSLi4uXCInLFxuICAgICAgICAgICAgJ2F3cyBlY3IgZ2V0LWxvZ2luLXBhc3N3b3JkIC0tcmVnaW9uICRBV1NfREVGQVVMVF9SRUdJT04gfCBkb2NrZXIgbG9naW4gLS11c2VybmFtZSBBV1MgLS1wYXNzd29yZC1zdGRpbiAkQUNDT1VOVF9JRC5ka3IuZWNyLiRBV1NfREVGQVVMVF9SRUdJT04uYW1hem9uYXdzLmNvbScsXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgICAgYnVpbGQ6IHtcbiAgICAgICAgICBjb21tYW5kczogW1xuICAgICAgICAgICAgYGVjaG8gXCJCdWlsZGluZyBEb2NrZXIgaW1hZ2Ugd2l0aCB0YWcgJHtpbWFnZVRhZ30uLi5cImAsXG4gICAgICAgICAgICBgZG9ja2VyIGJ1aWxkICR7YnVpbGRBcmdzU3RyaW5nfSAtdCAkRUNSX1JFUE9fVVJJOiR7aW1hZ2VUYWd9ICRDT0RFQlVJTERfU1JDX0RJUmAsXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgICAgcG9zdF9idWlsZDoge1xuICAgICAgICAgIGNvbW1hbmRzOiBbXG4gICAgICAgICAgICBgZWNobyBcIlB1c2hpbmcgRG9ja2VyIGltYWdlIHdpdGggdGFnICR7aW1hZ2VUYWd9Li4uXCJgLFxuICAgICAgICAgICAgYGRvY2tlciBwdXNoICRFQ1JfUkVQT19VUkk6JHtpbWFnZVRhZ31gLFxuICAgICAgICAgIF0sXG4gICAgICAgIH0sXG4gICAgICB9LFxuICAgIH07XG5cbiAgICAvLyBDcmVhdGUgdGhlIENvZGVCdWlsZCBwcm9qZWN0XG4gICAgY29uc3QgY29kZUJ1aWxkUHJvamVjdCA9IG5ldyBQcm9qZWN0KHRoaXMsICdDb2RlQnVpbGRQcm9qZWN0Jywge1xuICAgICAgc291cmNlOiBTb3VyY2UuczMoe1xuICAgICAgICBidWNrZXQ6IHNvdXJjZUFzc2V0LmJ1Y2tldCxcbiAgICAgICAgcGF0aDogc291cmNlQXNzZXQuczNPYmplY3RLZXksXG4gICAgICB9KSxcbiAgICAgIGVudmlyb25tZW50OiB7XG4gICAgICAgIGJ1aWxkSW1hZ2U6IExpbnV4QnVpbGRJbWFnZS5TVEFOREFSRF83XzAsXG4gICAgICAgIHByaXZpbGVnZWQ6IHRydWUsXG4gICAgICB9LFxuICAgICAgZW52aXJvbm1lbnRWYXJpYWJsZXM6IHtcbiAgICAgICAgRUNSX1JFUE9fVVJJOiB7IHZhbHVlOiB0aGlzLmVjclJlcG9zaXRvcnkucmVwb3NpdG9yeVVyaSB9LFxuICAgICAgfSxcbiAgICAgIGJ1aWxkU3BlYzogQnVpbGRTcGVjLmZyb21PYmplY3QoYnVpbGRTcGVjT2JqKSxcbiAgICAgIHZwYyxcbiAgICAgIHNlY3VyaXR5R3JvdXBzLFxuICAgICAgc3VibmV0U2VsZWN0aW9uLFxuICAgIH0pO1xuXG4gICAgLy8gR3JhbnQgQ29kZUJ1aWxkIHRoZSBhYmlsaXR5IHRvIGludGVyYWN0IHdpdGggRUNSXG4gICAgdGhpcy5lY3JSZXBvc2l0b3J5LmdyYW50UHVsbFB1c2goY29kZUJ1aWxkUHJvamVjdCk7XG4gICAgY29kZUJ1aWxkUHJvamVjdC5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgYWN0aW9uczogW1xuICAgICAgICAgICdlY3I6R2V0QXV0aG9yaXphdGlvblRva2VuJyxcbiAgICAgICAgICAnZWNyOkdldERvd25sb2FkVXJsRm9yTGF5ZXInLFxuICAgICAgICAgICdlY3I6QmF0Y2hDaGVja0xheWVyQXZhaWxhYmlsaXR5JyxcbiAgICAgICAgXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgIH0pLFxuICAgICk7XG4gICAgaWYgKGRvY2tlckxvZ2luU2VjcmV0QXJuKSB7XG4gICAgICBjb2RlQnVpbGRQcm9qZWN0LmFkZFRvUm9sZVBvbGljeShcbiAgICAgICAgbmV3IFBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgICAgYWN0aW9uczogWydzZWNyZXRzbWFuYWdlcjpHZXRTZWNyZXRWYWx1ZSddLFxuICAgICAgICAgIHJlc291cmNlczogW2RvY2tlckxvZ2luU2VjcmV0QXJuXSxcbiAgICAgICAgfSksXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIENvbmRpdGlvbmFsbHkgZ3JhbnQgS01TIGVuY3J5cHQvZGVjcnlwdCBpZiBhIGtleSBpcyB1c2VkXG4gICAgaWYgKGVuY3J5cHRpb25LZXkpIHtcbiAgICAgIGVuY3J5cHRpb25LZXkuZ3JhbnRFbmNyeXB0RGVjcnlwdChjb2RlQnVpbGRQcm9qZWN0LnJvbGUhKTtcbiAgICB9XG5cbiAgICAvLyBEZWZpbmUgTGFtYmRhIGZ1bmN0aW9ucyBmb3IgY3VzdG9tIHJlc291cmNlIGV2ZW50IGFuZCBjb21wbGV0aW9uIGhhbmRsaW5nXG4gICAgY29uc3Qgb25FdmVudEhhbmRsZXJGdW5jdGlvbiA9IG5ldyBGdW5jdGlvbih0aGlzLCAnT25FdmVudEhhbmRsZXJGdW5jdGlvbicsIHtcbiAgICAgIHJ1bnRpbWU6IFJ1bnRpbWUuTk9ERUpTXzE4X1gsXG4gICAgICBjb2RlOiBDb2RlLmZyb21Bc3NldChwYXRoLnJlc29sdmUoX19kaXJuYW1lLCAnLi4vb25FdmVudCcpKSxcbiAgICAgIGhhbmRsZXI6ICdvbkV2ZW50LmhhbmRsZXInLFxuICAgICAgdGltZW91dDogRHVyYXRpb24ubWludXRlcygxNSksXG4gICAgfSk7XG4gICAgb25FdmVudEhhbmRsZXJGdW5jdGlvbi5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgYWN0aW9uczogWydjb2RlYnVpbGQ6U3RhcnRCdWlsZCddLFxuICAgICAgICByZXNvdXJjZXM6IFtjb2RlQnVpbGRQcm9qZWN0LnByb2plY3RBcm5dLFxuICAgICAgfSksXG4gICAgKTtcblxuICAgIGNvbnN0IGlzQ29tcGxldGVIYW5kbGVyRnVuY3Rpb24gPSBuZXcgRnVuY3Rpb24odGhpcywgJ0lzQ29tcGxldGVIYW5kbGVyRnVuY3Rpb24nLCB7XG4gICAgICBydW50aW1lOiBSdW50aW1lLk5PREVKU18xOF9YLFxuICAgICAgY29kZTogQ29kZS5mcm9tQXNzZXQocGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJy4uL2lzQ29tcGxldGUnKSksXG4gICAgICBlbnZpcm9ubWVudDoge1xuICAgICAgICBJTUFHRV9UQUc6IGltYWdlVGFnLFxuICAgICAgfSxcbiAgICAgIGhhbmRsZXI6ICdpc0NvbXBsZXRlLmhhbmRsZXInLFxuICAgICAgdGltZW91dDogRHVyYXRpb24ubWludXRlcygxNSksXG4gICAgfSk7XG4gICAgaXNDb21wbGV0ZUhhbmRsZXJGdW5jdGlvbi5hZGRUb1JvbGVQb2xpY3koXG4gICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgYWN0aW9uczogW1xuICAgICAgICAgICdjb2RlYnVpbGQ6QmF0Y2hHZXRCdWlsZHMnLFxuICAgICAgICAgICdjb2RlYnVpbGQ6TGlzdEJ1aWxkc0ZvclByb2plY3QnLFxuICAgICAgICAgICdsb2dzOkdldExvZ0V2ZW50cycsXG4gICAgICAgICAgJ2xvZ3M6RGVzY3JpYmVMb2dTdHJlYW1zJyxcbiAgICAgICAgICAnbG9nczpEZXNjcmliZUxvZ0dyb3VwcycsXG4gICAgICAgIF0sXG4gICAgICAgIHJlc291cmNlczogWycqJ10sXG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgLy8gQ29uZGl0aW9uYWxseSBhbGxvdyBlbmNyeXB0aW9uIGlmIGEga2V5IGlzIHVzZWRcbiAgICBpZiAoZW5jcnlwdGlvbktleSkge1xuICAgICAgZW5jcnlwdGlvbktleS5ncmFudEVuY3J5cHREZWNyeXB0KG9uRXZlbnRIYW5kbGVyRnVuY3Rpb24pO1xuICAgICAgZW5jcnlwdGlvbktleS5ncmFudEVuY3J5cHREZWNyeXB0KGlzQ29tcGxldGVIYW5kbGVyRnVuY3Rpb24pO1xuICAgIH1cbiAgICB0aGlzLmVjclJlcG9zaXRvcnkuZ3JhbnRQdWxsUHVzaChvbkV2ZW50SGFuZGxlckZ1bmN0aW9uKTtcbiAgICB0aGlzLmVjclJlcG9zaXRvcnkuZ3JhbnRQdWxsUHVzaChpc0NvbXBsZXRlSGFuZGxlckZ1bmN0aW9uKTtcblxuICAgIC8vIENyZWF0ZSBhIGN1c3RvbSByZXNvdXJjZSBwcm92aWRlciB0aGF0IHVzZXMgdGhlIGFib3ZlIExhbWJkYXNcbiAgICBjb25zdCBwcm92aWRlciA9IG5ldyBQcm92aWRlcih0aGlzLCAnQ3VzdG9tUmVzb3VyY2VQcm92aWRlcicsIHtcbiAgICAgIG9uRXZlbnRIYW5kbGVyOiBvbkV2ZW50SGFuZGxlckZ1bmN0aW9uLFxuICAgICAgaXNDb21wbGV0ZUhhbmRsZXI6IGlzQ29tcGxldGVIYW5kbGVyRnVuY3Rpb24sXG4gICAgICBxdWVyeUludGVydmFsOiBjb21wbGV0ZW5lc3NRdWVyeUludGVydmFsID8/IER1cmF0aW9uLnNlY29uZHMoMzApLFxuICAgIH0pO1xuXG4gICAgLy8gQ3VzdG9tIFJlc291cmNlIHRoYXQgdHJpZ2dlcnMgdGhlIENvZGVCdWlsZCBhbmQgd2FpdHMgZm9yIGNvbXBsZXRpb25cbiAgICBjb25zdCBidWlsZFRyaWdnZXJSZXNvdXJjZSA9IG5ldyBDdXN0b21SZXNvdXJjZSh0aGlzLCAnQnVpbGRUcmlnZ2VyUmVzb3VyY2UnLCB7XG4gICAgICBzZXJ2aWNlVG9rZW46IHByb3ZpZGVyLnNlcnZpY2VUb2tlbixcbiAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgUHJvamVjdE5hbWU6IGNvZGVCdWlsZFByb2plY3QucHJvamVjdE5hbWUsXG4gICAgICAgIEltYWdlVGFnOiBpbWFnZVRhZyxcbiAgICAgICAgVHJpZ2dlcjogc291cmNlQXNzZXQuYXNzZXRIYXNoLFxuICAgICAgfSxcbiAgICB9KTtcbiAgICBidWlsZFRyaWdnZXJSZXNvdXJjZS5ub2RlLmFkZERlcGVuZGVuY3koY29kZUJ1aWxkUHJvamVjdCk7XG5cbiAgICAvLyBSZXRyaWV2ZSB0aGUgZmluYWwgRG9ja2VyIGltYWdlIHRhZyBmcm9tIERhdGEuSW1hZ2VUYWdcbiAgICBjb25zdCBpbWFnZVRhZ1JlZiA9IGJ1aWxkVHJpZ2dlclJlc291cmNlLmdldEF0dFN0cmluZygnSW1hZ2VUYWcnKTtcbiAgICB0aGlzLmNvbnRhaW5lckltYWdlID0gQ29udGFpbmVySW1hZ2UuZnJvbUVjclJlcG9zaXRvcnkodGhpcy5lY3JSZXBvc2l0b3J5LCBpbWFnZVRhZ1JlZik7XG4gICAgdGhpcy5kb2NrZXJJbWFnZUNvZGUgPSBEb2NrZXJJbWFnZUNvZGUuZnJvbUVjcih0aGlzLmVjclJlcG9zaXRvcnksIHtcbiAgICAgIHRhZ09yRGlnZXN0OiBpbWFnZVRhZ1JlZixcbiAgICB9KTtcbiAgfVxufVxuIl19