cdk-rds-sql
Version:
A CDK construct that allows creating roles or users and databases on Aurora Serverless PostgreSQL or MySQL/MariaDB clusters, as well as AWS DSQL clusters.
240 lines • 37.4 kB
JavaScript
;
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Provider = exports.DatabaseEngine = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const fs_1 = require("fs");
const path = require("path");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const dsql = require("aws-cdk-lib/aws-dsql");
const aws_ec2_1 = require("aws-cdk-lib/aws-ec2");
const iam = require("aws-cdk-lib/aws-iam");
const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
const lambda = require("aws-cdk-lib/aws-lambda-nodejs");
const customResources = require("aws-cdk-lib/custom-resources");
const constructs_1 = require("constructs");
/**
* Helper function to determine if a cluster is a DSQL cluster
*/
function isDsqlCluster(cluster) {
return cluster instanceof dsql.CfnCluster;
}
/**
* Supported database engines
*/
var DatabaseEngine;
(function (DatabaseEngine) {
DatabaseEngine["POSTGRES"] = "postgres";
DatabaseEngine["MYSQL"] = "mysql";
DatabaseEngine["DSQL"] = "dsql";
})(DatabaseEngine || (exports.DatabaseEngine = DatabaseEngine = {}));
class Provider extends constructs_1.Construct {
/**
* Import an existing provider Lambda function
*/
static fromProviderAttributes(scope, id, attrs) {
return new ImportedProvider(scope, id, attrs);
}
constructor(scope, id, props) {
super(scope, id);
// Validate configuration
const isDsql = isDsqlCluster(props.cluster);
if (!isDsql && !props.secret) {
throw new Error("Either secret (for traditional RDS) or cluster with DSQL must be provided");
}
if (!isDsql && !props.vpc) {
throw new Error("VPC is required for traditional RDS databases");
}
if (isDsql && props.secret) {
throw new Error("secret should not be provided when using DSQL cluster (uses IAM authentication)");
}
this.secret = props.secret;
this.cluster = props.cluster;
// Determine engine from cluster/instance instead of hardcoding
if (isDsql) {
// DSQL is always PostgreSQL-compatible
this.engine = DatabaseEngine.DSQL;
}
else if ("clusterIdentifier" in props.cluster) {
// It's a DatabaseCluster
const clusterEngine = props.cluster.engine;
this.engine =
clusterEngine && clusterEngine.engineFamily === "MYSQL"
? DatabaseEngine.MYSQL
: DatabaseEngine.POSTGRES;
}
else if ("instanceIdentifier" in props.cluster) {
// It's a DatabaseInstance
const instanceEngine = props.cluster.engine;
this.engine =
instanceEngine && instanceEngine.engineFamily === "MYSQL"
? DatabaseEngine.MYSQL
: DatabaseEngine.POSTGRES;
}
else {
// Fallback to postgres if engine hasn't been provided
this.engine = DatabaseEngine.POSTGRES;
}
const functionName = "RdsSql" + slugify("28b9e791-af60-4a33-bca8-ffb6f30ef8c5");
this.handler =
aws_cdk_lib_1.Stack.of(this).node.tryFindChild(functionName) ??
this.newCustomResourceHandler(scope, functionName, props);
const provider = new customResources.Provider(this, "RdsSql", {
onEventHandler: this.handler,
});
this.serviceToken = provider.serviceToken;
// Handle database connection setup
if (isDsql) {
// For DSQL, grant IAM permissions instead of VPC security groups
const dsqlCluster = props.cluster;
this.handler.addToRolePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["dsql:DbConnectAdmin"],
resources: [dsqlCluster.attrResourceArn],
}));
}
else {
// Traditional RDS setup with security groups and secrets
this.secret.grantRead(this.handler);
if (this.secret.encryptionKey) {
// It seems we need to grant explicit permission
this.secret.encryptionKey.grantDecrypt(this.handler);
}
if (props.cluster.connections.securityGroups.length === 0) {
throw new Error("Cluster does not have a security group.");
}
else {
const securityGroup = props.cluster.connections.securityGroups[0];
this.handler.node.defaultChild?.node.addDependency(securityGroup);
}
}
this.node.addDependency(props.cluster);
}
newCustomResourceHandler(scope, id, props) {
const isDsql = isDsqlCluster(props.cluster);
const handlerDir = path.join(__dirname, "handler");
const index_ts = path.join(handlerDir, "index.ts");
const index_js = path.join(handlerDir, "index.js");
let entry;
if ((0, fs_1.existsSync)(index_ts)) {
entry = index_ts;
}
else if ((0, fs_1.existsSync)(index_js)) {
entry = index_js;
}
else {
// Ugly hack to support SST (possibly caused by my hack to make SST work with CommonJS libraries)
entry = path.join(path.dirname(process.env.npm_package_json || process.cwd()), "node_modules/cdk-rds-sql/lib/handler/index.js");
}
let ssl_options;
if (props.ssl !== undefined && !props.ssl) {
ssl_options = {
SSL: JSON.stringify(props.ssl),
};
}
const logger = props.logger ?? false;
// Build environment variables
const environment = {
LOGGER: logger.toString(),
...ssl_options,
};
// Add DSQL-specific environment variables
if (isDsql) {
const dsqlCluster = props.cluster;
const clusterId = dsqlCluster.attrIdentifier;
const region = aws_cdk_lib_1.Stack.of(scope).region;
environment.DSQL_ENDPOINT = `${clusterId}.dsql.${region}.on.aws`;
environment.DSQL_PORT = "5432";
}
else if (props.secret) {
// Add secret ARN to environment for traditional RDS
environment.SECRET_ARN = props.secret.secretArn;
}
const { partition, region: stackRegion, account } = aws_cdk_lib_1.Stack.of(scope);
const deleteParameterPolicy = new iam.PolicyStatement({
actions: ["ssm:DeleteParameter"],
resources: [`arn:${partition}:ssm:${stackRegion}:${account}:parameter/*`],
conditions: {
StringEquals: {
"ssm:ResourceTag/created-by": "cdk-rds-sql",
},
},
});
const fn = new lambda.NodejsFunction(scope, id, {
...props.functionProps,
depsLockFilePath: path.join(__dirname, "..", "pnpm-lock.yaml"),
// Only configure VPC for traditional RDS
...(isDsql
? {}
: {
vpc: props.vpc,
vpcSubnets: props.vpcSubnets ?? {
subnetType: aws_ec2_1.SubnetType.PRIVATE_ISOLATED,
},
}),
entry: entry,
runtime: aws_lambda_1.Runtime.NODEJS_22_X,
timeout: props.timeout ?? props.functionProps?.timeout ?? aws_cdk_lib_1.Duration.seconds(300),
bundling: {
// Include the migrations directory in the bundle
commandHooks: {
beforeBundling(_, outputDir) {
return [
`curl --silent -fL https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o ${path.join(outputDir, "global-bundle.pem")}`,
];
},
afterBundling() {
return [];
},
beforeInstall() {
return [];
},
},
},
environment,
initialPolicy: [
deleteParameterPolicy,
...(props.functionProps?.initialPolicy ?? []),
],
});
// Only configure security groups for traditional RDS (not DSQL)
if (!isDsql &&
(!props.functionProps?.securityGroups ||
props.functionProps?.securityGroups.length === 0)) {
const rdsCluster = props.cluster;
rdsCluster.connections.allowDefaultPortFrom(fn, "Allow the rds sql handler to connect to db");
}
return fn;
}
}
exports.Provider = Provider;
_a = JSII_RTTI_SYMBOL_1;
Provider[_a] = { fqn: "cdk-rds-sql.Provider", version: "8.1.1" };
class ImportedProvider extends constructs_1.Construct {
constructor(scope, id, attrs) {
super(scope, id);
// Validate that either functionArn or functionName is provided
if (!attrs.functionArn && !attrs.functionName) {
throw new Error("Either functionArn or functionName must be provided");
}
if (attrs.functionArn && attrs.functionName) {
throw new Error("Provide either functionArn or functionName, not both");
}
// Import the existing Lambda function
this.handler = attrs.functionArn
? aws_lambda_1.Function.fromFunctionArn(this, "Handler", attrs.functionArn)
: aws_lambda_1.Function.fromFunctionName(this, "Handler", attrs.functionName);
// Derive serviceToken by wrapping in custom resource provider
const provider = new customResources.Provider(this, "Provider", {
onEventHandler: this.handler,
});
this.serviceToken = provider.serviceToken;
this.engine = attrs.engine;
this.secret = undefined; // Imported providers get secret from environment
this.cluster = attrs.cluster; // Optional cluster for role creation
}
}
function slugify(x) {
return x.replace(/[^a-zA-Z0-9]/g, "");
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcHJvdmlkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFBQSwyQkFBK0I7QUFDL0IsNkJBQTRCO0FBQzVCLDZDQUE2QztBQUM3Qyw2Q0FBNEM7QUFDNUMsaURBQXVFO0FBQ3ZFLDJDQUEwQztBQUMxQyx1REFBcUU7QUFDckUsd0RBQXVEO0FBSXZELGdFQUErRDtBQUMvRCwyQ0FBc0M7QUFFdEM7O0dBRUc7QUFDSCxTQUFTLGFBQWEsQ0FDcEIsT0FBK0Q7SUFFL0QsT0FBTyxPQUFPLFlBQVksSUFBSSxDQUFDLFVBQVUsQ0FBQTtBQUMzQyxDQUFDO0FBd0VEOztHQUVHO0FBQ0gsSUFBWSxjQUlYO0FBSkQsV0FBWSxjQUFjO0lBQ3hCLHVDQUFxQixDQUFBO0lBQ3JCLGlDQUFlLENBQUE7SUFDZiwrQkFBYSxDQUFBO0FBQ2YsQ0FBQyxFQUpXLGNBQWMsOEJBQWQsY0FBYyxRQUl6QjtBQWlDRCxNQUFhLFFBQVMsU0FBUSxzQkFBUztJQUNyQzs7T0FFRztJQUNILE1BQU0sQ0FBQyxzQkFBc0IsQ0FDM0IsS0FBZ0IsRUFDaEIsRUFBVSxFQUNWLEtBQXlCO1FBRXpCLE9BQU8sSUFBSSxnQkFBZ0IsQ0FBQyxLQUFLLEVBQUUsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFBO0lBQy9DLENBQUM7SUFjRCxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQWtCO1FBQzFELEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUE7UUFFaEIseUJBQXlCO1FBQ3pCLE1BQU0sTUFBTSxHQUFHLGFBQWEsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDM0MsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUM3QixNQUFNLElBQUksS0FBSyxDQUNiLDJFQUEyRSxDQUM1RSxDQUFBO1FBQ0gsQ0FBQztRQUNELElBQUksQ0FB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LENBQUE7UUFDeEUsQ0FBQztRQUNELElBQUksS0FBSyxDQUFDLFdBQVcsSUFBSSxLQUFLLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDNUMsTUFBTSxJQUFJLEtBQUssQ0FBQyxzREFBc0QsQ0FBQyxDQUFBO1FBQ3pFLENBQUM7UUFFRCxzQ0FBc0M7UUFDdEMsSUFBSSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUMsV0FBVztZQUM5QixDQUFDLENBQUMscUJBQVEsQ0FBQyxlQUFlLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxLQUFLLENBQUMsV0FBVyxDQUFDO1lBQzlELENBQUMsQ0FBQyxxQkFBUSxDQUFDLGdCQUFnQixDQUFDLElBQUksRUFBRSxTQUFTLEVBQUUsS0FBSyxDQUFDLFlBQWEsQ0FBQyxDQUFBO1FBRW5FLDhEQUE4RDtRQUM5RCxNQUFNLFFBQVEsR0FBRyxJQUFJLGVBQWUsQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRTtZQUM5RCxjQUFjLEVBQUUsSUFBSSxDQUFDLE9BQU87U0FDN0IsQ0FBQyxDQUFBO1FBQ0YsSUFBSSxDQUFDLFlBQVksR0FBRyxRQUFRLENBQUMsWUFBWSxDQUFBO1FBRXpDLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQTtRQUMxQixJQUFJLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQSxDQUFDLGlEQUFpRDtRQUN6RSxJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUEsQ0FBQyxxQ0FBcUM7SUFDcEUsQ0FBQztDQUNGO0FBRUQsU0FBUyxPQUFPLENBQUMsQ0FBUztJQUN4QixPQUFPLENBQUMsQ0FBQyxPQUFPLENBQUMsZUFBZSxFQUFFLEVBQUUsQ0FBQyxDQUFBO0FBQ3ZDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBleGlzdHNTeW5jIH0gZnJvbSBcImZzXCJcbmltcG9ydCAqIGFzIHBhdGggZnJvbSBcInBhdGhcIlxuaW1wb3J0IHsgRHVyYXRpb24sIFN0YWNrIH0gZnJvbSBcImF3cy1jZGstbGliXCJcbmltcG9ydCAqIGFzIGRzcWwgZnJvbSBcImF3cy1jZGstbGliL2F3cy1kc3FsXCJcbmltcG9ydCB7IElWcGMsIFN1Ym5ldFR5cGUsIFN1Ym5ldFNlbGVjdGlvbiB9IGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtZWMyXCJcbmltcG9ydCAqIGFzIGlhbSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWlhbVwiXG5pbXBvcnQgeyBGdW5jdGlvbiwgSUZ1bmN0aW9uLCBSdW50aW1lIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGFcIlxuaW1wb3J0ICogYXMgbGFtYmRhIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtbGFtYmRhLW5vZGVqc1wiXG5pbXBvcnQgeyBOb2RlanNGdW5jdGlvblByb3BzIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGEtbm9kZWpzXCJcbmltcG9ydCB7IElEYXRhYmFzZUNsdXN0ZXIsIElEYXRhYmFzZUluc3RhbmNlIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1yZHNcIlxuaW1wb3J0IHsgSVNlY3JldCB9IGZyb20gXCJhd3MtY2RrLWxpYi9hd3Mtc2VjcmV0c21hbmFnZXJcIlxuaW1wb3J0ICogYXMgY3VzdG9tUmVzb3VyY2VzIGZyb20gXCJhd3MtY2RrLWxpYi9jdXN0b20tcmVzb3VyY2VzXCJcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gXCJjb25zdHJ1Y3RzXCJcblxuLyoqXG4gKiBIZWxwZXIgZnVuY3Rpb24gdG8gZGV0ZXJtaW5lIGlmIGEgY2x1c3RlciBpcyBhIERTUUwgY2x1c3RlclxuICovXG5mdW5jdGlvbiBpc0RzcWxDbHVzdGVyKFxuICBjbHVzdGVyOiBJRGF0YWJhc2VDbHVzdGVyIHwgSURhdGFiYXNlSW5zdGFuY2UgfCBkc3FsLkNmbkNsdXN0ZXJcbik6IGNsdXN0ZXIgaXMgZHNxbC5DZm5DbHVzdGVyIHtcbiAgcmV0dXJuIGNsdXN0ZXIgaW5zdGFuY2VvZiBkc3FsLkNmbkNsdXN0ZXJcbn1cblxuZXhwb3J0IGludGVyZmFjZSBSZHNTcWxQcm9wcyB7XG4gIC8qKlxuICAgKiBWUEMgbmV0d29yayB0byBwbGFjZSB0aGUgcHJvdmlkZXIgbGFtYmRhLlxuICAgKlxuICAgKiBOb3JtYWxseSB0aGlzIGlzIHRoZSBWUEMgb2YgeW91ciBkYXRhYmFzZS5cbiAgICogUmVxdWlyZWQgd2hlbiB5b3VyIGRhdGFiYXNlIGlzIG9ubHkgYWNjZXNzaWJsZSBpbiBhIFZQQy5cbiAgICogTm90IHJlcXVpcmVkIGZvciBEU1FMIGFzIGl0IHVzZXMgcHVibGljIGVuZHBvaW50cyB3aXRoIElBTSBhdXRoZW50aWNhdGlvbi5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBGdW5jdGlvbiBpcyBub3QgcGxhY2VkIHdpdGhpbiBhIFZQQy5cbiAgICovXG4gIHJlYWRvbmx5IHZwYz86IElWcGNcblxuICAvKipcbiAgICogV2hlcmUgdG8gcGxhY2UgdGhlIG5ldHdvcmsgcHJvdmlkZXIgbGFtYmRhIHdpdGhpbiB0aGUgVlBDLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIHRoZSBpc29sYXRlZCBzdWJuZXQgaWYgbm90IHNwZWNpZmllZFxuICAgKi9cbiAgcmVhZG9ubHkgdnBjU3VibmV0cz86IFN1Ym5ldFNlbGVjdGlvblxuXG4gIC8qKlxuICAgKiBZb3VyIGRhdGFiYXNlIGNsdXN0ZXIgb3IgaW5zdGFuY2UuXG4gICAqIFN1cHBvcnRzIGJvdGggdHJhZGl0aW9uYWwgUkRTL0F1cm9yYSBjbHVzdGVycyBhbmQgRFNRTCBjbHVzdGVycy5cbiAgICogLSBGb3IgUkRTL0F1cm9yYTogc2VjdXJpdHkgZ3JvdXBzIHdpbGwgYmUgY29uZmlndXJlZCB0byBhbGxvdyBhY2Nlc3NcbiAgICogLSBGb3IgRFNRTDogSUFNIGF1dGhlbnRpY2F0aW9uIHdpbGwgYmUgdXNlZCBpbnN0ZWFkIG9mIHNlY3JldHNcbiAgICovXG4gIHJlYWRvbmx5IGNsdXN0ZXI6IElEYXRhYmFzZUNsdXN0ZXIgfCBJRGF0YWJhc2VJbnN0YW5jZSB8IGRzcWwuQ2ZuQ2x1c3RlclxuXG4gIC8qKlxuICAgKiBTZWNyZXQgdGhhdCBncmFudHMgYWNjZXNzIHRvIHlvdXIgZGF0YWJhc2UuXG4gICAqXG4gICAqIFVzdWFsbHkgdGhpcyBpcyB5b3VyIGNsdXN0ZXIncyBtYXN0ZXIgc2VjcmV0LlxuICAgKiBOb3QgcmVxdWlyZWQgd2hlbiByZWx5aW5nIG9uIElBTSBhdXRoZW50aWNhdGlvbiAoc3VjaCBhcyBEU1FMKS5cbiAgICpcbiAgICogQGRlZmF1bHQgLSB1bmRlZmluZWQgZm9yIERTUUwgY2x1c3RlcnMgdXNpbmcgSUFNIGF1dGhlbnRpY2F0aW9uXG4gICAqL1xuICByZWFkb25seSBzZWNyZXQ/OiBJU2VjcmV0XG5cbiAgLyoqXG4gICAqIFRpbWVvdXQgZm9yIGxhbWJkYSB0byBkbyBpdHMgd29yay5cbiAgICpcbiAgICogQGRlZmF1bHQgLSA1IG1pbnV0ZXNcbiAgICovXG4gIHJlYWRvbmx5IHRpbWVvdXQ/OiBEdXJhdGlvblxuXG4gIC8qKlxuICAgKiBMb2cgU1FMIHN0YXRlbWVudHMuIFRoaXMgaW5jbHVkZXMgcGFzc3dvcmRzLiBVc2Ugb25seSBmb3IgZGVidWdnaW5nLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGZhbHNlXG4gICAqL1xuICByZWFkb25seSBsb2dnZXI/OiBib29sZWFuXG5cbiAgLyoqXG4gICAqIEFkZGl0aW9uYWwgZnVuY3Rpb24gY3VzdG9taXphdGlvbi5cbiAgICpcbiAgICogVGhpcyBlbmFibGVzIGFkZGl0aW9uYWwgZnVuY3Rpb24gY3VzdG9taXphdGlvbiBzdWNoIGFzIHRoZSBsb2cgZ3JvdXAuIEhvd2V2ZXIsXG4gICAqIGxhbWJkYSBmdW5jdGlvbiBwcm9wZXJ0aWVzIGNvbnRyb2xsZWQgYnkgb3RoZXIge1Jkc1NxbFByb3BzfSBwYXJhbWV0ZXJzIHdpbGwgdHJ1bXBcbiAgICogb3Bpb25zIHNldCB2aWEgdGhpcyBwYXJhbWV0ZXIuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gZW1wdHlcbiAgICovXG4gIHJlYWRvbmx5IGZ1bmN0aW9uUHJvcHM/OiBOb2RlanNGdW5jdGlvblByb3BzXG5cbiAgLyoqXG4gICAqIFVzZSBTU0w/XG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdHJ1ZVxuICAgKi9cbiAgcmVhZG9ubHkgc3NsPzogYm9vbGVhblxufVxuXG4vKipcbiAqIFN1cHBvcnRlZCBkYXRhYmFzZSBlbmdpbmVzXG4gKi9cbmV4cG9ydCBlbnVtIERhdGFiYXNlRW5naW5lIHtcbiAgUE9TVEdSRVMgPSBcInBvc3RncmVzXCIsXG4gIE1ZU1FMID0gXCJteXNxbFwiLFxuICBEU1FMID0gXCJkc3FsXCIsXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSVByb3ZpZGVyIHtcbiAgcmVhZG9ubHkgc2VydmljZVRva2VuOiBzdHJpbmdcbiAgcmVhZG9ubHkgaGFuZGxlcjogSUZ1bmN0aW9uXG4gIHJlYWRvbmx5IHNlY3JldD86IElTZWNyZXRcbiAgcmVhZG9ubHkgZW5naW5lOiBzdHJpbmdcbiAgcmVhZG9ubHkgY2x1c3Rlcj86IElEYXRhYmFzZUNsdXN0ZXIgfCBJRGF0YWJhc2VJbnN0YW5jZSB8IGRzcWwuQ2ZuQ2x1c3RlclxufVxuXG5leHBvcnQgaW50ZXJmYWNlIFByb3ZpZGVyQXR0cmlidXRlcyB7XG4gIC8qKlxuICAgKiBFaXRoZXIgdGhlIEFSTiBvciBuYW1lIG9mIHRoZSBMYW1iZGEgZnVuY3Rpb24uXG4gICAqIFVzZSBmdW5jdGlvbkFybiBmb3IgY3Jvc3MtYWNjb3VudCBvciBjcm9zcy1yZWdpb24gc2NlbmFyaW9zLlxuICAgKiBVc2UgZnVuY3Rpb25OYW1lIGZvciBzYW1lLWFjY291bnQsIHNhbWUtcmVnaW9uIHNjZW5hcmlvcy5cbiAgICovXG4gIHJlYWRvbmx5IGZ1bmN0aW9uQXJuPzogc3RyaW5nXG4gIHJlYWRvbmx5IGZ1bmN0aW9uTmFtZT86IHN0cmluZ1xuICByZWFkb25seSBlbmdpbmU6IERhdGFiYXNlRW5naW5lXG4gIC8qKlxuICAgKiBPcHRpb25hbCBjbHVzdGVyIGluZm9ybWF0aW9uIGZvciByb2xlIGNyZWF0aW9uLlxuICAgKlxuICAgKiBXaGVuIGltcG9ydGluZyBhIHByb3ZpZGVyLCBjbHVzdGVyIGRldGFpbHMgYXJlIG9mdGVuIG5vdCBhdmFpbGFibGUuXG4gICAqIEhvd2V2ZXIsIHNvbWUgb3BlcmF0aW9ucyBsaWtlIHJvbGUgY3JlYXRpb24gcmVxdWlyZSBjbHVzdGVyIGVuZHBvaW50XG4gICAqIGluZm9ybWF0aW9uIHRvIGJ1aWxkIGNvbm5lY3Rpb24gc2VjcmV0cy5cbiAgICpcbiAgICogSWYgeW91IHBsYW4gdG8gY3JlYXRlIHJvbGVzIHdpdGggdGhlIGltcG9ydGVkIHByb3ZpZGVyLCB5b3UgbXVzdFxuICAgKiBwcm92aWRlIHRoZSBjbHVzdGVyIHJlZmVyZW5jZS4gSWYgeW91IG9ubHkgcGxhbiB0byB1c2UgZXhpc3RpbmdcbiAgICogcm9sZXMsIGRhdGFiYXNlcywgc2NoZW1hcywgb3IgU1FMIG9wZXJhdGlvbnMsIHRoaXMgY2FuIGJlIG9taXR0ZWQuXG4gICAqL1xuICByZWFkb25seSBjbHVzdGVyPzogSURhdGFiYXNlQ2x1c3RlciB8IElEYXRhYmFzZUluc3RhbmNlIHwgZHNxbC5DZm5DbHVzdGVyXG59XG5cbmV4cG9ydCBjbGFzcyBQcm92aWRlciBleHRlbmRzIENvbnN0cnVjdCBpbXBsZW1lbnRzIElQcm92aWRlciB7XG4gIC8qKlxuICAgKiBJbXBvcnQgYW4gZXhpc3RpbmcgcHJvdmlkZXIgTGFtYmRhIGZ1bmN0aW9uXG4gICAqL1xuICBzdGF0aWMgZnJvbVByb3ZpZGVyQXR0cmlidXRlcyhcbiAgICBzY29wZTogQ29uc3RydWN0LFxuICAgIGlkOiBzdHJpbmcsXG4gICAgYXR0cnM6IFByb3ZpZGVyQXR0cmlidXRlc1xuICApOiBJUHJvdmlkZXIge1xuICAgIHJldHVybiBuZXcgSW1wb3J0ZWRQcm92aWRlcihzY29wZSwgaWQsIGF0dHJzKVxuICB9XG5cbiAgcHVibGljIHJlYWRvbmx5IHNlcnZpY2VUb2tlbjogc3RyaW5nXG4gIHB1YmxpYyByZWFkb25seSBzZWNyZXQ/OiBJU2VjcmV0XG4gIHB1YmxpYyByZWFkb25seSBoYW5kbGVyOiBJRnVuY3Rpb25cbiAgcHVibGljIHJlYWRvbmx5IGNsdXN0ZXI/OiBJRGF0YWJhc2VDbHVzdGVyIHwgSURhdGFiYXNlSW5zdGFuY2UgfCBkc3FsLkNmbkNsdXN0ZXJcblxuICAvKipcbiAgICogVGhlIGVuZ2luZSBsaWtlIFwicG9zdGdyZXNcIiBvciBcIm15c3FsXCJcbiAgICpcbiAgICogQGRlZmF1bHQgLSBpZiB3ZSBjYW5ub3QgZGV0ZXJtaW5lIHRoaXMgXCJwb3N0Z3Jlc1wiXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgZW5naW5lOiBzdHJpbmdcblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogUmRzU3FsUHJvcHMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpXG5cbiAgICAvLyBWYWxpZGF0ZSBjb25maWd1cmF0aW9uXG4gICAgY29uc3QgaXNEc3FsID0gaXNEc3FsQ2x1c3Rlcihwcm9wcy5jbHVzdGVyKVxuICAgIGlmICghaXNEc3FsICYmICFwcm9wcy5zZWNyZXQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgXCJFaXRoZXIgc2VjcmV0IChmb3IgdHJhZGl0aW9uYWwgUkRTKSBvciBjbHVzdGVyIHdpdGggRFNRTCBtdXN0IGJlIHByb3ZpZGVkXCJcbiAgICAgIClcbiAgICB9XG4gICAgaWYgKCFpc0RzcWwgJiYgIXByb3BzLnZwYykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiVlBDIGlzIHJlcXVpcmVkIGZvciB0cmFkaXRpb25hbCBSRFMgZGF0YWJhc2VzXCIpXG4gICAgfVxuICAgIGlmIChpc0RzcWwgJiYgcHJvcHMuc2VjcmV0KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIFwic2VjcmV0IHNob3VsZCBub3QgYmUgcHJvdmlkZWQgd2hlbiB1c2luZyBEU1FMIGNsdXN0ZXIgKHVzZXMgSUFNIGF1dGhlbnRpY2F0aW9uKVwiXG4gICAgICApXG4gICAgfVxuXG4gICAgdGhpcy5zZWNyZXQgPSBwcm9wcy5zZWNyZXRcbiAgICB0aGlzLmNsdXN0ZXIgPSBwcm9wcy5jbHVzdGVyXG5cbiAgICAvLyBEZXRlcm1pbmUgZW5naW5lIGZyb20gY2x1c3Rlci9pbnN0YW5jZSBpbnN0ZWFkIG9mIGhhcmRjb2RpbmdcbiAgICBpZiAoaXNEc3FsKSB7XG4gICAgICAvLyBEU1FMIGlzIGFsd2F5cyBQb3N0Z3JlU1FMLWNvbXBhdGlibGVcbiAgICAgIHRoaXMuZW5naW5lID0gRGF0YWJhc2VFbmdpbmUuRFNRTFxuICAgIH0gZWxzZSBpZiAoXCJjbHVzdGVySWRlbnRpZmllclwiIGluIHByb3BzLmNsdXN0ZXIpIHtcbiAgICAgIC8vIEl0J3MgYSBEYXRhYmFzZUNsdXN0ZXJcbiAgICAgIGNvbnN0IGNsdXN0ZXJFbmdpbmUgPSAocHJvcHMuY2x1c3RlciBhcyBJRGF0YWJhc2VDbHVzdGVyKS5lbmdpbmVcbiAgICAgIHRoaXMuZW5naW5lID1cbiAgICAgICAgY2x1c3RlckVuZ2luZSAmJiBjbHVzdGVyRW5naW5lLmVuZ2luZUZhbWlseSA9PT0gXCJNWVNRTFwiXG4gICAgICAgICAgPyBEYXRhYmFzZUVuZ2luZS5NWVNRTFxuICAgICAgICAgIDogRGF0YWJhc2VFbmdpbmUuUE9TVEdSRVNcbiAgICB9IGVsc2UgaWYgKFwiaW5zdGFuY2VJZGVudGlmaWVyXCIgaW4gcHJvcHMuY2x1c3Rlcikge1xuICAgICAgLy8gSXQncyBhIERhdGFiYXNlSW5zdGFuY2VcbiAgICAgIGNvbnN0IGluc3RhbmNlRW5naW5lID0gKHByb3BzLmNsdXN0ZXIgYXMgSURhdGFiYXNlSW5zdGFuY2UpLmVuZ2luZVxuICAgICAgdGhpcy5lbmdpbmUgPVxuICAgICAgICBpbnN0YW5jZUVuZ2luZSAmJiBpbnN0YW5jZUVuZ2luZS5lbmdpbmVGYW1pbHkgPT09IFwiTVlTUUxcIlxuICAgICAgICAgID8gRGF0YWJhc2VFbmdpbmUuTVlTUUxcbiAgICAgICAgICA6IERhdGFiYXNlRW5naW5lLlBPU1RHUkVTXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEZhbGxiYWNrIHRvIHBvc3RncmVzIGlmIGVuZ2luZSBoYXNuJ3QgYmVlbiBwcm92aWRlZFxuICAgICAgdGhpcy5lbmdpbmUgPSBEYXRhYmFzZUVuZ2luZS5QT1NUR1JFU1xuICAgIH1cblxuICAgIGNvbnN0IGZ1bmN0aW9uTmFtZSA9IFwiUmRzU3FsXCIgKyBzbHVnaWZ5KFwiMjhiOWU3OTEtYWY2MC00YTMzLWJjYTgtZmZiNmYzMGVmOGM1XCIpXG4gICAgdGhpcy5oYW5kbGVyID1cbiAgICAgIChTdGFjay5vZih0aGlzKS5ub2RlLnRyeUZpbmRDaGlsZChmdW5jdGlvbk5hbWUpIGFzIElGdW5jdGlvbikgPz9cbiAgICAgIHRoaXMubmV3Q3VzdG9tUmVzb3VyY2VIYW5kbGVyKHNjb3BlLCBmdW5jdGlvbk5hbWUsIHByb3BzKVxuXG4gICAgY29uc3QgcHJvdmlkZXIgPSBuZXcgY3VzdG9tUmVzb3VyY2VzLlByb3ZpZGVyKHRoaXMsIFwiUmRzU3FsXCIsIHtcbiAgICAgIG9uRXZlbnRIYW5kbGVyOiB0aGlzLmhhbmRsZXIsXG4gICAgfSlcbiAgICB0aGlzLnNlcnZpY2VUb2tlbiA9IHByb3ZpZGVyLnNlcnZpY2VUb2tlblxuXG4gICAgLy8gSGFuZGxlIGRhdGFiYXNlIGNvbm5lY3Rpb24gc2V0dXBcbiAgICBpZiAoaXNEc3FsKSB7XG4gICAgICAvLyBGb3IgRFNRTCwgZ3JhbnQgSUFNIHBlcm1pc3Npb25zIGluc3RlYWQgb2YgVlBDIHNlY3VyaXR5IGdyb3Vwc1xuICAgICAgY29uc3QgZHNxbENsdXN0ZXIgPSBwcm9wcy5jbHVzdGVyIGFzIGRzcWwuQ2ZuQ2x1c3RlclxuICAgICAgdGhpcy5oYW5kbGVyLmFkZFRvUm9sZVBvbGljeShcbiAgICAgICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICAgIGVmZmVjdDogaWFtLkVmZmVjdC5BTExPVyxcbiAgICAgICAgICBhY3Rpb25zOiBbXCJkc3FsOkRiQ29ubmVjdEFkbWluXCJdLFxuICAgICAgICAgIHJlc291cmNlczogW2RzcWxDbHVzdGVyLmF0dHJSZXNvdXJjZUFybl0sXG4gICAgICAgIH0pXG4gICAgICApXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFRyYWRpdGlvbmFsIFJEUyBzZXR1cCB3aXRoIHNlY3VyaXR5IGdyb3VwcyBhbmQgc2VjcmV0c1xuICAgICAgdGhpcy5zZWNyZXQhLmdyYW50UmVhZCh0aGlzLmhhbmRsZXIpXG4gICAgICBpZiAodGhpcy5zZWNyZXQhLmVuY3J5cHRpb25LZXkpIHtcbiAgICAgICAgLy8gSXQgc2VlbXMgd2UgbmVlZCB0byBncmFudCBleHBsaWNpdCBwZXJtaXNzaW9uXG4gICAgICAgIHRoaXMuc2VjcmV0IS5lbmNyeXB0aW9uS2V5LmdyYW50RGVjcnlwdCh0aGlzLmhhbmRsZXIpXG4gICAgICB9XG4gICAgICBpZiAocHJvcHMuY2x1c3Rlci5jb25uZWN0aW9ucy5zZWN1cml0eUdyb3Vwcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiQ2x1c3RlciBkb2VzIG5vdCBoYXZlIGEgc2VjdXJpdHkgZ3JvdXAuXCIpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBzZWN1cml0eUdyb3VwID0gcHJvcHMuY2x1c3Rlci5jb25uZWN0aW9ucy5zZWN1cml0eUdyb3Vwc1swXSFcbiAgICAgICAgdGhpcy5oYW5kbGVyLm5vZGUuZGVmYXVsdENoaWxkPy5ub2RlLmFkZERlcGVuZGVuY3koc2VjdXJpdHlHcm91cClcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5ub2RlLmFkZERlcGVuZGVuY3kocHJvcHMuY2x1c3RlcilcbiAgfVxuXG4gIHByb3RlY3RlZCBuZXdDdXN0b21SZXNvdXJjZUhhbmRsZXIoXG4gICAgc2NvcGU6IENvbnN0cnVjdCxcbiAgICBpZDogc3RyaW5nLFxuICAgIHByb3BzOiBSZHNTcWxQcm9wc1xuICApOiBsYW1iZGEuTm9kZWpzRnVuY3Rpb24ge1xuICAgIGNvbnN0IGlzRHNxbCA9IGlzRHNxbENsdXN0ZXIocHJvcHMuY2x1c3RlcilcbiAgICBjb25zdCBoYW5kbGVyRGlyID0gcGF0aC5qb2luKF9fZGlybmFtZSwgXCJoYW5kbGVyXCIpXG4gICAgY29uc3QgaW5kZXhfdHMgPSBwYXRoLmpvaW4oaGFuZGxlckRpciwgXCJpbmRleC50c1wiKVxuICAgIGNvbnN0IGluZGV4X2pzID0gcGF0aC5qb2luKGhhbmRsZXJEaXIsIFwiaW5kZXguanNcIilcbiAgICBsZXQgZW50cnk6IHN0cmluZ1xuXG4gICAgaWYgKGV4aXN0c1N5bmMoaW5kZXhfdHMpKSB7XG4gICAgICBlbnRyeSA9IGluZGV4X3RzXG4gICAgfSBlbHNlIGlmIChleGlzdHNTeW5jKGluZGV4X2pzKSkge1xuICAgICAgZW50cnkgPSBpbmRleF9qc1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBVZ2x5IGhhY2sgdG8gc3VwcG9ydCBTU1QgKHBvc3NpYmx5IGNhdXNlZCBieSBteSBoYWNrIHRvIG1ha2UgU1NUIHdvcmsgd2l0aCBDb21tb25KUyBsaWJyYXJpZXMpXG4gICAgICBlbnRyeSA9IHBhdGguam9pbihcbiAgICAgICAgcGF0aC5kaXJuYW1lKHByb2Nlc3MuZW52Lm5wbV9wYWNrYWdlX2pzb24gfHwgcHJvY2Vzcy5jd2QoKSksXG4gICAgICAgIFwibm9kZV9tb2R1bGVzL2Nkay1yZHMtc3FsL2xpYi9oYW5kbGVyL2luZGV4LmpzXCJcbiAgICAgIClcbiAgICB9XG4gICAgbGV0IHNzbF9vcHRpb25zOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHwgdW5kZWZpbmVkXG4gICAgaWYgKHByb3BzLnNzbCAhPT0gdW5kZWZpbmVkICYmICFwcm9wcy5zc2wpIHtcbiAgICAgIHNzbF9vcHRpb25zID0ge1xuICAgICAgICBTU0w6IEpTT04uc3RyaW5naWZ5KHByb3BzLnNzbCksXG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IGxvZ2dlciA9IHByb3BzLmxvZ2dlciA/PyBmYWxzZVxuXG4gICAgLy8gQnVpbGQgZW52aXJvbm1lbnQgdmFyaWFibGVzXG4gICAgY29uc3QgZW52aXJvbm1lbnQ6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgICBMT0dHRVI6IGxvZ2dlci50b1N0cmluZygpLFxuICAgICAgLi4uc3NsX29wdGlvbnMsXG4gICAgfVxuXG4gICAgLy8gQWRkIERTUUwtc3BlY2lmaWMgZW52aXJvbm1lbnQgdmFyaWFibGVzXG4gICAgaWYgKGlzRHNxbCkge1xuICAgICAgY29uc3QgZHNxbENsdXN0ZXIgPSBwcm9wcy5jbHVzdGVyIGFzIGRzcWwuQ2ZuQ2x1c3RlclxuICAgICAgY29uc3QgY2x1c3RlcklkID0gZHNxbENsdXN0ZXIuYXR0cklkZW50aWZpZXJcbiAgICAgIGNvbnN0IHJlZ2lvbiA9IFN0YWNrLm9mKHNjb3BlKS5yZWdpb25cbiAgICAgIGVudmlyb25tZW50LkRTUUxfRU5EUE9JTlQgPSBgJHtjbHVzdGVySWR9LmRzcWwuJHtyZWdpb259Lm9uLmF3c2BcbiAgICAgIGVudmlyb25tZW50LkRTUUxfUE9SVCA9IFwiNTQzMlwiXG4gICAgfSBlbHNlIGlmIChwcm9wcy5zZWNyZXQpIHtcbiAgICAgIC8vIEFkZCBzZWNyZXQgQVJOIHRvIGVudmlyb25tZW50IGZvciB0cmFkaXRpb25hbCBSRFNcbiAgICAgIGVudmlyb25tZW50LlNFQ1JFVF9BUk4gPSBwcm9wcy5zZWNyZXQuc2VjcmV0QXJuXG4gICAgfVxuXG4gICAgY29uc3QgeyBwYXJ0aXRpb24sIHJlZ2lvbjogc3RhY2tSZWdpb24sIGFjY291bnQgfSA9IFN0YWNrLm9mKHNjb3BlKVxuICAgIGNvbnN0IGRlbGV0ZVBhcmFtZXRlclBvbGljeSA9IG5ldyBpYW0uUG9saWN5U3RhdGVtZW50KHtcbiAgICAgIGFjdGlvbnM6IFtcInNzbTpEZWxldGVQYXJhbWV0ZXJcIl0sXG4gICAgICByZXNvdXJjZXM6IFtgYXJuOiR7cGFydGl0aW9ufTpzc206JHtzdGFja1JlZ2lvbn06JHthY2NvdW50fTpwYXJhbWV0ZXIvKmBdLFxuICAgICAgY29uZGl0aW9uczoge1xuICAgICAgICBTdHJpbmdFcXVhbHM6IHtcbiAgICAgICAgICBcInNzbTpSZXNvdXJjZVRhZy9jcmVhdGVkLWJ5XCI6IFwiY2RrLXJkcy1zcWxcIixcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgfSlcblxuICAgIGNvbnN0IGZuID0gbmV3IGxhbWJkYS5Ob2RlanNGdW5jdGlvbihzY29wZSwgaWQsIHtcbiAgICAgIC4uLnByb3BzLmZ1bmN0aW9uUHJvcHMsXG4gICAgICBkZXBzTG9ja0ZpbGVQYXRoOiBwYXRoLmpvaW4oX19kaXJuYW1lLCBcIi4uXCIsIFwicG5wbS1sb2NrLnlhbWxcIiksXG4gICAgICAvLyBPbmx5IGNvbmZpZ3VyZSBWUEMgZm9yIHRyYWRpdGlvbmFsIFJEU1xuICAgICAgLi4uKGlzRHNxbFxuICAgICAgICA/IHt9XG4gICAgICAgIDoge1xuICAgICAgICAgICAgdnBjOiBwcm9wcy52cGMsXG4gICAgICAgICAgICB2cGNTdWJuZXRzOiBwcm9wcy52cGNTdWJuZXRzID8/IHtcbiAgICAgICAgICAgICAgc3VibmV0VHlwZTogU3VibmV0VHlwZS5QUklWQVRFX0lTT0xBVEVELFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KSxcbiAgICAgIGVudHJ5OiBlbnRyeSxcbiAgICAgIHJ1bnRpbWU6IFJ1bnRpbWUuTk9ERUpTXzIyX1gsXG4gICAgICB0aW1lb3V0OiBwcm9wcy50aW1lb3V0ID8/IHByb3BzLmZ1bmN0aW9uUHJvcHM/LnRpbWVvdXQgPz8gRHVyYXRpb24uc2Vjb25kcygzMDApLFxuICAgICAgYnVuZGxpbmc6IHtcbiAgICAgICAgLy8gSW5jbHVkZSB0aGUgbWlncmF0aW9ucyBkaXJlY3RvcnkgaW4gdGhlIGJ1bmRsZVxuICAgICAgICBjb21tYW5kSG9va3M6IHtcbiAgICAgICAgICBiZWZvcmVCdW5kbGluZyhfOiBzdHJpbmcsIG91dHB1dERpcjogc3RyaW5nKTogc3RyaW5nW10ge1xuICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgYGN1cmwgLS1zaWxlbnQgLWZMIGh0dHBzOi8vdHJ1c3RzdG9yZS5wa2kucmRzLmFtYXpvbmF3cy5jb20vZ2xvYmFsL2dsb2JhbC1idW5kbGUucGVtIC1vICR7cGF0aC5qb2luKFxuICAgICAgICAgICAgICAgIG91dHB1dERpcixcbiAgICAgICAgICAgICAgICBcImdsb2JhbC1idW5kbGUucGVtXCJcbiAgICAgICAgICAgICAgKX1gLFxuICAgICAgICAgICAgXVxuICAgICAgICAgIH0sXG4gICAgICAgICAgYWZ0ZXJCdW5kbGluZygpOiBzdHJpbmdbXSB7XG4gICAgICAgICAgICByZXR1cm4gW11cbiAgICAgICAgICB9LFxuICAgICAgICAgIGJlZm9yZUluc3RhbGwoKTogc3RyaW5nW10ge1xuICAgICAgICAgICAgcmV0dXJuIFtdXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICBlbnZpcm9ubWVudCxcbiAgICAgIGluaXRpYWxQb2xpY3k6IFtcbiAgICAgICAgZGVsZXRlUGFyYW1ldGVyUG9saWN5LFxuICAgICAgICAuLi4ocHJvcHMuZnVuY3Rpb25Qcm9wcz8uaW5pdGlhbFBvbGljeSA/PyBbXSksXG4gICAgICBdLFxuICAgIH0pXG5cbiAgICAvLyBPbmx5IGNvbmZpZ3VyZSBzZWN1cml0eSBncm91cHMgZm9yIHRyYWRpdGlvbmFsIFJEUyAobm90IERTUUwpXG4gICAgaWYgKFxuICAgICAgIWlzRHNxbCAmJlxuICAgICAgKCFwcm9wcy5mdW5jdGlvblByb3BzPy5zZWN1cml0eUdyb3VwcyB8fFxuICAgICAgICBwcm9wcy5mdW5jdGlvblByb3BzPy5zZWN1cml0eUdyb3Vwcy5sZW5ndGggPT09IDApXG4gICAgKSB7XG4gICAgICBjb25zdCByZHNDbHVzdGVyID0gcHJvcHMuY2x1c3RlciBhcyBJRGF0YWJhc2VDbHVzdGVyIHwgSURhdGFiYXNlSW5zdGFuY2VcbiAgICAgIHJkc0NsdXN0ZXIuY29ubmVjdGlvbnMuYWxsb3dEZWZhdWx0UG9ydEZyb20oXG4gICAgICAgIGZuLFxuICAgICAgICBcIkFsbG93IHRoZSByZHMgc3FsIGhhbmRsZXIgdG8gY29ubmVjdCB0byBkYlwiXG4gICAgICApXG4gICAgfVxuXG4gICAgcmV0dXJuIGZuXG4gIH1cbn1cblxuY2xhc3MgSW1wb3J0ZWRQcm92aWRlciBleHRlbmRzIENvbnN0cnVjdCBpbXBsZW1lbnRzIElQcm92aWRlciB7XG4gIHB1YmxpYyByZWFkb25seSBzZXJ2aWNlVG9rZW46IHN0cmluZ1xuICBwdWJsaWMgcmVhZG9ubHkgaGFuZGxlcjogSUZ1bmN0aW9uXG4gIHB1YmxpYyByZWFkb25seSBzZWNyZXQ/OiBJU2VjcmV0XG4gIHB1YmxpYyByZWFkb25seSBlbmdpbmU6IHN0cmluZ1xuICBwdWJsaWMgcmVhZG9ubHkgY2x1c3Rlcj86IElEYXRhYmFzZUNsdXN0ZXIgfCBJRGF0YWJhc2VJbnN0YW5jZSB8IGRzcWwuQ2ZuQ2x1c3RlclxuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIGF0dHJzOiBQcm92aWRlckF0dHJpYnV0ZXMpIHtcbiAgICBzdXBlcihzY29wZSwgaWQpXG5cbiAgICAvLyBWYWxpZGF0ZSB0aGF0IGVpdGhlciBmdW5jdGlvbkFybiBvciBmdW5jdGlvbk5hbWUgaXMgcHJvdmlkZWRcbiAgICBpZiAoIWF0dHJzLmZ1bmN0aW9uQXJuICYmICFhdHRycy5mdW5jdGlvbk5hbWUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkVpdGhlciBmdW5jdGlvbkFybiBvciBmdW5jdGlvbk5hbWUgbXVzdCBiZSBwcm92aWRlZFwiKVxuICAgIH1cbiAgICBpZiAoYXR0cnMuZnVuY3Rpb25Bcm4gJiYgYXR0cnMuZnVuY3Rpb25OYW1lKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJQcm92aWRlIGVpdGhlciBmdW5jdGlvbkFybiBvciBmdW5jdGlvbk5hbWUsIG5vdCBib3RoXCIpXG4gICAgfVxuXG4gICAgLy8gSW1wb3J0IHRoZSBleGlzdGluZyBMYW1iZGEgZnVuY3Rpb25cbiAgICB0aGlzLmhhbmRsZXIgPSBhdHRycy5mdW5jdGlvbkFyblxuICAgICAgPyBGdW5jdGlvbi5mcm9tRnVuY3Rpb25Bcm4odGhpcywgXCJIYW5kbGVyXCIsIGF0dHJzLmZ1bmN0aW9uQXJuKVxuICAgICAgOiBGdW5jdGlvbi5mcm9tRnVuY3Rpb25OYW1lKHRoaXMsIFwiSGFuZGxlclwiLCBhdHRycy5mdW5jdGlvbk5hbWUhKVxuXG4gICAgLy8gRGVyaXZlIHNlcnZpY2VUb2tlbiBieSB3cmFwcGluZyBpbiBjdXN0b20gcmVzb3VyY2UgcHJvdmlkZXJcbiAgICBjb25zdCBwcm92aWRlciA9IG5ldyBjdXN0b21SZXNvdXJjZXMuUHJvdmlkZXIodGhpcywgXCJQcm92aWRlclwiLCB7XG4gICAgICBvbkV2ZW50SGFuZGxlcjogdGhpcy5oYW5kbGVyLFxuICAgIH0pXG4gICAgdGhpcy5zZXJ2aWNlVG9rZW4gPSBwcm92aWRlci5zZXJ2aWNlVG9rZW5cblxuICAgIHRoaXMuZW5naW5lID0gYXR0cnMuZW5naW5lXG4gICAgdGhpcy5zZWNyZXQgPSB1bmRlZmluZWQgLy8gSW1wb3J0ZWQgcHJvdmlkZXJzIGdldCBzZWNyZXQgZnJvbSBlbnZpcm9ubWVudFxuICAgIHRoaXMuY2x1c3RlciA9IGF0dHJzLmNsdXN0ZXIgLy8gT3B0aW9uYWwgY2x1c3RlciBmb3Igcm9sZSBjcmVhdGlvblxuICB9XG59XG5cbmZ1bmN0aW9uIHNsdWdpZnkoeDogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIHgucmVwbGFjZSgvW15hLXpBLVowLTldL2csIFwiXCIpXG59XG4iXX0=