cdk-drizzle-migrate
Version:
AWS CDK construct for running Drizzle ORM migrations
154 lines • 24.7 kB
JavaScript
;
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DrizzleMigrate = void 0;
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const assert_1 = require("assert");
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 iam = require("aws-cdk-lib/aws-iam");
const lambda = require("aws-cdk-lib/aws-lambda");
const aws_lambda_nodejs_1 = require("aws-cdk-lib/aws-lambda-nodejs");
const logs = require("aws-cdk-lib/aws-logs");
const cr = 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;
}
/**
* A custom resource that runs Drizzle migrations
*/
class DrizzleMigrate extends constructs_1.Construct {
constructor(scope, id, props) {
super(scope, id);
// Validate configuration
const isDsql = props.cluster && isDsqlCluster(props.cluster);
if (!isDsql && !props.dbSecret) {
throw new Error("Either dbSecret (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.dbSecret) {
throw new Error("dbSecret should not be provided when using DSQL cluster (uses IAM authentication)");
}
const migrationsDir = path.join(process.cwd(), props.migrationsPath);
(0, assert_1.strict)((0, fs_1.existsSync)(migrationsDir), `Migrations directory ${migrationsDir} does not exist`);
const handlerDir = path.join(__dirname, "handler");
const ts_filename = path.join(handlerDir, "index.ts");
const js_filename = path.join(handlerDir, "index.js");
const entry = (0, fs_1.existsSync)(ts_filename) ? ts_filename : js_filename;
const environment = {
NO_COLOR: "1",
...(props.handlerProps?.environment || {}),
};
// Create explicit log group for the Lambda handler
const logGroup = new logs.LogGroup(this, "MigrateHandlerLogGroup", {
retention: logs.RetentionDays.ONE_WEEK,
// Keep log group, it's annoying when it gets deleted for new
// deploys, and something has gone wrong. You have no way to
// look at the logs in that case.
removalPolicy: aws_cdk_lib_1.RemovalPolicy.RETAIN,
});
const onEventHandler = new aws_lambda_nodejs_1.NodejsFunction(this, "MigrateHandler", {
runtime: lambda.Runtime.NODEJS_20_X,
entry: entry,
logGroup: logGroup,
loggingFormat: lambda.LoggingFormat.JSON,
applicationLogLevelV2: lambda.ApplicationLogLevel.INFO,
timeout: aws_cdk_lib_1.Duration.minutes(5),
vpc: props.vpc,
vpcSubnets: props.vpcSubnets,
...props.handlerProps,
environment,
bundling: {
sourceMap: false,
// Include the migrations directory in the bundle
commandHooks: {
beforeBundling(_, outputDir) {
const commands = [
`cp ${handlerDir}/handler.js ${outputDir}`,
`cp -r ${migrationsDir} ${path.join(outputDir, "migrations")}`,
// Always download RDS certificate for SSL connections (both RDS and DSQL need it)
`mkdir -p ${path.join(outputDir, "certs")}`,
`curl --silent -fL https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o ${path.join(outputDir, "certs", "global-bundle.pem")}`,
];
return commands;
},
afterBundling() {
return [];
},
beforeInstall() {
return [];
},
},
...props.handlerProps?.bundling,
},
});
// Handle database connection setup
if (isDsql) {
// For DSQL, grant IAM permissions instead of VPC security groups
const dsqlCluster = props.cluster;
onEventHandler.addToRolePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["dsql:DbConnectAdmin"],
resources: [dsqlCluster.attrResourceArn],
}));
}
else {
// Traditional RDS setup with security groups
if (props.cluster &&
(!props.handlerProps ||
typeof props.handlerProps.securityGroups === "undefined" ||
props.handlerProps.securityGroups.length === 0)) {
const rdsCluster = props.cluster;
rdsCluster.connections.allowDefaultPortFrom(onEventHandler.connections, "Allow drizzle migrate lambda to connect to db");
}
// Grant the Lambda function permission to read the secret
props.dbSecret.grantRead(onEventHandler);
}
this.handler = onEventHandler;
const provider = new cr.Provider(this, "Provider", {
onEventHandler,
logGroup,
});
// Build custom resource properties based on database type
const customResourceProperties = {
// We're now using a fixed path inside the Lambda bundle
migrationsPath: "migrations",
// Adding a timestamp ensures the resource is updated on each deployment
timestamp: Date.now().toString(),
};
if (isDsql) {
// For DSQL, construct the endpoint from cluster ID and region
// DSQL endpoint format: ${clusterId}.dsql.${region}.on.aws
const dsqlCluster = props.cluster;
const clusterId = dsqlCluster.attrIdentifier;
const region = aws_cdk_lib_1.Stack.of(this).region;
customResourceProperties.endpoint = `${clusterId}.dsql.${region}.on.aws`;
customResourceProperties.port = "5432";
}
else {
customResourceProperties.secretArn = props.dbSecret.secretArn;
}
this.resource = new aws_cdk_lib_1.CustomResource(this, "CustomResource", {
serviceToken: provider.serviceToken,
properties: customResourceProperties,
});
const resourceCfn = this.resource.node.defaultChild;
resourceCfn.addPropertyOverride("ServiceTimeout", 900);
// Add dependency to ensure database is created before migrations run
if (props.cluster) {
this.resource.node.addDependency(props.cluster);
}
}
}
exports.DrizzleMigrate = DrizzleMigrate;
_a = JSII_RTTI_SYMBOL_1;
DrizzleMigrate[_a] = { fqn: "cdk-drizzle-migrate.DrizzleMigrate", version: "2.0.4" };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJpenpsZS1taWdyYXRlLXByb3ZpZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2RyaXp6bGUtbWlncmF0ZS1wcm92aWRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLG1DQUF5QztBQUN6QywyQkFBK0I7QUFDL0IsNkJBQTRCO0FBQzVCLDZDQUF5RjtBQUN6Riw2Q0FBNEM7QUFFNUMsMkNBQTBDO0FBQzFDLGlEQUFnRDtBQUNoRCxxRUFBbUY7QUFDbkYsNkNBQTRDO0FBRzVDLG1EQUFrRDtBQUNsRCwyQ0FBc0M7QUFFdEM7O0dBRUc7QUFDSCxTQUFTLGFBQWEsQ0FDcEIsT0FBdUU7SUFFdkUsT0FBTyxPQUFPLFlBQVksSUFBSSxDQUFDLFVBQVUsQ0FBQTtBQUMzQyxDQUFDO0FBcUREOztHQUVHO0FBQ0gsTUFBYSxjQUFlLFNBQVEsc0JBQVM7SUFXM0MsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUEwQjtRQUNsRSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFBO1FBRWhCLHlCQUF5QjtRQUN6QixNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsT0FBTyxJQUFJLGFBQWEsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDNUQsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUMvQixNQUFNLElBQUksS0FBSyxDQUNiLDZFQUE2RSxDQUM5RSxDQUFBO1FBQ0gsQ0FBQztRQUNELElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUM7WUFDMUIsTUFBTSxJQUFJLEtBQUssQ0FBQywrQ0FBK0MsQ0FBQyxDQUFBO1FBQ2xFLENBQUM7UUFDRCxJQUFJLE1BQU0sSUFBSSxLQUFLLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDN0IsTUFBTSxJQUFJLEtBQUssQ0FDYixtRkFBbUYsQ0FDcEYsQ0FBQTtRQUNILENBQUM7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QztZQUNYLDhEQUE4RDtZQUM5RCwyREFBMkQ7WUFDM0QsTUFBTSxXQUFXLEdBQUcsS0FBSyxDQUFDLE9BQTBCLENBQUE7WUFDcEQsTUFBTSxTQUFTLEdBQUcsV0FBVyxDQUFDLGNBQWMsQ0FBQTtZQUM1QyxNQUFNLE1BQU0sR0FBRyxtQkFBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUE7WUFDcEMsd0JBQXdCLENBQUMsUUFBUSxHQUFHLEdBQUcsU0FBUyxTQUFTLE1BQU0sU0FBUyxDQUFBO1lBQ3hFLHdCQUF3QixDQUFDLElBQUksR0FBRyxNQUFNLENBQUE7UUFDeEMsQ0FBQzthQUFNLENBQUM7WUFDTix3QkFBd0IsQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDLFFBQVMsQ0FBQyxTQUFTLENBQUE7UUFDaEUsQ0FBQztRQUVELElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSw0QkFBYyxDQUFDLElBQUksRUFBRSxnQkFBZ0IsRUFBRTtZQUN6RCxZQUFZLEVBQUUsUUFBUSxDQUFDLFlBQVk7WUFDbkMsVUFBVSxFQUFFLHdCQUF3QjtTQUNyQyxDQUFDLENBQUE7UUFFRixNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxZQUEyQixDQUFBO1FBQ2xFLFdBQVcsQ0FBQyxtQkFBbUIsQ0FBQyxnQkFBZ0IsRUFBRSxHQUFHLENBQUMsQ0FBQTtRQUV0RCxxRUFBcUU7UUFDckUsSUFBSSxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDbEIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQTtRQUNqRCxDQUFDO0lBQ0gsQ0FBQzs7QUFqS0gsd0NBa0tDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgc3RyaWN0IGFzIGFzc2VydCB9IGZyb20gXCJhc3NlcnRcIlxuaW1wb3J0IHsgZXhpc3RzU3luYyB9IGZyb20gXCJmc1wiXG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gXCJwYXRoXCJcbmltcG9ydCB7IENmblJlc291cmNlLCBDdXN0b21SZXNvdXJjZSwgRHVyYXRpb24sIFN0YWNrLCBSZW1vdmFsUG9saWN5IH0gZnJvbSBcImF3cy1jZGstbGliXCJcbmltcG9ydCAqIGFzIGRzcWwgZnJvbSBcImF3cy1jZGstbGliL2F3cy1kc3FsXCJcbmltcG9ydCAqIGFzIGVjMiBmcm9tIFwiYXdzLWNkay1saWIvYXdzLWVjMlwiXG5pbXBvcnQgKiBhcyBpYW0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1pYW1cIlxuaW1wb3J0ICogYXMgbGFtYmRhIGZyb20gXCJhd3MtY2RrLWxpYi9hd3MtbGFtYmRhXCJcbmltcG9ydCB7IE5vZGVqc0Z1bmN0aW9uLCBOb2RlanNGdW5jdGlvblByb3BzIH0gZnJvbSBcImF3cy1jZGstbGliL2F3cy1sYW1iZGEtbm9kZWpzXCJcbmltcG9ydCAqIGFzIGxvZ3MgZnJvbSBcImF3cy1jZGstbGliL2F3cy1sb2dzXCJcbmltcG9ydCAqIGFzIHJkcyBmcm9tIFwiYXdzLWNkay1saWIvYXdzLXJkc1wiXG5pbXBvcnQgKiBhcyBzZWNyZXRzbWFuYWdlciBmcm9tIFwiYXdzLWNkay1saWIvYXdzLXNlY3JldHNtYW5hZ2VyXCJcbmltcG9ydCAqIGFzIGNyIGZyb20gXCJhd3MtY2RrLWxpYi9jdXN0b20tcmVzb3VyY2VzXCJcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gXCJjb25zdHJ1Y3RzXCJcblxuLyoqXG4gKiBIZWxwZXIgZnVuY3Rpb24gdG8gZGV0ZXJtaW5lIGlmIGEgY2x1c3RlciBpcyBhIERTUUwgY2x1c3RlclxuICovXG5mdW5jdGlvbiBpc0RzcWxDbHVzdGVyKFxuICBjbHVzdGVyOiByZHMuSURhdGFiYXNlQ2x1c3RlciB8IHJkcy5JRGF0YWJhc2VJbnN0YW5jZSB8IGRzcWwuQ2ZuQ2x1c3RlclxuKTogY2x1c3RlciBpcyBkc3FsLkNmbkNsdXN0ZXIge1xuICByZXR1cm4gY2x1c3RlciBpbnN0YW5jZW9mIGRzcWwuQ2ZuQ2x1c3RlclxufVxuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIERyaXp6bGVNaWdyYXRlXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRHJpenpsZU1pZ3JhdGVQcm9wcyB7XG4gIC8qKlxuICAgKiBUaGUgZGF0YWJhc2Ugc2VjcmV0IGNvbnRhaW5pbmcgY29ubmVjdGlvbiBkZXRhaWxzXG4gICAqIE11c3QgY29udGFpbiBzdGFuZGFyZCBDREsgZGF0YWJhc2Ugc2VjcmV0IHByb3BlcnRpZXM6IHVzZXJuYW1lLFxuICAgKiBwYXNzd29yZCwgaG9zdCwgcG9ydCwgZW5naW5lLCBldGMuXG4gICAqIE5vdCByZXF1aXJlZCB3aGVuIHJlbHlpbmcgb24gSUFNIGF1dGhlbnRpY2F0aW9uIChzdWNoIGFzIERTUUwpLlxuICAgKiBAZGVmYXVsdCAtIHVuZGVmaW5lZCBmb3IgRFNRTCBjbHVzdGVycyB1c2luZyBJQU0gYXV0aGVudGljYXRpb25cbiAgICovXG4gIHJlYWRvbmx5IGRiU2VjcmV0Pzogc2VjcmV0c21hbmFnZXIuSVNlY3JldFxuXG4gIC8qKlxuICAgKiBUaGUgcGF0aCB0byB0aGUgbWlncmF0aW9ucyBkaXJlY3RvcnlcbiAgICogVGhpcyBkaXJlY3Rvcnkgd2lsbCBiZSBidW5kbGVkIHdpdGggdGhlIExhbWJkYSBmdW5jdGlvblxuICAgKi9cbiAgcmVhZG9ubHkgbWlncmF0aW9uc1BhdGg6IHN0cmluZ1xuXG4gIC8qKlxuICAgKiBPcHRpb25hbCBwcm9wZXJ0aWVzIHRvIGN1c3RvbWl6ZSB0aGUgTGFtYmRhIGZ1bmN0aW9uXG4gICAqIEV4Y2x1ZGVzIHJ1bnRpbWUsIGVudHJ5LCBhbmQgaGFuZGxlciB3aGljaCBhcmUgbWFuYWdlZCBieSB0aGUgY29uc3RydWN0XG4gICAqIEBkZWZhdWx0IC0gRGVmYXVsdCBMYW1iZGEgY29uZmlndXJhdGlvbiBpcyB1c2VkXG4gICAqL1xuICByZWFkb25seSBoYW5kbGVyUHJvcHM/OiBOb2RlanNGdW5jdGlvblByb3BzXG5cbiAgLyoqXG4gICAqIFRoZSBWUEMgd2hlcmUgdGhlIExhbWJkYSBmdW5jdGlvbiB3aWxsIGJlIGRlcGxveWVkXG4gICAqIFJlcXVpcmVkIHdoZW4geW91ciBkYXRhYmFzZSBpcyBvbmx5IGFjY2Vzc2libGUgaW4gYSBWUEMuXG4gICAqIE5vdCByZXF1aXJlZCBmb3IgRFNRTCBhcyBpdCB1c2VzIHB1YmxpYyBlbmRwb2ludHMgd2l0aCBJQU0gYXV0aGVudGljYXRpb25cbiAgICogQGRlZmF1bHQgLSB1c2UgVlBDIG9mIHlvdXIgUkRTL0F1cm9yYSBjbHVzdGVyXG4gICAqL1xuICByZWFkb25seSB2cGM/OiBlYzIuSVZwY1xuXG4gIC8qKlxuICAgKiBPcHRpb25hbCBzdWJuZXQgc2VsZWN0aW9uIHRvIGRlcGxveSB0aGUgTGFtYmRhIGZ1bmN0aW9uXG4gICAqIE9ubHkgdXNlZCB3aGVuIHZwYyBpcyBzcGVjaWZpZWRcbiAgICogQGRlZmF1bHQgLSBQUklWQVRFX1dJVEhfRUdSRVNTIHN1Ym5ldHNcbiAgICovXG4gIHJlYWRvbmx5IHZwY1N1Ym5ldHM/OiBlYzIuU3VibmV0U2VsZWN0aW9uXG5cbiAgLyoqXG4gICAqIE9wdGlvbmFsIGRhdGFiYXNlIGNsdXN0ZXIgb3IgaW5zdGFuY2VcbiAgICogU3VwcG9ydHMgYm90aCB0cmFkaXRpb25hbCBSRFMvQXVyb3JhIGNsdXN0ZXJzIGFuZCBEU1FMIGNsdXN0ZXJzXG4gICAqIC0gRm9yIFJEUy9BdXJvcmE6IHNlY3VyaXR5IGdyb3VwcyB3aWxsIGJlIGNvbmZpZ3VyZWQgdG8gYWxsb3cgYWNjZXNzXG4gICAqIC0gRm9yIERTUUw6IElBTSBhdXRoZW50aWNhdGlvbiB3aWxsIGJlIHVzZWQgaW5zdGVhZCBvZiBzZWNyZXRzXG4gICAqIEBkZWZhdWx0IC0gTm8gZGF0YWJhc2UgY29ubmVjdGlvbiBpcyBjb25maWd1cmVkXG4gICAqL1xuICByZWFkb25seSBjbHVzdGVyPzogcmRzLklEYXRhYmFzZUNsdXN0ZXIgfCByZHMuSURhdGFiYXNlSW5zdGFuY2UgfCBkc3FsLkNmbkNsdXN0ZXJcbn1cblxuLyoqXG4gKiBBIGN1c3RvbSByZXNvdXJjZSB0aGF0IHJ1bnMgRHJpenpsZSBtaWdyYXRpb25zXG4gKi9cbmV4cG9ydCBjbGFzcyBEcml6emxlTWlncmF0ZSBleHRlbmRzIENvbnN0cnVjdCB7XG4gIC8qKlxuICAgKiBUaGUgY3VzdG9tIHJlc291cmNlIHRoYXQgd2FzIGNyZWF0ZWRcbiAgICovXG4gIHB1YmxpYyByZWFkb25seSByZXNvdXJjZTogQ3VzdG9tUmVzb3VyY2VcblxuICAvKipcbiAgICogVGhlIExhbWJkYSBmdW5jdGlvbiB0aGF0IGV4ZWN1dGVzIHRoZSBtaWdyYXRpb25zXG4gICAqL1xuICBwdWJsaWMgcmVhZG9ubHkgaGFuZGxlcjogTm9kZWpzRnVuY3Rpb25cblxuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogRHJpenpsZU1pZ3JhdGVQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZClcblxuICAgIC8vIFZhbGlkYXRlIGNvbmZpZ3VyYXRpb25cbiAgICBjb25zdCBpc0RzcWwgPSBwcm9wcy5jbHVzdGVyICYmIGlzRHNxbENsdXN0ZXIocHJvcHMuY2x1c3RlcilcbiAgICBpZiAoIWlzRHNxbCAmJiAhcHJvcHMuZGJTZWNyZXQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgXCJFaXRoZXIgZGJTZWNyZXQgKGZvciB0cmFkaXRpb25hbCBSRFMpIG9yIGNsdXN0ZXIgd2l0aCBEU1FMIG11c3QgYmUgcHJvdmlkZWRcIlxuICAgICAgKVxuICAgIH1cbiAgICBpZiAoIWlzRHNxbCAmJiAhcHJvcHMudnBjKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJWUEMgaXMgcmVxdWlyZWQgZm9yIHRyYWRpdGlvbmFsIFJEUyBkYXRhYmFzZXNcIilcbiAgICB9XG4gICAgaWYgKGlzRHNxbCAmJiBwcm9wcy5kYlNlY3JldCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICBcImRiU2VjcmV0IHNob3VsZCBub3QgYmUgcHJvdmlkZWQgd2hlbiB1c2luZyBEU1FMIGNsdXN0ZXIgKHVzZXMgSUFNIGF1dGhlbnRpY2F0aW9uKVwiXG4gICAgICApXG4gICAgfVxuXG4gICAgY29uc3QgbWlncmF0aW9uc0RpciA9IHBhdGguam9pbihwcm9jZXNzLmN3ZCgpLCBwcm9wcy5taWdyYXRpb25zUGF0aClcbiAgICBhc3NlcnQoXG4gICAgICBleGlzdHNTeW5jKG1pZ3JhdGlvbnNEaXIpLFxuICAgICAgYE1pZ3JhdGlvbnMgZGlyZWN0b3J5ICR7bWlncmF0aW9uc0Rpcn0gZG9lcyBub3QgZXhpc3RgXG4gICAgKVxuICAgIGNvbnN0IGhhbmRsZXJEaXIgPSBwYXRoLmpvaW4oX19kaXJuYW1lLCBcImhhbmRsZXJcIilcblxuICAgIGNvbnN0IHRzX2ZpbGVuYW1lID0gcGF0aC5qb2luKGhhbmRsZXJEaXIsIFwiaW5kZXgudHNcIilcbiAgICBjb25zdCBqc19maWxlbmFtZSA9IHBhdGguam9pbihoYW5kbGVyRGlyLCBcImluZGV4LmpzXCIpXG4gICAgY29uc3QgZW50cnkgPSBleGlzdHNTeW5jKHRzX2ZpbGVuYW1lKSA/IHRzX2ZpbGVuYW1lIDoganNfZmlsZW5hbWVcblxuICAgIGNvbnN0IGVudmlyb25tZW50OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgICAgTk9fQ09MT1I6IFwiMVwiLFxuICAgICAgLi4uKHByb3BzLmhhbmRsZXJQcm9wcz8uZW52aXJvbm1lbnQgfHwge30pLFxuICAgIH1cblxuICAgIC8vIENyZWF0ZSBleHBsaWNpdCBsb2cgZ3JvdXAgZm9yIHRoZSBMYW1iZGEgaGFuZGxlclxuICAgIGNvbnN0IGxvZ0dyb3VwID0gbmV3IGxvZ3MuTG9nR3JvdXAodGhpcywgXCJNaWdyYXRlSGFuZGxlckxvZ0dyb3VwXCIsIHtcbiAgICAgIHJldGVudGlvbjogbG9ncy5SZXRlbnRpb25EYXlzLk9ORV9XRUVLLFxuICAgICAgLy8gS2VlcCBsb2cgZ3JvdXAsIGl0J3MgYW5ub3lpbmcgd2hlbiBpdCBnZXRzIGRlbGV0ZWQgZm9yIG5ld1xuICAgICAgLy8gZGVwbG95cywgYW5kIHNvbWV0aGluZyBoYXMgZ29uZSB3cm9uZy4gWW91IGhhdmUgbm8gd2F5IHRvXG4gICAgICAvLyBsb29rIGF0IHRoZSBsb2dzIGluIHRoYXQgY2FzZS5cbiAgICAgIHJlbW92YWxQb2xpY3k6IFJlbW92YWxQb2xpY3kuUkVUQUlOLFxuICAgIH0pXG5cbiAgICBjb25zdCBvbkV2ZW50SGFuZGxlciA9IG5ldyBOb2RlanNGdW5jdGlvbih0aGlzLCBcIk1pZ3JhdGVIYW5kbGVyXCIsIHtcbiAgICAgIHJ1bnRpbWU6IGxhbWJkYS5SdW50aW1lLk5PREVKU18yMF9YLFxuICAgICAgZW50cnk6IGVudHJ5LFxuICAgICAgbG9nR3JvdXA6IGxvZ0dyb3VwLFxuICAgICAgbG9nZ2luZ0Zvcm1hdDogbGFtYmRhLkxvZ2dpbmdGb3JtYXQuSlNPTixcbiAgICAgIGFwcGxpY2F0aW9uTG9nTGV2ZWxWMjogbGFtYmRhLkFwcGxpY2F0aW9uTG9nTGV2ZWwuSU5GTyxcbiAgICAgIHRpbWVvdXQ6IER1cmF0aW9uLm1pbnV0ZXMoNSksXG4gICAgICB2cGM6IHByb3BzLnZwYyxcbiAgICAgIHZwY1N1Ym5ldHM6IHByb3BzLnZwY1N1Ym5ldHMsXG4gICAgICAuLi5wcm9wcy5oYW5kbGVyUHJvcHMsXG4gICAgICBlbnZpcm9ubWVudCxcbiAgICAgIGJ1bmRsaW5nOiB7XG4gICAgICAgIHNvdXJjZU1hcDogZmFsc2UsXG4gICAgICAgIC8vIEluY2x1ZGUgdGhlIG1pZ3JhdGlvbnMgZGlyZWN0b3J5IGluIHRoZSBidW5kbGVcbiAgICAgICAgY29tbWFuZEhvb2tzOiB7XG4gICAgICAgICAgYmVmb3JlQnVuZGxpbmcoXzogc3RyaW5nLCBvdXRwdXREaXI6IHN0cmluZyk6IHN0cmluZ1tdIHtcbiAgICAgICAgICAgIGNvbnN0IGNvbW1hbmRzID0gW1xuICAgICAgICAgICAgICBgY3AgJHtoYW5kbGVyRGlyfS9oYW5kbGVyLmpzICR7b3V0cHV0RGlyfWAsXG4gICAgICAgICAgICAgIGBjcCAtciAke21pZ3JhdGlvbnNEaXJ9ICR7cGF0aC5qb2luKG91dHB1dERpciwgXCJtaWdyYXRpb25zXCIpfWAsXG4gICAgICAgICAgICAgIC8vIEFsd2F5cyBkb3dubG9hZCBSRFMgY2VydGlmaWNhdGUgZm9yIFNTTCBjb25uZWN0aW9ucyAoYm90aCBSRFMgYW5kIERTUUwgbmVlZCBpdClcbiAgICAgICAgICAgICAgYG1rZGlyIC1wICR7cGF0aC5qb2luKG91dHB1dERpciwgXCJjZXJ0c1wiKX1gLFxuICAgICAgICAgICAgICBgY3VybCAtLXNpbGVudCAtZkwgaHR0cHM6Ly90cnVzdHN0b3JlLnBraS5yZHMuYW1hem9uYXdzLmNvbS9nbG9iYWwvZ2xvYmFsLWJ1bmRsZS5wZW0gLW8gJHtwYXRoLmpvaW4ob3V0cHV0RGlyLCBcImNlcnRzXCIsIFwiZ2xvYmFsLWJ1bmRsZS5wZW1cIil9YCxcbiAgICAgICAgICAgIF1cblxuICAgICAgICAgICAgcmV0dXJuIGNvbW1hbmRzXG4gICAgICAgICAgfSxcbiAgICAgICAgICBhZnRlckJ1bmRsaW5nKCk6IHN0cmluZ1tdIHtcbiAgICAgICAgICAgIHJldHVybiBbXVxuICAgICAgICAgIH0sXG4gICAgICAgICAgYmVmb3JlSW5zdGFsbCgpOiBzdHJpbmdbXSB7XG4gICAgICAgICAgICByZXR1cm4gW11cbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgICAuLi5wcm9wcy5oYW5kbGVyUHJvcHM/LmJ1bmRsaW5nLFxuICAgICAgfSxcbiAgICB9KVxuXG4gICAgLy8gSGFuZGxlIGRhdGFiYXNlIGNvbm5lY3Rpb24gc2V0dXBcbiAgICBpZiAoaXNEc3FsKSB7XG4gICAgICAvLyBGb3IgRFNRTCwgZ3JhbnQgSUFNIHBlcm1pc3Npb25zIGluc3RlYWQgb2YgVlBDIHNlY3VyaXR5IGdyb3Vwc1xuICAgICAgY29uc3QgZHNxbENsdXN0ZXIgPSBwcm9wcy5jbHVzdGVyIGFzIGRzcWwuQ2ZuQ2x1c3RlclxuICAgICAgb25FdmVudEhhbmRsZXIuYWRkVG9Sb2xlUG9saWN5KFxuICAgICAgICBuZXcgaWFtLlBvbGljeVN0YXRlbWVudCh7XG4gICAgICAgICAgZWZmZWN0OiBpYW0uRWZmZWN0LkFMTE9XLFxuICAgICAgICAgIGFjdGlvbnM6IFtcImRzcWw6RGJDb25uZWN0QWRtaW5cIl0sXG4gICAgICAgICAgcmVzb3VyY2VzOiBbZHNxbENsdXN0ZXIuYXR0clJlc291cmNlQXJuXSxcbiAgICAgICAgfSlcbiAgICAgIClcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gVHJhZGl0aW9uYWwgUkRTIHNldHVwIHdpdGggc2VjdXJpdHkgZ3JvdXBzXG4gICAgICBpZiAoXG4gICAgICAgIHByb3BzLmNsdXN0ZXIgJiZcbiAgICAgICAgKCFwcm9wcy5oYW5kbGVyUHJvcHMgfHxcbiAgICAgICAgICB0eXBlb2YgcHJvcHMuaGFuZGxlclByb3BzLnNlY3VyaXR5R3JvdXBzID09PSBcInVuZGVmaW5lZFwiIHx8XG4gICAgICAgICAgcHJvcHMuaGFuZGxlclByb3BzLnNlY3VyaXR5R3JvdXBzLmxlbmd0aCA9PT0gMClcbiAgICAgICkge1xuICAgICAgICBjb25zdCByZHNDbHVzdGVyID0gcHJvcHMuY2x1c3RlciBhcyByZHMuSURhdGFiYXNlQ2x1c3RlciB8IHJkcy5JRGF0YWJhc2VJbnN0YW5jZVxuICAgICAgICByZHNDbHVzdGVyLmNvbm5lY3Rpb25zLmFsbG93RGVmYXVsdFBvcnRGcm9tKFxuICAgICAgICAgIG9uRXZlbnRIYW5kbGVyLmNvbm5lY3Rpb25zLFxuICAgICAgICAgIFwiQWxsb3cgZHJpenpsZSBtaWdyYXRlIGxhbWJkYSB0byBjb25uZWN0IHRvIGRiXCJcbiAgICAgICAgKVxuICAgICAgfVxuXG4gICAgICAvLyBHcmFudCB0aGUgTGFtYmRhIGZ1bmN0aW9uIHBlcm1pc3Npb24gdG8gcmVhZCB0aGUgc2VjcmV0XG4gICAgICBwcm9wcy5kYlNlY3JldCEuZ3JhbnRSZWFkKG9uRXZlbnRIYW5kbGVyKVxuICAgIH1cblxuICAgIHRoaXMuaGFuZGxlciA9IG9uRXZlbnRIYW5kbGVyXG5cbiAgICBjb25zdCBwcm92aWRlciA9IG5ldyBjci5Qcm92aWRlcih0aGlzLCBcIlByb3ZpZGVyXCIsIHtcbiAgICAgIG9uRXZlbnRIYW5kbGVyLFxuICAgICAgbG9nR3JvdXAsXG4gICAgfSlcblxuICAgIC8vIEJ1aWxkIGN1c3RvbSByZXNvdXJjZSBwcm9wZXJ0aWVzIGJhc2VkIG9uIGRhdGFiYXNlIHR5cGVcbiAgICBjb25zdCBjdXN0b21SZXNvdXJjZVByb3BlcnRpZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgICAvLyBXZSdyZSBub3cgdXNpbmcgYSBmaXhlZCBwYXRoIGluc2lkZSB0aGUgTGFtYmRhIGJ1bmRsZVxuICAgICAgbWlncmF0aW9uc1BhdGg6IFwibWlncmF0aW9uc1wiLFxuICAgICAgLy8gQWRkaW5nIGEgdGltZXN0YW1wIGVuc3VyZXMgdGhlIHJlc291cmNlIGlzIHVwZGF0ZWQgb24gZWFjaCBkZXBsb3ltZW50XG4gICAgICB0aW1lc3RhbXA6IERhdGUubm93KCkudG9TdHJpbmcoKSxcbiAgICB9XG5cbiAgICBpZiAoaXNEc3FsKSB7XG4gICAgICAvLyBGb3IgRFNRTCwgY29uc3RydWN0IHRoZSBlbmRwb2ludCBmcm9tIGNsdXN0ZXIgSUQgYW5kIHJlZ2lvblxuICAgICAgLy8gRFNRTCBlbmRwb2ludCBmb3JtYXQ6ICR7Y2x1c3RlcklkfS5kc3FsLiR7cmVnaW9ufS5vbi5hd3NcbiAgICAgIGNvbnN0IGRzcWxDbHVzdGVyID0gcHJvcHMuY2x1c3RlciBhcyBkc3FsLkNmbkNsdXN0ZXJcbiAgICAgIGNvbnN0IGNsdXN0ZXJJZCA9IGRzcWxDbHVzdGVyLmF0dHJJZGVudGlmaWVyXG4gICAgICBjb25zdCByZWdpb24gPSBTdGFjay5vZih0aGlzKS5yZWdpb25cbiAgICAgIGN1c3RvbVJlc291cmNlUHJvcGVydGllcy5lbmRwb2ludCA9IGAke2NsdXN0ZXJJZH0uZHNxbC4ke3JlZ2lvbn0ub24uYXdzYFxuICAgICAgY3VzdG9tUmVzb3VyY2VQcm9wZXJ0aWVzLnBvcnQgPSBcIjU0MzJcIlxuICAgIH0gZWxzZSB7XG4gICAgICBjdXN0b21SZXNvdXJjZVByb3BlcnRpZXMuc2VjcmV0QXJuID0gcHJvcHMuZGJTZWNyZXQhLnNlY3JldEFyblxuICAgIH1cblxuICAgIHRoaXMucmVzb3VyY2UgPSBuZXcgQ3VzdG9tUmVzb3VyY2UodGhpcywgXCJDdXN0b21SZXNvdXJjZVwiLCB7XG4gICAgICBzZXJ2aWNlVG9rZW46IHByb3ZpZGVyLnNlcnZpY2VUb2tlbixcbiAgICAgIHByb3BlcnRpZXM6IGN1c3RvbVJlc291cmNlUHJvcGVydGllcyxcbiAgICB9KVxuXG4gICAgY29uc3QgcmVzb3VyY2VDZm4gPSB0aGlzLnJlc291cmNlLm5vZGUuZGVmYXVsdENoaWxkIGFzIENmblJlc291cmNlXG4gICAgcmVzb3VyY2VDZm4uYWRkUHJvcGVydHlPdmVycmlkZShcIlNlcnZpY2VUaW1lb3V0XCIsIDkwMClcblxuICAgIC8vIEFkZCBkZXBlbmRlbmN5IHRvIGVuc3VyZSBkYXRhYmFzZSBpcyBjcmVhdGVkIGJlZm9yZSBtaWdyYXRpb25zIHJ1blxuICAgIGlmIChwcm9wcy5jbHVzdGVyKSB7XG4gICAgICB0aGlzLnJlc291cmNlLm5vZGUuYWRkRGVwZW5kZW5jeShwcm9wcy5jbHVzdGVyKVxuICAgIH1cbiAgfVxufVxuIl19