UNPKG

@aws-solutions-constructs/core

Version:
390 lines 59.3 kB
"use strict"; /** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * with the License. A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0 * * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * and limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.buildSagemakerNotebook = buildSagemakerNotebook; exports.BuildSagemakerEndpoint = BuildSagemakerEndpoint; exports.deploySagemakerEndpoint = deploySagemakerEndpoint; exports.createSagemakerModel = createSagemakerModel; exports.createSagemakerEndpointConfig = createSagemakerEndpointConfig; exports.createSagemakerEndpoint = createSagemakerEndpoint; exports.CheckSagemakerProps = CheckSagemakerProps; /* * The functions found here in the core library are for internal use and can be changed * or removed outside of a major release. We recommend against calling them directly from client code. */ const sagemaker = require("aws-cdk-lib/aws-sagemaker"); const ec2 = require("aws-cdk-lib/aws-ec2"); const kms_helper_1 = require("./kms-helper"); const sagemaker_defaults_1 = require("./sagemaker-defaults"); const cdk = require("aws-cdk-lib"); const utils_1 = require("./utils"); const vpc_helper_1 = require("./vpc-helper"); const iam = require("aws-cdk-lib/aws-iam"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const vpc_defaults_1 = require("./vpc-defaults"); const security_group_helper_1 = require("./security-group-helper"); function addPermissions(role, props) { // Grant permissions to NoteBookInstance for creating and training the model role.addToPolicy(new iam.PolicyStatement({ resources: [`arn:${aws_cdk_lib_1.Aws.PARTITION}:sagemaker:${aws_cdk_lib_1.Aws.REGION}:${aws_cdk_lib_1.Aws.ACCOUNT_ID}:*`], actions: [ 'sagemaker:CreateTrainingJob', 'sagemaker:DescribeTrainingJob', 'sagemaker:CreateModel', 'sagemaker:DescribeModel', 'sagemaker:DeleteModel', 'sagemaker:CreateEndpoint', 'sagemaker:CreateEndpointConfig', 'sagemaker:DescribeEndpoint', 'sagemaker:DescribeEndpointConfig', 'sagemaker:DeleteEndpoint', 'sagemaker:DeleteEndpointConfig', 'sagemaker:InvokeEndpoint', ], })); // Grant CloudWatch Logging permissions role.addToPolicy(new iam.PolicyStatement({ resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/sagemaker/*`], actions: [ 'logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:DescribeLogStreams', 'logs:GetLogEvents', 'logs:PutLogEvents', ], })); // To place the Sagemaker endpoint in a VPC if (props && props.vpc) { role.addToPolicy(new iam.PolicyStatement({ resources: ['*'], actions: [ 'ec2:CreateNetworkInterface', 'ec2:CreateNetworkInterfacePermission', 'ec2:DeleteNetworkInterface', 'ec2:DeleteNetworkInterfacePermission', 'ec2:DescribeNetworkInterfaces', 'ec2:AssignPrivateIpAddresses', 'ec2:UnassignPrivateIpAddresses', 'ec2:DescribeVpcs', 'ec2:DescribeDhcpOptions', 'ec2:DescribeSubnets', 'ec2:DescribeSecurityGroups', ], })); } // To create a Sagemaker model using Bring-Your-Own-Model (BYOM) algorithm image // The image URL is specified in the modelProps role.addToPolicy(new iam.PolicyStatement({ resources: [`arn:${cdk.Aws.PARTITION}:ecr:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:repository/*`], actions: [ 'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', 'ecr:DescribeRepositories', 'ecr:DescribeImages', 'ecr:BatchGetImage', ], })); // Add GetAuthorizationToken (it can not be bound to resources other than *) role.addToPolicy(new iam.PolicyStatement({ resources: ['*'], actions: ['ecr:GetAuthorizationToken'], })); // add permission to use Elastic Inference accelerator if (props && props.endpointConfigProps) { // Get the acceleratorType, if any const acceleratorType = (props.endpointConfigProps ?.productionVariants)[0].acceleratorType; if (acceleratorType !== undefined) { role.addToPolicy(new iam.PolicyStatement({ resources: ['*'], actions: ['elastic-inference:Connect'], })); } } // add kms permissions role.addToPolicy(new iam.PolicyStatement({ // the kmsKeyId in the endpointConfigProps can be any of the following formats: // Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab // Key ARN: arn:aws:kms:<region>:<accountID>:key/1234abcd-12ab-34cd-56ef-1234567890ab // Alias name: alias/ExampleAlias // Alias name ARN: arn:aws:kms:<region>:<accountID>:alias/ExampleAlias // the key is used to encrypt/decrypt data captured by the Sagemaker endpoint and stored in S3 bucket resources: [ `arn:${cdk.Aws.PARTITION}:kms:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:key/*`, `arn:${cdk.Aws.PARTITION}:kms:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:alias/*`, ], actions: ['kms:Encrypt', 'kms:Decrypt', 'kms:ReEncrypt*', 'kms:GenerateDataKey*', 'kms:DescribeKey'], })); // Add S3 permissions to get Model artifact, put data capture files, etc. role.addToPolicy(new iam.PolicyStatement({ actions: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject', 's3:ListBucket'], resources: [`arn:aws:s3:::*`], })); // Grant GetRole permissions to the Sagemaker service role.addToPolicy(new iam.PolicyStatement({ resources: [role.roleArn], actions: ['iam:GetRole'], })); // Grant PassRole permissions to the Sagemaker service role.addToPolicy(new iam.PolicyStatement({ resources: [role.roleArn], actions: ['iam:PassRole'], conditions: { StringLike: { 'iam:PassedToService': 'sagemaker.amazonaws.com' }, }, })); // Add CFN NAG uppress to allow for "Resource": "*" for ENI access in VPC, // ECR authorization token for custom model images, and elastic inference // Add CFN NAG for Complex Role because Sagmaker needs permissions to access several services const roleDefaultPolicy = role.node.tryFindChild('DefaultPolicy')?.node.findChild('Resource'); (0, utils_1.addCfnSuppressRules)(roleDefaultPolicy, [ { id: 'W12', reason: `Sagemaker needs the following minimum required permissions to access ENIs in a VPC, ECR for custom model images, and elastic inference.`, }, { id: 'W76', reason: 'Complex role becuase Sagemaker needs permissions to access several services', } ]); (0, utils_1.addCfnGuardSuppressRules)(roleDefaultPolicy, ["IAM_POLICY_NON_COMPLIANT_ARN"]); } /** * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. * * The constructs deploying Sagemaker services only deploy Sagemaker Endpoints, Endpoint * Configurations and (already developed) Models. There are no Constructs that deploy Notebooks. * There doesn't appear to be any references to this function outside the unit test file - so * this function is unneccessary. */ function buildSagemakerNotebook(scope, id, props) { // Setup the notebook properties let sagemakerNotebookProps; let vpcInstance; let securityGroup; let kmsKeyId; let subnetId; // Conditional Sagemaker Notebook creation if (props.existingNotebookObj) { return { notebook: props.existingNotebookObj }; } if (CheckNotebookVpcProps(props)) { throw new Error('Must define both sagemakerNotebookProps.subnetId and sagemakerNotebookProps.securityGroupIds'); } addPermissions(props.role); if (props.sagemakerNotebookProps?.kmsKeyId === undefined) { kmsKeyId = (0, kms_helper_1.buildEncryptionKey)(scope, id).keyId; } else { kmsKeyId = props.sagemakerNotebookProps.kmsKeyId; } if (props.deployInsideVpc === undefined || props.deployInsideVpc) { if (props.sagemakerNotebookProps?.subnetId === undefined && props.sagemakerNotebookProps?.securityGroupIds === undefined) { vpcInstance = (0, vpc_helper_1.buildVpc)(scope, { defaultVpcProps: (0, vpc_defaults_1.DefaultPublicPrivateVpcProps)(), }); securityGroup = (0, security_group_helper_1.buildSecurityGroup)(scope, 'SecurityGroup', { vpc: vpcInstance, allowAllOutbound: false, }, [], [{ peer: ec2.Peer.anyIpv4(), connection: ec2.Port.tcp(443) }]); subnetId = vpcInstance.privateSubnets[0].subnetId; sagemakerNotebookProps = (0, sagemaker_defaults_1.DefaultSagemakerNotebookProps)(props.role.roleArn, kmsKeyId, subnetId, [ securityGroup.securityGroupId, ]); } else { sagemakerNotebookProps = (0, sagemaker_defaults_1.DefaultSagemakerNotebookProps)(props.role.roleArn, kmsKeyId, props.sagemakerNotebookProps?.subnetId, props.sagemakerNotebookProps?.securityGroupIds); } } else { sagemakerNotebookProps = (0, sagemaker_defaults_1.DefaultSagemakerNotebookProps)(props.role.roleArn, kmsKeyId); } sagemakerNotebookProps = (0, utils_1.consolidateProps)(sagemakerNotebookProps, props.sagemakerNotebookProps); // Create the notebook // NOSONAR: (typescript:S6319) // keyID is created above in the if (props.sagemakerNotebookProps?.kmsKeyId === undefined) // block. It is then passed to DefaultSagemakerNotebookProps() // This behavior is validated in unit test const sagemakerInstance = new sagemaker.CfnNotebookInstance(scope, 'SagemakerNotebook', sagemakerNotebookProps // NOSONAR ); if (vpcInstance) { return { notebook: sagemakerInstance, vpc: vpcInstance, securityGroup }; } else { return { notebook: sagemakerInstance }; } } /** * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. */ function CheckNotebookVpcProps(props) { if ((props.sagemakerNotebookProps?.subnetId && props.sagemakerNotebookProps?.securityGroupIds === undefined) || (props.sagemakerNotebookProps?.subnetId === undefined && props.sagemakerNotebookProps?.securityGroupIds)) { return true; } return false; } /** * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. */ function BuildSagemakerEndpoint(scope, id, props) { /** Conditional Sagemaker endpoint creation */ if (!props.existingSagemakerEndpointObj) { if (props.modelProps) { const deploySagemakerEndpointResponse = deploySagemakerEndpoint(scope, id, props); return { ...deploySagemakerEndpointResponse }; } else { throw Error('Either existingSagemakerEndpointObj or at least modelProps is required'); } } else { /** Otherwise, return [endpoint] */ return { endpoint: props.existingSagemakerEndpointObj }; } } /** * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. */ function deploySagemakerEndpoint(scope, id, props) { let model; let endpointConfig; let endpoint; let sagemakerRole; // Create Sagemaker's model, endpointConfig, and endpoint if (props.modelProps) { // Check if the client has provided executionRoleArn if (props.modelProps.executionRoleArn) { sagemakerRole = iam.Role.fromRoleArn(scope, 'SagemakerRoleCustomer', props.modelProps.executionRoleArn); } else { // Create the Sagemaker Role sagemakerRole = new iam.Role(scope, 'SagemakerRole', { assumedBy: new iam.ServicePrincipal('sagemaker.amazonaws.com'), }); // Add required permissions addPermissions(sagemakerRole, props); (0, utils_1.addCfnGuardSuppressRules)(sagemakerRole, ["IAM_NO_INLINE_POLICY_CHECK"]); } // Create Sagemaker Model model = createSagemakerModel(scope, props.modelProps, sagemakerRole, props.vpc); // Create Sagemaker EndpointConfig endpointConfig = createSagemakerEndpointConfig(scope, `${id}`, model.attrModelName, props.endpointConfigProps); // Add dependency on model endpointConfig.addDependency(model); // Create Sagemaker Endpoint endpoint = createSagemakerEndpoint(scope, endpointConfig.attrEndpointConfigName, props.endpointProps); // Add dependency on EndpointConfig endpoint.addDependency(endpointConfig); return { endpoint, endpointConfig, model }; } else { throw Error('You need to provide at least modelProps to create Sagemaker Endpoint'); } } /** * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. */ function createSagemakerModel(scope, modelProps, role, vpc) { let finalModelProps; let primaryContainer; let vpcConfig; let model; if (vpc) { const modelDefaultSecurityGroup = new ec2.SecurityGroup(scope, 'ReplaceModelDefaultSecurityGroup', { vpc, allowAllOutbound: true, }); // Allow https traffic from within the VPC modelDefaultSecurityGroup.addIngressRule(ec2.Peer.ipv4(vpc.vpcCidrBlock), ec2.Port.tcp(443)); const cfnSecurityGroup = modelDefaultSecurityGroup.node.findChild('Resource'); (0, utils_1.addCfnSuppressRules)(cfnSecurityGroup, [ { id: 'W5', reason: 'Egress of 0.0.0.0/0 is default and generally considered OK', }, { id: 'W40', reason: 'Egress IPProtocol of -1 is default and generally considered OK', } ]); // Throw an error if the VPC does not contain private or isolated subnets if (vpc.privateSubnets.length === 0 && vpc.isolatedSubnets.length === 0) { throw Error('VPC must contain private or isolated subnets to deploy the Sagemaker endpoint in a vpc'); } vpcConfig = { // default SubnetType.PRIVATE (or ISOLATED or PUBLIC if there are no PRIVATE subnets) // So, private subnets will be used if provided by customer. Otherwise, use the default isolated subnets, subnets: vpc.selectSubnets({ onePerAz: true, }).subnetIds, securityGroupIds: [modelDefaultSecurityGroup.securityGroupId], }; } if (modelProps.primaryContainer) { // Get user provided Model's primary container primaryContainer = modelProps.primaryContainer; // Get default Model props finalModelProps = (0, utils_1.consolidateProps)((0, sagemaker_defaults_1.DefaultSagemakerModelProps)(role.roleArn, primaryContainer, vpcConfig), modelProps); // Create the Sagemaker's Model model = new sagemaker.CfnModel(scope, 'SagemakerModel', finalModelProps); // Add dependency on the Sagemaker's role model.node.addDependency(role); return model; } else { throw Error('You need to provide at least primaryContainer to create Sagemaker Model'); } } /** * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. */ function createSagemakerEndpointConfig(scope, id, modelName, endpointConfigProps) { let kmsKeyId; // Create encryption key if one is not provided if (endpointConfigProps && endpointConfigProps.kmsKeyId) { kmsKeyId = endpointConfigProps.kmsKeyId; } else { kmsKeyId = (0, kms_helper_1.buildEncryptionKey)(scope, id).keyId; } // Overwrite default EndpointConfig properties const finalEndpointConfigProps = (0, utils_1.consolidateProps)((0, sagemaker_defaults_1.DefaultSagemakerEndpointConfigProps)(modelName, kmsKeyId), endpointConfigProps); // Create the Sagemaker's EndpointConfig const endpointConfig = new sagemaker.CfnEndpointConfig(scope, 'SagemakerEndpointConfig', finalEndpointConfigProps); return endpointConfig; } /** * @internal This is an internal core function and should not be called directly by Solutions Constructs clients. */ function createSagemakerEndpoint(scope, endpointConfigName, endpointProps) { // Overwrite default Endpoint properties const finalEndpointProps = (0, utils_1.consolidateProps)((0, sagemaker_defaults_1.DefaultSagemakerEndpointProps)(endpointConfigName), endpointProps); // Create the Sagemaker's Endpoint const endpoint = new sagemaker.CfnEndpoint(scope, 'SagemakerEndpoint', finalEndpointProps); return endpoint; } function CheckSagemakerProps(propsObject) { let errorMessages = ''; let errorFound = false; if (propsObject.existingSagemakerEndpointObj && propsObject.endpointProps) { errorMessages += 'Error - Either provide endpointProps or existingSagemakerEndpointObj, but not both.\n'; errorFound = true; } if (errorFound) { throw new Error(errorMessages); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2FnZW1ha2VyLWhlbHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNhZ2VtYWtlci1oZWxwZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7OztHQVdHOztBQWlPSCx3REFrRkM7QUF3REQsd0RBaUJDO0FBV0QsMERBNENDO0FBS0Qsb0RBOERDO0FBS0Qsc0VBdUJDO0FBS0QsMERBYUM7QUFPRCxrREFZQztBQXJqQkQ7OztHQUdHO0FBRUgsdURBQXVEO0FBQ3ZELDJDQUEyQztBQUMzQyw2Q0FBa0Q7QUFDbEQsNkRBSzhCO0FBQzlCLG1DQUFtQztBQUNuQyxtQ0FBMEY7QUFDMUYsNkNBQXdDO0FBQ3hDLDJDQUEyQztBQUMzQyw2Q0FBa0M7QUFDbEMsaURBQThEO0FBQzlELG1FQUE2RDtBQWdDN0QsU0FBUyxjQUFjLENBQUMsSUFBYyxFQUFFLEtBQW1DO0lBQ3pFLDRFQUE0RTtJQUM1RSxJQUFJLENBQUMsV0FBVyxDQUNkLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztRQUN0QixTQUFTLEVBQUUsQ0FBQyxPQUFPLGlCQUFHLENBQUMsU0FBUyxjQUFjLGlCQUFHLENBQUMsTUFBTSxJQUFJLGlCQUFHLENBQUMsVUFBVSxJQUFJLENBQUM7UUFDL0UsT0FBTyxFQUFFO1lBQ1AsNkJBQTZCO1lBQzdCLCtCQUErQjtZQUMvQix1QkFBdUI7WUFDdkIseUJBQXlCO1lBQ3pCLHVCQUF1QjtZQUN2QiwwQkFBMEI7WUFDMUIsZ0NBQWdDO1lBQ2hDLDRCQUE0QjtZQUM1QixrQ0FBa0M7WUFDbEMsMEJBQTBCO1lBQzFCLGdDQUFnQztZQUNoQywwQkFBMEI7U0FDM0I7S0FDRixDQUFDLENBQ0gsQ0FBQztJQUVGLHVDQUF1QztJQUN2QyxJQUFJLENBQUMsV0FBVyxDQUNkLElBQUksR0FBRyxDQUFDLGVBQWUsQ0FBQztRQUN0QixTQUFTLEVBQUUsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxHQUFHLENBQUMsU0FBUyxTQUFTLEdBQUcsQ0FBQyxH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Q0FBQztJQUVsSCxPQUFPLFFBQVEsQ0FBQztBQUNsQixDQUFDO0FBT0QsU0FBZ0IsbUJBQW1CLENBQUMsV0FBaUM7SUFDbkUsSUFBSSxhQUFhLEdBQUcsRUFBRSxDQUFDO0lBQ3ZCLElBQUksVUFBVSxHQUFHLEtBQUssQ0FBQztJQUV2QixJQUFJLFdBQVcsQ0FBQyw0QkFBNEIsSUFBSSxXQUFXLENBQUMsYUFBYSxFQUFFLENBQUM7UUFDMUUsYUFBYSxJQUFJLHVGQUF1RixDQUFDO1FBQ3pHLFVBQVUsR0FBRyxJQUFJLENBQUM7SUFDcEIsQ0FBQztJQUVELElBQUksVUFBVSxFQUFFLENBQUM7UUFDZixNQUFNLElBQUksS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ2pDLENBQUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiAgQ29weXJpZ2h0IEFtYXpvbi5jb20sIEluYy4gb3IgaXRzIGFmZmlsaWF0ZXMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIikuIFlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2VcbiAqICB3aXRoIHRoZSBMaWNlbnNlLiBBIGNvcHkgb2YgdGhlIExpY2Vuc2UgaXMgbG9jYXRlZCBhdFxuICpcbiAqICAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogIG9yIGluIHRoZSAnbGljZW5zZScgZmlsZSBhY2NvbXBhbnlpbmcgdGhpcyBmaWxlLiBUaGlzIGZpbGUgaXMgZGlzdHJpYnV0ZWQgb24gYW4gJ0FTIElTJyBCQVNJUywgV0lUSE9VVCBXQVJSQU5USUVTXG4gKiAgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZXhwcmVzcyBvciBpbXBsaWVkLiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnNcbiAqICBhbmQgbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxuLypcbiAqICBUaGUgZnVuY3Rpb25zIGZvdW5kIGhlcmUgaW4gdGhlIGNvcmUgbGlicmFyeSBhcmUgZm9yIGludGVybmFsIHVzZSBhbmQgY2FuIGJlIGNoYW5nZWRcbiAqICBvciByZW1vdmVkIG91dHNpZGUgb2YgYSBtYWpvciByZWxlYXNlLiBXZSByZWNvbW1lbmQgYWdhaW5zdCBjYWxsaW5nIHRoZW0gZGlyZWN0bHkgZnJvbSBjbGllbnQgY29kZS5cbiAqL1xuXG5pbXBvcnQgKiBhcyBzYWdlbWFrZXIgZnJvbSAnYXdzLWNkay1saWIvYXdzLXNhZ2VtYWtlcic7XG5pbXBvcnQgKiBhcyBlYzIgZnJvbSAnYXdzLWNkay1saWIvYXdzLWVjMic7XG5pbXBvcnQgeyBidWlsZEVuY3J5cHRpb25LZXkgfSBmcm9tICcuL2ttcy1oZWxwZXInO1xuaW1wb3J0IHtcbiAgRGVmYXVsdFNhZ2VtYWtlck5vdGVib29rUHJvcHMsXG4gIERlZmF1bHRTYWdlbWFrZXJNb2RlbFByb3BzLFxuICBEZWZhdWx0U2FnZW1ha2VyRW5kcG9pbnRDb25maWdQcm9wcyxcbiAgRGVmYXVsdFNhZ2VtYWtlckVuZHBvaW50UHJvcHMsXG59IGZyb20gJy4vc2FnZW1ha2VyLWRlZmF1bHRzJztcbmltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBhZGRDZm5HdWFyZFN1cHByZXNzUnVsZXMsIGFkZENmblN1cHByZXNzUnVsZXMsIGNvbnNvbGlkYXRlUHJvcHMgfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7IGJ1aWxkVnBjIH0gZnJvbSAnLi92cGMtaGVscGVyJztcbmltcG9ydCAqIGFzIGlhbSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcbmltcG9ydCB7IEF3cyB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IERlZmF1bHRQdWJsaWNQcml2YXRlVnBjUHJvcHMgfSBmcm9tICcuL3ZwYy1kZWZhdWx0cyc7XG5pbXBvcnQgeyBidWlsZFNlY3VyaXR5R3JvdXAgfSBmcm9tICcuL3NlY3VyaXR5LWdyb3VwLWhlbHBlcic7XG4vLyBOb3RlOiBUbyBlbnN1cmUgQ0RLdjIgY29tcGF0aWJpbGl0eSwga2VlcCB0aGUgaW1wb3J0IHN0YXRlbWVudCBmb3IgQ29uc3RydWN0IHNlcGFyYXRlXG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcblxuZXhwb3J0IGludGVyZmFjZSBCdWlsZFNhZ2VtYWtlck5vdGVib29rUHJvcHMge1xuICAvKipcbiAgICogT3B0aW9uYWwgdXNlciBwcm92aWRlZCBwcm9wcyBmb3IgQ2ZuTm90ZWJvb2tJbnN0YW5jZVByb3BzXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gRGVmYXVsdCBwcm9wcyBhcmUgdXNlZFxuICAgKi9cbiAgcmVhZG9ubHkgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcz86IHNhZ2VtYWtlci5DZm5Ob3RlYm9va0luc3RhbmNlUHJvcHMgfCBhbnk7XG4gIC8qKlxuICAgKiBPcHRpb25hbCB1c2VyIHByb3ZpZGVkIHByb3BzIHRvIGRlcGxveSBpbnNpZGUgdnBjXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdHJ1ZVxuICAgKi9cbiAgcmVhZG9ubHkgZGVwbG95SW5zaWRlVnBjPzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIEFuIG9wdGlvbmFsLCBFeGlzdGluZyBpbnN0YW5jZSBvZiBub3RlYm9vayBvYmplY3QuXG4gICAqIElmIHRoaXMgaXMgc2V0IHRoZW4gdGhlIHNhZ2VtYWtlck5vdGVib29rUHJvcHMgaXMgaWdub3JlZFxuICAgKlxuICAgKiBAZGVmYXVsdCAtIE5vbmVcbiAgICovXG4gIHJlYWRvbmx5IGV4aXN0aW5nTm90ZWJvb2tPYmo/OiBzYWdlbWFrZXIuQ2ZuTm90ZWJvb2tJbnN0YW5jZTtcbiAgLyoqXG4gICAqIElBTSBSb2xlIEFybiBmb3IgU2FnZW1ha2VyIE5vdGVCb29rSW5zdGFuY2VcbiAgICpcbiAgICogQGRlZmF1bHQgLSBOb25lXG4gICAqL1xuICByZWFkb25seSByb2xlOiBpYW0uUm9sZTtcbn1cblxuZnVuY3Rpb24gYWRkUGVybWlzc2lvbnMocm9sZTogaWFtLlJvbGUsIHByb3BzPzogQnVpbGRTYWdlbWFrZXJFbmRwb2ludFByb3BzKSB7XG4gIC8vIEdyYW50IHBlcm1pc3Npb25zIHRvIE5vdGVCb29rSW5zdGFuY2UgZm9yIGNyZWF0aW5nIGFuZCB0cmFpbmluZyB0aGUgbW9kZWxcbiAgcm9sZS5hZGRUb1BvbGljeShcbiAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICByZXNvdXJjZXM6IFtgYXJuOiR7QXdzLlBBUlRJVElPTn06c2FnZW1ha2VyOiR7QXdzLlJFR0lPTn06JHtBd3MuQUNDT1VOVF9JRH06KmBdLFxuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnc2FnZW1ha2VyOkNyZWF0ZVRyYWluaW5nSm9iJyxcbiAgICAgICAgJ3NhZ2VtYWtlcjpEZXNjcmliZVRyYWluaW5nSm9iJyxcbiAgICAgICAgJ3NhZ2VtYWtlcjpDcmVhdGVNb2RlbCcsXG4gICAgICAgICdzYWdlbWFrZXI6RGVzY3JpYmVNb2RlbCcsXG4gICAgICAgICdzYWdlbWFrZXI6RGVsZXRlTW9kZWwnLFxuICAgICAgICAnc2FnZW1ha2VyOkNyZWF0ZUVuZHBvaW50JyxcbiAgICAgICAgJ3NhZ2VtYWtlcjpDcmVhdGVFbmRwb2ludENvbmZpZycsXG4gICAgICAgICdzYWdlbWFrZXI6RGVzY3JpYmVFbmRwb2ludCcsXG4gICAgICAgICdzYWdlbWFrZXI6RGVzY3JpYmVFbmRwb2ludENvbmZpZycsXG4gICAgICAgICdzYWdlbWFrZXI6RGVsZXRlRW5kcG9pbnQnLFxuICAgICAgICAnc2FnZW1ha2VyOkRlbGV0ZUVuZHBvaW50Q29uZmlnJyxcbiAgICAgICAgJ3NhZ2VtYWtlcjpJbnZva2VFbmRwb2ludCcsXG4gICAgICBdLFxuICAgIH0pXG4gICk7XG5cbiAgLy8gR3JhbnQgQ2xvdWRXYXRjaCBMb2dnaW5nIHBlcm1pc3Npb25zXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgcmVzb3VyY2VzOiBbYGFybjoke2Nkay5Bd3MuUEFSVElUSU9OfTpsb2dzOiR7Y2RrLkF3cy5SRUdJT059OiR7Y2RrLkF3cy5BQ0NPVU5UX0lEfTpsb2ctZ3JvdXA6L2F3cy9zYWdlbWFrZXIvKmBdLFxuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnbG9nczpDcmVhdGVMb2dHcm91cCcsXG4gICAgICAgICdsb2dzOkNyZWF0ZUxvZ1N0cmVhbScsXG4gICAgICAgICdsb2dzOkRlc2NyaWJlTG9nU3RyZWFtcycsXG4gICAgICAgICdsb2dzOkdldExvZ0V2ZW50cycsXG4gICAgICAgICdsb2dzOlB1dExvZ0V2ZW50cycsXG4gICAgICBdLFxuICAgIH0pXG4gICk7XG5cbiAgLy8gVG8gcGxhY2UgdGhlIFNhZ2VtYWtlciBlbmRwb2ludCBpbiBhIFZQQ1xuICBpZiAocHJvcHMgJiYgcHJvcHMudnBjKSB7XG4gICAgcm9sZS5hZGRUb1BvbGljeShcbiAgICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgICAgYWN0aW9uczogW1xuICAgICAgICAgICdlYzI6Q3JlYXRlTmV0d29ya0ludGVyZmFjZScsXG4gICAgICAgICAgJ2VjMjpDcmVhdGVOZXR3b3JrSW50ZXJmYWNlUGVybWlzc2lvbicsXG4gICAgICAgICAgJ2VjMjpEZWxldGVOZXR3b3JrSW50ZXJmYWNlJyxcbiAgICAgICAgICAnZWMyOkRlbGV0ZU5ldHdvcmtJbnRlcmZhY2VQZXJtaXNzaW9uJyxcbiAgICAgICAgICAnZWMyOkRlc2NyaWJlTmV0d29ya0ludGVyZmFjZXMnLFxuICAgICAgICAgICdlYzI6QXNzaWduUHJpdmF0ZUlwQWRkcmVzc2VzJyxcbiAgICAgICAgICAnZWMyOlVuYXNzaWduUHJpdmF0ZUlwQWRkcmVzc2VzJyxcbiAgICAgICAgICAnZWMyOkRlc2NyaWJlVnBjcycsXG4gICAgICAgICAgJ2VjMjpEZXNjcmliZURoY3BPcHRpb25zJyxcbiAgICAgICAgICAnZWMyOkRlc2NyaWJlU3VibmV0cycsXG4gICAgICAgICAgJ2VjMjpEZXNjcmliZVNlY3VyaXR5R3JvdXBzJyxcbiAgICAgICAgXSxcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIC8vIFRvIGNyZWF0ZSBhIFNhZ2VtYWtlciBtb2RlbCB1c2luZyBCcmluZy1Zb3VyLU93bi1Nb2RlbCAoQllPTSkgYWxnb3JpdGhtIGltYWdlXG4gIC8vIFRoZSBpbWFnZSBVUkwgaXMgc3BlY2lmaWVkIGluIHRoZSBtb2RlbFByb3BzXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgcmVzb3VyY2VzOiBbYGFybjoke2Nkay5Bd3MuUEFSVElUSU9OfTplY3I6JHtjZGsuQXdzLlJFR0lPTn06JHtjZGsuQXdzLkFDQ09VTlRfSUR9OnJlcG9zaXRvcnkvKmBdLFxuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnZWNyOkJhdGNoQ2hlY2tMYXllckF2YWlsYWJpbGl0eScsXG4gICAgICAgICdlY3I6R2V0RG93bmxvYWRVcmxGb3JMYXllcicsXG4gICAgICAgICdlY3I6RGVzY3JpYmVSZXBvc2l0b3JpZXMnLFxuICAgICAgICAnZWNyOkRlc2NyaWJlSW1hZ2VzJyxcbiAgICAgICAgJ2VjcjpCYXRjaEdldEltYWdlJyxcbiAgICAgIF0sXG4gICAgfSlcbiAgKTtcblxuICAvLyBBZGQgR2V0QXV0aG9yaXphdGlvblRva2VuIChpdCBjYW4gbm90IGJlIGJvdW5kIHRvIHJlc291cmNlcyBvdGhlciB0aGFuICopXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgIGFjdGlvbnM6IFsnZWNyOkdldEF1dGhvcml6YXRpb25Ub2tlbiddLFxuICAgIH0pXG4gICk7XG5cbiAgLy8gYWRkIHBlcm1pc3Npb24gdG8gdXNlIEVsYXN0aWMgSW5mZXJlbmNlIGFjY2VsZXJhdG9yXG4gIGlmIChwcm9wcyAmJiBwcm9wcy5lbmRwb2ludENvbmZpZ1Byb3BzKSB7XG4gICAgLy8gR2V0IHRoZSBhY2NlbGVyYXRvclR5cGUsIGlmIGFueVxuICAgIGNvbnN0IGFjY2VsZXJhdG9yVHlwZSA9IChwcm9wcy5lbmRwb2ludENvbmZpZ1Byb3BzXG4gICAgICA/LnByb2R1Y3Rpb25WYXJpYW50cyBhcyBzYWdlbWFrZXIuQ2ZuRW5kcG9pbnRDb25maWcuUHJvZHVjdGlvblZhcmlhbnRQcm9wZXJ0eVtdKVswXS5hY2NlbGVyYXRvclR5cGU7XG4gICAgaWYgKGFjY2VsZXJhdG9yVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByb2xlLmFkZFRvUG9saWN5KFxuICAgICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgICAgcmVzb3VyY2VzOiBbJyonXSxcbiAgICAgICAgICBhY3Rpb25zOiBbJ2VsYXN0aWMtaW5mZXJlbmNlOkNvbm5lY3QnXSxcbiAgICAgICAgfSlcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgLy8gYWRkIGttcyBwZXJtaXNzaW9uc1xuICByb2xlLmFkZFRvUG9saWN5KFxuICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgIC8vIHRoZSBrbXNLZXlJZCBpbiB0aGUgZW5kcG9pbnRDb25maWdQcm9wcyBjYW4gYmUgYW55IG9mIHRoZSBmb2xsb3dpbmcgZm9ybWF0czpcbiAgICAgIC8vIEtleSBJRDogMTIzNGFiY2QtMTJhYi0zNGNkLTU2ZWYtMTIzNDU2Nzg5MGFiXG4gICAgICAvLyBLZXkgQVJOOiBhcm46YXdzOmttczo8cmVnaW9uPjo8YWNjb3VudElEPjprZXkvMTIzNGFiY2QtMTJhYi0zNGNkLTU2ZWYtMTIzNDU2Nzg5MGFiXG4gICAgICAvLyBBbGlhcyBuYW1lOiBhbGlhcy9FeGFtcGxlQWxpYXNcbiAgICAgIC8vIEFsaWFzIG5hbWUgQVJOOiBhcm46YXdzOmttczo8cmVnaW9uPjo8YWNjb3VudElEPjphbGlhcy9FeGFtcGxlQWxpYXNcbiAgICAgIC8vIHRoZSBrZXkgaXMgdXNlZCB0byBlbmNyeXB0L2RlY3J5cHQgZGF0YSBjYXB0dXJlZCBieSB0aGUgU2FnZW1ha2VyIGVuZHBvaW50IGFuZCBzdG9yZWQgaW4gUzMgYnVja2V0XG4gICAgICByZXNvdXJjZXM6IFtcbiAgICAgICAgYGFybjoke2Nkay5Bd3MuUEFSVElUSU9OfTprbXM6JHtjZGsuQXdzLlJFR0lPTn06JHtjZGsuQXdzLkFDQ09VTlRfSUR9OmtleS8qYCxcbiAgICAgICAgYGFybjoke2Nkay5Bd3MuUEFSVElUSU9OfTprbXM6JHtjZGsuQXdzLlJFR0lPTn06JHtjZGsuQXdzLkFDQ09VTlRfSUR9OmFsaWFzLypgLFxuICAgICAgXSxcbiAgICAgIGFjdGlvbnM6IFsna21zOkVuY3J5cHQnLCAna21zOkRlY3J5cHQnLCAna21zOlJlRW5jcnlwdConLCAna21zOkdlbmVyYXRlRGF0YUtleSonLCAna21zOkRlc2NyaWJlS2V5J10sXG4gICAgfSlcbiAgKTtcblxuICAvLyBBZGQgUzMgcGVybWlzc2lvbnMgdG8gZ2V0IE1vZGVsIGFydGlmYWN0LCBwdXQgZGF0YSBjYXB0dXJlIGZpbGVzLCBldGMuXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgYWN0aW9uczogWydzMzpHZXRPYmplY3QnLCAnczM6UHV0T2JqZWN0JywgJ3MzOkRlbGV0ZU9iamVjdCcsICdzMzpMaXN0QnVja2V0J10sXG4gICAgICByZXNvdXJjZXM6IFtgYXJuOmF3czpzMzo6OipgXSxcbiAgICB9KVxuICApO1xuXG4gIC8vIEdyYW50IEdldFJvbGUgcGVybWlzc2lvbnMgdG8gdGhlIFNhZ2VtYWtlciBzZXJ2aWNlXG4gIHJvbGUuYWRkVG9Qb2xpY3koXG4gICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgcmVzb3VyY2VzOiBbcm9sZS5yb2xlQXJuXSxcbiAgICAgIGFjdGlvbnM6IFsnaWFtOkdldFJvbGUnXSxcbiAgICB9KVxuICApO1xuXG4gIC8vIEdyYW50IFBhc3NSb2xlIHBlcm1pc3Npb25zIHRvIHRoZSBTYWdlbWFrZXIgc2VydmljZVxuICByb2xlLmFkZFRvUG9saWN5KFxuICAgIG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgIHJlc291cmNlczogW3JvbGUucm9sZUFybl0sXG4gICAgICBhY3Rpb25zOiBbJ2lhbTpQYXNzUm9sZSddLFxuICAgICAgY29uZGl0aW9uczoge1xuICAgICAgICBTdHJpbmdMaWtlOiB7ICdpYW06UGFzc2VkVG9TZXJ2aWNlJzogJ3NhZ2VtYWtlci5hbWF6b25hd3MuY29tJyB9LFxuICAgICAgfSxcbiAgICB9KVxuICApO1xuXG4gIC8vIEFkZCBDRk4gTkFHIHVwcHJlc3MgdG8gYWxsb3cgZm9yIFwiUmVzb3VyY2VcIjogXCIqXCIgZm9yIEVOSSBhY2Nlc3MgaW4gVlBDLFxuICAvLyBFQ1IgYXV0aG9yaXphdGlvbiB0b2tlbiBmb3IgY3VzdG9tIG1vZGVsIGltYWdlcywgYW5kIGVsYXN0aWMgaW5mZXJlbmNlXG4gIC8vIEFkZCBDRk4gTkFHIGZvciBDb21wbGV4IFJvbGUgYmVjYXVzZSBTYWdtYWtlciBuZWVkcyBwZXJtaXNzaW9ucyB0byBhY2Nlc3Mgc2V2ZXJhbCBzZXJ2aWNlc1xuICBjb25zdCByb2xlRGVmYXVsdFBvbGljeSA9IHJvbGUubm9kZS50cnlGaW5kQ2hpbGQoJ0RlZmF1bHRQb2xpY3knKT8ubm9kZS5maW5kQ2hpbGQoJ1Jlc291cmNlJykgYXMgaWFtLkNmblBvbGljeTtcbiAgYWRkQ2ZuU3VwcHJlc3NSdWxlcyhyb2xlRGVmYXVsdFBvbGljeSwgW1xuICAgIHtcbiAgICAgIGlkOiAnVzEyJyxcbiAgICAgIHJlYXNvbjogYFNhZ2VtYWtlciBuZWVkcyB0aGUgZm9sbG93aW5nIG1pbmltdW0gcmVxdWlyZWQgcGVybWlzc2lvbnMgdG8gYWNjZXNzIEVOSXMgaW4gYSBWUEMsIEVDUiBmb3IgY3VzdG9tIG1vZGVsIGltYWdlcywgYW5kIGVsYXN0aWMgaW5mZXJlbmNlLmAsXG4gICAgfSxcbiAgICB7XG4gICAgICBpZDogJ1c3NicsXG4gICAgICByZWFzb246ICdDb21wbGV4IHJvbGUgYmVjdWFzZSBTYWdlbWFrZXIgbmVlZHMgcGVybWlzc2lvbnMgdG8gYWNjZXNzIHNldmVyYWwgc2VydmljZXMnLFxuICAgIH1cbiAgXSk7XG4gIGFkZENmbkd1YXJkU3VwcHJlc3NSdWxlcyhyb2xlRGVmYXVsdFBvbGljeSwgW1wiSUFNX1BPTElDWV9OT05fQ09NUExJQU5UX0FSTlwiXSk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQnVpbGRTYWdlbWFrZXJOb3RlYm9va1Jlc3BvbnNlIHtcbiAgcmVhZG9ubHkgbm90ZWJvb2s6IHNhZ2VtYWtlci5DZm5Ob3RlYm9va0luc3RhbmNlLFxuICByZWFkb25seSB2cGM/OiBlYzIuSVZwYyxcbiAgcmVhZG9ubHkgc2VjdXJpdHlHcm91cD86IGVjMi5TZWN1cml0eUdyb3VwXG59XG5cbi8qKlxuICogQGludGVybmFsIFRoaXMgaXMgYW4gaW50ZXJuYWwgY29yZSBmdW5jdGlvbiBhbmQgc2hvdWxkIG5vdCBiZSBjYWxsZWQgZGlyZWN0bHkgYnkgU29sdXRpb25zIENvbnN0cnVjdHMgY2xpZW50cy5cbiAqXG4gKiBUaGUgY29uc3RydWN0cyBkZXBsb3lpbmcgU2FnZW1ha2VyIHNlcnZpY2VzIG9ubHkgZGVwbG95IFNhZ2VtYWtlciBFbmRwb2ludHMsIEVuZHBvaW50XG4gKiBDb25maWd1cmF0aW9ucyBhbmQgKGFscmVhZHkgZGV2ZWxvcGVkKSBNb2RlbHMuIFRoZXJlIGFyZSBubyBDb25zdHJ1Y3RzIHRoYXQgZGVwbG95IE5vdGVib29rcy5cbiAqIFRoZXJlIGRvZXNuJ3QgYXBwZWFyIHRvIGJlIGFueSByZWZlcmVuY2VzIHRvIHRoaXMgZnVuY3Rpb24gb3V0c2lkZSB0aGUgdW5pdCB0ZXN0IGZpbGUgLSBzb1xuICogdGhpcyBmdW5jdGlvbiBpcyB1bm5lY2Nlc3NhcnkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBidWlsZFNhZ2VtYWtlck5vdGVib29rKFxuICBzY29wZTogQ29uc3RydWN0LFxuICBpZDogc3RyaW5nLFxuICBwcm9wczogQnVpbGRTYWdlbWFrZXJOb3RlYm9va1Byb3BzXG4pOiBCdWlsZFNhZ2VtYWtlck5vdGVib29rUmVzcG9uc2Uge1xuICAvLyBTZXR1cCB0aGUgbm90ZWJvb2sgcHJvcGVydGllc1xuICBsZXQgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcztcbiAgbGV0IHZwY0luc3RhbmNlO1xuICBsZXQgc2VjdXJpdHlHcm91cDtcbiAgbGV0IGttc0tleUlkOiBzdHJpbmc7XG4gIGxldCBzdWJuZXRJZDogc3RyaW5nO1xuXG4gIC8vIENvbmRpdGlvbmFsIFNhZ2VtYWtlciBOb3RlYm9vayBjcmVhdGlvblxuICBpZiAocHJvcHMuZXhpc3RpbmdOb3RlYm9va09iaikge1xuICAgIHJldHVybiB7IG5vdGVib29rOiBwcm9wcy5leGlzdGluZ05vdGVib29rT2JqIH07XG4gIH1cblxuICBpZiAoQ2hlY2tOb3RlYm9va1ZwY1Byb3BzKHByb3BzKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignTXVzdCBkZWZpbmUgYm90aCBzYWdlbWFrZXJOb3RlYm9va1Byb3BzLnN1Ym5ldElkIGFuZCBzYWdlbWFrZXJOb3RlYm9va1Byb3BzLnNlY3VyaXR5R3JvdXBJZHMnKTtcbiAgfVxuXG4gIGFkZFBlcm1pc3Npb25zKHByb3BzLnJvbGUpO1xuXG4gIGlmIChwcm9wcy5zYWdlbWFrZXJOb3RlYm9va1Byb3BzPy5rbXNLZXlJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAga21zS2V5SWQgPSBidWlsZEVuY3J5cHRpb25LZXkoc2NvcGUsICBpZCkua2V5SWQ7XG4gIH0gZWxzZSB7XG4gICAga21zS2V5SWQgPSBwcm9wcy5zYWdlbWFrZXJOb3RlYm9va1Byb3BzLmttc0tleUlkO1xuICB9XG5cbiAgaWYgKHByb3BzLmRlcGxveUluc2lkZVZwYyA9PT0gdW5kZWZpbmVkIHx8IHByb3BzLmRlcGxveUluc2lkZVZwYykge1xuICAgIGlmIChcbiAgICAgIHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/LnN1Ym5ldElkID09PSB1bmRlZmluZWQgJiZcbiAgICAgIHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/LnNlY3VyaXR5R3JvdXBJZHMgPT09IHVuZGVmaW5lZFxuICAgICkge1xuICAgICAgdnBjSW5zdGFuY2UgPSBidWlsZFZwYyhzY29wZSwge1xuICAgICAgICBkZWZhdWx0VnBjUHJvcHM6IERlZmF1bHRQdWJsaWNQcml2YXRlVnBjUHJvcHMoKSxcbiAgICAgIH0pO1xuICAgICAgc2VjdXJpdHlHcm91cCA9IGJ1aWxkU2VjdXJpdHlHcm91cChcbiAgICAgICAgc2NvcGUsXG4gICAgICAgICdTZWN1cml0eUdyb3VwJyxcbiAgICAgICAge1xuICAgICAgICAgIHZwYzogdnBjSW5zdGFuY2UsXG4gICAgICAgICAgYWxsb3dBbGxPdXRib3VuZDogZmFsc2UsXG4gICAgICAgIH0sXG4gICAgICAgIFtdLFxuICAgICAgICBbeyBwZWVyOiBlYzIuUGVlci5hbnlJcHY0KCksIGNvbm5lY3Rpb246IGVjMi5Qb3J0LnRjcCg0NDMpIH1dXG4gICAgICApO1xuXG4gICAgICBzdWJuZXRJZCA9IHZwY0luc3RhbmNlLnByaXZhdGVTdWJuZXRzWzBdLnN1Ym5ldElkO1xuXG4gICAgICBzYWdlbWFrZXJOb3RlYm9va1Byb3BzID0gRGVmYXVsdFNhZ2VtYWtlck5vdGVib29rUHJvcHMocHJvcHMucm9sZS5yb2xlQXJuLCBrbXNLZXlJZCwgc3VibmV0SWQsIFtcbiAgICAgICAgc2VjdXJpdHlHcm91cC5zZWN1cml0eUdyb3VwSWQsXG4gICAgICBdKTtcbiAgICB9IGVsc2Uge1xuICAgICAgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcyA9IERlZmF1bHRTYWdlbWFrZXJOb3RlYm9va1Byb3BzKFxuICAgICAgICBwcm9wcy5yb2xlLnJvbGVBcm4sXG4gICAgICAgIGttc0tleUlkLFxuICAgICAgICBwcm9wcy5zYWdlbWFrZXJOb3RlYm9va1Byb3BzPy5zdWJuZXRJZCxcbiAgICAgICAgcHJvcHMuc2FnZW1ha2VyTm90ZWJvb2tQcm9wcz8uc2VjdXJpdHlHcm91cElkc1xuICAgICAgKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcyA9IERlZmF1bHRTYWdlbWFrZXJOb3RlYm9va1Byb3BzKHByb3BzLnJvbGUucm9sZUFybiwga21zS2V5SWQpO1xuICB9XG5cbiAgc2FnZW1ha2VyTm90ZWJvb2tQcm9wcyA9IGNvbnNvbGlkYXRlUHJvcHMoc2FnZW1ha2VyTm90ZWJvb2tQcm9wcywgcHJvcHMuc2FnZW1ha2VyTm90ZWJvb2tQcm9wcyk7XG5cbiAgLy8gQ3JlYXRlIHRoZSBub3RlYm9va1xuICAvLyBOT1NPTkFSOiAodHlwZXNjcmlwdDpTNjMxOSlcbiAgLy8ga2V5SUQgaXMgY3JlYXRlZCBhYm92ZSBpbiB0aGUgaWYgKHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/Lmttc0tleUlkID09PSB1bmRlZmluZWQpXG4gIC8vIGJsb2NrLiBJdCBpcyB0aGVuIHBhc3NlZCB0byBEZWZhdWx0U2FnZW1ha2VyTm90ZWJvb2tQcm9wcygpXG4gIC8vIFRoaXMgYmVoYXZpb3IgaXMgdmFsaWRhdGVkIGluIHVuaXQgdGVzdFxuICBjb25zdCBzYWdlbWFrZXJJbnN0YW5jZTogc2FnZW1ha2VyLkNmbk5vdGVib29rSW5zdGFuY2UgPSBuZXcgc2FnZW1ha2VyLkNmbk5vdGVib29rSW5zdGFuY2UoXG4gICAgc2NvcGUsXG4gICAgJ1NhZ2VtYWtlck5vdGVib29rJyxcbiAgICBzYWdlbWFrZXJOb3RlYm9va1Byb3BzIC8vIE5PU09OQVJcbiAgKTtcbiAgaWYgKHZwY0luc3RhbmNlKSB7XG4gICAgcmV0dXJuIHsgbm90ZWJvb2s6IHNhZ2VtYWtlckluc3RhbmNlLCB2cGM6IHZwY0luc3RhbmNlLCBzZWN1cml0eUdyb3VwIH07XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHsgbm90ZWJvb2s6IHNhZ2VtYWtlckluc3RhbmNlIH07XG4gIH1cbn1cblxuLyoqXG4gKiBAaW50ZXJuYWwgVGhpcyBpcyBhbiBpbnRlcm5hbCBjb3JlIGZ1bmN0aW9uIGFuZCBzaG91bGQgbm90IGJlIGNhbGxlZCBkaXJlY3RseSBieSBTb2x1dGlvbnMgQ29uc3RydWN0cyBjbGllbnRzLlxuICovXG5mdW5jdGlvbiBDaGVja05vdGVib29rVnBjUHJvcHMocHJvcHM6IEJ1aWxkU2FnZW1ha2VyTm90ZWJvb2tQcm9wcyk6IGJvb2xlYW4ge1xuICBpZiAoKHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/LnN1Ym5ldElkICYmIHByb3BzLnNhZ2VtYWtlck5vdGVib29rUHJvcHM/LnNlY3VyaXR5R3JvdXBJZHMgPT09IHVuZGVmaW5lZCkgfHxcbiAgICAocHJvcHMuc2FnZW1ha2VyTm90ZWJvb2tQcm9wcz8uc3VibmV0SWQgPT09IHVuZGVmaW5lZCAmJiBwcm9wcy5zYWdlbWFrZXJOb3RlYm9va1Byb3BzPy5zZWN1cml0eUdyb3VwSWRzKVxuICApIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICByZXR1cm4gZmFsc2U7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQnVpbGRTYWdlbWFrZXJFbmRwb2ludFByb3BzIHtcbiAgLyoqXG4gICAqIEV4aXN0aW5nIFNhZ2VtYWtlciBFbmRwb2ludCBvYmplY3QsIGlmIHRoaXMgaXMgc2V0IHRoZW4gdGhlIG1vZGVsUHJvcHMsIGVuZHBvaW50Q29uZmlnUHJvcHMsIGFuZCBlbmRwb2ludFByb3BzIGFyZSBpZ25vcmVkXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gTm9uZVxuICAgKi9cbiAgcmVhZG9ubHkgZXhpc3RpbmdTYWdlbWFrZXJFbmRwb2ludE9iaj86IHNhZ2VtYWtlci5DZm5FbmRwb2ludDtcbiAgLyoqXG4gICAqIFVzZXIgcHJvdmlkZWQgcHJvcHMgdG8gY3JlYXRlIFNhZ2VtYWtlciBNb2RlbFxuICAgKlxuICAgKiBAZGVmYXVsdCAtIE5vbmVcbiAgICovXG4gIHJlYWRvbmx5IG1vZGVsUHJvcHM/OiBzYWdlbWFrZXIuQ2ZuTW9kZWxQcm9wcyB8IGFueTtcbiAgLyoqXG4gICAqIFVzZXIgcHJvdmlkZWQgcHJvcHMgdG8gY3JlYXRlIFNhZ2VtYWtlciBFbmRwb2ludCBDb25maWd1cmF0aW9uXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gTm9uZVxuICAgKi9cbiAgcmVhZG9ubHkgZW5kcG9pbnRDb25maWdQcm9wcz86IHNhZ2VtYWtlci5DZm5FbmRwb2ludENvbmZpZ1Byb3BzO1xuICAvKipcbiAgICogVXNlciBwcm92aWRlZCBwcm9wcyB0byBjcmVhdGUgU2FnZW1ha2VyIEVuZHBvaW50XG4gICAqXG4gICAqIEBkZWZhdWx0IC0gTm9uZVxuICAgKi9cbiAgcmVhZG9ubHkgZW5kcG9pbnRQcm9wcz86IHNhZ2VtYWtlci5DZm5FbmRwb2ludFByb3BzO1xuICAvKipcbiAgICogQSBWUEMgd2hlcmUgdGhlIFNhZ2VtYWtlciBFbmRwb2ludCB3aWxsIGJlIHBsYWNlZFxuICAgKlxuICAgKiBAZGVmYXVsdCAtIE5vbmVcbiAgICovXG4gIHJlYWRvbmx5IHZwYz86IGVjMi5JVnBjO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEJ1aWxkU2FnZW1ha2VyRW5kcG9pbnRSZXNwb25zZSB7XG4gIHJlYWRvbmx5IGVuZHBvaW50OiBzYWdlbWFrZXIuQ2ZuRW5kcG9pbnQsXG4gIHJlYWRvbmx5IGVuZHBvaW50Q29uZmlnPzogc2FnZW1ha2VyLkNmbkVuZHBvaW50Q29uZmlnLFxuICByZWFkb25seSBtb2RlbD86IHNhZ2VtYWtlci5DZm5Nb2RlbFxufVxuXG4vKipcbiAqIEBpbnRlcm5hbCBUaGlzIGlzIGFuIGludGVybmFsIGNvcmUgZnVuY3Rpb24gYW5kIHNob3VsZCBub3QgYmUgY2FsbGVkIGRpcmVjdGx5IGJ5IFNvbHV0aW9ucyBDb25zdHJ1Y3RzIGNsaWVudHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBCdWlsZFNhZ2VtYWtlckVuZHBvaW50KFxuICBzY29wZTogQ29uc3RydWN0LFxuICBpZDogc3RyaW5nLFxuICBwcm9wczogQnVpbGRTYWdlbWFrZXJFbmRwb2ludFByb3BzXG4pOiBCdWlsZFNhZ2VtYWtlckVuZHBvaW50UmVzcG9uc2Uge1xuICAvKiogQ29uZGl0aW9uYWwgU2FnZW1ha2VyIGVuZHBvaW50IGNyZWF0aW9uICovXG4gIGlmICghcHJvcHMuZXhpc3RpbmdTYWdlbWFrZXJFbmRwb2ludE9iaikge1xuICAgIGlmIChwcm9wcy5tb2RlbFByb3BzKSB7XG4gICAgICBjb25zdCBkZXBsb3lTYWdlbWFrZXJFbmRwb2ludFJlc3BvbnNlID0gZGVwbG95U2FnZW1ha2VyRW5kcG9pbnQoc2NvcGUsIGlkLCBwcm9wcyk7XG4gICAgICByZXR1cm4geyAuLi5kZXBsb3lTYWdlbWFrZXJFbmRwb2ludFJlc3BvbnNlIH07XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IEVycm9yKCdFaXRoZXIgZXhpc3RpbmdTYWdlbWFrZXJFbmRwb2ludE9iaiBvciBhdCBsZWFzdCBtb2RlbFByb3BzIGlzIHJlcXVpcmVkJyk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIC8qKiBPdGhlcndpc2UsIHJldHVybiBbZW5kcG9pbnRdICovXG4gICAgcmV0dXJuIHsgZW5kcG9pbnQ6IHByb3BzLmV4aXN0aW5nU2FnZW1ha2VyRW5kcG9pbnRPYmogfTtcbi