rds-database-auto-start-preventer 0.2.6 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +6 -6
- package/README.md +3 -3
- package/assets/funcs/auto-start-prevent.lambda/index.js +720 -219
- package/assets/funcs/auto-start-prevent.lambda/index.js.map +4 -4
- package/lib/constructs/rds-database-auto-start-preventer.d.ts +2 -1
- package/lib/constructs/rds-database-auto-start-preventer.js +5 -3
- package/lib/funcs/auto-start-prevent.lambda.d.ts +7 -4
- package/lib/funcs/auto-start-prevent.lambda.js +45 -169
- package/lib/funcs/auto-start-prevent.predicates.d.ts +251 -0
- package/lib/funcs/auto-start-prevent.predicates.js +253 -0
- package/lib/stacks/rds-database-auto-start-prevent-stack.js +1 -1
- package/package.json +7 -7
|
@@ -20566,7 +20566,7 @@ var require_lib = __commonJS({
|
|
|
20566
20566
|
"node_modules/fetch-retrier/lib/index.js"(exports2) {
|
|
20567
20567
|
"use strict";
|
|
20568
20568
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20569
|
-
exports2.fetchRetrier = exports2.FetchRetrierUnreachableError = exports2.FetchRetrierNetworkError = exports2.FetchRetrierHttpError = exports2.FetchRetrierAlreadyAbortedError = exports2.FetchRetrierAbortError = void 0;
|
|
20569
|
+
exports2.parseRetryAfterMs = exports2.fetchRetrier = exports2.defaultShouldRetry = exports2.DEFAULT_RETRYABLE_HTTP_STATUSES = exports2.FetchRetrierUnreachableError = exports2.FetchRetrierInvalidOptionsError = exports2.FetchRetrierNetworkError = exports2.FetchRetrierHttpError = exports2.FetchRetrierAlreadyAbortedError = exports2.FetchRetrierAbortError = void 0;
|
|
20570
20570
|
var FetchRetrierAbortError = class _FetchRetrierAbortError extends Error {
|
|
20571
20571
|
/**
|
|
20572
20572
|
* @param message - Human-readable reason (default: `'Aborted'`)
|
|
@@ -20593,10 +20593,12 @@ var require_lib = __commonJS({
|
|
|
20593
20593
|
/**
|
|
20594
20594
|
* @param message - Error description
|
|
20595
20595
|
* @param status - HTTP status code from the last non-OK response
|
|
20596
|
+
* @param body - Response body text already read via `response.text()` for that attempt
|
|
20596
20597
|
*/
|
|
20597
|
-
constructor(message, status) {
|
|
20598
|
+
constructor(message, status, body) {
|
|
20598
20599
|
super(message);
|
|
20599
20600
|
this.status = status;
|
|
20601
|
+
this.body = body;
|
|
20600
20602
|
this.name = "FetchRetrierHttpError";
|
|
20601
20603
|
Object.setPrototypeOf(this, _FetchRetrierHttpError.prototype);
|
|
20602
20604
|
}
|
|
@@ -20615,6 +20617,17 @@ var require_lib = __commonJS({
|
|
|
20615
20617
|
}
|
|
20616
20618
|
};
|
|
20617
20619
|
exports2.FetchRetrierNetworkError = FetchRetrierNetworkError;
|
|
20620
|
+
var FetchRetrierInvalidOptionsError = class _FetchRetrierInvalidOptionsError extends TypeError {
|
|
20621
|
+
/**
|
|
20622
|
+
* @param message - Human-readable reason describing the invalid option
|
|
20623
|
+
*/
|
|
20624
|
+
constructor(message) {
|
|
20625
|
+
super(message);
|
|
20626
|
+
this.name = "FetchRetrierInvalidOptionsError";
|
|
20627
|
+
Object.setPrototypeOf(this, _FetchRetrierInvalidOptionsError.prototype);
|
|
20628
|
+
}
|
|
20629
|
+
};
|
|
20630
|
+
exports2.FetchRetrierInvalidOptionsError = FetchRetrierInvalidOptionsError;
|
|
20618
20631
|
var FetchRetrierUnreachableError = class _FetchRetrierUnreachableError extends Error {
|
|
20619
20632
|
/**
|
|
20620
20633
|
* @param message - Human-readable reason (default: `'Unreachable'`)
|
|
@@ -20626,11 +20639,26 @@ var require_lib = __commonJS({
|
|
|
20626
20639
|
}
|
|
20627
20640
|
};
|
|
20628
20641
|
exports2.FetchRetrierUnreachableError = FetchRetrierUnreachableError;
|
|
20629
|
-
|
|
20630
|
-
|
|
20642
|
+
exports2.DEFAULT_RETRYABLE_HTTP_STATUSES = [408, 425, 429, 500, 502, 503, 504];
|
|
20643
|
+
var defaultShouldRetry = (response, _body) => {
|
|
20644
|
+
return exports2.DEFAULT_RETRYABLE_HTTP_STATUSES.includes(response.status);
|
|
20645
|
+
};
|
|
20646
|
+
exports2.defaultShouldRetry = defaultShouldRetry;
|
|
20647
|
+
var validateRequestOptions = (options) => {
|
|
20648
|
+
const { retries, timeoutMs, baseBackoffMs } = options;
|
|
20649
|
+
if (retries < 1) {
|
|
20650
|
+
throw new FetchRetrierInvalidOptionsError("retries must be >= 1");
|
|
20651
|
+
}
|
|
20652
|
+
if (timeoutMs <= 0) {
|
|
20653
|
+
throw new FetchRetrierInvalidOptionsError("timeoutMs must be > 0");
|
|
20654
|
+
}
|
|
20655
|
+
if (baseBackoffMs < 0) {
|
|
20656
|
+
throw new FetchRetrierInvalidOptionsError("baseBackoffMs must be >= 0");
|
|
20657
|
+
}
|
|
20631
20658
|
};
|
|
20632
20659
|
var fetchRetrier = async (url, options) => {
|
|
20633
|
-
const { headers, init, retries, timeoutMs, baseBackoffMs, signal: externalSignal, shouldRetry = defaultShouldRetry } = options;
|
|
20660
|
+
const { headers, init, retries, timeoutMs, baseBackoffMs, signal: externalSignal, shouldRetry = exports2.defaultShouldRetry } = options;
|
|
20661
|
+
validateRequestOptions({ retries, timeoutMs, baseBackoffMs });
|
|
20634
20662
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
20635
20663
|
if (externalSignal?.aborted) {
|
|
20636
20664
|
throw new FetchRetrierAlreadyAbortedError();
|
|
@@ -20659,11 +20687,11 @@ var require_lib = __commonJS({
|
|
|
20659
20687
|
const isContinue = shouldRetry(res, text);
|
|
20660
20688
|
if (isContinue) {
|
|
20661
20689
|
if (attempt === retries) {
|
|
20662
|
-
throw new FetchRetrierHttpError(`HTTP ${res.status}`, res.status);
|
|
20690
|
+
throw new FetchRetrierHttpError(`HTTP ${res.status}`, res.status, text);
|
|
20663
20691
|
}
|
|
20664
|
-
await wait(
|
|
20692
|
+
await wait(resolveRetryDelayMs(res, baseBackoffMs, attempt));
|
|
20665
20693
|
} else {
|
|
20666
|
-
throw new FetchRetrierHttpError(`Non-retriable HTTP error: ${res.status}`, res.status);
|
|
20694
|
+
throw new FetchRetrierHttpError(`Non-retriable HTTP error: ${res.status}`, res.status, text);
|
|
20667
20695
|
}
|
|
20668
20696
|
} catch (err) {
|
|
20669
20697
|
clearTimeout(timer);
|
|
@@ -20693,32 +20721,251 @@ var require_lib = __commonJS({
|
|
|
20693
20721
|
const cap = base * Math.pow(2, attempt);
|
|
20694
20722
|
return Math.floor(Math.random() * cap);
|
|
20695
20723
|
};
|
|
20724
|
+
var parseRetryAfterMs = (value, nowMs = Date.now()) => {
|
|
20725
|
+
const trimmed = value.trim();
|
|
20726
|
+
if (trimmed === "") {
|
|
20727
|
+
return void 0;
|
|
20728
|
+
}
|
|
20729
|
+
if (/^\d+$/.test(trimmed)) {
|
|
20730
|
+
return Number.parseInt(trimmed, 10) * 1e3;
|
|
20731
|
+
}
|
|
20732
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
20733
|
+
return void 0;
|
|
20734
|
+
}
|
|
20735
|
+
const dateMs = Date.parse(trimmed);
|
|
20736
|
+
if (Number.isNaN(dateMs)) {
|
|
20737
|
+
return void 0;
|
|
20738
|
+
}
|
|
20739
|
+
return Math.max(0, dateMs - nowMs);
|
|
20740
|
+
};
|
|
20741
|
+
exports2.parseRetryAfterMs = parseRetryAfterMs;
|
|
20742
|
+
var resolveRetryDelayMs = (response, baseBackoffMs, attempt, nowMs = Date.now()) => {
|
|
20743
|
+
const header = response.headers?.get("Retry-After");
|
|
20744
|
+
if (header == null) {
|
|
20745
|
+
return fullJitter(baseBackoffMs, attempt);
|
|
20746
|
+
}
|
|
20747
|
+
const fromHeader = (0, exports2.parseRetryAfterMs)(header, nowMs);
|
|
20748
|
+
if (fromHeader === void 0) {
|
|
20749
|
+
return fullJitter(baseBackoffMs, attempt);
|
|
20750
|
+
}
|
|
20751
|
+
return fromHeader;
|
|
20752
|
+
};
|
|
20696
20753
|
}
|
|
20697
20754
|
});
|
|
20698
20755
|
|
|
20699
|
-
// node_modules/
|
|
20756
|
+
// node_modules/quiet-json-parser/lib/index.js
|
|
20700
20757
|
var require_lib2 = __commonJS({
|
|
20758
|
+
"node_modules/quiet-json-parser/lib/index.js"(exports2) {
|
|
20759
|
+
"use strict";
|
|
20760
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20761
|
+
exports2.quietParse = void 0;
|
|
20762
|
+
var safeReviver = (key, value) => {
|
|
20763
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
20764
|
+
return void 0;
|
|
20765
|
+
}
|
|
20766
|
+
return value;
|
|
20767
|
+
};
|
|
20768
|
+
var quietParse = (jsonString, fallback, onError) => {
|
|
20769
|
+
if (!jsonString) {
|
|
20770
|
+
return fallback;
|
|
20771
|
+
}
|
|
20772
|
+
try {
|
|
20773
|
+
return JSON.parse(jsonString, safeReviver);
|
|
20774
|
+
} catch (error) {
|
|
20775
|
+
onError?.(error);
|
|
20776
|
+
return fallback;
|
|
20777
|
+
}
|
|
20778
|
+
};
|
|
20779
|
+
exports2.quietParse = quietParse;
|
|
20780
|
+
}
|
|
20781
|
+
});
|
|
20782
|
+
|
|
20783
|
+
// node_modules/aws-lambda-secret-fetcher/node_modules/strict-env-resolver/lib/index.js
|
|
20784
|
+
var require_lib3 = __commonJS({
|
|
20785
|
+
"node_modules/aws-lambda-secret-fetcher/node_modules/strict-env-resolver/lib/index.js"(exports2) {
|
|
20786
|
+
"use strict";
|
|
20787
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20788
|
+
exports2.StrictEnvResolver = exports2.StrictEnvType = exports2.StrictEnvValidationError = exports2.StrictEnvError = void 0;
|
|
20789
|
+
var TRUE_BOOLEAN_PATTERN = /^(1|true|yes|on)$/i;
|
|
20790
|
+
var parseNumber = (raw) => {
|
|
20791
|
+
const n = Number(raw);
|
|
20792
|
+
if (!Number.isFinite(n)) {
|
|
20793
|
+
return void 0;
|
|
20794
|
+
}
|
|
20795
|
+
return n;
|
|
20796
|
+
};
|
|
20797
|
+
var parseBooleanEnvValue = (normalizedRaw) => TRUE_BOOLEAN_PATTERN.test(normalizedRaw);
|
|
20798
|
+
var StrictEnvError = class extends Error {
|
|
20799
|
+
/**
|
|
20800
|
+
* @param message - Error message.
|
|
20801
|
+
*/
|
|
20802
|
+
constructor(message) {
|
|
20803
|
+
super(message);
|
|
20804
|
+
this.name = "StrictEnvError";
|
|
20805
|
+
}
|
|
20806
|
+
};
|
|
20807
|
+
exports2.StrictEnvError = StrictEnvError;
|
|
20808
|
+
var StrictEnvValidationError = class _StrictEnvValidationError extends StrictEnvError {
|
|
20809
|
+
/**
|
|
20810
|
+
* Formats validation errors into a human-readable error message.
|
|
20811
|
+
*
|
|
20812
|
+
* @template K - Environment variable key type.
|
|
20813
|
+
* @param errors - Validation error entries to format.
|
|
20814
|
+
* @returns Multi-line summary listing each key and message.
|
|
20815
|
+
*/
|
|
20816
|
+
static format(errors) {
|
|
20817
|
+
const lines = errors.map((e) => `- ${e.key}: ${e.message}${e.raw == null ? "" : ` (raw="${e.raw}")`}`);
|
|
20818
|
+
return `Invalid environment variables (${errors.length}):
|
|
20819
|
+
${lines.join("\n")}`;
|
|
20820
|
+
}
|
|
20821
|
+
/**
|
|
20822
|
+
* Creates a new validation error from one or more `StrictEnvValidationEntry` entries.
|
|
20823
|
+
*
|
|
20824
|
+
* @param errors - One or more structured validation errors.
|
|
20825
|
+
*/
|
|
20826
|
+
constructor(errors) {
|
|
20827
|
+
const msg = _StrictEnvValidationError.format(errors);
|
|
20828
|
+
super(msg);
|
|
20829
|
+
this.name = "StrictEnvValidationError";
|
|
20830
|
+
this.errors = errors;
|
|
20831
|
+
this.keys = errors.map((e) => e.key);
|
|
20832
|
+
}
|
|
20833
|
+
};
|
|
20834
|
+
exports2.StrictEnvValidationError = StrictEnvValidationError;
|
|
20835
|
+
exports2.StrictEnvType = {
|
|
20836
|
+
/** Spec for a string value (returned as-is unless `trim: true` is set in options). */
|
|
20837
|
+
String: { type: "string" },
|
|
20838
|
+
/**
|
|
20839
|
+
* Spec for a finite numeric value (`Number()` parsing; rejects `NaN` and `Infinity`).
|
|
20840
|
+
*/
|
|
20841
|
+
Number: { type: "number" },
|
|
20842
|
+
/** Spec for a boolean value (`1`/`true`/`yes`/`on` → `true`; other non-empty values → `false`). */
|
|
20843
|
+
Boolean: { type: "boolean" },
|
|
20844
|
+
/**
|
|
20845
|
+
* Returns a spec that restricts the value to one of the given choices.
|
|
20846
|
+
*
|
|
20847
|
+
* @param choices - Allowed string literals.
|
|
20848
|
+
* @returns Enum spec for use with `resolve` or `resolveAll`.
|
|
20849
|
+
*/
|
|
20850
|
+
Enum: (choices) => ({ type: "enum", choices })
|
|
20851
|
+
};
|
|
20852
|
+
var defaultTrimForSpec = (spec) => spec.type !== "string";
|
|
20853
|
+
var normalizeEnvRaw = (raw, trim) => {
|
|
20854
|
+
if (raw == null) {
|
|
20855
|
+
return void 0;
|
|
20856
|
+
}
|
|
20857
|
+
if (!trim) {
|
|
20858
|
+
return raw;
|
|
20859
|
+
}
|
|
20860
|
+
return raw.trim();
|
|
20861
|
+
};
|
|
20862
|
+
var resolveSchemaEntry = (entry) => ({
|
|
20863
|
+
spec: Array.isArray(entry) ? entry[0] : entry,
|
|
20864
|
+
options: Array.isArray(entry) ? entry[1] : void 0
|
|
20865
|
+
});
|
|
20866
|
+
var parseEnvValue = (key, spec, raw, defaultValue, options) => {
|
|
20867
|
+
const trim = options?.trim ?? defaultTrimForSpec(spec);
|
|
20868
|
+
const normalizedRaw = normalizeEnvRaw(raw, trim);
|
|
20869
|
+
const hasDefault = defaultValue !== void 0;
|
|
20870
|
+
if (normalizedRaw == null || normalizedRaw === "") {
|
|
20871
|
+
if (hasDefault) {
|
|
20872
|
+
return { ok: true, value: defaultValue };
|
|
20873
|
+
}
|
|
20874
|
+
return {
|
|
20875
|
+
ok: false,
|
|
20876
|
+
error: { key, message: `Missing required environment variable: ${key}`, raw, kind: "missing" }
|
|
20877
|
+
};
|
|
20878
|
+
}
|
|
20879
|
+
switch (spec.type) {
|
|
20880
|
+
case "number": {
|
|
20881
|
+
const n = parseNumber(normalizedRaw);
|
|
20882
|
+
if (n === void 0) {
|
|
20883
|
+
return {
|
|
20884
|
+
ok: false,
|
|
20885
|
+
error: { key, message: `Env ${key}: expected number, got "${raw}"`, raw, kind: "invalid_number" }
|
|
20886
|
+
};
|
|
20887
|
+
}
|
|
20888
|
+
return { ok: true, value: n };
|
|
20889
|
+
}
|
|
20890
|
+
case "boolean":
|
|
20891
|
+
return { ok: true, value: parseBooleanEnvValue(normalizedRaw) };
|
|
20892
|
+
case "enum":
|
|
20893
|
+
if (!spec.choices.includes(normalizedRaw)) {
|
|
20894
|
+
return {
|
|
20895
|
+
ok: false,
|
|
20896
|
+
error: {
|
|
20897
|
+
key,
|
|
20898
|
+
message: `Env ${key}: must be one of [${spec.choices.join(", ")}]`,
|
|
20899
|
+
raw,
|
|
20900
|
+
kind: "invalid_enum"
|
|
20901
|
+
}
|
|
20902
|
+
};
|
|
20903
|
+
}
|
|
20904
|
+
return { ok: true, value: normalizedRaw };
|
|
20905
|
+
default:
|
|
20906
|
+
return { ok: true, value: normalizedRaw };
|
|
20907
|
+
}
|
|
20908
|
+
};
|
|
20909
|
+
var resolve = (key, spec = exports2.StrictEnvType.String, options) => {
|
|
20910
|
+
const result = parseEnvValue(key, spec, process.env[key], options?.default, options);
|
|
20911
|
+
if (!result.ok) {
|
|
20912
|
+
throw new StrictEnvValidationError([result.error]);
|
|
20913
|
+
}
|
|
20914
|
+
return result.value;
|
|
20915
|
+
};
|
|
20916
|
+
var resolveAll = (schema) => {
|
|
20917
|
+
const envs = {};
|
|
20918
|
+
const errors = [];
|
|
20919
|
+
for (const key of Object.keys(schema)) {
|
|
20920
|
+
const { spec, options } = resolveSchemaEntry(schema[key]);
|
|
20921
|
+
const result = parseEnvValue(key, spec, process.env[key], options?.default, options);
|
|
20922
|
+
if (!result.ok) {
|
|
20923
|
+
errors.push(result.error);
|
|
20924
|
+
continue;
|
|
20925
|
+
}
|
|
20926
|
+
envs[key] = result.value;
|
|
20927
|
+
}
|
|
20928
|
+
if (errors.length > 0)
|
|
20929
|
+
throw new StrictEnvValidationError(errors);
|
|
20930
|
+
return envs;
|
|
20931
|
+
};
|
|
20932
|
+
exports2.StrictEnvResolver = {
|
|
20933
|
+
/** Reads and parses a single environment variable. See {@link resolve}. */
|
|
20934
|
+
resolve,
|
|
20935
|
+
/** Reads and parses multiple environment variables in one pass. See {@link resolveAll}. */
|
|
20936
|
+
resolveAll
|
|
20937
|
+
};
|
|
20938
|
+
}
|
|
20939
|
+
});
|
|
20940
|
+
|
|
20941
|
+
// node_modules/aws-lambda-secret-fetcher/lib/index.js
|
|
20942
|
+
var require_lib4 = __commonJS({
|
|
20701
20943
|
"node_modules/aws-lambda-secret-fetcher/lib/index.js"(exports2) {
|
|
20702
20944
|
"use strict";
|
|
20703
20945
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20704
20946
|
exports2.secretFetcher = void 0;
|
|
20705
20947
|
var fetch_retrier_1 = require_lib();
|
|
20948
|
+
var quiet_json_parser_1 = require_lib2();
|
|
20949
|
+
var strict_env_resolver_1 = require_lib3();
|
|
20706
20950
|
var getSecretValue = async (name, options = {}) => {
|
|
20707
20951
|
const { extensionHttpPort, timeoutMs = 2e3, retries = 3, baseBackoffMs = 300 } = options;
|
|
20708
20952
|
const port = resolveExtensionHttpPort(extensionHttpPort);
|
|
20953
|
+
const sessionToken = resolveAwsSessionToken();
|
|
20709
20954
|
const url = `http://localhost:${port}/secretsmanager/get?secretId=${encodeURIComponent(name)}`;
|
|
20710
20955
|
const requestOptions = {
|
|
20711
20956
|
headers: {
|
|
20712
|
-
"X-Aws-Parameters-Secrets-Token":
|
|
20957
|
+
"X-Aws-Parameters-Secrets-Token": sessionToken
|
|
20713
20958
|
},
|
|
20714
20959
|
retries,
|
|
20715
20960
|
timeoutMs,
|
|
20716
20961
|
baseBackoffMs,
|
|
20717
20962
|
shouldRetry: (res, body) => {
|
|
20718
|
-
if (
|
|
20963
|
+
if ((0, fetch_retrier_1.defaultShouldRetry)(res, body)) {
|
|
20719
20964
|
return true;
|
|
20720
|
-
|
|
20965
|
+
}
|
|
20966
|
+
if (res.status === 400 && /not\s+ready.*traffic/i.test(body)) {
|
|
20721
20967
|
return true;
|
|
20968
|
+
}
|
|
20722
20969
|
return false;
|
|
20723
20970
|
}
|
|
20724
20971
|
};
|
|
@@ -20728,21 +20975,34 @@ var require_lib2 = __commonJS({
|
|
|
20728
20975
|
throw new Error("Invalid secret response format");
|
|
20729
20976
|
}
|
|
20730
20977
|
const data = raw;
|
|
20731
|
-
|
|
20732
|
-
|
|
20733
|
-
|
|
20978
|
+
return (0, quiet_json_parser_1.quietParse)(data.SecretString, data.SecretString);
|
|
20979
|
+
};
|
|
20980
|
+
var AWS_SESSION_TOKEN_GUIDANCE = "AWS_SESSION_TOKEN is not set. This library only works inside an AWS Lambda execution environment where the runtime provides AWS_SESSION_TOKEN for the Parameters and Secrets Extension. Attach the extension layer and run your code as a Lambda function handler.";
|
|
20981
|
+
var MIN_TCP_PORT = 1;
|
|
20982
|
+
var MAX_TCP_PORT = 65535;
|
|
20983
|
+
var resolveAwsSessionToken = () => {
|
|
20984
|
+
try {
|
|
20985
|
+
return strict_env_resolver_1.StrictEnvResolver.resolve("AWS_SESSION_TOKEN", strict_env_resolver_1.StrictEnvType.String, { trim: true });
|
|
20986
|
+
} catch (e) {
|
|
20987
|
+
if (e instanceof strict_env_resolver_1.StrictEnvValidationError && e.errors.length === 1 && e.errors[0]?.key === "AWS_SESSION_TOKEN" && e.errors[0]?.kind === "missing") {
|
|
20988
|
+
throw new Error(AWS_SESSION_TOKEN_GUIDANCE);
|
|
20989
|
+
}
|
|
20990
|
+
throw e;
|
|
20734
20991
|
}
|
|
20735
|
-
return secretString;
|
|
20736
20992
|
};
|
|
20737
20993
|
var resolveExtensionHttpPort = (overridePort) => {
|
|
20738
|
-
const
|
|
20739
|
-
|
|
20740
|
-
|
|
20994
|
+
const port = overridePort === void 0 ? strict_env_resolver_1.StrictEnvResolver.resolve("PARAMETERS_SECRETS_EXTENSION_HTTP_PORT", strict_env_resolver_1.StrictEnvType.Number, { default: 2773 }) : parseExtensionHttpPortOverride(overridePort);
|
|
20995
|
+
return assertValidTcpPort(port);
|
|
20996
|
+
};
|
|
20997
|
+
var parseExtensionHttpPortOverride = (overridePort) => {
|
|
20998
|
+
const candidate = String(overridePort).trim();
|
|
20741
20999
|
if (!/^\d+$/.test(candidate)) {
|
|
20742
21000
|
throw new Error("Invalid extension HTTP port: must be a number");
|
|
20743
21001
|
}
|
|
20744
|
-
|
|
20745
|
-
|
|
21002
|
+
return Number.parseInt(candidate, 10);
|
|
21003
|
+
};
|
|
21004
|
+
var assertValidTcpPort = (port) => {
|
|
21005
|
+
if (port < MIN_TCP_PORT || port > MAX_TCP_PORT) {
|
|
20746
21006
|
throw new Error("Invalid extension HTTP port: must be between 1 and 65535");
|
|
20747
21007
|
}
|
|
20748
21008
|
return String(port);
|
|
@@ -20753,19 +21013,217 @@ var require_lib2 = __commonJS({
|
|
|
20753
21013
|
const v = value;
|
|
20754
21014
|
return typeof v.SecretString === "string" && typeof v.Name === "string" && typeof v.ARN === "string" && (v.VersionId === void 0 || typeof v.VersionId === "string");
|
|
20755
21015
|
};
|
|
20756
|
-
var looksLikeJson = (str) => {
|
|
20757
|
-
return typeof str === "string" && str.trim().startsWith("{");
|
|
20758
|
-
};
|
|
20759
21016
|
exports2.secretFetcher = {
|
|
20760
21017
|
getSecretValue
|
|
20761
21018
|
};
|
|
20762
21019
|
}
|
|
20763
21020
|
});
|
|
20764
21021
|
|
|
21022
|
+
// node_modules/strict-env-resolver/lib/index.js
|
|
21023
|
+
var require_lib5 = __commonJS({
|
|
21024
|
+
"node_modules/strict-env-resolver/lib/index.js"(exports2) {
|
|
21025
|
+
"use strict";
|
|
21026
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
21027
|
+
exports2.StrictEnvResolver = exports2.StrictEnvType = exports2.StrictEnvValidationError = exports2.StrictEnvError = void 0;
|
|
21028
|
+
var TRUE_BOOLEAN_PATTERN = /^(1|true|yes|on)$/i;
|
|
21029
|
+
var parseNumber = (raw) => {
|
|
21030
|
+
const n = Number(raw);
|
|
21031
|
+
if (!Number.isFinite(n)) {
|
|
21032
|
+
return void 0;
|
|
21033
|
+
}
|
|
21034
|
+
return n;
|
|
21035
|
+
};
|
|
21036
|
+
var parseBooleanEnvValue = (normalizedRaw) => TRUE_BOOLEAN_PATTERN.test(normalizedRaw);
|
|
21037
|
+
var StrictEnvError = class extends Error {
|
|
21038
|
+
/**
|
|
21039
|
+
* @param message - Error message.
|
|
21040
|
+
*/
|
|
21041
|
+
constructor(message) {
|
|
21042
|
+
super(message);
|
|
21043
|
+
this.name = "StrictEnvError";
|
|
21044
|
+
}
|
|
21045
|
+
};
|
|
21046
|
+
exports2.StrictEnvError = StrictEnvError;
|
|
21047
|
+
var StrictEnvValidationError = class _StrictEnvValidationError extends StrictEnvError {
|
|
21048
|
+
/**
|
|
21049
|
+
* Formats validation errors into a human-readable error message.
|
|
21050
|
+
*
|
|
21051
|
+
* @template K - Environment variable key type.
|
|
21052
|
+
* @param errors - Validation error entries to format.
|
|
21053
|
+
* @returns Multi-line summary listing each key and message.
|
|
21054
|
+
*/
|
|
21055
|
+
static format(errors) {
|
|
21056
|
+
const lines = errors.map((e) => `- ${e.key}: ${e.message}${e.raw == null ? "" : ` (raw="${e.raw}")`}`);
|
|
21057
|
+
return `Invalid environment variables (${errors.length}):
|
|
21058
|
+
${lines.join("\n")}`;
|
|
21059
|
+
}
|
|
21060
|
+
/**
|
|
21061
|
+
* Creates a new validation error from one or more `StrictEnvValidationEntry` entries.
|
|
21062
|
+
*
|
|
21063
|
+
* @param errors - One or more structured validation errors.
|
|
21064
|
+
*/
|
|
21065
|
+
constructor(errors) {
|
|
21066
|
+
const msg = _StrictEnvValidationError.format(errors);
|
|
21067
|
+
super(msg);
|
|
21068
|
+
this.name = "StrictEnvValidationError";
|
|
21069
|
+
this.errors = errors;
|
|
21070
|
+
this.keys = errors.map((e) => e.key);
|
|
21071
|
+
}
|
|
21072
|
+
};
|
|
21073
|
+
exports2.StrictEnvValidationError = StrictEnvValidationError;
|
|
21074
|
+
var createNumberSpec = (constraints) => ({
|
|
21075
|
+
type: "number",
|
|
21076
|
+
...constraints?.min !== void 0 ? { min: constraints.min } : {},
|
|
21077
|
+
...constraints?.max !== void 0 ? { max: constraints.max } : {},
|
|
21078
|
+
...constraints?.integer !== void 0 ? { integer: constraints.integer } : {}
|
|
21079
|
+
});
|
|
21080
|
+
var NumberSpec = Object.assign((constraints) => createNumberSpec(constraints), { type: "number" });
|
|
21081
|
+
exports2.StrictEnvType = {
|
|
21082
|
+
/** Spec for a string value (returned as-is unless `trim: true` is set in options). */
|
|
21083
|
+
String: { type: "string" },
|
|
21084
|
+
/**
|
|
21085
|
+
* Spec for a finite numeric value (`Number()` parsing; rejects `NaN` and `Infinity`).
|
|
21086
|
+
* Use as `StrictEnvType.Number`, or call as `StrictEnvType.Number({ min, max, integer })`.
|
|
21087
|
+
*/
|
|
21088
|
+
Number: NumberSpec,
|
|
21089
|
+
/**
|
|
21090
|
+
* Spec for a positive integer (`>= 1`). Sugar for `Number({ min: 1, integer: true })`.
|
|
21091
|
+
*/
|
|
21092
|
+
PositiveInt: createNumberSpec({ min: 1, integer: true }),
|
|
21093
|
+
/**
|
|
21094
|
+
* Spec for a negative integer (`<= -1`). Sugar for `Number({ max: -1, integer: true })`.
|
|
21095
|
+
*/
|
|
21096
|
+
NegativeInt: createNumberSpec({ max: -1, integer: true }),
|
|
21097
|
+
/** Spec for a boolean value (`1`/`true`/`yes`/`on` → `true`; other non-empty values → `false`). */
|
|
21098
|
+
Boolean: { type: "boolean" },
|
|
21099
|
+
/**
|
|
21100
|
+
* Returns a spec that restricts the value to one of the given choices.
|
|
21101
|
+
*
|
|
21102
|
+
* @param choices - Allowed string literals.
|
|
21103
|
+
* @returns Enum spec for use with `resolve` or `resolveAll`.
|
|
21104
|
+
*/
|
|
21105
|
+
Enum: (choices) => ({ type: "enum", choices })
|
|
21106
|
+
};
|
|
21107
|
+
var defaultTrimForSpec = (spec) => spec.type !== "string";
|
|
21108
|
+
var normalizeEnvRaw = (raw, trim) => {
|
|
21109
|
+
if (raw == null) {
|
|
21110
|
+
return void 0;
|
|
21111
|
+
}
|
|
21112
|
+
if (!trim) {
|
|
21113
|
+
return raw;
|
|
21114
|
+
}
|
|
21115
|
+
return raw.trim();
|
|
21116
|
+
};
|
|
21117
|
+
var validateNumberConstraints = (key, raw, n, constraints) => {
|
|
21118
|
+
if (constraints.integer === true && !Number.isInteger(n)) {
|
|
21119
|
+
return { key, message: `Env ${key}: expected integer, got "${raw}"`, raw, kind: "invalid_number" };
|
|
21120
|
+
}
|
|
21121
|
+
if (constraints.min !== void 0 && n < constraints.min) {
|
|
21122
|
+
return {
|
|
21123
|
+
key,
|
|
21124
|
+
message: `Env ${key}: must be >= ${constraints.min}, got ${n}`,
|
|
21125
|
+
raw,
|
|
21126
|
+
kind: "invalid_number"
|
|
21127
|
+
};
|
|
21128
|
+
}
|
|
21129
|
+
if (constraints.max !== void 0 && n > constraints.max) {
|
|
21130
|
+
return {
|
|
21131
|
+
key,
|
|
21132
|
+
message: `Env ${key}: must be <= ${constraints.max}, got ${n}`,
|
|
21133
|
+
raw,
|
|
21134
|
+
kind: "invalid_number"
|
|
21135
|
+
};
|
|
21136
|
+
}
|
|
21137
|
+
return void 0;
|
|
21138
|
+
};
|
|
21139
|
+
var resolveSchemaEntry = (entry) => ({
|
|
21140
|
+
spec: Array.isArray(entry) ? entry[0] : entry,
|
|
21141
|
+
options: Array.isArray(entry) ? entry[1] : void 0
|
|
21142
|
+
});
|
|
21143
|
+
var parseEnvValue = (key, spec, raw, defaultValue, options) => {
|
|
21144
|
+
const trim = options?.trim ?? defaultTrimForSpec(spec);
|
|
21145
|
+
const normalizedRaw = normalizeEnvRaw(raw, trim);
|
|
21146
|
+
const hasDefault = defaultValue !== void 0;
|
|
21147
|
+
if (normalizedRaw == null || normalizedRaw === "") {
|
|
21148
|
+
if (hasDefault) {
|
|
21149
|
+
return { ok: true, value: defaultValue };
|
|
21150
|
+
}
|
|
21151
|
+
return {
|
|
21152
|
+
ok: false,
|
|
21153
|
+
error: { key, message: `Missing required environment variable: ${key}`, raw, kind: "missing" }
|
|
21154
|
+
};
|
|
21155
|
+
}
|
|
21156
|
+
switch (spec.type) {
|
|
21157
|
+
case "number": {
|
|
21158
|
+
const n = parseNumber(normalizedRaw);
|
|
21159
|
+
if (n === void 0) {
|
|
21160
|
+
return {
|
|
21161
|
+
ok: false,
|
|
21162
|
+
error: { key, message: `Env ${key}: expected number, got "${raw}"`, raw, kind: "invalid_number" }
|
|
21163
|
+
};
|
|
21164
|
+
}
|
|
21165
|
+
const constraintError = validateNumberConstraints(key, raw, n, spec);
|
|
21166
|
+
if (constraintError !== void 0) {
|
|
21167
|
+
return { ok: false, error: constraintError };
|
|
21168
|
+
}
|
|
21169
|
+
return { ok: true, value: n };
|
|
21170
|
+
}
|
|
21171
|
+
case "boolean":
|
|
21172
|
+
return { ok: true, value: parseBooleanEnvValue(normalizedRaw) };
|
|
21173
|
+
case "enum":
|
|
21174
|
+
if (!spec.choices.includes(normalizedRaw)) {
|
|
21175
|
+
return {
|
|
21176
|
+
ok: false,
|
|
21177
|
+
error: {
|
|
21178
|
+
key,
|
|
21179
|
+
message: `Env ${key}: must be one of [${spec.choices.join(", ")}]`,
|
|
21180
|
+
raw,
|
|
21181
|
+
kind: "invalid_enum"
|
|
21182
|
+
}
|
|
21183
|
+
};
|
|
21184
|
+
}
|
|
21185
|
+
return { ok: true, value: normalizedRaw };
|
|
21186
|
+
default:
|
|
21187
|
+
return { ok: true, value: normalizedRaw };
|
|
21188
|
+
}
|
|
21189
|
+
};
|
|
21190
|
+
var resolve = (key, spec = exports2.StrictEnvType.String, options) => {
|
|
21191
|
+
const result = parseEnvValue(key, spec, process.env[key], options?.default, options);
|
|
21192
|
+
if (!result.ok) {
|
|
21193
|
+
throw new StrictEnvValidationError([result.error]);
|
|
21194
|
+
}
|
|
21195
|
+
return result.value;
|
|
21196
|
+
};
|
|
21197
|
+
var resolveAll = (schema) => {
|
|
21198
|
+
const envs = {};
|
|
21199
|
+
const errors = [];
|
|
21200
|
+
for (const key of Object.keys(schema)) {
|
|
21201
|
+
const { spec, options } = resolveSchemaEntry(schema[key]);
|
|
21202
|
+
const result = parseEnvValue(key, spec, process.env[key], options?.default, options);
|
|
21203
|
+
if (!result.ok) {
|
|
21204
|
+
errors.push(result.error);
|
|
21205
|
+
continue;
|
|
21206
|
+
}
|
|
21207
|
+
envs[key] = result.value;
|
|
21208
|
+
}
|
|
21209
|
+
if (errors.length > 0)
|
|
21210
|
+
throw new StrictEnvValidationError(errors);
|
|
21211
|
+
return envs;
|
|
21212
|
+
};
|
|
21213
|
+
exports2.StrictEnvResolver = {
|
|
21214
|
+
/** Reads and parses a single environment variable. See {@link resolve}. */
|
|
21215
|
+
resolve,
|
|
21216
|
+
/** Reads and parses multiple environment variables in one pass. See {@link resolveAll}. */
|
|
21217
|
+
resolveAll
|
|
21218
|
+
};
|
|
21219
|
+
}
|
|
21220
|
+
});
|
|
21221
|
+
|
|
20765
21222
|
// src/funcs/auto-start-prevent.lambda.ts
|
|
20766
21223
|
var auto_start_prevent_lambda_exports = {};
|
|
20767
21224
|
__export(auto_start_prevent_lambda_exports, {
|
|
20768
|
-
handler: () => handler
|
|
21225
|
+
handler: () => handler,
|
|
21226
|
+
processAutoStartPrevent: () => processAutoStartPrevent
|
|
20769
21227
|
});
|
|
20770
21228
|
module.exports = __toCommonJS(auto_start_prevent_lambda_exports);
|
|
20771
21229
|
|
|
@@ -24286,36 +24744,10 @@ var DEFAULT_CONFIG = {
|
|
|
24286
24744
|
// src/funcs/auto-start-prevent.lambda.ts
|
|
24287
24745
|
var import_client_rds = require("@aws-sdk/client-rds");
|
|
24288
24746
|
var import_web_api = __toESM(require_dist5());
|
|
24289
|
-
var import_aws_lambda_secret_fetcher = __toESM(
|
|
24290
|
-
var
|
|
24291
|
-
|
|
24292
|
-
|
|
24293
|
-
const tagValuesJson = process.env.TAG_VALUES;
|
|
24294
|
-
if (!tagKey || !tagValuesJson) {
|
|
24295
|
-
throw new Error("missing environment variables TAG_KEY or TAG_VALUES.");
|
|
24296
|
-
}
|
|
24297
|
-
let tagValues;
|
|
24298
|
-
try {
|
|
24299
|
-
tagValues = JSON.parse(tagValuesJson);
|
|
24300
|
-
} catch {
|
|
24301
|
-
throw new Error("TAG_VALUES must be a JSON array of strings.");
|
|
24302
|
-
}
|
|
24303
|
-
if (!Array.isArray(tagValues)) {
|
|
24304
|
-
throw new Error("TAG_VALUES must be a JSON array of strings.");
|
|
24305
|
-
}
|
|
24306
|
-
return { tagKey, tagValues };
|
|
24307
|
-
};
|
|
24308
|
-
var normalizeInput = (input) => {
|
|
24309
|
-
if (isRawEvent(input)) {
|
|
24310
|
-
return { event: input, params: paramsFromEnv() };
|
|
24311
|
-
}
|
|
24312
|
-
const candidate = input;
|
|
24313
|
-
if (typeof candidate === "object" && candidate != null && candidate.event != null && candidate.params != null) {
|
|
24314
|
-
return candidate;
|
|
24315
|
-
}
|
|
24316
|
-
throw new Error("Invalid input: expected RDS event or { event, params }.");
|
|
24317
|
-
};
|
|
24318
|
-
var rdsClient = new import_client_rds.RDSClient({});
|
|
24747
|
+
var import_aws_lambda_secret_fetcher = __toESM(require_lib4());
|
|
24748
|
+
var import_strict_env_resolver = __toESM(require_lib5());
|
|
24749
|
+
|
|
24750
|
+
// src/funcs/auto-start-prevent.predicates.ts
|
|
24319
24751
|
var TRANSITIONAL_STATUSES = /* @__PURE__ */ new Set([
|
|
24320
24752
|
"starting",
|
|
24321
24753
|
"configuring-enhanced-monitoring",
|
|
@@ -24323,6 +24755,31 @@ var TRANSITIONAL_STATUSES = /* @__PURE__ */ new Set([
|
|
|
24323
24755
|
"modifying",
|
|
24324
24756
|
"stopping"
|
|
24325
24757
|
]);
|
|
24758
|
+
var isTransitionalStatus = (status) => TRANSITIONAL_STATUSES.has(status);
|
|
24759
|
+
var isSlackSecret = (value) => {
|
|
24760
|
+
if (typeof value !== "object" || value == null) {
|
|
24761
|
+
return false;
|
|
24762
|
+
}
|
|
24763
|
+
if (!("token" in value) || !("channel" in value)) {
|
|
24764
|
+
return false;
|
|
24765
|
+
}
|
|
24766
|
+
return typeof value.token === "string" && value.token.length > 0 && typeof value.channel === "string" && value.channel.length > 0;
|
|
24767
|
+
};
|
|
24768
|
+
var isAutoStartPreventInput = (input) => {
|
|
24769
|
+
if (typeof input !== "object" || input == null) {
|
|
24770
|
+
return false;
|
|
24771
|
+
}
|
|
24772
|
+
if (!("event" in input) || !("params" in input)) {
|
|
24773
|
+
return false;
|
|
24774
|
+
}
|
|
24775
|
+
return input.event != null && input.params != null;
|
|
24776
|
+
};
|
|
24777
|
+
var normalizeInput = (input) => {
|
|
24778
|
+
if (isAutoStartPreventInput(input)) {
|
|
24779
|
+
return input;
|
|
24780
|
+
}
|
|
24781
|
+
throw new Error("Invalid input: expected { event, params }.");
|
|
24782
|
+
};
|
|
24326
24783
|
var matchTag = (params, tags) => {
|
|
24327
24784
|
if (!tags || tags.length === 0) {
|
|
24328
24785
|
return false;
|
|
@@ -24333,29 +24790,175 @@ var matchTag = (params, tags) => {
|
|
|
24333
24790
|
}
|
|
24334
24791
|
return params.tagValues.includes(value);
|
|
24335
24792
|
};
|
|
24336
|
-
var
|
|
24337
|
-
|
|
24338
|
-
|
|
24339
|
-
|
|
24340
|
-
|
|
24341
|
-
|
|
24342
|
-
|
|
24343
|
-
|
|
24344
|
-
|
|
24345
|
-
|
|
24346
|
-
}
|
|
24347
|
-
|
|
24348
|
-
|
|
24793
|
+
var isDbInstanceAutoStart = (detailType, detail) => detailType === "RDS DB Instance Event" && detail.SourceType === "DB_INSTANCE" && detail.EventID === "RDS-EVENT-0154";
|
|
24794
|
+
var isDbClusterAutoStart = (detailType, detail) => detailType === "RDS DB Cluster Event" && detail.SourceType === "CLUSTER" && detail.EventID === "RDS-EVENT-0153";
|
|
24795
|
+
var waitStrategyUntilStable = (state) => {
|
|
24796
|
+
if (isTransitionalStatus(state.status)) {
|
|
24797
|
+
return { shouldContinue: true, delay: { minutes: 5 } };
|
|
24798
|
+
}
|
|
24799
|
+
return { shouldContinue: false };
|
|
24800
|
+
};
|
|
24801
|
+
var waitStrategyUntilStopped = (state) => {
|
|
24802
|
+
if (state.status === "stopped") {
|
|
24803
|
+
return { shouldContinue: false };
|
|
24804
|
+
}
|
|
24805
|
+
if (isTransitionalStatus(state.status)) {
|
|
24806
|
+
return { shouldContinue: true, delay: { minutes: 5 } };
|
|
24807
|
+
}
|
|
24808
|
+
throw new Error(`Unexpected status while waiting for stop: ${state.status}`);
|
|
24809
|
+
};
|
|
24810
|
+
var decideAfterTagMatch = (status) => {
|
|
24811
|
+
if (status === "available") {
|
|
24812
|
+
return { kind: "stop" };
|
|
24813
|
+
}
|
|
24814
|
+
if (status === "stopped") {
|
|
24815
|
+
return {
|
|
24816
|
+
kind: "no-op",
|
|
24817
|
+
result: {
|
|
24818
|
+
action: "no-op",
|
|
24819
|
+
reason: "already stopped",
|
|
24820
|
+
status
|
|
24821
|
+
}
|
|
24822
|
+
};
|
|
24823
|
+
}
|
|
24824
|
+
return {
|
|
24825
|
+
kind: "error",
|
|
24826
|
+
message: `DB status is not stopped after processing: ${status}`
|
|
24827
|
+
};
|
|
24828
|
+
};
|
|
24829
|
+
var tagMismatchNoOp = (status) => ({
|
|
24830
|
+
action: "no-op",
|
|
24831
|
+
reason: "tag not matched or not found",
|
|
24832
|
+
status
|
|
24833
|
+
});
|
|
24834
|
+
var parseRdsSourceArn = (sourceArn) => {
|
|
24835
|
+
const parts = sourceArn.split(":");
|
|
24836
|
+
return {
|
|
24837
|
+
region: parts[3] ?? "",
|
|
24838
|
+
account: parts[4] ?? ""
|
|
24839
|
+
};
|
|
24840
|
+
};
|
|
24841
|
+
var buildSlackStopNotification = (args) => ({
|
|
24842
|
+
channel: args.channel,
|
|
24843
|
+
attachments: [
|
|
24844
|
+
{
|
|
24845
|
+
color: "#36a64f",
|
|
24846
|
+
pretext: `\u{1F634} Successfully stopped the automatically running RDS ${args.sourceType} ${args.sourceIdentifier}.`,
|
|
24847
|
+
fields: [
|
|
24848
|
+
{
|
|
24849
|
+
title: "Account",
|
|
24850
|
+
value: args.account,
|
|
24851
|
+
short: true
|
|
24852
|
+
},
|
|
24853
|
+
{
|
|
24854
|
+
title: "Region",
|
|
24855
|
+
value: args.region,
|
|
24856
|
+
short: true
|
|
24857
|
+
},
|
|
24858
|
+
{
|
|
24859
|
+
title: "Type",
|
|
24860
|
+
value: args.sourceType,
|
|
24861
|
+
short: true
|
|
24862
|
+
},
|
|
24863
|
+
{
|
|
24864
|
+
title: "Identifier",
|
|
24865
|
+
value: args.sourceIdentifier,
|
|
24866
|
+
short: true
|
|
24867
|
+
}
|
|
24868
|
+
]
|
|
24349
24869
|
}
|
|
24350
|
-
|
|
24351
|
-
|
|
24352
|
-
|
|
24353
|
-
|
|
24354
|
-
|
|
24870
|
+
]
|
|
24871
|
+
});
|
|
24872
|
+
var buildStoppedResult = (args) => ({
|
|
24873
|
+
action: "stopped",
|
|
24874
|
+
finalStatus: args.finalStatus,
|
|
24875
|
+
account: args.account,
|
|
24876
|
+
region: args.region,
|
|
24877
|
+
identifier: args.identifier
|
|
24878
|
+
});
|
|
24879
|
+
|
|
24880
|
+
// src/funcs/auto-start-prevent.lambda.ts
|
|
24881
|
+
var rdsClient = new import_client_rds.RDSClient({});
|
|
24882
|
+
var processAutoStartPrevent = async (input, context) => {
|
|
24883
|
+
const { event, params } = normalizeInput(input);
|
|
24884
|
+
const { detail, "detail-type": detailType } = event;
|
|
24885
|
+
const slackSecretName = import_strict_env_resolver.StrictEnvResolver.resolve("SLACK_SECRET_NAME", import_strict_env_resolver.StrictEnvType.String, {
|
|
24886
|
+
trim: true
|
|
24887
|
+
});
|
|
24888
|
+
const slackSecretValue = await context.step("fetch-slack-secret", async () => {
|
|
24889
|
+
return import_aws_lambda_secret_fetcher.secretFetcher.getSecretValue(slackSecretName);
|
|
24890
|
+
});
|
|
24891
|
+
if (!isSlackSecret(slackSecretValue)) {
|
|
24892
|
+
throw new Error("Slack secret must be JSON with non-empty token and channel.");
|
|
24893
|
+
}
|
|
24894
|
+
const isInstance = isDbInstanceAutoStart(detailType, detail);
|
|
24895
|
+
const isCluster = isDbClusterAutoStart(detailType, detail);
|
|
24896
|
+
if (!isInstance && !isCluster) {
|
|
24897
|
+
throw new Error(
|
|
24898
|
+
`Unsupported event: detail-type=${detailType}, SourceType=${detail.SourceType}, EventID=${detail.EventID}`
|
|
24899
|
+
);
|
|
24900
|
+
}
|
|
24901
|
+
await context.wait({ minutes: 1 });
|
|
24902
|
+
const firstDescribe = await context.waitForCondition(
|
|
24903
|
+
async (_state, _ctx) => {
|
|
24904
|
+
if (isInstance) {
|
|
24905
|
+
const res2 = await rdsClient.send(
|
|
24906
|
+
new import_client_rds.DescribeDBInstancesCommand({
|
|
24907
|
+
DBInstanceIdentifier: detail.SourceIdentifier
|
|
24908
|
+
})
|
|
24909
|
+
);
|
|
24910
|
+
const db = res2.DBInstances?.[0];
|
|
24911
|
+
return {
|
|
24912
|
+
status: db?.DBInstanceStatus ?? "unknown",
|
|
24913
|
+
identifier: db?.DBInstanceIdentifier ?? detail.SourceIdentifier,
|
|
24914
|
+
tags: db?.TagList ?? []
|
|
24915
|
+
};
|
|
24916
|
+
}
|
|
24917
|
+
const res = await rdsClient.send(
|
|
24918
|
+
new import_client_rds.DescribeDBClustersCommand({
|
|
24919
|
+
DBClusterIdentifier: detail.SourceIdentifier
|
|
24920
|
+
})
|
|
24355
24921
|
);
|
|
24922
|
+
const cluster = res.DBClusters?.[0];
|
|
24923
|
+
return {
|
|
24924
|
+
status: cluster?.Status ?? "unknown",
|
|
24925
|
+
identifier: cluster?.DBClusterIdentifier ?? detail.SourceIdentifier,
|
|
24926
|
+
tags: cluster?.TagList ?? []
|
|
24927
|
+
};
|
|
24928
|
+
},
|
|
24929
|
+
{
|
|
24930
|
+
initialState: {
|
|
24931
|
+
status: "starting",
|
|
24932
|
+
identifier: detail.SourceIdentifier
|
|
24933
|
+
},
|
|
24934
|
+
waitStrategy: waitStrategyUntilStable
|
|
24935
|
+
}
|
|
24936
|
+
);
|
|
24937
|
+
if (!matchTag(params, firstDescribe.tags)) {
|
|
24938
|
+
return tagMismatchNoOp(firstDescribe.status);
|
|
24939
|
+
}
|
|
24940
|
+
let didStop = false;
|
|
24941
|
+
let finalStatus = firstDescribe.status;
|
|
24942
|
+
const afterTagMatch = decideAfterTagMatch(firstDescribe.status);
|
|
24943
|
+
if (afterTagMatch.kind === "stop") {
|
|
24944
|
+
if (isInstance) {
|
|
24945
|
+
await context.step("stop-db-instance", async () => {
|
|
24946
|
+
await rdsClient.send(
|
|
24947
|
+
new import_client_rds.StopDBInstanceCommand({
|
|
24948
|
+
DBInstanceIdentifier: detail.SourceIdentifier
|
|
24949
|
+
})
|
|
24950
|
+
);
|
|
24951
|
+
});
|
|
24952
|
+
} else {
|
|
24953
|
+
await context.step("stop-db-cluster", async () => {
|
|
24954
|
+
await rdsClient.send(
|
|
24955
|
+
new import_client_rds.StopDBClusterCommand({
|
|
24956
|
+
DBClusterIdentifier: detail.SourceIdentifier
|
|
24957
|
+
})
|
|
24958
|
+
);
|
|
24959
|
+
});
|
|
24356
24960
|
}
|
|
24357
|
-
await context.
|
|
24358
|
-
const firstDescribe = await context.waitForCondition(
|
|
24961
|
+
const stopped = await context.waitForCondition(
|
|
24359
24962
|
async (_state, _ctx) => {
|
|
24360
24963
|
if (isInstance) {
|
|
24361
24964
|
const res2 = await rdsClient.send(
|
|
@@ -24366,8 +24969,7 @@ var handler = withDurableExecution(
|
|
|
24366
24969
|
const db = res2.DBInstances?.[0];
|
|
24367
24970
|
return {
|
|
24368
24971
|
status: db?.DBInstanceStatus ?? "unknown",
|
|
24369
|
-
identifier: db?.DBInstanceIdentifier ?? detail.SourceIdentifier
|
|
24370
|
-
tags: db?.TagList ?? []
|
|
24972
|
+
identifier: db?.DBInstanceIdentifier ?? detail.SourceIdentifier
|
|
24371
24973
|
};
|
|
24372
24974
|
}
|
|
24373
24975
|
const res = await rdsClient.send(
|
|
@@ -24378,157 +24980,56 @@ var handler = withDurableExecution(
|
|
|
24378
24980
|
const cluster = res.DBClusters?.[0];
|
|
24379
24981
|
return {
|
|
24380
24982
|
status: cluster?.Status ?? "unknown",
|
|
24381
|
-
identifier: cluster?.DBClusterIdentifier ?? detail.SourceIdentifier
|
|
24382
|
-
tags: cluster?.TagList ?? []
|
|
24983
|
+
identifier: cluster?.DBClusterIdentifier ?? detail.SourceIdentifier
|
|
24383
24984
|
};
|
|
24384
24985
|
},
|
|
24385
24986
|
{
|
|
24386
24987
|
initialState: {
|
|
24387
|
-
status:
|
|
24388
|
-
identifier:
|
|
24988
|
+
status: firstDescribe.status,
|
|
24989
|
+
identifier: firstDescribe.identifier
|
|
24389
24990
|
},
|
|
24390
|
-
waitStrategy:
|
|
24391
|
-
if (TRANSITIONAL_STATUSES.has(state.status)) {
|
|
24392
|
-
return { shouldContinue: true, delay: { minutes: 5 } };
|
|
24393
|
-
}
|
|
24394
|
-
return { shouldContinue: false };
|
|
24395
|
-
}
|
|
24991
|
+
waitStrategy: waitStrategyUntilStopped
|
|
24396
24992
|
}
|
|
24397
24993
|
);
|
|
24398
|
-
|
|
24399
|
-
|
|
24400
|
-
|
|
24401
|
-
|
|
24402
|
-
|
|
24403
|
-
|
|
24404
|
-
}
|
|
24405
|
-
|
|
24406
|
-
|
|
24407
|
-
|
|
24408
|
-
|
|
24409
|
-
|
|
24410
|
-
|
|
24411
|
-
|
|
24412
|
-
|
|
24413
|
-
|
|
24414
|
-
|
|
24415
|
-
|
|
24416
|
-
|
|
24417
|
-
|
|
24418
|
-
|
|
24419
|
-
|
|
24420
|
-
|
|
24421
|
-
|
|
24422
|
-
|
|
24423
|
-
|
|
24424
|
-
|
|
24425
|
-
|
|
24426
|
-
|
|
24427
|
-
|
|
24428
|
-
|
|
24429
|
-
|
|
24430
|
-
|
|
24431
|
-
|
|
24432
|
-
|
|
24433
|
-
const db = res2.DBInstances?.[0];
|
|
24434
|
-
return {
|
|
24435
|
-
status: db?.DBInstanceStatus ?? "unknown",
|
|
24436
|
-
identifier: db?.DBInstanceIdentifier ?? detail.SourceIdentifier
|
|
24437
|
-
};
|
|
24438
|
-
}
|
|
24439
|
-
const res = await rdsClient.send(
|
|
24440
|
-
new import_client_rds.DescribeDBClustersCommand({
|
|
24441
|
-
DBClusterIdentifier: detail.SourceIdentifier
|
|
24442
|
-
})
|
|
24443
|
-
);
|
|
24444
|
-
const cluster = res.DBClusters?.[0];
|
|
24445
|
-
return {
|
|
24446
|
-
status: cluster?.Status ?? "unknown",
|
|
24447
|
-
identifier: cluster?.DBClusterIdentifier ?? detail.SourceIdentifier
|
|
24448
|
-
};
|
|
24449
|
-
},
|
|
24450
|
-
{
|
|
24451
|
-
initialState: {
|
|
24452
|
-
status: firstDescribe.status,
|
|
24453
|
-
identifier: firstDescribe.identifier
|
|
24454
|
-
},
|
|
24455
|
-
waitStrategy: (state) => {
|
|
24456
|
-
if (state.status === "stopped") {
|
|
24457
|
-
return { shouldContinue: false };
|
|
24458
|
-
}
|
|
24459
|
-
if (TRANSITIONAL_STATUSES.has(state.status)) {
|
|
24460
|
-
return { shouldContinue: true, delay: { minutes: 5 } };
|
|
24461
|
-
}
|
|
24462
|
-
throw new Error(`Unexpected status while waiting for stop: ${state.status}`);
|
|
24463
|
-
}
|
|
24464
|
-
}
|
|
24465
|
-
);
|
|
24466
|
-
didStop = true;
|
|
24467
|
-
finalStatus = stopped.status;
|
|
24468
|
-
}
|
|
24469
|
-
if (!didStop) {
|
|
24470
|
-
if (finalStatus === "stopped") {
|
|
24471
|
-
return {
|
|
24472
|
-
action: "no-op",
|
|
24473
|
-
reason: "already stopped",
|
|
24474
|
-
status: finalStatus
|
|
24475
|
-
};
|
|
24476
|
-
}
|
|
24477
|
-
throw new Error(`DB status is not stopped after processing: ${finalStatus}`);
|
|
24478
|
-
}
|
|
24479
|
-
if (finalStatus !== "stopped") {
|
|
24480
|
-
throw new Error(`DB status is not stopped after processing: ${finalStatus}`);
|
|
24481
|
-
}
|
|
24482
|
-
const sourceArnParts = detail.SourceArn.split(":");
|
|
24483
|
-
const region = sourceArnParts[3];
|
|
24484
|
-
const account = sourceArnParts[4];
|
|
24485
|
-
const client = new import_web_api.WebClient(slackSecretValue.token);
|
|
24486
|
-
const channel = slackSecretValue.channel;
|
|
24487
|
-
await context.step("post-slack-messages", async () => {
|
|
24488
|
-
return client.chat.postMessage({
|
|
24489
|
-
channel,
|
|
24490
|
-
attachments: [
|
|
24491
|
-
{
|
|
24492
|
-
color: "#36a64f",
|
|
24493
|
-
pretext: `\u{1F634} Successfully stopped the automatically running RDS ${detail.SourceType} ${detail.SourceIdentifier}.`,
|
|
24494
|
-
fields: [
|
|
24495
|
-
{
|
|
24496
|
-
title: "Account",
|
|
24497
|
-
value: account,
|
|
24498
|
-
short: true
|
|
24499
|
-
},
|
|
24500
|
-
{
|
|
24501
|
-
title: "Region",
|
|
24502
|
-
value: region,
|
|
24503
|
-
short: true
|
|
24504
|
-
},
|
|
24505
|
-
{
|
|
24506
|
-
title: "Type",
|
|
24507
|
-
value: detail.SourceType,
|
|
24508
|
-
short: true
|
|
24509
|
-
},
|
|
24510
|
-
{
|
|
24511
|
-
title: "Identifier",
|
|
24512
|
-
value: detail.SourceIdentifier,
|
|
24513
|
-
short: true
|
|
24514
|
-
}
|
|
24515
|
-
]
|
|
24516
|
-
}
|
|
24517
|
-
]
|
|
24518
|
-
});
|
|
24519
|
-
});
|
|
24520
|
-
return {
|
|
24521
|
-
action: "stopped",
|
|
24522
|
-
finalStatus,
|
|
24523
|
-
account,
|
|
24524
|
-
region,
|
|
24525
|
-
identifier: detail.SourceIdentifier
|
|
24526
|
-
};
|
|
24527
|
-
}
|
|
24528
|
-
);
|
|
24994
|
+
didStop = true;
|
|
24995
|
+
finalStatus = stopped.status;
|
|
24996
|
+
}
|
|
24997
|
+
if (!didStop) {
|
|
24998
|
+
if (afterTagMatch.kind === "no-op") {
|
|
24999
|
+
return afterTagMatch.result;
|
|
25000
|
+
}
|
|
25001
|
+
if (afterTagMatch.kind === "error") {
|
|
25002
|
+
throw new Error(afterTagMatch.message);
|
|
25003
|
+
}
|
|
25004
|
+
}
|
|
25005
|
+
if (finalStatus !== "stopped") {
|
|
25006
|
+
throw new Error(`DB status is not stopped after processing: ${finalStatus}`);
|
|
25007
|
+
}
|
|
25008
|
+
const { region, account } = parseRdsSourceArn(detail.SourceArn);
|
|
25009
|
+
const client = new import_web_api.WebClient(slackSecretValue.token);
|
|
25010
|
+
await context.step("post-slack-messages", async () => {
|
|
25011
|
+
return client.chat.postMessage(
|
|
25012
|
+
buildSlackStopNotification({
|
|
25013
|
+
channel: slackSecretValue.channel,
|
|
25014
|
+
sourceType: detail.SourceType,
|
|
25015
|
+
sourceIdentifier: detail.SourceIdentifier,
|
|
25016
|
+
account,
|
|
25017
|
+
region
|
|
25018
|
+
})
|
|
25019
|
+
);
|
|
25020
|
+
});
|
|
25021
|
+
return buildStoppedResult({
|
|
25022
|
+
finalStatus,
|
|
25023
|
+
account,
|
|
25024
|
+
region,
|
|
25025
|
+
identifier: detail.SourceIdentifier
|
|
25026
|
+
});
|
|
25027
|
+
};
|
|
25028
|
+
var handler = withDurableExecution(processAutoStartPrevent);
|
|
24529
25029
|
// Annotate the CommonJS export names for ESM import in node:
|
|
24530
25030
|
0 && (module.exports = {
|
|
24531
|
-
handler
|
|
25031
|
+
handler,
|
|
25032
|
+
processAutoStartPrevent
|
|
24532
25033
|
});
|
|
24533
25034
|
/*! Bundled license information:
|
|
24534
25035
|
|