@go-to-k/cdkd 0.291.9 → 0.291.11
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/dist/{asg-provider-D9EJApN9.js → asg-provider-BDhrXE5Y.js} +2 -2
- package/dist/{asg-provider-D9EJApN9.js.map → asg-provider-BDhrXE5Y.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/{deploy-engine-TkL9nyq8.js → deploy-engine-D4svwrnK.js} +444 -106
- package/dist/deploy-engine-D4svwrnK.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{program-CY92mXcX.js → program-Buh8Yn40.js} +287 -197
- package/dist/{program-CY92mXcX.js.map → program-Buh8Yn40.js.map} +1 -1
- package/dist/{version-RzKz4JAW.js → version-CuVd0lOr.js} +2 -2
- package/dist/{version-RzKz4JAW.js.map → version-CuVd0lOr.js.map} +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-TkL9nyq8.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as displayAwsMessage, D as truncateCodePoints, E as isPasteableIdent, S as UNRENDERABLE, T as displaySafe, d as applyDefaultNameForFallback, f as explicitNamePropertyFor, g as looksLikeCdkdGeneratedName, h as getCurrentStackName, m as generateResourceNameWithFallback, n as getLogger, p as generateResourceName, r as isStdoutReservedForPayload, s as getLiveRenderer, v as withStackName, w as displayIdent, x as STACK_REF_MAX_CODE_POINTS, y as withoutGeneratedFallbackName } from "./logger-GASr9bAF.js";
|
|
2
2
|
import { a as setAssumedRoleCredentials, c as currentStackAwsScope, i as getPreAssumeEnvCredentials, l as runInStackAwsScope, n as awsClientDefaults, o as setPreAssumeEnvCredentials, r as getAssumedRoleCredentials, s as ambientRegion, t as SECOND_ROLE_PUBLISH_REFUSAL } from "./aws-client-defaults-jISU4PHo.js";
|
|
3
|
-
import { t as getCdkdVersion } from "./version-
|
|
3
|
+
import { t as getCdkdVersion } from "./version-CuVd0lOr.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
6
|
import { CreateBucketCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetBucketReplicationCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchKey, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -10,7 +10,7 @@ import { SQSClient } from "@aws-sdk/client-sqs";
|
|
|
10
10
|
import { PublishCommand, SNSClient } from "@aws-sdk/client-sns";
|
|
11
11
|
import { GetFunctionCommand, GetFunctionUrlConfigCommand, InvokeCommand, LambdaClient, UpdateFunctionConfigurationCommand, waitUntilFunctionActiveV2, waitUntilFunctionUpdatedV2 } from "@aws-sdk/client-lambda";
|
|
12
12
|
import { AssumeRoleCommand, GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
|
|
13
|
-
import { CreateSnapshotCommand, DescribeAvailabilityZonesCommand, DescribeImagesCommand, DescribeInstancesCommand, DescribeLaunchTemplatesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupsCommand, DescribeSnapshotsCommand, DescribeSubnetsCommand, DescribeVpcsCommand, DescribeVpnGatewaysCommand, EC2Client, ModifyInstanceAttributeCommand } from "@aws-sdk/client-ec2";
|
|
13
|
+
import { CreateSnapshotCommand, DeleteVolumeCommand, DescribeAvailabilityZonesCommand, DescribeImagesCommand, DescribeInstancesCommand, DescribeLaunchTemplatesCommand, DescribeRouteTablesCommand, DescribeSecurityGroupsCommand, DescribeSnapshotsCommand, DescribeSubnetsCommand, DescribeVolumesCommand, DescribeVpcsCommand, DescribeVpnGatewaysCommand, EC2Client, ModifyInstanceAttributeCommand } from "@aws-sdk/client-ec2";
|
|
14
14
|
import { DescribeTableCommand, DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
15
15
|
import { CloudFormationClient, CreateChangeSetCommand, DeleteStackCommand, DescribeChangeSetCommand, DescribeStacksCommand, DescribeTypeCommand, GetTemplateCommand, ListExportsCommand, waitUntilChangeSetCreateComplete } from "@aws-sdk/client-cloudformation";
|
|
16
16
|
import { APIGatewayClient, GetRestApiCommand } from "@aws-sdk/client-api-gateway";
|
|
@@ -35,9 +35,10 @@ import { GetHostedZoneCommand, ListHostedZonesByNameCommand, Route53Client } fro
|
|
|
35
35
|
import { DescribeListenersCommand, DescribeLoadBalancersCommand, ElasticLoadBalancingV2Client } from "@aws-sdk/client-elastic-load-balancing-v2";
|
|
36
36
|
import { KMSClient, ListAliasesCommand } from "@aws-sdk/client-kms";
|
|
37
37
|
import { readFile } from "fs/promises";
|
|
38
|
-
import { isAbsolute as isAbsolute$1, resolve as resolve$1 } from "path";
|
|
38
|
+
import { isAbsolute as isAbsolute$1, relative, resolve as resolve$1, sep } from "path";
|
|
39
39
|
import { CreateRepositoryCommand, DescribeImagesCommand as DescribeImagesCommand$1, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, PutImageTagMutabilityCommand } from "@aws-sdk/client-ecr";
|
|
40
40
|
import { inspect } from "node:util";
|
|
41
|
+
import { realpathSync as realpathSync$1 } from "fs";
|
|
41
42
|
import { hostname } from "os";
|
|
42
43
|
import graphlib from "graphlib";
|
|
43
44
|
import { DescribeDBClustersCommand, DescribeDBInstancesCommand, RDSClient } from "@aws-sdk/client-rds";
|
|
@@ -8135,6 +8136,39 @@ const DOCKER_CLIENT_ENV_KEYS = /* @__PURE__ */ new Set([
|
|
|
8135
8136
|
"GLIBC_TUNABLES",
|
|
8136
8137
|
"GCONV_PATH",
|
|
8137
8138
|
"BASH_ENV",
|
|
8139
|
+
"SHELLOPTS",
|
|
8140
|
+
"PS4",
|
|
8141
|
+
"PYTHONPATH",
|
|
8142
|
+
"PYTHONHOME",
|
|
8143
|
+
"PYTHONUSERBASE",
|
|
8144
|
+
"PYTHONPYCACHEPREFIX",
|
|
8145
|
+
"PYTHONPLATLIBDIR",
|
|
8146
|
+
"PYTHONWARNINGS",
|
|
8147
|
+
"BROWSER",
|
|
8148
|
+
"REQUESTS_CA_BUNDLE",
|
|
8149
|
+
"CURL_CA_BUNDLE",
|
|
8150
|
+
"HTTPLIB2_CA_CERTS",
|
|
8151
|
+
"SSLKEYLOGFILE",
|
|
8152
|
+
"GCE_METADATA_HOST",
|
|
8153
|
+
"GCE_METADATA_ROOT",
|
|
8154
|
+
"GCE_METADATA_IP",
|
|
8155
|
+
"NODE_OPTIONS",
|
|
8156
|
+
"NODE_PATH",
|
|
8157
|
+
"NODE_COMPILE_CACHE",
|
|
8158
|
+
"NODE_EXTRA_CA_CERTS",
|
|
8159
|
+
"NODE_TLS_REJECT_UNAUTHORIZED",
|
|
8160
|
+
"RUBYOPT",
|
|
8161
|
+
"RUBYLIB",
|
|
8162
|
+
"GEM_PATH",
|
|
8163
|
+
"GEM_HOME",
|
|
8164
|
+
"RUBYGEMS_GEMDEPS",
|
|
8165
|
+
"PERL5OPT",
|
|
8166
|
+
"PERL5LIB",
|
|
8167
|
+
"PERLLIB",
|
|
8168
|
+
"PERL5DB",
|
|
8169
|
+
"OPENSSL_CONF",
|
|
8170
|
+
"OPENSSL_MODULES",
|
|
8171
|
+
"OPENSSL_ENGINES",
|
|
8138
8172
|
"SSH_AUTH_SOCK",
|
|
8139
8173
|
"SSH_ASKPASS",
|
|
8140
8174
|
"SSH_ASKPASS_REQUIRE",
|
|
@@ -8203,10 +8237,16 @@ const DOCKER_CLIENT_ENV_KEYS_UPPER = new Set([...DOCKER_CLIENT_ENV_KEYS].map((k)
|
|
|
8203
8237
|
* family aws-sdk-go-v2 (and so `docker-credential-ecr-login`) honours — a
|
|
8204
8238
|
* secret named `AWS_ENDPOINT_URL_ECR` walks around the exact
|
|
8205
8239
|
* `AWS_ENDPOINT_URL` entry and redirects a request signed with the operator's
|
|
8206
|
-
* real credentials (#2186 round 4).
|
|
8207
|
-
*
|
|
8208
|
-
*
|
|
8209
|
-
*
|
|
8240
|
+
* real credentials (#2186 round 4). `CLOUDSDK_` is gcloud's (#3599): every
|
|
8241
|
+
* gcloud property is settable as `CLOUDSDK_<SECTION>_<NAME>`, which covers
|
|
8242
|
+
* its token host, API endpoint overrides, proxy, CA bundle and account, and
|
|
8243
|
+
* the `docker-credential-gcloud` wrapper execs `$CLOUDSDK_PYTHON
|
|
8244
|
+
* $CLOUDSDK_PYTHON_ARGS`. `BASH_FUNC_` is bash's exported-function family.
|
|
8245
|
+
* No plausible secret name collides with these, apart from the one listed in
|
|
8246
|
+
* {@link DOCKER_CLIENT_ENV_PREFIX_EXEMPTIONS}. Matched by prefix rather than
|
|
8247
|
+
* enumerated (issue #2183 review). `SSH_` was a prefix here and was demoted to
|
|
8248
|
+
* an EXACT enumeration in {@link DOCKER_CLIENT_ENV_KEYS} (#2186 review round
|
|
8249
|
+
* 3): the family is not
|
|
8210
8250
|
* uniformly dangerous and is not growing, while the prefix broke realistic,
|
|
8211
8251
|
* currently-working secrets (`SSH_PRIVATE_KEY`, GitLab CI's canonical
|
|
8212
8252
|
* deploy-key spelling). Exported so the test fence can assert the EXACT
|
|
@@ -8216,19 +8256,37 @@ const DOCKER_CLIENT_ENV_KEYS_UPPER = new Set([...DOCKER_CLIENT_ENV_KEYS].map((k)
|
|
|
8216
8256
|
const DOCKER_CLIENT_ENV_PREFIXES = [
|
|
8217
8257
|
"LD_",
|
|
8218
8258
|
"DYLD_",
|
|
8219
|
-
"AWS_ENDPOINT_URL_"
|
|
8259
|
+
"AWS_ENDPOINT_URL_",
|
|
8260
|
+
"CLOUDSDK_",
|
|
8261
|
+
"BASH_FUNC_"
|
|
8220
8262
|
];
|
|
8221
8263
|
/**
|
|
8264
|
+
* Exact names inside a {@link DOCKER_CLIENT_ENV_PREFIXES} family that are still
|
|
8265
|
+
* delivered. An entry must be a realistic secret name AND harmless to the
|
|
8266
|
+
* helper that reads it. `CLOUDSDK_AUTH_ACCESS_TOKEN` is gcloud's own variable
|
|
8267
|
+
* for a caller-supplied access token (#3599). Given to `docker-credential-gcloud`,
|
|
8268
|
+
* it only changes which token gcloud hands docker, with no network call of its
|
|
8269
|
+
* own. gcloud's `auth docker-helper` answers only for a registry in its own
|
|
8270
|
+
* supported list unless `artifacts/allow_unrecognized_registry` is set, and
|
|
8271
|
+
* that property, the token host, the universe domain and every other variable
|
|
8272
|
+
* that could redirect or weaken the exchange stay refused by the `CLOUDSDK_`
|
|
8273
|
+
* prefix. Stored upper-case and matched case-insensitively. Never exempt an
|
|
8274
|
+
* exact {@link DOCKER_CLIENT_ENV_KEYS} member: the exact list wins.
|
|
8275
|
+
*/
|
|
8276
|
+
const DOCKER_CLIENT_ENV_PREFIX_EXEMPTIONS = /* @__PURE__ */ new Set(["CLOUDSDK_AUTH_ACCESS_TOKEN"]);
|
|
8277
|
+
/**
|
|
8222
8278
|
* Is `key` the name of a var the docker client reads? Case-INSENSITIVE, because
|
|
8223
8279
|
* Windows environment lookups are, so a lowercase `docker_host` must be caught
|
|
8224
8280
|
* too (issue #2183). Matches the exact denylist OR a prefixed family — the
|
|
8225
8281
|
* prefix families are fail-closed on the whole prefix, so an unlisted `LD_*` /
|
|
8226
|
-
* `DYLD_*` / `AWS_ENDPOINT_URL_*`
|
|
8227
|
-
* rather than reaching the client
|
|
8282
|
+
* `DYLD_*` / `AWS_ENDPOINT_URL_*` / `CLOUDSDK_*` / `BASH_FUNC_*` secret is
|
|
8283
|
+
* dropped (with a rename warning) rather than reaching the client, except for
|
|
8284
|
+
* a name in {@link DOCKER_CLIENT_ENV_PREFIX_EXEMPTIONS}.
|
|
8228
8285
|
*/
|
|
8229
8286
|
function isDockerClientEnvKey(key) {
|
|
8230
8287
|
const upper = key.toUpperCase();
|
|
8231
8288
|
if (DOCKER_CLIENT_ENV_KEYS_UPPER.has(upper)) return true;
|
|
8289
|
+
if (DOCKER_CLIENT_ENV_PREFIX_EXEMPTIONS.has(upper)) return false;
|
|
8232
8290
|
return DOCKER_CLIENT_ENV_PREFIXES.some((prefix) => upper.startsWith(prefix));
|
|
8233
8291
|
}
|
|
8234
8292
|
/**
|
|
@@ -8325,6 +8383,70 @@ function dockerSpawnEnvWithSensitive(sensitiveEnv) {
|
|
|
8325
8383
|
}
|
|
8326
8384
|
return env;
|
|
8327
8385
|
}
|
|
8386
|
+
/**
|
|
8387
|
+
* The env var that accepts, while it is set, that a container client puts secret
|
|
8388
|
+
* VALUES on a process command line ({@link finchSecretArgvRefusal}). Only `1`
|
|
8389
|
+
* or `true` (any case) opts in.
|
|
8390
|
+
*/
|
|
8391
|
+
const ALLOW_SECRETS_ON_ARGV_ENV = "CDKD_ALLOW_SECRETS_ON_ARGV";
|
|
8392
|
+
/**
|
|
8393
|
+
* Is the container client finch running its Lima VM (macOS / Windows)? There
|
|
8394
|
+
* the value-less `-e KEY` that {@link partitionSensitiveEnv} emits does NOT
|
|
8395
|
+
* keep the value off argv (#3600): finch's `handleEnv` resolves each bare
|
|
8396
|
+
* `KEY` against its own environment and re-emits `-e KEY=<value>` on the argv
|
|
8397
|
+
* of the `limactl shell finch sudo -E nerdctl ...` child, and `--env-file` is
|
|
8398
|
+
* read on the host and re-emitted the same way (runfinch/finch
|
|
8399
|
+
* `cmd/finch/nerdctl_remote.go`, `handleEnv` / `handleEnvFile` and the
|
|
8400
|
+
* `envArgs` rebuild). Its `passedEnvs` list also puts `AWS_ACCESS_KEY_ID` /
|
|
8401
|
+
* `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN` from its environment on that
|
|
8402
|
+
* argv for every command. finch on Linux passes the argv to nerdctl unchanged
|
|
8403
|
+
* and is NOT matched.
|
|
8404
|
+
*
|
|
8405
|
+
* Detection is by the BASENAME of the resolved binary (`finch`, `finch.exe`,
|
|
8406
|
+
* any case): a wrapper script or a symlink under another name is not
|
|
8407
|
+
* recognised, and neither is Lima's own `nerdctl.lima`.
|
|
8408
|
+
*/
|
|
8409
|
+
function isFinchVmClient(cmd = getDockerCmd(), platform = process.platform) {
|
|
8410
|
+
if (platform !== "darwin" && platform !== "win32") return false;
|
|
8411
|
+
const base = (cmd.split(/[\\/]/).pop() ?? "").toLowerCase();
|
|
8412
|
+
return base === "finch" || base === "finch.exe";
|
|
8413
|
+
}
|
|
8414
|
+
function secretsOnArgvAllowed() {
|
|
8415
|
+
const v = process.env[ALLOW_SECRETS_ON_ARGV_ENV];
|
|
8416
|
+
return v !== void 0 && ["1", "true"].includes(v.trim().toLowerCase());
|
|
8417
|
+
}
|
|
8418
|
+
/**
|
|
8419
|
+
* The refusal text when template-sourced secrets (ECS `Secrets`, decrypted
|
|
8420
|
+
* SecureString parameters) would be forwarded under {@link isFinchVmClient},
|
|
8421
|
+
* or `undefined` when they may be forwarded: another client, no such secret,
|
|
8422
|
+
* or {@link ALLOW_SECRETS_ON_ARGV_ENV} set. The caller throws it in its own
|
|
8423
|
+
* error class BEFORE any `docker run`. The AWS credential set is not refused
|
|
8424
|
+
* here (it is warned about by {@link warnFinchArgvExposure}): finch already
|
|
8425
|
+
* puts the operator's own credentials on that argv by design, so refusing them
|
|
8426
|
+
* would only disable finch without changing the operator's exposure.
|
|
8427
|
+
* `subject` is caller-rendered context (e.g. the container name) and is not
|
|
8428
|
+
* sanitized here.
|
|
8429
|
+
*/
|
|
8430
|
+
function finchSecretArgvRefusal(secretNames, subject) {
|
|
8431
|
+
if (secretNames.length === 0 || !isFinchVmClient() || secretsOnArgvAllowed()) return void 0;
|
|
8432
|
+
return `${subject}: refusing to forward secret(s) ${[...new Set(secretNames)].map((k) => displayIdent(k)).join(", ")} under CDK_DOCKER=finch on macOS / Windows. finch turns each value-less '-e KEY' into '-e KEY=<value>' on the command line of the limactl process it starts, where other local processes can read the plaintext. Use a container client that keeps the value off the command line (Docker, podman, nerdctl, or finch on Linux), or set ${ALLOW_SECRETS_ON_ARGV_ENV}=1 to accept that exposure while it stays set.`;
|
|
8433
|
+
}
|
|
8434
|
+
/** Key sets already warned about by {@link warnFinchArgvExposure} in this process. */
|
|
8435
|
+
const finchArgvWarned = /* @__PURE__ */ new Set();
|
|
8436
|
+
/**
|
|
8437
|
+
* Warn, once per process per distinct key set, that under
|
|
8438
|
+
* {@link isFinchVmClient} the values of `keys` (the sensitive env about to be
|
|
8439
|
+
* forwarded as value-less `-e KEY`) reach the `limactl` command line. Names
|
|
8440
|
+
* only, never a value.
|
|
8441
|
+
*/
|
|
8442
|
+
function warnFinchArgvExposure(keys) {
|
|
8443
|
+
if (keys.length === 0 || !isFinchVmClient()) return;
|
|
8444
|
+
const names = [...new Set(keys)].sort();
|
|
8445
|
+
const latch = JSON.stringify(names);
|
|
8446
|
+
if (finchArgvWarned.has(latch)) return;
|
|
8447
|
+
finchArgvWarned.add(latch);
|
|
8448
|
+
getLogger().warn(`CDK_DOCKER=finch on macOS / Windows puts the values of ${names.map((k) => displayIdent(k)).join(", ")} on the command line of the limactl process it starts (finch turns a value-less '-e KEY' into '-e KEY=<value>'), where other local processes can read them. Docker, podman, nerdctl, or finch on Linux keep them off the command line.`);
|
|
8449
|
+
}
|
|
8328
8450
|
function mergeEnv(overrides) {
|
|
8329
8451
|
const merged = { ...process.env };
|
|
8330
8452
|
for (const [k, v] of Object.entries(overrides)) if (v === void 0) delete merged[k];
|
|
@@ -9436,6 +9558,62 @@ function dockerSubject(assetId) {
|
|
|
9436
9558
|
return assetId === void 0 ? "A Docker asset" : `Docker asset '${displaySafe(assetId)}'`;
|
|
9437
9559
|
}
|
|
9438
9560
|
/**
|
|
9561
|
+
* Refuse a Docker asset whose build context the bundled `cdk-local` engine
|
|
9562
|
+
* would take from outside the assembly (issue
|
|
9563
|
+
* [#3503](https://github.com/go-to-k/cdkd/issues/3503)).
|
|
9564
|
+
*
|
|
9565
|
+
* `cdk-local`'s own `buildDockerImage` spells the context (and the
|
|
9566
|
+
* `executable` arm's cwd) as `${cdkOutDir}/${source.directory}` with no
|
|
9567
|
+
* containment, and cdkd cannot hand it a pre-resolved path without changing
|
|
9568
|
+
* the image tag it derives from `source`. So this judges the path the engine
|
|
9569
|
+
* WILL open, before the engine runs, through the same
|
|
9570
|
+
* {@link resolveDockerContextDirectory} cdkd's own build uses.
|
|
9571
|
+
*
|
|
9572
|
+
* An ABSOLUTE value is judged by the engine's spelling, not honoured: the
|
|
9573
|
+
* concatenation folds it UNDER the manifest directory, so its leading
|
|
9574
|
+
* separators are stripped and the remainder takes the relative arm. That
|
|
9575
|
+
* keeps a symlink inside the assembly from becoming an escape through an
|
|
9576
|
+
* absolute spelling, and keeps the honour-and-warn arm, which describes a
|
|
9577
|
+
* build this engine never performs, out of these paths.
|
|
9578
|
+
*/
|
|
9579
|
+
function assertCdkLocalDockerContextContained(opts) {
|
|
9580
|
+
const { manifestDir, source, assetOutdir, wrapError } = opts;
|
|
9581
|
+
const directory = source.directory;
|
|
9582
|
+
if (!directory) return;
|
|
9583
|
+
const asEngineJoinsIt = isAbsolute$1(directory) ? directory.replace(/^[/\\]+/, "") : directory;
|
|
9584
|
+
resolveDockerContextDirectory({
|
|
9585
|
+
manifestDir,
|
|
9586
|
+
directory: asEngineJoinsIt,
|
|
9587
|
+
wrapError,
|
|
9588
|
+
assetOutdir,
|
|
9589
|
+
sink: source.executable && source.executable.length > 0 ? "run this asset's source.executable with that directory as its working directory" : "send that directory to docker build as the context of an image cdkd then runs locally"
|
|
9590
|
+
});
|
|
9591
|
+
const engineSpelled = `${manifestDir}/${asEngineJoinsIt}`;
|
|
9592
|
+
const physical = tryRealpathNative(engineSpelled);
|
|
9593
|
+
if (physical === void 0) return;
|
|
9594
|
+
const bound = tryRealpathNative(assetOutdir) ?? resolve$1(assetOutdir);
|
|
9595
|
+
const rel = relative(bound, physical);
|
|
9596
|
+
if (rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute$1(rel)) return;
|
|
9597
|
+
throw wrapError(`asset source.directory='${displaySafe(directory)}' which ` + renderAssemblyPathEscape({
|
|
9598
|
+
contained: false,
|
|
9599
|
+
escape: "symlink",
|
|
9600
|
+
path: engineSpelled,
|
|
9601
|
+
realPath: physical
|
|
9602
|
+
}, assetOutdir, "build it"));
|
|
9603
|
+
}
|
|
9604
|
+
/**
|
|
9605
|
+
* `realpath(3)`, or `undefined` when the path does not resolve. `.native` is
|
|
9606
|
+
* load-bearing: the JavaScript `realpathSync` folds `..` lexically first,
|
|
9607
|
+
* which is the very reading the check above exists to avoid.
|
|
9608
|
+
*/
|
|
9609
|
+
function tryRealpathNative(p) {
|
|
9610
|
+
try {
|
|
9611
|
+
return realpathSync$1.native(p);
|
|
9612
|
+
} catch {
|
|
9613
|
+
return;
|
|
9614
|
+
}
|
|
9615
|
+
}
|
|
9616
|
+
/**
|
|
9439
9617
|
* Build a Docker image from a CDK asset source. Returns the local image
|
|
9440
9618
|
* tag the caller should use for `docker tag` / `docker push` (publisher)
|
|
9441
9619
|
* or `docker run` (local-invoke).
|
|
@@ -34318,6 +34496,221 @@ function isTerminationProtectionPropagationError(message) {
|
|
|
34318
34496
|
return /may not be terminated|disableApiTermination/i.test(message);
|
|
34319
34497
|
}
|
|
34320
34498
|
|
|
34499
|
+
//#endregion
|
|
34500
|
+
//#region src/provisioning/wait-abandoned.ts
|
|
34501
|
+
/**
|
|
34502
|
+
* The "cdkd stopped watching an operation that may still be running" marker
|
|
34503
|
+
* (issue [#3236](https://github.com/go-to-k/cdkd/issues/3236), widened by
|
|
34504
|
+
* [#3253](https://github.com/go-to-k/cdkd/issues/3253)).
|
|
34505
|
+
*
|
|
34506
|
+
* `CloudControlWaitAbandonedError` is raised in `cloud-control-provider.ts`,
|
|
34507
|
+
* and `ec2-volume-delete.ts` marks its own abandoned volume-delete wait (issue
|
|
34508
|
+
* [#3455](https://github.com/go-to-k/cdkd/issues/3455)) with the same marker,
|
|
34509
|
+
* but FIVE call sites have to refuse it, and three of them live in other
|
|
34510
|
+
* files. Four are GOVERNED by
|
|
34511
|
+
* `tests/unit/provisioning/wait-abandoned-guard-population.test.ts`:
|
|
34512
|
+
*
|
|
34513
|
+
* - `cloud-control-provider.ts`'s own `delete()` catch,
|
|
34514
|
+
* - `deploy-engine.ts`'s replacement delete-then-CREATE arm,
|
|
34515
|
+
* - `deploy-engine.ts`'s template-removal delete arm,
|
|
34516
|
+
* - `destroy-runner.ts`'s per-resource delete loop.
|
|
34517
|
+
*
|
|
34518
|
+
* The FIFTH is `cleanupFailedCreateRemnant`, and it is outside that population
|
|
34519
|
+
* by construction rather than by oversight: its partner classifier is the
|
|
34520
|
+
* REGEX helper `isNotFoundMessage`, which carries no literal needles, so no
|
|
34521
|
+
* needle-driven scan can find it. It has a behavioural case instead, and it
|
|
34522
|
+
* needs the guard for a sharper reason than the other four — that regex is
|
|
34523
|
+
* case-INSENSITIVE, so `getaddrinfo ENOTFOUND ...` matches `/not\s*found/i`.
|
|
34524
|
+
*
|
|
34525
|
+
* Each decides "the resource is already gone" by SUBSTRING-matching the error
|
|
34526
|
+
* message, and each then DROPS the state row. An abandoned wait says the
|
|
34527
|
+
* opposite — the delete may still be running — and its message interpolates
|
|
34528
|
+
* the LOGICAL ID and the last-seen IDENTIFIER, both user- or template-chosen,
|
|
34529
|
+
* so a resource named `PageNotFound` satisfies those needles. Round 1 of
|
|
34530
|
+
* go-to-k/cdkd#3249 guarded the first site with a local `instanceof` and left
|
|
34531
|
+
* the other three, which is what this module exists to fix: ONE predicate the
|
|
34532
|
+
* three foreign sites can import without importing the provider.
|
|
34533
|
+
*
|
|
34534
|
+
* **A marker rather than an exported class + `instanceof`**, for the reason
|
|
34535
|
+
* `markNonRetryable` is one: `deploy-engine.ts` reaches the provider only
|
|
34536
|
+
* through `ProviderRegistry`, so importing the class there would add an edge
|
|
34537
|
+
* into an already dense ring, and a marker survives a wrapper that copies a
|
|
34538
|
+
* message without preserving a prototype.
|
|
34539
|
+
*
|
|
34540
|
+
* **NOT `markNonRetryable`**, though the mechanism is identical: a DELETE or
|
|
34541
|
+
* UPDATE abandonment is deliberately left RETRYABLE so the destroy runner can
|
|
34542
|
+
* re-issue an idempotent delete, and only CREATE is marked. Reusing that
|
|
34543
|
+
* marker would either make the guards miss DELETE — the case that drops a
|
|
34544
|
+
* state row — or make a DELETE non-retryable and lose the recovery.
|
|
34545
|
+
*
|
|
34546
|
+
* A LEAF: no imports, ever. All four consumers sit on the deploy engine ->
|
|
34547
|
+
* rollback executor -> registry -> provider ring, and a new edge into it from
|
|
34548
|
+
* any of them would close a cycle.
|
|
34549
|
+
*/
|
|
34550
|
+
const WAIT_ABANDONED_MARKER = Symbol.for("cdkd.waitAbandoned");
|
|
34551
|
+
/**
|
|
34552
|
+
* The `.cause` walk depth, the same bound `retryable-errors.ts` uses. cdkd
|
|
34553
|
+
* wraps errors, so the marked error is routinely one or two hops down.
|
|
34554
|
+
*/
|
|
34555
|
+
const MAX_CAUSE_CHAIN_DEPTH = 5;
|
|
34556
|
+
/**
|
|
34557
|
+
* Stamp an error as an abandoned wait.
|
|
34558
|
+
*
|
|
34559
|
+
* `E extends Error`, not `object`: the reader below is a PROTOTYPE-CHAIN
|
|
34560
|
+
* lookup, so marking a class or a shared prototype would silently mark every
|
|
34561
|
+
* instance of it. A non-extensible (frozen / sealed) error is returned
|
|
34562
|
+
* UNMARKED rather than allowed to throw — callers use this inline, so a
|
|
34563
|
+
* `TypeError` raised here would REPLACE the error the caller meant to raise.
|
|
34564
|
+
* Both rules are `markNonRetryable`'s, for its reasons.
|
|
34565
|
+
*/
|
|
34566
|
+
function markWaitAbandoned(error) {
|
|
34567
|
+
if (!Object.isExtensible(error)) return error;
|
|
34568
|
+
Object.defineProperty(error, WAIT_ABANDONED_MARKER, {
|
|
34569
|
+
value: true,
|
|
34570
|
+
enumerable: false,
|
|
34571
|
+
configurable: true,
|
|
34572
|
+
writable: false
|
|
34573
|
+
});
|
|
34574
|
+
return error;
|
|
34575
|
+
}
|
|
34576
|
+
/**
|
|
34577
|
+
* True when the error, or anything in its bounded `.cause` chain, was marked
|
|
34578
|
+
* by {@link markWaitAbandoned}.
|
|
34579
|
+
*
|
|
34580
|
+
* Every already-deleted classifier must test this BEFORE its substring match,
|
|
34581
|
+
* and must treat a true answer as "not already deleted" — i.e. re-throw. The
|
|
34582
|
+
* ordering is the whole point: a message-first guard has already decided by
|
|
34583
|
+
* the time it would consult the marker.
|
|
34584
|
+
*/
|
|
34585
|
+
function isWaitAbandonedError(error) {
|
|
34586
|
+
let current = error;
|
|
34587
|
+
for (let depth = 0; depth < MAX_CAUSE_CHAIN_DEPTH && current != null; depth++) {
|
|
34588
|
+
if (current[WAIT_ABANDONED_MARKER] === true) return true;
|
|
34589
|
+
current = current.cause;
|
|
34590
|
+
}
|
|
34591
|
+
return false;
|
|
34592
|
+
}
|
|
34593
|
+
|
|
34594
|
+
//#endregion
|
|
34595
|
+
//#region src/provisioning/ec2-volume-delete.ts
|
|
34596
|
+
/**
|
|
34597
|
+
* Delete a Cloud-Control-routed `AWS::EC2::Volume` with EC2 `DeleteVolume`
|
|
34598
|
+
* instead of Cloud Control `DeleteResource` (issue
|
|
34599
|
+
* [#3455](https://github.com/go-to-k/cdkd/issues/3455)).
|
|
34600
|
+
*
|
|
34601
|
+
* WHY THE CLOUD CONTROL DELETE IS BYPASSED. The registry delete handler for
|
|
34602
|
+
* this type holds `ec2:CreateSnapshot` (it implements CloudFormation's
|
|
34603
|
+
* `DeletionPolicy: Snapshot` itself), and it has been observed taking that
|
|
34604
|
+
* snapshot on a plain Cloud Control delete, which carries no deletion policy
|
|
34605
|
+
* at all: CloudTrail shows a `CreateSnapshot` from `cloudformation.amazonaws.com`
|
|
34606
|
+
* in the same second as cdkd's `DeleteResource`, then the handler's
|
|
34607
|
+
* `DeleteVolume` while that snapshot was still pending. Both times it
|
|
34608
|
+
* happened, the volume then sat in `deleting` for well over the 15-minute
|
|
34609
|
+
* wait, the delete failed, and the handler's snapshot was left behind
|
|
34610
|
+
* untracked. What makes the handler do it is not known, so the only fix that
|
|
34611
|
+
* does not depend on the trigger is not to invoke the handler. The EC2 call is
|
|
34612
|
+
* the same one the handler makes; cdkd's own final snapshot (when the policy
|
|
34613
|
+
* asks for one) is taken and waited to `completed` before this runs.
|
|
34614
|
+
*
|
|
34615
|
+
* CONTRACT, matching the Cloud Control delete it replaces:
|
|
34616
|
+
* - Resolves only once the volume is GONE (`InvalidVolume.NotFound`, or state
|
|
34617
|
+
* `deleted`) — returning means "deleted" to every caller.
|
|
34618
|
+
* - `DeleteVolume`'s own errors propagate UNCHANGED, so the caller's
|
|
34619
|
+
* existing already-deleted arm (region-checked) sees `InvalidVolume.NotFound`
|
|
34620
|
+
* exactly as it saw Cloud Control's `NotFound`, and every other failure
|
|
34621
|
+
* takes the caller's ordinary wrap.
|
|
34622
|
+
* - `IncorrectState` from `DeleteVolume` on a volume that is already
|
|
34623
|
+
* `deleting`, `deleted` or gone (a re-run after an interrupted destroy, or
|
|
34624
|
+
* a race with one) is waited on rather than failed. If the describe that
|
|
34625
|
+
* classifies it fails, the ORIGINAL `IncorrectState` is rethrown, carrying
|
|
34626
|
+
* the describe failure as its `cause`.
|
|
34627
|
+
* - A TRANSIENT describe failure during the wait (a throttle, a 5xx, a
|
|
34628
|
+
* socket error — `isTransientFailure`, the Cloud Control poll's own
|
|
34629
|
+
* classifier) is re-polled for up to `transientGraceMs` of unbroken
|
|
34630
|
+
* failures, the same grace the Cloud Control wait gives its status poll.
|
|
34631
|
+
* - Every way the wait can end without seeing the volume gone — the
|
|
34632
|
+
* `maxWaitMs` deadline, the grace running out, a non-transient describe
|
|
34633
|
+
* failure — throws an error marked as an abandoned wait
|
|
34634
|
+
* (`wait-abandoned.ts`). Every already-deleted classifier refuses that
|
|
34635
|
+
* marker structurally, and they must: they match SUBSTRINGS of a message
|
|
34636
|
+
* that interpolates the user's logical id (`PageNotFound` would match). So
|
|
34637
|
+
* the state record is KEPT, and the error stays retryable, like a Cloud
|
|
34638
|
+
* Control DELETE abandonment, because a re-issued `DeleteVolume` is
|
|
34639
|
+
* idempotent.
|
|
34640
|
+
*/
|
|
34641
|
+
async function deleteEc2VolumeDirect(client, volumeId, logicalId, opts) {
|
|
34642
|
+
const now = opts.now ?? Date.now;
|
|
34643
|
+
try {
|
|
34644
|
+
await client.send(new DeleteVolumeCommand({ VolumeId: volumeId }));
|
|
34645
|
+
opts.logger.debug(`DeleteVolume accepted for ${logicalId} (${volumeId})`);
|
|
34646
|
+
} catch (error) {
|
|
34647
|
+
if (errorName(error) !== "IncorrectState") throw error;
|
|
34648
|
+
let state;
|
|
34649
|
+
try {
|
|
34650
|
+
state = await readVolumeState(client, volumeId);
|
|
34651
|
+
} catch (describeError) {
|
|
34652
|
+
throw withCause(error, describeError);
|
|
34653
|
+
}
|
|
34654
|
+
if (!(state === void 0 || state === "deleted" || state === "deleting")) throw error;
|
|
34655
|
+
opts.logger.debug(`${logicalId} (${volumeId}) is already ${state ?? "gone"}; waiting for it to go`);
|
|
34656
|
+
}
|
|
34657
|
+
const abandoned = (reason, cause) => markWaitAbandoned(new ProvisioningError(`DELETE of ${logicalId} (${volumeId}) was accepted by EC2, but ${reason}. The state record is kept; re-run the command once the volume is gone.`, "AWS::EC2::Volume", logicalId, volumeId, cause instanceof Error ? cause : void 0));
|
|
34658
|
+
const deadline = now() + opts.maxWaitMs;
|
|
34659
|
+
let interval = 2e3;
|
|
34660
|
+
let outageStartedAt;
|
|
34661
|
+
for (;;) {
|
|
34662
|
+
let state;
|
|
34663
|
+
let polled = false;
|
|
34664
|
+
try {
|
|
34665
|
+
state = await readVolumeState(client, volumeId);
|
|
34666
|
+
polled = true;
|
|
34667
|
+
outageStartedAt = void 0;
|
|
34668
|
+
} catch (pollError) {
|
|
34669
|
+
if (!opts.isTransientFailure(pollError)) throw abandoned(`cdkd could not read the volume's state: ${describeAwsFailure(pollError).summary}`, pollError);
|
|
34670
|
+
outageStartedAt ??= now();
|
|
34671
|
+
const outageMs = now() - outageStartedAt;
|
|
34672
|
+
if (outageMs >= opts.transientGraceMs) throw abandoned(`cdkd could not read the volume's state for ${Math.round(outageMs / 1e3)}s: ` + describeAwsFailure(pollError).summary, pollError);
|
|
34673
|
+
opts.logger.debug(`Could not read ${logicalId} (${volumeId}) while waiting for its delete; re-polling: ${describeAwsFailure(pollError).detail}`);
|
|
34674
|
+
}
|
|
34675
|
+
if (polled && (state === void 0 || state === "deleted")) return;
|
|
34676
|
+
if (now() >= deadline) throw abandoned(polled ? `the volume was still '${state}' after ${Math.round(opts.maxWaitMs / 1e3)}s` : `cdkd could not read the volume's state before the ${Math.round(opts.maxWaitMs / 1e3)}s wait ran out`);
|
|
34677
|
+
if (polled) opts.logger.debug(`Waiting for ${logicalId} (${volumeId}) to be deleted (state: ${state})`);
|
|
34678
|
+
await opts.sleep(interval);
|
|
34679
|
+
interval = Math.min(Math.ceil(interval * 1.5), 1e4);
|
|
34680
|
+
}
|
|
34681
|
+
}
|
|
34682
|
+
/**
|
|
34683
|
+
* Rethrow `original` carrying `cause`, without replacing it: the caller's
|
|
34684
|
+
* classification (the idempotent arm, the retry classifier) must see the
|
|
34685
|
+
* `DeleteVolume` failure, not the describe that tried to explain it. A
|
|
34686
|
+
* non-extensible error, or one that already has a cause, is returned as-is.
|
|
34687
|
+
*/
|
|
34688
|
+
function withCause(original, cause) {
|
|
34689
|
+
if (typeof original === "object" && original !== null && Object.isExtensible(original) && original.cause === void 0) Object.defineProperty(original, "cause", {
|
|
34690
|
+
value: cause,
|
|
34691
|
+
configurable: true,
|
|
34692
|
+
writable: true,
|
|
34693
|
+
enumerable: false
|
|
34694
|
+
});
|
|
34695
|
+
return original;
|
|
34696
|
+
}
|
|
34697
|
+
/** The volume's state, or `undefined` when EC2 reports it does not exist. */
|
|
34698
|
+
async function readVolumeState(client, volumeId) {
|
|
34699
|
+
try {
|
|
34700
|
+
const volume = (await client.send(new DescribeVolumesCommand({ VolumeIds: [volumeId] }))).Volumes?.[0];
|
|
34701
|
+
return volume === void 0 ? void 0 : volume.State ?? "unknown";
|
|
34702
|
+
} catch (error) {
|
|
34703
|
+
if (errorName(error) === "InvalidVolume.NotFound") return void 0;
|
|
34704
|
+
throw error;
|
|
34705
|
+
}
|
|
34706
|
+
}
|
|
34707
|
+
/** The error's `name`, or `''` — never throws, whatever was rejected. */
|
|
34708
|
+
function errorName(error) {
|
|
34709
|
+
if (typeof error !== "object" || error === null) return "";
|
|
34710
|
+
const name = error.name;
|
|
34711
|
+
return typeof name === "string" ? name : "";
|
|
34712
|
+
}
|
|
34713
|
+
|
|
34321
34714
|
//#endregion
|
|
34322
34715
|
//#region src/deployment/delete-outcome.ts
|
|
34323
34716
|
/**
|
|
@@ -35188,99 +35581,6 @@ function slowCcOperationTimeoutMs(resourceType, operation) {
|
|
|
35188
35581
|
}
|
|
35189
35582
|
}
|
|
35190
35583
|
|
|
35191
|
-
//#endregion
|
|
35192
|
-
//#region src/provisioning/wait-abandoned.ts
|
|
35193
|
-
/**
|
|
35194
|
-
* The "cdkd stopped watching an operation that may still be running" marker
|
|
35195
|
-
* (issue [#3236](https://github.com/go-to-k/cdkd/issues/3236), widened by
|
|
35196
|
-
* [#3253](https://github.com/go-to-k/cdkd/issues/3253)).
|
|
35197
|
-
*
|
|
35198
|
-
* `CloudControlWaitAbandonedError` is raised in `cloud-control-provider.ts`,
|
|
35199
|
-
* but FIVE call sites have to refuse it, and three of them live in other
|
|
35200
|
-
* files. Four are GOVERNED by
|
|
35201
|
-
* `tests/unit/provisioning/wait-abandoned-guard-population.test.ts`:
|
|
35202
|
-
*
|
|
35203
|
-
* - `cloud-control-provider.ts`'s own `delete()` catch,
|
|
35204
|
-
* - `deploy-engine.ts`'s replacement delete-then-CREATE arm,
|
|
35205
|
-
* - `deploy-engine.ts`'s template-removal delete arm,
|
|
35206
|
-
* - `destroy-runner.ts`'s per-resource delete loop.
|
|
35207
|
-
*
|
|
35208
|
-
* The FIFTH is `cleanupFailedCreateRemnant`, and it is outside that population
|
|
35209
|
-
* by construction rather than by oversight: its partner classifier is the
|
|
35210
|
-
* REGEX helper `isNotFoundMessage`, which carries no literal needles, so no
|
|
35211
|
-
* needle-driven scan can find it. It has a behavioural case instead, and it
|
|
35212
|
-
* needs the guard for a sharper reason than the other four — that regex is
|
|
35213
|
-
* case-INSENSITIVE, so `getaddrinfo ENOTFOUND ...` matches `/not\s*found/i`.
|
|
35214
|
-
*
|
|
35215
|
-
* Each decides "the resource is already gone" by SUBSTRING-matching the error
|
|
35216
|
-
* message, and each then DROPS the state row. An abandoned wait says the
|
|
35217
|
-
* opposite — the delete may still be running — and its message interpolates
|
|
35218
|
-
* the LOGICAL ID and the last-seen IDENTIFIER, both user- or template-chosen,
|
|
35219
|
-
* so a resource named `PageNotFound` satisfies those needles. Round 1 of
|
|
35220
|
-
* go-to-k/cdkd#3249 guarded the first site with a local `instanceof` and left
|
|
35221
|
-
* the other three, which is what this module exists to fix: ONE predicate the
|
|
35222
|
-
* three foreign sites can import without importing the provider.
|
|
35223
|
-
*
|
|
35224
|
-
* **A marker rather than an exported class + `instanceof`**, for the reason
|
|
35225
|
-
* `markNonRetryable` is one: `deploy-engine.ts` reaches the provider only
|
|
35226
|
-
* through `ProviderRegistry`, so importing the class there would add an edge
|
|
35227
|
-
* into an already dense ring, and a marker survives a wrapper that copies a
|
|
35228
|
-
* message without preserving a prototype.
|
|
35229
|
-
*
|
|
35230
|
-
* **NOT `markNonRetryable`**, though the mechanism is identical: a DELETE or
|
|
35231
|
-
* UPDATE abandonment is deliberately left RETRYABLE so the destroy runner can
|
|
35232
|
-
* re-issue an idempotent delete, and only CREATE is marked. Reusing that
|
|
35233
|
-
* marker would either make the guards miss DELETE — the case that drops a
|
|
35234
|
-
* state row — or make a DELETE non-retryable and lose the recovery.
|
|
35235
|
-
*
|
|
35236
|
-
* A LEAF: no imports, ever. All four consumers sit on the deploy engine ->
|
|
35237
|
-
* rollback executor -> registry -> provider ring, and a new edge into it from
|
|
35238
|
-
* any of them would close a cycle.
|
|
35239
|
-
*/
|
|
35240
|
-
const WAIT_ABANDONED_MARKER = Symbol.for("cdkd.waitAbandoned");
|
|
35241
|
-
/**
|
|
35242
|
-
* The `.cause` walk depth, the same bound `retryable-errors.ts` uses. cdkd
|
|
35243
|
-
* wraps errors, so the marked error is routinely one or two hops down.
|
|
35244
|
-
*/
|
|
35245
|
-
const MAX_CAUSE_CHAIN_DEPTH = 5;
|
|
35246
|
-
/**
|
|
35247
|
-
* Stamp an error as an abandoned wait.
|
|
35248
|
-
*
|
|
35249
|
-
* `E extends Error`, not `object`: the reader below is a PROTOTYPE-CHAIN
|
|
35250
|
-
* lookup, so marking a class or a shared prototype would silently mark every
|
|
35251
|
-
* instance of it. A non-extensible (frozen / sealed) error is returned
|
|
35252
|
-
* UNMARKED rather than allowed to throw — callers use this inline, so a
|
|
35253
|
-
* `TypeError` raised here would REPLACE the error the caller meant to raise.
|
|
35254
|
-
* Both rules are `markNonRetryable`'s, for its reasons.
|
|
35255
|
-
*/
|
|
35256
|
-
function markWaitAbandoned(error) {
|
|
35257
|
-
if (!Object.isExtensible(error)) return error;
|
|
35258
|
-
Object.defineProperty(error, WAIT_ABANDONED_MARKER, {
|
|
35259
|
-
value: true,
|
|
35260
|
-
enumerable: false,
|
|
35261
|
-
configurable: true,
|
|
35262
|
-
writable: false
|
|
35263
|
-
});
|
|
35264
|
-
return error;
|
|
35265
|
-
}
|
|
35266
|
-
/**
|
|
35267
|
-
* True when the error, or anything in its bounded `.cause` chain, was marked
|
|
35268
|
-
* by {@link markWaitAbandoned}.
|
|
35269
|
-
*
|
|
35270
|
-
* Every already-deleted classifier must test this BEFORE its substring match,
|
|
35271
|
-
* and must treat a true answer as "not already deleted" — i.e. re-throw. The
|
|
35272
|
-
* ordering is the whole point: a message-first guard has already decided by
|
|
35273
|
-
* the time it would consult the marker.
|
|
35274
|
-
*/
|
|
35275
|
-
function isWaitAbandonedError(error) {
|
|
35276
|
-
let current = error;
|
|
35277
|
-
for (let depth = 0; depth < MAX_CAUSE_CHAIN_DEPTH && current != null; depth++) {
|
|
35278
|
-
if (current[WAIT_ABANDONED_MARKER] === true) return true;
|
|
35279
|
-
current = current.cause;
|
|
35280
|
-
}
|
|
35281
|
-
return false;
|
|
35282
|
-
}
|
|
35283
|
-
|
|
35284
35584
|
//#endregion
|
|
35285
35585
|
//#region src/provisioning/cloud-control-provider.ts
|
|
35286
35586
|
/**
|
|
@@ -35961,7 +36261,7 @@ var CloudControlProvider = class {
|
|
|
35961
36261
|
const indeterminateGuard = await this.confirmDeleteTargetIdentity(logicalId, resourceType, physicalId, context);
|
|
35962
36262
|
if (context?.removeProtection === true && resourceType === "AWS::AutoScaling::AutoScalingGroup") {
|
|
35963
36263
|
this.logger.debug(`Delegating protected AutoScalingGroup ${logicalId} delete to the SDK ASGProvider (Cloud Control cannot force-delete a protected ASG)`);
|
|
35964
|
-
const { ASGProvider } = await import("./asg-provider-
|
|
36264
|
+
const { ASGProvider } = await import("./asg-provider-BDhrXE5Y.js").then((n) => n.n);
|
|
35965
36265
|
const asgProvider = new ASGProvider();
|
|
35966
36266
|
return withIndeterminateGuard(await asgProvider.delete(logicalId, physicalId, resourceType, _properties, context), indeterminateGuard);
|
|
35967
36267
|
}
|
|
@@ -35971,8 +36271,20 @@ var CloudControlProvider = class {
|
|
|
35971
36271
|
const protectionEntry = ccProtectionProperty(resourceType);
|
|
35972
36272
|
if (protectionEntry) await this.disableCcProtection(logicalId, physicalId, resourceType, protectionEntry);
|
|
35973
36273
|
}
|
|
36274
|
+
const volumeEc2Client = resourceType === "AWS::EC2::Volume" ? await this.ec2ClientInCcRegion(resourceType, logicalId, physicalId) : void 0;
|
|
35974
36275
|
const maxAttempts = isProtectedEc2Instance ? 5 : 1;
|
|
35975
36276
|
for (let attempt = 1;; attempt++) try {
|
|
36277
|
+
if (volumeEc2Client !== void 0) {
|
|
36278
|
+
await deleteEc2VolumeDirect(volumeEc2Client, physicalId, logicalId, {
|
|
36279
|
+
logger: this.logger,
|
|
36280
|
+
sleep: (ms) => this.sleep(ms),
|
|
36281
|
+
maxWaitMs: Math.max(this.MAX_WAIT_TIME_MS, slowCcOperationTimeoutMs(resourceType, "DELETE")),
|
|
36282
|
+
transientGraceMs: this.POLL_TRANSIENT_GRACE_MS,
|
|
36283
|
+
isTransientFailure: isTransientPollFailure
|
|
36284
|
+
});
|
|
36285
|
+
this.logger.debug(`Deleted resource ${logicalId}`);
|
|
36286
|
+
return withIndeterminateGuard(void 0, indeterminateGuard);
|
|
36287
|
+
}
|
|
35976
36288
|
const deleteResponse = await this.cloudControlClient.send(new DeleteResourceCommand({
|
|
35977
36289
|
TypeName: resourceType,
|
|
35978
36290
|
Identifier: physicalId
|
|
@@ -36000,6 +36312,32 @@ var CloudControlProvider = class {
|
|
|
36000
36312
|
}
|
|
36001
36313
|
}
|
|
36002
36314
|
/**
|
|
36315
|
+
* The EC2 client for a delete this provider issues through EC2 instead of
|
|
36316
|
+
* Cloud Control (issue #3455), refused unless it targets the SAME region as
|
|
36317
|
+
* the Cloud Control client the region pre-flight vetted.
|
|
36318
|
+
*
|
|
36319
|
+
* `getAwsClients()` is read at call time while `cloudControlClient` was
|
|
36320
|
+
* captured at construction, so the two are not the same object and could
|
|
36321
|
+
* name different regions. The comparison is load-bearing rather than
|
|
36322
|
+
* defensive: a volume id is region-scoped, so a wrong-region `DeleteVolume`
|
|
36323
|
+
* answers `InvalidVolume.NotFound`, which the caller's already-deleted arm
|
|
36324
|
+
* would take as success and drop the record of a live volume.
|
|
36325
|
+
*/
|
|
36326
|
+
async ec2ClientInCcRegion(resourceType, logicalId, physicalId) {
|
|
36327
|
+
const ec2 = getAwsClients().ec2;
|
|
36328
|
+
const resolve = async (client) => {
|
|
36329
|
+
try {
|
|
36330
|
+
return canonicalizeRegion((await client.config.region())?.trim());
|
|
36331
|
+
} catch {
|
|
36332
|
+
return;
|
|
36333
|
+
}
|
|
36334
|
+
};
|
|
36335
|
+
const ccRegion = await resolve(this.cloudControlClient);
|
|
36336
|
+
const ec2Region = await resolve(ec2);
|
|
36337
|
+
if (ccRegion === void 0 || ccRegion === "" || ccRegion !== ec2Region) throw markNonRetryable(new ProvisioningError(`Refusing to delete ${logicalId} (${resourceType}, ${physicalId}): the EC2 client targets region '${ec2Region ?? "unknown"}' but the Cloud Control client targets '${ccRegion ?? "unknown"}', so cdkd cannot show the delete would reach this volume. Re-run with --region set to the stack's region.`, resourceType, logicalId, physicalId));
|
|
36338
|
+
return ec2;
|
|
36339
|
+
}
|
|
36340
|
+
/**
|
|
36003
36341
|
* Refuse a Cloud Control call whose target region cannot be shown to be the
|
|
36004
36342
|
* one the state record was written in (issue #2301).
|
|
36005
36343
|
*
|
|
@@ -51280,5 +51618,5 @@ var DeployEngine = class DeployEngine {
|
|
|
51280
51618
|
};
|
|
51281
51619
|
|
|
51282
51620
|
//#endregion
|
|
51283
|
-
export { yellow as $,
|
|
51284
|
-
//# sourceMappingURL=deploy-engine-
|
|
51621
|
+
export { yellow as $, isMarkedNonRetryable as $a, injectiveKeyPrefix as $i, repairMalformedResourcePropertiesForReadOnly as $n, AssetModeResolver as $r, requireConfigObject as $t, createPreDeleteFinalSnapshot as A, DependencyError as Aa, AssetManifestLoader as Ai, malformedOutputsRefusalMessage as An, buildForceUnlockCommand as Ar, carriesDynamicReference as At, isStatefulRecreateTargetSync as B, ProvisioningError as Ba, resolveStateBucketWithDefault as Bi, refuseMalformedOrphansForDestroy as Bn, importableOutputKeys as Br, isSettledInstanceState as Bt, namesAnyDeclaredParameter as C, resetAwsClients as Ca, partitionSensitiveEnv as Ci, isReadableResourceEntry as Cn, recoverMaskedOutput as Cr, UNSPECIFIED_SKIP_REASON as Ct, PRE_DELETE_SNAPSHOT_TYPES as D, CdkdError as Da, warnFinchArgvExposure as Di, malformedOrphanRecordsWarning as Dn, S3StateBackend as Dr, disableInstanceApiTermination as Dt, ATOMIC_FINAL_SNAPSHOT_TYPES as E, AssetError as Ea, runDockerStreaming as Ei, malformedLocalOutputsWarning as En, LockManager as Er, isWaitAbandonedError as Et, extractDeploymentEventError as F, LocalStartServiceError as Fa, getLegacyStateBucketName as Fi, malformedResourcesWarning as Fn, DEFAULT_STATE_PREFIX as Fr, isImpossibleEmptyStoredAttribute as Ft, toCfnInteger as G, StateError as Ga, ssmResolvedValueType as Gi, refuseMalformedResourceEntries as Gn, shouldRetainResource as Gr, coerceCfnBoolean as Gt, WARM_THROUGHPUT_MEMBERS as H, ResourceUpdateNotSupportedError as Ha, resolveUseCdkBootstrapAssets as Hi, refuseMalformedOutputs as Hn, isReadableBag as Hr, normalizeAwsTagsToCfn as Ht, makeSiblingClaimReader as I, LockError as Ia, resolveApp as Ii, malformedStateRefusalMessage as In, exportNamesCarriedFrom as Ir, isNamelessDynamicReferenceError as It, bold as J, isCdkdError as Ja, MIGRATE_TMP_PREFIX as Ji, refuseMalformedResourcesForDestroy as Jn, WorkGraph as Jr, configIntegerRefusal as Jt, toFiniteNumber as K, SynthesisError as Ka, CFN_TEMPLATE_BODY_LIMIT as Ki, refuseMalformedResourceEntriesForOrphan as Kn, AssetPublisher as Kr, coerceCfnInteger as Kt, planOrphanAdoption as L, MalformedProducerRecordRefusalError as La, resolveAutoAssetStorage as Li, refuseDivergentRecordRegionForDestroy as Ln, hasReadableExportSet as Lr, isUnboundTemplateParameter as Lt, refusesFinalSnapshot as M, DynamicReferenceRegionAmbiguousError as Ma, Synthesizer as Mi, malformedRenderedContainersWarning as Mn, forceQuitRecoveryClause as Mr, cfnRefValueFromPhysicalId as Mt, unsupportedFinalSnapshotError as N, IntrinsicResolutionRefusalError as Na, synthesisStatusMessage as Ni, malformedResourceEntriesWarning as Nn, formatLockExpiry as Nr, coerceParameterTypedValue as Nt, buildFinalSnapshotIdentifier as O, ConfigError as Oa, escapeRegExp$1 as Oi, malformedOrphansRefusalMessage as On, rebuildClientForBucketRegion as Or, isTerminationProtectionPropagationError as Ot, makeCanonicalizePropertiesFn as P, LocalInvokeBuildError as Pa, getDefaultStateBucketName as Pi, malformedResourcePropertiesWarning as Pn, CUSTOM_RESOURCE_RESPONSE_PREFIX as Pr, getAccountInfo as Pt, red as Q, shellQuote as Qa, injectiveKey as Qi, repairMalformedResourceEntriesForReadOnly as Qn, rewriteTemplateAssetReferences as Qr, requireConfigArray as Qt, MULTI_REGION_RECREATE_BLOCKED_TYPES as R, NestedStackChildDirectDestroyError as Ra, resolveCaptureObservedState as Ri, refuseMalformedNestedChildOutputs as Rn, hasReasonlessBaselineRefusal as Rr, parameterTypeMayLoseSecretIdentity as Rt, computeParameterTaint as S, getAwsClients as Sa, isMalformedEnvKey as Si, hasReadableResources as Sn, recordMaskOnlyValue as Sr, slowCcOperationTimeoutMs as St, resourcesNamingDeclaredParameter as T, setAwsClients as Ta, runDockerForeground as Ti, malformedExportSourceWarning as Tn, scrubResourceRecord as Tr, deleteSkipReason as Tt, coerceWarmThroughput as U, StackHasActiveImportsError as Ua, stateBucketExistenceConfirmed as Ui, refuseMalformedOutputsForDestroy as Un, orphansAfterRollback as Ur, resolveExplicitPhysicalId as Ut, renderStatefulReason as V, ResourceTimeoutError as Va, resolveStateBucketWithDefaultAndSource as Vi, refuseMalformedOrphansForOrphan as Vn, importableOutputs as Vr, WAFv2WebACLProvider as Vt, isWarmThroughputDecrease as W, StackTerminationProtectionError as Wa, warnDeprecatedNoPrefixCliFlag as Wi, refuseMalformedResourceAttributesForOrphan as Wn, orphansCarriedFrom as Wr, assertRegionMatch as Wt, gray as X, withErrorHandling as Xa, uploadCfnTemplate as Xi, repairMalformedOrphansForReadOnly as Xn, createAssetRedirectResolver as Xr, readConfigString as Xt, cyan as Y, normalizeAwsError as Ya, findLargeInlineResources as Yi, refuseMalformedState as Yn, buildAssetRedirectMap as Yr, configStringRefusal as Yt, green as Z, pasteableCommand as Za, CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION as Zi, repairMalformedOutputsForReadOnly as Zn, loadPublishableAssetManifest as Zr, replayWarn as Zt, bagHoldsSecretExpression as _, renderAssemblyPathEscape as _a, dockerSpawnEnvWithSensitive as _i, UNREADABLE_ORPHANS_CONTAINER_ROW as _n, identityKeyFor as _r, endCommandInterruptScope as _t, DeploymentEventsStore as a, canonicalizeRegion as aa, parseBootstrapMarker as ai, s3BucketDomainName as an, retryClassificationText as ao, describeTypeWithThrottleRetry as ar, secretBearingStateKeyWarning as at, mergeNoChangeOutputs as b, resolveBucketRegion as ba, getDockerCmd as bi, hasReadableOrphans as bn, maskSecretsInError as br, startInterruptWatch as bt, replayFailedOperations as c, failedStageNote as ca, validateContainerRepoName as ci, s3BucketWebsiteUrl as cn, __exportAll as co, TemplateParser as cr, IAMRoleProvider as ct, updatePartialMessage as d, defineOwnKey as da, buildDockerImage as di, applyCallerIdentityCredentials as dn, STATE_SOURCED_READBACK_RULES as dr, ProviderRegistry as dt, producerCoordinateKey as ea, BOOTSTRAP_MARKER_PREFIX as ei, requireConfigString as en, isRetryableTransientError as eo, repairMalformedResourcesForReadOnly as er, collectDeclaredOutputNames as et, updatePartialReason as f, hasOwnKey as fa, resolveDockerContextDirectory as fi, callerEnvCredentials as fn, TEMPLATE_SOURCED_RULES as fr, wouldReturnToSdkProvider as ft, computeImplicitDeleteEdges as g, absoluteAssemblyPathEscape as ga, describeDockerFailure as gi, STATE_RESOURCES_MALFORMED as gn, errorCauseChain as gr, beginCommandInterruptScope as gt, IMPLICIT_DELETE_DEPENDENCIES as h, ownValue as ha, describeDockerExecFailure as hi, INTRINSIC_KEYS as hn, dynamicReferenceTokens as hr, maskerOrIdentity as ht, DeploymentEventsReader as i, PARTITION_TABLE as ia, isCrossRegionRedirect as ii, s3BucketArn as in, markRedactedCause as io, findSilentDropProperties as ir, secretBearing as it, isFinalSnapshotError as j, DeployCancelledError as ja, getDockerImageBySourceHash as ji, malformedOutputsWarning as jn, buildLockContentionMessage as jr, carriesFetchableDynamicReference as jt, ccRoutedFinalSnapshotError as k, CrossAccountSecretRefusalError as ka, stripControlChars as ki, malformedOrphansWarning as kn, UNREPRODUCIBLE_LOCK_CLAUSE as kr, IntrinsicFunctionResolver as kt, replayRollback as l, globMatches as la, buildDenyExternalAccessPolicy as li, applyRoleArnIfSet as ln, STATE_SOURCED_BASELINE_RULES as lr, collectInlinePolicyNamesManagedBySiblings as lt, maskingRetryLogger as m, nullPrototypeRecord as ma, describeDockerCapturedOutput as mi, DiffCalculator as mn, createSecretMasker as mr, maskDeep as mt, DEFAULT_RESOURCE_WARN_AFTER_MS as n, decodeListingKey as na, ensureAssetStorage as ni, producerRegionsFromState as nn, isTransientServerError as no, safeStackName as nr, exportAliasCollisionScrubWarning as nt, planFailedOps as o, derivePartitionAndUrlSuffix as oa, readBootstrapMarkerBody as oi, s3BucketDualStackDomainName as on, describeAwsFailure as oo, withRetry as or, secretSafeKeyDisplay as ot, withResourceDeadline as p, hasPlainPrototype$1 as pa, warnManifestExecutable as pi, envCredentialsAreAssumedRole as pn, carriesSecretMask as pr, createMaskedRetryLogger as pt, formatResourceLine as q, formatError as qa, CFN_TEMPLATE_URL_LIMIT as qi, refuseMalformedResourcePropertiesForOrphan as qn, stringifyValue as qr, configBooleanRefusal as qt, DeployEngine as r, expectedOwnerParam as ra, getBootstrapMarkerKey as ri, withSharedDrainBudget as rn, markNonRetryable as ro, findActionableSilentDrops as rr, isExportAliasCollision as rt, planRollback as s, AssemblyReader as sa, validateAssetBucketName as si, s3BucketRegionalDomainName as sn, safeStringify$1 as so, DagBuilder as sr, getCurrentResourceSecrets as st, DEFAULT_RESOURCE_TIMEOUT_MS as t, producerRecordKey as ta, assertAssetBucketRegion as ti, classifyReplaySecretRegion as tn, isThrottlingError as to, safeRegion as tr, collectPublishedOutputNames as tt, resolveReplacementOldType as u, processStackMessages as ua, assertCdkLocalDockerContextContained as ui, AWS_CREDENTIAL_ENV_KEYS as un, STATE_SOURCED_CROSS_GENERATION_RULES as ur, clearOnUpdateRemoval as ut, isSecretBearingReferenceString as v, resolveAssemblyPath as va, finchSecretArgvRefusal as vi, UNREADABLE_RESOURCES_MAP_ROW as vn, isSingleDynamicReferenceToken as vr, interruptWatchListenerCount as vt, reachableDivergentParameters as w, runWithStackAwsClients as wa, redactDockerArgvValues as wi, malformedExportNamesWarning as wn, redactSecretsForState as wr, deleteIndeterminateGuards as wt, bindingSkippedOutputs as x, AwsClients as xa, isDockerClientEnvKey as xi, hasReadableOutputs as xn, maskSecretsInText as xr, CloudControlProvider as xt, keptWholeReasonText as y, clearBucketRegionCache as ya, formatDockerLoginError as yi, displayLogicalId as yn, markSameGenerationBag as yr, isInterruptedWaitError as yt, isStatefulRecreateTargetForReplace as z, PartialFailureError as za, resolveSkipPrefix as zi, refuseMalformedOrphans as zn, hasUnverifiableParameterRefusal as zr, refStateLookupFromResource as zt };
|
|
51622
|
+
//# sourceMappingURL=deploy-engine-D4svwrnK.js.map
|