@go-to-k/cdkd 0.250.0 → 0.252.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/dist/{asg-provider-er3xGYRo.js → asg-provider-BzBdoSYp.js} +2 -2
- package/dist/{asg-provider-er3xGYRo.js.map → asg-provider-BzBdoSYp.js.map} +1 -1
- package/dist/cli.js +534 -125
- package/dist/cli.js.map +1 -1
- package/dist/{deploy-engine-D-gBbMiZ.js → deploy-engine-IkhnJXWR.js} +25 -17
- package/dist/deploy-engine-IkhnJXWR.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/deploy-engine-D-gBbMiZ.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as
|
|
2
|
+
import { $ as BOOTSTRAP_MARKER_PREFIX, A as refStateLookupFromResource, At as AssemblyReader, B as TemplateParser, C as ProviderRegistry, Ct as warnDeprecatedNoPrefixCliFlag, D as isTerminationProtectionPropagationError, Dt as findLargeInlineResources, E as disableInstanceApiTermination, Et as MIGRATE_TMP_PREFIX, F as resolveExplicitPhysicalId, Ft as resetAwsClients, G as AssetPublisher, Gt as MissingCdkCliError, H as S3StateBackend, Ht as LocalMigrateError, I as assertRegionMatch, It as setAwsClients, J as buildAssetRedirectMap, Jt as ProvisioningError, K as stringifyValue, Kt as NestedStackChildDirectDestroyError, L as applyRoleArnIfSet, M as CDK_PATH_TAG, Mt as resolveBucketRegion, N as matchesCdkPath, Nt as AwsClients, O as IntrinsicFunctionResolver, Ot as uploadCfnTemplate, P as normalizeAwsTagsToCfn, Pt as getAwsClients, Q as AssetModeResolver, Qt as StackTerminationProtectionError, R as DiffCalculator, Rt as CdkdError, S as collectInlinePolicyNamesManagedBySiblings, St as resolveUseCdkBootstrapAssets, T as CloudControlProvider, Tt as CFN_TEMPLATE_URL_LIMIT, U as rebuildClientForBucketRegion, Ut as LocalStartServiceError, V as LockManager, Vt as LocalInvokeBuildError$1, W as shouldRetainResource, X as loadPublishableAssetManifest, Xt as ResourceUpdateNotSupportedError, Y as createAssetRedirectResolver, Yt as ResourceTimeoutError, Z as rewriteTemplateAssetReferences, Zt as StackHasActiveImportsError, _ as gray, _t as resolveAutoAssetStorage, a as withRetry, at as buildDockerImage, b as yellow, bt as resolveStateBucketWithDefault, c as IMPLICIT_DELETE_DEPENDENCIES, ct as runDockerForeground, d as MULTI_REGION_RECREATE_BLOCKED_TYPES, dt as getDockerImageBySourceHash, et as ensureAssetStorage, f as isStatefulRecreateTargetSync, ft as Synthesizer, g as cyan, gt as resolveApp, h as bold, ht as getLegacyStateBucketName, i as withResourceDeadline, in as withErrorHandling, it as validateContainerRepoName, j as WAFv2WebACLProvider, k as cfnRefValueFromPhysicalId, kt as expectedOwnerParam, l as computeImplicitDeleteEdges, lt as runDockerStreaming, m as formatResourceLine, mt as getDefaultStateBucketName, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as parseBootstrapMarker, o as isRetryableTransientError, ot as formatDockerLoginError, p as renderStatefulReason, pt as synthesisStatusMessage, q as WorkGraph, qt as PartialFailureError, r as DeployEngine, rn as normalizeAwsError, rt as validateAssetBucketName, s as isThrottlingError, st as getDockerCmd, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as getBootstrapMarkerKey, u as extractDeploymentEventError, ut as AssetManifestLoader, v as green, vt as resolveCaptureObservedState, w as findActionableSilentDrops, wt as CFN_TEMPLATE_BODY_LIMIT, x as IAMRoleProvider, xt as resolveStateBucketWithDefaultAndSource, y as red, yt as resolveSkipPrefix, z as DagBuilder } from "./deploy-engine-IkhnJXWR.js";
|
|
3
3
|
import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
|
|
4
|
-
import { t as ASGProvider } from "./asg-provider-
|
|
4
|
+
import { t as ASGProvider } from "./asg-provider-BzBdoSYp.js";
|
|
5
5
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
6
6
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
7
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListBucketsCommand, ListDirectoryBucketsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
@@ -1888,7 +1888,7 @@ const FLUSH_INTERVAL_MS = 2e3;
|
|
|
1888
1888
|
const FLUSH_EVENT_THRESHOLD = 50;
|
|
1889
1889
|
/** Build-time cdkd version, with a dev fallback for non-built contexts. */
|
|
1890
1890
|
function getCdkdVersion() {
|
|
1891
|
-
return "0.
|
|
1891
|
+
return "0.252.0";
|
|
1892
1892
|
}
|
|
1893
1893
|
/**
|
|
1894
1894
|
* Generate a time-sortable unique run id, e.g.
|
|
@@ -25221,6 +25221,155 @@ var RDSDBProxyTargetGroupProvider = class {
|
|
|
25221
25221
|
}
|
|
25222
25222
|
};
|
|
25223
25223
|
|
|
25224
|
+
//#endregion
|
|
25225
|
+
//#region src/provisioning/import-tag-walk.ts
|
|
25226
|
+
/**
|
|
25227
|
+
* Shared, throttle-tolerant `aws:cdk:path` tag walk for `ResourceProvider.import`.
|
|
25228
|
+
*
|
|
25229
|
+
* Providers that adopt a resource without an explicit name property fall back
|
|
25230
|
+
* to step 3 of the lookup order documented in `./import-helpers.ts`: enumerate
|
|
25231
|
+
* the service's `List*`/`Describe*` pages, then issue ONE per-candidate read
|
|
25232
|
+
* (`DescribeX` / `ListTagsForResource`) to obtain the tag set — the list
|
|
25233
|
+
* summaries usually do not carry tags. That is an inherent **N+1** read
|
|
25234
|
+
* pattern: an account with many resources of the type produces one API call
|
|
25235
|
+
* per candidate in a tight loop, which is exactly the shape AWS rate-limits.
|
|
25236
|
+
*
|
|
25237
|
+
* Every provider previously hand-rolled this loop with NO backoff, so a single
|
|
25238
|
+
* throttled `Describe*` aborted the whole `cdkd import` run. This helper
|
|
25239
|
+
* centralises the loop and wraps BOTH the list page fetch and the per-candidate
|
|
25240
|
+
* describe in the deploy engine's `withRetry` with exponential backoff.
|
|
25241
|
+
*
|
|
25242
|
+
* ## Why a narrower classifier than `isRetryableTransientError`
|
|
25243
|
+
*
|
|
25244
|
+
* The deploy engine's classifier is tuned for the WRITE path: it also treats
|
|
25245
|
+
* eventual-consistency phrasings like `does not exist` / `not authorized to
|
|
25246
|
+
* perform` as transient, because a just-created dependency legitimately needs a
|
|
25247
|
+
* moment to propagate. On a read-only import walk those messages mean the
|
|
25248
|
+
* opposite — the candidate really is gone, or the caller's credentials really
|
|
25249
|
+
* lack the permission — and retrying them burns the full backoff budget per
|
|
25250
|
+
* candidate before surfacing the true error.
|
|
25251
|
+
*
|
|
25252
|
+
* So the walk retries throttling ONLY, reusing the deploy engine's throttle
|
|
25253
|
+
* tables verbatim ({@link THROTTLING_ERROR_NAMES} /
|
|
25254
|
+
* {@link RETRYABLE_HTTP_STATUS_CODES}) rather than maintaining a second copy.
|
|
25255
|
+
*
|
|
25256
|
+
* ## Batching
|
|
25257
|
+
*
|
|
25258
|
+
* Batched tag reads are deliberately NOT modelled here: the services this
|
|
25259
|
+
* helper currently serves (EMR `DescribeCluster`, DocDB
|
|
25260
|
+
* `ListTagsForResource`) expose only single-resource reads. A service that
|
|
25261
|
+
* genuinely offers a batch API (e.g. CodeCommit `BatchGetRepositories`) can
|
|
25262
|
+
* satisfy several candidates from one call inside its own `describe` callback,
|
|
25263
|
+
* or bypass the helper entirely.
|
|
25264
|
+
*/
|
|
25265
|
+
/** Max number of retries after the first attempt, per API call in the walk. */
|
|
25266
|
+
const DEFAULT_MAX_RETRIES = 5;
|
|
25267
|
+
/** Initial backoff; each retry doubles it up to {@link DEFAULT_MAX_DELAY_MS}. */
|
|
25268
|
+
const DEFAULT_INITIAL_DELAY_MS = 500;
|
|
25269
|
+
/** Cap for the per-retry delay (0.5s -> 1s -> 2s -> 4s -> 5s, ~12.5s total). */
|
|
25270
|
+
const DEFAULT_MAX_DELAY_MS = 5e3;
|
|
25271
|
+
/**
|
|
25272
|
+
* Wall-clock ceiling for the WHOLE walk (all pages + all candidates), not just
|
|
25273
|
+
* one call. Backoff is per-call, so without this a sustained throttle against a
|
|
25274
|
+
* large account degrades into `(pages + candidates) x ~12.5s` of near-silent
|
|
25275
|
+
* retrying — ~42 minutes for 200 candidates, with no way to tell a slow walk
|
|
25276
|
+
* from a hung one. 10 minutes is generous for a healthy walk (a 200-candidate
|
|
25277
|
+
* DocDB account completes in seconds when AWS is not throttling) and bounds the
|
|
25278
|
+
* pathological case to something a user will wait through.
|
|
25279
|
+
*/
|
|
25280
|
+
const DEFAULT_MAX_WALK_MS = 10 * 6e4;
|
|
25281
|
+
/**
|
|
25282
|
+
* Ceiling on pages fetched. A service that returns a non-advancing pagination
|
|
25283
|
+
* token (a bug, or a marker cdkd echoes back wrongly) would otherwise spin
|
|
25284
|
+
* forever. This is now the shared path every migrated provider runs on, so the
|
|
25285
|
+
* guard belongs here rather than in each caller.
|
|
25286
|
+
*/
|
|
25287
|
+
const DEFAULT_MAX_PAGES = 1e3;
|
|
25288
|
+
/**
|
|
25289
|
+
* Whether an error hit during the read-only tag walk is a rate-limit rejection
|
|
25290
|
+
* worth backing off on.
|
|
25291
|
+
*
|
|
25292
|
+
* Delegates the error + `.cause` chain traversal to the deploy engine's
|
|
25293
|
+
* {@link isThrottlingError} (throttling error names + retryable HTTP statuses,
|
|
25294
|
+
* at every cause depth) and adds the canonical `Rate exceeded` message, which
|
|
25295
|
+
* several services return with an HTTP 400 and a service-specific error name.
|
|
25296
|
+
*
|
|
25297
|
+
* This is deliberately NARROWER than `isRetryableTransientError`: that
|
|
25298
|
+
* classifier also treats write-path eventual-consistency phrasings (`does not
|
|
25299
|
+
* exist`, `not authorized to perform`) as transient, because a just-created
|
|
25300
|
+
* dependency legitimately needs a moment to propagate. On a read-only walk
|
|
25301
|
+
* those mean the candidate really is gone or the credentials really lack the
|
|
25302
|
+
* permission, and retrying them burns the full backoff budget per candidate
|
|
25303
|
+
* before surfacing the true error.
|
|
25304
|
+
*
|
|
25305
|
+
* Exported for direct unit testing and for providers whose tag walk cannot use
|
|
25306
|
+
* {@link importTagWalk} verbatim.
|
|
25307
|
+
*/
|
|
25308
|
+
function isThrottlingLikeError(error, message) {
|
|
25309
|
+
return isThrottlingError(error) || message.includes("Rate exceeded");
|
|
25310
|
+
}
|
|
25311
|
+
/** Thrown when the walk exceeds its wall-clock budget or page ceiling. */
|
|
25312
|
+
var ImportTagWalkLimitError = class extends Error {
|
|
25313
|
+
constructor(message) {
|
|
25314
|
+
super(message);
|
|
25315
|
+
this.name = "ImportTagWalkLimitError";
|
|
25316
|
+
}
|
|
25317
|
+
};
|
|
25318
|
+
/**
|
|
25319
|
+
* Paginate `listPage`, `describe` each candidate, and return the first one
|
|
25320
|
+
* whose tags carry the requested `aws:cdk:path`. Returns `null` when no
|
|
25321
|
+
* candidate matches (or when `cdkPath` is empty).
|
|
25322
|
+
*
|
|
25323
|
+
* Both callbacks are individually retried with exponential backoff on
|
|
25324
|
+
* throttling errors, so one rate-limited call mid-walk no longer aborts the
|
|
25325
|
+
* whole import.
|
|
25326
|
+
*/
|
|
25327
|
+
async function importTagWalk(options) {
|
|
25328
|
+
const { cdkPath, listPage, describe, tagsOf } = options;
|
|
25329
|
+
if (!cdkPath) return null;
|
|
25330
|
+
const logicalId = options.logicalId ?? "import";
|
|
25331
|
+
const logger = options.retry?.logger ?? getLogger();
|
|
25332
|
+
const maxWalkMs = options.retry?.maxWalkMs ?? DEFAULT_MAX_WALK_MS;
|
|
25333
|
+
const maxPages = options.retry?.maxPages ?? DEFAULT_MAX_PAGES;
|
|
25334
|
+
const retryOpts = {
|
|
25335
|
+
maxRetries: options.retry?.maxRetries ?? DEFAULT_MAX_RETRIES,
|
|
25336
|
+
initialDelayMs: options.retry?.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS,
|
|
25337
|
+
maxDelayMs: options.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY_MS,
|
|
25338
|
+
isRetryable: (message, error) => isThrottlingLikeError(error, message),
|
|
25339
|
+
logger,
|
|
25340
|
+
...options.retry?.isInterrupted && { isInterrupted: options.retry.isInterrupted },
|
|
25341
|
+
...options.retry?.onInterrupted && { onInterrupted: options.retry.onInterrupted },
|
|
25342
|
+
...options.retry?.sleep && { sleep: options.retry.sleep }
|
|
25343
|
+
};
|
|
25344
|
+
const startedAt = Date.now();
|
|
25345
|
+
const assertWithinBudget = (stage) => {
|
|
25346
|
+
const elapsed = Date.now() - startedAt;
|
|
25347
|
+
if (elapsed >= maxWalkMs) throw new ImportTagWalkLimitError(`Timed out looking up ${logicalId} by its aws:cdk:path tag after ${Math.round(elapsed / 1e3)}s (limit ${Math.round(maxWalkMs / 1e3)}s, while ${stage}). AWS is likely throttling the lookup; retry, or pass an explicit physical id with --resource ${logicalId}=<physicalId>.`);
|
|
25348
|
+
};
|
|
25349
|
+
let marker;
|
|
25350
|
+
let pages = 0;
|
|
25351
|
+
do {
|
|
25352
|
+
assertWithinBudget("listing candidates");
|
|
25353
|
+
if (++pages > maxPages) throw new ImportTagWalkLimitError(`Gave up looking up ${logicalId} by its aws:cdk:path tag after ${maxPages} pages (the service may be returning a non-advancing pagination token). Pass an explicit physical id with --resource ${logicalId}=<physicalId>.`);
|
|
25354
|
+
const currentMarker = marker;
|
|
25355
|
+
const page = await withRetry(() => listPage(currentMarker), logicalId, retryOpts);
|
|
25356
|
+
for (const summary of page.items ?? []) {
|
|
25357
|
+
assertWithinBudget("reading candidate tags");
|
|
25358
|
+
const detail = await withRetry(() => describe(summary), logicalId, retryOpts);
|
|
25359
|
+
if (detail === void 0) {
|
|
25360
|
+
logger.debug(` ↷ Skipped an ${logicalId} import candidate: its detail lookup returned no result`);
|
|
25361
|
+
continue;
|
|
25362
|
+
}
|
|
25363
|
+
if (matchesCdkPath(tagsOf(detail, summary), cdkPath)) return {
|
|
25364
|
+
summary,
|
|
25365
|
+
detail
|
|
25366
|
+
};
|
|
25367
|
+
}
|
|
25368
|
+
marker = page.nextMarker;
|
|
25369
|
+
} while (marker);
|
|
25370
|
+
return null;
|
|
25371
|
+
}
|
|
25372
|
+
|
|
25224
25373
|
//#endregion
|
|
25225
25374
|
//#region src/provisioning/providers/docdb-provider.ts
|
|
25226
25375
|
/**
|
|
@@ -25696,6 +25845,9 @@ var DocDBProvider = class {
|
|
|
25696
25845
|
* Identifier` / `DBClusterIdentifier` / `DBSubnetGroupName`) are usually
|
|
25697
25846
|
* present in CDK templates; fall back to `aws:cdk:path` tag lookup via
|
|
25698
25847
|
* the corresponding `Describe*` + `ListTagsForResource` pair otherwise.
|
|
25848
|
+
* The fallback walk runs through the shared `importTagWalk` helper, so the
|
|
25849
|
+
* N+1 `ListTagsForResource` burst is retried with exponential backoff when
|
|
25850
|
+
* AWS throttles it instead of aborting the whole import.
|
|
25699
25851
|
*/
|
|
25700
25852
|
async import(input) {
|
|
25701
25853
|
switch (input.resourceType) {
|
|
@@ -25811,20 +25963,24 @@ var DocDBProvider = class {
|
|
|
25811
25963
|
if (err.name === "DBInstanceNotFoundFault") return null;
|
|
25812
25964
|
throw err;
|
|
25813
25965
|
}
|
|
25814
|
-
|
|
25815
|
-
|
|
25816
|
-
|
|
25817
|
-
|
|
25818
|
-
|
|
25819
|
-
|
|
25820
|
-
|
|
25821
|
-
|
|
25822
|
-
attributes: {}
|
|
25966
|
+
const match = await importTagWalk({
|
|
25967
|
+
cdkPath: input.cdkPath,
|
|
25968
|
+
logicalId: input.logicalId,
|
|
25969
|
+
listPage: async (marker) => {
|
|
25970
|
+
const list = await this.getClient().send(new DescribeDBInstancesCommand$1({ ...marker && { Marker: marker } }));
|
|
25971
|
+
return {
|
|
25972
|
+
items: list.DBInstances,
|
|
25973
|
+
nextMarker: list.Marker
|
|
25823
25974
|
};
|
|
25824
|
-
}
|
|
25825
|
-
|
|
25826
|
-
|
|
25827
|
-
|
|
25975
|
+
},
|
|
25976
|
+
describe: async (inst) => inst.DBInstanceIdentifier && inst.DBInstanceArn ? await this.listTagsForResource(inst.DBInstanceArn) : void 0,
|
|
25977
|
+
tagsOf: (tags) => tags
|
|
25978
|
+
});
|
|
25979
|
+
if (!match) return null;
|
|
25980
|
+
return {
|
|
25981
|
+
physicalId: match.summary.DBInstanceIdentifier,
|
|
25982
|
+
attributes: {}
|
|
25983
|
+
};
|
|
25828
25984
|
}
|
|
25829
25985
|
async importDBCluster(input) {
|
|
25830
25986
|
const explicit = resolveExplicitPhysicalId(input, "DBClusterIdentifier");
|
|
@@ -25838,20 +25994,24 @@ var DocDBProvider = class {
|
|
|
25838
25994
|
if (err.name === "DBClusterNotFoundFault") return null;
|
|
25839
25995
|
throw err;
|
|
25840
25996
|
}
|
|
25841
|
-
|
|
25842
|
-
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
25849
|
-
attributes: {}
|
|
25997
|
+
const match = await importTagWalk({
|
|
25998
|
+
cdkPath: input.cdkPath,
|
|
25999
|
+
logicalId: input.logicalId,
|
|
26000
|
+
listPage: async (marker) => {
|
|
26001
|
+
const list = await this.getClient().send(new DescribeDBClustersCommand$1({ ...marker && { Marker: marker } }));
|
|
26002
|
+
return {
|
|
26003
|
+
items: list.DBClusters,
|
|
26004
|
+
nextMarker: list.Marker
|
|
25850
26005
|
};
|
|
25851
|
-
}
|
|
25852
|
-
|
|
25853
|
-
|
|
25854
|
-
|
|
26006
|
+
},
|
|
26007
|
+
describe: async (c) => c.DBClusterIdentifier && c.DBClusterArn ? await this.listTagsForResource(c.DBClusterArn) : void 0,
|
|
26008
|
+
tagsOf: (tags) => tags
|
|
26009
|
+
});
|
|
26010
|
+
if (!match) return null;
|
|
26011
|
+
return {
|
|
26012
|
+
physicalId: match.summary.DBClusterIdentifier,
|
|
26013
|
+
attributes: {}
|
|
26014
|
+
};
|
|
25855
26015
|
}
|
|
25856
26016
|
async importDBSubnetGroup(input) {
|
|
25857
26017
|
const explicit = resolveExplicitPhysicalId(input, "DBSubnetGroupName");
|
|
@@ -25865,20 +26025,33 @@ var DocDBProvider = class {
|
|
|
25865
26025
|
if (err.name === "DBSubnetGroupNotFoundFault") return null;
|
|
25866
26026
|
throw err;
|
|
25867
26027
|
}
|
|
25868
|
-
|
|
25869
|
-
|
|
25870
|
-
|
|
25871
|
-
|
|
25872
|
-
|
|
25873
|
-
|
|
25874
|
-
|
|
25875
|
-
|
|
25876
|
-
attributes: {}
|
|
26028
|
+
const match = await importTagWalk({
|
|
26029
|
+
cdkPath: input.cdkPath,
|
|
26030
|
+
logicalId: input.logicalId,
|
|
26031
|
+
listPage: async (marker) => {
|
|
26032
|
+
const list = await this.getClient().send(new DescribeDBSubnetGroupsCommand$1({ ...marker && { Marker: marker } }));
|
|
26033
|
+
return {
|
|
26034
|
+
items: list.DBSubnetGroups,
|
|
26035
|
+
nextMarker: list.Marker
|
|
25877
26036
|
};
|
|
25878
|
-
}
|
|
25879
|
-
|
|
25880
|
-
|
|
25881
|
-
|
|
26037
|
+
},
|
|
26038
|
+
describe: async (sg) => sg.DBSubnetGroupName && sg.DBSubnetGroupArn ? await this.listTagsForResource(sg.DBSubnetGroupArn) : void 0,
|
|
26039
|
+
tagsOf: (tags) => tags
|
|
26040
|
+
});
|
|
26041
|
+
if (!match) return null;
|
|
26042
|
+
return {
|
|
26043
|
+
physicalId: match.summary.DBSubnetGroupName,
|
|
26044
|
+
attributes: {}
|
|
26045
|
+
};
|
|
26046
|
+
}
|
|
26047
|
+
/**
|
|
26048
|
+
* `ListTagsForResource` for the tag-walk import path. Returns the tag list
|
|
26049
|
+
* (possibly empty) so `importTagWalk` can match `aws:cdk:path` against it;
|
|
26050
|
+
* `undefined` is reserved for "skip this candidate", which the callers signal
|
|
26051
|
+
* before calling this.
|
|
26052
|
+
*/
|
|
26053
|
+
async listTagsForResource(resourceArn) {
|
|
26054
|
+
return (await this.getClient().send(new ListTagsForResourceCommand$13({ ResourceName: resourceArn }))).TagList ?? [];
|
|
25882
26055
|
}
|
|
25883
26056
|
};
|
|
25884
26057
|
|
|
@@ -34494,6 +34667,11 @@ const VARIANT_CONFIG_KEY = {
|
|
|
34494
34667
|
ONTAP: "OntapConfiguration",
|
|
34495
34668
|
OPENZFS: "OpenZFSConfiguration"
|
|
34496
34669
|
};
|
|
34670
|
+
/**
|
|
34671
|
+
* Exported so a unit test can assert every declared-mutable sub-property has
|
|
34672
|
+
* a matching `apply*UpdateField` case — the invariant the unreachable
|
|
34673
|
+
* `default:` guard enforces at runtime.
|
|
34674
|
+
*/
|
|
34497
34675
|
const VARIANT_MUTABLE_SUBPROPS = {
|
|
34498
34676
|
LustreConfiguration: LUSTRE_MUTABLE_SUBPROPS,
|
|
34499
34677
|
WindowsConfiguration: WINDOWS_MUTABLE_SUBPROPS,
|
|
@@ -34501,6 +34679,21 @@ const VARIANT_MUTABLE_SUBPROPS = {
|
|
|
34501
34679
|
OpenZFSConfiguration: OPENZFS_MUTABLE_SUBPROPS
|
|
34502
34680
|
};
|
|
34503
34681
|
/**
|
|
34682
|
+
* Copy `value` into `target[key]` only when AWS actually returned it, so the
|
|
34683
|
+
* snapshot carries no `undefined`-valued keys.
|
|
34684
|
+
*
|
|
34685
|
+
* The drift comparator walks the BASELINE's keys (`observedProperties`, or
|
|
34686
|
+
* `properties` when a resource predates observed-capture), not the AWS side —
|
|
34687
|
+
* so an omitted key here is not itself phantom drift. It matters because this
|
|
34688
|
+
* snapshot BECOMES the next baseline: `deploy-engine` stores it as
|
|
34689
|
+
* `observedProperties`, and an explicit `Foo: undefined` would then be walked
|
|
34690
|
+
* as a real key on every later comparison. Omitting is also what keeps the
|
|
34691
|
+
* emitted shape a faithful subset of the CFn input shape.
|
|
34692
|
+
*/
|
|
34693
|
+
const putIfDefined = (target, key, value) => {
|
|
34694
|
+
if (value !== void 0) target[key] = value;
|
|
34695
|
+
};
|
|
34696
|
+
/**
|
|
34504
34697
|
* Compute the Add/Remove route-table-id deltas an ONTAP / OpenZFS
|
|
34505
34698
|
* `UpdateFileSystem` needs from the previous vs. next `RouteTableIds` list.
|
|
34506
34699
|
* Route table membership is an unordered set, so a pure reorder yields no
|
|
@@ -34927,14 +35120,30 @@ var FSxFileSystemProvider = class {
|
|
|
34927
35120
|
const prevVal = prev[key];
|
|
34928
35121
|
if (JSON.stringify(nextVal) === JSON.stringify(prevVal)) continue;
|
|
34929
35122
|
if (!mutable.has(key)) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, `AWS FSx FileSystem ${configKey}.${key} is immutable on AWS — UpdateFileSystem cannot change it after creation. Re-deploy with cdkd deploy --replace, or destroy + redeploy the stack.`);
|
|
34930
|
-
|
|
35123
|
+
const ctx = {
|
|
35124
|
+
resourceType,
|
|
35125
|
+
logicalId,
|
|
35126
|
+
configKey
|
|
35127
|
+
};
|
|
35128
|
+
if (configKey === "LustreConfiguration" ? this.applyLustreUpdateField(ctx, key, nextVal, diff) : configKey === "WindowsConfiguration" ? this.applyWindowsUpdateField(ctx, key, nextVal, diff) : configKey === "OntapConfiguration" ? this.applyOntapUpdateField(ctx, key, nextVal, prevVal, diff) : this.applyOpenZFSUpdateField(ctx, key, nextVal, prevVal, diff)) hasMutableDiff = true;
|
|
34931
35129
|
}
|
|
34932
35130
|
return {
|
|
34933
35131
|
diff,
|
|
34934
35132
|
hasMutableDiff
|
|
34935
35133
|
};
|
|
34936
35134
|
}
|
|
34937
|
-
|
|
35135
|
+
/**
|
|
35136
|
+
* A sub-property listed in a `*_MUTABLE_SUBPROPS` set with no matching
|
|
35137
|
+
* `apply*UpdateField` case. Reaching here is a cdkd bug, not user error:
|
|
35138
|
+
* `computeVariantConfigDiff` already accepted the key as mutable, so
|
|
35139
|
+
* falling through would issue a no-op `UpdateFileSystem` (`hasMutableDiff`
|
|
35140
|
+
* true, empty diff) and silently drop the user's change. Fail loudly
|
|
35141
|
+
* instead so the missing mapping surfaces the moment it is added.
|
|
35142
|
+
*/
|
|
35143
|
+
unmappedMutableSubprop(ctx, key) {
|
|
35144
|
+
throw new ProvisioningError(`AWS FSx FileSystem ${ctx.configKey}.${key} is declared mutable but has no UpdateFileSystem mapping in cdkd. This is a cdkd bug — please report it at https://github.com/go-to-k/cdkd/issues`, ctx.resourceType, ctx.logicalId);
|
|
35145
|
+
}
|
|
35146
|
+
applyLustreUpdateField(ctx, key, next, diff) {
|
|
34938
35147
|
const out = diff;
|
|
34939
35148
|
switch (key) {
|
|
34940
35149
|
case "WeeklyMaintenanceStartTime":
|
|
@@ -34966,10 +35175,11 @@ var FSxFileSystemProvider = class {
|
|
|
34966
35175
|
} : void 0;
|
|
34967
35176
|
break;
|
|
34968
35177
|
}
|
|
35178
|
+
default: this.unmappedMutableSubprop(ctx, key);
|
|
34969
35179
|
}
|
|
34970
35180
|
return true;
|
|
34971
35181
|
}
|
|
34972
|
-
applyWindowsUpdateField(key, next, diff) {
|
|
35182
|
+
applyWindowsUpdateField(ctx, key, next, diff) {
|
|
34973
35183
|
const out = diff;
|
|
34974
35184
|
switch (key) {
|
|
34975
35185
|
case "WeeklyMaintenanceStartTime":
|
|
@@ -34992,10 +35202,11 @@ var FSxFileSystemProvider = class {
|
|
|
34992
35202
|
case "FsrmConfiguration":
|
|
34993
35203
|
out.FsrmConfiguration = this.toWindowsFsrmConfiguration(next);
|
|
34994
35204
|
break;
|
|
35205
|
+
default: this.unmappedMutableSubprop(ctx, key);
|
|
34995
35206
|
}
|
|
34996
35207
|
return true;
|
|
34997
35208
|
}
|
|
34998
|
-
applyOntapUpdateField(key, next, prev, diff) {
|
|
35209
|
+
applyOntapUpdateField(ctx, key, next, prev, diff) {
|
|
34999
35210
|
const out = diff;
|
|
35000
35211
|
switch (key) {
|
|
35001
35212
|
case "DailyAutomaticBackupStartTime":
|
|
@@ -35019,10 +35230,11 @@ var FSxFileSystemProvider = class {
|
|
|
35019
35230
|
if (remove) out.RemoveRouteTableIds = remove;
|
|
35020
35231
|
return add !== void 0 || remove !== void 0;
|
|
35021
35232
|
}
|
|
35233
|
+
default: this.unmappedMutableSubprop(ctx, key);
|
|
35022
35234
|
}
|
|
35023
35235
|
return true;
|
|
35024
35236
|
}
|
|
35025
|
-
applyOpenZFSUpdateField(key, next, prev, diff) {
|
|
35237
|
+
applyOpenZFSUpdateField(ctx, key, next, prev, diff) {
|
|
35026
35238
|
const out = diff;
|
|
35027
35239
|
switch (key) {
|
|
35028
35240
|
case "DailyAutomaticBackupStartTime":
|
|
@@ -35050,6 +35262,7 @@ var FSxFileSystemProvider = class {
|
|
|
35050
35262
|
if (remove) out.RemoveRouteTableIds = remove;
|
|
35051
35263
|
return add !== void 0 || remove !== void 0;
|
|
35052
35264
|
}
|
|
35265
|
+
default: this.unmappedMutableSubprop(ctx, key);
|
|
35053
35266
|
}
|
|
35054
35267
|
return true;
|
|
35055
35268
|
}
|
|
@@ -35223,28 +35436,122 @@ var FSxFileSystemProvider = class {
|
|
|
35223
35436
|
default: return;
|
|
35224
35437
|
}
|
|
35225
35438
|
}
|
|
35439
|
+
/** Map the read-side `WindowsConfiguration` back to CFn input shape. */
|
|
35440
|
+
readWindowsConfiguration(windows) {
|
|
35441
|
+
const config = {};
|
|
35442
|
+
putIfDefined(config, "ActiveDirectoryId", windows.ActiveDirectoryId);
|
|
35443
|
+
putIfDefined(config, "DeploymentType", windows.DeploymentType);
|
|
35444
|
+
putIfDefined(config, "PreferredSubnetId", windows.PreferredSubnetId);
|
|
35445
|
+
putIfDefined(config, "ThroughputCapacity", windows.ThroughputCapacity);
|
|
35446
|
+
putIfDefined(config, "WeeklyMaintenanceStartTime", windows.WeeklyMaintenanceStartTime);
|
|
35447
|
+
putIfDefined(config, "DailyAutomaticBackupStartTime", windows.DailyAutomaticBackupStartTime);
|
|
35448
|
+
putIfDefined(config, "AutomaticBackupRetentionDays", windows.AutomaticBackupRetentionDays);
|
|
35449
|
+
putIfDefined(config, "CopyTagsToBackups", windows.CopyTagsToBackups);
|
|
35450
|
+
if (windows.Aliases !== void 0) config["Aliases"] = windows.Aliases.map((alias) => alias.Name).filter((name) => name !== void 0);
|
|
35451
|
+
if (windows.SelfManagedActiveDirectoryConfiguration) {
|
|
35452
|
+
const ad = windows.SelfManagedActiveDirectoryConfiguration;
|
|
35453
|
+
const adConfig = {};
|
|
35454
|
+
putIfDefined(adConfig, "DomainName", ad.DomainName);
|
|
35455
|
+
putIfDefined(adConfig, "OrganizationalUnitDistinguishedName", ad.OrganizationalUnitDistinguishedName);
|
|
35456
|
+
putIfDefined(adConfig, "FileSystemAdministratorsGroup", ad.FileSystemAdministratorsGroup);
|
|
35457
|
+
putIfDefined(adConfig, "UserName", ad.UserName);
|
|
35458
|
+
putIfDefined(adConfig, "DnsIps", ad.DnsIps ? [...ad.DnsIps] : void 0);
|
|
35459
|
+
putIfDefined(adConfig, "DomainJoinServiceAccountSecret", ad.DomainJoinServiceAccountSecret);
|
|
35460
|
+
if (Object.keys(adConfig).length > 0) config["SelfManagedActiveDirectoryConfiguration"] = adConfig;
|
|
35461
|
+
}
|
|
35462
|
+
if (windows.AuditLogConfiguration) {
|
|
35463
|
+
const audit = {};
|
|
35464
|
+
putIfDefined(audit, "FileAccessAuditLogLevel", windows.AuditLogConfiguration.FileAccessAuditLogLevel);
|
|
35465
|
+
putIfDefined(audit, "FileShareAccessAuditLogLevel", windows.AuditLogConfiguration.FileShareAccessAuditLogLevel);
|
|
35466
|
+
putIfDefined(audit, "AuditLogDestination", windows.AuditLogConfiguration.AuditLogDestination);
|
|
35467
|
+
if (Object.keys(audit).length > 0) config["AuditLogConfiguration"] = audit;
|
|
35468
|
+
}
|
|
35469
|
+
const diskIops = this.readDiskIopsConfiguration(windows.DiskIopsConfiguration);
|
|
35470
|
+
if (diskIops) config["DiskIopsConfiguration"] = diskIops;
|
|
35471
|
+
if (windows.FsrmConfiguration) {
|
|
35472
|
+
const fsrm = {};
|
|
35473
|
+
putIfDefined(fsrm, "FsrmServiceEnabled", windows.FsrmConfiguration.FsrmServiceEnabled);
|
|
35474
|
+
putIfDefined(fsrm, "EventLogDestination", windows.FsrmConfiguration.EventLogDestination);
|
|
35475
|
+
if (Object.keys(fsrm).length > 0) config["FsrmConfiguration"] = fsrm;
|
|
35476
|
+
}
|
|
35477
|
+
return config;
|
|
35478
|
+
}
|
|
35479
|
+
/** Map the read-side `OntapConfiguration` back to CFn input shape. */
|
|
35480
|
+
readOntapConfiguration(ontap) {
|
|
35481
|
+
const config = {};
|
|
35482
|
+
putIfDefined(config, "DeploymentType", ontap.DeploymentType);
|
|
35483
|
+
putIfDefined(config, "AutomaticBackupRetentionDays", ontap.AutomaticBackupRetentionDays);
|
|
35484
|
+
putIfDefined(config, "DailyAutomaticBackupStartTime", ontap.DailyAutomaticBackupStartTime);
|
|
35485
|
+
putIfDefined(config, "WeeklyMaintenanceStartTime", ontap.WeeklyMaintenanceStartTime);
|
|
35486
|
+
putIfDefined(config, "EndpointIpAddressRange", ontap.EndpointIpAddressRange);
|
|
35487
|
+
putIfDefined(config, "PreferredSubnetId", ontap.PreferredSubnetId);
|
|
35488
|
+
putIfDefined(config, "ThroughputCapacity", ontap.ThroughputCapacity);
|
|
35489
|
+
putIfDefined(config, "ThroughputCapacityPerHAPair", ontap.ThroughputCapacityPerHAPair);
|
|
35490
|
+
putIfDefined(config, "HAPairs", ontap.HAPairs);
|
|
35491
|
+
putIfDefined(config, "RouteTableIds", ontap.RouteTableIds ? [...ontap.RouteTableIds] : void 0);
|
|
35492
|
+
putIfDefined(config, "EndpointIpv6AddressRange", ontap.EndpointIpv6AddressRange);
|
|
35493
|
+
const diskIops = this.readDiskIopsConfiguration(ontap.DiskIopsConfiguration);
|
|
35494
|
+
if (diskIops) config["DiskIopsConfiguration"] = diskIops;
|
|
35495
|
+
return config;
|
|
35496
|
+
}
|
|
35497
|
+
/** Map the read-side `OpenZFSConfiguration` back to CFn input shape. */
|
|
35498
|
+
readOpenZFSConfiguration(openzfs) {
|
|
35499
|
+
const config = {};
|
|
35500
|
+
putIfDefined(config, "DeploymentType", openzfs.DeploymentType);
|
|
35501
|
+
putIfDefined(config, "AutomaticBackupRetentionDays", openzfs.AutomaticBackupRetentionDays);
|
|
35502
|
+
putIfDefined(config, "CopyTagsToBackups", openzfs.CopyTagsToBackups);
|
|
35503
|
+
putIfDefined(config, "CopyTagsToVolumes", openzfs.CopyTagsToVolumes);
|
|
35504
|
+
putIfDefined(config, "DailyAutomaticBackupStartTime", openzfs.DailyAutomaticBackupStartTime);
|
|
35505
|
+
putIfDefined(config, "WeeklyMaintenanceStartTime", openzfs.WeeklyMaintenanceStartTime);
|
|
35506
|
+
putIfDefined(config, "ThroughputCapacity", openzfs.ThroughputCapacity);
|
|
35507
|
+
putIfDefined(config, "PreferredSubnetId", openzfs.PreferredSubnetId);
|
|
35508
|
+
putIfDefined(config, "EndpointIpAddressRange", openzfs.EndpointIpAddressRange);
|
|
35509
|
+
putIfDefined(config, "RouteTableIds", openzfs.RouteTableIds ? [...openzfs.RouteTableIds] : void 0);
|
|
35510
|
+
putIfDefined(config, "EndpointIpv6AddressRange", openzfs.EndpointIpv6AddressRange);
|
|
35511
|
+
const diskIops = this.readDiskIopsConfiguration(openzfs.DiskIopsConfiguration);
|
|
35512
|
+
if (diskIops) config["DiskIopsConfiguration"] = diskIops;
|
|
35513
|
+
if (openzfs.ReadCacheConfiguration) {
|
|
35514
|
+
const readCache = {};
|
|
35515
|
+
putIfDefined(readCache, "SizingMode", openzfs.ReadCacheConfiguration.SizingMode);
|
|
35516
|
+
putIfDefined(readCache, "SizeGiB", openzfs.ReadCacheConfiguration.SizeGiB);
|
|
35517
|
+
if (Object.keys(readCache).length > 0) config["ReadCacheConfiguration"] = readCache;
|
|
35518
|
+
}
|
|
35519
|
+
return config;
|
|
35520
|
+
}
|
|
35521
|
+
/** Shared `DiskIopsConfiguration` reverse-mapper (Windows / ONTAP / OpenZFS). */
|
|
35522
|
+
readDiskIopsConfiguration(diskIops) {
|
|
35523
|
+
if (!diskIops) return void 0;
|
|
35524
|
+
const out = {};
|
|
35525
|
+
putIfDefined(out, "Mode", diskIops.Mode);
|
|
35526
|
+
putIfDefined(out, "Iops", diskIops.Iops);
|
|
35527
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
35528
|
+
}
|
|
35226
35529
|
/**
|
|
35227
35530
|
* State property paths this provider cannot read back from AWS:
|
|
35228
35531
|
* - `SecurityGroupIds` — `DescribeFileSystems` returns network
|
|
35229
35532
|
* interface ids, never the original security group list.
|
|
35230
35533
|
* - `BackupId` — creation-source input, not surfaced on the deployed
|
|
35231
35534
|
* file system.
|
|
35232
|
-
* - `WindowsConfiguration`
|
|
35233
|
-
*
|
|
35234
|
-
*
|
|
35235
|
-
*
|
|
35236
|
-
*
|
|
35237
|
-
*
|
|
35238
|
-
*
|
|
35535
|
+
* - `WindowsConfiguration.SelfManagedActiveDirectoryConfiguration.Password`
|
|
35536
|
+
* / `OntapConfiguration.FsxAdminPassword` — write-only credentials; the
|
|
35537
|
+
* API never echoes them back.
|
|
35538
|
+
* - `OpenZFSConfiguration.RootVolumeConfiguration` — configured on the
|
|
35539
|
+
* root VOLUME, not the file system; `DescribeFileSystems` returns only
|
|
35540
|
+
* `RootVolumeId`, so reading it back would need a separate
|
|
35541
|
+
* `DescribeVolumes` call (out of scope here).
|
|
35542
|
+
*
|
|
35543
|
+
* The whole-block `WindowsConfiguration` / `OntapConfiguration` /
|
|
35544
|
+
* `OpenZFSConfiguration` entries are gone as of the variant reverse-mappers
|
|
35545
|
+
* — only the individually-unreadable leaves above stay unknown.
|
|
35239
35546
|
*/
|
|
35240
35547
|
getDriftUnknownPaths(resourceType) {
|
|
35241
35548
|
if (resourceType !== "AWS::FSx::FileSystem") return [];
|
|
35242
35549
|
return [
|
|
35243
35550
|
"SecurityGroupIds",
|
|
35244
35551
|
"BackupId",
|
|
35245
|
-
"WindowsConfiguration",
|
|
35246
|
-
"OntapConfiguration",
|
|
35247
|
-
"OpenZFSConfiguration"
|
|
35552
|
+
"WindowsConfiguration.SelfManagedActiveDirectoryConfiguration.Password",
|
|
35553
|
+
"OntapConfiguration.FsxAdminPassword",
|
|
35554
|
+
"OpenZFSConfiguration.RootVolumeConfiguration"
|
|
35248
35555
|
];
|
|
35249
35556
|
}
|
|
35250
35557
|
/**
|
|
@@ -35252,7 +35559,11 @@ var FSxFileSystemProvider = class {
|
|
|
35252
35559
|
* via `DescribeFileSystems`. Lustre data-repository fields (`ImportPath`
|
|
35253
35560
|
* / `ExportPath` / `AutoImportPolicy` / `ImportedFileChunkSize`) are
|
|
35254
35561
|
* mapped back from the nested `DataRepositoryConfiguration` the API
|
|
35255
|
-
* returns.
|
|
35562
|
+
* returns. The Windows / ONTAP / OpenZFS variant blocks are reverse-mapped
|
|
35563
|
+
* by their respective `read*Configuration` helpers; the few inputs AWS
|
|
35564
|
+
* never returns stay listed in {@link getDriftUnknownPaths}. At most one
|
|
35565
|
+
* variant block is ever present (they key off `FileSystemType`).
|
|
35566
|
+
* Returns `undefined` when the file system is gone.
|
|
35256
35567
|
*/
|
|
35257
35568
|
async readCurrentState(physicalId, _logicalId, resourceType) {
|
|
35258
35569
|
if (resourceType !== "AWS::FSx::FileSystem") return void 0;
|
|
@@ -35265,47 +35576,59 @@ var FSxFileSystemProvider = class {
|
|
|
35265
35576
|
}
|
|
35266
35577
|
if (!fs) return void 0;
|
|
35267
35578
|
const result = {};
|
|
35268
|
-
|
|
35269
|
-
|
|
35270
|
-
|
|
35271
|
-
|
|
35272
|
-
|
|
35273
|
-
|
|
35274
|
-
|
|
35579
|
+
putIfDefined(result, "FileSystemType", fs.FileSystemType);
|
|
35580
|
+
putIfDefined(result, "StorageCapacity", fs.StorageCapacity);
|
|
35581
|
+
putIfDefined(result, "StorageType", fs.StorageType);
|
|
35582
|
+
putIfDefined(result, "SubnetIds", fs.SubnetIds ? [...fs.SubnetIds] : void 0);
|
|
35583
|
+
putIfDefined(result, "KmsKeyId", fs.KmsKeyId);
|
|
35584
|
+
putIfDefined(result, "FileSystemTypeVersion", fs.FileSystemTypeVersion);
|
|
35585
|
+
putIfDefined(result, "NetworkType", fs.NetworkType);
|
|
35275
35586
|
const lustre = fs.LustreConfiguration;
|
|
35276
35587
|
if (lustre) {
|
|
35277
35588
|
const config = {};
|
|
35278
|
-
|
|
35279
|
-
|
|
35280
|
-
|
|
35281
|
-
|
|
35282
|
-
|
|
35283
|
-
|
|
35284
|
-
|
|
35285
|
-
|
|
35286
|
-
|
|
35287
|
-
|
|
35589
|
+
putIfDefined(config, "WeeklyMaintenanceStartTime", lustre.WeeklyMaintenanceStartTime);
|
|
35590
|
+
putIfDefined(config, "DeploymentType", lustre.DeploymentType);
|
|
35591
|
+
putIfDefined(config, "PerUnitStorageThroughput", lustre.PerUnitStorageThroughput);
|
|
35592
|
+
putIfDefined(config, "DailyAutomaticBackupStartTime", lustre.DailyAutomaticBackupStartTime);
|
|
35593
|
+
putIfDefined(config, "AutomaticBackupRetentionDays", lustre.AutomaticBackupRetentionDays);
|
|
35594
|
+
putIfDefined(config, "CopyTagsToBackups", lustre.CopyTagsToBackups);
|
|
35595
|
+
putIfDefined(config, "DriveCacheType", lustre.DriveCacheType);
|
|
35596
|
+
putIfDefined(config, "DataCompressionType", lustre.DataCompressionType);
|
|
35597
|
+
putIfDefined(config, "EfaEnabled", lustre.EfaEnabled);
|
|
35598
|
+
putIfDefined(config, "ThroughputCapacity", lustre.ThroughputCapacity);
|
|
35288
35599
|
if (lustre.MetadataConfiguration) {
|
|
35289
35600
|
const metadata = {};
|
|
35290
|
-
|
|
35291
|
-
|
|
35601
|
+
putIfDefined(metadata, "Mode", lustre.MetadataConfiguration.Mode);
|
|
35602
|
+
putIfDefined(metadata, "Iops", lustre.MetadataConfiguration.Iops);
|
|
35292
35603
|
if (Object.keys(metadata).length > 0) config["MetadataConfiguration"] = metadata;
|
|
35293
35604
|
}
|
|
35294
35605
|
if (lustre.DataReadCacheConfiguration) {
|
|
35295
35606
|
const readCache = {};
|
|
35296
|
-
|
|
35297
|
-
|
|
35607
|
+
putIfDefined(readCache, "SizingMode", lustre.DataReadCacheConfiguration.SizingMode);
|
|
35608
|
+
putIfDefined(readCache, "SizeGiB", lustre.DataReadCacheConfiguration.SizeGiB);
|
|
35298
35609
|
if (Object.keys(readCache).length > 0) config["DataReadCacheConfiguration"] = readCache;
|
|
35299
35610
|
}
|
|
35300
35611
|
const dataRepo = lustre.DataRepositoryConfiguration;
|
|
35301
35612
|
if (dataRepo) {
|
|
35302
|
-
|
|
35303
|
-
|
|
35304
|
-
|
|
35305
|
-
|
|
35613
|
+
putIfDefined(config, "ImportPath", dataRepo.ImportPath);
|
|
35614
|
+
putIfDefined(config, "ExportPath", dataRepo.ExportPath);
|
|
35615
|
+
putIfDefined(config, "AutoImportPolicy", dataRepo.AutoImportPolicy);
|
|
35616
|
+
putIfDefined(config, "ImportedFileChunkSize", dataRepo.ImportedFileChunkSize);
|
|
35306
35617
|
}
|
|
35307
35618
|
if (Object.keys(config).length > 0) result["LustreConfiguration"] = config;
|
|
35308
35619
|
}
|
|
35620
|
+
if (fs.WindowsConfiguration) {
|
|
35621
|
+
const config = this.readWindowsConfiguration(fs.WindowsConfiguration);
|
|
35622
|
+
if (Object.keys(config).length > 0) result["WindowsConfiguration"] = config;
|
|
35623
|
+
}
|
|
35624
|
+
if (fs.OntapConfiguration) {
|
|
35625
|
+
const config = this.readOntapConfiguration(fs.OntapConfiguration);
|
|
35626
|
+
if (Object.keys(config).length > 0) result["OntapConfiguration"] = config;
|
|
35627
|
+
}
|
|
35628
|
+
if (fs.OpenZFSConfiguration) {
|
|
35629
|
+
const config = this.readOpenZFSConfiguration(fs.OpenZFSConfiguration);
|
|
35630
|
+
if (Object.keys(config).length > 0) result["OpenZFSConfiguration"] = config;
|
|
35631
|
+
}
|
|
35309
35632
|
result["Tags"] = normalizeAwsTagsToCfn(fs.Tags);
|
|
35310
35633
|
return result;
|
|
35311
35634
|
}
|
|
@@ -35887,45 +36210,58 @@ var EMRClusterProvider = class {
|
|
|
35887
36210
|
* 2. Tag-based lookup: `ListClusters` filtered to the non-terminated states
|
|
35888
36211
|
* (a `TERMINATED*` cluster is gone and must never be adopted), then a
|
|
35889
36212
|
* `DescribeCluster` per candidate to read `Tags` (the list summaries do
|
|
35890
|
-
* NOT carry tags) and match `aws:cdk:path`.
|
|
36213
|
+
* NOT carry tags) and match `aws:cdk:path`. The walk runs through the
|
|
36214
|
+
* shared `importTagWalk` helper, so each list page / describe is retried
|
|
36215
|
+
* with exponential backoff when AWS throttles the N+1 read burst.
|
|
35891
36216
|
*/
|
|
35892
36217
|
async import(input) {
|
|
35893
36218
|
const explicit = resolveExplicitPhysicalId(input, null);
|
|
35894
36219
|
if (explicit) {
|
|
35895
|
-
const cluster = await this.
|
|
36220
|
+
const cluster = await this.describeClusterOrUndefined(explicit);
|
|
35896
36221
|
if (!cluster || cluster.Status?.State && TERMINAL_STATES.has(cluster.Status.State)) return null;
|
|
35897
36222
|
return {
|
|
35898
36223
|
physicalId: explicit,
|
|
35899
36224
|
attributes: this.buildAttributes(cluster)
|
|
35900
36225
|
};
|
|
35901
36226
|
}
|
|
35902
|
-
|
|
35903
|
-
|
|
35904
|
-
|
|
35905
|
-
|
|
35906
|
-
|
|
35907
|
-
|
|
35908
|
-
|
|
35909
|
-
|
|
35910
|
-
|
|
35911
|
-
|
|
35912
|
-
|
|
35913
|
-
if (matchesCdkPath(cluster.Tags, input.cdkPath)) return {
|
|
35914
|
-
physicalId: summary.Id,
|
|
35915
|
-
attributes: this.buildAttributes(cluster)
|
|
36227
|
+
const match = await importTagWalk({
|
|
36228
|
+
cdkPath: input.cdkPath,
|
|
36229
|
+
logicalId: input.logicalId,
|
|
36230
|
+
listPage: async (marker) => {
|
|
36231
|
+
const list = await this.getClient().send(new ListClustersCommand$1({
|
|
36232
|
+
ClusterStates: [...NON_TERMINATED_STATES],
|
|
36233
|
+
...marker && { Marker: marker }
|
|
36234
|
+
}));
|
|
36235
|
+
return {
|
|
36236
|
+
items: list.Clusters,
|
|
36237
|
+
nextMarker: list.Marker
|
|
35916
36238
|
};
|
|
35917
|
-
}
|
|
35918
|
-
|
|
35919
|
-
|
|
35920
|
-
|
|
36239
|
+
},
|
|
36240
|
+
describe: async (summary) => summary.Id ? await this.describeClusterOrUndefined(summary.Id) : void 0,
|
|
36241
|
+
tagsOf: (cluster) => cluster.Tags
|
|
36242
|
+
});
|
|
36243
|
+
if (!match) return null;
|
|
36244
|
+
return {
|
|
36245
|
+
physicalId: match.summary.Id,
|
|
36246
|
+
attributes: this.buildAttributes(match.detail)
|
|
36247
|
+
};
|
|
35921
36248
|
}
|
|
35922
36249
|
/**
|
|
35923
36250
|
* `DescribeCluster` that maps a not-found (`InvalidRequestException` — the
|
|
35924
|
-
* cluster id is unknown in this region / aged out of Describe) to
|
|
35925
|
-
* instead of throwing, so `import()` can treat it as "no match"
|
|
35926
|
-
* aborting the whole adoption run.
|
|
36251
|
+
* cluster id is unknown in this region / aged out of Describe) to
|
|
36252
|
+
* `undefined` instead of throwing, so `import()` can treat it as "no match"
|
|
36253
|
+
* rather than aborting the whole adoption run.
|
|
36254
|
+
*
|
|
36255
|
+
* `undefined`, NOT `null`, is load-bearing: `importTagWalk` skips a candidate
|
|
36256
|
+
* on `detail === undefined`, and an actual `null` would flow into `tagsOf`
|
|
36257
|
+
* and `TypeError`. Also note the mapping is BROAD — `InvalidRequestException`
|
|
36258
|
+
* covers more than not-found, so a genuine failure here degrades to "no
|
|
36259
|
+
* match" and a subsequent deploy would CREATE a duplicate cluster rather than
|
|
36260
|
+
* adopt the existing one. The walk logs each skip at debug so the case is at
|
|
36261
|
+
* least visible under `--verbose`; narrowing the mapping needs a
|
|
36262
|
+
* message/code-level discriminator AWS does not currently document.
|
|
35927
36263
|
*/
|
|
35928
|
-
async
|
|
36264
|
+
async describeClusterOrUndefined(clusterId) {
|
|
35929
36265
|
try {
|
|
35930
36266
|
return (await this.getClient().send(new DescribeClusterCommand({ ClusterId: clusterId }))).Cluster;
|
|
35931
36267
|
} catch (err) {
|
|
@@ -35964,7 +36300,7 @@ var EMRClusterProvider = class {
|
|
|
35964
36300
|
* optional `import` method's incremental opt-in shape.
|
|
35965
36301
|
*/
|
|
35966
36302
|
async readCurrentState(physicalId, _logicalId, _resourceType) {
|
|
35967
|
-
const cluster = await this.
|
|
36303
|
+
const cluster = await this.describeClusterOrUndefined(physicalId);
|
|
35968
36304
|
if (!cluster) return void 0;
|
|
35969
36305
|
let instanceGroups = [];
|
|
35970
36306
|
let instanceFleets = [];
|
|
@@ -36164,14 +36500,21 @@ const DEFAULT_POLL_INTERVAL_MS$1 = 15e3;
|
|
|
36164
36500
|
/** Instance-group state that means "the group is up and idle/serving". */
|
|
36165
36501
|
const READY_STATES$1 = /* @__PURE__ */ new Set(["RUNNING"]);
|
|
36166
36502
|
/**
|
|
36167
|
-
* Instance-group states that mean "the group
|
|
36168
|
-
* error during a create/resize wait
|
|
36169
|
-
*
|
|
36503
|
+
* Instance-group states that mean "the group will never reach the requested
|
|
36504
|
+
* size" — a hard error during a create/resize wait.
|
|
36505
|
+
*
|
|
36506
|
+
* `ARRESTED` means the group failed to provision; `TERMINATED`/`ENDED` mean it
|
|
36507
|
+
* is gone. `SUSPENDED` means a resize could not complete: the existing
|
|
36508
|
+
* instances keep running but AWS can no longer add or remove any, so the wait
|
|
36509
|
+
* would poll to the full `maxWaitMs` timeout instead of failing fast with the
|
|
36510
|
+
* service's own state-change reason — the exact defect fixed on the
|
|
36511
|
+
* instance-fleet side in issue #1092 item 2, which applies verbatim here.
|
|
36170
36512
|
*/
|
|
36171
36513
|
const FAILED_STATES$1 = /* @__PURE__ */ new Set([
|
|
36172
36514
|
"ARRESTED",
|
|
36173
|
-
"
|
|
36174
|
-
"
|
|
36515
|
+
"ENDED",
|
|
36516
|
+
"SUSPENDED",
|
|
36517
|
+
"TERMINATED"
|
|
36175
36518
|
]);
|
|
36176
36519
|
const toNumber$1 = (v) => {
|
|
36177
36520
|
if (v === void 0) return void 0;
|
|
@@ -36321,7 +36664,7 @@ var EMRInstanceGroupConfigProvider = class {
|
|
|
36321
36664
|
InstanceCount: toNumber$1(properties["InstanceCount"])
|
|
36322
36665
|
}]
|
|
36323
36666
|
}));
|
|
36324
|
-
if (jobFlowId) await this.waitForGroupReady(jobFlowId, physicalId, logicalId, resourceType, toNumber$1(properties["InstanceCount"]) ?? 0);
|
|
36667
|
+
if (jobFlowId) await this.waitForGroupReady(jobFlowId, physicalId, logicalId, resourceType, toNumber$1(properties["InstanceCount"]) ?? 0, true);
|
|
36325
36668
|
}
|
|
36326
36669
|
if (autoScalingChanged) {
|
|
36327
36670
|
if (!jobFlowId) throw new ProvisioningError(`EMR InstanceGroupConfig ${logicalId} AutoScalingPolicy update needs JobFlowId (the cluster id), which is absent`, resourceType, logicalId, physicalId);
|
|
@@ -36382,15 +36725,43 @@ var EMRInstanceGroupConfigProvider = class {
|
|
|
36382
36725
|
* would return before the resize even starts. Waiting on the running count
|
|
36383
36726
|
* reaching the requested target settles both create (0 -> N provisioning)
|
|
36384
36727
|
* and resize (M -> N add/remove) correctly.
|
|
36385
|
-
|
|
36386
|
-
|
|
36728
|
+
*
|
|
36729
|
+
* `toleratesStaleFailedState` MUST be true on the resize path and false on
|
|
36730
|
+
* create. It exists for the RECOVERY case: `SUSPENDED` (a resize that could
|
|
36731
|
+
* not complete) is precisely the state a user re-runs `cdkd deploy` to fix,
|
|
36732
|
+
* but the same pre-resize read lag documented above means the first poll
|
|
36733
|
+
* after `ModifyInstanceGroups` still reports `SUSPENDED`. Enforcing
|
|
36734
|
+
* {@link FAILED_STATES} on that stale read would abort the very resize that
|
|
36735
|
+
* recovers the group, on every attempt, making the state permanently
|
|
36736
|
+
* unrecoverable through cdkd.
|
|
36737
|
+
*
|
|
36738
|
+
* So on a resize the failed-state check is suppressed until the group is
|
|
36739
|
+
* observed LEAVING whatever state it started in; from then on it is enforced
|
|
36740
|
+
* normally (a resize that transitions RUNNING -> RESIZING -> SUSPENDED still
|
|
36741
|
+
* fails fast, which is the case #1092 item 2 is about). A group that never
|
|
36742
|
+
* leaves its initial state falls through to the `maxWaitMs` timeout — the
|
|
36743
|
+
* pre-fix behavior, and strictly better than an unrecoverable resource.
|
|
36744
|
+
*
|
|
36745
|
+
* Note the asymmetry that makes this specific to failed states: a stale
|
|
36746
|
+
* pre-resize `RUNNING` is harmless because the instance-count mismatch keeps
|
|
36747
|
+
* the loop polling. A stale `SUSPENDED` is terminal.
|
|
36748
|
+
*/
|
|
36749
|
+
async waitForGroupReady(clusterId, groupId, logicalId, resourceType, expectedCount, toleratesStaleFailedState = false) {
|
|
36387
36750
|
const startTime = Date.now();
|
|
36388
36751
|
const transientState = { count: 0 };
|
|
36752
|
+
let initialState;
|
|
36753
|
+
let hasLeftInitialState = false;
|
|
36389
36754
|
while (Date.now() - startTime < this.maxWaitMs) {
|
|
36390
36755
|
const group = await this.findGroupForPoll(clusterId, groupId, transientState);
|
|
36391
36756
|
const state = group?.Status?.State;
|
|
36757
|
+
if (state) {
|
|
36758
|
+
if (initialState === void 0) initialState = state;
|
|
36759
|
+
else if (state !== initialState) hasLeftInitialState = true;
|
|
36760
|
+
}
|
|
36392
36761
|
if (state && READY_STATES$1.has(state) && group?.RunningInstanceCount === expectedCount) return;
|
|
36393
|
-
|
|
36762
|
+
const enforceFailedStates = !toleratesStaleFailedState || hasLeftInitialState;
|
|
36763
|
+
if (state && enforceFailedStates && FAILED_STATES$1.has(state)) throw new ProvisioningError(`EMR instance group ${groupId} entered failed state ${state}: ${group?.Status?.StateChangeReason?.Message ?? "no state-change reason reported"}`, resourceType, logicalId, groupId);
|
|
36764
|
+
if (state && !enforceFailedStates && FAILED_STATES$1.has(state)) this.logger.debug(`EMR instance group ${groupId} still reads ${state} from before the resize; waiting for it to leave that state before treating it as failed`);
|
|
36394
36765
|
this.logger.debug(`EMR instance group ${groupId} state: ${state ?? "unknown"}, running ${group?.RunningInstanceCount ?? "?"}/${expectedCount}, waiting...`);
|
|
36395
36766
|
await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
|
|
36396
36767
|
}
|
|
@@ -36446,10 +36817,20 @@ const DEFAULT_POLL_INTERVAL_MS = 15e3;
|
|
|
36446
36817
|
/** Instance-fleet state that means "the fleet is up and idle/serving". */
|
|
36447
36818
|
const READY_STATES = /* @__PURE__ */ new Set(["RUNNING"]);
|
|
36448
36819
|
/**
|
|
36449
|
-
* Instance-fleet states that mean "the fleet
|
|
36450
|
-
*
|
|
36820
|
+
* Instance-fleet states that mean "the fleet will never reach the requested
|
|
36821
|
+
* capacity" — a hard error during a create/resize wait.
|
|
36822
|
+
*
|
|
36823
|
+
* `TERMINATED` means the fleet is gone. `SUSPENDED` means a resize could not
|
|
36824
|
+
* complete: the existing instances keep running but AWS can no longer add or
|
|
36825
|
+
* remove any, so the wait would poll to the full `maxWaitMs` timeout instead of
|
|
36826
|
+
* failing fast with the service's own state-change reason (issue #1092 item 2).
|
|
36827
|
+
*
|
|
36828
|
+
* This is the fleet-state ANALOGUE of the instance-group provider's failed set,
|
|
36829
|
+
* not a copy of it: the two enums differ. `ARRESTED` and `ENDED` are
|
|
36830
|
+
* `InstanceGroupState` members that do not exist for fleets, so `TERMINATED` is
|
|
36831
|
+
* the only state the two sets share.
|
|
36451
36832
|
*/
|
|
36452
|
-
const FAILED_STATES = /* @__PURE__ */ new Set(["TERMINATED"]);
|
|
36833
|
+
const FAILED_STATES = /* @__PURE__ */ new Set(["SUSPENDED", "TERMINATED"]);
|
|
36453
36834
|
const toNumber = (v) => {
|
|
36454
36835
|
if (v === void 0) return void 0;
|
|
36455
36836
|
const n = Number(v);
|
|
@@ -36602,7 +36983,7 @@ var EMRInstanceFleetConfigProvider = class {
|
|
|
36602
36983
|
}));
|
|
36603
36984
|
const newTarget = this.targetCapacity(properties);
|
|
36604
36985
|
const prevTarget = this.targetCapacity(previousProperties);
|
|
36605
|
-
await this.waitForFleetReady(clusterId, physicalId, logicalId, resourceType, newTarget, newTarget >= prevTarget);
|
|
36986
|
+
await this.waitForFleetReady(clusterId, physicalId, logicalId, resourceType, newTarget, newTarget >= prevTarget, true);
|
|
36606
36987
|
this.logger.debug(`Successfully updated EMR instance fleet ${logicalId}`);
|
|
36607
36988
|
return {
|
|
36608
36989
|
physicalId,
|
|
@@ -36662,17 +37043,45 @@ var EMRInstanceFleetConfigProvider = class {
|
|
|
36662
37043
|
*
|
|
36663
37044
|
* A `targetCapacity` of 0 on the up path (never valid for a real create —
|
|
36664
37045
|
* at least one target must be > 0) degrades to a State-only wait.
|
|
36665
|
-
|
|
36666
|
-
|
|
37046
|
+
*
|
|
37047
|
+
* `toleratesStaleFailedState` MUST be true on the resize path and false on
|
|
37048
|
+
* create. It exists for the RECOVERY case: `SUSPENDED` (a resize that could
|
|
37049
|
+
* not complete) is precisely the state a user re-runs `cdkd deploy` to fix,
|
|
37050
|
+
* but the same pre-resize read lag documented above means the first poll
|
|
37051
|
+
* after `ModifyInstanceFleet` still reports `SUSPENDED`. Enforcing
|
|
37052
|
+
* {@link FAILED_STATES} on that stale read would abort the very resize that
|
|
37053
|
+
* recovers the fleet, on every attempt, making the state permanently
|
|
37054
|
+
* unrecoverable through cdkd.
|
|
37055
|
+
*
|
|
37056
|
+
* So on a resize the failed-state check is suppressed until the fleet is
|
|
37057
|
+
* observed LEAVING whatever state it started in; from then on it is enforced
|
|
37058
|
+
* normally (a resize that transitions RUNNING -> RESIZING -> SUSPENDED still
|
|
37059
|
+
* fails fast, which is the case #1092 item 2 is about). A fleet that never
|
|
37060
|
+
* leaves its initial state falls through to the `maxWaitMs` timeout — the
|
|
37061
|
+
* pre-fix behavior, and strictly better than an unrecoverable resource.
|
|
37062
|
+
*
|
|
37063
|
+
* Note the asymmetry that makes this specific to failed states: a stale
|
|
37064
|
+
* pre-resize `RUNNING` is harmless because the capacity check keeps the loop
|
|
37065
|
+
* polling. A stale `SUSPENDED` is terminal.
|
|
37066
|
+
*/
|
|
37067
|
+
async waitForFleetReady(clusterId, fleetId, logicalId, resourceType, targetCapacity, atLeast, toleratesStaleFailedState = false) {
|
|
36667
37068
|
const startTime = Date.now();
|
|
36668
37069
|
const transientState = { count: 0 };
|
|
37070
|
+
let initialState;
|
|
37071
|
+
let hasLeftInitialState = false;
|
|
36669
37072
|
while (Date.now() - startTime < this.maxWaitMs) {
|
|
36670
37073
|
const fleet = await this.findFleetForPoll(clusterId, fleetId, transientState);
|
|
36671
37074
|
const state = fleet?.Status?.State;
|
|
36672
37075
|
const provisioned = (fleet?.ProvisionedOnDemandCapacity ?? 0) + (fleet?.ProvisionedSpotCapacity ?? 0);
|
|
36673
37076
|
const capacityReady = atLeast ? provisioned >= targetCapacity : provisioned <= targetCapacity;
|
|
37077
|
+
if (state) {
|
|
37078
|
+
if (initialState === void 0) initialState = state;
|
|
37079
|
+
else if (state !== initialState) hasLeftInitialState = true;
|
|
37080
|
+
}
|
|
36674
37081
|
if (state && READY_STATES.has(state) && capacityReady) return;
|
|
36675
|
-
|
|
37082
|
+
const enforceFailedStates = !toleratesStaleFailedState || hasLeftInitialState;
|
|
37083
|
+
if (state && enforceFailedStates && FAILED_STATES.has(state)) throw new ProvisioningError(`EMR instance fleet ${fleetId} entered failed state ${state}: ${fleet?.Status?.StateChangeReason?.Message ?? "no state-change reason reported"}`, resourceType, logicalId, fleetId);
|
|
37084
|
+
if (state && !enforceFailedStates && FAILED_STATES.has(state)) this.logger.debug(`EMR instance fleet ${fleetId} still reads ${state} from before the resize; waiting for it to leave that state before treating it as failed`);
|
|
36676
37085
|
this.logger.debug(`EMR instance fleet ${fleetId} state: ${state ?? "unknown"}, provisioned ${provisioned}/${targetCapacity}, waiting...`);
|
|
36677
37086
|
await new Promise((resolve) => setTimeout(resolve, this.pollIntervalMs));
|
|
36678
37087
|
}
|
|
@@ -61608,7 +62017,7 @@ function reorderArgs(argv) {
|
|
|
61608
62017
|
async function main() {
|
|
61609
62018
|
installPipeCloseHandler();
|
|
61610
62019
|
const program = new Command();
|
|
61611
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
62020
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.252.0");
|
|
61612
62021
|
program.addCommand(createBootstrapCommand());
|
|
61613
62022
|
program.addCommand(createSynthCommand());
|
|
61614
62023
|
program.addCommand(createListCommand());
|