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.
239 lines • 37.1 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,
// 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.0.11" };
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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QztZQUM1QyxNQUFNLElBQUksS0FBSyxDQUFDLHNEQUFzRCxDQUFDLENBQUE7UUFDekUsQ0FBQztRQUVELHNDQUFzQztRQUN0QyxJQUFJLENBQUMsT0FBTyxHQUFHLEtBQUssQ0FBQyxXQUFXO1lBQzlCLENBQUMsQ0FBQyxxQkFBUSxDQUFDLGVBQWUsQ0FBQyxJQUFJLEVBQUUsU0FBUyxFQUFFLEtBQUssQ0FBQyxXQUFXLENBQUM7WUFDOUQsQ0FBQyxDQUFDLHFCQUFRLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxLQUFLLENBQUMsWUFBYSxDQUFDLENBQUE7UUFFbkUsOERBQThEO1FBQzlELE1BQU0sUUFBUSxHQUFHLElBQUksZUFBZSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFO1lBQzlELGNBQWMsRUFBRSxJQUFJLENBQUMsT0FBTztTQUM3QixDQUFDLENBQUE7UUFDRixJQUFJLENBQUMsWUFBWSxHQUFHLFFBQVEsQ0FBQyxZQUFZLENBQUE7UUFFekMsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFBO1FBQzFCLElBQUksQ0FBQyxNQUFNLEdBQUcsU0FBUyxDQUFBLENBQUMsaURBQWlEO1FBQ3pFLElBQUksQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQSxDQUFDLHFDQUFxQztJQUNwRSxDQUFDO0NBQ0Y7QUFFRCxTQUFTLE9BQU8sQ0FBQyxDQUFTO0lBQ3hCLE9BQU8sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxlQUFlLEVBQUUsRUFBRSxDQUFDLENBQUE7QUFDdkMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4aXN0c1N5bmMgfSBmcm9tIFwiZnNcIlxuaW1wb3J0ICogYXMgcGF0aCBmcm9tIFwicGF0aFwiXG5pbXBvcnQgeyBEdXJhdGlvbiwgU3RhY2sgfSBmcm9tIFwiYXdzLWNkay1saWJcIlxuaW1wb3J0ICogYXMgZHNxbCBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWRzcWxcIlxuaW1wb3J0IHsgSVZwYywgU3VibmV0VHlwZSwgU3VibmV0U2VsZWN0aW9uIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1lYzJcIlxuaW1wb3J0ICogYXMgaWFtIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtaWFtXCJcbmltcG9ydCB7IEZ1bmN0aW9uLCBJRnVuY3Rpb24sIFJ1bnRpbWUgfSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWxhbWJkYVwiXG5pbXBvcnQgKiBhcyBsYW1iZGEgZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGEtbm9kZWpzXCJcbmltcG9ydCB7IE5vZGVqc0Z1bmN0aW9uUHJvcHMgfSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWxhbWJkYS1ub2RlanNcIlxuaW1wb3J0IHsgSURhdGFiYXNlQ2x1c3RlciwgSURhdGFiYXNlSW5zdGFuY2UgfSBmcm9tIFwiYXdzLWNkay1saWIvYXdzLXJkc1wiXG5pbXBvcnQgeyBJU2VjcmV0IH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1zZWNyZXRzbWFuYWdlclwiXG5pbXBvcnQgKiBhcyBjdXN0b21SZXNvdXJjZXMgZnJvbSBcImF3cy1jZGstbGliL2N1c3RvbS1yZXNvdXJjZXNcIlxuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSBcImNvbnN0cnVjdHNcIlxuXG4vKipcbiAqIEhlbHBlciBmdW5jdGlvbiB0byBkZXRlcm1pbmUgaWYgYSBjbHVzdGVyIGlzIGEgRFNRTCBjbHVzdGVyXG4gKi9cbmZ1bmN0aW9uIGlzRHNxbENsdXN0ZXIoXG4gIGNsdXN0ZXI6IElEYXRhYmFzZUNsdXN0ZXIgfCBJRGF0YWJhc2VJbnN0YW5jZSB8IGRzcWwuQ2ZuQ2x1c3RlclxuKTogY2x1c3RlciBpcyBkc3FsLkNmbkNsdXN0ZXIge1xuICByZXR1cm4gY2x1c3RlciBpbnN0YW5jZW9mIGRzcWwuQ2ZuQ2x1c3RlclxufVxuXG5leHBvcnQgaW50ZXJmYWNlIFJkc1NxbFByb3BzIHtcbiAgLyoqXG4gICAqIFZQQyBuZXR3b3JrIHRvIHBsYWNlIHRoZSBwcm92aWRlciBsYW1iZGEuXG4gICAqXG4gICAqIE5vcm1hbGx5IHRoaXMgaXMgdGhlIFZQQyBvZiB5b3VyIGRhdGFiYXNlLlxuICAgKiBSZXF1aXJlZCB3aGVuIHlvdXIgZGF0YWJhc2UgaXMgb25seSBhY2Nlc3NpYmxlIGluIGEgVlBDLlxuICAgKiBOb3QgcmVxdWlyZWQgZm9yIERTUUwgYXMgaXQgdXNlcyBwdWJsaWMgZW5kcG9pbnRzIHdpdGggSUFNIGF1dGhlbnRpY2F0aW9uLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIEZ1bmN0aW9uIGlzIG5vdCBwbGFjZWQgd2l0aGluIGEgVlBDLlxuICAgKi9cbiAgcmVhZG9ubHkgdnBjPzogSVZwY1xuXG4gIC8qKlxuICAgKiBXaGVyZSB0byBwbGFjZSB0aGUgbmV0d29yayBwcm92aWRlciBsYW1iZGEgd2l0aGluIHRoZSBWUEMuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gdGhlIGlzb2xhdGVkIHN1Ym5ldCBpZiBub3Qgc3BlY2lmaWVkXG4gICAqL1xuICByZWFkb25seSB2cGNTdWJuZXRzPzogU3VibmV0U2VsZWN0aW9uXG5cbiAgLyoqXG4gICAqIFlvdXIgZGF0YWJhc2UgY2x1c3RlciBvciBpbnN0YW5jZS5cbiAgICogU3VwcG9ydHMgYm90aCB0cmFkaXRpb25hbCBSRFMvQXVyb3JhIGNsdXN0ZXJzIGFuZCBEU1FMIGNsdXN0ZXJzLlxuICAgKiAtIEZvciBSRFMvQXVyb3JhOiBzZWN1cml0eSBncm91cHMgd2lsbCBiZSBjb25maWd1cmVkIHRvIGFsbG93IGFjY2Vzc1xuICAgKiAtIEZvciBEU1FMOiBJQU0gYXV0aGVudGljYXRpb24gd2lsbCBiZSB1c2VkIGluc3RlYWQgb2Ygc2VjcmV0c1xuICAgKi9cbiAgcmVhZG9ubHkgY2x1c3RlcjogSURhdGFiYXNlQ2x1c3RlciB8IElEYXRhYmFzZUluc3RhbmNlIHwgZHNxbC5DZm5DbHVzdGVyXG5cbiAgLyoqXG4gICAqIFNlY3JldCB0aGF0IGdyYW50cyBhY2Nlc3MgdG8geW91ciBkYXRhYmFzZS5cbiAgICpcbiAgICogVXN1YWxseSB0aGlzIGlzIHlvdXIgY2x1c3RlcidzIG1hc3RlciBzZWNyZXQuXG4gICAqIE5vdCByZXF1aXJlZCB3aGVuIHJlbHlpbmcgb24gSUFNIGF1dGhlbnRpY2F0aW9uIChzdWNoIGFzIERTUUwpLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIHVuZGVmaW5lZCBmb3IgRFNRTCBjbHVzdGVycyB1c2luZyBJQU0gYXV0aGVudGljYXRpb25cbiAgICovXG4gIHJlYWRvbmx5IHNlY3JldD86IElTZWNyZXRcblxuICAvKipcbiAgICogVGltZW91dCBmb3IgbGFtYmRhIHRvIGRvIGl0cyB3b3JrLlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIDUgbWludXRlc1xuICAgKi9cbiAgcmVhZG9ubHkgdGltZW91dD86IER1cmF0aW9uXG5cbiAgLyoqXG4gICAqIExvZyBTUUwgc3RhdGVtZW50cy4gVGhpcyBpbmNsdWRlcyBwYXNzd29yZHMuIFVzZSBvbmx5IGZvciBkZWJ1Z2dpbmcuXG4gICAqXG4gICAqIEBkZWZhdWx0IC0gZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IGxvZ2dlcj86IGJvb2xlYW5cblxuICAvKipcbiAgICogQWRkaXRpb25hbCBmdW5jdGlvbiBjdXN0b21pemF0aW9uLlxuICAgKlxuICAgKiBUaGlzIGVuYWJsZXMgYWRkaXRpb25hbCBmdW5jdGlvbiBjdXN0b21pemF0aW9uIHN1Y2ggYXMgdGhlIGxvZyBncm91cC4gSG93ZXZlcixcbiAgICogbGFtYmRhIGZ1bmN0aW9uIHByb3BlcnRpZXMgY29udHJvbGxlZCBieSBvdGhlciB7UmRzU3FsUHJvcHN9IHBhcmFtZXRlcnMgd2lsbCB0cnVtcFxuICAgKiBvcGlvbnMgc2V0IHZpYSB0aGlzIHBhcmFtZXRlci5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBlbXB0eVxuICAgKi9cbiAgcmVhZG9ubHkgZnVuY3Rpb25Qcm9wcz86IE5vZGVqc0Z1bmN0aW9uUHJvcHNcblxuICAvKipcbiAgICogVXNlIFNTTD9cbiAgICpcbiAgICogQGRlZmF1bHQgLSB0cnVlXG4gICAqL1xuICByZWFkb25seSBzc2w/OiBib29sZWFuXG59XG5cbi8qKlxuICogU3VwcG9ydGVkIGRhdGFiYXNlIGVuZ2luZXNcbiAqL1xuZXhwb3J0IGVudW0gRGF0YWJhc2VFbmdpbmUge1xuICBQT1NUR1JFUyA9IFwicG9zdGdyZXNcIixcbiAgTVlTUUwgPSBcIm15c3FsXCIsXG4gIERTUUwgPSBcImRzcWxcIixcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJUHJvdmlkZXIge1xuICByZWFkb25seSBzZXJ2aWNlVG9rZW46IHN0cmluZ1xuICByZWFkb25seSBoYW5kbGVyOiBJRnVuY3Rpb25cbiAgcmVhZG9ubHkgc2VjcmV0PzogSVNlY3JldFxuICByZWFkb25seSBlbmdpbmU6IHN0cmluZ1xuICByZWFkb25seSBjbHVzdGVyPzogSURhdGFiYXNlQ2x1c3RlciB8IElEYXRhYmFzZUluc3RhbmNlIHwgZHNxbC5DZm5DbHVzdGVyXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJvdmlkZXJBdHRyaWJ1dGVzIHtcbiAgLyoqXG4gICAqIEVpdGhlciB0aGUgQVJOIG9yIG5hbWUgb2YgdGhlIExhbWJkYSBmdW5jdGlvbi5cbiAgICogVXNlIGZ1bmN0aW9uQXJuIGZvciBjcm9zcy1hY2NvdW50IG9yIGNyb3NzLXJlZ2lvbiBzY2VuYXJpb3MuXG4gICAqIFVzZSBmdW5jdGlvbk5hbWUgZm9yIHNhbWUtYWNjb3VudCwgc2FtZS1yZWdpb24gc2NlbmFyaW9zLlxuICAgKi9cbiAgcmVhZG9ubHkgZnVuY3Rpb25Bcm4/OiBzdHJpbmdcbiAgcmVhZG9ubHkgZnVuY3Rpb25OYW1lPzogc3RyaW5nXG4gIHJlYWRvbmx5IGVuZ2luZTogRGF0YWJhc2VFbmdpbmVcbiAgLyoqXG4gICAqIE9wdGlvbmFsIGNsdXN0ZXIgaW5mb3JtYXRpb24gZm9yIHJvbGUgY3JlYXRpb24uXG4gICAqXG4gICAqIFdoZW4gaW1wb3J0aW5nIGEgcHJvdmlkZXIsIGNsdXN0ZXIgZGV0YWlscyBhcmUgb2Z0ZW4gbm90IGF2YWlsYWJsZS5cbiAgICogSG93ZXZlciwgc29tZSBvcGVyYXRpb25zIGxpa2Ugcm9sZSBjcmVhdGlvbiByZXF1aXJlIGNsdXN0ZXIgZW5kcG9pbnRcbiAgICogaW5mb3JtYXRpb24gdG8gYnVpbGQgY29ubmVjdGlvbiBzZWNyZXRzLlxuICAgKlxuICAgKiBJZiB5b3UgcGxhbiB0byBjcmVhdGUgcm9sZXMgd2l0aCB0aGUgaW1wb3J0ZWQgcHJvdmlkZXIsIHlvdSBtdXN0XG4gICAqIHByb3ZpZGUgdGhlIGNsdXN0ZXIgcmVmZXJlbmNlLiBJZiB5b3Ugb25seSBwbGFuIHRvIHVzZSBleGlzdGluZ1xuICAgKiByb2xlcywgZGF0YWJhc2VzLCBzY2hlbWFzLCBvciBTUUwgb3BlcmF0aW9ucywgdGhpcyBjYW4gYmUgb21pdHRlZC5cbiAgICovXG4gIHJlYWRvbmx5IGNsdXN0ZXI/OiBJRGF0YWJhc2VDbHVzdGVyIHwgSURhdGFiYXNlSW5zdGFuY2UgfCBkc3FsLkNmbkNsdXN0ZXJcbn1cblxuZXhwb3J0IGNsYXNzIFByb3ZpZGVyIGV4dGVuZHMgQ29uc3RydWN0IGltcGxlbWVudHMgSVByb3ZpZGVyIHtcbiAgLyoqXG4gICAqIEltcG9ydCBhbiBleGlzdGluZyBwcm92aWRlciBMYW1iZGEgZnVuY3Rpb25cbiAgICovXG4gIHN0YXRpYyBmcm9tUHJvdmlkZXJBdHRyaWJ1dGVzKFxuICAgIHNjb3BlOiBDb25zdHJ1Y3QsXG4gICAgaWQ6IHN0cmluZyxcbiAgICBhdHRyczogUHJvdmlkZXJBdHRyaWJ1dGVzXG4gICk6IElQcm92aWRlciB7XG4gICAgcmV0dXJuIG5ldyBJbXBvcnRlZFByb3ZpZGVyKHNjb3BlLCBpZCwgYXR0cnMpXG4gIH1cblxuICBwdWJsaWMgcmVhZG9ubHkgc2VydmljZVRva2VuOiBzdHJpbmdcbiAgcHVibGljIHJlYWRvbmx5IHNlY3JldD86IElTZWNyZXRcbiAgcHVibGljIHJlYWRvbmx5IGhhbmRsZXI6IElGdW5jdGlvblxuICBwdWJsaWMgcmVhZG9ubHkgY2x1c3Rlcj86IElEYXRhYmFzZUNsdXN0ZXIgfCBJRGF0YWJhc2VJbnN0YW5jZSB8IGRzcWwuQ2ZuQ2x1c3RlclxuXG4gIC8qKlxuICAgKiBUaGUgZW5naW5lIGxpa2UgXCJwb3N0Z3Jlc1wiIG9yIFwibXlzcWxcIlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGlmIHdlIGNhbm5vdCBkZXRlcm1pbmUgdGhpcyBcInBvc3RncmVzXCJcbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBlbmdpbmU6IHN0cmluZ1xuXG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIHByb3BzOiBSZHNTcWxQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZClcblxuICAgIC8vIFZhbGlkYXRlIGNvbmZpZ3VyYXRpb25cbiAgICBjb25zdCBpc0RzcWwgPSBpc0RzcWxDbHVzdGVyKHByb3BzLmNsdXN0ZXIpXG4gICAgaWYgKCFpc0RzcWwgJiYgIXByb3BzLnNlY3JldCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBcIkVpdGhlciBzZWNyZXQgKGZvciB0cmFkaXRpb25hbCBSRFMpIG9yIGNsdXN0ZXIgd2l0aCBEU1FMIG11c3QgYmUgcHJvdmlkZWRcIlxuICAgICAgKVxuICAgIH1cbiAgICBpZiAoIWlzRHNxbCAmJiAhcHJvcHMudnBjKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJWUEMgaXMgcmVxdWlyZWQgZm9yIHRyYWRpdGlvbmFsIFJEUyBkYXRhYmFzZXNcIilcbiAgICB9XG4gICAgaWYgKGlzRHNxbCAmJiBwcm9wcy5zZWNyZXQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgXCJzZWNyZXQgc2hvdWxkIG5vdCBiZSBwcm92aWRlZCB3aGVuIHVzaW5nIERTUUwgY2x1c3RlciAodXNlcyBJQU0gYXV0aGVudGljYXRpb24pXCJcbiAgICAgIClcbiAgICB9XG5cbiAgICB0aGlzLnNlY3JldCA9IHByb3BzLnNlY3JldFxuICAgIHRoaXMuY2x1c3RlciA9IHByb3BzLmNsdXN0ZXJcblxuICAgIC8vIERldGVybWluZSBlbmdpbmUgZnJvbSBjbHVzdGVyL2luc3RhbmNlIGluc3RlYWQgb2YgaGFyZGNvZGluZ1xuICAgIGlmIChpc0RzcWwpIHtcbiAgICAgIC8vIERTUUwgaXMgYWx3YXlzIFBvc3RncmVTUUwtY29tcGF0aWJsZVxuICAgICAgdGhpcy5lbmdpbmUgPSBEYXRhYmFzZUVuZ2luZS5EU1FMXG4gICAgfSBlbHNlIGlmIChcImNsdXN0ZXJJZGVudGlmaWVyXCIgaW4gcHJvcHMuY2x1c3Rlcikge1xuICAgICAgLy8gSXQncyBhIERhdGFiYXNlQ2x1c3RlclxuICAgICAgY29uc3QgY2x1c3RlckVuZ2luZSA9IChwcm9wcy5jbHVzdGVyIGFzIElEYXRhYmFzZUNsdXN0ZXIpLmVuZ2luZVxuICAgICAgdGhpcy5lbmdpbmUgPVxuICAgICAgICBjbHVzdGVyRW5naW5lICYmIGNsdXN0ZXJFbmdpbmUuZW5naW5lRmFtaWx5ID09PSBcIk1ZU1FMXCJcbiAgICAgICAgICA/IERhdGFiYXNlRW5naW5lLk1ZU1FMXG4gICAgICAgICAgOiBEYXRhYmFzZUVuZ2luZS5QT1NUR1JFU1xuICAgIH0gZWxzZSBpZiAoXCJpbnN0YW5jZUlkZW50aWZpZXJcIiBpbiBwcm9wcy5jbHVzdGVyKSB7XG4gICAgICAvLyBJdCdzIGEgRGF0YWJhc2VJbnN0YW5jZVxuICAgICAgY29uc3QgaW5zdGFuY2VFbmdpbmUgPSAocHJvcHMuY2x1c3RlciBhcyBJRGF0YWJhc2VJbnN0YW5jZSkuZW5naW5lXG4gICAgICB0aGlzLmVuZ2luZSA9XG4gICAgICAgIGluc3RhbmNlRW5naW5lICYmIGluc3RhbmNlRW5naW5lLmVuZ2luZUZhbWlseSA9PT0gXCJNWVNRTFwiXG4gICAgICAgICAgPyBEYXRhYmFzZUVuZ2luZS5NWVNRTFxuICAgICAgICAgIDogRGF0YWJhc2VFbmdpbmUuUE9TVEdSRVNcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gRmFsbGJhY2sgdG8gcG9zdGdyZXMgaWYgZW5naW5lIGhhc24ndCBiZWVuIHByb3ZpZGVkXG4gICAgICB0aGlzLmVuZ2luZSA9IERhdGFiYXNlRW5naW5lLlBPU1RHUkVTXG4gICAgfVxuXG4gICAgY29uc3QgZnVuY3Rpb25OYW1lID0gXCJSZHNTcWxcIiArIHNsdWdpZnkoXCIyOGI5ZTc5MS1hZjYwLTRhMzMtYmNhOC1mZmI2ZjMwZWY4YzVcIilcbiAgICB0aGlzLmhhbmRsZXIgPVxuICAgICAgKFN0YWNrLm9mKHRoaXMpLm5vZGUudHJ5RmluZENoaWxkKGZ1bmN0aW9uTmFtZSkgYXMgSUZ1bmN0aW9uKSA/P1xuICAgICAgdGhpcy5uZXdDdXN0b21SZXNvdXJjZUhhbmRsZXIoc2NvcGUsIGZ1bmN0aW9uTmFtZSwgcHJvcHMpXG5cbiAgICBjb25zdCBwcm92aWRlciA9IG5ldyBjdXN0b21SZXNvdXJjZXMuUHJvdmlkZXIodGhpcywgXCJSZHNTcWxcIiwge1xuICAgICAgb25FdmVudEhhbmRsZXI6IHRoaXMuaGFuZGxlcixcbiAgICB9KVxuICAgIHRoaXMuc2VydmljZVRva2VuID0gcHJvdmlkZXIuc2VydmljZVRva2VuXG5cbiAgICAvLyBIYW5kbGUgZGF0YWJhc2UgY29ubmVjdGlvbiBzZXR1cFxuICAgIGlmIChpc0RzcWwpIHtcbiAgICAgIC8vIEZvciBEU1FMLCBncmFudCBJQU0gcGVybWlzc2lvbnMgaW5zdGVhZCBvZiBWUEMgc2VjdXJpdHkgZ3JvdXBzXG4gICAgICBjb25zdCBkc3FsQ2x1c3RlciA9IHByb3BzLmNsdXN0ZXIgYXMgZHNxbC5DZm5DbHVzdGVyXG4gICAgICB0aGlzLmhhbmRsZXIuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgICAgZWZmZWN0OiBpYW0uRWZmZWN0LkFMTE9XLFxuICAgICAgICAgIGFjdGlvbnM6IFtcImRzcWw6RGJDb25uZWN0QWRtaW5cIl0sXG4gICAgICAgICAgcmVzb3VyY2VzOiBbZHNxbENsdXN0ZXIuYXR0clJlc291cmNlQXJuXSxcbiAgICAgICAgfSlcbiAgICAgIClcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gVHJhZGl0aW9uYWwgUkRTIHNldHVwIHdpdGggc2VjdXJpdHkgZ3JvdXBzIGFuZCBzZWNyZXRzXG4gICAgICB0aGlzLnNlY3JldCEuZ3JhbnRSZWFkKHRoaXMuaGFuZGxlcilcbiAgICAgIGlmICh0aGlzLnNlY3JldCEuZW5jcnlwdGlvbktleSkge1xuICAgICAgICAvLyBJdCBzZWVtcyB3ZSBuZWVkIHRvIGdyYW50IGV4cGxpY2l0IHBlcm1pc3Npb25cbiAgICAgICAgdGhpcy5zZWNyZXQhLmVuY3J5cHRpb25LZXkuZ3JhbnREZWNyeXB0KHRoaXMuaGFuZGxlcilcbiAgICAgIH1cbiAgICAgIGlmIChwcm9wcy5jbHVzdGVyLmNvbm5lY3Rpb25zLnNlY3VyaXR5R3JvdXBzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDbHVzdGVyIGRvZXMgbm90IGhhdmUgYSBzZWN1cml0eSBncm91cC5cIilcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IHNlY3VyaXR5R3JvdXAgPSBwcm9wcy5jbHVzdGVyLmNvbm5lY3Rpb25zLnNlY3VyaXR5R3JvdXBzWzBdIVxuICAgICAgICB0aGlzLmhhbmRsZXIubm9kZS5kZWZhdWx0Q2hpbGQ/Lm5vZGUuYWRkRGVwZW5kZW5jeShzZWN1cml0eUdyb3VwKVxuICAgICAgfVxuICAgIH1cbiAgICB0aGlzLm5vZGUuYWRkRGVwZW5kZW5jeShwcm9wcy5jbHVzdGVyKVxuICB9XG5cbiAgcHJvdGVjdGVkIG5ld0N1c3RvbVJlc291cmNlSGFuZGxlcihcbiAgICBzY29wZTogQ29uc3RydWN0LFxuICAgIGlkOiBzdHJpbmcsXG4gICAgcHJvcHM6IFJkc1NxbFByb3BzXG4gICk6IGxhbWJkYS5Ob2RlanNGdW5jdGlvbiB7XG4gICAgY29uc3QgaXNEc3FsID0gaXNEc3FsQ2x1c3Rlcihwcm9wcy5jbHVzdGVyKVxuICAgIGNvbnN0IGhhbmRsZXJEaXIgPSBwYXRoLmpvaW4oX19kaXJuYW1lLCBcImhhbmRsZXJcIilcbiAgICBjb25zdCBpbmRleF90cyA9IHBhdGguam9pbihoYW5kbGVyRGlyLCBcImluZGV4LnRzXCIpXG4gICAgY29uc3QgaW5kZXhfanMgPSBwYXRoLmpvaW4oaGFuZGxlckRpciwgXCJpbmRleC5qc1wiKVxuICAgIGxldCBlbnRyeTogc3RyaW5nXG5cbiAgICBpZiAoZXhpc3RzU3luYyhpbmRleF90cykpIHtcbiAgICAgIGVudHJ5ID0gaW5kZXhfdHNcbiAgICB9IGVsc2UgaWYgKGV4aXN0c1N5bmMoaW5kZXhfanMpKSB7XG4gICAgICBlbnRyeSA9IGluZGV4X2pzXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFVnbHkgaGFjayB0byBzdXBwb3J0IFNTVCAocG9zc2libHkgY2F1c2VkIGJ5IG15IGhhY2sgdG8gbWFrZSBTU1Qgd29yayB3aXRoIENvbW1vbkpTIGxpYnJhcmllcylcbiAgICAgIGVudHJ5ID0gcGF0aC5qb2luKFxuICAgICAgICBwYXRoLmRpcm5hbWUocHJvY2Vzcy5lbnYubnBtX3BhY2thZ2VfanNvbiB8fCBwcm9jZXNzLmN3ZCgpKSxcbiAgICAgICAgXCJub2RlX21vZHVsZXMvY2RrLXJkcy1zcWwvbGliL2hhbmRsZXIvaW5kZXguanNcIlxuICAgICAgKVxuICAgIH1cbiAgICBsZXQgc3NsX29wdGlvbnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gfCB1bmRlZmluZWRcbiAgICBpZiAocHJvcHMuc3NsICE9PSB1bmRlZmluZWQgJiYgIXByb3BzLnNzbCkge1xuICAgICAgc3NsX29wdGlvbnMgPSB7XG4gICAgICAgIFNTTDogSlNPTi5zdHJpbmdpZnkocHJvcHMuc3NsKSxcbiAgICAgIH1cbiAgICB9XG4gICAgY29uc3QgbG9nZ2VyID0gcHJvcHMubG9nZ2VyID8/IGZhbHNlXG5cbiAgICAvLyBCdWlsZCBlbnZpcm9ubWVudCB2YXJpYWJsZXNcbiAgICBjb25zdCBlbnZpcm9ubWVudDogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICAgIExPR0dFUjogbG9nZ2VyLnRvU3RyaW5nKCksXG4gICAgICAuLi5zc2xfb3B0aW9ucyxcbiAgICB9XG5cbiAgICAvLyBBZGQgRFNRTC1zcGVjaWZpYyBlbnZpcm9ubWVudCB2YXJpYWJsZXNcbiAgICBpZiAoaXNEc3FsKSB7XG4gICAgICBjb25zdCBkc3FsQ2x1c3RlciA9IHByb3BzLmNsdXN0ZXIgYXMgZHNxbC5DZm5DbHVzdGVyXG4gICAgICBjb25zdCBjbHVzdGVySWQgPSBkc3FsQ2x1c3Rlci5hdHRySWRlbnRpZmllclxuICAgICAgY29uc3QgcmVnaW9uID0gU3RhY2sub2Yoc2NvcGUpLnJlZ2lvblxuICAgICAgZW52aXJvbm1lbnQuRFNRTF9FTkRQT0lOVCA9IGAke2NsdXN0ZXJJZH0uZHNxbC4ke3JlZ2lvbn0ub24uYXdzYFxuICAgICAgZW52aXJvbm1lbnQuRFNRTF9QT1JUID0gXCI1NDMyXCJcbiAgICB9IGVsc2UgaWYgKHByb3BzLnNlY3JldCkge1xuICAgICAgLy8gQWRkIHNlY3JldCBBUk4gdG8gZW52aXJvbm1lbnQgZm9yIHRyYWRpdGlvbmFsIFJEU1xuICAgICAgZW52aXJvbm1lbnQuU0VDUkVUX0FSTiA9IHByb3BzLnNlY3JldC5zZWNyZXRBcm5cbiAgICB9XG5cbiAgICBjb25zdCB7IHBhcnRpdGlvbiwgcmVnaW9uOiBzdGFja1JlZ2lvbiwgYWNjb3VudCB9ID0gU3RhY2sub2Yoc2NvcGUpXG4gICAgY29uc3QgZGVsZXRlUGFyYW1ldGVyUG9saWN5ID0gbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgYWN0aW9uczogW1wic3NtOkRlbGV0ZVBhcmFtZXRlclwiXSxcbiAgICAgIHJlc291cmNlczogW2Bhcm46JHtwYXJ0aXRpb259OnNzbToke3N0YWNrUmVnaW9ufToke2FjY291bnR9OnBhcmFtZXRlci8qYF0sXG4gICAgICBjb25kaXRpb25zOiB7XG4gICAgICAgIFN0cmluZ0VxdWFsczoge1xuICAgICAgICAgIFwic3NtOlJlc291cmNlVGFnL2NyZWF0ZWQtYnlcIjogXCJjZGstcmRzLXNxbFwiLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICB9KVxuXG4gICAgY29uc3QgZm4gPSBuZXcgbGFtYmRhLk5vZGVqc0Z1bmN0aW9uKHNjb3BlLCBpZCwge1xuICAgICAgLi4ucHJvcHMuZnVuY3Rpb25Qcm9wcyxcbiAgICAgIC8vIE9ubHkgY29uZmlndXJlIFZQQyBmb3IgdHJhZGl0aW9uYWwgUkRTXG4gICAgICAuLi4oaXNEc3FsXG4gICAgICAgID8ge31cbiAgICAgICAgOiB7XG4gICAgICAgICAgICB2cGM6IHByb3BzLnZwYyxcbiAgICAgICAgICAgIHZwY1N1Ym5ldHM6IHByb3BzLnZwY1N1Ym5ldHMgPz8ge1xuICAgICAgICAgICAgICBzdWJuZXRUeXBlOiBTdWJuZXRUeXBlLlBSSVZBVEVfSVNPTEFURUQsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0pLFxuICAgICAgZW50cnk6IGVudHJ5LFxuICAgICAgcnVudGltZTogUnVudGltZS5OT0RFSlNfMjJfWCxcbiAgICAgIHRpbWVvdXQ6IHByb3BzLnRpbWVvdXQgPz8gcHJvcHMuZnVuY3Rpb25Qcm9wcz8udGltZW91dCA/PyBEdXJhdGlvbi5zZWNvbmRzKDMwMCksXG4gICAgICBidW5kbGluZzoge1xuICAgICAgICAvLyBJbmNsdWRlIHRoZSBtaWdyYXRpb25zIGRpcmVjdG9yeSBpbiB0aGUgYnVuZGxlXG4gICAgICAgIGNvbW1hbmRIb29rczoge1xuICAgICAgICAgIGJlZm9yZUJ1bmRsaW5nKF86IHN0cmluZywgb3V0cHV0RGlyOiBzdHJpbmcpOiBzdHJpbmdbXSB7XG4gICAgICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAgICBgY3VybCAtLXNpbGVudCAtZkwgaHR0cHM6Ly90cnVzdHN0b3JlLnBraS5yZHMuYW1hem9uYXdzLmNvbS9nbG9iYWwvZ2xvYmFsLWJ1bmRsZS5wZW0gLW8gJHtwYXRoLmpvaW4oXG4gICAgICAgICAgICAgICAgb3V0cHV0RGlyLFxuICAgICAgICAgICAgICAgIFwiZ2xvYmFsLWJ1bmRsZS5wZW1cIlxuICAgICAgICAgICAgICApfWAsXG4gICAgICAgICAgICBdXG4gICAgICAgICAgfSxcbiAgICAgICAgICBhZnRlckJ1bmRsaW5nKCk6IHN0cmluZ1tdIHtcbiAgICAgICAgICAgIHJldHVybiBbXVxuICAgICAgICAgIH0sXG4gICAgICAgICAgYmVmb3JlSW5zdGFsbCgpOiBzdHJpbmdbXSB7XG4gICAgICAgICAgICByZXR1cm4gW11cbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIGVudmlyb25tZW50LFxuICAgICAgaW5pdGlhbFBvbGljeTogW1xuICAgICAgICBkZWxldGVQYXJhbWV0ZXJQb2xpY3ksXG4gICAgICAgIC4uLihwcm9wcy5mdW5jdGlvblByb3BzPy5pbml0aWFsUG9saWN5ID8/IFtdKSxcbiAgICAgIF0sXG4gICAgfSlcblxuICAgIC8vIE9ubHkgY29uZmlndXJlIHNlY3VyaXR5IGdyb3VwcyBmb3IgdHJhZGl0aW9uYWwgUkRTIChub3QgRFNRTClcbiAgICBpZiAoXG4gICAgICAhaXNEc3FsICYmXG4gICAgICAoIXByb3BzLmZ1bmN0aW9uUHJvcHM/LnNlY3VyaXR5R3JvdXBzIHx8XG4gICAgICAgIHByb3BzLmZ1bmN0aW9uUHJvcHM/LnNlY3VyaXR5R3JvdXBzLmxlbmd0aCA9PT0gMClcbiAgICApIHtcbiAgICAgIGNvbnN0IHJkc0NsdXN0ZXIgPSBwcm9wcy5jbHVzdGVyIGFzIElEYXRhYmFzZUNsdXN0ZXIgfCBJRGF0YWJhc2VJbnN0YW5jZVxuICAgICAgcmRzQ2x1c3Rlci5jb25uZWN0aW9ucy5hbGxvd0RlZmF1bHRQb3J0RnJvbShcbiAgICAgICAgZm4sXG4gICAgICAgIFwiQWxsb3cgdGhlIHJkcyBzcWwgaGFuZGxlciB0byBjb25uZWN0IHRvIGRiXCJcbiAgICAgIClcbiAgICB9XG5cbiAgICByZXR1cm4gZm5cbiAgfVxufVxuXG5jbGFzcyBJbXBvcnRlZFByb3ZpZGVyIGV4dGVuZHMgQ29uc3RydWN0IGltcGxlbWVudHMgSVByb3ZpZGVyIHtcbiAgcHVibGljIHJlYWRvbmx5IHNlcnZpY2VUb2tlbjogc3RyaW5nXG4gIHB1YmxpYyByZWFkb25seSBoYW5kbGVyOiBJRnVuY3Rpb25cbiAgcHVibGljIHJlYWRvbmx5IHNlY3JldD86IElTZWNyZXRcbiAgcHVibGljIHJlYWRvbmx5IGVuZ2luZTogc3RyaW5nXG4gIHB1YmxpYyByZWFkb25seSBjbHVzdGVyPzogSURhdGFiYXNlQ2x1c3RlciB8IElEYXRhYmFzZUluc3RhbmNlIHwgZHNxbC5DZm5DbHVzdGVyXG5cbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgYXR0cnM6IFByb3ZpZGVyQXR0cmlidXRlcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZClcblxuICAgIC8vIFZhbGlkYXRlIHRoYXQgZWl0aGVyIGZ1bmN0aW9uQXJuIG9yIGZ1bmN0aW9uTmFtZSBpcyBwcm92aWRlZFxuICAgIGlmICghYXR0cnMuZnVuY3Rpb25Bcm4gJiYgIWF0dHJzLmZ1bmN0aW9uTmFtZSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRWl0aGVyIGZ1bmN0aW9uQXJuIG9yIGZ1bmN0aW9uTmFtZSBtdXN0IGJlIHByb3ZpZGVkXCIpXG4gICAgfVxuICAgIGlmIChhdHRycy5mdW5jdGlvbkFybiAmJiBhdHRycy5mdW5jdGlvbk5hbWUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIlByb3ZpZGUgZWl0aGVyIGZ1bmN0aW9uQXJuIG9yIGZ1bmN0aW9uTmFtZSwgbm90IGJvdGhcIilcbiAgICB9XG5cbiAgICAvLyBJbXBvcnQgdGhlIGV4aXN0aW5nIExhbWJkYSBmdW5jdGlvblxuICAgIHRoaXMuaGFuZGxlciA9IGF0dHJzLmZ1bmN0aW9uQXJuXG4gICAgICA/IEZ1bmN0aW9uLmZyb21GdW5jdGlvbkFybih0aGlzLCBcIkhhbmRsZXJcIiwgYXR0cnMuZnVuY3Rpb25Bcm4pXG4gICAgICA6IEZ1bmN0aW9uLmZyb21GdW5jdGlvbk5hbWUodGhpcywgXCJIYW5kbGVyXCIsIGF0dHJzLmZ1bmN0aW9uTmFtZSEpXG5cbiAgICAvLyBEZXJpdmUgc2VydmljZVRva2VuIGJ5IHdyYXBwaW5nIGluIGN1c3RvbSByZXNvdXJjZSBwcm92aWRlclxuICAgIGNvbnN0IHByb3ZpZGVyID0gbmV3IGN1c3RvbVJlc291cmNlcy5Qcm92aWRlcih0aGlzLCBcIlByb3ZpZGVyXCIsIHtcbiAgICAgIG9uRXZlbnRIYW5kbGVyOiB0aGlzLmhhbmRsZXIsXG4gICAgfSlcbiAgICB0aGlzLnNlcnZpY2VUb2tlbiA9IHByb3ZpZGVyLnNlcnZpY2VUb2tlblxuXG4gICAgdGhpcy5lbmdpbmUgPSBhdHRycy5lbmdpbmVcbiAgICB0aGlzLnNlY3JldCA9IHVuZGVmaW5lZCAvLyBJbXBvcnRlZCBwcm92aWRlcnMgZ2V0IHNlY3JldCBmcm9tIGVudmlyb25tZW50XG4gICAgdGhpcy5jbHVzdGVyID0gYXR0cnMuY2x1c3RlciAvLyBPcHRpb25hbCBjbHVzdGVyIGZvciByb2xlIGNyZWF0aW9uXG4gIH1cbn1cblxuZnVuY3Rpb24gc2x1Z2lmeSh4OiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4geC5yZXBsYWNlKC9bXmEtekEtWjAtOV0vZywgXCJcIilcbn1cbiJdfQ==