@cdklabs/cdk-amazonmq
Version:
<!--BEGIN STABILITY BANNER-->
449 lines (440 loc) • 14.5 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/rabbitmq/custom-resource/handler/index.ts
var index_exports = {};
__export(index_exports, {
handler: () => handler
});
module.exports = __toCommonJS(index_exports);
// src/rabbitmq/custom-resource/handler/dynamic-references/secret.ts
var import_util_arn_parser = require("@aws-sdk/util-arn-parser");
var SecretsDynamicRefereceParser = class _SecretsDynamicRefereceParser {
static get regex() {
return /^\{\{(resolve:secretsmanager[a-zA-Z0-9:_-]*?)\}\}$/;
}
static looksLikeSecretDynamicReference(text) {
return _SecretsDynamicRefereceParser.regex.test(text);
}
static parse(text) {
if (!_SecretsDynamicRefereceParser.looksLikeSecretDynamicReference(text)) {
return void 0;
}
const matches = text.match(_SecretsDynamicRefereceParser.regex);
if (!matches) {
throw new Error(`Invalid secret dynamic reference: ${text}`);
}
const referenceContents = matches[matches.length === 2 ? 1 : 2];
const arnBased = referenceContents.startsWith(
"resolve:secretsmanager:arn:"
);
const parts = referenceContents.split(":");
const valueAtOrDefault = (arr, idx, defaultValue) => arr.length > idx && arr[idx] !== "" ? arr[idx] : defaultValue;
if (!arnBased) {
return {
secretId: parts[2],
secretString: valueAtOrDefault(parts, 3),
jsonKey: valueAtOrDefault(parts, 4),
versionStage: valueAtOrDefault(parts, 5),
versionId: valueAtOrDefault(parts, 6)
};
}
return {
secretId: (0, import_util_arn_parser.build)({
partition: parts[3],
service: parts[4],
region: parts[5],
accountId: parts[6],
resource: `${parts[7]}:${parts[8]}`
}),
secretString: valueAtOrDefault(parts, 9),
jsonKey: valueAtOrDefault(parts, 10),
versionStage: valueAtOrDefault(parts, 11),
versionId: valueAtOrDefault(parts, 12)
};
}
};
// src/rabbitmq/custom-resource/handler/dynamic-references/string-parameter.ts
var StringParameterDynamicRefereceParser = class _StringParameterDynamicRefereceParser {
static get regex() {
return /^\{\{(resolve:ssm[a-zA-Z0-9:_-]*?)\}\}$/;
}
static looksLikeParameterDynamicReference(text) {
return _StringParameterDynamicRefereceParser.regex.test(text);
}
static parse(text) {
if (!_StringParameterDynamicRefereceParser.looksLikeParameterDynamicReference(
text
)) {
return void 0;
}
const matches = text.match(_StringParameterDynamicRefereceParser.regex);
if (!matches) {
throw new Error(`Invalid secret dynamic reference: ${text}`);
}
const referenceContents = matches[matches.length === 2 ? 1 : 2];
const parts = referenceContents.split(":");
if (parts.length < 3) {
throw new Error(`Invalid secret dynamic reference: ${text}`);
}
return {
isSecure: parts[1] === "ssm-secure",
parameterName: parts[2],
version: parts.length === 4 ? parseInt(parts[3]) : void 0
};
}
};
// src/rabbitmq/custom-resource/handler/dynamic-references/dynamic-reference-extractor.ts
var DynamicReferenceExtractor = class _DynamicReferenceExtractor {
static get regex() {
return /(\{\{resolve:[a-zA-Z0-9:_-]*?\}\})/gm;
}
static extract(text) {
let secrets;
let parameters;
const matches = text.matchAll(_DynamicReferenceExtractor.regex);
if (!matches) {
return [secrets, parameters];
}
const matchesArray = [...matches];
const secretsArray = matchesArray.filter(
(m) => SecretsDynamicRefereceParser.looksLikeSecretDynamicReference(m[0])
).map((m) => ({
reference: m[0],
components: SecretsDynamicRefereceParser.parse(m[0])
})).filter((s) => s.reference !== void 0);
if (secretsArray.length > 0) {
secrets = secretsArray;
}
const paramsArray = matchesArray.filter(
(m) => StringParameterDynamicRefereceParser.looksLikeParameterDynamicReference(
m[0]
)
).map((m) => ({
reference: m[0],
components: StringParameterDynamicRefereceParser.parse(m[0])
})).filter((s) => s.reference !== void 0);
if (paramsArray.length > 0) {
parameters = paramsArray;
}
return [secrets, parameters];
}
};
// src/rabbitmq/custom-resource/handler/dynamic-references/index.ts
var import_client_secrets_manager = require("@aws-sdk/client-secrets-manager");
var import_client_ssm = require("@aws-sdk/client-ssm");
var smClient = new import_client_secrets_manager.SecretsManagerClient({});
var ssmClient = new import_client_ssm.SSMClient({});
async function replaceDynamicReferences(text) {
if (!text) {
return text;
}
let resultText = text;
const [secrets, parameters] = DynamicReferenceExtractor.extract(text);
resultText = await processSecrets(secrets, resultText);
resultText = await processParameters(parameters, resultText);
return resultText;
}
async function processParameters(parameters, resultText) {
if (parameters) {
for (const { reference, components } of parameters) {
const { Parameter } = await ssmClient.send(
new import_client_ssm.GetParameterCommand({
Name: components.version ? `${components.parameterName}:${components.version}` : components.parameterName,
WithDecryption: components.isSecure
})
);
if (Parameter && Parameter.Value) {
if (components.parameterName) {
resultText = resultText.replace(
reference,
encodeURIComponent(Parameter.Value)
);
}
}
}
}
return resultText;
}
async function processSecrets(secrets, resultText) {
if (secrets) {
for (const { reference, components } of secrets) {
const { SecretString } = await smClient.send(
new import_client_secrets_manager.GetSecretValueCommand({
SecretId: components.secretId,
VersionId: components.versionId,
VersionStage: components.versionStage
})
);
if (!SecretString) {
continue;
}
if (components.jsonKey) {
resultText = resultText.replace(
reference,
encodeURIComponent(JSON.parse(SecretString)[components.jsonKey])
);
} else {
resultText = resultText.replace(
reference,
encodeURIComponent(SecretString)
);
}
}
}
return resultText;
}
// src/rabbitmq/custom-resource/handler/rabbitmq-management-api.ts
var https = __toESM(require("https"));
var import_client_secrets_manager2 = require("@aws-sdk/client-secrets-manager");
var smClient2 = new import_client_secrets_manager2.SecretsManagerClient({});
var request2 = async (options) => {
validateOptions(options);
const { credentials } = options;
if (credentials === void 0) {
throw new Error("CREDENTIALS");
}
const { SecretString } = await smClient2.send(
new import_client_secrets_manager2.GetSecretValueCommand({
SecretId: credentials
})
);
if (SecretString === void 0) {
throw new Error("SecretString");
}
const { username, password } = JSON.parse(SecretString);
const url = new URL(options.url);
const input = {
rabbitUrl: url,
username,
password,
path: options.path,
method: options.method ?? "GET",
payload: options.payload
};
const { payload } = await httpsRequest(input);
return payload;
};
function validateOptions(options) {
if (!options.path.startsWith("/api")) {
throw new Error(
`There is no RabbitMQ Management HTTP API call that does not start with '/api'. Received ${options.url}`
);
}
}
var httpsRequest = (input) => {
const { rabbitUrl, path, method, username, password, payload } = input;
const options = {
hostname: rabbitUrl.hostname,
port: 443,
path,
method,
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${Buffer.from(`${username}:${password}`).toString(
"base64"
)}`
}
};
return new Promise((resolve, reject) => {
const req = https.request({ ...options }, (res) => {
res.setEncoding("utf8");
let rawData = "";
res.on("data", (chunk) => {
rawData += chunk;
});
res.on("end", () => {
try {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
resolve({
headers: res.headers,
statusCode: res.statusCode,
payload: rawData !== "" ? JSON.parse(rawData) : void 0
});
} else {
reject(
new Error(
`Request failed with status code ${res.statusCode}: ${rawData}`
)
);
}
} catch (e) {
reject(e);
}
});
});
req.on("error", (e) => {
reject(e);
});
if (payload !== void 0) {
req.write(JSON.stringify(payload));
}
req.end();
});
};
// src/rabbitmq/custom-resource/handler/utils.ts
var https2 = __toESM(require("https"));
function decodeCall(call) {
if (!call) {
return void 0;
}
return JSON.parse(call);
}
function respond(event, responseStatus, reason, physicalResourceId, data) {
const responseBody = JSON.stringify({
Status: responseStatus,
Reason: reason,
PhysicalResourceId: physicalResourceId,
StackId: event.StackId,
RequestId: event.RequestId,
LogicalResourceId: event.LogicalResourceId,
NoEcho: false,
Data: data
});
const parsedUrl = new URL(event.ResponseURL);
const requestOptions = {
hostname: parsedUrl.hostname,
path: `${parsedUrl.pathname}${parsedUrl.search}`,
port: parsedUrl.port || 443,
method: "PUT",
headers: {
"content-type": "",
"content-length": Buffer.byteLength(responseBody, "utf8")
}
};
return new Promise((resolve, reject) => {
try {
const request4 = https2.request(requestOptions, (response) => {
response.setEncoding("utf8");
const responseData = [];
response.on("data", (chunk) => responseData.push(chunk));
response.on("end", () => {
resolve(responseData.join(""));
});
response.on("error", (e) => {
reject(e);
});
});
request4.on("error", (e) => {
reject(e);
});
request4.write(responseBody);
request4.end();
} catch (e) {
reject(e);
}
});
}
function filterKeys(object, pred) {
return Object.entries(object).reduce(
(acc, [k, v]) => pred(k) ? { ...acc, [k]: v } : acc,
{}
);
}
function startsWithOneOf(searchStrings) {
return function(string) {
for (const searchString of searchStrings) {
if (string.startsWith(searchString)) {
return true;
}
}
return false;
};
}
function flattenObj(ob) {
let result = {};
for (const i in ob) {
if (typeof ob[i] === "object") {
const temp = flattenObj(ob[i]);
for (const j in temp) {
result[i + "." + j] = temp[j];
}
} else {
result[i] = ob[i];
}
}
return result;
}
// src/rabbitmq/custom-resource/handler/index.ts
async function handler(event, context) {
try {
event.ResourceProperties[event.RequestType] = await replaceDynamicReferences(
event.ResourceProperties[event.RequestType]
);
event.ResourceProperties.Create = decodeCall(
event.ResourceProperties.Create
);
event.ResourceProperties.Update = decodeCall(
event.ResourceProperties.Update
);
event.ResourceProperties.Delete = decodeCall(
event.ResourceProperties.Delete
);
let physicalResourceId;
switch (event.RequestType) {
case "Create":
physicalResourceId = event.ResourceProperties.Create?.physicalResourceId?.id ?? event.ResourceProperties.Update?.physicalResourceId?.id ?? event.ResourceProperties.Delete?.physicalResourceId?.id ?? event.LogicalResourceId;
break;
case "Update":
case "Delete":
physicalResourceId = event.ResourceProperties[event.RequestType]?.physicalResourceId?.id ?? event.PhysicalResourceId;
break;
}
let data = void 0;
const apiCallProps = event.ResourceProperties[event.RequestType];
if (apiCallProps !== void 0) {
const { Url: url, Credentials: credentials } = event.ResourceProperties;
const options = {
url,
credentials,
...apiCallProps
};
const response = await request2(options);
if (response) {
const flatData = flattenObj(response);
data = options.outputPaths ? filterKeys(flatData, startsWithOneOf(options.outputPaths)) : flatData;
if (apiCallProps.physicalResourceId?.responsePath) {
physicalResourceId = flatData[apiCallProps.physicalResourceId.responsePath];
}
}
}
await respond(event, "SUCCESS", "OK", physicalResourceId, data);
} catch (e) {
await respond(
event,
"FAILED",
e.message || "Internal Error",
context.logStreamName,
{}
);
}
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
handler
});