ec2-instance-running-scheduler 0.3.6 → 0.4.0
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/.devcontainer/devcontainer.json +4 -3
- package/.jsii +298 -41
- package/API.md +239 -6
- package/README.md +76 -11
- package/assets/funcs/running-scheduler.lambda/index.js +355 -54
- package/assets/funcs/running-scheduler.lambda/index.js.map +4 -4
- package/lib/constructs/ec2-instance-running-scheduler.d.ts +105 -3
- package/lib/constructs/ec2-instance-running-scheduler.js +101 -17
- package/lib/constructs/running-scheduler-failure-detection.js +1 -1
- package/lib/funcs/running-scheduler-predicates.d.ts +4 -0
- package/lib/funcs/running-scheduler-predicates.js +5 -2
- package/lib/funcs/running-scheduler-wait-config.d.ts +13 -1
- package/lib/funcs/running-scheduler-wait-config.js +15 -3
- package/lib/funcs/running-scheduler-wait-env.d.ts +8 -2
- package/lib/funcs/running-scheduler-wait-env.js +29 -27
- package/lib/funcs/running-scheduler.lambda.d.ts +8 -5
- package/lib/funcs/running-scheduler.lambda.js +44 -21
- package/lib/stacks/ec2-instance-running-schedule-stack.d.ts +6 -3
- package/lib/stacks/ec2-instance-running-schedule-stack.js +6 -4
- package/package.json +13 -13
|
@@ -20305,7 +20305,7 @@ var require_lib = __commonJS({
|
|
|
20305
20305
|
"node_modules/fetch-retrier/lib/index.js"(exports2) {
|
|
20306
20306
|
"use strict";
|
|
20307
20307
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20308
|
-
exports2.fetchRetrier = exports2.FetchRetrierUnreachableError = exports2.FetchRetrierNetworkError = exports2.FetchRetrierHttpError = exports2.FetchRetrierAlreadyAbortedError = exports2.FetchRetrierAbortError = void 0;
|
|
20308
|
+
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;
|
|
20309
20309
|
var FetchRetrierAbortError = class _FetchRetrierAbortError extends Error {
|
|
20310
20310
|
/**
|
|
20311
20311
|
* @param message - Human-readable reason (default: `'Aborted'`)
|
|
@@ -20332,10 +20332,12 @@ var require_lib = __commonJS({
|
|
|
20332
20332
|
/**
|
|
20333
20333
|
* @param message - Error description
|
|
20334
20334
|
* @param status - HTTP status code from the last non-OK response
|
|
20335
|
+
* @param body - Response body text already read via `response.text()` for that attempt
|
|
20335
20336
|
*/
|
|
20336
|
-
constructor(message, status) {
|
|
20337
|
+
constructor(message, status, body) {
|
|
20337
20338
|
super(message);
|
|
20338
20339
|
this.status = status;
|
|
20340
|
+
this.body = body;
|
|
20339
20341
|
this.name = "FetchRetrierHttpError";
|
|
20340
20342
|
Object.setPrototypeOf(this, _FetchRetrierHttpError.prototype);
|
|
20341
20343
|
}
|
|
@@ -20354,6 +20356,17 @@ var require_lib = __commonJS({
|
|
|
20354
20356
|
}
|
|
20355
20357
|
};
|
|
20356
20358
|
exports2.FetchRetrierNetworkError = FetchRetrierNetworkError;
|
|
20359
|
+
var FetchRetrierInvalidOptionsError = class _FetchRetrierInvalidOptionsError extends TypeError {
|
|
20360
|
+
/**
|
|
20361
|
+
* @param message - Human-readable reason describing the invalid option
|
|
20362
|
+
*/
|
|
20363
|
+
constructor(message) {
|
|
20364
|
+
super(message);
|
|
20365
|
+
this.name = "FetchRetrierInvalidOptionsError";
|
|
20366
|
+
Object.setPrototypeOf(this, _FetchRetrierInvalidOptionsError.prototype);
|
|
20367
|
+
}
|
|
20368
|
+
};
|
|
20369
|
+
exports2.FetchRetrierInvalidOptionsError = FetchRetrierInvalidOptionsError;
|
|
20357
20370
|
var FetchRetrierUnreachableError = class _FetchRetrierUnreachableError extends Error {
|
|
20358
20371
|
/**
|
|
20359
20372
|
* @param message - Human-readable reason (default: `'Unreachable'`)
|
|
@@ -20365,11 +20378,26 @@ var require_lib = __commonJS({
|
|
|
20365
20378
|
}
|
|
20366
20379
|
};
|
|
20367
20380
|
exports2.FetchRetrierUnreachableError = FetchRetrierUnreachableError;
|
|
20368
|
-
|
|
20369
|
-
|
|
20381
|
+
exports2.DEFAULT_RETRYABLE_HTTP_STATUSES = [408, 425, 429, 500, 502, 503, 504];
|
|
20382
|
+
var defaultShouldRetry = (response, _body) => {
|
|
20383
|
+
return exports2.DEFAULT_RETRYABLE_HTTP_STATUSES.includes(response.status);
|
|
20384
|
+
};
|
|
20385
|
+
exports2.defaultShouldRetry = defaultShouldRetry;
|
|
20386
|
+
var validateRequestOptions = (options) => {
|
|
20387
|
+
const { retries, timeoutMs, baseBackoffMs } = options;
|
|
20388
|
+
if (retries < 1) {
|
|
20389
|
+
throw new FetchRetrierInvalidOptionsError("retries must be >= 1");
|
|
20390
|
+
}
|
|
20391
|
+
if (timeoutMs <= 0) {
|
|
20392
|
+
throw new FetchRetrierInvalidOptionsError("timeoutMs must be > 0");
|
|
20393
|
+
}
|
|
20394
|
+
if (baseBackoffMs < 0) {
|
|
20395
|
+
throw new FetchRetrierInvalidOptionsError("baseBackoffMs must be >= 0");
|
|
20396
|
+
}
|
|
20370
20397
|
};
|
|
20371
20398
|
var fetchRetrier = async (url, options) => {
|
|
20372
|
-
const { headers, init, retries, timeoutMs, baseBackoffMs, signal: externalSignal, shouldRetry = defaultShouldRetry } = options;
|
|
20399
|
+
const { headers, init, retries, timeoutMs, baseBackoffMs, signal: externalSignal, shouldRetry = exports2.defaultShouldRetry } = options;
|
|
20400
|
+
validateRequestOptions({ retries, timeoutMs, baseBackoffMs });
|
|
20373
20401
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
20374
20402
|
if (externalSignal?.aborted) {
|
|
20375
20403
|
throw new FetchRetrierAlreadyAbortedError();
|
|
@@ -20398,11 +20426,11 @@ var require_lib = __commonJS({
|
|
|
20398
20426
|
const isContinue = shouldRetry(res, text);
|
|
20399
20427
|
if (isContinue) {
|
|
20400
20428
|
if (attempt === retries) {
|
|
20401
|
-
throw new FetchRetrierHttpError(`HTTP ${res.status}`, res.status);
|
|
20429
|
+
throw new FetchRetrierHttpError(`HTTP ${res.status}`, res.status, text);
|
|
20402
20430
|
}
|
|
20403
|
-
await wait(
|
|
20431
|
+
await wait(resolveRetryDelayMs(res, baseBackoffMs, attempt));
|
|
20404
20432
|
} else {
|
|
20405
|
-
throw new FetchRetrierHttpError(`Non-retriable HTTP error: ${res.status}`, res.status);
|
|
20433
|
+
throw new FetchRetrierHttpError(`Non-retriable HTTP error: ${res.status}`, res.status, text);
|
|
20406
20434
|
}
|
|
20407
20435
|
} catch (err) {
|
|
20408
20436
|
clearTimeout(timer);
|
|
@@ -20432,12 +20460,68 @@ var require_lib = __commonJS({
|
|
|
20432
20460
|
const cap = base * Math.pow(2, attempt);
|
|
20433
20461
|
return Math.floor(Math.random() * cap);
|
|
20434
20462
|
};
|
|
20463
|
+
var parseRetryAfterMs = (value, nowMs = Date.now()) => {
|
|
20464
|
+
const trimmed = value.trim();
|
|
20465
|
+
if (trimmed === "") {
|
|
20466
|
+
return void 0;
|
|
20467
|
+
}
|
|
20468
|
+
if (/^\d+$/.test(trimmed)) {
|
|
20469
|
+
return Number.parseInt(trimmed, 10) * 1e3;
|
|
20470
|
+
}
|
|
20471
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
20472
|
+
return void 0;
|
|
20473
|
+
}
|
|
20474
|
+
const dateMs = Date.parse(trimmed);
|
|
20475
|
+
if (Number.isNaN(dateMs)) {
|
|
20476
|
+
return void 0;
|
|
20477
|
+
}
|
|
20478
|
+
return Math.max(0, dateMs - nowMs);
|
|
20479
|
+
};
|
|
20480
|
+
exports2.parseRetryAfterMs = parseRetryAfterMs;
|
|
20481
|
+
var resolveRetryDelayMs = (response, baseBackoffMs, attempt, nowMs = Date.now()) => {
|
|
20482
|
+
const header = response.headers?.get("Retry-After");
|
|
20483
|
+
if (header == null) {
|
|
20484
|
+
return fullJitter(baseBackoffMs, attempt);
|
|
20485
|
+
}
|
|
20486
|
+
const fromHeader = (0, exports2.parseRetryAfterMs)(header, nowMs);
|
|
20487
|
+
if (fromHeader === void 0) {
|
|
20488
|
+
return fullJitter(baseBackoffMs, attempt);
|
|
20489
|
+
}
|
|
20490
|
+
return fromHeader;
|
|
20491
|
+
};
|
|
20435
20492
|
}
|
|
20436
20493
|
});
|
|
20437
20494
|
|
|
20438
|
-
// node_modules/
|
|
20495
|
+
// node_modules/quiet-json-parser/lib/index.js
|
|
20439
20496
|
var require_lib2 = __commonJS({
|
|
20440
|
-
"node_modules/
|
|
20497
|
+
"node_modules/quiet-json-parser/lib/index.js"(exports2) {
|
|
20498
|
+
"use strict";
|
|
20499
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20500
|
+
exports2.quietParse = void 0;
|
|
20501
|
+
var safeReviver = (key, value) => {
|
|
20502
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
20503
|
+
return void 0;
|
|
20504
|
+
}
|
|
20505
|
+
return value;
|
|
20506
|
+
};
|
|
20507
|
+
var quietParse = (jsonString, fallback, onError) => {
|
|
20508
|
+
if (!jsonString) {
|
|
20509
|
+
return fallback;
|
|
20510
|
+
}
|
|
20511
|
+
try {
|
|
20512
|
+
return JSON.parse(jsonString, safeReviver);
|
|
20513
|
+
} catch (error) {
|
|
20514
|
+
onError?.(error);
|
|
20515
|
+
return fallback;
|
|
20516
|
+
}
|
|
20517
|
+
};
|
|
20518
|
+
exports2.quietParse = quietParse;
|
|
20519
|
+
}
|
|
20520
|
+
});
|
|
20521
|
+
|
|
20522
|
+
// node_modules/aws-lambda-secret-fetcher/node_modules/strict-env-resolver/lib/index.js
|
|
20523
|
+
var require_lib3 = __commonJS({
|
|
20524
|
+
"node_modules/aws-lambda-secret-fetcher/node_modules/strict-env-resolver/lib/index.js"(exports2) {
|
|
20441
20525
|
"use strict";
|
|
20442
20526
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20443
20527
|
exports2.StrictEnvResolver = exports2.StrictEnvType = exports2.StrictEnvValidationError = exports2.StrictEnvError = void 0;
|
|
@@ -20594,13 +20678,14 @@ ${lines.join("\n")}`;
|
|
|
20594
20678
|
});
|
|
20595
20679
|
|
|
20596
20680
|
// node_modules/aws-lambda-secret-fetcher/lib/index.js
|
|
20597
|
-
var
|
|
20681
|
+
var require_lib4 = __commonJS({
|
|
20598
20682
|
"node_modules/aws-lambda-secret-fetcher/lib/index.js"(exports2) {
|
|
20599
20683
|
"use strict";
|
|
20600
20684
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20601
20685
|
exports2.secretFetcher = void 0;
|
|
20602
20686
|
var fetch_retrier_1 = require_lib();
|
|
20603
|
-
var
|
|
20687
|
+
var quiet_json_parser_1 = require_lib2();
|
|
20688
|
+
var strict_env_resolver_1 = require_lib3();
|
|
20604
20689
|
var getSecretValue = async (name, options = {}) => {
|
|
20605
20690
|
const { extensionHttpPort, timeoutMs = 2e3, retries = 3, baseBackoffMs = 300 } = options;
|
|
20606
20691
|
const port = resolveExtensionHttpPort(extensionHttpPort);
|
|
@@ -20614,10 +20699,12 @@ var require_lib3 = __commonJS({
|
|
|
20614
20699
|
timeoutMs,
|
|
20615
20700
|
baseBackoffMs,
|
|
20616
20701
|
shouldRetry: (res, body) => {
|
|
20617
|
-
if (
|
|
20702
|
+
if ((0, fetch_retrier_1.defaultShouldRetry)(res, body)) {
|
|
20618
20703
|
return true;
|
|
20619
|
-
|
|
20704
|
+
}
|
|
20705
|
+
if (res.status === 400 && /not\s+ready.*traffic/i.test(body)) {
|
|
20620
20706
|
return true;
|
|
20707
|
+
}
|
|
20621
20708
|
return false;
|
|
20622
20709
|
}
|
|
20623
20710
|
};
|
|
@@ -20627,11 +20714,7 @@ var require_lib3 = __commonJS({
|
|
|
20627
20714
|
throw new Error("Invalid secret response format");
|
|
20628
20715
|
}
|
|
20629
20716
|
const data = raw;
|
|
20630
|
-
|
|
20631
|
-
if (looksLikeJson(secretString)) {
|
|
20632
|
-
return JSON.parse(secretString);
|
|
20633
|
-
}
|
|
20634
|
-
return secretString;
|
|
20717
|
+
return (0, quiet_json_parser_1.quietParse)(data.SecretString, data.SecretString);
|
|
20635
20718
|
};
|
|
20636
20719
|
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.";
|
|
20637
20720
|
var MIN_TCP_PORT = 1;
|
|
@@ -20669,15 +20752,212 @@ var require_lib3 = __commonJS({
|
|
|
20669
20752
|
const v = value;
|
|
20670
20753
|
return typeof v.SecretString === "string" && typeof v.Name === "string" && typeof v.ARN === "string" && (v.VersionId === void 0 || typeof v.VersionId === "string");
|
|
20671
20754
|
};
|
|
20672
|
-
var looksLikeJson = (str) => {
|
|
20673
|
-
return typeof str === "string" && str.trim().startsWith("{");
|
|
20674
|
-
};
|
|
20675
20755
|
exports2.secretFetcher = {
|
|
20676
20756
|
getSecretValue
|
|
20677
20757
|
};
|
|
20678
20758
|
}
|
|
20679
20759
|
});
|
|
20680
20760
|
|
|
20761
|
+
// node_modules/strict-env-resolver/lib/index.js
|
|
20762
|
+
var require_lib5 = __commonJS({
|
|
20763
|
+
"node_modules/strict-env-resolver/lib/index.js"(exports2) {
|
|
20764
|
+
"use strict";
|
|
20765
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
20766
|
+
exports2.StrictEnvResolver = exports2.StrictEnvType = exports2.StrictEnvValidationError = exports2.StrictEnvError = void 0;
|
|
20767
|
+
var TRUE_BOOLEAN_PATTERN = /^(1|true|yes|on)$/i;
|
|
20768
|
+
var parseNumber = (raw) => {
|
|
20769
|
+
const n = Number(raw);
|
|
20770
|
+
if (!Number.isFinite(n)) {
|
|
20771
|
+
return void 0;
|
|
20772
|
+
}
|
|
20773
|
+
return n;
|
|
20774
|
+
};
|
|
20775
|
+
var parseBooleanEnvValue = (normalizedRaw) => TRUE_BOOLEAN_PATTERN.test(normalizedRaw);
|
|
20776
|
+
var StrictEnvError = class extends Error {
|
|
20777
|
+
/**
|
|
20778
|
+
* @param message - Error message.
|
|
20779
|
+
*/
|
|
20780
|
+
constructor(message) {
|
|
20781
|
+
super(message);
|
|
20782
|
+
this.name = "StrictEnvError";
|
|
20783
|
+
}
|
|
20784
|
+
};
|
|
20785
|
+
exports2.StrictEnvError = StrictEnvError;
|
|
20786
|
+
var StrictEnvValidationError = class _StrictEnvValidationError extends StrictEnvError {
|
|
20787
|
+
/**
|
|
20788
|
+
* Formats validation errors into a human-readable error message.
|
|
20789
|
+
*
|
|
20790
|
+
* @template K - Environment variable key type.
|
|
20791
|
+
* @param errors - Validation error entries to format.
|
|
20792
|
+
* @returns Multi-line summary listing each key and message.
|
|
20793
|
+
*/
|
|
20794
|
+
static format(errors) {
|
|
20795
|
+
const lines = errors.map((e) => `- ${e.key}: ${e.message}${e.raw == null ? "" : ` (raw="${e.raw}")`}`);
|
|
20796
|
+
return `Invalid environment variables (${errors.length}):
|
|
20797
|
+
${lines.join("\n")}`;
|
|
20798
|
+
}
|
|
20799
|
+
/**
|
|
20800
|
+
* Creates a new validation error from one or more `StrictEnvValidationEntry` entries.
|
|
20801
|
+
*
|
|
20802
|
+
* @param errors - One or more structured validation errors.
|
|
20803
|
+
*/
|
|
20804
|
+
constructor(errors) {
|
|
20805
|
+
const msg = _StrictEnvValidationError.format(errors);
|
|
20806
|
+
super(msg);
|
|
20807
|
+
this.name = "StrictEnvValidationError";
|
|
20808
|
+
this.errors = errors;
|
|
20809
|
+
this.keys = errors.map((e) => e.key);
|
|
20810
|
+
}
|
|
20811
|
+
};
|
|
20812
|
+
exports2.StrictEnvValidationError = StrictEnvValidationError;
|
|
20813
|
+
var createNumberSpec = (constraints) => ({
|
|
20814
|
+
type: "number",
|
|
20815
|
+
...constraints?.min !== void 0 ? { min: constraints.min } : {},
|
|
20816
|
+
...constraints?.max !== void 0 ? { max: constraints.max } : {},
|
|
20817
|
+
...constraints?.integer !== void 0 ? { integer: constraints.integer } : {}
|
|
20818
|
+
});
|
|
20819
|
+
var NumberSpec = Object.assign((constraints) => createNumberSpec(constraints), { type: "number" });
|
|
20820
|
+
exports2.StrictEnvType = {
|
|
20821
|
+
/** Spec for a string value (returned as-is unless `trim: true` is set in options). */
|
|
20822
|
+
String: { type: "string" },
|
|
20823
|
+
/**
|
|
20824
|
+
* Spec for a finite numeric value (`Number()` parsing; rejects `NaN` and `Infinity`).
|
|
20825
|
+
* Use as `StrictEnvType.Number`, or call as `StrictEnvType.Number({ min, max, integer })`.
|
|
20826
|
+
*/
|
|
20827
|
+
Number: NumberSpec,
|
|
20828
|
+
/**
|
|
20829
|
+
* Spec for a positive integer (`>= 1`). Sugar for `Number({ min: 1, integer: true })`.
|
|
20830
|
+
*/
|
|
20831
|
+
PositiveInt: createNumberSpec({ min: 1, integer: true }),
|
|
20832
|
+
/**
|
|
20833
|
+
* Spec for a negative integer (`<= -1`). Sugar for `Number({ max: -1, integer: true })`.
|
|
20834
|
+
*/
|
|
20835
|
+
NegativeInt: createNumberSpec({ max: -1, integer: true }),
|
|
20836
|
+
/** Spec for a boolean value (`1`/`true`/`yes`/`on` → `true`; other non-empty values → `false`). */
|
|
20837
|
+
Boolean: { type: "boolean" },
|
|
20838
|
+
/**
|
|
20839
|
+
* Returns a spec that restricts the value to one of the given choices.
|
|
20840
|
+
*
|
|
20841
|
+
* @param choices - Allowed string literals.
|
|
20842
|
+
* @returns Enum spec for use with `resolve` or `resolveAll`.
|
|
20843
|
+
*/
|
|
20844
|
+
Enum: (choices) => ({ type: "enum", choices })
|
|
20845
|
+
};
|
|
20846
|
+
var defaultTrimForSpec = (spec) => spec.type !== "string";
|
|
20847
|
+
var normalizeEnvRaw = (raw, trim) => {
|
|
20848
|
+
if (raw == null) {
|
|
20849
|
+
return void 0;
|
|
20850
|
+
}
|
|
20851
|
+
if (!trim) {
|
|
20852
|
+
return raw;
|
|
20853
|
+
}
|
|
20854
|
+
return raw.trim();
|
|
20855
|
+
};
|
|
20856
|
+
var validateNumberConstraints = (key, raw, n, constraints) => {
|
|
20857
|
+
if (constraints.integer === true && !Number.isInteger(n)) {
|
|
20858
|
+
return { key, message: `Env ${key}: expected integer, got "${raw}"`, raw, kind: "invalid_number" };
|
|
20859
|
+
}
|
|
20860
|
+
if (constraints.min !== void 0 && n < constraints.min) {
|
|
20861
|
+
return {
|
|
20862
|
+
key,
|
|
20863
|
+
message: `Env ${key}: must be >= ${constraints.min}, got ${n}`,
|
|
20864
|
+
raw,
|
|
20865
|
+
kind: "invalid_number"
|
|
20866
|
+
};
|
|
20867
|
+
}
|
|
20868
|
+
if (constraints.max !== void 0 && n > constraints.max) {
|
|
20869
|
+
return {
|
|
20870
|
+
key,
|
|
20871
|
+
message: `Env ${key}: must be <= ${constraints.max}, got ${n}`,
|
|
20872
|
+
raw,
|
|
20873
|
+
kind: "invalid_number"
|
|
20874
|
+
};
|
|
20875
|
+
}
|
|
20876
|
+
return void 0;
|
|
20877
|
+
};
|
|
20878
|
+
var resolveSchemaEntry = (entry) => ({
|
|
20879
|
+
spec: Array.isArray(entry) ? entry[0] : entry,
|
|
20880
|
+
options: Array.isArray(entry) ? entry[1] : void 0
|
|
20881
|
+
});
|
|
20882
|
+
var parseEnvValue = (key, spec, raw, defaultValue, options) => {
|
|
20883
|
+
const trim = options?.trim ?? defaultTrimForSpec(spec);
|
|
20884
|
+
const normalizedRaw = normalizeEnvRaw(raw, trim);
|
|
20885
|
+
const hasDefault = defaultValue !== void 0;
|
|
20886
|
+
if (normalizedRaw == null || normalizedRaw === "") {
|
|
20887
|
+
if (hasDefault) {
|
|
20888
|
+
return { ok: true, value: defaultValue };
|
|
20889
|
+
}
|
|
20890
|
+
return {
|
|
20891
|
+
ok: false,
|
|
20892
|
+
error: { key, message: `Missing required environment variable: ${key}`, raw, kind: "missing" }
|
|
20893
|
+
};
|
|
20894
|
+
}
|
|
20895
|
+
switch (spec.type) {
|
|
20896
|
+
case "number": {
|
|
20897
|
+
const n = parseNumber(normalizedRaw);
|
|
20898
|
+
if (n === void 0) {
|
|
20899
|
+
return {
|
|
20900
|
+
ok: false,
|
|
20901
|
+
error: { key, message: `Env ${key}: expected number, got "${raw}"`, raw, kind: "invalid_number" }
|
|
20902
|
+
};
|
|
20903
|
+
}
|
|
20904
|
+
const constraintError = validateNumberConstraints(key, raw, n, spec);
|
|
20905
|
+
if (constraintError !== void 0) {
|
|
20906
|
+
return { ok: false, error: constraintError };
|
|
20907
|
+
}
|
|
20908
|
+
return { ok: true, value: n };
|
|
20909
|
+
}
|
|
20910
|
+
case "boolean":
|
|
20911
|
+
return { ok: true, value: parseBooleanEnvValue(normalizedRaw) };
|
|
20912
|
+
case "enum":
|
|
20913
|
+
if (!spec.choices.includes(normalizedRaw)) {
|
|
20914
|
+
return {
|
|
20915
|
+
ok: false,
|
|
20916
|
+
error: {
|
|
20917
|
+
key,
|
|
20918
|
+
message: `Env ${key}: must be one of [${spec.choices.join(", ")}]`,
|
|
20919
|
+
raw,
|
|
20920
|
+
kind: "invalid_enum"
|
|
20921
|
+
}
|
|
20922
|
+
};
|
|
20923
|
+
}
|
|
20924
|
+
return { ok: true, value: normalizedRaw };
|
|
20925
|
+
default:
|
|
20926
|
+
return { ok: true, value: normalizedRaw };
|
|
20927
|
+
}
|
|
20928
|
+
};
|
|
20929
|
+
var resolve = (key, spec = exports2.StrictEnvType.String, options) => {
|
|
20930
|
+
const result = parseEnvValue(key, spec, process.env[key], options?.default, options);
|
|
20931
|
+
if (!result.ok) {
|
|
20932
|
+
throw new StrictEnvValidationError([result.error]);
|
|
20933
|
+
}
|
|
20934
|
+
return result.value;
|
|
20935
|
+
};
|
|
20936
|
+
var resolveAll = (schema) => {
|
|
20937
|
+
const envs = {};
|
|
20938
|
+
const errors = [];
|
|
20939
|
+
for (const key of Object.keys(schema)) {
|
|
20940
|
+
const { spec, options } = resolveSchemaEntry(schema[key]);
|
|
20941
|
+
const result = parseEnvValue(key, spec, process.env[key], options?.default, options);
|
|
20942
|
+
if (!result.ok) {
|
|
20943
|
+
errors.push(result.error);
|
|
20944
|
+
continue;
|
|
20945
|
+
}
|
|
20946
|
+
envs[key] = result.value;
|
|
20947
|
+
}
|
|
20948
|
+
if (errors.length > 0)
|
|
20949
|
+
throw new StrictEnvValidationError(errors);
|
|
20950
|
+
return envs;
|
|
20951
|
+
};
|
|
20952
|
+
exports2.StrictEnvResolver = {
|
|
20953
|
+
/** Reads and parses a single environment variable. See {@link resolve}. */
|
|
20954
|
+
resolve,
|
|
20955
|
+
/** Reads and parses multiple environment variables in one pass. See {@link resolveAll}. */
|
|
20956
|
+
resolveAll
|
|
20957
|
+
};
|
|
20958
|
+
}
|
|
20959
|
+
});
|
|
20960
|
+
|
|
20681
20961
|
// src/funcs/running-scheduler.lambda.ts
|
|
20682
20962
|
var running_scheduler_lambda_exports = {};
|
|
20683
20963
|
__export(running_scheduler_lambda_exports, {
|
|
@@ -24203,14 +24483,16 @@ var DEFAULT_CONFIG = {
|
|
|
24203
24483
|
var import_client_ec2 = require("@aws-sdk/client-ec2");
|
|
24204
24484
|
var import_client_resource_groups_tagging_api = require("@aws-sdk/client-resource-groups-tagging-api");
|
|
24205
24485
|
var import_web_api = __toESM(require_dist5());
|
|
24206
|
-
var import_aws_lambda_secret_fetcher = __toESM(
|
|
24207
|
-
var import_strict_env_resolver2 = __toESM(
|
|
24486
|
+
var import_aws_lambda_secret_fetcher = __toESM(require_lib4());
|
|
24487
|
+
var import_strict_env_resolver2 = __toESM(require_lib5());
|
|
24208
24488
|
|
|
24209
24489
|
// src/funcs/running-scheduler-predicates.ts
|
|
24210
24490
|
var DEFAULT_RESOURCE_WAIT_LIMITS = {
|
|
24211
24491
|
maxLoopCount: 90,
|
|
24212
|
-
maxElapsedSeconds: 1800
|
|
24492
|
+
maxElapsedSeconds: 1800,
|
|
24493
|
+
statusChangeWaitSeconds: 20
|
|
24213
24494
|
};
|
|
24495
|
+
var DEFAULT_MAX_CONCURRENCY = 10;
|
|
24214
24496
|
var isDesiredStableState = (mode, currentState) => mode === "Start" && currentState === "running" || mode === "Stop" && currentState === "stopped";
|
|
24215
24497
|
var isTransitioningState = (mode, currentState) => mode === "Start" && currentState === "pending" || mode === "Stop" && (currentState === "stopping" || currentState === "shutting-down");
|
|
24216
24498
|
var getWaitAbortReason = (loopCount, startedAtMs, nowMs, limits) => {
|
|
@@ -24235,54 +24517,71 @@ var formatResourceWaitFailure = (reason, context) => {
|
|
|
24235
24517
|
};
|
|
24236
24518
|
|
|
24237
24519
|
// src/funcs/running-scheduler-wait-env.ts
|
|
24238
|
-
var import_strict_env_resolver = __toESM(
|
|
24520
|
+
var import_strict_env_resolver = __toESM(require_lib5());
|
|
24239
24521
|
|
|
24240
24522
|
// src/funcs/running-scheduler-wait-config.ts
|
|
24241
24523
|
var PROCESS_RESOURCE_MAX_LOOP_COUNT_ENV = "PROCESS_RESOURCE_MAX_LOOP_COUNT";
|
|
24242
24524
|
var PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV = "PROCESS_RESOURCE_MAX_ELAPSED_SECONDS";
|
|
24525
|
+
var PROCESS_RESOURCE_STATUS_CHANGE_WAIT_SECONDS_ENV = "PROCESS_RESOURCE_STATUS_CHANGE_WAIT_SECONDS";
|
|
24526
|
+
var PROCESS_RESOURCES_MAX_CONCURRENCY_ENV = "PROCESS_RESOURCES_MAX_CONCURRENCY";
|
|
24243
24527
|
|
|
24244
24528
|
// src/funcs/running-scheduler-wait-env.ts
|
|
24245
|
-
var assertPositiveEnvInt = (key, value) => {
|
|
24246
|
-
if (value <= 0) {
|
|
24247
|
-
const raw = process.env[key];
|
|
24248
|
-
throw new Error(`Invalid ${key}: must be a positive integer (got "${raw ?? ""}")`);
|
|
24249
|
-
}
|
|
24250
|
-
return value;
|
|
24251
|
-
};
|
|
24252
24529
|
var parseResourceWaitLimitsFromEnv = () => {
|
|
24253
24530
|
const parsed = import_strict_env_resolver.StrictEnvResolver.resolveAll({
|
|
24254
24531
|
[PROCESS_RESOURCE_MAX_LOOP_COUNT_ENV]: [
|
|
24255
|
-
import_strict_env_resolver.StrictEnvType.
|
|
24532
|
+
import_strict_env_resolver.StrictEnvType.PositiveInt,
|
|
24256
24533
|
{ default: DEFAULT_RESOURCE_WAIT_LIMITS.maxLoopCount }
|
|
24257
24534
|
],
|
|
24258
24535
|
[PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV]: [
|
|
24259
|
-
import_strict_env_resolver.StrictEnvType.
|
|
24536
|
+
import_strict_env_resolver.StrictEnvType.PositiveInt,
|
|
24260
24537
|
{ default: DEFAULT_RESOURCE_WAIT_LIMITS.maxElapsedSeconds }
|
|
24538
|
+
],
|
|
24539
|
+
[PROCESS_RESOURCE_STATUS_CHANGE_WAIT_SECONDS_ENV]: [
|
|
24540
|
+
import_strict_env_resolver.StrictEnvType.PositiveInt,
|
|
24541
|
+
{ default: DEFAULT_RESOURCE_WAIT_LIMITS.statusChangeWaitSeconds }
|
|
24261
24542
|
]
|
|
24262
24543
|
});
|
|
24263
24544
|
return {
|
|
24264
|
-
maxLoopCount:
|
|
24265
|
-
|
|
24266
|
-
|
|
24267
|
-
),
|
|
24268
|
-
maxElapsedSeconds: assertPositiveEnvInt(
|
|
24269
|
-
PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV,
|
|
24270
|
-
parsed[PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV]
|
|
24271
|
-
)
|
|
24545
|
+
maxLoopCount: parsed[PROCESS_RESOURCE_MAX_LOOP_COUNT_ENV],
|
|
24546
|
+
maxElapsedSeconds: parsed[PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV],
|
|
24547
|
+
statusChangeWaitSeconds: parsed[PROCESS_RESOURCE_STATUS_CHANGE_WAIT_SECONDS_ENV]
|
|
24272
24548
|
};
|
|
24273
24549
|
};
|
|
24550
|
+
var parseMaxConcurrencyFromEnv = () => {
|
|
24551
|
+
const parsed = import_strict_env_resolver.StrictEnvResolver.resolveAll({
|
|
24552
|
+
[PROCESS_RESOURCES_MAX_CONCURRENCY_ENV]: [
|
|
24553
|
+
import_strict_env_resolver.StrictEnvType.PositiveInt,
|
|
24554
|
+
{ default: DEFAULT_MAX_CONCURRENCY }
|
|
24555
|
+
]
|
|
24556
|
+
});
|
|
24557
|
+
return parsed[PROCESS_RESOURCES_MAX_CONCURRENCY_ENV];
|
|
24558
|
+
};
|
|
24274
24559
|
|
|
24275
24560
|
// src/funcs/running-scheduler.lambda.ts
|
|
24276
24561
|
var STATE_LIST = [
|
|
24277
24562
|
{ name: "RUNNING", emoji: "\u{1F606}", state: "running" },
|
|
24278
24563
|
{ name: "STOPPED", emoji: "\u{1F634}", state: "stopped" }
|
|
24279
24564
|
];
|
|
24280
|
-
var STATUS_CHANGE_WAIT_SECONDS = 20;
|
|
24281
24565
|
var SLACK_SECRET_FETCH_OPTIONS = {
|
|
24282
24566
|
timeoutMs: 3e3,
|
|
24283
24567
|
retries: 5,
|
|
24284
24568
|
baseBackoffMs: 300
|
|
24285
24569
|
};
|
|
24570
|
+
var isSlackSecret = (value) => {
|
|
24571
|
+
if (typeof value !== "object" || value === null) {
|
|
24572
|
+
return false;
|
|
24573
|
+
}
|
|
24574
|
+
if (!("token" in value) || !("channel" in value)) {
|
|
24575
|
+
return false;
|
|
24576
|
+
}
|
|
24577
|
+
if (typeof value.token !== "string" || value.token === "") {
|
|
24578
|
+
return false;
|
|
24579
|
+
}
|
|
24580
|
+
if (typeof value.channel !== "string" || value.channel === "") {
|
|
24581
|
+
return false;
|
|
24582
|
+
}
|
|
24583
|
+
return true;
|
|
24584
|
+
};
|
|
24286
24585
|
var getStateDisplay = (current) => {
|
|
24287
24586
|
const found = STATE_LIST.find((s) => s.state === current);
|
|
24288
24587
|
return found ? { emoji: found.emoji, name: found.name } : void 0;
|
|
@@ -24301,7 +24600,8 @@ var processOneResource = async (ctx, targetResource, params, resourceIndex, wait
|
|
|
24301
24600
|
account,
|
|
24302
24601
|
mode: params.Mode,
|
|
24303
24602
|
maxLoopCount: waitLimits.maxLoopCount,
|
|
24304
|
-
maxElapsedSeconds: waitLimits.maxElapsedSeconds
|
|
24603
|
+
maxElapsedSeconds: waitLimits.maxElapsedSeconds,
|
|
24604
|
+
statusChangeWaitSeconds: waitLimits.statusChangeWaitSeconds
|
|
24305
24605
|
});
|
|
24306
24606
|
const startedAtMs = await ctx.step(`${stepPrefix}-wait-started-at`, async () => Date.now());
|
|
24307
24607
|
let loopCount = 0;
|
|
@@ -24349,9 +24649,9 @@ var processOneResource = async (ctx, targetResource, params, resourceIndex, wait
|
|
|
24349
24649
|
});
|
|
24350
24650
|
ctx.logger.info("processOneResource: wait after start", {
|
|
24351
24651
|
identifier,
|
|
24352
|
-
seconds:
|
|
24652
|
+
seconds: waitLimits.statusChangeWaitSeconds
|
|
24353
24653
|
});
|
|
24354
|
-
await ctx.wait({ seconds:
|
|
24654
|
+
await ctx.wait({ seconds: waitLimits.statusChangeWaitSeconds });
|
|
24355
24655
|
loopCount += 1;
|
|
24356
24656
|
continue;
|
|
24357
24657
|
}
|
|
@@ -24363,9 +24663,9 @@ var processOneResource = async (ctx, targetResource, params, resourceIndex, wait
|
|
|
24363
24663
|
});
|
|
24364
24664
|
ctx.logger.info("processOneResource: wait after stop", {
|
|
24365
24665
|
identifier,
|
|
24366
|
-
seconds:
|
|
24666
|
+
seconds: waitLimits.statusChangeWaitSeconds
|
|
24367
24667
|
});
|
|
24368
|
-
await ctx.wait({ seconds:
|
|
24668
|
+
await ctx.wait({ seconds: waitLimits.statusChangeWaitSeconds });
|
|
24369
24669
|
loopCount += 1;
|
|
24370
24670
|
continue;
|
|
24371
24671
|
}
|
|
@@ -24376,9 +24676,9 @@ var processOneResource = async (ctx, targetResource, params, resourceIndex, wait
|
|
|
24376
24676
|
loopCount,
|
|
24377
24677
|
currentState,
|
|
24378
24678
|
mode,
|
|
24379
|
-
seconds:
|
|
24679
|
+
seconds: waitLimits.statusChangeWaitSeconds
|
|
24380
24680
|
});
|
|
24381
|
-
await ctx.wait({ seconds:
|
|
24681
|
+
await ctx.wait({ seconds: waitLimits.statusChangeWaitSeconds });
|
|
24382
24682
|
loopCount += 1;
|
|
24383
24683
|
continue;
|
|
24384
24684
|
}
|
|
@@ -24423,13 +24723,14 @@ var handler = withDurableExecution(async (event, ctx) => {
|
|
|
24423
24723
|
throw new Error("Invalid event: Params.TagKey, Params.TagValues, Params.Mode are required.");
|
|
24424
24724
|
}
|
|
24425
24725
|
const waitLimits = parseResourceWaitLimitsFromEnv();
|
|
24726
|
+
const maxConcurrency = parseMaxConcurrencyFromEnv();
|
|
24426
24727
|
const slackSecretName = import_strict_env_resolver2.StrictEnvResolver.resolve("SLACK_SECRET_NAME", import_strict_env_resolver2.StrictEnvType.String);
|
|
24427
24728
|
const slackSecretValue = await ctx.step("fetch-slack-secret", async () => {
|
|
24428
24729
|
ctx.logger.info("running-scheduler: fetching Slack secret", { secretName: slackSecretName });
|
|
24429
24730
|
return import_aws_lambda_secret_fetcher.secretFetcher.getSecretValue(slackSecretName, SLACK_SECRET_FETCH_OPTIONS);
|
|
24430
24731
|
});
|
|
24431
24732
|
ctx.logger.info("running-scheduler: Slack secret loaded");
|
|
24432
|
-
if (!slackSecretValue
|
|
24733
|
+
if (!isSlackSecret(slackSecretValue)) {
|
|
24433
24734
|
throw new Error("Slack secret must contain token and channel.");
|
|
24434
24735
|
}
|
|
24435
24736
|
const targetResources = await ctx.step("get-target-resources", async () => {
|
|
@@ -24473,7 +24774,7 @@ var handler = withDurableExecution(async (event, ctx) => {
|
|
|
24473
24774
|
});
|
|
24474
24775
|
ctx.logger.info("running-scheduler: starting parallel instance processing", {
|
|
24475
24776
|
count: targetResources.length,
|
|
24476
|
-
maxConcurrency
|
|
24777
|
+
maxConcurrency
|
|
24477
24778
|
});
|
|
24478
24779
|
const results = await ctx.map(
|
|
24479
24780
|
targetResources,
|
|
@@ -24521,7 +24822,7 @@ var handler = withDurableExecution(async (event, ctx) => {
|
|
|
24521
24822
|
return result;
|
|
24522
24823
|
});
|
|
24523
24824
|
},
|
|
24524
|
-
{ maxConcurrency
|
|
24825
|
+
{ maxConcurrency }
|
|
24525
24826
|
);
|
|
24526
24827
|
const resultList = Array.isArray(results) ? results : [];
|
|
24527
24828
|
ctx.logger.info("running-scheduler: completed", {
|