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

234 lines 37.8 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_1 = require("aws-cdk-lib/aws-s3"); 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('#')); } } // 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, }); // Create an S3 bucket to store the CodeBuild artifacts const artifactBucket = new aws_s3_1.Bucket(this, 'ArtifactBucket', { removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY, autoDeleteObjects: true, blockPublicAccess: aws_s3_1.BlockPublicAccess.BLOCK_ALL, }); // 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: ['*'], })); artifactBucket.grantReadWrite(isCompleteHandlerFunction); // 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.8" }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSxpQ0FBaUM7QUFDakMseUJBQXlCO0FBQ3pCLDZCQUE2QjtBQUU3Qiw2Q0FBc0U7QUFDdEUsNkRBQXdGO0FBRXhGLGlEQUFrRjtBQUNsRixpREFBcUQ7QUFDckQsaURBQXNEO0FBQ3RELGlEQUEwQztBQUMxQyx1REFBa0Y7QUFDbEYsK0NBQStEO0FBQy9ELDZEQUFrRDtBQUNsRCxtRUFBd0Q7QUFDeEQsMkNBQXVDO0FBdUh2Qzs7OztHQUlHO0FBQ0gsTUFBYSw0QkFBNkIsU0FBUSxzQkFBUztJQWtCekQ7Ozs7OztPQU1HO0lBQ0gsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUF3QztRQUNoRixLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRWpCLE1BQU0sRUFDSixJQUFJLEVBQUUsVUFBVSxFQUNoQixTQUFTLEVBQ1Qsb0JBQW9CLEVBQ3BCLEdBQUcsRUFDSCxjQUFjLEVBQ2QsZUFBZSxFQUNmLGVBQWUsRUFDZixnQkFBZ0IsRUFDaEIsYUFBYSxHQUFHLEtBQUssRUFDckIseUJBQXlCLEVBQ3pCLE9BQU8sR0FDUixHQUFHLEtBQUssQ0FBQztRQUVWLDBDQUEwQztRQUMxQyxNQUFNLFFBQVEsR0FBRyxNQUFNLENBQUMsVUFBVSxFQUFFLENBQUM7UUFFckMsOERBQThEO1FBQzlELElBQUksYUFBOEIsQ0FBQztRQUNuQyxJQUFJLGFBQWEsRUFBRSxDQUFDO1lBQ2xCLGFBQWEsR0FBRyxJQUFJLGFBQUcsQ0FBQyxJQUFJLEVBQUUsa0JBQWtCLEVBQUU7Z0JBQ2hELGlCQUFpQixFQUFFLElBQUk7YUFDeEIsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUVELDREQUE0RDtRQUM1RCxJQUFJLENBQUMsYUFBYSxHQUFHLElBQUksb0JBQVUsQ0FBQyxJQUFJLEVBQUUsZUFBZSxFQUFFO1lBQ3pELGNBQWMsRUFBRTtnQkFDZDtvQkFDRSxZQUFZLEVBQUUsQ0FBQztvQkFDZixXQUFXLEVBQUUsb0NBQW9DO29CQUNqRCxTQUFTLEVBQUUsbUJBQVMsQ0FBQyxRQUFRO29CQUM3QixXQUFXLEVBQUUsc0JBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2lCQUM5QjthQUNGO1lBQ0QsVUFBVSxFQUFFLGFBQWEsQ0FBQyxDQUFDLENBQUMsOEJBQW9CLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyw4QkFBb0IsQ0FBQyxPQUFPO1lBQ25GLGFBQWEsRUFBRSxhQUFhLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsU0FBUztZQUN4RCxlQUFlLEVBQUUsSUFBSTtTQUN0QixDQUFDLENBQUM7UUFFSCxJQUFJLGdCQUFnQixHQUFHLE9BQU8sQ0FBQztRQUMvQixJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztZQUN0QixNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLGVBQWUsQ0FBQyxDQUFDO1lBQ2hFLElBQUksRUFBRSxDQUFDLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLENBQUM7Z0JBQ3BDLE1BQU0sV0FBVyxHQUFHLEVBQUUsQ0FBQyxZQUFZLENBQUMsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLENBQUM7Z0JBQzlELGdCQUFnQixHQUFHLFdBQVc7cUJBQzNCLEtBQUssQ0FBQyxJQUFJLENBQUM7cUJBQ1gsR0FBRyxDQUFDLENBQUMsSUFBWSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7cUJBQ2xDLE1BQU0sQ0FBQyxDQUFDLElBQVksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDeEUsQ0FBQztRQUNILENBQUM7UUFFRCw2REFBNkQ7UUFDN0QsTUFBTSxXQUFXLEdBQUcsSUFBSSxxQkFBSyxDQUFDLElBQUksRUFBRSxhQUFhLEVBQUU7WUFDakQsSUFBSSxFQUFFLFVBQVU7WUFDaEIsT0FBTyxFQUFFLGdCQUFnQjtTQUMxQixDQUFDLENBQUM7UUFFSCx1REFBdUQ7UUFDdkQsTUFBTSxjQUFjLEdBQUcsSUFBSSxlQUFNLENBQUMsSUFBSSxFQUFFLGdCQUFnQixFQUFFO1lBQ3hELGFBQWEsRUFBRSwyQkFBYSxDQUFDLE9BQU87WUFDcEMsaUJBQWlCLEVBQUUsSUFBSTtZQUN2QixpQkFBaUIsRUFBRSwwQkFBaUIsQ0FBQyxTQUFTO1NBQy9DLENBQUMsQ0FBQztRQUVILDZDQUE2QztRQUM3QyxNQUFNLGVBQWUsR0FBRyxTQUFTO1lBQy9CLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQztpQkFDeEIsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO2lCQUN4QyxJQUFJLENBQUMsR0FBRyxDQUFDO1lBQ1osQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUVQLHdEQUF3RDtRQUN4RCxNQUFNLG1CQUFtQixHQUFHLG9CQUFvQjtZQUM5QyxDQUFDLENBQUM7Z0JBQ0EseUNBQXlDO2dCQUN6Qyw0Q0FBNEM7Z0JBQzVDLHFFQUFxRSxvQkFBb0Isd0RBQXdEO2dCQUNqSixxRUFBcUUsb0JBQW9CLHdEQUF3RDtnQkFDakosb0NBQW9DO2dCQUNwQyxtRkFBbUY7YUFDcEY7WUFDRCxDQUFDLENBQUMsQ0FBQywwREFBMEQsQ0FBQyxDQUFDO1FBRWpFLE1BQU0sWUFBWSxHQUFHO1lBQ25CLE9BQU8sRUFBRSxLQUFLO1lBQ2QsTUFBTSxFQUFFO2dCQUNOLE9BQU8sRUFBRTtvQkFDUCxRQUFRLEVBQUU7d0JBQ1IsbUNBQW1DO3dCQUNuQyxHQUFHLENBQUMsZUFBZSxJQUFJLEVBQUUsQ0FBQztxQkFDM0I7aUJBQ0Y7Z0JBQ0QsU0FBUyxFQUFFO29CQUNULFFBQVEsRUFBRTt3QkFDUixHQUFHLENBQUMsZ0JBQWdCLElBQUksRUFBRSxDQUFDO3dCQUMzQixHQUFHLG1CQUFtQjt3QkFDdEIscUNBQXFDO3dCQUNyQyxnRkFBZ0Y7d0JBQ2hGLG1DQUFtQzt3QkFDbkMsOEpBQThKO3FCQUMvSjtpQkFDRjtnQkFDRCxLQUFLLEVBQUU7b0JBQ0wsUUFBUSxFQUFFO3dCQUNSLHdDQUF3QyxRQUFRLE1BQU07d0JBQ3RELGdCQUFnQixlQUFlLHFCQUFxQixRQUFRLHFCQUFxQjtxQkFDbEY7aUJBQ0Y7Z0JBQ0QsVUFBVSxFQUFFO29CQUNWLFFBQVEsRUFBRTt3QkFDUix1Q0FBdUMsUUFBUSxNQUFNO3dCQUNyRCw2QkFBNkIsUUFBUSxFQUFFO3FCQUN4QztpQkFDRjthQUNGO1NBQ0YsQ0FBQztRQUVGLCtCQUErQjtRQUMvQixNQUFNLGdCQUFnQixHQUFHLElBQUksdUJBQU8sQ0FBQyxJQUFJLEVBQUUsa0JBQWtCLEVBQUU7WUFDN0QsTUFBTSxFQUFFLHNCQUFNLENBQUMsRUFBRSxDQUFDO2dCQUNoQixNQUFNLEVBQUUsV0FBVyxDQUFDLE1BQU07Z0JBQzFCLElBQUksRUFBRSxXQUFXLENBQUMsV0FBVzthQUM5QixDQUFDO1lBQ0YsV0FBVyxFQUFFO2dCQUNYLFVBQVUsRUFBRSwrQkFBZSxDQUFDLFlBQVk7Z0JBQ3hDLFVBQVUsRUFBRSxJQUFJO2FBQ2pCO1lBQ0Qsb0JBQW9CLEVBQUU7Z0JBQ3BCLFlBQVksRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsYUFBYSxDQUFDLGFBQWEsRUFBRTthQUMxRDtZQUNELFNBQVMsRUFBRSx5QkFBUyxDQUFDLFVBQVUsQ0FBQyxZQUFZLENBQUM7WUFDN0MsR0FBRztZQUNILGNBQWM7WUFDZCxlQUFlO1NBQ2hCLENBQUMsQ0FBQztRQUVILG1EQUFtRDtRQUNuRCxJQUFJLENBQUMsYUFBYSxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ25ELGdCQUFnQixDQUFDLGVBQWUsQ0FDOUIsSUFBSSx5QkFBZSxDQUFDO1lBQ2xCLE9BQU8sRUFBRTtnQkFDUCwyQkFBMkI7Z0JBQzNCLDRCQUE0QjtnQkFDNUIsaUNBQWlDO2FBQ2xDO1lBQ0QsU0FBUyxFQUFFLENBQUMsR0FBRyxDQUFDO1NBQ2pCLENBQUMsQ0FDSCxDQUFDO1FBQ0YsSUFBSSxvQkFBb0IsRUFBRSxDQUFDO1lBQ3pCLGdCQUFnQixDQUFDLGVBQWUsQ0FDOUIsSUFBSSx5QkFBZSxDQUFDO2dCQUNsQixPQUFPLEVBQUUsQ0FBQywrQkFBK0IsQ0FBQztnQkFDMUMsU0FBUyxFQUFFLENBQUMsb0JBQW9CLENBQUM7YUFDbEMsQ0FBQyxDQUNILENBQUM7UUFDSixDQUFDO1FBRUQsMkRBQTJEO1FBQzNELElBQUksYUFBYSxFQUFFLENBQUM7WUFDbEIsYUFBYSxDQUFDLG1CQUFtQixDQUFDLGdCQUFnQixDQUFDLElBQUssQ0FBQyxDQUFDO1FBQzVELENBQUM7UUFFRCw0RUFBNEU7UUFDNUUsTUFBTSxzQkFBc0IsR0FBRyxJQUFJLHFCQUFRLENBQUMsSUFBSSxFQUFFLHdCQUF3QixFQUFFO1lBQzFFLE9BQU8sRUFBRSxvQkFBTyxDQUFDLFdBQVc7WUFDNUIsSUFBSSxFQUFFLGlCQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLFlBQVksQ0FBQyxDQUFDO1lBQzNELE9BQU8sRUFBRSxpQkFBaUI7WUFDMUIsT0FBTyxFQUFFLHNCQUFRLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztTQUM5QixDQUFDLENBQUM7UUFDSCxzQkFBc0IsQ0FBQyxlQUFlLENBQ3BDLElBQUkseUJBQWUsQ0FBQztZQUNsQixPQUFPLEVBQUUsQ0FBQyxzQkFBc0IsQ0FBQztZQUNqQyxTQUFTLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQyxVQUFVLENBQUM7U0FDekMsQ0FBQyxDQUNILENBQUM7UUFFRixNQUFNLHlCQUF5QixHQUFHLElBQUkscUJBQVEsQ0FBQyxJQUFJLEVBQUUsMkJBQTJCLEVBQUU7WUFDaEYsT0FBTyxFQUFFLG9CQUFPLENBQUMsV0FBVztZQUM1QixJQUFJLEVBQUUsaUJBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsZUFBZSxDQUFDLENBQUM7WUFDOUQsV0FBVyxFQUFFO2dCQUNYLFNBQVMsRUFBRSxRQUFRO2FBQ3BCO1lBQ0QsT0FBTyxFQUFFLG9CQUFvQjtZQUM3QixPQUFPLEVBQUUsc0JBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1NBQzlCLENBQUMsQ0FBQztRQUNILHlCQUF5QixDQUFDLGVBQWUsQ0FDdkMsSUFBSSx5QkFBZSxDQUFDO1lBQ2xCLE9BQU8sRUFBRTtnQkFDUCwwQkFBMEI7Z0JBQzFCLGdDQUFnQztnQkFDaEMsbUJBQW1CO2dCQUNuQix5QkFBeUI7Z0JBQ3pCLHdCQUF3QjthQUN6QjtZQUNELFNBQVMsRUFBRSxDQUFDLEdBQUcsQ0FBQztTQUNqQixDQUFDLENBQ0gsQ0FBQztRQUVGLGNBQWMsQ0FBQyxjQUFjLENBQUMseUJBQXlCLENBQUMsQ0FBQztRQUN6RCxrREFBa0Q7UUFDbEQsSUFBSSxhQUFhLEVBQUUsQ0FBQztZQUNsQixhQUFhLENBQUMsbUJBQW1CLENBQUMsc0JBQXNCLENBQUMsQ0FBQztZQUMxRCxhQUFhLENBQUMsbUJBQW1CLENBQUMseUJBQXlCLENBQUMsQ0FBQztRQUMvRCxDQUFDO1FBQ0QsSUFBSSxDQUFDLGFBQWEsQ0FBQyxhQUFhLENBQUMsc0JBQXNCLENBQUMsQ0FBQztRQUN6RCxJQUFJLENBQUMsYUFBYSxDQUFDLGFBQWEsQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO1FBRTVELGdFQUFnRTtRQUNoRSxNQUFNLFFBQVEsR0FBRyxJQUFJLDJCQUFRLENBQUMsSUFBSSxFQUFFLHdCQUF3QixFQUFFO1lBQzVELGNBQWMsRUFBRSxzQkFBc0I7WUFDdEMsaUJBQWlCLEVBQUUseUJBQXlCO1lBQzVDLGFBQWEsRUFBRSx5QkFBeUIsSUFBSSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7U0FDakUsQ0FBQyxDQUFDO1FBRUgsdUVBQXVFO1FBQ3ZFLE1BQU0sb0JBQW9CLEdBQUcsSUFBSSw0QkFBYyxDQUFDLElBQUksRUFBRSxzQkFBc0IsRUFBRTtZQUM1RSxZQUFZLEVBQUUsUUFBUSxDQUFDLFlBQVk7WUFDbkMsVUFBVSxFQUFFO2dCQUNWLFdBQVcsRUFBRSxnQkFBZ0IsQ0FBQyxXQUFXO2dCQUN6QyxRQUFRLEVBQUUsUUFBUTtnQkFDbEIsT0FBTyxFQUFFLFdBQVcsQ0FBQyxTQUFTO2FBQy9CO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gsb0JBQW9CLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBRTFELHlEQUF5RDtRQUN6RCxNQUFNLFdBQVcsR0FBRyxvQkFBb0IsQ0FBQyxZQUFZLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDbEUsSUFBSSxDQUFDLGNBQWMsR0FBRyx3QkFBYyxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDeEYsSUFBSSxDQUFDLGVBQWUsR0FBRyw0QkFBZSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFO1lBQ2pFLFdBQVcsRUFBRSxXQUFXO1NBQ3pCLENBQUMsQ0FBQztJQUNMLENBQUM7O0FBcFFILG9FQXFRQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNyeXB0byBmcm9tICdjcnlwdG8nO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcblxuaW1wb3J0IHsgQ3VzdG9tUmVzb3VyY2UsIER1cmF0aW9uLCBSZW1vdmFsUG9saWN5IH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgUHJvamVjdCwgU291cmNlLCBMaW51eEJ1aWxkSW1hZ2UsIEJ1aWxkU3BlYyB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1jb2RlYnVpbGQnO1xuaW1wb3J0IHsgSVZwYywgSVNlY3VyaXR5R3JvdXAsIFN1Ym5ldFNlbGVjdGlvbiB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lYzInO1xuaW1wb3J0IHsgUmVwb3NpdG9yeSwgUmVwb3NpdG9yeUVuY3J5cHRpb24sIFRhZ1N0YXR1cyB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1lY3InO1xuaW1wb3J0IHsgQ29udGFpbmVySW1hZ2UgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWNzJztcbmltcG9ydCB7IFBvbGljeVN0YXRlbWVudCB9IGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuaW1wb3J0IHsgS2V5IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWttcyc7XG5pbXBvcnQgeyBSdW50aW1lLCBDb2RlLCBEb2NrZXJJbWFnZUNvZGUsIEZ1bmN0aW9uIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBCdWNrZXQsIEJsb2NrUHVibGljQWNjZXNzIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXMzJztcbmltcG9ydCB7IEFzc2V0IH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXMzLWFzc2V0cyc7XG5pbXBvcnQgeyBQcm92aWRlciB9IGZyb20gJ2F3cy1jZGstbGliL2N1c3RvbS1yZXNvdXJjZXMnO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5cbi8qKlxuICogUHJvcGVydGllcyBmb3IgdGhlIGBUb2tlbkluamVjdGFibGVEb2NrZXJCdWlsZGVyYCBjb25zdHJ1Y3QuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVG9rZW5JbmplY3RhYmxlRG9ja2VyQnVpbGRlclByb3BzIHtcbiAgLyoqXG4gICAqIFRoZSBwYXRoIHRvIHRoZSBkaXJlY3RvcnkgY29udGFpbmluZyB0aGUgRG9ja2VyZmlsZSBvciBzb3VyY2UgY29kZS5cbiAgICovXG4gIHJlYWRvbmx5IHBhdGg6IHN0cmluZztcblxuICAvKipcbiAgICogQnVpbGQgYXJndW1lbnRzIHRvIHBhc3MgdG8gdGhlIERvY2tlciBidWlsZCBwcm9jZXNzLlxuICAgKiBUaGVzZSBhcmUgdHJhbnNmb3JtZWQgaW50byBgLS1idWlsZC1hcmcgS0VZPVZBTFVFYCBmbGFncy5cbiAgICogQGV4YW1wbGVcbiAgICoge1xuICAgKiAgIFRPS0VOOiAnbXktc2VjcmV0LXRva2VuJyxcbiAgICogICBFTlY6ICdwcm9kdWN0aW9uJ1xuICAgKiB9XG4gICAqL1xuICByZWFkb25seSBidWlsZEFyZ3M/OiB7IFtrZXk6IHN0cmluZ106IHN0cmluZyB9O1xuXG4gIC8qKlxuICAgKiBUaGUgQVJOIG9mIHRoZSBBV1MgU2VjcmV0cyBNYW5hZ2VyIHNlY3JldCBjb250YWluaW5nIERvY2tlciBsb2dpbiBjcmVkZW50aWFscy5cbiAgICogVGhpcyBzZWNyZXQgc2hvdWxkIHN0b3JlIGEgSlNPTiBvYmplY3Qgd2l0aCB0aGUgZm9sbG93aW5nIHN0cnVjdHVyZTpcbiAgICogYGBganNvblxuICAgKiB7XG4gICAqICAgXCJ1c2VybmFtZVwiOiBcIm15LWRvY2tlci11c2VybmFtZVwiLFxuICAgKiAgIFwicGFzc3dvcmRcIjogXCJteS1kb2NrZXItcGFzc3dvcmRcIlxuICAgKiB9XG4gICAqIGBgYFxuICAgKiBJZiBub3QgcHJvdmlkZWQgKG9yIG5vdCBuZWVkZWQpLCB0aGUgY29uc3RydWN0IHdpbGwgc2tpcCBEb2NrZXIgSHViIGxvZ2luLlxuICAgKlxuICAgKiAqKk5vdGUqKjogVGhlIHNlY3JldCBtdXN0IGJlIGluIHRoZSBzYW1lIHJlZ2lvbiBhcyB0aGUgc3RhY2suXG4gICAqXG4gICAqIEBleGFtcGxlICdhcm46YXdzOnNlY3JldHNtYW5hZ2VyOnVzLWVhc3QtMToxMjM0NTY3ODkwMTI6c2VjcmV0OkRvY2tlckxvZ2luU2VjcmV0J1xuICAgKi9cbiAgcmVhZG9ubHkgZG9ja2VyTG9naW5TZWNyZXRBcm4/OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBWUEMgaW4gd2hpY2ggdGhlIENvZGVCdWlsZCBwcm9qZWN0IHdpbGwgYmUgZGVwbG95ZWQuXG4gICAqIElmIHByb3ZpZGVkLCB0aGUgQ29kZUJ1aWxkIHByb2plY3Qgd2lsbCBiZSBsYXVuY2hlZCB3aXRoaW4gdGhlIHNwZWNpZmllZCBWUEMuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gTm8gVlBDIGlzIGF0dGFjaGVkLCBhbmQgdGhlIENvZGVCdWlsZCBwcm9qZWN0IHdpbGwgdXNlIHB1YmxpYyBpbnRlcm5ldC5cbiAgICovXG4gIHJlYWRvbmx5IHZwYz86IElWcGM7XG5cbiAgLyoqXG4gICAqIFRoZSBzZWN1cml0eSBncm91cHMgdG8gYXR0YWNoIHRvIHRoZSBDb2RlQnVpbGQgcHJvamVjdC5cbiAgICogVGhlc2UgZGVmaW5lIHRoZSBuZXR3b3JrIGFjY2VzcyBydWxlcyBmb3IgdGhlIENvZGVCdWlsZCBwcm9qZWN0LlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIE5vIHNlY3VyaXR5IGdyb3VwcyBhcmUgYXR0YWNoZWQuXG4gICAqL1xuICByZWFkb25seSBzZWN1cml0eUdyb3Vwcz86IElTZWN1cml0eUdyb3VwW107XG5cbiAgLyoqXG4gICAqIFRoZSBzdWJuZXQgc2VsZWN0aW9uIHRvIHNwZWNpZnkgd2hpY2ggc3VibmV0cyB0byB1c2Ugd2l0aGluIHRoZSBWUEMuXG4gICAqIEFsbG93cyB0aGUgdXNlciB0byBzZWxlY3QgcHJpdmF0ZSwgcHVibGljLCBvciBpc29sYXRlZCBzdWJuZXRzLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIEFsbCBzdWJuZXRzIGluIHRoZSBWUEMgYXJlIHVzZWQuXG4gICAqL1xuICByZWFkb25seSBzdWJuZXRTZWxlY3Rpb24/OiBTdWJuZXRTZWxlY3Rpb247XG5cbiAgLyoqXG4gICAqIEN1c3RvbSBjb21tYW5kcyB0byBydW4gZHVyaW5nIHRoZSBpbnN0YWxsIHBoYXNlIG9mIENvZGVCdWlsZC5cbiAgICpcbiAgICogKipFeGFtcGxlKio6XG4gICAqIGBgYHRzXG4gICAqIGluc3RhbGxDb21tYW5kczogW1xuICAgKiAgICdlY2hvIFwiVXBkYXRpbmcgcGFja2FnZSBsaXN0cy4uLlwiJyxcbiAgICogICAnYXB0LWdldCB1cGRhdGUgLXknLFxuICAgKiAgICdlY2hvIFwiSW5zdGFsbGluZyByZXF1aXJlZCBwYWNrYWdlcy4uLlwiJyxcbiAgICogICAnYXB0LWdldCBpbnN0YWxsIC15IGN1cmwgZG5zdXRpbHMnLFxuICAgKiBdLFxuICAgKiBgYGBcbiAgICogQGRlZmF1bHQgLSBObyBhZGRpdGlvbmFsIGluc3RhbGwgY29tbWFuZHMuXG4gICAqL1xuICByZWFkb25seSBpbnN0YWxsQ29tbWFuZHM/OiBzdHJpbmdbXTtcblxuICAvKipcbiAgICogQ3VzdG9tIGNvbW1hbmRzIHRvIHJ1biBkdXJpbmcgdGhlIHByZV9idWlsZCBwaGFzZSBvZiBDb2RlQnVpbGQuXG4gICAqXG4gICAqICoqRXhhbXBsZSoqOlxuICAgKiBgYGB0c1xuICAgKiBwcmVCdWlsZENvbW1hbmRzOiBbXG4gICAqICAgJ2VjaG8gXCJGZXRjaGluZyBjb25maWd1cmF0aW9uIGZyb20gcHJpdmF0ZSBBUEkuLi5cIicsXG4gICAqICAgJ2N1cmwgLW8gY29uZmlnLmpzb24gaHR0cHM6Ly9hcGkuZXhhbXBsZS5jb20vY29uZmlnJyxcbiAgICogXSxcbiAgICogYGBgXG4gICAqIEBkZWZhdWx0IC0gTm8gYWRkaXRpb25hbCBwcmUtYnVpbGQgY29tbWFuZHMuXG4gICAqL1xuICByZWFkb25seSBwcmVCdWlsZENvbW1hbmRzPzogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdG8gZW5hYmxlIEtNUyBlbmNyeXB0aW9uIGZvciB0aGUgRUNSIHJlcG9zaXRvcnkuXG4gICAqIElmIGB0cnVlYCwgYSBLTVMga2V5IHdpbGwgYmUgY3JlYXRlZCBmb3IgZW5jcnlwdGluZyBFQ1IgaW1hZ2VzLlxuICAgKiBJZiBgZmFsc2VgLCB0aGUgcmVwb3NpdG9yeSB3aWxsIHVzZSBBRVMtMjU2IGVuY3J5cHRpb24uXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IGttc0VuY3J5cHRpb24/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBUaGUgcXVlcnkgaW50ZXJ2YWwgZm9yIGNoZWNraW5nIGlmIHRoZSBDb2RlQnVpbGQgcHJvamVjdCBoYXMgY29tcGxldGVkLlxuICAgKiBUaGlzIGRldGVybWluZXMgaG93IGZyZXF1ZW50bHkgdGhlIGN1c3RvbSByZXNvdXJjZSBwb2xscyBmb3IgYnVpbGQgY29tcGxldGlvbi5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBEdXJhdGlvbi5zZWNvbmRzKDMwKVxuICAgKi9cbiAgcmVhZG9ubHkgY29tcGxldGVuZXNzUXVlcnlJbnRlcnZhbD86IER1cmF0aW9uO1xuXG4gIC8qKlxuICAgKiBBIGxpc3Qgb2YgZmlsZSBwYXRocyBpbiB0aGUgRG9ja2VyIGRpcmVjdG9yeSB0byBleGNsdWRlIGZyb20gYnVpbGQuXG4gICAqIFdpbGwgdXNlIHBhdGhzIGluIC5kb2NrZXJpZ25vcmUgZmlsZSBpZiBwcmVzZW50LlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIE5vIGZpbGUgcGF0aCBleGNsdXNpb25zXG4gICAqL1xuICByZWFkb25seSBleGNsdWRlPzogc3RyaW5nW107XG59XG5cbi8qKlxuICogQSBDREsgY29uc3RydWN0IHRvIGJ1aWxkIGFuZCBwdXNoIERvY2tlciBpbWFnZXMgdG8gYW4gRUNSIHJlcG9zaXRvcnkgdXNpbmdcbiAqIENvZGVCdWlsZCBhbmQgTGFtYmRhIGN1c3RvbSByZXNvdXJjZXMsICoqdGhlbioqIHJldHJpZXZlIHRoZSBmaW5hbCBpbWFnZSB0YWdcbiAqIHNvIHRoYXQgRUNTL0xhbWJkYSByZWZlcmVuY2VzIHVzZSB0aGUgZXhhY3QgZGlnZXN0LlxuICovXG5leHBvcnQgY2xhc3MgVG9rZW5JbmplY3RhYmxlRG9ja2VyQnVpbGRlciBleHRlbmRzIENvbnN0cnVjdCB7XG4gIC8qKlxuICAgKiBUaGUgRUNSIHJlcG9zaXRvcnkgdGhhdCBzdG9yZXMgdGhlIHJlc3VsdGluZyBEb2NrZXIgaW1hZ2UuXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGVjclJlcG9zaXRvcnk6IFJlcG9zaXRvcnk7XG5cbiAgLyoqXG4gICAqIEFuIEVDUy1jb21wYXRpYmxlIGNvbnRhaW5lciBpbWFnZSByZWZlcmVuY2luZyB0aGUgdGFnXG4gICAqIG9mIHRoZSBidWlsdCBEb2NrZXIgaW1hZ2UuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgY29udGFpbmVySW1hZ2U6IENvbnRhaW5lckltYWdlO1xuXG4gIC8qKlxuICAgKiBBIExhbWJkYS1jb21wYXRpYmxlIERvY2tlckltYWdlQ29kZSByZWZlcmVuY2luZyB0aGUgdGFnXG4gICAqIG9mIHRoZSBidWlsdCBEb2NrZXIgaW1hZ2UuXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZG9ja2VySW1hZ2VDb2RlOiBEb2NrZXJJbWFnZUNvZGU7XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBuZXcgYFRva2VuSW5qZWN0YWJsZURvY2tlckJ1aWxkZXJgLlxuICAgKlxuICAgKiBAcGFyYW0gc2NvcGUgVGhlIHNjb3BlIGluIHdoaWNoIHRvIGRlZmluZSB0aGlzIGNvbnN0cnVjdC5cbiAgICogQHBhcmFtIGlkIFRoZSBzY29wZWQgY29uc3RydWN0IElELlxuICAgKiBAcGFyYW0gcHJvcHMgQ29uZmlndXJhdGlvbiBmb3IgYnVpbGRpbmcgYW5kIHB1c2hpbmcgdGhlIERvY2tlciBpbWFnZS5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBUb2tlbkluamVjdGFibGVEb2NrZXJCdWlsZGVyUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuXG4gICAgY29uc3Qge1xuICAgICAgcGF0aDogc291cmNlUGF0aCxcbiAgICAgIGJ1aWxkQXJncyxcbiAgICAgIGRvY2tlckxvZ2luU2VjcmV0QXJuLFxuICAgICAgdnBjLFxuICAgICAgc2VjdXJpdHlHcm91cHMsXG4gICAgICBzdWJuZXRTZWxlY3Rpb24sXG4gICAgICBpbnN0YWxsQ29tbWFuZHMsXG4gICAgICBwcmVCdWlsZENvbW1hbmRzLFxuICAgICAga21zRW5jcnlwdGlvbiA9IGZhbHNlLFxuICAgICAgY29tcGxldGVuZXNzUXVlcnlJbnRlcnZhbCxcbiAgICAgIGV4Y2x1ZGUsXG4gICAgfSA9IHByb3BzO1xuXG4gICAgLy8gR2VuZXJhdGUgYW4gZXBoZW1lcmFsIHRhZyBmb3IgQ29kZUJ1aWxkXG4gICAgY29uc3QgaW1hZ2VUYWcgPSBjcnlwdG8ucmFuZG9tVVVJRCgpO1xuXG4gICAgLy8gT3B0aW9uYWxseSBkZWZpbmUgYSBLTVMga2V5IGZvciBFQ1IgZW5jcnlwdGlvbiBpZiByZXF1ZXN0ZWRcbiAgICBsZXQgZW5jcnlwdGlvbktleTogS2V5IHwgdW5kZWZpbmVkO1xuICAgIGlmIChrbXNFbmNyeXB0aW9uKSB7XG4gICAgICBlbmNyeXB0aW9uS2V5ID0gbmV3IEtleSh0aGlzLCAnRWNyRW5jcnlwdGlvbktleScsIHtcbiAgICAgICAgZW5hYmxlS2V5Um90YXRpb246IHRydWUsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBDcmVhdGUgYW4gRUNSIHJlcG9zaXRvcnkgKG9wdGlvbmFsbHkgd2l0aCBLTVMgZW5jcnlwdGlvbilcbiAgICB0aGlzLmVjclJlcG9zaXRvcnkgPSBuZXcgUmVwb3NpdG9yeSh0aGlzLCAnRUNSUmVwb3NpdG9yeScsIHtcbiAgICAgIGxpZmVjeWNsZVJ1bGVzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBydWxlUHJpb3JpdHk6IDEsXG4gICAgICAgICAgZGVzY3JpcHRpb246ICdSZW1vdmUgdW50YWdnZWQgaW1hZ2VzIGFmdGVyIDEgZGF5JyxcbiAgICAgICAgICB0YWdTdGF0dXM6IFRhZ1N0YXR1cy5VTlRBR0dFRCxcbiAgICAgICAgICBtYXhJbWFnZUFnZTogRHVyYXRpb24uZGF5cygxKSxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgICBlbmNyeXB0aW9uOiBrbXNFbmNyeXB0aW9uID8gUmVwb3NpdG9yeUVuY3J5cHRpb24uS01TIDogUmVwb3NpdG9yeUVuY3J5cHRpb24uQUVTXzI1NixcbiAgICAgIGVuY3J5cHRpb25LZXk6IGttc0VuY3J5cHRpb24gPyBlbmNyeXB0aW9uS2V5IDogdW5kZWZpbmVkLFxuICAgICAgaW1hZ2VTY2FuT25QdXNoOiB0cnVlLFxuICAgIH0pO1xuXG4gICAgbGV0IGVmZmVjdGl2ZUV4Y2x1ZGUgPSBleGNsdWRlO1xuICAgIGlmICghZWZmZWN0aXZlRXhjbHVkZSkge1xuICAgICAgY29uc3QgZG9ja2VyaWdub3JlUGF0aCA9IHBhdGguam9pbihzb3VyY2VQYXRoLCAnLmRvY2tlcmlnbm9yZScpO1xuICAgICAgaWYgKGZzLmV4aXN0c1N5bmMoZG9ja2VyaWdub3JlUGF0aCkpIHtcbiAgICAgICAgY29uc3QgZmlsZUNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmMoZG9ja2VyaWdub3JlUGF0aCwgJ3V0ZjgnKTtcbiAgICAgICAgZWZmZWN0aXZlRXhjbHVkZSA9IGZpbGVDb250ZW50XG4gICAgICAgICAgLnNwbGl0KCdcXG4nKVxuICAgICAgICAgIC5tYXAoKGxpbmU6IHN0cmluZykgPT4gbGluZS50cmltKCkpXG4gICAgICAgICAgLmZpbHRlcigobGluZTogc3RyaW5nKSA9PiBsaW5lLmxlbmd0aCA+IDAgJiYgIWxpbmUuc3RhcnRzV2l0aCgnIycpKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBXcmFwIHRoZSBzb3VyY2UgZm9sZGVyIGFzIGFuIFMzIGFzc2V0IGZvciBDb2RlQnVpbGQgdG8gdXNlXG4gICAgY29uc3Qgc291cmNlQXNzZXQgPSBuZXcgQXNzZXQodGhpcywgJ1NvdXJjZUFzc2V0Jywge1xuICAgICAgcGF0aDogc291cmNlUGF0aCxcbiAgICAgIGV4Y2x1ZGU6IGVmZmVjdGl2ZUV4Y2x1ZGUsXG4gICAgfSk7XG5cbiAgICAvLyBDcmVhdGUgYW4gUzMgYnVja2V0IHRvIHN0b3JlIHRoZSBDb2RlQnVpbGQgYXJ0aWZhY3RzXG4gICAgY29uc3QgYXJ0aWZhY3RCdWNrZXQgPSBuZXcgQnVja2V0KHRoaXMsICdBcnRpZmFjdEJ1Y2tldCcsIHtcbiAgICAgIHJlbW92YWxQb2xpY3k6IFJlbW92YWxQb2xpY3kuREVTVFJPWSxcbiAgICAgIGF1dG9EZWxldGVPYmplY3RzOiB0cnVlLFxuICAgICAgYmxvY2tQdWJsaWNBY2Nlc3M6IEJsb2NrUHVibGljQWNjZXNzLkJMT0NLX0FMTCxcbiAgICB9KTtcblxuICAgIC8vIENvbnZlcnQgYnVpbGRBcmdzIHRvIGEgQ0xJLWZyaWVuZGx5IHN0cmluZ1xuICAgIGNvbnN0IGJ1aWxkQXJnc1N0cmluZyA9IGJ1aWxkQXJnc1xuICAgICAgPyBPYmplY3QuZW50cmllcyhidWlsZEFyZ3MpXG4gICAgICAgIC5tYXAoKFtrLCB2XSkgPT4gYC0tYnVpbGQtYXJnICR7a309JHt2fWApXG4gICAgICAgIC5qb2luKCcgJylcbiAgICAgIDogJyc7XG5cbiAgICAvLyBPcHRpb25hbCBEb2NrZXJIdWIgbG9naW4sIGlmIGEgc2VjcmV0IEFSTiBpcyBwcm92aWRlZFxuICAgIGNvbnN0IGRvY2tlckxvZ2luQ29tbWFuZHMgPSBkb2NrZXJMb2dpblNlY3JldEFyblxuICAgICAgPyBbXG4gICAgICAgICdlY2hvIFwiUmV0cmlldmluZyBEb2NrZXIgY3JlZGVudGlhbHMuLi5cIicsXG4gICAgICAgICdhcHQtZ2V0IHVwZGF0ZSAteSAmJiBhcHQtZ2V0IGluc3RhbGwgLXkganEnLFxuICAgICAgICBgRE9DS0VSX1VTRVJOQU1FPSQoYXdzIHNlY3JldHNtYW5hZ2VyIGdldC1zZWNyZXQtdmFsdWUgLS1zZWNyZXQtaWQgJHtkb2NrZXJMb2dpblNlY3JldEFybn0gLS1xdWVyeSBTZWNyZXRTdHJpbmcgLS1vdXRwdXQgdGV4dCB8IGpxIC1yIC51c2VybmFtZSlgLFxuICAgICAgICBgRE9DS0VSX1BBU1NXT1JEPSQoYXdzIHNlY3JldHNtYW5hZ2VyIGdldC1zZWNyZXQtdmFsdWUgLS1zZWNyZXQtaWQgJHtkb2NrZXJMb2dpblNlY3JldEFybn0gLS1xdWVyeSBTZWNyZXRTdHJpbmcgLS1vdXRwdXQgdGV4dCB8IGpxIC1yIC5wYXNzd29yZClgLFxuICAgICAgICAnZWNobyBcIkxvZ2dpbmcgaW4gdG8gRG9ja2VyIEh1Yi4uLlwiJyxcbiAgICAgICAgJ2VjaG8gJERPQ0tFUl9QQVNTV09SRCB8IGRvY2tlciBsb2dpbiAtLXVzZXJuYW1lICRET0NLRVJfVVNFUk5BTUUgLS1wYXNzd29yZC1zdGRpbicsXG4gICAgICBdXG4gICAgICA6IFsnZWNobyBcIk5vIERvY2tlciBjcmVkZW50aWFscy4gU2tpcHBpbmcgRG9ja2VyIEh1YiBsb2dpbi5cIiddO1xuXG4gICAgY29uc3QgYnVpbGRTcGVjT2JqID0ge1xuICAgICAgdmVyc2lvbjogJzAuMicsXG4gICAgICBwaGFzZXM6IHtcbiAgICAgICAgaW5zdGFsbDoge1xuICAgICAgICAgIGNvbW1hbmRzOiBbXG4gICAgICAgICAgICAnZWNobyBcIkJlZ2lubmluZyBpbnN0YWxsIHBoYXNlLi4uXCInLFxuICAgICAgICAgICAgLi4uKGluc3RhbGxDb21tYW5kcyA/PyBbXSksXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgICAgcHJlX2J1aWxkOiB7XG4gICAgICAgICAgY29tbWFuZHM6IFtcbiAgICAgICAgICAgIC4uLihwcmVCdWlsZENvbW1hbmRzID8/IFtdKSxcbiAgICAgICAgICAgIC4uLmRvY2tlckxvZ2luQ29tbWFuZHMsXG4gICAgICAgICAgICAnZWNobyBcIlJldHJpZXZpbmcgQVdTIEFjY291bnQgSUQuLi5cIicsXG4gICAgICAgICAgICAnZXhwb3J0IEFDQ09VTlRfSUQ9JChhd3Mgc3RzIGdldC1jYWxsZXItaWRlbnRpdHkgLS1xdWVyeSBBY2NvdW50IC0tb3V0cHV0IHRleHQpJyxcbiAgICAgICAgICAgICdlY2hvIFwiTG9nZ2luZyBpbnRvIEFtYXpvbiBFQ1IuLi5cIicsXG4gICAgICAgICAgICAnYXdzIGVjciBnZXQtbG9naW4tcGFzc3dvcmQgLS1yZWdpb24gJEFXU19ERUZBVUxUX1JFR0lPTiB8IGRvY2tlciBsb2dpbiAtLXVzZXJuYW1lIEFXUyAtLXBhc3N3b3JkLXN0ZGluICRBQ0NPVU5UX0lELmRrci5lY3IuJEFXU19ERUZBVUxUX1JFR0lPTi5hbWF6b25hd3MuY29tJyxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgICBidWlsZDoge1xuICAgICAgICAgIGNvbW1hbmRzOiBbXG4gICAgICAgICAgICBgZWNobyBcIkJ1aWxkaW5nIERvY2tlciBpbWFnZSB3aXRoIHRhZyAke2ltYWdlVGFnfS4uLlwiYCxcbiAgICAgICAgICAgIGBkb2NrZXIgYnVpbGQgJHtidWlsZEFyZ3NTdHJpbmd9IC10ICRFQ1JfUkVQT19VUkk6JHtpbWFnZVRhZ30gJENPREVCVUlMRF9TUkNfRElSYCxcbiAgICAgICAgICBdLFxuICAgICAgICB9LFxuICAgICAgICBwb3N0X2J1aWxkOiB7XG4gICAgICAgICAgY29tbWFuZHM6IFtcbiAgICAgICAgICAgIGBlY2hvIFwiUHVzaGluZyBEb2NrZXIgaW1hZ2Ugd2l0aCB0YWcgJHtpbWFnZVRhZ30uLi5cImAsXG4gICAgICAgICAgICBgZG9ja2VyIHB1c2ggJEVDUl9SRVBPX1VSSToke2ltYWdlVGFnfWAsXG4gICAgICAgICAgXSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfTtcblxuICAgIC8vIENyZWF0ZSB0aGUgQ29kZUJ1aWxkIHByb2plY3RcbiAgICBjb25zdCBjb2RlQnVpbGRQcm9qZWN0ID0gbmV3IFByb2plY3QodGhpcywgJ0NvZGVCdWlsZFByb2plY3QnLCB7XG4gICAgICBzb3VyY2U6IFNvdXJjZS5zMyh7XG4gICAgICAgIGJ1Y2tldDogc291cmNlQXNzZXQuYnVja2V0LFxuICAgICAgICBwYXRoOiBzb3VyY2VBc3NldC5zM09iamVjdEtleSxcbiAgICAgIH0pLFxuICAgICAgZW52aXJvbm1lbnQ6IHtcbiAgICAgICAgYnVpbGRJbWFnZTogTGludXhCdWlsZEltYWdlLlNUQU5EQVJEXzdfMCxcbiAgICAgICAgcHJpdmlsZWdlZDogdHJ1ZSxcbiAgICAgIH0sXG4gICAgICBlbnZpcm9ubWVudFZhcmlhYmxlczoge1xuICAgICAgICBFQ1JfUkVQT19VUkk6IHsgdmFsdWU6IHRoaXMuZWNyUmVwb3NpdG9yeS5yZXBvc2l0b3J5VXJpIH0sXG4gICAgICB9LFxuICAgICAgYnVpbGRTcGVjOiBCdWlsZFNwZWMuZnJvbU9iamVjdChidWlsZFNwZWNPYmopLFxuICAgICAgdnBjLFxuICAgICAgc2VjdXJpdHlHcm91cHMsXG4gICAgICBzdWJuZXRTZWxlY3Rpb24sXG4gICAgfSk7XG5cbiAgICAvLyBHcmFudCBDb2RlQnVpbGQgdGhlIGFiaWxpdHkgdG8gaW50ZXJhY3Qgd2l0aCBFQ1JcbiAgICB0aGlzLmVjclJlcG9zaXRvcnkuZ3JhbnRQdWxsUHVzaChjb2RlQnVpbGRQcm9qZWN0KTtcbiAgICBjb2RlQnVpbGRQcm9qZWN0LmFkZFRvUm9sZVBvbGljeShcbiAgICAgIG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBhY3Rpb25zOiBbXG4gICAgICAgICAgJ2VjcjpHZXRBdXRob3JpemF0aW9uVG9rZW4nLFxuICAgICAgICAgICdlY3I6R2V0RG93bmxvYWRVcmxGb3JMYXllcicsXG4gICAgICAgICAgJ2VjcjpCYXRjaENoZWNrTGF5ZXJBdmFpbGFiaWxpdHknLFxuICAgICAgICBdLFxuICAgICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgICAgfSksXG4gICAgKTtcbiAgICBpZiAoZG9ja2VyTG9naW5TZWNyZXRBcm4pIHtcbiAgICAgIGNvZGVCdWlsZFByb2plY3QuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgICBhY3Rpb25zOiBbJ3NlY3JldHNtYW5hZ2VyOkdldFNlY3JldFZhbHVlJ10sXG4gICAgICAgICAgcmVzb3VyY2VzOiBbZG9ja2VyTG9naW5TZWNyZXRBcm5dLFxuICAgICAgICB9KSxcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gQ29uZGl0aW9uYWxseSBncmFudCBLTVMgZW5jcnlwdC9kZWNyeXB0IGlmIGEga2V5IGlzIHVzZWRcbiAgICBpZiAoZW5jcnlwdGlvbktleSkge1xuICAgICAgZW5jcnlwdGlvbktleS5ncmFudEVuY3J5cHREZWNyeXB0KGNvZGVCdWlsZFByb2plY3Qucm9sZSEpO1xuICAgIH1cblxuICAgIC8vIERlZmluZSBMYW1iZGEgZnVuY3Rpb25zIGZvciBjdXN0b20gcmVzb3VyY2UgZXZlbnQgYW5kIGNvbXBsZXRpb24gaGFuZGxpbmdcbiAgICBjb25zdCBvbkV2ZW50SGFuZGxlckZ1bmN0aW9uID0gbmV3IEZ1bmN0aW9uKHRoaXMsICdPbkV2ZW50SGFuZGxlckZ1bmN0aW9uJywge1xuICAgICAgcnVudGltZTogUnVudGltZS5OT0RFSlNfMThfWCxcbiAgICAgIGNvZGU6IENvZGUuZnJvbUFzc2V0KHBhdGgucmVzb2x2ZShfX2Rpcm5hbWUsICcuLi9vbkV2ZW50JykpLFxuICAgICAgaGFuZGxlcjogJ29uRXZlbnQuaGFuZGxlcicsXG4gICAgICB0aW1lb3V0OiBEdXJhdGlvbi5taW51dGVzKDE1KSxcbiAgICB9KTtcbiAgICBvbkV2ZW50SGFuZGxlckZ1bmN0aW9uLmFkZFRvUm9sZVBvbGljeShcbiAgICAgIG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBhY3Rpb25zOiBbJ2NvZGVidWlsZDpTdGFydEJ1aWxkJ10sXG4gICAgICAgIHJlc291cmNlczogW2NvZGVCdWlsZFByb2plY3QucHJvamVjdEFybl0sXG4gICAgICB9KSxcbiAgICApO1xuXG4gICAgY29uc3QgaXNDb21wbGV0ZUhhbmRsZXJGdW5jdGlvbiA9IG5ldyBGdW5jdGlvbih0aGlzLCAnSXNDb21wbGV0ZUhhbmRsZXJGdW5jdGlvbicsIHtcbiAgICAgIHJ1bnRpbWU6IFJ1bnRpbWUuTk9ERUpTXzE4X1gsXG4gICAgICBjb2RlOiBDb2RlLmZyb21Bc3NldChwYXRoLnJlc29sdmUoX19kaXJuYW1lLCAnLi4vaXNDb21wbGV0ZScpKSxcbiAgICAgIGVudmlyb25tZW50OiB7XG4gICAgICAgIElNQUdFX1RBRzogaW1hZ2VUYWcsXG4gICAgICB9LFxuICAgICAgaGFuZGxlcjogJ2lzQ29tcGxldGUuaGFuZGxlcicsXG4gICAgICB0aW1lb3V0OiBEdXJhdGlvbi5taW51dGVzKDE1KSxcbiAgICB9KTtcbiAgICBpc0NvbXBsZXRlSGFuZGxlckZ1bmN0aW9uLmFkZFRvUm9sZVBvbGljeShcbiAgICAgIG5ldyBQb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICBhY3Rpb25zOiBbXG4gICAgICAgICAgJ2NvZGVidWlsZDpCYXRjaEdldEJ1aWxkcycsXG4gICAgICAgICAgJ2NvZGVidWlsZDpMaXN0QnVpbGRzRm9yUHJvamVjdCcsXG4gICAgICAgICAgJ2xvZ3M6R2V0TG9nRXZlbnRzJyxcbiAgICAgICAgICAnbG9nczpEZXNjcmliZUxvZ1N0cmVhbXMnLFxuICAgICAgICAgICdsb2dzOkRlc2NyaWJlTG9nR3JvdXBzJyxcbiAgICAgICAgXSxcbiAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgIH0pLFxuICAgICk7XG5cbiAgICBhcnRpZmFjdEJ1Y2tldC5ncmFudFJlYWRXcml0ZShpc0NvbXBsZXRlSGFuZGxlckZ1bmN0aW9uKTtcbiAgICAvLyBDb25kaXRpb25hbGx5IGFsbG93IGVuY3J5cHRpb24gaWYgYSBrZXkgaXMgdXNlZFxuICAgIGlmIChlbmNyeXB0aW9uS2V5KSB7XG4gICAgICBlbmNyeXB0aW9uS2V5LmdyYW50RW5jcnlwdERlY3J5cHQob25FdmVudEhhbmRsZXJGdW5jdGlvbik7XG4gICAgICBlbmNyeXB0aW9uS2V5LmdyYW50RW5jcnlwdERlY3J5cHQoaXNDb21wbGV0ZUhhbmRsZXJGdW5jdGlvbik7XG4gICAgfVxuICAgIHRoaXMuZWNyUmVwb3NpdG9yeS5ncmFudFB1bGxQdXNoKG9uRXZlbnRIYW5kbGVyRnVuY3Rpb24pO1xuICAgIHRoaXMuZWNyUmVwb3NpdG9yeS5ncmFudFB1bGxQdXNoKGlzQ29tcGxldGVIYW5kbGVyRnVuY3Rpb24pO1xuXG4gICAgLy8gQ3JlYXRlIGEgY3VzdG9tIHJlc291cmNlIHByb3ZpZGVyIHRoYXQgdXNlcyB0aGUgYWJvdmUgTGFtYmRhc1xuICAgIGNvbnN0IHByb3ZpZGVyID0gbmV3IFByb3ZpZGVyKHRoaXMsICdDdXN0b21SZXNvdXJjZVByb3ZpZGVyJywge1xuICAgICAgb25FdmVudEhhbmRsZXI6IG9uRXZlbnRIYW5kbGVyRnVuY3Rpb24sXG4gICAgICBpc0NvbXBsZXRlSGFuZGxlcjogaXNDb21wbGV0ZUhhbmRsZXJGdW5jdGlvbixcbiAgICAgIHF1ZXJ5SW50ZXJ2YWw6IGNvbXBsZXRlbmVzc1F1ZXJ5SW50ZXJ2YWwgPz8gRHVyYXRpb24uc2Vjb25kcygzMCksXG4gICAgfSk7XG5cbiAgICAvLyBDdXN0b20gUmVzb3VyY2UgdGhhdCB0cmlnZ2VycyB0aGUgQ29kZUJ1aWxkIGFuZCB3YWl0cyBmb3IgY29tcGxldGlvblxuICAgIGNvbnN0IGJ1aWxkVHJpZ2dlclJlc291cmNlID0gbmV3IEN1c3RvbVJlc291cmNlKHRoaXMsICdCdWlsZFRyaWdnZXJSZXNvdXJjZScsIHtcbiAgICAgIHNlcnZpY2VUb2tlbjogcHJvdmlkZXIuc2VydmljZVRva2VuLFxuICAgICAgcHJvcGVydGllczoge1xuICAgICAgICBQcm9qZWN0TmFtZTogY29kZUJ1aWxkUHJvamVjdC5wcm9qZWN0TmFtZSxcbiAgICAgICAgSW1hZ2VUYWc6IGltYWdlVGFnLFxuICAgICAgICBUcmlnZ2VyOiBzb3VyY2VBc3NldC5hc3NldEhhc2gsXG4gICAgICB9LFxuICAgIH0pO1xuICAgIGJ1aWxkVHJpZ2dlclJlc291cmNlLm5vZGUuYWRkRGVwZW5kZW5jeShjb2RlQnVpbGRQcm9qZWN0KTtcblxuICAgIC8vIFJldHJpZXZlIHRoZSBmaW5hbCBEb2NrZXIgaW1hZ2UgdGFnIGZyb20gRGF0YS5JbWFnZVRhZ1xuICAgIGNvbnN0IGltYWdlVGFnUmVmID0gYnVpbGRUcmlnZ2VyUmVzb3VyY2UuZ2V0QXR0U3RyaW5nKCdJbWFnZVRhZycpO1xuICAgIHRoaXMuY29udGFpbmVySW1hZ2UgPSBDb250YWluZXJJbWFnZS5mcm9tRWNyUmVwb3NpdG9yeSh0aGlzLmVjclJlcG9zaXRvcnksIGltYWdlVGFnUmVmKTtcbiAgICB0aGlzLmRvY2tlckltYWdlQ29kZSA9IERvY2tlckltYWdlQ29kZS5mcm9tRWNyKHRoaXMuZWNyUmVwb3NpdG9yeSwge1xuICAgICAgdGFnT3JEaWdlc3Q6IGltYWdlVGFnUmVmLFxuICAgIH0pO1xuICB9XG59XG4iXX0=