cdk-rds-sql
Version:
A CDK construct that allows creating roles and databases an on Aurora Serverless Postgresql cluster.
320 lines • 44.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.handler = void 0;
const fs = require("fs");
const client_secrets_manager_1 = require("@aws-sdk/client-secrets-manager");
const exponential_backoff_1 = require("exponential-backoff");
const node_pg_format_1 = require("node-pg-format");
const pg_1 = require("pg");
const enum_1 = require("./enum");
const maxAttempts = 20;
const jumpTable = {
sql: {
Create: (_, props) => {
return props.Statement;
},
Update: (_, __, props) => {
return props.Statement;
},
Delete: (_, props) => {
return props.Rollback;
},
},
schema: {
Create: async (resourceId, props) => {
const sql = [node_pg_format_1.format("create schema if not exists %I", resourceId)];
if (props.RoleName) {
grantRoleForSchema(resourceId, props.RoleName).forEach((stmt) => sql.push(stmt));
}
return sql;
},
Update: async (resourceId, oldResourceId, props) => {
const sql = [];
// TODO: revoke old role-name if props.RoleName was removed or changed
if (props.RoleName) {
revokeRoleFromSchema(oldResourceId, props.RoleName).forEach((stmt) => sql.push(stmt));
}
sql.push(node_pg_format_1.format("alter schema %I rename to %I", oldResourceId, resourceId));
if (props.RoleName) {
grantRoleForSchema(resourceId, props.RoleName).forEach((stmt) => sql.push(stmt));
}
return sql;
},
Delete: (resourceId, props) => {
const sql = [];
if (props.RoleName) {
revokeRoleFromSchema(resourceId, props.RoleName).forEach((stmt) => sql.push(stmt));
}
sql.push(node_pg_format_1.format("drop schema if exists %I cascade", resourceId));
return sql;
},
},
role: {
Create: async (resourceId, props) => {
if (!props.PasswordArn)
throw "No PasswordArn provided";
const password = await getPassword(props.PasswordArn);
if (password) {
const sql = [
"start transaction",
node_pg_format_1.format("create role %I with login password %L", resourceId, password),
];
if (props.DatabaseName) {
// grant connect to database if database already exists
sql.push(node_pg_format_1.format(`DO $$
BEGIN
IF EXISTS (select from pg_database where datname = '%s' and datistemplate = false) THEN
grant connect on database %I to %I;
END IF;
END$$;`, props.DatabaseName, props.DatabaseName, resourceId));
}
sql.push("commit");
return sql;
}
else {
throw `Cannot parse password from ${props.PasswordArn}`;
}
},
Update: async (resourceId, oldResourceId, props) => {
// TODO: if database name has changed in OldResourceProperties, revoke connect
if (props && props.PasswordArn) {
const password = await getPassword(props.PasswordArn);
if (password) {
const sql = ["start transaction"];
if (oldResourceId !== resourceId) {
sql.push(node_pg_format_1.format("alter role %I rename to %I", oldResourceId, resourceId));
}
sql.push(node_pg_format_1.format("alter role %I with password %L", resourceId, password));
if (props.DatabaseName) {
sql.push(node_pg_format_1.format("grant connect on database %I to %I", props.DatabaseName, resourceId));
}
sql.push("commit");
return sql;
}
else {
throw `Cannot parse password from ${props.PasswordArn}`;
}
}
else {
const sql = ["start transaction"];
if (oldResourceId !== resourceId) {
sql.push(node_pg_format_1.format("alter role %I rename to %I", oldResourceId, resourceId));
}
sql.push(node_pg_format_1.format(`DO $$
BEGIN
IF EXISTS (select from pg_database where datname = '%s' and datistemplate = false) THEN
grant connect on database %I to %I;
END IF;
END$$;`, props.DatabaseName, props.DatabaseName, resourceId));
sql.push("commit");
return sql;
}
},
Delete: (resourceId, props) => {
// TODO: if user is owner of a database, assign ownership to master user
// This will require a specified inheritor on role creation
return [
"start transaction",
node_pg_format_1.format(`DO $$
BEGIN
IF EXISTS (select from pg_catalog.pg_roles WHERE rolname = '%s') AND EXISTS (select from pg_database WHERE datname = '%s') THEN
revoke all privileges on database %I from %I;
END IF;
END$$;`, resourceId, props.DatabaseName, props.DatabaseName, resourceId),
node_pg_format_1.format("drop role if exists %I", resourceId),
"commit",
];
},
},
database: {
Create: async (resourceId, props) => {
const owner = props.Owner;
if (owner) {
return [
node_pg_format_1.format("create database %I", resourceId),
node_pg_format_1.format("alter database %I owner to %I", resourceId, owner),
];
}
else {
return node_pg_format_1.format("create database %I", resourceId);
}
},
Update: async (resourceId, oldResourceId, props) => {
const statements = [];
if (resourceId !== oldResourceId) {
if (props.MasterOwner) {
statements.push(node_pg_format_1.format("alter database %I owner to %I", oldResourceId, props.MasterOwner));
}
statements.push(node_pg_format_1.format("alter database %I rename to %I", oldResourceId, resourceId));
}
const owner = props.Owner;
if (owner) {
statements.push(node_pg_format_1.format("alter database %I owner to %I", resourceId, props.Owner));
}
return statements;
},
Delete: (resourceId, newOwner) => {
return [
node_pg_format_1.format("select pg_terminate_backend(pg_stat_activity.pid) from pg_stat_activity where datname = %L", resourceId),
node_pg_format_1.format("DO $$BEGIN\nIF EXISTS (select from pg_database WHERE datname = '%s') THEN alter database %I owner to %I; END IF;\nEND$$;", resourceId, resourceId, newOwner),
node_pg_format_1.format("drop database if exists %I", resourceId),
];
},
},
};
const grantRoleForSchema = (schema, roleName) => [
node_pg_format_1.format("GRANT USAGE ON SCHEMA %I TO %I", schema, roleName),
node_pg_format_1.format("GRANT CREATE ON SCHEMA %I TO %I", schema, roleName),
];
const revokeRoleFromSchema = (schema, roleName) => [
node_pg_format_1.format("REVOKE CREATE ON SCHEMA %I FROM %I", schema, roleName),
node_pg_format_1.format("REVOKE ALL ON SCHEMA %I FROM %I", schema, roleName),
];
const log = process.env.LOGGER === "true"
? console.debug
: (_message, ..._optionalParams) => { };
exports.handler = async (event) => {
log(event);
const requestType = event.RequestType;
const resource = event.ResourceProperties.Resource;
const resourceId = event.ResourceProperties.ResourceId;
const databaseName = event.ResourceProperties.DatabaseName;
if (!Object.keys(jumpTable).includes(event.ResourceProperties.Resource)) {
throw `Resource type '${resource}' not recognised.`;
}
const secrets_client = new client_secrets_manager_1.SecretsManagerClient({});
const command = new client_secrets_manager_1.GetSecretValueCommand({
SecretId: event.ResourceProperties.SecretArn,
});
// As the IAM credentials can be cached, an update makde very recent
// could not yet be available.
// So we retry this a bit.
log("Fetching secret");
const secret = await exponential_backoff_1.backOff(async () => {
try {
const result = await secrets_client.send(command);
return result;
}
catch (e) {
log("Error fetching secret %o", e);
throw e;
}
}, {
numOfAttempts: 10,
startingDelay: 500,
});
if (!secret.SecretString)
throw "No secret string";
const secretValues = JSON.parse(secret.SecretString);
let sql;
switch (requestType) {
case "Create": {
sql = await jumpTable[resource][requestType](resourceId, event.ResourceProperties);
break;
}
case "Update": {
const oldResourceId = event
.PhysicalResourceId;
sql = await jumpTable[resource][requestType](resourceId, oldResourceId, {
...event.ResourceProperties,
MasterOwner: secretValues.username,
});
break;
}
case "Delete": {
if (resource === enum_1.RdsSqlResource.DATABASE) {
sql = jumpTable[resource][requestType](resourceId, secretValues.username);
}
else {
sql = jumpTable[resource][requestType](resourceId, event.ResourceProperties);
}
break;
}
}
if (sql) {
let database;
if (resource === enum_1.RdsSqlResource.ROLE) {
database = secretValues.dbname;
}
else {
database = databaseName ?? secretValues.dbname; // connect to given database if possible, else to database mentioned in secret
}
// Parse SSL option from env, if provided.
// If process.env.SSL is the string "false", disable SSL.
const isSslEnabled = process.env.SSL ? JSON.parse(process.env.SSL) : true;
const ssl = isSslEnabled
? {
ca: fs.readFileSync(`${process.env.LAMBDA_TASK_ROOT}/global-bundle.pem`),
rejectUnauthorized: true,
}
: false;
const params = {
host: secretValues.host,
port: secretValues.port,
user: secretValues.username,
password: secretValues.password,
database: database,
connectionTimeoutMillis: 30000,
ssl,
};
log(`Connecting to host ${params.host}:${params.port}${ssl ? " using a secure connection" : ""}, database ${params.database} as ${params.user}`);
log("Executing SQL", sql);
const pg_client = new pg_1.Client(params);
await pg_client.connect();
try {
await exponential_backoff_1.backOff(async () => {
if (typeof sql === "string") {
return pg_client.query(sql);
}
else {
if (sql) {
return Promise.all(sql.map((statement) => {
return pg_client.query(statement);
}));
}
else {
return;
}
}
}, {
retry: errorFilter,
numOfAttempts: maxAttempts,
});
}
finally {
await pg_client.end();
}
}
let response = {};
// Except for the SQL resource, return the new resource id. This
// will cause a delete to be send for the old resource.
if (resource !== enum_1.RdsSqlResource.SQL) {
response.PhysicalResourceId = resourceId;
}
return response;
};
// Custom error filter, mainly to retry role creation.
// Frequently see "tuple concurrently updated", and adding
// dependencies is very hard to make work.
const errorFilter = (error, nextAttemptNumber) => {
// Retry only if the error message contains "tuple concurrently"
// This will cover concurrent updates and deletes
const willRetry = error.message.includes("tuple concurrently");
log("Encountered an error on attempt %d/%d retry=%s error=[%o]", nextAttemptNumber - 1, maxAttempts, willRetry, error);
return willRetry;
};
/**
* Parse password field from secret. Returns void on error.
*/
const getPassword = async (arn) => {
const secrets_client = new client_secrets_manager_1.SecretsManagerClient({});
const command = new client_secrets_manager_1.GetSecretValueCommand({
SecretId: arn,
});
const secret = await secrets_client.send(command);
if (secret.SecretString) {
const json = JSON.parse(secret.SecretString);
return json.password;
}
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHlCQUF3QjtBQUV4Qiw0RUFJd0M7QUFNeEMsNkRBQTZDO0FBQzdDLG1EQUF1QztBQUN2QywyQkFBeUM7QUFDekMsaUNBQXVDO0FBc0R2QyxNQUFNLFdBQVcsR0FBRyxFQUFFLENBQUE7QUFFdEIsTUFBTSxTQUFTLEdBQWM7SUFDM0IsR0FBRyxFQUFFO1FBQ0gsTUFBTSxFQUFFLENBQUMsQ0FBUyxFQUFFLEtBQVcsRUFBRSxFQUFFO1lBQ2pDLE9BQU8sS0FBSyxDQUFDLFNBQVMsQ0FBQTtRQUN4QixDQUFDO1FBQ0QsTUFBTSxFQUFFLENBQUMsQ0FBUyxFQUFFLEVBQVUsRUFBRSxLQUFXLEVBQUUsRUFBRTtZQUM3QyxPQUFPLEtBQUssQ0FBQyxTQUFTLENBQUE7UUFDeEIsQ0FBQztRQUNELE1BQU0sRUFBRSxDQUFDLENBQVMsRUFBRSxLQUFXLEVBQUUsRUFBRTtZQUNqQyxPQUFPLEtBQUssQ0FBQyxRQUFRLENBQUE7UUFDdkIsQ0FBQztLQUNGO0lBQ0QsTUFBTSxFQUFFO1FBQ04sTUFBTSxFQUFFLEtBQUssRUFBRSxVQUFrQixFQUFFLEtBQWtCLEVBQUUsRUFBRTtZQUN2RCxNQUFNLEdBQUcsR0FBYSxDQUFDLHVCQUFNLENBQUMsZ0NBQWdDLEVBQUUsVUFBVSxDQUFDLENBQUMsQ0FBQTtZQUM1RSxJQUFJLEtBQUssQ0FBQyxRQUFRLEVBQUU7Z0JBQ2xCLGtCQUFrQixDQUFDLFVBQVUsRUFBRSxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUE7YUFDakY7WUFDRCxPQUFPLEdBQUcsQ0FBQTtRQUNaLENBQUM7UUFDRCxNQUFNLEVBQUUsS0FBSyxFQUFFLFVBQWtCLEVBQUUsYUFB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QVMsQ0FBQTtBQUNsQixDQUFDLENBQUE7QUFFRDs7R0FFRztBQUNILE1BQU0sV0FBVyxHQUFHLEtBQUssRUFBRSxHQUFXLEVBQTBCLEVBQUU7SUFDaEUsTUFBTSxjQUFjLEdBQUcsSUFBSSw2Q0FBb0IsQ0FBQyxFQUFFLENBQUMsQ0FBQTtJQUNuRCxNQUFNLE9BQU8sR0FBRyxJQUFJLDhDQUFxQixDQUFDO1FBQ3hDLFFBQVEsRUFBRSxHQUFHO0tBQ2QsQ0FBQyxDQUFBO0lBQ0YsTUFBTSxNQUFNLEdBQUcsTUFBTSxjQUFjLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFBO0lBQ2pELElBQUksTUFBTSxDQUFDLFlBQVksRUFBRTtRQUN2QixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQTtRQUM1QyxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUE7S0FDckI7QUFDSCxDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBmcyBmcm9tIFwiZnNcIlxuaW1wb3J0IHsgQ29ubmVjdGlvbk9wdGlvbnMgfSBmcm9tIFwidGxzXCJcbmltcG9ydCB7XG4gIFNlY3JldHNNYW5hZ2VyQ2xpZW50LFxuICBHZXRTZWNyZXRWYWx1ZUNvbW1hbmQsXG4gIEdldFNlY3JldFZhbHVlQ29tbWFuZE91dHB1dCxcbn0gZnJvbSBcIkBhd3Mtc2RrL2NsaWVudC1zZWNyZXRzLW1hbmFnZXJcIlxuaW1wb3J0IHtcbiAgQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZUNyZWF0ZUV2ZW50LFxuICBDbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlVXBkYXRlRXZlbnQsXG4gIENsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VEZWxldGVFdmVudCxcbn0gZnJvbSBcImF3cy1sYW1iZGFcIlxuaW1wb3J0IHsgYmFja09mZiB9IGZyb20gXCJleHBvbmVudGlhbC1iYWNrb2ZmXCJcbmltcG9ydCB7IGZvcm1hdCB9IGZyb20gXCJub2RlLXBnLWZvcm1hdFwiXG5pbXBvcnQgeyBDbGllbnQsIENsaWVudENvbmZpZyB9IGZyb20gXCJwZ1wiXG5pbXBvcnQgeyBSZHNTcWxSZXNvdXJjZSB9IGZyb20gXCIuL2VudW1cIlxuXG5pbnRlcmZhY2UgQ3VzdG9tUmVzb3VyY2VSZXNwb25zZSB7XG4gIFBoeXNpY2FsUmVzb3VyY2VJZD86IHN0cmluZ1xuICBEYXRhPzogYW55XG4gIE5vRWNobz86IGJvb2xlYW5cbn1cblxudHlwZSBDcmVhdGVSZXNvdXJjZUhhbmRsZXIgPSAoXG4gIHJlc291cmNlSWQ6IHN0cmluZyxcbiAgcHJvcHM/OiBhbnlcbikgPT4gUHJvbWlzZTxzdHJpbmcgfCBzdHJpbmdbXT5cbnR5cGUgVXBkYXRlUmVzb3VyY2VIYW5kbGVyID0gKFxuICByZXNvdXJjZUlkOiBzdHJpbmcsXG4gIG9sZFJlc291cmNlSWQ6IHN0cmluZyxcbiAgcHJvcHM/OiBhbnlcbikgPT4gUHJvbWlzZTxzdHJpbmcgfCBzdHJpbmdbXSB8IHZvaWQ+XG50eXBlIERlbGV0ZVJlc291cmNlSGFuZGxlciA9IChyZXNvdXJjZUlkOiBzdHJpbmcsIHByb3BzPzogYW55KSA9PiBzdHJpbmcgfCBzdHJpbmdbXSB8IHZvaWRcblxudHlwZSBKdW1wVGFibGUgPSB7XG4gIFtrZXkgaW4gUmRzU3FsUmVzb3VyY2VdOiB7XG4gICAgQ3JlYXRlOiBDcmVhdGVSZXNvdXJjZUhhbmRsZXJcbiAgICBVcGRhdGU6IFVwZGF0ZVJlc291cmNlSGFuZGxlclxuICAgIERlbGV0ZTogRGVsZXRlUmVzb3VyY2VIYW5kbGVyXG4gIH1cbn1cblxuaW50ZXJmYWNlIFJvbGVQcm9wcyB7XG4gIC8qKlxuICAgKiBTZWNyZXQgQVJOLlxuICAgKi9cbiAgUGFzc3dvcmRBcm46IHN0cmluZ1xuXG4gIC8qKlxuICAgKiBPcHRpb25hbCBkYXRhYmFzZSB0byB3aGljaCByb2xlIGlzIGdyYW50ZWQgY29ubmVjdCBwZXJtaXNzaW9ucy5cbiAgICovXG4gIERhdGFiYXNlTmFtZT86IHN0cmluZ1xufVxuXG5pbnRlcmZhY2UgRGF0YWJhc2VQcm9wcyB7XG4gIE93bmVyOiBzdHJpbmdcbn1cblxuaW50ZXJmYWNlIERhdGFiYXNlVXBkYXRlUHJvcHMgZXh0ZW5kcyBEYXRhYmFzZVByb3BzIHtcbiAgTWFzdGVyT3duZXI6IHN0cmluZ1xufVxuXG5pbnRlcmZhY2UgU2NoZW1hUHJvcHMge1xuICAvKipcbiAgICogT3B0aW9uYWwgcm9sZSBpcyBncmFudGVkIHBlcm1pc3Npb25zLlxuICAgKi9cbiAgUm9sZU5hbWU/OiBzdHJpbmdcbn1cblxuY29uc3QgbWF4QXR0ZW1wdHMgPSAyMFxuXG5jb25zdCBqdW1wVGFibGU6IEp1bXBUYWJsZSA9IHtcbiAgc3FsOiB7XG4gICAgQ3JlYXRlOiAoXzogc3RyaW5nLCBwcm9wcz86IGFueSkgPT4ge1xuICAgICAgcmV0dXJuIHByb3BzLlN0YXRlbWVudFxuICAgIH0sXG4gICAgVXBkYXRlOiAoXzogc3RyaW5nLCBfXzogc3RyaW5nLCBwcm9wcz86IGFueSkgPT4ge1xuICAgICAgcmV0dXJuIHByb3BzLlN0YXRlbWVudFxuICAgIH0sXG4gICAgRGVsZXRlOiAoXzogc3RyaW5nLCBwcm9wcz86IGFueSkgPT4ge1xuICAgICAgcmV0dXJuIHByb3BzLlJvbGxiYWNrXG4gICAgfSxcbiAgfSxcbiAgc2NoZW1hOiB7XG4gICAgQ3JlYXRlOiBhc3luYyAocmVzb3VyY2VJZDogc3RyaW5nLCBwcm9wczogU2NoZW1hUHJvcHMpID0+IHtcbiAgICAgIGNvbnN0IHNxbDogc3RyaW5nW10gPSBbZm9ybWF0KFwiY3JlYXRlIHNjaGVtYSBpZiBub3QgZXhpc3RzICVJXCIsIHJlc291cmNlSWQpXVxuICAgICAgaWYgKHByb3BzLlJvbGVOYW1lKSB7XG4gICAgICAgIGdyYW50Um9sZUZvclNjaGVtYShyZXNvdXJjZUlkLCBwcm9wcy5Sb2xlTmFtZSkuZm9yRWFjaCgoc3RtdCkgPT4gc3FsLnB1c2goc3RtdCkpXG4gICAgICB9XG4gICAgICByZXR1cm4gc3FsXG4gICAgfSxcbiAgICBVcGRhdGU6IGFzeW5jIChyZXNvdXJjZUlkOiBzdHJpbmcsIG9sZFJlc291cmNlSWQ6IHN0cmluZywgcHJvcHM6IFNjaGVtYVByb3BzKSA9PiB7XG4gICAgICBjb25zdCBzcWw6IHN0cmluZ1tdID0gW11cbiAgICAgIC8vIFRPRE86IHJldm9rZSBvbGQgcm9sZS1uYW1lIGlmIHByb3BzLlJvbGVOYW1lIHdhcyByZW1vdmVkIG9yIGNoYW5nZWRcbiAgICAgIGlmIChwcm9wcy5Sb2xlTmFtZSkge1xuICAgICAgICByZXZva2VSb2xlRnJvbVNjaGVtYShvbGRSZXNvdXJjZUlkLCBwcm9wcy5Sb2xlTmFtZSkuZm9yRWFjaCgoc3RtdCkgPT5cbiAgICAgICAgICBzcWwucHVzaChzdG10KVxuICAgICAgICApXG4gICAgICB9XG4gICAgICBzcWwucHVzaChmb3JtYXQoXCJhbHRlciBzY2hlbWEgJUkgcmVuYW1lIHRvICVJXCIsIG9sZFJlc291cmNlSWQsIHJlc291cmNlSWQpKVxuICAgICAgaWYgKHByb3BzLlJvbGVOYW1lKSB7XG4gICAgICAgIGdyYW50Um9sZUZvclNjaGVtYShyZXNvdXJjZUlkLCBwcm9wcy5Sb2xlTmFtZSkuZm9yRWFjaCgoc3RtdCkgPT4gc3FsLnB1c2goc3RtdCkpXG4gICAgICB9XG4gICAgICByZXR1cm4gc3FsXG4gICAgfSxcbiAgICBEZWxldGU6IChyZXNvdXJjZUlkOiBzdHJpbmcsIHByb3BzOiBTY2hlbWFQcm9wcykgPT4ge1xuICAgICAgY29uc3Qgc3FsOiBzdHJpbmdbXSA9IFtdXG4gICAgICBpZiAocHJvcHMuUm9sZU5hbWUpIHtcbiAgICAgICAgcmV2b2tlUm9sZUZyb21TY2hlbWEocmVzb3VyY2VJZCwgcHJvcHMuUm9sZU5hbWUpLmZvckVhY2goKHN0bXQpID0+IHNxbC5wdXNoKHN0bXQpKVxuICAgICAgfVxuICAgICAgc3FsLnB1c2goZm9ybWF0KFwiZHJvcCBzY2hlbWEgaWYgZXhpc3RzICVJIGNhc2NhZGVcIiwgcmVzb3VyY2VJZCkpXG4gICAgICByZXR1cm4gc3FsXG4gICAgfSxcbiAgfSxcbiAgcm9sZToge1xuICAgIENyZWF0ZTogYXN5bmMgKHJlc291cmNlSWQ6IHN0cmluZywgcHJvcHM6IFJvbGVQcm9wcykgPT4ge1xuICAgICAgaWYgKCFwcm9wcy5QYXNzd29yZEFybikgdGhyb3cgXCJObyBQYXNzd29yZEFybiBwcm92aWRlZFwiXG4gICAgICBjb25zdCBwYXNzd29yZCA9IGF3YWl0IGdldFBhc3N3b3JkKHByb3BzLlBhc3N3b3JkQXJuKVxuICAgICAgaWYgKHBhc3N3b3JkKSB7XG4gICAgICAgIGNvbnN0IHNxbCA9IFtcbiAgICAgICAgICBcInN0YXJ0IHRyYW5zYWN0aW9uXCIsXG4gICAgICAgICAgZm9ybWF0KFwiY3JlYXRlIHJvbGUgJUkgd2l0aCBsb2dpbiBwYXNzd29yZCAlTFwiLCByZXNvdXJjZUlkLCBwYXNzd29yZCksXG4gICAgICAgIF1cbiAgICAgICAgaWYgKHByb3BzLkRhdGFiYXNlTmFtZSkge1xuICAgICAgICAgIC8vIGdyYW50IGNvbm5lY3QgdG8gZGF0YWJhc2UgaWYgZGF0YWJhc2UgYWxyZWFkeSBleGlzdHNcbiAgICAgICAgICBzcWwucHVzaChcbiAgICAgICAgICAgIGZvcm1hdChcbiAgICAgICAgICAgICAgYERPICQkXG5CRUdJTlxuICBJRiBFWElTVFMgKHNlbGVjdCBmcm9tIHBnX2RhdGFiYXNlIHdoZXJlIGRhdG5hbWUgPSAnJXMnIGFuZCBkYXRpc3RlbXBsYXRlID0gZmFsc2UpIFRIRU5cbiAgICBncmFudCBjb25uZWN0IG9uIGRhdGFiYXNlICVJIHRvICVJO1xuICBFTkQgSUY7XG5FTkQkJDtgLFxuICAgICAgICAgICAgICBwcm9wcy5EYXRhYmFzZU5hbWUsXG4gICAgICAgICAgICAgIHByb3BzLkRhdGFiYXNlTmFtZSxcbiAgICAgICAgICAgICAgcmVzb3VyY2VJZFxuICAgICAgICAgICAgKVxuICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgICBzcWwucHVzaChcImNvbW1pdFwiKVxuICAgICAgICByZXR1cm4gc3FsXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBgQ2Fubm90IHBhcnNlIHBhc3N3b3JkIGZyb20gJHtwcm9wcy5QYXNzd29yZEFybn1gXG4gICAgICB9XG4gICAgfSxcbiAgICBVcGRhdGU6IGFzeW5jIChyZXNvdXJjZUlkOiBzdHJpbmcsIG9sZFJlc291cmNlSWQ6IHN0cmluZywgcHJvcHM6IFJvbGVQcm9wcykgPT4ge1xuICAgICAgLy8gVE9ETzogaWYgZGF0YWJhc2UgbmFtZSBoYXMgY2hhbmdlZCBpbiBPbGRSZXNvdXJjZVByb3BlcnRpZXMsIHJldm9rZSBjb25uZWN0XG4gICAgICBpZiAocHJvcHMgJiYgcHJvcHMuUGFzc3dvcmRBcm4pIHtcbiAgICAgICAgY29uc3QgcGFzc3dvcmQgPSBhd2FpdCBnZXRQYXNzd29yZChwcm9wcy5QYXNzd29yZEFybilcbiAgICAgICAgaWYgKHBhc3N3b3JkKSB7XG4gICAgICAgICAgY29uc3Qgc3FsID0gW1wic3RhcnQgdHJhbnNhY3Rpb25cIl1cbiAgICAgICAgICBpZiAob2xkUmVzb3VyY2VJZCAhPT0gcmVzb3VyY2VJZCkge1xuICAgICAgICAgICAgc3FsLnB1c2goZm9ybWF0KFwiYWx0ZXIgcm9sZSAlSSByZW5hbWUgdG8gJUlcIiwgb2xkUmVzb3VyY2VJZCwgcmVzb3VyY2VJZCkpXG4gICAgICAgICAgfVxuICAgICAgICAgIHNxbC5wdXNoKGZvcm1hdChcImFsdGVyIHJvbGUgJUkgd2l0aCBwYXNzd29yZCAlTFwiLCByZXNvdXJjZUlkLCBwYXNzd29yZCkpXG4gICAgICAgICAgaWYgKHByb3BzLkRhdGFiYXNlTmFtZSkge1xuICAgICAgICAgICAgc3FsLnB1c2goXG4gICAgICAgICAgICAgIGZvcm1hdChcImdyYW50IGNvbm5lY3Qgb24gZGF0YWJhc2UgJUkgdG8gJUlcIiwgcHJvcHMuRGF0YWJhc2VOYW1lLCByZXNvdXJjZUlkKVxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cbiAgICAgICAgICBzcWwucHVzaChcImNvbW1pdFwiKVxuICAgICAgICAgIHJldHVybiBzcWxcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aHJvdyBgQ2Fubm90IHBhcnNlIHBhc3N3b3JkIGZyb20gJHtwcm9wcy5QYXNzd29yZEFybn1gXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IHNxbCA9IFtcInN0YXJ0IHRyYW5zYWN0aW9uXCJdXG4gICAgICAgIGlmIChvbGRSZXNvdXJjZUlkICE9PSByZXNvdXJjZUlkKSB7XG4gICAgICAgICAgc3FsLnB1c2goZm9ybWF0KFwiYWx0ZXIgcm9sZSAlSSByZW5hbWUgdG8gJUlcIiwgb2xkUmVzb3VyY2VJZCwgcmVzb3VyY2VJZCkpXG4gICAgICAgIH1cbiAgICAgICAgc3FsLnB1c2goXG4gICAgICAgICAgZm9ybWF0KFxuICAgICAgICAgICAgYERPICQkXG5CRUdJTlxuICBJRiBFWElTVFMgKHNlbGVjdCBmcm9tIHBnX2RhdGFiYXNlIHdoZXJlIGRhdG5hbWUgPSAnJXMnIGFuZCBkYXRpc3RlbXBsYXRlID0gZmFsc2UpIFRIRU5cbiAgICBncmFudCBjb25uZWN0IG9uIGRhdGFiYXNlICVJIHRvICVJO1xuICBFTkQgSUY7XG5FTkQkJDtgLFxuICAgICAgICAgICAgcHJvcHMuRGF0YWJhc2VOYW1lLFxuICAgICAgICAgICAgcHJvcHMuRGF0YWJhc2VOYW1lLFxuICAgICAgICAgICAgcmVzb3VyY2VJZFxuICAgICAgICAgIClcbiAgICAgICAgKVxuICAgICAgICBzcWwucHVzaChcImNvbW1pdFwiKVxuICAgICAgICByZXR1cm4gc3FsXG4gICAgICB9XG4gICAgfSxcbiAgICBEZWxldGU6IChyZXNvdXJjZUlkOiBzdHJpbmcsIHByb3BzOiBSb2xlUHJvcHMpID0+IHtcbiAgICAgIC8vIFRPRE86IGlmIHVzZXIgaXMgb3duZXIgb2YgYSBkYXRhYmFzZSwgYXNzaWduIG93bmVyc2hpcCB0byBtYXN0ZXIgdXNlclxuICAgICAgLy8gVGhpcyB3aWxsIHJlcXVpcmUgYSBzcGVjaWZpZWQgaW5oZXJpdG9yIG9uIHJvbGUgY3JlYXRpb25cbiAgICAgIHJldHVybiBbXG4gICAgICAgIFwic3RhcnQgdHJhbnNhY3Rpb25cIixcbiAgICAgICAgZm9ybWF0KFxuICAgICAgICAgIGBETyAkJFxuQkVHSU5cbiAgSUYgRVhJU1RTIChzZWxlY3QgZnJvbSBwZ19jYXRhbG9nLnBnX3JvbGVzIFdIRVJFIHJvbG5hbWUgPSAnJXMnKSBBTkQgRVhJU1RTIChzZWxlY3QgZnJvbSBwZ19kYXRhYmFzZSBXSEVSRSBkYXRuYW1lID0gJyVzJykgVEhFTlxuICAgIHJldm9rZSBhbGwgcHJpdmlsZWdlcyBvbiBkYXRhYmFzZSAlSSBmcm9tICVJO1xuICBFTkQgSUY7XG5FTkQkJDtgLFxuICAgICAgICAgIHJlc291cmNlSWQsXG4gICAgICAgICAgcHJvcHMuRGF0YWJhc2VOYW1lLFxuICAgICAgICAgIHByb3BzLkRhdGFiYXNlTmFtZSxcbiAgICAgICAgICByZXNvdXJjZUlkXG4gICAgICAgICksXG4gICAgICAgIGZvcm1hdChcImRyb3Agcm9sZSBpZiBleGlzdHMgJUlcIiwgcmVzb3VyY2VJZCksXG4gICAgICAgIFwiY29tbWl0XCIsXG4gICAgICBdXG4gICAgfSxcbiAgfSxcbiAgZGF0YWJhc2U6IHtcbiAgICBDcmVhdGU6IGFzeW5jIChyZXNvdXJjZUlkOiBzdHJpbmcsIHByb3BzOiBEYXRhYmFzZVByb3BzKSA9PiB7XG4gICAgICBjb25zdCBvd25lciA9IHByb3BzLk93bmVyXG4gICAgICBpZiAob3duZXIpIHtcbiAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICBmb3JtYXQoXCJjcmVhdGUgZGF0YWJhc2UgJUlcIiwgcmVzb3VyY2VJZCksXG4gICAgICAgICAgZm9ybWF0KFwiYWx0ZXIgZGF0YWJhc2UgJUkgb3duZXIgdG8gJUlcIiwgcmVzb3VyY2VJZCwgb3duZXIpLFxuICAgICAgICBdXG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gZm9ybWF0KFwiY3JlYXRlIGRhdGFiYXNlICVJXCIsIHJlc291cmNlSWQpXG4gICAgICB9XG4gICAgfSxcbiAgICBVcGRhdGU6IGFzeW5jIChcbiAgICAgIHJlc291cmNlSWQ6IHN0cmluZyxcbiAgICAgIG9sZFJlc291cmNlSWQ6IHN0cmluZyxcbiAgICAgIHByb3BzOiBEYXRhYmFzZVVwZGF0ZVByb3BzXG4gICAgKTogUHJvbWlzZTxzdHJpbmdbXT4gPT4ge1xuICAgICAgY29uc3Qgc3RhdGVtZW50czogc3RyaW5nW10gPSBbXVxuICAgICAgaWYgKHJlc291cmNlSWQgIT09IG9sZFJlc291cmNlSWQpIHtcbiAgICAgICAgaWYgKHByb3BzLk1hc3Rlck93bmVyKSB7XG4gICAgICAgICAgc3RhdGVtZW50cy5wdXNoKFxuICAgICAgICAgICAgZm9ybWF0KFwiYWx0ZXIgZGF0YWJhc2UgJUkgb3duZXIgdG8gJUlcIiwgb2xkUmVzb3VyY2VJZCwgcHJvcHMuTWFzdGVyT3duZXIpXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICAgIHN0YXRlbWVudHMucHVzaChcbiAgICAgICAgICBmb3JtYXQoXCJhbHRlciBkYXRhYmFzZSAlSSByZW5hbWUgdG8gJUlcIiwgb2xkUmVzb3VyY2VJZCwgcmVzb3VyY2VJZClcbiAgICAgICAgKVxuICAgICAgfVxuICAgICAgY29uc3Qgb3duZXIgPSBwcm9wcy5Pd25lclxuICAgICAgaWYgKG93bmVyKSB7XG4gICAgICAgIHN0YXRlbWVudHMucHVzaChmb3JtYXQoXCJhbHRlciBkYXRhYmFzZSAlSSBvd25lciB0byAlSVwiLCByZXNvdXJjZUlkLCBwcm9wcy5Pd25lcikpXG4gICAgICB9XG4gICAgICByZXR1cm4gc3RhdGVtZW50c1xuICAgIH0sXG4gICAgRGVsZXRlOiAocmVzb3VyY2VJZDogc3RyaW5nLCBuZXdPd25lcjogc3RyaW5nKSA9PiB7XG4gICAgICByZXR1cm4gW1xuICAgICAgICBmb3JtYXQoXG4gICAgICAgICAgXCJzZWxlY3QgcGdfdGVybWluYXRlX2JhY2tlbmQocGdfc3RhdF9hY3Rpdml0eS5waWQpIGZyb20gcGdfc3RhdF9hY3Rpdml0eSB3aGVyZSBkYXRuYW1lID0gJUxcIixcbiAgICAgICAgICByZXNvdXJjZUlkXG4gICAgICAgICksXG4gICAgICAgIGZvcm1hdChcbiAgICAgICAgICBcIkRPICQkQkVHSU5cXG5JRiBFWElTVFMgKHNlbGVjdCBmcm9tIHBnX2RhdGFiYXNlIFdIRVJFIGRhdG5hbWUgPSAnJXMnKSBUSEVOIGFsdGVyIGRhdGFiYXNlICVJIG93bmVyIHRvICVJOyBFTkQgSUY7XFxuRU5EJCQ7XCIsXG4gICAgICAgICAgcmVzb3VyY2VJZCxcbiAgICAgICAgICByZXNvdXJjZUlkLFxuICAgICAgICAgIG5ld093bmVyXG4gICAgICAgICksXG4gICAgICAgIGZvcm1hdChcImRyb3AgZGF0YWJhc2UgaWYgZXhpc3RzICVJXCIsIHJlc291cmNlSWQpLFxuICAgICAgXVxuICAgIH0sXG4gIH0sXG59XG5cbmNvbnN0IGdyYW50Um9sZUZvclNjaGVtYSA9IChzY2hlbWE6IHN0cmluZywgcm9sZU5hbWU6IHN0cmluZykgPT4gW1xuICBmb3JtYXQoXCJHUkFOVCBVU0FHRSBPTiBTQ0hFTUEgJUkgVE8gJUlcIiwgc2NoZW1hLCByb2xlTmFtZSksXG4gIGZvcm1hdChcIkdSQU5UIENSRUFURSBPTiBTQ0hFTUEgJUkgVE8gJUlcIiwgc2NoZW1hLCByb2xlTmFtZSksXG5dXG5jb25zdCByZXZva2VSb2xlRnJvbVNjaGVtYSA9IChzY2hlbWE6IHN0cmluZywgcm9sZU5hbWU6IHN0cmluZykgPT4gW1xuICBmb3JtYXQoXCJSRVZPS0UgQ1JFQVRFIE9OIFNDSEVNQSAlSSBGUk9NICVJXCIsIHNjaGVtYSwgcm9sZU5hbWUpLFxuICBmb3JtYXQoXCJSRVZPS0UgQUxMIE9OIFNDSEVNQSAlSSBGUk9NICVJXCIsIHNjaGVtYSwgcm9sZU5hbWUpLFxuXVxuXG5jb25zdCBsb2cgPVxuICBwcm9jZXNzLmVudi5MT0dHRVIgPT09IFwidHJ1ZVwiXG4gICAgPyBjb25zb2xlLmRlYnVnXG4gICAgOiAoX21lc3NhZ2U/OiBhbnksIC4uLl9vcHRpb25hbFBhcmFtczogYW55W10pID0+IHt9XG5cbmV4cG9ydCBjb25zdCBoYW5kbGVyID0gYXN5bmMgKFxuICBldmVudDpcbiAgICB8IENsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VDcmVhdGVFdmVudFxuICAgIHwgQ2xvdWRGb3JtYXRpb25DdXN0b21SZXNvdXJjZVVwZGF0ZUV2ZW50XG4gICAgfCBDbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRGVsZXRlRXZlbnRcbik6IFByb21pc2U8YW55PiA9PiB7XG4gIGxvZyhldmVudClcblxuICBjb25zdCByZXF1ZXN0VHlwZSA9IGV2ZW50LlJlcXVlc3RUeXBlXG4gIGNvbnN0IHJlc291cmNlOiBSZHNTcWxSZXNvdXJjZSA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5SZXNvdXJjZVxuICBjb25zdCByZXNvdXJjZUlkID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLlJlc291cmNlSWRcbiAgY29uc3QgZGF0YWJhc2VOYW1lID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkRhdGFiYXNlTmFtZVxuXG4gIGlmICghT2JqZWN0LmtleXMoanVtcFRhYmxlKS5pbmNsdWRlcyhldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuUmVzb3VyY2UpKSB7XG4gICAgdGhyb3cgYFJlc291cmNlIHR5cGUgJyR7cmVzb3VyY2V9JyBub3QgcmVjb2duaXNlZC5gXG4gIH1cblxuICBjb25zdCBzZWNyZXRzX2NsaWVudCA9IG5ldyBTZWNyZXRzTWFuYWdlckNsaWVudCh7fSlcbiAgY29uc3QgY29tbWFuZCA9IG5ldyBHZXRTZWNyZXRWYWx1ZUNvbW1hbmQoe1xuICAgIFNlY3JldElkOiBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuU2VjcmV0QXJuLFxuICB9KVxuICAvLyBBcyB0aGUgSUFNIGNyZWRlbnRpYWxzIGNhbiBiZSBjYWNoZWQsIGFuIHVwZGF0ZSBtYWtkZSB2ZXJ5IHJlY2VudFxuICAvLyBjb3VsZCBub3QgeWV0IGJlIGF2YWlsYWJsZS5cbiAgLy8gU28gd2UgcmV0cnkgdGhpcyBhIGJpdC5cbiAgbG9nKFwiRmV0Y2hpbmcgc2VjcmV0XCIpXG4gIGNvbnN0IHNlY3JldDogR2V0U2VjcmV0VmFsdWVDb21tYW5kT3V0cHV0ID0gYXdhaXQgYmFja09mZihcbiAgICBhc3luYyAoKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzZWNyZXRzX2NsaWVudC5zZW5kKGNvbW1hbmQpXG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgbG9nKFwiRXJyb3IgZmV0Y2hpbmcgc2VjcmV0ICVvXCIsIGUpXG4gICAgICAgIHRocm93IGVcbiAgICAgIH1cbiAgICB9LFxuICAgIHtcbiAgICAgIG51bU9mQXR0ZW1wdHM6IDEwLFxuICAgICAgc3RhcnRpbmdEZWxheTogNTAwLFxuICAgIH1cbiAgKVxuICBpZiAoIXNlY3JldC5TZWNyZXRTdHJpbmcpIHRocm93IFwiTm8gc2VjcmV0IHN0cmluZ1wiXG4gIGNvbnN0IHNlY3JldFZhbHVlcyA9IEpTT04ucGFyc2Uoc2VjcmV0LlNlY3JldFN0cmluZylcblxuICBsZXQgc3FsOiBzdHJpbmcgfCBzdHJpbmdbXSB8IHZvaWRcbiAgc3dpdGNoIChyZXF1ZXN0VHlwZSkge1xuICAgIGNhc2UgXCJDcmVhdGVcIjoge1xuICAgICAgc3FsID0gYXdhaXQganVtcFRhYmxlW3Jlc291cmNlXVtyZXF1ZXN0VHlwZV0ocmVzb3VyY2VJZCwgZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzKVxuICAgICAgYnJlYWtcbiAgICB9XG4gICAgY2FzZSBcIlVwZGF0ZVwiOiB7XG4gICAgICBjb25zdCBvbGRSZXNvdXJjZUlkID0gKGV2ZW50IGFzIENsb3VkRm9ybWF0aW9uQ3VzdG9tUmVzb3VyY2VVcGRhdGVFdmVudClcbiAgICAgICAgLlBoeXNpY2FsUmVzb3VyY2VJZFxuICAgICAgc3FsID0gYXdhaXQganVtcFRhYmxlW3Jlc291cmNlXVtyZXF1ZXN0VHlwZV0ocmVzb3VyY2VJZCwgb2xkUmVzb3VyY2VJZCwge1xuICAgICAgICAuLi5ldmVudC5SZXNvdXJjZVByb3BlcnRpZXMsXG4gICAgICAgIE1hc3Rlck93bmVyOiBzZWNyZXRWYWx1ZXMudXNlcm5hbWUsXG4gICAgICB9KVxuICAgICAgYnJlYWtcbiAgICB9XG4gICAgY2FzZSBcIkRlbGV0ZVwiOiB7XG4gICAgICBpZiAocmVzb3VyY2UgPT09IFJkc1NxbFJlc291cmNlLkRBVEFCQVNFKSB7XG4gICAgICAgIHNxbCA9IGp1bXBUYWJsZVtyZXNvdXJjZV1bcmVxdWVzdFR5cGVdKHJlc291cmNlSWQsIHNlY3JldFZhbHVlcy51c2VybmFtZSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNxbCA9IGp1bXBUYWJsZVtyZXNvdXJjZV1bcmVxdWVzdFR5cGVdKHJlc291cmNlSWQsIGV2ZW50LlJlc291cmNlUHJvcGVydGllcylcbiAgICAgIH1cbiAgICAgIGJyZWFrXG4gICAgfVxuICB9XG5cbiAgaWYgKHNxbCkge1xuICAgIGxldCBkYXRhYmFzZTogc3RyaW5nXG4gICAgaWYgKHJlc291cmNlID09PSBSZHNTcWxSZXNvdXJjZS5ST0xFKSB7XG4gICAgICBkYXRhYmFzZSA9IHNlY3JldFZhbHVlcy5kYm5hbWVcbiAgICB9IGVsc2Uge1xuICAgICAgZGF0YWJhc2UgPSBkYXRhYmFzZU5hbWUgPz8gc2VjcmV0VmFsdWVzLmRibmFtZSAvLyBjb25uZWN0IHRvIGdpdmVuIGRhdGFiYXNlIGlmIHBvc3NpYmxlLCBlbHNlIHRvIGRhdGFiYXNlIG1lbnRpb25lZCBpbiBzZWNyZXRcbiAgICB9XG4gICAgLy8gUGFyc2UgU1NMIG9wdGlvbiBmcm9tIGVudiwgaWYgcHJvdmlkZWQuXG4gICAgLy8gSWYgcHJvY2Vzcy5lbnYuU1NMIGlzIHRoZSBzdHJpbmcgXCJmYWxzZVwiLCBkaXNhYmxlIFNTTC5cbiAgICBjb25zdCBpc1NzbEVuYWJsZWQgPSBwcm9jZXNzLmVudi5TU0wgPyBKU09OLnBhcnNlKHByb2Nlc3MuZW52LlNTTCkgOiB0cnVlXG4gICAgY29uc3Qgc3NsOiBDb25uZWN0aW9uT3B0aW9ucyB8IGZhbHNlID0gaXNTc2xFbmFibGVkXG4gICAgICA/IHtcbiAgICAgICAgICBjYTogZnMucmVhZEZpbGVTeW5jKGAke3Byb2Nlc3MuZW52LkxBTUJEQV9UQVNLX1JPT1R9L2dsb2JhbC1idW5kbGUucGVtYCksXG4gICAgICAgICAgcmVqZWN0VW5hdXRob3JpemVkOiB0cnVlLCAvLyBUaGlzIGZvcmNlcyB0aGUgY2xpZW50IHRvIHZlcmlmeSB0aGUgc2VydmVyJ3MgY2VydGlmaWNhdGUuXG4gICAgICAgIH1cbiAgICAgIDogZmFsc2VcblxuICAgIGNvbnN0IHBhcmFtczogQ2xpZW50Q29uZmlnID0ge1xuICAgICAgaG9zdDogc2VjcmV0VmFsdWVzLmhvc3QsXG4gICAgICBwb3J0OiBzZWNyZXRWYWx1ZXMucG9ydCxcbiAgICAgIHVzZXI6IHNlY3JldFZhbHVlcy51c2VybmFtZSxcbiAgICAgIHBhc3N3b3JkOiBzZWNyZXRWYWx1ZXMucGFzc3dvcmQsXG4gICAgICBkYXRhYmFzZTogZGF0YWJhc2UsXG4gICAgICBjb25uZWN0aW9uVGltZW91dE1pbGxpczogMzAwMDAsIC8vIHJldHVybiBhbiBlcnJvciBpZiBhIGNvbm5lY3Rpb24gY291bGQgbm90IGJlIGVzdGFibGlzaGVkIHdpdGhpbiAzMCBzZWNvbmRzXG4gICAgICBzc2wsXG4gICAgfVxuICAgIGxvZyhcbiAgICAgIGBDb25uZWN0aW5nIHRvIGhvc3QgJHtwYXJhbXMuaG9zdH06JHtwYXJhbXMucG9ydH0ke1xuICAgICAgICBzc2wgPyBcIiB1c2luZyBhIHNlY3VyZSBjb25uZWN0aW9uXCIgOiBcIlwiXG4gICAgICB9LCBkYXRhYmFzZSAke3BhcmFtcy5kYXRhYmFzZX0gYXMgJHtwYXJhbXMudXNlcn1gXG4gICAgKVxuICAgIGxvZyhcIkV4ZWN1dGluZyBTUUxcIiwgc3FsKVxuICAgIGNvbnN0IHBnX2NsaWVudCA9IG5ldyBDbGllbnQocGFyYW1zKVxuICAgIGF3YWl0IHBnX2NsaWVudC5jb25uZWN0KClcbiAgICB0cnkge1xuICAgICAgYXdhaXQgYmFja09mZihcbiAgICAgICAgYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIGlmICh0eXBlb2Ygc3FsID09PSBcInN0cmluZ1wiKSB7XG4gICAgICAgICAgICByZXR1cm4gcGdfY2xpZW50LnF1ZXJ5KHNxbClcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKHNxbCkge1xuICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5hbGwoXG4gICAgICAgICAgICAgICAgc3FsLm1hcCgoc3RhdGVtZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgICByZXR1cm4gcGdfY2xpZW50LnF1ZXJ5KHN0YXRlbWVudClcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICByZXRyeTogZXJyb3JGaWx0ZXIsXG4gICAgICAgICAgbnVtT2ZBdHRlbXB0czogbWF4QXR0ZW1wdHMsXG4gICAgICAgIH1cbiAgICAgIClcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgcGdfY2xpZW50LmVuZCgpXG4gICAgfVxuICB9XG5cbiAgbGV0IHJlc3BvbnNlOiBDdXN0b21SZXNvdXJjZVJlc3BvbnNlID0ge31cbiAgLy8gRXhjZXB0IGZvciB0aGUgU1FMIHJlc291cmNlLCByZXR1cm4gdGhlIG5ldyByZXNvdXJjZSBpZC4gVGhpc1xuICAvLyB3aWxsIGNhdXNlIGEgZGVsZXRlIHRvIGJlIHNlbmQgZm9yIHRoZSBvbGQgcmVzb3VyY2UuXG4gIGlmIChyZXNvdXJjZSAhPT0gUmRzU3FsUmVzb3VyY2UuU1FMKSB7XG4gICAgcmVzcG9uc2UuUGh5c2ljYWxSZXNvdXJjZUlkID0gcmVzb3VyY2VJZFxuICB9XG5cbiAgcmV0dXJuIHJlc3BvbnNlXG59XG5cbi8vIEN1c3RvbSBlcnJvciBmaWx0ZXIsIG1haW5seSB0byByZXRyeSByb2xlIGNyZWF0aW9uLlxuLy8gRnJlcXVlbnRseSBzZWUgXCJ0dXBsZSBjb25jdXJyZW50bHkgdXBkYXRlZFwiLCBhbmQgYWRkaW5nXG4vLyBkZXBlbmRlbmNpZXMgaXMgdmVyeSBoYXJkIHRvIG1ha2Ugd29yay5cbmNvbnN0IGVycm9yRmlsdGVyID0gKGVycm9yOiBhbnksIG5leHRBdHRlbXB0TnVtYmVyOiBudW1iZXIpID0+IHtcbiAgLy8gUmV0cnkgb25seSBpZiB0aGUgZXJyb3IgbWVzc2FnZSBjb250YWlucyBcInR1cGxlIGNvbmN1cnJlbnRseVwiXG4gIC8vIFRoaXMgd2lsbCBjb3ZlciBjb25jdXJyZW50IHVwZGF0ZXMgYW5kIGRlbGV0ZXNcbiAgY29uc3Qgd2lsbFJldHJ5ID0gZXJyb3IubWVzc2FnZS5pbmNsdWRlcyhcInR1cGxlIGNvbmN1cnJlbnRseVwiKVxuICBsb2coXG4gICAgXCJFbmNvdW50ZXJlZCBhbiBlcnJvciBvbiBhdHRlbXB0ICVkLyVkIHJldHJ5PSVzIGVycm9yPVslb11cIixcbiAgICBuZXh0QXR0ZW1wdE51bWJlciAtIDEsXG4gICAgbWF4QXR0ZW1wdHMsXG4gICAgd2lsbFJldHJ5LFxuICAgIGVycm9yXG4gIClcbiAgcmV0dXJuIHdpbGxSZXRyeVxufVxuXG4vKipcbiAqIFBhcnNlIHBhc3N3b3JkIGZpZWxkIGZyb20gc2VjcmV0LiBSZXR1cm5zIHZvaWQgb24gZXJyb3IuXG4gKi9cbmNvbnN0IGdldFBhc3N3b3JkID0gYXN5bmMgKGFybjogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmcgfCB2b2lkPiA9PiB7XG4gIGNvbnN0IHNlY3JldHNfY2xpZW50ID0gbmV3IFNlY3JldHNNYW5hZ2VyQ2xpZW50KHt9KVxuICBjb25zdCBjb21tYW5kID0gbmV3IEdldFNlY3JldFZhbHVlQ29tbWFuZCh7XG4gICAgU2VjcmV0SWQ6IGFybixcbiAgfSlcbiAgY29uc3Qgc2VjcmV0ID0gYXdhaXQgc2VjcmV0c19jbGllbnQuc2VuZChjb21tYW5kKVxuICBpZiAoc2VjcmV0LlNlY3JldFN0cmluZykge1xuICAgIGNvbnN0IGpzb24gPSBKU09OLnBhcnNlKHNlY3JldC5TZWNyZXRTdHJpbmcpXG4gICAgcmV0dXJuIGpzb24ucGFzc3dvcmRcbiAgfVxufVxuIl19