@go-to-k/cdkd 0.302.3 → 0.303.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-DCrwgSCZ.js → asg-provider-CJIPV0St.js} +11 -8
- package/dist/asg-provider-CJIPV0St.js.map +1 -0
- package/dist/{aws-client-defaults-DBXBcg0J.js → aws-client-defaults-BX9Y_fKp.js} +11 -2
- package/dist/{aws-client-defaults-DBXBcg0J.js.map → aws-client-defaults-BX9Y_fKp.js.map} +1 -1
- package/dist/{aws-clients-B_CEQGE1.js → aws-clients-BJ7dmM_T.js} +3 -8
- package/dist/aws-clients-BJ7dmM_T.js.map +1 -0
- package/dist/{aws-failure-text-e_ddrDDo.js → aws-failure-text-w5_iao51.js} +3 -2
- package/dist/{aws-failure-text-e_ddrDDo.js.map → aws-failure-text-w5_iao51.js.map} +1 -1
- package/dist/cli.js +3 -2
- package/dist/cli.js.map +1 -1
- package/dist/db-instance-modify-retry-f79-MmD9.js +38 -0
- package/dist/db-instance-modify-retry-f79-MmD9.js.map +1 -0
- package/dist/{deploy-engine-D9SDE-Pj.js → deploy-engine-Bx98Q92J.js} +5433 -303
- package/dist/deploy-engine-Bx98Q92J.js.map +1 -0
- package/dist/{iam-policy-targets-OtGPguLH.js → iam-policy-targets-NZn9Idzv.js} +150 -11
- package/dist/iam-policy-targets-NZn9Idzv.js.map +1 -0
- package/dist/{iam-role-provider-Q1sv3tF1.js → iam-role-provider-C8WZMqgE.js} +13 -8
- package/dist/iam-role-provider-C8WZMqgE.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/{neptune-provider-DjVgEWGf.js → neptune-provider-NdOn7ncA.js} +77 -53
- package/dist/neptune-provider-NdOn7ncA.js.map +1 -0
- package/dist/{program-DSbJoNKe.js → program-TEYrbsKm.js} +1938 -233
- package/dist/program-TEYrbsKm.js.map +1 -0
- package/dist/{provider-classes-CDtquwkU.js → provider-classes-g1s3vsUl.js} +1491 -564
- package/dist/provider-classes-g1s3vsUl.js.map +1 -0
- package/dist/{rds-provider-ByCpdpUq.js → rds-provider-BlAiy5-P.js} +90 -68
- package/dist/rds-provider-BlAiy5-P.js.map +1 -0
- package/dist/{version-DDpKuzTw.js → version-BhQymxXT.js} +2 -2
- package/dist/{version-DDpKuzTw.js.map → version-BhQymxXT.js.map} +1 -1
- package/package.json +1 -1
- package/dist/asg-provider-DCrwgSCZ.js.map +0 -1
- package/dist/aws-clients-B_CEQGE1.js.map +0 -1
- package/dist/deploy-engine-D9SDE-Pj.js.map +0 -1
- package/dist/iam-policy-targets-OtGPguLH.js.map +0 -1
- package/dist/iam-role-provider-Q1sv3tF1.js.map +0 -1
- package/dist/neptune-provider-DjVgEWGf.js.map +0 -1
- package/dist/program-DSbJoNKe.js.map +0 -1
- package/dist/provider-classes-CDtquwkU.js.map +0 -1
- package/dist/rds-provider-ByCpdpUq.js.map +0 -1
- package/dist/secret-redaction-BHWS-PZK.js +0 -3937
- package/dist/secret-redaction-BHWS-PZK.js.map +0 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { a as runWithStackAwsClients, i as resetAwsClients, o as setAwsClients, t as AwsClients } from "./aws-clients-
|
|
2
|
-
import { $ as stringifyJsonPayload, A as isNameGeneration, B as SECRET_REF_MAX_CODE_POINTS, C as generateResourceName, F as withSkipPrefix, G as displayAwsMessage, H as UNRENDERABLE, I as withStackScope, J as displayStackName, K as displayIdent, Q as safeMsg, V as STACK_REF_MAX_CODE_POINTS, W as cutMarker, Y as isPasteableIdent, Z as plainIdentOr, _ as PATTERN_B_RESOURCE_TYPES, a as safeStringify, b as derivesPatternBName, c as isStdoutReservedForPayload, d as runStackBuffered, et as terminalSafe, g as PATTERN_B_NAME_PROPERTIES, h as PATTERN_B_NAME_OPTIONS, l as reserveStdoutForPayload, m as getLiveRenderer, n as describeAwsFailure, q as displaySafe, s as getLogger, tt as truncateCodePoints, x as explicitNamePropertyFor, z as SAFE_MSG_ALTERED_CHAR } from "./aws-failure-text-
|
|
3
|
-
import {
|
|
4
|
-
import { a as setAssumedRoleCredentials,
|
|
5
|
-
import { $ as pendingRequestCount, $a as refuseMalformedOutputs, $c as absoluteAssemblyPathEscape, $i as isImpossibleEmptyStoredAttribute, $n as noEchoExactEchoLeavesOf, $o as prefetchCreateOnlyPropertyPaths, $r as applyCustomResourceResponseExpiration, $s as buildDockerImage, $t as nameGenerationCarriedFrom, A as inheritedProvenanceForChildDiff, Aa as malformedLocalResourceEntriesWarning, Ac as resolveUseCdkBootstrapAssets, Ai as refuseUnreadableCdkCrossAccountRefs, Ao as NO_CC_HANDLERS_REASON, As as orphansAfterRollback, At as replayRollback, B as conditionFingerprint, Ba as malformedResourceEntriesWarning, Bc as decodeListingKey, Bi as sendableAssumeRoleCommand, Bn as yellow, Bs as loadPublishableAssetManifest, Bt as replacementNeverSwapped, Ca as hasReadableResources, Cc as loadCdkJson, Cn as bindingSkippedOutputs, Co as unreadableOrphanRecords, Cr as ledgerForStack, Cs as exportNamesCarriedFrom, Ct as sameJournaledOrphans, D as IMPLICIT_DELETE_DEPENDENCIES, Da as malformedExportNamesWarning, Dc as resolveSkipPrefix, Dn as constructPathOf, Dr as DeploymentEventsReader, Ds as importableOutputKeys, Ea as malformedDriftResourcePropertiesWarning, Ec as resolveCaptureObservedState, En as maskingRetryLogger, Eo as findActionableSilentDrops, Es as hasUnverifiableParameterRefusal, F as findNestedTemplateTreeDefect, Fa as malformedOrphansRefusalMessage, Fc as CFN_TEMPLATE_URL_LIMIT, Fi as applyRoleArnIfSet, Fn as bold, Fo as isStatefulRecreateTargetForReplace, Fs as isSensitiveAttributeName, Ft as isReplacementOrphan, G as withResourceDeadline, Ga as previewableOrphanRecords, Gc as AssemblyReader, Gi as classifyRecordedSecretRegion, Gn as removeSuperseded, Gr as wouldReturnToSdkProvider, Gs as ensureAssetStorage, H as parentSuppliedValues, Ha as malformedResourcesWarning, Hc as PARTITION_TABLE, Hi as applyCallerIdentityCredentials, Hn as deleteSupersededEntry, Hr as ProviderRegistry, Hs as AssetModeResolver, Ht as seedsDeleteFirstGuard, I as isAbsoluteAssetPath, Ia as malformedOrphansWarning, Ic as MIGRATE_TMP_PREFIX, Ii as assertFlagRoleArn, In as cyan, Io as isStatefulRecreateTargetSync, Is as stringifyValue, It as planFailedOps, J as keptWholeReasonText, Ja as refuseMalformedOrphanRecords, Jc as defineOwnKey, Ji as regionLessSecretName, Jn as supersededHolderOf, Jr as readsNoEchoDeleteValues, Js as parseBootstrapMarker, K as diffSecurity, Ka as refuseDivergentRecordRegionForDestroy, Kc as StageLoadError, Ki as classifyReplaySecretRegion, Kn as supersededAfterRollback, Ks as getBootstrapMarkerKey, Kt as inheritProducerRegions, L as listNestedTemplateRows, La as malformedOutputsRefusalMessage, Lc as findLargeInlineResources, Li as assertSendableRoleArn, Ln as gray, Lo as renderStatefulReason, Ls as WorkGraph, Lt as planRollback, M as findServiceTokenRefusals, Ma as malformedOrphanRecordsRefusalMessage, Mc as warnDeprecatedNoPrefixCliFlag, Mo as findReplacedReadersOfRecreateTargets, Ms as shouldRetainResource, Mt as recordAfterRollbackUpdate, N as serviceTokenBlockingReason, Na as malformedOrphanRecordsWarning, Nc as ssmResolvedValueType, Ni as recordedOutputReferenceRegions, Nn as withStackRecords, No as shownType, Ns as stackInstanceIdCarriedFrom, Nt as demoteSupersededOrphans, O as computeImplicitDeleteEdges, Oa as malformedExportSourceWarning, Oc as resolveStateBucketWithDefault, Oi as resolveDynamicReferences, On as findDestructiveChanges, Oo as unwrittenCreateOnlyReplacement, Or as DeploymentEventsStore, Os as importableOutputs, P as childTemplateLoader, Pa as malformedOrphanRowsKeptWarning, Pc as CFN_TEMPLATE_BODY_LIMIT, Pi as recordedReferenceRegions, Po as MULTI_REGION_RECREATE_BLOCKED_TYPES, Ps as AssetPublisher, Pt as isJournaledOrphan, Q as planOrphanAdoption, Qa as refuseMalformedOrphansForOrphan, Qc as ownValue, Qi as getAccountInfo, Qn as noEchoComparisonForTemplate, Qr as CUSTOM_RESOURCE_RESPONSE_LIFECYCLE_RULE_ID, Qs as buildDenyExternalAccessPolicy, Qt as updatePartialReason, R as renderNestedTemplateTreeDefect, Ra as malformedOutputsWarning, Rc as uploadCfnTemplate, Ri as explicitRoleArnOrThrow, Rn as green, Rs as buildAssetRedirectMap, Rt as recheckFailedPlan, Sa as hasReadableOutputs, Sc as getLegacyStateBucketName, Sn as isExportAliasCollision, So as unpreviewableOrphanRecords, Ss as describeRegionValueKind, St as makeForeignHolderScan, Ta as isReadableResourceEntry, Tc as resolveAutoAssetStorage, Tn as DEFAULT_RESOURCE_WARN_AFTER_MS, To as withheldAccountClause, Tr as withCreateTokenLedger, Ts as hasReasonlessBaselineRefusal, U as readRecordedConditionVerdicts, Ua as malformedScrubResourceEntriesRefusalMessage, Uc as canonicalizeRegion, Ui as callerEnvCredentials, Un as holdsSupersededResources, Ur as ccBrokenReason, Us as BOOTSTRAP_MARKER_PREFIX, Ut as orphanNeedsIdentity, V as conditionInputsFrom, Va as malformedResourcePropertiesWarning, Vc as expectedOwnerParam, Vi as AWS_CREDENTIAL_ENV_KEYS, Vn as addSuperseded, Vr as CC_BROKEN_REASON, Vs as rewriteTemplateAssetReferences, Vt as resolveReplacementOldType, Wa as malformedStateRefusalMessage, Wc as derivePartitionAndUrlSuffix, Wi as envCredentialsAreAssumedRole, Wn as isSupersededResourceRecord, Wr as isCustomResource, Ws as assertAssetBucketRegion, Wt as findNestedStackTypeChanges, X as makeCanonicalizePropertiesFn, Xa as refuseMalformedOrphans, Xc as hasPlainPrototype, Xi as isUnboundTemplateParameter, Xn as withoutSuperseded, Xs as validateAssetBucketName, Y as mergeNoChangeOutputs, Ya as refuseMalformedOrphanRecordsForDestroy, Yc as hasOwnKey, Yi as coerceParameterTypedValue, Yn as supersededResourcesOf, Yr as isCustomResourceType$1, Ys as readBootstrapMarkerBody, Z as makeSiblingClaimReader, Za as refuseMalformedOrphansForDestroy, Zc as nullPrototypeRecord, Zi as parameterTypeMayLoseSecretIdentity, Zn as noEchoAttributeNamesOf, Zr as withPrintingSecrets, Zs as validateContainerRepoName, Zt as updatePartialMessage, _a as UNREADABLE_RESOURCES_MAP_ROW, _c as AssetManifestLoader, _n as exportNameSecretExposure, _o as repairMalformedOrphansForReadOnly, _r as createPreDeleteFinalSnapshot, _s as isNotImplemented, _t as dropFailedCommands, a as isProtectionValueActive, aa as cfnRefValueFromPhysicalId, ac as finchSecretArgvRefusal, ai as UNSPECIFIED_SKIP_REASON, al as injectedAwsRegionValue, an as computeParameterTaint, ao as refuseMalformedResourceEntriesForImport, ar as namingSecretsWithRecordedNoEcho, at as displacedPhysicalIdShown, ba as displayLogicalId, bc as synthesisStatusMessage, bi as IntrinsicFunctionResolver, bn as collectDeclaredOutputNames, bo as repairMalformedResourcePropertiesForReadOnly, br as refusesFinalSnapshot, bs as splitImportedOps, bt as journaledOrphanLines, cc as isDockerClientEnvKey, ci as deleteSkipReason, cn as resourcesNamingDeclaredParameter, co as refuseMalformedResourcePropertiesForDestroy, cr as secretNamePrintingBags, cs as LockManager, ct as nestedChildStackName$1, dc as redactDockerArgvValues, dn as exportAliasCollisionScrubWarning, do as refuseMalformedResourcePropertiesForOrphan, dr as stateSecretNameNeedles, ds as rebuildClientForBucketRegion, dt as revertedNestedRowIds, ea as carriesDynamicReference, ec as resolveDockerContextDirectory, ei as customResourceResponseExpirationWarning, el as describeFileReadFailure, en as nameGenerationHighWaterOf, eo as refuseMalformedOutputsForDestroy, er as noEchoParameterNamesOf, es as templateResourceTypes, et as resolvePendingCloudControlRequests, fa as declaresNoEcho, fc as redactedCause, fn as secretBearing, fo as refuseMalformedResourcesForDeploy, fr as storedSecretNameNeedles, fs as CrossPrefixReadError, ft as withNestedRevertRun, ga as UNREADABLE_ORPHANS_CONTAINER_ROW, gc as warnFinchArgvExposure, gn as exportNameNoEchoParameters, go as repairMalformedOrphanRecordsForReadOnly, gr as ccRoutedFinalSnapshotError, gs as isAccessDenied, gt as deleteJournaledOrphans, ha as STATE_RESOURCES_MALFORMED, hc as runDockerStreaming, hn as carriedExportAliasExposure, ho as refuseMalformedState, hr as buildFinalSnapshotIdentifier, hs as crossPrefixDeniedWarning, ht as withNestedStackContext, i as PROTECTION_PROPERTY_BY_TYPE, ia as AWS_NO_VALUE, ic as dockerSpawnEnvWithSensitive, ii as RollbackInlinePolicyWriters, il as injectAwsRegion, in as retiredNameGenerationsLeaving, io as refuseMalformedResourceEntriesForDestroy, ir as maskEventTextWithBoundBags, it as displacedOpClause, j as deferredServiceTokenWarning, ja as malformedLocalResourcesWarning, jc as stateBucketExistenceConfirmed, ji as keepHeldTokenProvenance, jn as destroyStackRecordsView, jo as hasNoCloudControlHandlers, js as orphansCarriedFrom, jt as removeProtectionTypeList, k as crossStackOutputProvenanceSource, ka as malformedLocalOutputsWarning, kc as resolveStateBucketWithDefaultAndSource, ki as producerLookupOver, kn as formatDestructiveChange, kr as extractDeploymentEventError, ks as isReadableBag, kt as replayFailedOperations, la as withSharedDrainBudget, lc as isMalformedEnvKey, ln as WITHHELD_NAME_DISPLAY, lo as refuseMalformedResourcePropertiesForDrift, ls as isLockInfoExpired, lt as recordDisplacedSkips, ma as STATE_REGION_DIVERGED, mc as runDockerForeground, mn as secretSafeKeyDisplay, mo as refuseMalformedResourcesForExport, mr as PRE_DELETE_SNAPSHOT_TYPES, ms as applyCrossPrefixScan, mt as getCurrentNestedStackContext, n as recordedProtectionEvidence, na as isNamelessDynamicReferenceError, nc as describeDockerExecFailure, ni as slowCcOperationTimeoutMs, nl as renderAssemblyPathEscape, nn as retiredAfterRollback, no as refuseMalformedResourceEntries, nr as completedReplayEntries, ns as withRetry, nt as recordRetainedForReadoption, o as perType, oa as isStalePlaceholderArnAttribute, oc as formatDockerLoginError, oi as deleteIndeterminateGuards, on as namesAnyDeclaredParameter, oo as refuseMalformedResourceEntriesForImportSave, or as orphanRecordsPrintingBag, os as DagBuilder, ot as dropSettledNestedJournals, pa as JSON_LISTING_HOLE_VALUE, pc as redactedDockerCause, pn as secretBearingStateKeyWarning, po as refuseMalformedResourcesForDestroy, pr as ATOMIC_FINAL_SNAPSHOT_TYPES, ps as CrossPrefixScanCache, pt as collectNestedChildUnaddressed, q as isSecretBearingReferenceString, qc as processStackMessages, qi as producerRegionsFromState, qn as supersededHeldByOtherTypeWarning, qr as TemplateNoEchoReresolver, qs as isCrossRegionRedirect, r as recordedProtectionNote, ra as keepsSecretReferenceToken, rc as describeDockerFailure, rl as resolveAssemblyPath, rn as retiredNameGenerationsAfter, ro as refuseMalformedResourceEntriesForDeploy, rr as journaledOrphanPrintingBag, rt as NESTED_PENDING_PARENT_REASON, s as readProtection, sa as refStateLookupFromResource, sc as getDockerCmd, si as deleteLeftInPlace, sl as resolveBucketRegion, sn as reachableDivergentParameters, so as refuseMalformedResourceEntriesForOrphan, sr as secretNamePrintingBag, ss as TemplateParser, t as DeployEngine, ta as carriesFetchableDynamicReference, tc as describeDockerCapturedOutput, ti as CloudControlProvider, tl as displayAssemblyPath, tn as nameScopeOf, to as refuseMalformedResourceAttributesForOrphan, tr as createReadOnlyAttributeHealerFactory, ts as describeTypeWithThrottleRetry, tt as keptForRetainedRecord, ua as parameterInputsFor, uc as partitionSensitiveEnv, ui as isWaitAbandonedError, un as displayTextOrWithheld, uo as refuseMalformedResourcePropertiesForExport, ur as secretNamedRecordNeedles, us as S3StateBackend, va as accountArgs, vc as getDockerImageBySourceHash, vi as DiffCalculator, vn as isWholeDynamicReferenceValue, vo as repairMalformedOutputsForReadOnly, vr as effectiveDeletionPolicy, vs as stackInstanceIdOf, vt as dropFailedHint, wa as isReadableOrphanRecord, wc as resolveApp, wn as DEFAULT_RESOURCE_TIMEOUT_MS, wo as unreadableResourceEntries, ws as hasReadableExportSet, wt as formatResourceLine, xa as hasReadableOrphans, xc as getDefaultStateBucketName, xi as poisonRenderedSpellingsCollidingIn, xn as collectPublishedOutputNames, xo as repairMalformedResourcesForReadOnly, xr as unsupportedFinalSnapshotError, xs as acceptedCreateOnlyDropsOf, xt as loadJournaledOrphans, ya as deployRefusesOrphanRowsReason, yc as Synthesizer, yi as INTRINSIC_KEYS, yn as noEchoParameterValueSeed, yo as repairMalformedResourceEntriesForReadOnly, yr as isFinalSnapshotError, ys as withStackInstanceIdWithheld, yt as isHandledOrphan, z as templateIdentity, za as malformedRenderedContainersWarning, zc as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, zi as isIamRoleArn, zn as red, zs as createAssetRedirectResolver, zt as recordUnderIdIsNotOwn } from "./deploy-engine-D9SDE-Pj.js";
|
|
6
|
-
import {
|
|
7
|
-
import { t as getCdkdVersion } from "./version-DDpKuzTw.js";
|
|
1
|
+
import { a as runWithStackAwsClients, i as resetAwsClients, o as setAwsClients, t as AwsClients } from "./aws-clients-BJ7dmM_T.js";
|
|
2
|
+
import { $ as stringifyJsonPayload, A as isNameGeneration, B as SECRET_REF_MAX_CODE_POINTS, C as generateResourceName, F as withSkipPrefix, G as displayAwsMessage, H as UNRENDERABLE, I as withStackScope, J as displayStackName, K as displayIdent, Q as safeMsg, U as asIsMessage, V as STACK_REF_MAX_CODE_POINTS, W as cutMarker, Y as isPasteableIdent, Z as plainIdentOr, _ as PATTERN_B_RESOURCE_TYPES, a as safeStringify, b as derivesPatternBName, c as isStdoutReservedForPayload, d as runStackBuffered, et as terminalSafe, g as PATTERN_B_NAME_PROPERTIES, h as PATTERN_B_NAME_OPTIONS, l as reserveStdoutForPayload, m as getLiveRenderer, n as describeAwsFailure, q as displaySafe, s as getLogger, tt as truncateCodePoints, x as explicitNamePropertyFor, z as SAFE_MSG_ALTERED_CHAR } from "./aws-failure-text-w5_iao51.js";
|
|
3
|
+
import { An as StateError, Cn as NestedStackChildDirectDestroyError, Cr as markNonRetryable, Dn as ResourceUpdateNotSupportedError, E as isMarkedNotGone, En as ResourceTimeoutError, Fn as normalizeAwsError, Gt as formatLockExpiry, Ht as buildForceUnlockCommand, I as remedyLogicalId, In as withErrorHandling, Jn as shellBoundedDisplay, Kn as plainOrDescribed, Kt as formatLockOperation, Ln as TIMEOUT_FLAG_RESOURCE_TYPE, Mn as formatError, O as assertRegionMatch, On as StackHasActiveImportsError, Qn as setPasteableVerbFlags, Qt as injectiveKey, Sn as MalformedProducerRecordRefusalError, Sr as isThrottlingError, T as RESOURCE_NOT_FOUND, Tn as ProvisioningError, Un as isInertUnquoted, Ut as buildLockContentionMessage, Vn as commandHole, Vt as UNREPRODUCIBLE_LOCK_CLAUSE, Wn as pasteableCommand, Wt as forceQuitRecoveryClause, Xn as withheldTargetClause, Xt as CUSTOM_RESOURCE_RESPONSE_PREFIX, Yn as shellQuote, Z as runDeleteAttempt, Zn as setPasteableRunFlags, Zt as DEFAULT_STATE_PREFIX, _n as IntrinsicResolutionRefusalError, _r as isRetryableTransientError, an as ambientCredentialConfig, bt as resourceTypeShown, cn as AccountIdUnavailableError, cr as escapeRegExp, ct as endCommandInterruptScope, en as producerCoordinateKey, fn as CrossAccountSecretRefusalError, gn as DynamicReferenceRegionAmbiguousError, in as ambientClientDefaults, jn as SynthesisError, k as VALUE_NOT_SHOWN, kn as StackTerminationProtectionError, lr as stripControlChars, lt as interruptWatchListenerCount, mn as DeployCancelledError, mr as isAmbiguousOutcomeError, n as RESOLVED_PREVIOUS_PRINCIPAL_KEYS, nn as setPasteableAwsProfile, on as clientDefaultsFor, qn as quotedOrDescribed, qt as formatLockOwner, rn as withPasteableAwsProfile, rt as isAwsCliLiteral, s as readRecordedPrincipals, sn as credentialFingerprint, st as beginCommandInterruptScope, t as IAM_PRINCIPAL_NAME, tn as producerRecordKey, un as CdkdError, ur as stripForDisplay, ut as isInterruptedWaitError, vn as LocalInvokeBuildError$1, vr as isTransientServerError, vt as logicalIdShown, wn as PartialFailureError, xr as isMarkedNonRetryable, yn as LocalStartServiceError, yr as retryClassificationText, zn as SHELL_ACTIVE_WHY } from "./iam-policy-targets-NZn9Idzv.js";
|
|
4
|
+
import { a as setAssumedRoleCredentials, d as formatAuthority, i as getPreAssumeEnvCredentials, n as awsClientDefaults, r as getAssumedRoleCredentials, u as runInStackAwsScope } from "./aws-client-defaults-BX9Y_fKp.js";
|
|
5
|
+
import { $ as pendingRequestCount, $a as malformedLocalResourcesWarning, $c as importableOutputKeys, $i as keepHeldTokenProvenance, $l as resolveApp, $n as destroyStackRecordsView, $o as hasNoCloudControlHandlers, $s as logOnlyValueCount, $t as seedsDeleteFirstGuard, A as inheritedProvenanceForChildDiff, Ac as recordProvenPublicExpression, Ai as deleteSkipReason, Al as describeDockerCapturedOutput, An as isWholeDynamicReferenceValue, Ao as refuseMalformedResourcePropertiesForDestroy, Ar as secretNamePrintingBags, As as canonicalCoordinates, At as readRefactorMarkerOnce, Au as ownValue, B as conditionFingerprint, Ba as accountArgs, Bc as applyCrossPrefixScan, Bi as DiffCalculator, Bl as redactDockerArgvValues, Bn as findDestructiveChanges, Bo as repairMalformedOutputsForReadOnly, Br as effectiveDeletionPolicy, Bs as readbackPathFor, Bt as replayFailedOperations, Ca as carriesFetchableDynamicReference, Cc as splitGetAttStringForm, Ci as CloudControlProvider, Cl as parseBootstrapMarker, Cn as exportAliasCollisionScrubWarning, Co as refuseMalformedResourceAttributesForOrphan, Cr as createReadOnlyAttributeHealerFactory, Cs as describeTypeWithThrottleRetry, Ct as sameJournaledOrphans, Cu as describeJsonParseFailure, D as IMPLICIT_DELETE_DEPENDENCIES, Da as cfnRefValueFromPhysicalId, Dc as recordMaskOnlyValuesIn, Di as UNSPECIFIED_SKIP_REASON, Dl as buildDenyExternalAccessPolicy, Dn as carriedExportAliasExposure, Do as refuseMalformedResourceEntriesForImport, Dr as namingSecretsWithRecordedNoEcho, Du as hasOwnKey, Ea as AWS_NO_VALUE, Ec as recordMaskOnlyValue, Ei as RollbackInlinePolicyWriters, El as validateContainerRepoName, En as secretSafeKeyDisplay, Eo as refuseMalformedResourceEntriesForDestroy, Er as maskEventTextWithBoundBags, Eu as defineOwnKey, F as findNestedTemplateTreeDefect, Fa as JSON_LISTING_HOLE_VALUE, Fc as isLockInfoExpired, Fl as formatDockerLoginError, Fn as bindingSkippedOutputs, Fo as refuseMalformedResourcesForDestroy, Fr as ATOMIC_FINAL_SNAPSHOT_TYPES, Fs as maskedLeafCoordinatesOf, Ft as updateRefactorMarker, Fu as resolveAssemblyPath, G as withResourceDeadline, Ga as hasReadableResources, Gc as withStackInstanceIdWithheld, Gl as warnFinchArgvExposure, Gn as rollbackOrphanIds, Go as unreadableOrphanRecords, Gr as ledgerForStack, Gt as isJournaledOrphan, H as parentSuppliedValues, Ha as displayLogicalId, Hc as isAccessDenied, Hi as IntrinsicFunctionResolver, Hl as redactedDockerCause, Hn as findRefactorHints, Ho as repairMalformedResourcePropertiesForReadOnly, Hr as refusesFinalSnapshot, Hs as recordedNoEchoValuesOf, Ht as removeProtectionTypeList, I as isAbsoluteAssetPath, Ia as STATE_REGION_DIVERGED, Ic as S3StateBackend, Il as getDockerCmd, In as DEFAULT_RESOURCE_TIMEOUT_MS, Io as refuseMalformedResourcesForExport, Ir as PRE_DELETE_SNAPSHOT_TYPES, Is as noEchoCoordinatesOf, It as hasPendingMovesOut, Iu as injectAwsRegion, J as keptWholeReasonText, Ja as malformedDriftResourcePropertiesWarning, Jc as describeRegionValueKind, Jl as Synthesizer, Jn as looselyEqual, Jo as findActionableSilentDrops, Jt as planRollback, K as diffSecurity, Ka as isReadableOrphanRecord, Kc as splitImportedOps, Kl as AssetManifestLoader, Kn as withMovedTo, Ko as unreadableResourceEntries, Ks as redactForState, Kt as isReplacementOrphan, L as listNestedTemplateRows, La as STATE_RESOURCES_MALFORMED, Lc as rebuildClientForBucketRegion, Ll as isDockerClientEnvKey, Ln as DEFAULT_RESOURCE_WARN_AFTER_MS, Lo as refuseMalformedState, Lr as buildFinalSnapshotIdentifier, Ls as noEchoLeavesOf, Lt as pendingMovesOutOf, Lu as injectedAwsRegionValue, M as findServiceTokenRefusals, Ma as parameterInputsFor, Mc as WHOLE_DYNAMIC_REFERENCE_PATTERN, Mi as isWaitAbandonedError, Ml as describeDockerFailure, Mn as collectDeclaredOutputNames, Mo as refuseMalformedResourcePropertiesForExport, Mr as secretNamedRecordNeedles, Ms as maskAtCoordinates, Mt as refactorMarkerRefusalMessage, Mu as describeFileReadFailure, N as serviceTokenBlockingReason, Nc as isSingleDynamicReferenceToken, Nl as dockerSpawnEnvWithSensitive, Nn as collectPublishedOutputNames, No as refuseMalformedResourcePropertiesForOrphan, Nr as stateSecretNameNeedles, Ns as maskReadbackAtCoordinates, Nt as refuseRefactorMarker, Nu as displayAssemblyPath, O as computeImplicitDeleteEdges, Oa as isStalePlaceholderArnAttribute, Oc as recordNoEchoAttributeValues, Oi as deleteIndeterminateGuards, Ol as buildDockerImage, On as exportNameNoEchoParameters, Oo as refuseMalformedResourceEntriesForImportSave, Or as orphanRecordsPrintingBag, Os as DagBuilder, Ou as hasPlainPrototype, P as childTemplateLoader, Pa as declaresNoEcho, Pc as LockManager, Pl as finchSecretArgvRefusal, Pn as isExportAliasCollision, Po as refuseMalformedResourcesForDeploy, Pr as storedSecretNameNeedles, Ps as maskWholeValue, Pt as sameMove, Pu as renderAssemblyPathEscape, Q as planOrphanAdoption, Qa as malformedLocalResourceEntriesWarning, Qc as hasUnverifiableParameterRefusal, Qi as refuseUnreadableCdkCrossAccountRefs, Ql as loadCdkJson, Qo as NO_CC_HANDLERS_REASON, Qs as literalSplitDelimitersOf, Qt as resolveReplacementOldType, R as renderNestedTemplateTreeDefect, Ra as UNREADABLE_ORPHANS_CONTAINER_ROW, Rc as CrossPrefixReadError, Rl as isMalformedEnvKey, Rn as maskingRetryLogger, Ro as repairMalformedOrphanRecordsForReadOnly, Rr as ccRoutedFinalSnapshotError, Rs as noEchoOutputsComparison, Rt as pendingMovesRefusalMessage, Sa as carriesDynamicReference, Sc as recordPhysicalNameCopy, Si as customResourceResponseExpirationWarning, Sl as isCrossRegionRedirect, Sn as displayTextOrWithheld, So as refuseMalformedOutputsForDestroy, Sr as noEchoParameterNamesOf, Ss as templateResourceTypes, St as makeForeignHolderScan, Su as processStackMessages, Ta as keepsSecretReferenceToken, Tc as provenPublicValue, Tl as validateAssetBucketName, Tn as secretBearingStateKeyWarning, To as refuseMalformedResourceEntriesForDeploy, Tr as journaledOrphanPrintingBag, Tu as utf8ByteLength, U as readRecordedConditionVerdicts, Ua as hasReadableOrphans, Uc as isNotImplemented, Ui as poisonRenderedSpellingsCollidingIn, Ul as runDockerForeground, Un as hintSidesOf, Uo as repairMalformedResourcesForReadOnly, Ur as unsupportedFinalSnapshotError, Us as valueAtCoordinate, Ut as recordAfterRollbackUpdate, V as conditionInputsFrom, Va as deployRefusesOrphanRowsReason, Vc as crossPrefixDeniedWarning, Vi as INTRINSIC_KEYS, Vl as redactedCause, Vn as formatDestructiveChange, Vo as repairMalformedResourceEntriesForReadOnly, Vr as isFinalSnapshotError, Vs as readsNoEchoSource, Vt as replayRollback, Wa as hasReadableOutputs, Wc as stackInstanceIdOf, Wl as runDockerStreaming, Wn as movedToHint, Wo as unpreviewableOrphanRecords, Ws as liveMatchesUnresolvedTokenFrame, Wt as demoteSupersededOrphans, X as makeCanonicalizePropertiesFn, Xa as malformedExportSourceWarning, Xc as hasReadableExportSet, Xi as resolveDynamicReferences, Xl as getDefaultStateBucketName, Xn as parseMappingFile, Xo as unwrittenCreateOnlyReplacement, Xr as DeploymentEventsStore, Xs as createUnionSecretMasker, Xt as recordUnderIdIsNotOwn, Y as mergeNoChangeOutputs, Ya as malformedExportNamesWarning, Yc as exportNamesCarriedFrom, Yl as synthesisStatusMessage, Yn as mappingPairs, Yr as DeploymentEventsReader, Ys as carryLogOnlyValues, Yt as recheckFailedPlan, Z as makeSiblingClaimReader, Za as malformedLocalOutputsWarning, Zc as hasReasonlessBaselineRefusal, Zi as producerLookupOver, Zl as getLegacyStateBucketName, Zn as proposeRenames, Zr as extractDeploymentEventError, Zs as hasMaskableValues, Zt as replacementNeverSwapped, _a as coerceParameterTypedValue, _c as inheritNestedStackParameterAssociations, _i as isCustomResourceType$1, _l as AssetModeResolver, _n as computeParameterTaint, _o as refuseMalformedOrphanRecordsForDestroy, _r as supersededResourcesOf, _t as dropFailedCommands, _u as PARTITION_TABLE, a as isProtectionValueActive, aa as assertSendableRoleArn, ac as createSecretMasker, al as stackInstanceIdCarriedFrom, ao as malformedOutputsRefusalMessage, ar as gray, as as renderStatefulReason, at as displacedPhysicalIdShown, au as resolveUseCdkBootstrapAssets, ba as getAccountInfo, bc as physicalNameCopiesOf, bi as CUSTOM_RESOURCE_RESPONSE_LIFECYCLE_RULE_ID, bl as ensureAssetStorage, bn as resourcesNamingDeclaredParameter, bo as refuseMalformedOrphansForOrphan, br as noEchoComparisonForTemplate, bt as journaledOrphanLines, bu as AssemblyReader, ca as sendableAssumeRoleCommand, cc as maskSecretsInText, cl as timePhase, cn as updatePartialMessage, co as malformedResourceEntriesWarning, cr as yellow, ct as nestedChildStackName$1, cu as ssmResolvedValueType, da as callerEnvCredentials, di as ccBrokenReason, dl as stringifyValue, dn as nameGenerationHighWaterOf, do as malformedScrubResourceEntriesRefusalMessage, dr as holdsSupersededResources, dt as revertedNestedRowIds, du as MIGRATE_TMP_PREFIX, ec as printingCorpusOf, el as importableOutputs, en as orphanNeedsIdentity, eo as malformedOrphanRecordsRefusalMessage, es as findReplacedReadersOfRecreateTargets, et as resolvePendingCloudControlRequests, eu as resolveAutoAssetStorage, fa as envCredentialsAreAssumedRole, fc as DYNAMIC_REFERENCE_TOKEN_SCAN, fi as isCustomResource, fl as WorkGraph, fn as nameScopeOf, fo as malformedStateRefusalMessage, fr as isSupersededResourceRecord, ft as withNestedRevertRun, fu as findLargeInlineResources, ga as regionLessSecretName, gc as identityKeyFor, gi as readsNoEchoDeleteValues, gl as rewriteTemplateAssetReferences, gn as withNameGenerationReached, go as refuseMalformedOrphanRecords, gr as supersededHolderOf, gt as deleteJournaledOrphans, gu as expectedOwnerParam, ha as producerRegionsFromState, hc as pathCrossesDottedKey, hi as TemplateNoEchoReresolver, hl as loadPublishableAssetManifest, hn as retiredNameGenerationsLeaving, hr as supersededHeldByOtherTypeWarning, ht as withNestedStackContext, hu as decodeListingKey, i as PROTECTION_PROPERTY_BY_TYPE, ia as assertFlagRoleArn, ic as unionOfSecretBags, il as shouldRetainResource, io as malformedOrphansWarning, ir as cyan, is as isStatefulRecreateTargetSync, it as displacedOpClause, iu as resolveStateBucketWithDefaultAndSource, j as deferredServiceTokenWarning, ja as withSharedDrainBudget, jc as wholeStringLeavesOf, jl as describeDockerExecFailure, jn as noEchoParameterValueSeed, jo as refuseMalformedResourcePropertiesForDrift, js as isMarkedCoordinate, jt as refactorMarkerMovesOf, ju as absoluteAssemblyPathEscape, k as crossStackOutputProvenanceSource, ka as refStateLookupFromResource, kc as recordNoEchoParameterFreshValue, ki as deleteLeftInPlace, kl as resolveDockerContextDirectory, kn as exportNameSecretExposure, ko as refuseMalformedResourceEntriesForOrphan, kr as secretNamePrintingBag, ks as TemplateParser, kt as readRefactorMarker, ku as nullPrototypeRecord, la as AWS_CREDENTIAL_ENV_KEYS, lc as scrubResourceRecord, li as CC_BROKEN_REASON, ll as AssetPublisher, ln as updatePartialReason, lo as malformedResourcePropertiesWarning, lr as addSuperseded, lt as recordDisplacedSkips, lu as CFN_TEMPLATE_BODY_LIMIT, ma as classifyReplaySecretRegion, mc as isUncertifiedBaselineMaskPosition, ml as createAssetRedirectResolver, mn as retiredNameGenerationsAfter, mo as refuseDivergentRecordRegionForDestroy, mr as supersededAfterRollback, mt as getCurrentNestedStackContext, mu as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, n as recordedProtectionEvidence, na as recordedReferenceRegions, nc as recordLogOnlyValue, nl as orphansAfterRollback, no as malformedOrphanRowsKeptWarning, ns as MULTI_REGION_RECREATE_BLOCKED_TYPES, nt as recordRetainedForReadoption, nu as resolveSkipPrefix, o as perType, oa as explicitRoleArnOrThrow, oc as errorCauseChain, ol as PhaseClock, oo as malformedOutputsWarning, or as green, ot as dropSettledNestedJournals, ou as stateBucketExistenceConfirmed, pa as classifyRecordedSecretRegion, pc as dynamicReferenceTokens, pi as wouldReturnToSdkProvider, pl as buildAssetRedirectMap, pn as retiredAfterRollback, po as previewableOrphanRecords, pr as removeSuperseded, pt as collectNestedChildUnaddressed, pu as uploadCfnTemplate, q as isSecretBearingReferenceString, qa as isReadableResourceEntry, qc as acceptedCreateOnlyDropsOf, ql as getDockerImageBySourceHash, qn as isPairableType, qo as withheldAccountClause, qr as withCreateTokenLedger, qt as planFailedOps, r as recordedProtectionNote, ra as applyRoleArnIfSet, rc as shareLogOnlyValues, rl as orphansCarriedFrom, rn as inheritProducerRegions, ro as malformedOrphansRefusalMessage, rr as bold, rs as isStatefulRecreateTargetForReplace, rt as NESTED_PENDING_PARENT_REASON, ru as resolveStateBucketWithDefault, s as readProtection, sa as isIamRoleArn, sc as maskSecretsInError, sl as renderPhaseTimings, so as malformedRenderedContainersWarning, sr as red, su as warnDeprecatedNoPrefixCliFlag, t as DeployEngine, ta as recordedOutputReferenceRegions, tc as recordLogOnlyParameterValue, tl as isReadableBag, tn as findNestedStackTypeChanges, to as malformedOrphanRecordsWarning, tr as withStackRecords, ts as shownType, tt as keptForRetainedRecord, tu as resolveCaptureObservedState, ua as applyCallerIdentityCredentials, uc as withPhysicalNameCopies, ui as ProviderRegistry, ul as isSensitiveAttributeName, un as nameGenerationCarriedFrom, uo as malformedResourcesWarning, ur as deleteSupersededEntry, uu as CFN_TEMPLATE_URL_LIMIT, va as isUnboundTemplateParameter, vc as recordNestedStackParameterExpressions, vl as BOOTSTRAP_MARKER_PREFIX, vn as namesAnyDeclaredParameter, vo as refuseMalformedOrphans, vr as withoutSuperseded, vt as dropFailedHint, vu as canonicalizeRegion, wa as isNamelessDynamicReferenceError, wc as carriesSecretMask, wi as slowCcOperationTimeoutMs, wl as readBootstrapMarkerBody, wn as secretBearing, wo as refuseMalformedResourceEntries, wr as completedReplayEntries, ws as withRetry, wt as formatResourceLine, wu as parseFailureDebugDetail, xa as isImpossibleEmptyStoredAttribute, xi as applyCustomResourceResponseExpiration, xl as getBootstrapMarkerKey, xn as WITHHELD_NAME_DISPLAY, xo as refuseMalformedOutputs, xr as noEchoExactEchoLeavesOf, xs as prefetchCreateOnlyPropertyPaths, xt as loadJournaledOrphans, xu as StageLoadError, ya as parameterTypeMayLoseSecretIdentity, yi as withPrintingSecrets, yl as assertAssetBucketRegion, yn as reachableDivergentParameters, yo as refuseMalformedOrphansForDestroy, yr as noEchoAttributeNamesOf, yt as isHandledOrphan, yu as derivePartitionAndUrlSuffix, z as templateIdentity, za as UNREADABLE_RESOURCES_MAP_ROW, zc as CrossPrefixScanCache, zl as partitionSensitiveEnv, zn as constructPathOf$1, zo as repairMalformedOrphansForReadOnly, zr as createPreDeleteFinalSnapshot, zt as refusePendingMovesOut, zu as resolveBucketRegion } from "./deploy-engine-Bx98Q92J.js";
|
|
6
|
+
import { t as getCdkdVersion } from "./version-BhQymxXT.js";
|
|
8
7
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
9
8
|
import { CopyObjectCommand, CreateBucketCommand, DeleteBucketCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetObjectCommand, HeadBucketCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, NotFound, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutBucketVersioningCommand, PutObjectCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
|
|
10
9
|
import { GetRoleCommand, GetUserCommand } from "@aws-sdk/client-iam";
|
|
@@ -21,12 +20,13 @@ import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
|
21
20
|
import { execFile, spawn } from "node:child_process";
|
|
22
21
|
import { tmpdir } from "node:os";
|
|
23
22
|
import { BatchDeleteImageCommand, DeleteRepositoryCommand, DescribeImagesCommand as DescribeImagesCommand$1, ECRClient, GetAuthorizationTokenCommand } from "@aws-sdk/client-ecr";
|
|
24
|
-
import { promisify } from "node:util";
|
|
23
|
+
import { isDeepStrictEqual, promisify } from "node:util";
|
|
25
24
|
import graphlib from "graphlib";
|
|
26
25
|
import { Command, InvalidArgumentError, Option } from "commander";
|
|
27
26
|
import * as readline$1 from "node:readline/promises";
|
|
28
27
|
import readline from "node:readline/promises";
|
|
29
|
-
import { Document, Pair, Scalar, YAMLMap, YAMLSeq, isScalar, parse, stringify, visit } from "yaml";
|
|
28
|
+
import { Document, Pair, Scalar, YAMLError, YAMLMap, YAMLSeq, isScalar, parse, parseDocument, stringify, visit } from "yaml";
|
|
29
|
+
import { emitKeypressEvents } from "node:readline";
|
|
30
30
|
import { A2A_CONTAINER_PORT, A2A_PATH, AGENTCORE_A2A_PROTOCOL, AGENTCORE_AGUI_PROTOCOL, AGENTCORE_MCP_PROTOCOL, ConnectionRegistry, DynamicReferenceResolver, EcsTaskResolutionError, HOST_GATEWAY_MIN_VERSION, LocalInvokeBuildError, MCP_CONTAINER_PORT, MCP_PATH, a2aInvokeOnce, addAlbSpecificOptions, addCommonEcsServiceOptions, addStartServiceSpecificOptions, albStrategy, architectureToPlatform, attachAuthorizers, attachStageContext, availableApiIdentifiers, bufferToBody, buildAgentCoreCodeImage, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildJwksUrlFromIssuer, buildMessageEvent, buildMgmtEndpointEnvUrl, buildStageMap, classifySourceChange, containsDynamicReference, createAuthorizerCache, createFileWatcher, createFileWatcher as createFileWatcher$1, createJwksCache, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, describeAwsFailureForWarn, discoverRoutes, discoverWebSocketApis, downloadAndExtractS3Bundle, filterRoutesByApiIdentifier, firstUsableRegion, groupRoutesByServer, handleConnectionsRequest, invokeAgentCore, invokeAgentCoreWs, keysNotFromTemplate, keysOverriddenBy, materializeLayerFromArn, mcpInvokeOnce, parseConnectionsPath, parseSelectionExpressionPath, pickAgentCoreCandidateStack, pickAgentCoreCandidateStack as pickAgentCoreCandidateStack$1, probeHostGatewaySupport, readMtlsMaterialsFromDisk, recordAssumedRole, resolveAgentCoreTarget, resolveDynamicReferencesInEnv, resolveEnvVars, resolveHostGatewayExtraHosts, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveSingleTarget, resolveWatchConfig, runEcsServiceEmulator, signAgentCoreInvocation, startApiServer, substituteImagePlaceholders, tryResolveImageFnJoin, verifyJwtViaDiscovery, waitForAgentCorePing, withoutKeys } from "cdk-local/internal";
|
|
31
31
|
import { createLocalStartAgentCoreCommand, createLocalStartCloudFrontCommand, createLocalStateProvider, getEmbedConfig, isCfnFlagPresent, listTargets, rejectExplicitCfnStackWithMultipleStacks, resolveCfnFallbackRegion, setEmbedConfig, substituteAgainstState, substituteAgainstStateAsync, substituteEnvVarsFromState, substituteEnvVarsFromStateAsync } from "cdk-local";
|
|
32
32
|
import { createServer } from "node:net";
|
|
@@ -130,7 +130,7 @@ function parseResourceTimeoutToken(flagName) {
|
|
|
130
130
|
try {
|
|
131
131
|
ms = parseDuration(durationPart);
|
|
132
132
|
} catch (err) {
|
|
133
|
-
const inner =
|
|
133
|
+
const inner = describeAwsFailure(err).detail;
|
|
134
134
|
throw new Error(`Invalid ${flagName} value "${raw}": ${inner}`);
|
|
135
135
|
}
|
|
136
136
|
acc.perTypeMs[typePart] = ms;
|
|
@@ -249,7 +249,7 @@ const allowUnaddressedOption = new Option("--allow-unaddressed", "Exit 0 even wh
|
|
|
249
249
|
const noCfnFallbackOption = new Option("--no-cfn-fallback", "Do not fall back to CloudFormation (ListExports / stack outputs) when an Fn::ImportValue / Fn::GetStackOutput reference is not found in cdkd state (default: fall back, so producers still managed by CloudFormation can be referenced)");
|
|
250
250
|
const annotationMessageOptions = [new Option("--strict", "Do not proceed with stacks carrying warning annotations").default(false), new Option("--ignore-errors", "Ignore error annotations from stack synthesis, which will likely produce an invalid deployment").default(false)];
|
|
251
251
|
const deployOptions = [
|
|
252
|
-
new Option("--concurrency <number>", "Maximum concurrent resource operations").default(
|
|
252
|
+
new Option("--concurrency <number>", "Maximum concurrent resource operations").default(20).argParser((value) => parseInt(value, 10)),
|
|
253
253
|
new Option("--stack-concurrency <number>", "Maximum concurrent stack deployments").default(4).argParser((value) => parseInt(value, 10)),
|
|
254
254
|
new Option("--asset-publish-concurrency <number>", "Maximum concurrent asset publish operations (S3 uploads + ECR push)").default(8).argParser((value) => parseInt(value, 10)),
|
|
255
255
|
new Option("--image-build-concurrency <number>", "Maximum concurrent Docker image builds").default(4).argParser((value) => parseInt(value, 10)),
|
|
@@ -2261,14 +2261,6 @@ function mapsEqual(a, b) {
|
|
|
2261
2261
|
}
|
|
2262
2262
|
return true;
|
|
2263
2263
|
}
|
|
2264
|
-
function describeParseFailure(err, bytes) {
|
|
2265
|
-
const raw = err instanceof Error ? err.message : String(err);
|
|
2266
|
-
const at = /at position (\d+)/.exec(raw);
|
|
2267
|
-
return `invalid JSON${at ? ` at code-unit position ${at[1]}` : ""}; ${bytes} byte(s) read`;
|
|
2268
|
-
}
|
|
2269
|
-
function utf8ByteLength(body) {
|
|
2270
|
-
return Buffer.byteLength(body, "utf8");
|
|
2271
|
-
}
|
|
2272
2264
|
function describeIndexVersion(value) {
|
|
2273
2265
|
return typeof value === "number" ? String(value) : `a non-numeric value (${typeof value})`;
|
|
2274
2266
|
}
|
|
@@ -2348,7 +2340,7 @@ var ExportIndexStore = class {
|
|
|
2348
2340
|
if (this.loadState.kind === "loaded") return new Map(this.loadState.entries);
|
|
2349
2341
|
const outcome = await this.loadPersisted();
|
|
2350
2342
|
if (outcome.kind === "missing") return void 0;
|
|
2351
|
-
if (outcome.kind === "corrupt") throw new Error(`Exports index at ${this.indexKey()} could not be parsed (${
|
|
2343
|
+
if (outcome.kind === "corrupt") throw new Error(`Exports index at ${this.indexKey()} could not be parsed (${describeJsonParseFailure(outcome.error, outcome.bytes)}). Its contents were NOT examined.`);
|
|
2352
2344
|
return new Map(outcome.entries);
|
|
2353
2345
|
}
|
|
2354
2346
|
async patchEntry(exportName, entry, opts = {}) {
|
|
@@ -2434,7 +2426,7 @@ var ExportIndexStore = class {
|
|
|
2434
2426
|
return;
|
|
2435
2427
|
}
|
|
2436
2428
|
if (outcome.kind === "corrupt") {
|
|
2437
|
-
this.logger.warn(`Exports index corrupt (${outcome.error
|
|
2429
|
+
this.logger.warn(`Exports index corrupt (${describeJsonParseFailure(outcome.error, outcome.bytes)}); rebuilding from state.json files`);
|
|
2438
2430
|
await this.rebuild();
|
|
2439
2431
|
return;
|
|
2440
2432
|
}
|
|
@@ -2718,7 +2710,7 @@ function collectCrossStackRefs(node, onRef, unconditional) {
|
|
|
2718
2710
|
//#region src/provisioning/register-providers.ts
|
|
2719
2711
|
let providerClasses;
|
|
2720
2712
|
function loadProviderClasses() {
|
|
2721
|
-
providerClasses ??= import("./provider-classes-
|
|
2713
|
+
providerClasses ??= import("./provider-classes-g1s3vsUl.js");
|
|
2722
2714
|
return providerClasses;
|
|
2723
2715
|
}
|
|
2724
2716
|
function registerAllProviders(registry, classes) {
|
|
@@ -3226,7 +3218,10 @@ var CrossPrefixGuard = class {
|
|
|
3226
3218
|
//#endregion
|
|
3227
3219
|
//#region src/cli/commands/deploy.ts
|
|
3228
3220
|
async function deployCommand(stacks, options) {
|
|
3221
|
+
const runClock = new PhaseClock("run", { fromProcessStart: true });
|
|
3222
|
+
runClock.lap("node startup + module load");
|
|
3229
3223
|
const providerClasses = await loadProviderClasses();
|
|
3224
|
+
runClock.lap("provider load");
|
|
3230
3225
|
const logger = getLogger();
|
|
3231
3226
|
if (options.verbose) {
|
|
3232
3227
|
logger.setLevel("debug");
|
|
@@ -3296,6 +3291,19 @@ async function deployCommand(stacks, options) {
|
|
|
3296
3291
|
})
|
|
3297
3292
|
};
|
|
3298
3293
|
})();
|
|
3294
|
+
const callerIdentityPromise = (async () => {
|
|
3295
|
+
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
3296
|
+
const stsClient = new STSClient({
|
|
3297
|
+
...awsClientDefaults(),
|
|
3298
|
+
region
|
|
3299
|
+
});
|
|
3300
|
+
try {
|
|
3301
|
+
return await stsClient.send(new GetCallerIdentityCommand({}));
|
|
3302
|
+
} finally {
|
|
3303
|
+
stsClient.destroy();
|
|
3304
|
+
}
|
|
3305
|
+
})();
|
|
3306
|
+
callerIdentityPromise.catch(() => void 0);
|
|
3299
3307
|
let deployInterrupted = false;
|
|
3300
3308
|
const topLevelSigintHandler = () => {
|
|
3301
3309
|
if (deployInterrupted) {
|
|
@@ -3322,7 +3330,9 @@ Force-quit: stack locks may not be released. If the next run reports a lock, run
|
|
|
3322
3330
|
...options.profile && { macroExpandS3ClientOpts: { profile: options.profile } },
|
|
3323
3331
|
deferMacroExpansion: true
|
|
3324
3332
|
};
|
|
3325
|
-
|
|
3333
|
+
runClock.lap("config, region, credentials");
|
|
3334
|
+
const [result, statePrep] = await Promise.all([timePhase("run", "synth (beside the state bucket prep)", () => synthesizer.synthesize(synthOptions)), timePhase("run", "state bucket prep (beside synth)", () => statePrepPromise)]);
|
|
3335
|
+
runClock.lap("synth + state bucket prep");
|
|
3326
3336
|
const { stateBucket, preflightStateBackend, exportIndexStore } = statePrep;
|
|
3327
3337
|
preflightBackendForCleanup = preflightStateBackend;
|
|
3328
3338
|
synthOptions.stateBucket = stateBucket;
|
|
@@ -3410,14 +3420,11 @@ Force-quit: stack locks may not be released. If the next run reports a lock, run
|
|
|
3410
3420
|
const stackRegions = new Map(await Promise.all(targetStacks.map(async (s) => [s, await regionForStack(s.stackName, s.region)])));
|
|
3411
3421
|
refuseStackNameRegionCollisions(sameNamedAppStacks, (s) => stackRegions.get(s) ?? s.region, selectedSet);
|
|
3412
3422
|
const crossPrefixGuard = new CrossPrefixGuard(preflightStateBackend, { readOnly: options.dryRun === true });
|
|
3423
|
+
runClock.lap("stack selection, stack regions");
|
|
3413
3424
|
await synthesizer.expandMacrosForStacks(targetStacks, synthOptions);
|
|
3414
|
-
|
|
3415
|
-
const
|
|
3416
|
-
|
|
3417
|
-
region
|
|
3418
|
-
});
|
|
3419
|
-
const accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
|
|
3420
|
-
stsClient.destroy();
|
|
3425
|
+
runClock.lap("macro expansion");
|
|
3426
|
+
const accountId = (await callerIdentityPromise).Account;
|
|
3427
|
+
runClock.lap("caller identity");
|
|
3421
3428
|
const assetPublisher = new AssetPublisher();
|
|
3422
3429
|
const useCdkBootstrapAssets = resolveUseCdkBootstrapAssets(options.useCdkBootstrapAssets);
|
|
3423
3430
|
const autoAssetStorage = resolveAutoAssetStorage(options.autoAssetStorage);
|
|
@@ -3644,7 +3651,8 @@ Force-quit: stack locks may not be released. If the next run reports a lock, run
|
|
|
3644
3651
|
...approveDeployment && {
|
|
3645
3652
|
requireApproval,
|
|
3646
3653
|
approveDeployment
|
|
3647
|
-
}
|
|
3654
|
+
},
|
|
3655
|
+
...stackInfo.terminationProtection === true && { terminationProtection: true }
|
|
3648
3656
|
};
|
|
3649
3657
|
const stackDeployEngine = new DeployEngine(stackStateBackend, stackLockManager, dagBuilder, diffCalculator, stackProviderRegistry, deployEngineOptions, stackRegion, exportIndexStore);
|
|
3650
3658
|
const deployResult = await withNestedStackContext({
|
|
@@ -3727,12 +3735,13 @@ Force-quit: stack locks may not be released. If the next run reports a lock, run
|
|
|
3727
3735
|
if (reach.unmatched.length > 0) throw new CdkdError(reach.errorMessage, "PIN_CC_API_INVALID");
|
|
3728
3736
|
for (const partial of reach.partial) logger.info(`--pin-cc-api ${partial.logicalId}: applies to ${partial.appliesTo.join(", ")}; not declared by ${partial.absentFrom.join(", ")}`);
|
|
3729
3737
|
}
|
|
3738
|
+
runClock.lap("work graph build, recreate checks");
|
|
3730
3739
|
await workGraph.execute({
|
|
3731
3740
|
"asset-build": options.imageBuildConcurrency,
|
|
3732
3741
|
"asset-publish": options.assetPublishConcurrency,
|
|
3733
3742
|
stack: options.stackConcurrency
|
|
3734
3743
|
}, async (node) => {
|
|
3735
|
-
if (node.type === "asset-build" || node.type === "asset-publish") await assetPublisher.executeNode(node);
|
|
3744
|
+
if (node.type === "asset-build" || node.type === "asset-publish") await timePhase("run", node.type, () => assetPublisher.executeNode(node), node.id);
|
|
3736
3745
|
else {
|
|
3737
3746
|
const { stack: stackInfo } = node.data;
|
|
3738
3747
|
if (!bufferStackOutput) {
|
|
@@ -3746,10 +3755,12 @@ Force-quit: stack locks may not be released. If the next run reports a lock, run
|
|
|
3746
3755
|
});
|
|
3747
3756
|
if (totalUnaddressed > 0 && !options.allowUnaddressed) throw new PartialFailureError(`Deploy left ${totalUnaddressed} resource(s) unaddressed, so they may still exist in AWS. The two cases differ in what happens next: a DELETE the provider could not issue KEEPS its state record, so the next 'cdkd deploy' re-attempts it, while a replacement's surviving predecessor is NOT tracked and will never be retried — delete it by hand. ` + (totalCrossPrefixKept > 0 ? "A replacement kept because another state prefix records the stack, or the check could not run, keeps its old resource and applies no new value — resolve the pair, or let the check run, and re-deploy. " : "") + "The per-stack summaries above give the breakdown, and each resource's own warning names its cause and remedy. " + (cancelledStacks > 0 ? `Note ${cancelledStacks} stack(s) were also cancelled and never deployed, so the template is not fully applied beyond the resources named above. ` : "") + `Pass --allow-unaddressed to exit 0 instead.`);
|
|
3748
3757
|
} finally {
|
|
3758
|
+
runClock.lap("assets + stacks, summary");
|
|
3749
3759
|
unforwardSigterm();
|
|
3750
3760
|
process.removeListener("SIGINT", topLevelSigintHandler);
|
|
3751
3761
|
awsClients.destroy();
|
|
3752
3762
|
preflightBackendForCleanup?.destroyClient();
|
|
3763
|
+
for (const line of renderPhaseTimings()) logger.info(line);
|
|
3753
3764
|
}
|
|
3754
3765
|
}
|
|
3755
3766
|
function createDeployCommand() {
|
|
@@ -4255,7 +4266,7 @@ function buildCustomTags() {
|
|
|
4255
4266
|
tag: "!GetAtt",
|
|
4256
4267
|
resolve(value) {
|
|
4257
4268
|
const dot = value.indexOf(".");
|
|
4258
|
-
if (dot < 0) throw new Error(
|
|
4269
|
+
if (dot < 0) throw new Error(`${GETATT_SHAPE_REFUSAL}; got '${value}'`);
|
|
4259
4270
|
return { "Fn::GetAtt": [value.slice(0, dot), value.slice(dot + 1)] };
|
|
4260
4271
|
},
|
|
4261
4272
|
identify: () => false
|
|
@@ -4298,6 +4309,28 @@ function buildCustomTags() {
|
|
|
4298
4309
|
return tags;
|
|
4299
4310
|
}
|
|
4300
4311
|
const CUSTOM_TAGS = buildCustomTags();
|
|
4312
|
+
const GETATT_SHAPE_REFUSAL = "!GetAtt requires '<LogicalId>.<Attribute>'";
|
|
4313
|
+
const YAML_ALIAS_EXHAUSTION = "Excessive alias count indicates a resource exhaustion attack";
|
|
4314
|
+
function describeYamlFinding(err) {
|
|
4315
|
+
if (err instanceof YAMLError) {
|
|
4316
|
+
const at = err.linePos?.[0];
|
|
4317
|
+
const where = at ? ` at line ${at.line}, column ${at.col}` : "";
|
|
4318
|
+
if (err.code === "TAG_RESOLVE_FAILED" && err.message.startsWith(GETATT_SHAPE_REFUSAL)) return `${GETATT_SHAPE_REFUSAL}${where}`;
|
|
4319
|
+
return `${err.name} ${err.code}${where}`;
|
|
4320
|
+
}
|
|
4321
|
+
if (err instanceof Error) return err.message === YAML_ALIAS_EXHAUSTION ? `${err.name}: ${YAML_ALIAS_EXHAUSTION}` : err.name || "Error";
|
|
4322
|
+
return describeAwsFailure(err).detail;
|
|
4323
|
+
}
|
|
4324
|
+
function parseYamlTemplate(body) {
|
|
4325
|
+
const doc = parseDocument(body, { customTags: CUSTOM_TAGS });
|
|
4326
|
+
const logger = getLogger();
|
|
4327
|
+
for (const warning of doc.warnings) {
|
|
4328
|
+
logger.debug(safeMsg`Template YAML warning: ${parseFailureDebugDetail(warning)}`);
|
|
4329
|
+
logger.warn(safeMsg`Template YAML warning: ${describeYamlFinding(warning)}`);
|
|
4330
|
+
}
|
|
4331
|
+
if (doc.errors.length > 0) throw doc.errors[0];
|
|
4332
|
+
return doc.toJS();
|
|
4333
|
+
}
|
|
4301
4334
|
function parseCfnTemplate(text) {
|
|
4302
4335
|
const format = detectTemplateFormat(text);
|
|
4303
4336
|
const body = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
@@ -4305,12 +4338,14 @@ function parseCfnTemplate(text) {
|
|
|
4305
4338
|
if (format === "json") try {
|
|
4306
4339
|
parsed = JSON.parse(body);
|
|
4307
4340
|
} catch (err) {
|
|
4308
|
-
|
|
4341
|
+
getLogger().debug(safeMsg`Template failed to parse as JSON: ${parseFailureDebugDetail(err)}`);
|
|
4342
|
+
throw new Error(`Template is not valid JSON. Cause: ${describeJsonParseFailure(err, utf8ByteLength(body))}`);
|
|
4309
4343
|
}
|
|
4310
4344
|
else try {
|
|
4311
|
-
parsed =
|
|
4345
|
+
parsed = parseYamlTemplate(body);
|
|
4312
4346
|
} catch (err) {
|
|
4313
|
-
|
|
4347
|
+
getLogger().debug(safeMsg`Template failed to parse as YAML: ${parseFailureDebugDetail(err)}`);
|
|
4348
|
+
throw new Error(`Template is not valid YAML. Cause: ${describeYamlFinding(err)}`);
|
|
4314
4349
|
}
|
|
4315
4350
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`Template root is not an object.`);
|
|
4316
4351
|
return parsed;
|
|
@@ -4606,7 +4641,7 @@ function injectRetainPolicies(templateBody, cfnStackName) {
|
|
|
4606
4641
|
try {
|
|
4607
4642
|
parsed = parseCfnTemplate(templateBody);
|
|
4608
4643
|
} catch (err) {
|
|
4609
|
-
throw new Error(`Template for '${cfnStackName}' is not a valid CloudFormation template. cdkd's --migrate-from-cloudformation flow supports both JSON and YAML templates (YAML via a CFn-aware codec that preserves !Ref / !GetAtt / !Sub shorthand). Cause: ${
|
|
4644
|
+
throw new Error(`Template for '${cfnStackName}' is not a valid CloudFormation template. cdkd's --migrate-from-cloudformation flow supports both JSON and YAML templates (YAML via a CFn-aware codec that preserves !Ref / !GetAtt / !Sub shorthand). Cause: ${describeAwsFailure(err).detail}`);
|
|
4610
4645
|
}
|
|
4611
4646
|
if (!("Resources" in parsed) || typeof parsed["Resources"] !== "object" || parsed["Resources"] === null || Array.isArray(parsed["Resources"])) throw new Error(`Template for '${cfnStackName}' has no Resources section — refusing to retire.`);
|
|
4612
4647
|
const modified = injectRetainPoliciesOnParsedResources(parsed["Resources"]);
|
|
@@ -4675,7 +4710,7 @@ async function injectRetainPoliciesRecursiveInternal(templateBody, cfnStackName,
|
|
|
4675
4710
|
try {
|
|
4676
4711
|
parsed = parseCfnTemplate(templateBody);
|
|
4677
4712
|
} catch (err) {
|
|
4678
|
-
throw new Error(`Template for '${cfnStackName}' is not a valid CloudFormation template. cdkd's --migrate-from-cloudformation flow supports both JSON and YAML templates (YAML via a CFn-aware codec that preserves !Ref / !GetAtt / !Sub shorthand). Cause: ${
|
|
4713
|
+
throw new Error(`Template for '${cfnStackName}' is not a valid CloudFormation template. cdkd's --migrate-from-cloudformation flow supports both JSON and YAML templates (YAML via a CFn-aware codec that preserves !Ref / !GetAtt / !Sub shorthand). Cause: ${describeAwsFailure(err).detail}`);
|
|
4679
4714
|
}
|
|
4680
4715
|
if (!("Resources" in parsed) || typeof parsed["Resources"] !== "object" || parsed["Resources"] === null || Array.isArray(parsed["Resources"])) throw new Error(`Template for '${cfnStackName}' has no Resources section — refusing to retire.`);
|
|
4681
4716
|
const resources = parsed["Resources"];
|
|
@@ -4916,7 +4951,9 @@ function indexNestedChildTemplates(template, templatePath) {
|
|
|
4916
4951
|
return result;
|
|
4917
4952
|
}
|
|
4918
4953
|
async function loadStateOrEmpty(stackName, region, stateBackend, refusalRecovery) {
|
|
4954
|
+
const refactorMarker = readRefactorMarkerOnce(stateBackend);
|
|
4919
4955
|
const result = await stateBackend.getState(stackName, region);
|
|
4956
|
+
const markerRefusal = refactorMarkerRefusalMessage(await refactorMarker, stackName, region, "cdkd deploy");
|
|
4920
4957
|
if (result) {
|
|
4921
4958
|
const unreadable = [];
|
|
4922
4959
|
const unreadableContainers = [];
|
|
@@ -4946,15 +4983,19 @@ async function loadStateOrEmpty(stackName, region, stateBackend, refusalRecovery
|
|
|
4946
4983
|
unreadableContainers.push("orphans");
|
|
4947
4984
|
deployRefusals.push(DEPLOY_REFUSES_ORPHANS_CONTAINER_REASON);
|
|
4948
4985
|
}
|
|
4986
|
+
if (hasPendingMovesOut(result.state)) deployRefusals.push(pendingMovesRefusalMessage(result.state, stackName, region, "cdkd deploy"));
|
|
4987
|
+
if (markerRefusal !== void 0) deployRefusals.push(markerRefusal);
|
|
4949
4988
|
if (isReadableBag(result.state.outputs) && !hasReadableExportSet(result.state)) logger.warn(malformedExportNamesWarning(stackName, region, refusalRecovery));
|
|
4950
4989
|
return {
|
|
4951
4990
|
state: result.state,
|
|
4952
4991
|
unreadable,
|
|
4953
4992
|
unreadableContainers,
|
|
4954
|
-
deployRefusals
|
|
4993
|
+
deployRefusals,
|
|
4994
|
+
found: true
|
|
4955
4995
|
};
|
|
4956
4996
|
}
|
|
4957
4997
|
return {
|
|
4998
|
+
found: false,
|
|
4958
4999
|
state: {
|
|
4959
5000
|
stackName,
|
|
4960
5001
|
region,
|
|
@@ -4965,7 +5006,7 @@ async function loadStateOrEmpty(stackName, region, stateBackend, refusalRecovery
|
|
|
4965
5006
|
},
|
|
4966
5007
|
unreadable: [],
|
|
4967
5008
|
unreadableContainers: [],
|
|
4968
|
-
deployRefusals: []
|
|
5009
|
+
deployRefusals: markerRefusal === void 0 ? [] : [markerRefusal]
|
|
4969
5010
|
};
|
|
4970
5011
|
}
|
|
4971
5012
|
function tokenValueForComparison(token, declaredType) {
|
|
@@ -5754,7 +5795,7 @@ function healerUnderRecordPrintingBag(healer, resources, namingSecrets) {
|
|
|
5754
5795
|
async function buildDiffTree(args) {
|
|
5755
5796
|
const { stackName, displayName, region, template, nestedTemplates, recursive, stateBackend, diffCalculator, parentHasSecretReference, parameters, parentUnresolvedParameters, canonicalizeProperties, assetRedirect, cfnFallback, producerLookup, consumerAccount, previewOrphanAdoption, attributeHealerFor, ancestorTemplatePaths, isNestedChild, inheritedSecrets, inheritedDerivedNames, inheritedNoEchoParameters, inheritedNoEchoParametersAnyVerdict, refusalRecovery, inheritedReferenceProvenance, inheritedSecretTokens } = args;
|
|
5756
5797
|
const unboundHealer = attributeHealerFor?.(stackName, region);
|
|
5757
|
-
const { state, unreadable, unreadableContainers, deployRefusals } = await loadStateOrEmpty(stackName, region, stateBackend, refusalRecovery);
|
|
5798
|
+
const { state, unreadable, unreadableContainers, deployRefusals, found } = await loadStateOrEmpty(stackName, region, stateBackend, refusalRecovery);
|
|
5758
5799
|
const attributeHealer = unboundHealer;
|
|
5759
5800
|
const createOnlyPrefetch = prefetchCreateOnlyPropertyPaths(templateResourceTypes(recordedTemplateResources(template, state)));
|
|
5760
5801
|
const secretBearingAbove = parentHasSecretReference === true || templateHasSecretDynamicReference(template);
|
|
@@ -5811,6 +5852,7 @@ async function buildDiffTree(args) {
|
|
|
5811
5852
|
destructiveChanges: findDestructiveChanges(stackName, changes.values(), stateAfterAdoption.resources, effectiveTemplate),
|
|
5812
5853
|
children: []
|
|
5813
5854
|
};
|
|
5855
|
+
if (found && !isNestedChild && node.blocking.length === 0) node.refactorSides = hintSidesOf(changes.values(), rollbackOrphanIds(state.orphans));
|
|
5814
5856
|
if (!recursive) return node;
|
|
5815
5857
|
const templateChildIds = /* @__PURE__ */ new Set();
|
|
5816
5858
|
for (const [logicalId, resource] of Object.entries(effectiveTemplate.Resources ?? {})) {
|
|
@@ -6000,13 +6042,15 @@ function diffTreeToJson(node) {
|
|
|
6000
6042
|
for (const change of node.changes.values()) {
|
|
6001
6043
|
if (change.changeType === "NO_CHANGE") continue;
|
|
6002
6044
|
const ccApi = node.ccApiRoutes.get(change.logicalId);
|
|
6045
|
+
const movedTo = change.changeType === "DELETE" ? node.refactorHints?.get(change.logicalId) : void 0;
|
|
6003
6046
|
changes.push({
|
|
6004
6047
|
logicalId: change.logicalId,
|
|
6005
6048
|
changeType: change.changeType,
|
|
6006
6049
|
resourceType: change.resourceType,
|
|
6007
6050
|
...change.propertyChanges && change.propertyChanges.length > 0 ? { propertyChanges: change.propertyChanges } : {},
|
|
6008
6051
|
...change.attributeChanges && change.attributeChanges.length > 0 ? { attributeChanges: change.attributeChanges } : {},
|
|
6009
|
-
...ccApi && ccApi.length > 0 ? { ccApi } : {}
|
|
6052
|
+
...ccApi && ccApi.length > 0 ? { ccApi } : {},
|
|
6053
|
+
...movedTo !== void 0 && { movedTo }
|
|
6010
6054
|
});
|
|
6011
6055
|
}
|
|
6012
6056
|
return {
|
|
@@ -6026,11 +6070,12 @@ function diffTreeToJson(node) {
|
|
|
6026
6070
|
unreadable: node.unreadable,
|
|
6027
6071
|
unreadableContainers: node.unreadableContainers,
|
|
6028
6072
|
unreadableOrphans: node.unreadableOrphans,
|
|
6029
|
-
destructiveChanges: node.destructiveChanges.map(({ logicalId, resourceType, constructPath, impact }) => ({
|
|
6073
|
+
destructiveChanges: node.destructiveChanges.map(({ logicalId, resourceType, constructPath, impact, movedTo }) => ({
|
|
6030
6074
|
logicalId,
|
|
6031
6075
|
resourceType,
|
|
6032
6076
|
...constructPath !== void 0 && { constructPath },
|
|
6033
|
-
impact
|
|
6077
|
+
impact,
|
|
6078
|
+
...movedTo !== void 0 && { movedTo }
|
|
6034
6079
|
})),
|
|
6035
6080
|
children: node.children.map(diffTreeToJson)
|
|
6036
6081
|
};
|
|
@@ -6079,7 +6124,7 @@ function renderDiffValue(own, filtered, indent, isNewSide) {
|
|
|
6079
6124
|
}
|
|
6080
6125
|
return (JSON.stringify(filtered, null, 2) ?? "undefined").replace(/\n/g, `\n${indent}`);
|
|
6081
6126
|
}
|
|
6082
|
-
function renderChangeLines(changes, logFn, ccApiRoutes, adoptedOrphans) {
|
|
6127
|
+
function renderChangeLines(changes, logFn, ccApiRoutes, adoptedOrphans, refactorHints) {
|
|
6083
6128
|
let createCount = 0;
|
|
6084
6129
|
let updateCount = 0;
|
|
6085
6130
|
let deleteCount = 0;
|
|
@@ -6118,7 +6163,10 @@ function renderChangeLines(changes, logFn, ccApiRoutes, adoptedOrphans) {
|
|
|
6118
6163
|
break;
|
|
6119
6164
|
case "DELETE":
|
|
6120
6165
|
deleteCount++;
|
|
6121
|
-
|
|
6166
|
+
{
|
|
6167
|
+
const movedTo = refactorHints?.get(logicalId);
|
|
6168
|
+
logFn(` [-] ${logicalId} (${change.resourceType})` + (movedTo !== void 0 ? movedToHint(movedTo) : ""));
|
|
6169
|
+
}
|
|
6122
6170
|
}
|
|
6123
6171
|
return {
|
|
6124
6172
|
create: createCount,
|
|
@@ -6182,7 +6230,7 @@ function renderDiffTree(node, isRoot, logFn) {
|
|
|
6182
6230
|
const hasChanges = nodeHasChanges(node);
|
|
6183
6231
|
if (hasChanges || node.blocking.length > 0) logFn(isRoot ? `\nStack ${stripForDisplay(node.stackName)}:` : `\nNested stack: ${stripForDisplay(node.displayName)}`);
|
|
6184
6232
|
if (hasChanges) {
|
|
6185
|
-
const { create, update, delete: del } = renderChangeLines(node.changes, logFn, node.ccApiRoutes, node.adoptedOrphans);
|
|
6233
|
+
const { create, update, delete: del } = renderChangeLines(node.changes, logFn, node.ccApiRoutes, node.adoptedOrphans, node.refactorHints);
|
|
6186
6234
|
const outputs = renderOutputChangeLines(node.outputChanges, logFn);
|
|
6187
6235
|
logFn(`\n${create} to create, ${update} to update, ${del} to delete`);
|
|
6188
6236
|
if (outputs.add + outputs.change + outputs.remove > 0) logFn(`${outputs.add} output(s) to add, ${outputs.change} to change, ${outputs.remove} to remove`);
|
|
@@ -6394,6 +6442,7 @@ async function diffCommand(stacks, options) {
|
|
|
6394
6442
|
suppressLegacyNotice: true
|
|
6395
6443
|
});
|
|
6396
6444
|
const trees = [];
|
|
6445
|
+
const treeEnvs = [];
|
|
6397
6446
|
const producerLookup = producerLookupOver(allStacks);
|
|
6398
6447
|
const sharedNames = new Set(targetStacks.map((s) => s.stackName).filter((name, i, names) => names.indexOf(name) !== i));
|
|
6399
6448
|
for (const stackInfo of targetStacks) {
|
|
@@ -6424,7 +6473,20 @@ async function diffCommand(stacks, options) {
|
|
|
6424
6473
|
isNestedChild: false,
|
|
6425
6474
|
refusalRecovery
|
|
6426
6475
|
}));
|
|
6476
|
+
treeEnvs.push(`${stackInfo.account ?? ""}/${stackRegion}`);
|
|
6427
6477
|
}
|
|
6478
|
+
const hints = findRefactorHints(trees.flatMap((tree, i) => tree.refactorSides === void 0 ? [] : [{
|
|
6479
|
+
id: String(i),
|
|
6480
|
+
stackName: tree.stackName,
|
|
6481
|
+
env: treeEnvs[i],
|
|
6482
|
+
...tree.refactorSides
|
|
6483
|
+
}]));
|
|
6484
|
+
trees.forEach((tree, i) => {
|
|
6485
|
+
const forTree = hints.get(String(i));
|
|
6486
|
+
if (forTree === void 0) return;
|
|
6487
|
+
tree.refactorHints = forTree;
|
|
6488
|
+
tree.destructiveChanges = withMovedTo(tree.destructiveChanges, forTree);
|
|
6489
|
+
});
|
|
6428
6490
|
let securityDifferences = 0;
|
|
6429
6491
|
let securityBroadening = false;
|
|
6430
6492
|
if (securityOnly) for (const tree of trees) {
|
|
@@ -11995,6 +12057,12 @@ async function purgeStateHistoryIfOptedIn(ctx, stackName, region) {
|
|
|
11995
12057
|
if (ctx.purgeStateHistory === true) await ctx.stateBackend.purgeDeletedStateHistory(stackName, region);
|
|
11996
12058
|
}
|
|
11997
12059
|
const NESTED_STACK_TYPE = "AWS::CloudFormation::Stack";
|
|
12060
|
+
function maskedSnapshotLogger(logger, mask) {
|
|
12061
|
+
return {
|
|
12062
|
+
info: (message) => logger.info(mask(message)),
|
|
12063
|
+
debug: (message) => logger.debug(mask(message))
|
|
12064
|
+
};
|
|
12065
|
+
}
|
|
11998
12066
|
function recordDestroyEvent(recorder, event) {
|
|
11999
12067
|
recorder?.record(maskEventTextWithBoundBags(event));
|
|
12000
12068
|
}
|
|
@@ -12032,6 +12100,9 @@ async function releaseRegistryMarkerAfterDestroy(ctx, stackName, region, logger)
|
|
|
12032
12100
|
logger.warn(safeMsg`Could not delete the stack registry marker of ${displayStackName(stackName)} ` + safeMsg`(${describeAwsFailure(error).summary}). It names this state prefix, which no longer ` + `records the stack, so a deploy under another prefix treats it as stale.`);
|
|
12033
12101
|
}
|
|
12034
12102
|
}
|
|
12103
|
+
function holdsSameResourcesAs(snapshot, current) {
|
|
12104
|
+
return isDeepStrictEqual(snapshot.resources, current.resources) && isDeepStrictEqual(snapshot.supersededResources, current.supersededResources);
|
|
12105
|
+
}
|
|
12035
12106
|
async function runDestroyForStack(stackName, state, ctx) {
|
|
12036
12107
|
const stackRegion = state.region;
|
|
12037
12108
|
if (!stackRegion || stackRegion === ctx.baseRegion) return destroyStackInRegion(stackName, state, ctx, void 0);
|
|
@@ -12103,6 +12174,7 @@ async function destroyStackInRegion(stackName, state, ctx, stackScope) {
|
|
|
12103
12174
|
refuseMalformedResourcePropertiesForDestroy(state, stackName, regionForState, refusalRecovery);
|
|
12104
12175
|
refuseMalformedOrphansForDestroy(state, stackName, regionForState, refusalRecovery);
|
|
12105
12176
|
refuseMalformedOrphanRecordsForDestroy(state, stackName, regionForState, refusalRecovery);
|
|
12177
|
+
refusePendingMovesOut(state, stackName, regionForState, "cdkd destroy");
|
|
12106
12178
|
let journalAtEntry;
|
|
12107
12179
|
try {
|
|
12108
12180
|
journalAtEntry = { journal: await ctx.stateBackend.loadRollbackJournal(stackName, regionForState) };
|
|
@@ -12169,7 +12241,9 @@ async function destroyStackInRegion(stackName, state, ctx, stackScope) {
|
|
|
12169
12241
|
refuseMalformedResourceEntriesForDestroy(recheck.state, stackName, regionForState, refusalRecovery);
|
|
12170
12242
|
refuseMalformedOrphansForDestroy(recheck.state, stackName, regionForState, refusalRecovery);
|
|
12171
12243
|
refuseMalformedOrphanRecordsForDestroy(recheck.state, stackName, regionForState, refusalRecovery);
|
|
12244
|
+
refusePendingMovesOut(recheck.state, stackName, regionForState, "cdkd destroy");
|
|
12172
12245
|
}
|
|
12246
|
+
refuseRefactorMarker(await readRefactorMarker(ctx.stateBackend), stackName, regionForState, "cdkd destroy");
|
|
12173
12247
|
const recheckResources = recheck ? Object.keys(recheck.state.resources).length : 0;
|
|
12174
12248
|
const recheckOrphans = recheck ? (recheck.state.orphans ?? []).length : 0;
|
|
12175
12249
|
const recheckRead = await loadJournaledOrphans(ctx.stateBackend, stackName, regionForState, recheck?.state.orphans, journaledOrphans.unreadable === true ? { warn: () => void 0 } : logger);
|
|
@@ -12301,6 +12375,23 @@ async function destroyStackInRegion(stackName, state, ctx, stackScope) {
|
|
|
12301
12375
|
},
|
|
12302
12376
|
onAcquireFailure: restoreBaseRegionAndClients
|
|
12303
12377
|
});
|
|
12378
|
+
try {
|
|
12379
|
+
const [current, marker] = await Promise.all([ctx.stateBackend.getState(stackName, regionForState), readRefactorMarker(ctx.stateBackend)]);
|
|
12380
|
+
if (current === null || !holdsSameResourcesAs(state, current.state)) throw new CdkdError(safeMsg`cdkd destroy refused stack ${stackName} (${regionForState}): its state ` + "changed after it was read (another command wrote it while this one waited for confirmation), so nothing was deleted. Re-run cdkd destroy.", "STATE_CHANGED");
|
|
12381
|
+
if (current.state.terminationProtection === true && ctx.removeProtection !== true) throw new StackTerminationProtectionError(stackName, void 0, {
|
|
12382
|
+
args: accountArgs(refusalRecovery),
|
|
12383
|
+
clause: withheldAccountClause(refusalRecovery, "the command below prints")
|
|
12384
|
+
}, {
|
|
12385
|
+
command: ctx.stateDestroy === true ? "cdkd state destroy" : "cdkd destroy",
|
|
12386
|
+
stackRegion: regionForState
|
|
12387
|
+
});
|
|
12388
|
+
refusePendingMovesOut(current.state, stackName, regionForState, "cdkd destroy");
|
|
12389
|
+
refuseRefactorMarker(marker, stackName, regionForState, "cdkd destroy");
|
|
12390
|
+
} catch (error) {
|
|
12391
|
+
await lock.release({ failureMessage: "Failed to release lock after a state re-check refusal" });
|
|
12392
|
+
restoreBaseRegionAndClients();
|
|
12393
|
+
throw error;
|
|
12394
|
+
}
|
|
12304
12395
|
if (needsStrongRefCheck) {
|
|
12305
12396
|
const releaseThenUnregister = async () => {
|
|
12306
12397
|
await lock.release({ failureMessage: "Failed to release lock after strong-ref refusal/failure" });
|
|
@@ -12545,7 +12636,7 @@ async function destroyStackInRegion(stackName, state, ctx, stackScope) {
|
|
|
12545
12636
|
if (policy === "Snapshot" && ctx.skipFinalSnapshot !== true) {
|
|
12546
12637
|
if (ATOMIC_FINAL_SNAPSHOT_TYPES.has(resource.resourceType) && resource.provisionedBy !== "cc-api") finalSnapshotIdentifier = buildFinalSnapshotIdentifier(resource.physicalId, resource.resourceType);
|
|
12547
12638
|
else if (ATOMIC_FINAL_SNAPSHOT_TYPES.has(resource.resourceType)) throw ccRoutedFinalSnapshotError(logicalId, resource.resourceType, "--skip-final-snapshot");
|
|
12548
|
-
else if (PRE_DELETE_SNAPSHOT_TYPES.has(resource.resourceType)) await createPreDeleteFinalSnapshot(resource.resourceType, resource.physicalId, logicalId, destroyAwsClients ?? ctx.baseAwsClients, logger);
|
|
12639
|
+
else if (PRE_DELETE_SNAPSHOT_TYPES.has(resource.resourceType)) await createPreDeleteFinalSnapshot(resource.resourceType, resource.physicalId, logicalId, destroyAwsClients ?? ctx.baseAwsClients, maskedSnapshotLogger(logger, createUnionSecretMasker([printingBagOf(logicalId)])));
|
|
12549
12640
|
else throw unsupportedFinalSnapshotError(logicalId, resource.resourceType, "--skip-final-snapshot");
|
|
12550
12641
|
}
|
|
12551
12642
|
const provider = destroyProviderRegistry.getProviderFor({
|
|
@@ -12675,7 +12766,7 @@ async function destroyStackInRegion(stackName, state, ctx, stackScope) {
|
|
|
12675
12766
|
} catch (error) {
|
|
12676
12767
|
renderer.removeTask(logicalId);
|
|
12677
12768
|
const msg = describeAwsFailure(error).detail;
|
|
12678
|
-
if (!isInterruptedWaitError(error) && !isFinalSnapshotError(error) && !isMarkedNonRetryable(error) && !isWaitAbandonedError(error) && (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException"))) {
|
|
12769
|
+
if (!isInterruptedWaitError(error) && !isFinalSnapshotError(error) && !isMarkedNonRetryable(error) && !isWaitAbandonedError(error) && !isMarkedNotGone(error) && (msg.includes("does not exist") || msg.includes("not found") || msg.includes("No policy found") || msg.includes("NoSuchEntity") || msg.includes("NotFoundException"))) {
|
|
12679
12770
|
logger.debug(` ${displaySafe(logicalId)} already deleted, removing from state`);
|
|
12680
12771
|
result.deletedCount++;
|
|
12681
12772
|
recordDestroyEvent(ctx.eventRecorder, {
|
|
@@ -13270,6 +13361,18 @@ async function destroyCommand(stackArgs, options) {
|
|
|
13270
13361
|
logger.warn(`No state found for stack ${displaySafe(stackName)}, skipping`);
|
|
13271
13362
|
continue;
|
|
13272
13363
|
}
|
|
13364
|
+
if (synthStack?.terminationProtection !== true && stateResult.state.terminationProtection === true) {
|
|
13365
|
+
if (options.removeProtection) logger.warn(safeMsg`Stack ${stackName} was deployed with terminationProtection: true — bypassing because --remove-protection set`);
|
|
13366
|
+
else {
|
|
13367
|
+
const err = new StackTerminationProtectionError(stackName, void 0, {
|
|
13368
|
+
args: stateCommandAccount.args,
|
|
13369
|
+
clause: withheldAccountClause(accountRecovery, "the command below prints")
|
|
13370
|
+
}, { command: "cdkd destroy" });
|
|
13371
|
+
logger.error(safeMsg` ✗ ${asIsMessage(err.message)}`);
|
|
13372
|
+
totalErrors++;
|
|
13373
|
+
continue;
|
|
13374
|
+
}
|
|
13375
|
+
}
|
|
13273
13376
|
if (stateResult.state.parentStack) {
|
|
13274
13377
|
const err = new NestedStackChildDirectDestroyError(stackName, stateResult.state.parentStack, stateResult.state.parentLogicalId, void 0, stateCommandAccount);
|
|
13275
13378
|
logger.error(` ✗ ${err.message}`);
|
|
@@ -13539,6 +13642,160 @@ async function dropFailedJournalEntry(args) {
|
|
|
13539
13642
|
}
|
|
13540
13643
|
}
|
|
13541
13644
|
|
|
13645
|
+
//#endregion
|
|
13646
|
+
//#region src/cli/interactive-select.ts
|
|
13647
|
+
const ARM_DELAY_MS = 50;
|
|
13648
|
+
const FILTER_CHAR = /^[\p{L}\p{N}\p{P}\p{S} ]$/u;
|
|
13649
|
+
function isInteractiveTerminal(streams) {
|
|
13650
|
+
return streams.input.isTTY === true && typeof streams.input.setRawMode === "function" && streams.output.isTTY === true;
|
|
13651
|
+
}
|
|
13652
|
+
function filterItems(items, filter) {
|
|
13653
|
+
const needle = filter.toLowerCase();
|
|
13654
|
+
return needle === "" ? [...items] : items.filter((i) => i.label.toLowerCase().includes(needle));
|
|
13655
|
+
}
|
|
13656
|
+
function renderSelectFrame(title, visible, cursor, filter, pageSize) {
|
|
13657
|
+
const lines = [title];
|
|
13658
|
+
if (filter !== "") lines.push(` filter: ${filter}`);
|
|
13659
|
+
if (visible.length === 0) lines.push(" (no match)");
|
|
13660
|
+
else {
|
|
13661
|
+
const start = Math.min(Math.max(0, cursor - Math.floor(pageSize / 2)), Math.max(0, visible.length - pageSize));
|
|
13662
|
+
const end = Math.min(visible.length, start + pageSize);
|
|
13663
|
+
if (start > 0) lines.push(` ... ${start} more above`);
|
|
13664
|
+
for (let i = start; i < end; i++) lines.push(`${i === cursor ? "> " : " "}${visible[i].label}`);
|
|
13665
|
+
if (end < visible.length) lines.push(` ... ${visible.length - end} more below`);
|
|
13666
|
+
}
|
|
13667
|
+
lines.push(" (up/down move, type to filter, Enter select, Esc back, Ctrl+C quit)");
|
|
13668
|
+
return lines;
|
|
13669
|
+
}
|
|
13670
|
+
function fitToWidth(line, columns) {
|
|
13671
|
+
if (typeof columns !== "number" || columns < 2) return line;
|
|
13672
|
+
const chars = [...line];
|
|
13673
|
+
return chars.length < columns ? line : `${chars.slice(0, columns - 2).join("")}…`;
|
|
13674
|
+
}
|
|
13675
|
+
function selectFromList(title, items, streams) {
|
|
13676
|
+
const { input, output } = streams;
|
|
13677
|
+
const rows = typeof output.rows === "number" && output.rows > 0 ? output.rows : 24;
|
|
13678
|
+
const pageSize = Math.max(3, Math.min(15, rows - 5));
|
|
13679
|
+
let filter = "";
|
|
13680
|
+
let cursor = 0;
|
|
13681
|
+
let drawn = 0;
|
|
13682
|
+
const erase = () => {
|
|
13683
|
+
if (drawn > 0) output.write(`\x1b[${drawn}F\x1b[0J`);
|
|
13684
|
+
drawn = 0;
|
|
13685
|
+
};
|
|
13686
|
+
const draw = () => {
|
|
13687
|
+
erase();
|
|
13688
|
+
const lines = renderSelectFrame(title, filterItems(items, filter), cursor, filter, pageSize).map((line) => fitToWidth(line, output.columns));
|
|
13689
|
+
output.write(`${lines.join("\n")}\n`);
|
|
13690
|
+
drawn = lines.length;
|
|
13691
|
+
};
|
|
13692
|
+
return new Promise((resolve, reject) => {
|
|
13693
|
+
emitKeypressEvents(input);
|
|
13694
|
+
input.setRawMode?.(true);
|
|
13695
|
+
input.resume();
|
|
13696
|
+
let armed = false;
|
|
13697
|
+
const armTimer = setTimeout(() => {
|
|
13698
|
+
armed = true;
|
|
13699
|
+
}, streams.armDelayMs ?? ARM_DELAY_MS);
|
|
13700
|
+
const finish = (result) => {
|
|
13701
|
+
clearTimeout(armTimer);
|
|
13702
|
+
input.removeListener("keypress", onKey);
|
|
13703
|
+
input.setRawMode?.(false);
|
|
13704
|
+
input.pause();
|
|
13705
|
+
erase();
|
|
13706
|
+
resolve(result);
|
|
13707
|
+
};
|
|
13708
|
+
const onKey = (str, key) => {
|
|
13709
|
+
if (!armed) return;
|
|
13710
|
+
const visible = filterItems(items, filter);
|
|
13711
|
+
const name = key?.name;
|
|
13712
|
+
if (key?.ctrl === true && (name === "c" || name === "d")) return finish({ kind: "quit" });
|
|
13713
|
+
if (name === "escape") return finish({ kind: "back" });
|
|
13714
|
+
if (name === "return" || name === "enter") {
|
|
13715
|
+
const picked = visible[cursor];
|
|
13716
|
+
if (picked !== void 0) return finish({
|
|
13717
|
+
kind: "picked",
|
|
13718
|
+
value: picked.value
|
|
13719
|
+
});
|
|
13720
|
+
return;
|
|
13721
|
+
}
|
|
13722
|
+
if (name === "up" || key?.ctrl === true && name === "p") cursor = visible.length === 0 ? 0 : (cursor - 1 + visible.length) % visible.length;
|
|
13723
|
+
else if (name === "down" || key?.ctrl === true && name === "n") cursor = visible.length === 0 ? 0 : (cursor + 1) % visible.length;
|
|
13724
|
+
else if (name === "backspace") {
|
|
13725
|
+
filter = filter.slice(0, -1);
|
|
13726
|
+
cursor = 0;
|
|
13727
|
+
} else if (typeof str === "string" && key?.ctrl !== true && key?.meta !== true && FILTER_CHAR.test(str)) {
|
|
13728
|
+
filter += str;
|
|
13729
|
+
cursor = 0;
|
|
13730
|
+
} else return;
|
|
13731
|
+
draw();
|
|
13732
|
+
};
|
|
13733
|
+
input.on("keypress", onKey);
|
|
13734
|
+
try {
|
|
13735
|
+
draw();
|
|
13736
|
+
} catch (error) {
|
|
13737
|
+
clearTimeout(armTimer);
|
|
13738
|
+
input.removeListener("keypress", onKey);
|
|
13739
|
+
input.setRawMode?.(false);
|
|
13740
|
+
input.pause();
|
|
13741
|
+
reject(error);
|
|
13742
|
+
}
|
|
13743
|
+
});
|
|
13744
|
+
}
|
|
13745
|
+
|
|
13746
|
+
//#endregion
|
|
13747
|
+
//#region src/cli/commands/state-browse.ts
|
|
13748
|
+
const VIEW_ITEMS = [
|
|
13749
|
+
{
|
|
13750
|
+
label: "Resources (logical id, type, physical id)",
|
|
13751
|
+
value: "resources"
|
|
13752
|
+
},
|
|
13753
|
+
{
|
|
13754
|
+
label: "Resources with dependencies and attributes",
|
|
13755
|
+
value: "resources-long"
|
|
13756
|
+
},
|
|
13757
|
+
{
|
|
13758
|
+
label: "Full state record (cdkd state show)",
|
|
13759
|
+
value: "show"
|
|
13760
|
+
},
|
|
13761
|
+
{
|
|
13762
|
+
label: "Back to the stack list",
|
|
13763
|
+
value: "back"
|
|
13764
|
+
}
|
|
13765
|
+
];
|
|
13766
|
+
function nonInteractiveBrowseRefusal() {
|
|
13767
|
+
return new CdkdError("cdkd state browse needs an interactive terminal. Without one, use cdkd state list, cdkd state resources <stack> and cdkd state show <stack>.", "NON_INTERACTIVE");
|
|
13768
|
+
}
|
|
13769
|
+
async function runStateBrowse(deps) {
|
|
13770
|
+
if (!isInteractiveTerminal(deps.streams)) throw nonInteractiveBrowseRefusal();
|
|
13771
|
+
const refs = await deps.listStacks();
|
|
13772
|
+
if (refs.length === 0) {
|
|
13773
|
+
deps.streams.output.write("No stacks in the state bucket.\n");
|
|
13774
|
+
return;
|
|
13775
|
+
}
|
|
13776
|
+
const stackItems = refs.map((ref) => ({
|
|
13777
|
+
label: deps.label(ref),
|
|
13778
|
+
value: ref
|
|
13779
|
+
}));
|
|
13780
|
+
for (;;) {
|
|
13781
|
+
const stack = await selectFromList("Select a stack:", stackItems, deps.streams);
|
|
13782
|
+
if (stack.kind !== "picked") return;
|
|
13783
|
+
for (;;) {
|
|
13784
|
+
const title = `${deps.label(stack.value)}:`;
|
|
13785
|
+
const view = await selectFromList(title, VIEW_ITEMS, deps.streams);
|
|
13786
|
+
if (view.kind === "quit") return;
|
|
13787
|
+
if (view.kind === "back" || view.value === "back") break;
|
|
13788
|
+
deps.streams.output.write(`\n${title}\n`);
|
|
13789
|
+
try {
|
|
13790
|
+
await deps.printView(view.value, stack.value);
|
|
13791
|
+
} catch (error) {
|
|
13792
|
+
deps.reportError(error);
|
|
13793
|
+
}
|
|
13794
|
+
deps.streams.output.write("\n");
|
|
13795
|
+
}
|
|
13796
|
+
}
|
|
13797
|
+
}
|
|
13798
|
+
|
|
13542
13799
|
//#endregion
|
|
13543
13800
|
//#region src/deployment/public-ssm-proof.ts
|
|
13544
13801
|
var PublicSsmProver = class {
|
|
@@ -18173,59 +18430,63 @@ async function stateResourcesCommand(stackName, options) {
|
|
|
18173
18430
|
if (options.json) reserveStdoutForPayload();
|
|
18174
18431
|
const setup = await setupStateBackend(options);
|
|
18175
18432
|
try {
|
|
18176
|
-
|
|
18177
|
-
if (!ref.region) throw legacyRecordRefusal(stackName);
|
|
18178
|
-
const stateResult = await setup.stateBackend.getState(stackName, ref.region);
|
|
18179
|
-
if (!stateResult) throw new Error(`No state found for stack ${formatStackRefSafe(ref)} in s3://${setup.bucket}/${setup.prefix}/. Run 'cdkd state list' to see available stacks.`);
|
|
18180
|
-
if (repairMalformedResourcesForReadOnly(stateResult.state)) logger.warn(malformedResourcesWarning(stackName, ref.region, renderRecovery(options, setup)));
|
|
18181
|
-
if (options.long || options.json) repairRenderedContainers(stateResult.state, RESOURCES_RENDERED_CONTAINERS, stackName, ref.region, logger, renderRecovery(options, setup));
|
|
18182
|
-
const resources = stateResult.state.resources;
|
|
18183
|
-
const details = Object.entries(resources).map(([logicalId, entry]) => {
|
|
18184
|
-
const resource = entry ?? {};
|
|
18185
|
-
return {
|
|
18186
|
-
logicalId,
|
|
18187
|
-
resourceType: resource.resourceType,
|
|
18188
|
-
physicalId: resource.physicalId,
|
|
18189
|
-
dependencies: resource.dependencies ?? [],
|
|
18190
|
-
attributes: resource.attributes ?? {}
|
|
18191
|
-
};
|
|
18192
|
-
}).sort((a, b) => a.logicalId.localeCompare(b.logicalId));
|
|
18193
|
-
if (options.json) {
|
|
18194
|
-
process.stdout.write(`${stringifyJsonPayload(details)}\n`);
|
|
18195
|
-
return;
|
|
18196
|
-
}
|
|
18197
|
-
if (details.length === 0) return;
|
|
18198
|
-
if (options.long) {
|
|
18199
|
-
const lines = [];
|
|
18200
|
-
for (const detail of details) {
|
|
18201
|
-
lines.push(stripForDisplay(detail.logicalId));
|
|
18202
|
-
lines.push(` Type: ${formatAttributeValue(detail.resourceType)}`);
|
|
18203
|
-
lines.push(` PhysicalID: ${formatAttributeValue(detail.physicalId)}`);
|
|
18204
|
-
lines.push(` Dependencies: ${formatDependencyList(detail.dependencies)}`);
|
|
18205
|
-
const attrEntries = Object.entries(detail.attributes);
|
|
18206
|
-
if (attrEntries.length === 0) lines.push(" Attributes: (none)");
|
|
18207
|
-
else {
|
|
18208
|
-
lines.push(" Attributes:");
|
|
18209
|
-
for (const [k, v] of attrEntries) lines.push(` ${stripForDisplay(k)}: ${formatAttributeValue(v)}`);
|
|
18210
|
-
}
|
|
18211
|
-
lines.push("");
|
|
18212
|
-
}
|
|
18213
|
-
if (lines[lines.length - 1] === "") lines.pop();
|
|
18214
|
-
process.stdout.write(`${lines.join("\n")}\n`);
|
|
18215
|
-
return;
|
|
18216
|
-
}
|
|
18217
|
-
const rows = details.map((d) => ({
|
|
18218
|
-
logicalId: stripForDisplay(d.logicalId),
|
|
18219
|
-
resourceType: formatAttributeValue(d.resourceType),
|
|
18220
|
-
physicalId: formatAttributeValue(d.physicalId)
|
|
18221
|
-
}));
|
|
18222
|
-
const idWidth = Math.max(...rows.map((r) => r.logicalId.length));
|
|
18223
|
-
const typeWidth = Math.max(...rows.map((r) => r.resourceType.length));
|
|
18224
|
-
for (const row of rows) process.stdout.write(`${row.logicalId.padEnd(idWidth)} ${row.resourceType.padEnd(typeWidth)} ${row.physicalId}\n`);
|
|
18433
|
+
await printStackResources(stackName, options, setup);
|
|
18225
18434
|
} finally {
|
|
18226
18435
|
setup.dispose();
|
|
18227
18436
|
}
|
|
18228
18437
|
}
|
|
18438
|
+
async function printStackResources(stackName, options, setup) {
|
|
18439
|
+
const logger = getLogger();
|
|
18440
|
+
const ref = resolveSingleRegion(stackName, await setup.stateBackend.listStacks(), options.stackRegion);
|
|
18441
|
+
if (!ref.region) throw legacyRecordRefusal(stackName);
|
|
18442
|
+
const stateResult = await setup.stateBackend.getState(stackName, ref.region);
|
|
18443
|
+
if (!stateResult) throw new Error(`No state found for stack ${formatStackRefSafe(ref)} in s3://${setup.bucket}/${setup.prefix}/. Run 'cdkd state list' to see available stacks.`);
|
|
18444
|
+
if (repairMalformedResourcesForReadOnly(stateResult.state)) logger.warn(malformedResourcesWarning(stackName, ref.region, renderRecovery(options, setup)));
|
|
18445
|
+
if (options.long || options.json) repairRenderedContainers(stateResult.state, RESOURCES_RENDERED_CONTAINERS, stackName, ref.region, logger, renderRecovery(options, setup));
|
|
18446
|
+
const resources = stateResult.state.resources;
|
|
18447
|
+
const details = Object.entries(resources).map(([logicalId, entry]) => {
|
|
18448
|
+
const resource = entry ?? {};
|
|
18449
|
+
return {
|
|
18450
|
+
logicalId,
|
|
18451
|
+
resourceType: resource.resourceType,
|
|
18452
|
+
physicalId: resource.physicalId,
|
|
18453
|
+
dependencies: resource.dependencies ?? [],
|
|
18454
|
+
attributes: resource.attributes ?? {}
|
|
18455
|
+
};
|
|
18456
|
+
}).sort((a, b) => a.logicalId.localeCompare(b.logicalId));
|
|
18457
|
+
if (options.json) {
|
|
18458
|
+
process.stdout.write(`${stringifyJsonPayload(details)}\n`);
|
|
18459
|
+
return;
|
|
18460
|
+
}
|
|
18461
|
+
if (details.length === 0) return;
|
|
18462
|
+
if (options.long) {
|
|
18463
|
+
const lines = [];
|
|
18464
|
+
for (const detail of details) {
|
|
18465
|
+
lines.push(stripForDisplay(detail.logicalId));
|
|
18466
|
+
lines.push(` Type: ${formatAttributeValue(detail.resourceType)}`);
|
|
18467
|
+
lines.push(` PhysicalID: ${formatAttributeValue(detail.physicalId)}`);
|
|
18468
|
+
lines.push(` Dependencies: ${formatDependencyList(detail.dependencies)}`);
|
|
18469
|
+
const attrEntries = Object.entries(detail.attributes);
|
|
18470
|
+
if (attrEntries.length === 0) lines.push(" Attributes: (none)");
|
|
18471
|
+
else {
|
|
18472
|
+
lines.push(" Attributes:");
|
|
18473
|
+
for (const [k, v] of attrEntries) lines.push(` ${stripForDisplay(k)}: ${formatAttributeValue(v)}`);
|
|
18474
|
+
}
|
|
18475
|
+
lines.push("");
|
|
18476
|
+
}
|
|
18477
|
+
if (lines[lines.length - 1] === "") lines.pop();
|
|
18478
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
18479
|
+
return;
|
|
18480
|
+
}
|
|
18481
|
+
const rows = details.map((d) => ({
|
|
18482
|
+
logicalId: stripForDisplay(d.logicalId),
|
|
18483
|
+
resourceType: formatAttributeValue(d.resourceType),
|
|
18484
|
+
physicalId: formatAttributeValue(d.physicalId)
|
|
18485
|
+
}));
|
|
18486
|
+
const idWidth = Math.max(...rows.map((r) => r.logicalId.length));
|
|
18487
|
+
const typeWidth = Math.max(...rows.map((r) => r.resourceType.length));
|
|
18488
|
+
for (const row of rows) process.stdout.write(`${row.logicalId.padEnd(idWidth)} ${row.resourceType.padEnd(typeWidth)} ${row.physicalId}\n`);
|
|
18489
|
+
}
|
|
18229
18490
|
function formatAttributeValue(value) {
|
|
18230
18491
|
if (value === null) return "null";
|
|
18231
18492
|
if (value === void 0) return "undefined";
|
|
@@ -18270,34 +18531,38 @@ async function stateShowCommand(stackName, options) {
|
|
|
18270
18531
|
if (options.json) reserveStdoutForPayload();
|
|
18271
18532
|
const setup = await setupStateBackend(options);
|
|
18272
18533
|
try {
|
|
18273
|
-
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18277
|
-
|
|
18278
|
-
|
|
18279
|
-
|
|
18280
|
-
|
|
18281
|
-
|
|
18282
|
-
|
|
18283
|
-
|
|
18284
|
-
|
|
18285
|
-
|
|
18286
|
-
process.stdout.write(`${lines.join("\n")}\n`);
|
|
18287
|
-
return;
|
|
18288
|
-
}
|
|
18534
|
+
await printStackShow(stackName, options, setup);
|
|
18535
|
+
} finally {
|
|
18536
|
+
setup.dispose();
|
|
18537
|
+
}
|
|
18538
|
+
}
|
|
18539
|
+
async function printStackShow(stackName, options, setup) {
|
|
18540
|
+
const logger = getLogger();
|
|
18541
|
+
const ref = resolveSingleRegion(stackName, await setup.stateBackend.listStacks(), options.stackRegion);
|
|
18542
|
+
if (!ref.region) throw legacyRecordRefusal(stackName);
|
|
18543
|
+
const [stateResult, lockInfo] = await Promise.all([setup.stateBackend.getState(stackName, ref.region), setup.lockManager.getLockInfo(stackName, ref.region)]);
|
|
18544
|
+
if (!stateResult) throw new Error(`No state found for stack ${formatStackRefSafe(ref)} in s3://${setup.bucket}/${setup.prefix}/. Run 'cdkd state list' to see available stacks.`);
|
|
18545
|
+
if (options.showNested) {
|
|
18546
|
+
const treeWithLocks = await loadLocksForTree(await buildCdkdStateStackTree(stackName, ref.region, setup.stateBackend, stateResult.state, renderRecovery(options, setup)), setup.lockManager, lockInfo);
|
|
18289
18547
|
if (options.json) {
|
|
18290
|
-
|
|
18291
|
-
|
|
18292
|
-
lock: lockInfo
|
|
18293
|
-
})}\n`);
|
|
18548
|
+
warnUnreadableTreeNodes(treeWithLocks, logger, renderRecovery(options, setup));
|
|
18549
|
+
process.stdout.write(`${stringifyJsonPayload(treeToShowJson(treeWithLocks))}\n`);
|
|
18294
18550
|
return;
|
|
18295
18551
|
}
|
|
18296
|
-
|
|
18297
|
-
|
|
18298
|
-
|
|
18299
|
-
|
|
18552
|
+
repairTreeForTextRender(treeWithLocks, logger, renderRecovery(options, setup));
|
|
18553
|
+
const lines = renderTreeWithChildren(treeWithLocks);
|
|
18554
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
18555
|
+
return;
|
|
18556
|
+
}
|
|
18557
|
+
if (options.json) {
|
|
18558
|
+
process.stdout.write(`${stringifyJsonPayload({
|
|
18559
|
+
state: withStackInstanceIdWithheld(stateResult.state),
|
|
18560
|
+
lock: lockInfo
|
|
18561
|
+
})}\n`);
|
|
18562
|
+
return;
|
|
18300
18563
|
}
|
|
18564
|
+
repairRecordForTextRender(stateResult.state, stackName, ref.region, logger, renderRecovery(options, setup));
|
|
18565
|
+
process.stdout.write(`${renderStateBlock(stateResult.state, lockInfo, true).join("\n")}\n`);
|
|
18301
18566
|
}
|
|
18302
18567
|
const SKIPPED_DIGEST_PREVIEW_LEN = 12;
|
|
18303
18568
|
const UNSERIALIZABLE = "(unserializable)";
|
|
@@ -18715,6 +18980,20 @@ function createStateOrphanCommand() {
|
|
|
18715
18980
|
cmd.addOption(deprecatedRegionOption);
|
|
18716
18981
|
return cmd;
|
|
18717
18982
|
}
|
|
18983
|
+
async function protectedRootOf(stackName, state, region, backend) {
|
|
18984
|
+
let name = stackName;
|
|
18985
|
+
let record = state;
|
|
18986
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18987
|
+
for (let depth = 0; record !== void 0 && depth < 32; depth++) {
|
|
18988
|
+
if (record.terminationProtection === true) return name;
|
|
18989
|
+
const parent = record.parentStack;
|
|
18990
|
+
if (typeof parent !== "string" || parent === "" || seen.has(parent)) return void 0;
|
|
18991
|
+
seen.add(name);
|
|
18992
|
+
const parentRegion = typeof record.parentRegion === "string" ? record.parentRegion : region;
|
|
18993
|
+
name = parent;
|
|
18994
|
+
record = (await backend.getState(parent, parentRegion))?.state;
|
|
18995
|
+
}
|
|
18996
|
+
}
|
|
18718
18997
|
async function stateDestroyCommand(stackArgs, options) {
|
|
18719
18998
|
if (options.all) throw new CdkdError(`cdkd state destroy no longer accepts --all: it destroyed every stack in the state bucket, which every CDK app deployed to this account shares, so one command reached across apps. Name each stack to destroy: cdkd state destroy ${commandHole("stacks...")} (cdkd state list shows them), or run cdkd destroy --all from the CDK app, which destroys only that app's top-level stacks (cdkd destroy '**' destroys its CDK Stage stacks too).`, "STATE_DESTROY_ALL_REMOVED");
|
|
18720
18999
|
const providerClasses = await loadProviderClasses();
|
|
@@ -18789,6 +19068,24 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
18789
19068
|
logger.warn(safeMsg`No state found for stack ${displayStackName(stackName)}` + (ref.region ? safeMsg` in ${displayIdent(ref.region)}` : "") + ", skipping");
|
|
18790
19069
|
continue;
|
|
18791
19070
|
}
|
|
19071
|
+
const protectedRoot = await protectedRootOf(stackName, stateResult.state, refRegion, setup.stateBackend);
|
|
19072
|
+
if (protectedRoot !== void 0) {
|
|
19073
|
+
if (options.removeProtection === true) logger.warn(safeMsg`Stack ${displayStackName(protectedRoot)} was deployed with terminationProtection: true — bypassing because --remove-protection set`);
|
|
19074
|
+
else {
|
|
19075
|
+
const recovery = renderRecovery(options, setup);
|
|
19076
|
+
const err = new StackTerminationProtectionError(stackName, void 0, {
|
|
19077
|
+
args: accountArgs(recovery),
|
|
19078
|
+
clause: withheldAccountClause(recovery, "the command below prints")
|
|
19079
|
+
}, {
|
|
19080
|
+
command: "cdkd state destroy",
|
|
19081
|
+
stackRegion: refRegion,
|
|
19082
|
+
...protectedRoot !== stackName && { root: protectedRoot }
|
|
19083
|
+
});
|
|
19084
|
+
logger.error(safeMsg` ✗ ${asIsMessage(err.message)}`);
|
|
19085
|
+
totalErrors++;
|
|
19086
|
+
continue;
|
|
19087
|
+
}
|
|
19088
|
+
}
|
|
18792
19089
|
if (runInterrupted()) {
|
|
18793
19090
|
stoppedEarly = true;
|
|
18794
19091
|
break;
|
|
@@ -18835,6 +19132,7 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
18835
19132
|
statePrefix: options.statePrefix,
|
|
18836
19133
|
skipConfirmation: options.yes,
|
|
18837
19134
|
removeProtection: options.removeProtection === true,
|
|
19135
|
+
stateDestroy: true,
|
|
18838
19136
|
skipFinalSnapshot: options.skipFinalSnapshot === true,
|
|
18839
19137
|
resolveSecretDerivedPrincipals: {},
|
|
18840
19138
|
stackDestroy: true,
|
|
@@ -18889,7 +19187,7 @@ async function stateDestroyCommand(stackArgs, options) {
|
|
|
18889
19187
|
}
|
|
18890
19188
|
}
|
|
18891
19189
|
function createStateDestroyCommand() {
|
|
18892
|
-
const cmd = new Command("destroy").description("Destroy a stack's AWS resources and remove its state record without requiring the CDK app. For removing only the state record (keeping AWS resources intact), use 'cdkd state orphan'.").argument("[stacks...]", "Stack name(s) to destroy (physical CloudFormation names)").addOption(new Option("--all").hideHelp()).option("--remove-protection", `
|
|
19190
|
+
const cmd = new Command("destroy").description("Destroy a stack's AWS resources and remove its state record without requiring the CDK app. For removing only the state record (keeping AWS resources intact), use 'cdkd state orphan'.").argument("[stacks...]", "Stack name(s) to destroy (physical CloudFormation names)").addOption(new Option("--all").hideHelp()).option("--remove-protection", `Destroy a stack deployed with terminationProtection: true, and bypass deletion protection on protected resources by flipping the per-resource protection flag off in-place before delete. Covers ${removeProtectionTypeList()}.`, false).addOption(stackRegionOption$1()).addHelpText("after", [
|
|
18893
19191
|
"",
|
|
18894
19192
|
"Examples:",
|
|
18895
19193
|
" cdkd state destroy MyStack",
|
|
@@ -19366,7 +19664,9 @@ async function refreshObservedForStack(stackName, region, stateBackend, lockMana
|
|
|
19366
19664
|
refreshed++;
|
|
19367
19665
|
} catch (err) {
|
|
19368
19666
|
failed++;
|
|
19369
|
-
logger.
|
|
19667
|
+
if (err instanceof SyntaxError) logger.debug(safeMsg`readCurrentState parse failure for ${displayIdent(logicalId)}: ${parseFailureDebugDetail(err)}`);
|
|
19668
|
+
const detail = err instanceof SyntaxError ? "SyntaxError (its text is withheld: a parse error quotes the body it read)" : describeAwsFailure(err).detail;
|
|
19669
|
+
logger.warn(safeMsg` ✗ ${displayStackName(stackName)}/${displayIdent(logicalId)} ` + safeMsg`(${displayIdent(resource.resourceType)}): ` + safeMsg`readCurrentState failed — ${detail}`);
|
|
19370
19670
|
}
|
|
19371
19671
|
}));
|
|
19372
19672
|
await Promise.all(tasks);
|
|
@@ -19418,9 +19718,55 @@ function createStateRefreshObservedCommand() {
|
|
|
19418
19718
|
cmd.addOption(deprecatedRegionOption);
|
|
19419
19719
|
return cmd;
|
|
19420
19720
|
}
|
|
19721
|
+
async function stateBrowseCommand(options) {
|
|
19722
|
+
const logger = getLogger();
|
|
19723
|
+
if (options.verbose) logger.setLevel("debug");
|
|
19724
|
+
const streams = {
|
|
19725
|
+
input: process.stdin,
|
|
19726
|
+
output: process.stdout
|
|
19727
|
+
};
|
|
19728
|
+
if (!isInteractiveTerminal(streams)) throw nonInteractiveBrowseRefusal();
|
|
19729
|
+
const setup = await setupStateBackend(options);
|
|
19730
|
+
try {
|
|
19731
|
+
await runStateBrowse(stateBrowseDeps(setup, options, streams));
|
|
19732
|
+
} finally {
|
|
19733
|
+
setup.dispose();
|
|
19734
|
+
}
|
|
19735
|
+
}
|
|
19736
|
+
function stateBrowseDeps(setup, options, streams) {
|
|
19737
|
+
return {
|
|
19738
|
+
streams,
|
|
19739
|
+
listStacks: async () => sortRefs(await setup.stateBackend.listStacks()),
|
|
19740
|
+
label: formatStackRefSafe,
|
|
19741
|
+
printView: async (view, ref) => {
|
|
19742
|
+
if (!ref.region) throw legacyRecordRefusal(ref.stackName);
|
|
19743
|
+
const base = {
|
|
19744
|
+
...options,
|
|
19745
|
+
stackRegion: ref.region,
|
|
19746
|
+
json: false
|
|
19747
|
+
};
|
|
19748
|
+
if (view === "show") await printStackShow(ref.stackName, {
|
|
19749
|
+
...base,
|
|
19750
|
+
showNested: false
|
|
19751
|
+
}, setup);
|
|
19752
|
+
else await printStackResources(ref.stackName, {
|
|
19753
|
+
...base,
|
|
19754
|
+
long: view === "resources-long"
|
|
19755
|
+
}, setup);
|
|
19756
|
+
},
|
|
19757
|
+
reportError: (error) => getLogger().error(formatError(error))
|
|
19758
|
+
};
|
|
19759
|
+
}
|
|
19760
|
+
function createStateBrowseCommand() {
|
|
19761
|
+
const cmd = new Command("browse").description("Pick a stack with the keyboard and view its resources or state record (needs a terminal)").action(withErrorHandling(stateBrowseCommand));
|
|
19762
|
+
[...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
|
|
19763
|
+
cmd.addOption(deprecatedRegionOption);
|
|
19764
|
+
return cmd;
|
|
19765
|
+
}
|
|
19421
19766
|
function createStateCommand() {
|
|
19422
19767
|
const cmd = new Command("state").description("Manage cdkd state stored in S3");
|
|
19423
19768
|
cmd.addCommand(createStateInfoCommand());
|
|
19769
|
+
cmd.addCommand(createStateBrowseCommand());
|
|
19424
19770
|
cmd.addCommand(createStateListCommand());
|
|
19425
19771
|
cmd.addCommand(createStateResourcesCommand());
|
|
19426
19772
|
cmd.addCommand(createStateShowCommand());
|
|
@@ -20265,7 +20611,8 @@ function printRunEvents(stackName, region, runId, events) {
|
|
|
20265
20611
|
const headShown = safeText(head);
|
|
20266
20612
|
const laterShows = rest.some((line) => safeText(line) !== "");
|
|
20267
20613
|
const message = !e.error.message ? "" : headShown !== "" ? `: ${headShown}` : laterShows ? "" : `: ${UNRENDERABLE}`;
|
|
20268
|
-
|
|
20614
|
+
const line = rest.length === 0 && headShown.startsWith(`${name}. `) ? `${name}${e.error.awsErrorCode === e.error.name ? "" : code}${headShown.slice(name.length)}` : `${name}${code}${message}`;
|
|
20615
|
+
logger.info(` ${red(line)}${reqId}`);
|
|
20269
20616
|
for (const line of rest) {
|
|
20270
20617
|
const shown = safeText(line);
|
|
20271
20618
|
if (shown !== "") logger.info(red(safeMsg` ${shown}`));
|
|
@@ -20518,7 +20865,8 @@ async function scanReferencedAssets(stateBackend, marker, logger, account) {
|
|
|
20518
20865
|
statePrefix: void 0
|
|
20519
20866
|
}, "the command below prints").trimEnd(), keyPrefixHoleSentence(recovery.statePrefix)].filter((part) => part !== "").join(" ");
|
|
20520
20867
|
const inspect = inspectHint === void 0 ? "its stack name is not safe to paste into a command, so inspect the object at that key directly" : `see the command below`;
|
|
20521
|
-
|
|
20868
|
+
logger.debug(safeMsg`State file ${displayIdent(key, { maxCodePoints: STATE_KEY_MAX_CODE_POINTS })} failed to parse: ${displaySafe(describeAwsFailure(error).detail, { asciiOnly: true }) || "<unrenderable>"}`);
|
|
20869
|
+
throw new CdkdError(`State file ${displayIdent(key, { maxCodePoints: STATE_KEY_MAX_CODE_POINTS })} is not valid JSON — aborting: gc must know every referenced asset before deleting anything, and this file's references are unreadable. Repair or remove the corrupt state file (${inspect}), then re-run.` + (accountClause === "" ? "" : ` ${accountClause}`) + (inspectHint === void 0 ? "" : `\nInspect it with: ${inspectHint}`), "GC_STATE_UNREADABLE");
|
|
20522
20870
|
}
|
|
20523
20871
|
collectAssetReferences(parsed, marker, refs);
|
|
20524
20872
|
}
|
|
@@ -20847,87 +21195,1435 @@ function keyPrefixHoleSentence(prefix) {
|
|
|
20847
21195
|
}
|
|
20848
21196
|
|
|
20849
21197
|
//#endregion
|
|
20850
|
-
//#region src/
|
|
20851
|
-
|
|
20852
|
-
const
|
|
20853
|
-
|
|
20854
|
-
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
20859
|
-
|
|
20860
|
-
}
|
|
20861
|
-
const
|
|
20862
|
-
|
|
20863
|
-
|
|
20864
|
-
const
|
|
20865
|
-
|
|
20866
|
-
|
|
20867
|
-
|
|
20868
|
-
|
|
20869
|
-
...options.region && { region: options.region },
|
|
20870
|
-
...options.profile && { profile: options.profile },
|
|
20871
|
-
...Object.keys(context).length > 0 && { context }
|
|
20872
|
-
};
|
|
20873
|
-
const { stacks: allStacks } = await synthesizer.synthesize(synthOptions);
|
|
20874
|
-
logger.debug(`Found ${allStacks.length} stack(s) in assembly`);
|
|
20875
|
-
const stackPatterns = stacks.length > 0 ? stacks : options.stack ? [options.stack] : [];
|
|
20876
|
-
let targetStacks;
|
|
20877
|
-
if (allStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks));
|
|
20878
|
-
if (options.all) targetStacks = allStacks;
|
|
20879
|
-
else if (stackPatterns.length > 0) targetStacks = matchStacks(allStacks, stackPatterns, { dedupe: "identity" });
|
|
20880
|
-
else if (allStacks.length === 1) targetStacks = allStacks;
|
|
20881
|
-
else throw new Error(`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`);
|
|
20882
|
-
if (targetStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks));
|
|
20883
|
-
const effectiveRegion = await resolveEffectiveRegion(options);
|
|
20884
|
-
exportInferredRegionForSdk(effectiveRegion);
|
|
20885
|
-
const baseRegion = effectiveRegion.region;
|
|
20886
|
-
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
20887
|
-
const stsClient = new STSClient({
|
|
20888
|
-
...ambientClientDefaults(),
|
|
20889
|
-
region: baseRegion
|
|
20890
|
-
});
|
|
20891
|
-
const accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
|
|
20892
|
-
stsClient.destroy();
|
|
20893
|
-
const useCdkBootstrapAssets = resolveUseCdkBootstrapAssets(options.useCdkBootstrapAssets);
|
|
20894
|
-
let markerAccess;
|
|
20895
|
-
const getModeResolver = async () => {
|
|
20896
|
-
if (markerAccess !== void 0) return markerAccess?.resolver ?? null;
|
|
20897
|
-
let bucket;
|
|
20898
|
-
try {
|
|
20899
|
-
bucket = await resolveStateBucketWithDefault(options.stateBucket, baseRegion, legacyStateBucketFallbackRegion(effectiveRegion));
|
|
20900
|
-
} catch {
|
|
20901
|
-
logger.info("No cdkd state bucket found — publishing to the asset-manifest destinations (CDK bootstrap storage) verbatim. Run 'cdkd bootstrap' to opt into cdkd-owned asset storage that 'cdk gc' never touches.");
|
|
20902
|
-
markerAccess = null;
|
|
20903
|
-
return null;
|
|
21198
|
+
//#region src/state/logical-id-rename.ts
|
|
21199
|
+
function applyLogicalIdRenames(state, renames, template) {
|
|
21200
|
+
const byFrom = /* @__PURE__ */ new Map();
|
|
21201
|
+
const targets = /* @__PURE__ */ new Set();
|
|
21202
|
+
for (const { from, to } of renames) {
|
|
21203
|
+
if (!hasOwnKey(state.resources, from)) throw new Error(`Logical ID ${from} is not in the stack's state`);
|
|
21204
|
+
if (hasOwnKey(state.resources, to)) throw new Error(`Logical ID ${to} is already in the stack's state`);
|
|
21205
|
+
if (byFrom.has(from) || targets.has(to) || from === to) throw new Error(`Logical ID ${from} -> ${to} is named more than once`);
|
|
21206
|
+
byFrom.set(from, to);
|
|
21207
|
+
targets.add(to);
|
|
21208
|
+
}
|
|
21209
|
+
const retiredBefore = state.retiredNameGenerations ?? {};
|
|
21210
|
+
const resources = {};
|
|
21211
|
+
const leaving = [];
|
|
21212
|
+
for (const [logicalId, record] of Object.entries(state.resources)) {
|
|
21213
|
+
const to = byFrom.get(logicalId);
|
|
21214
|
+
if (to === void 0) {
|
|
21215
|
+
defineOwnKey(resources, logicalId, rewriteRecordReferences(record, byFrom));
|
|
21216
|
+
continue;
|
|
20904
21217
|
}
|
|
20905
|
-
|
|
20906
|
-
|
|
20907
|
-
|
|
20908
|
-
});
|
|
20909
|
-
const backend = new S3StateBackend(markerClients.s3, {
|
|
20910
|
-
bucket,
|
|
20911
|
-
prefix: options.statePrefix
|
|
20912
|
-
}, {
|
|
20913
|
-
region: baseRegion,
|
|
20914
|
-
...options.profile && { profile: options.profile }
|
|
21218
|
+
leaving.push({
|
|
21219
|
+
logicalId,
|
|
21220
|
+
record
|
|
20915
21221
|
});
|
|
20916
|
-
const
|
|
20917
|
-
|
|
20918
|
-
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
|
|
20923
|
-
|
|
20924
|
-
|
|
20925
|
-
|
|
20926
|
-
|
|
20927
|
-
|
|
21222
|
+
const constructPath = constructPathOf(template, to);
|
|
21223
|
+
const retiredTo = hasOwnKey(retiredBefore, to) ? retiredBefore[to] : void 0;
|
|
21224
|
+
const moved = withNameGenerationReached({
|
|
21225
|
+
...rewriteRecordReferences(record, byFrom),
|
|
21226
|
+
...constructPath !== void 0 && { constructPath }
|
|
21227
|
+
}, to, retiredTo);
|
|
21228
|
+
defineOwnKey(resources, to, moved);
|
|
21229
|
+
}
|
|
21230
|
+
const { skippedOutputs: _dropped, ...rest } = state;
|
|
21231
|
+
const retired = retiredNameGenerationsAfter(state, leaving);
|
|
21232
|
+
return {
|
|
21233
|
+
...rest,
|
|
21234
|
+
...stackInstanceIdCarriedFrom(state),
|
|
21235
|
+
resources,
|
|
21236
|
+
...retired,
|
|
21237
|
+
...Array.isArray(state.supersededResources) && { supersededResources: state.supersededResources.map((entry) => {
|
|
21238
|
+
if (entry === null || typeof entry !== "object") return entry;
|
|
21239
|
+
const to = byFrom.get(entry.logicalId);
|
|
21240
|
+
return {
|
|
21241
|
+
...entry,
|
|
21242
|
+
...to !== void 0 && { logicalId: to },
|
|
21243
|
+
state: rewriteRecordReferences(entry.state, byFrom)
|
|
21244
|
+
};
|
|
21245
|
+
}) },
|
|
21246
|
+
...Array.isArray(state.orphans) && { orphans: state.orphans.map((entry) => entry === null || typeof entry !== "object" ? entry : {
|
|
21247
|
+
...entry,
|
|
21248
|
+
state: rewriteRecordReferences(entry.state, byFrom)
|
|
21249
|
+
}) },
|
|
21250
|
+
lastModified: Date.now()
|
|
20928
21251
|
};
|
|
20929
|
-
|
|
20930
|
-
|
|
21252
|
+
}
|
|
21253
|
+
function constructPathOf(template, logicalId) {
|
|
21254
|
+
const resources = template.Resources ?? {};
|
|
21255
|
+
if (!hasOwnKey(resources, logicalId)) return void 0;
|
|
21256
|
+
const metadata = resources[logicalId].Metadata;
|
|
21257
|
+
if (metadata === null || typeof metadata !== "object") return void 0;
|
|
21258
|
+
const path = metadata["aws:cdk:path"];
|
|
21259
|
+
return typeof path === "string" ? path : void 0;
|
|
21260
|
+
}
|
|
21261
|
+
function rewriteRecordReferences(record, renames) {
|
|
21262
|
+
if (record === null || typeof record !== "object" || !hasPlainPrototype(record)) return record;
|
|
21263
|
+
const out = { ...record };
|
|
21264
|
+
if (Array.isArray(record.dependencies)) {
|
|
21265
|
+
const seen = /* @__PURE__ */ new Set();
|
|
21266
|
+
out.dependencies = [];
|
|
21267
|
+
for (const dependency of record.dependencies) {
|
|
21268
|
+
const renamed = typeof dependency === "string" ? renames.get(dependency) ?? dependency : dependency;
|
|
21269
|
+
if (typeof renamed === "string") {
|
|
21270
|
+
if (seen.has(renamed)) continue;
|
|
21271
|
+
seen.add(renamed);
|
|
21272
|
+
}
|
|
21273
|
+
out.dependencies.push(renamed);
|
|
21274
|
+
}
|
|
21275
|
+
}
|
|
21276
|
+
if (record.properties !== void 0) out.properties = renameReferencesInValue(record.properties, renames);
|
|
21277
|
+
if (record.attributes !== void 0) out.attributes = renameReferencesInValue(record.attributes, renames);
|
|
21278
|
+
return out;
|
|
21279
|
+
}
|
|
21280
|
+
function renameReferencesInValue(value, renames) {
|
|
21281
|
+
if (Array.isArray(value)) return value.map((item) => renameReferencesInValue(item, renames));
|
|
21282
|
+
if (value === null || typeof value !== "object" || !hasPlainPrototype(value)) return value;
|
|
21283
|
+
const entries = Object.entries(value);
|
|
21284
|
+
if (entries.length === 1) {
|
|
21285
|
+
const [key, arg] = entries[0];
|
|
21286
|
+
if (key === "Ref" && typeof arg === "string") return { Ref: renames.get(arg) ?? arg };
|
|
21287
|
+
if (key === "Fn::GetAtt") return { "Fn::GetAtt": renameGetAttArgument(arg, renames) };
|
|
21288
|
+
if (key === "Fn::Sub") return { "Fn::Sub": renameSubArgument(arg, renames) };
|
|
21289
|
+
}
|
|
21290
|
+
const out = {};
|
|
21291
|
+
for (const [key, item] of entries) defineOwnKey(out, key, renameReferencesInValue(item, renames));
|
|
21292
|
+
return out;
|
|
21293
|
+
}
|
|
21294
|
+
function renameGetAttArgument(arg, renames) {
|
|
21295
|
+
if (Array.isArray(arg) && arg.length >= 1 && typeof arg[0] === "string") {
|
|
21296
|
+
const [target, ...rest] = arg;
|
|
21297
|
+
return [renames.get(target) ?? target, ...rest.map((item) => renameReferencesInValue(item, renames))];
|
|
21298
|
+
}
|
|
21299
|
+
if (typeof arg === "string") {
|
|
21300
|
+
const dot = arg.indexOf(".");
|
|
21301
|
+
if (dot > 0) {
|
|
21302
|
+
const target = arg.slice(0, dot);
|
|
21303
|
+
const renamed = renames.get(target);
|
|
21304
|
+
if (renamed !== void 0) return `${renamed}${arg.slice(dot)}`;
|
|
21305
|
+
}
|
|
21306
|
+
return arg;
|
|
21307
|
+
}
|
|
21308
|
+
return renameReferencesInValue(arg, renames);
|
|
21309
|
+
}
|
|
21310
|
+
function renameSubArgument(arg, renames) {
|
|
21311
|
+
if (typeof arg === "string") return renameSubTemplate(arg, renames, /* @__PURE__ */ new Set());
|
|
21312
|
+
if (Array.isArray(arg) && arg.length >= 1 && typeof arg[0] === "string") {
|
|
21313
|
+
const variables = arg[1];
|
|
21314
|
+
const shadowed = new Set(variables !== null && typeof variables === "object" && !Array.isArray(variables) ? Object.keys(variables) : []);
|
|
21315
|
+
return [renameSubTemplate(arg[0], renames, shadowed), ...arg.slice(1).map((item) => renameReferencesInValue(item, renames))];
|
|
21316
|
+
}
|
|
21317
|
+
return renameReferencesInValue(arg, renames);
|
|
21318
|
+
}
|
|
21319
|
+
function renameSubTemplate(template, renames, shadowed) {
|
|
21320
|
+
return template.replace(/\$\{([^}]*)\}/g, (whole, inner) => {
|
|
21321
|
+
if (inner.startsWith("!")) return whole;
|
|
21322
|
+
const dot = inner.indexOf(".");
|
|
21323
|
+
const target = dot === -1 ? inner : inner.slice(0, dot);
|
|
21324
|
+
if (shadowed.has(target) || shadowed.has(inner)) return whole;
|
|
21325
|
+
const renamed = renames.get(target);
|
|
21326
|
+
if (renamed === void 0) return whole;
|
|
21327
|
+
return `\${${renamed}${dot === -1 ? "" : inner.slice(dot)}}`;
|
|
21328
|
+
});
|
|
21329
|
+
}
|
|
21330
|
+
function removeMovedRecords(state, moves) {
|
|
21331
|
+
const leavingIds = /* @__PURE__ */ new Set();
|
|
21332
|
+
for (const { from } of moves) {
|
|
21333
|
+
if (!hasOwnKey(state.resources, from)) throw new Error(`Logical ID ${from} is not in the source stack's state`);
|
|
21334
|
+
leavingIds.add(from);
|
|
21335
|
+
}
|
|
21336
|
+
const resources = {};
|
|
21337
|
+
const leaving = [];
|
|
21338
|
+
for (const [logicalId, record] of Object.entries(state.resources)) {
|
|
21339
|
+
if (leavingIds.has(logicalId)) {
|
|
21340
|
+
leaving.push({
|
|
21341
|
+
logicalId,
|
|
21342
|
+
record
|
|
21343
|
+
});
|
|
21344
|
+
continue;
|
|
21345
|
+
}
|
|
21346
|
+
defineOwnKey(resources, logicalId, withoutDependencies(record, leavingIds));
|
|
21347
|
+
}
|
|
21348
|
+
const { skippedOutputs: _dropped, ...rest } = state;
|
|
21349
|
+
return {
|
|
21350
|
+
...rest,
|
|
21351
|
+
...stackInstanceIdCarriedFrom(state),
|
|
21352
|
+
resources,
|
|
21353
|
+
...retiredNameGenerationsAfter(state, leaving),
|
|
21354
|
+
...Array.isArray(state.orphans) && { orphans: state.orphans.map((entry) => entry === null || typeof entry !== "object" ? entry : {
|
|
21355
|
+
...entry,
|
|
21356
|
+
state: withoutDependencies(entry.state, leavingIds)
|
|
21357
|
+
}) },
|
|
21358
|
+
lastModified: Date.now()
|
|
21359
|
+
};
|
|
21360
|
+
}
|
|
21361
|
+
function insertMovedRecords(state, moved, template) {
|
|
21362
|
+
const renamesBySource = /* @__PURE__ */ new Map();
|
|
21363
|
+
for (const m of moved) {
|
|
21364
|
+
const group = renamesBySource.get(m.source) ?? /* @__PURE__ */ new Map();
|
|
21365
|
+
group.set(m.from, m.to);
|
|
21366
|
+
renamesBySource.set(m.source, group);
|
|
21367
|
+
}
|
|
21368
|
+
const resources = {};
|
|
21369
|
+
for (const [logicalId, record] of Object.entries(state.resources)) defineOwnKey(resources, logicalId, record);
|
|
21370
|
+
const retired = state.retiredNameGenerations ?? {};
|
|
21371
|
+
for (const { to, record, source } of moved) {
|
|
21372
|
+
if (hasOwnKey(state.resources, to) || hasOwnKey(resources, to)) throw new Error(`Logical ID ${to} is already in the destination stack's state`);
|
|
21373
|
+
const group = renamesBySource.get(source);
|
|
21374
|
+
const coMoved = new Set(group.values());
|
|
21375
|
+
const renamed = rewriteRecordReferences(record, group);
|
|
21376
|
+
const kept = renamed !== null && typeof renamed === "object" && Array.isArray(renamed.dependencies) ? {
|
|
21377
|
+
...renamed,
|
|
21378
|
+
dependencies: renamed.dependencies.filter((dep) => typeof dep !== "string" || coMoved.has(dep))
|
|
21379
|
+
} : renamed;
|
|
21380
|
+
const constructPath = constructPathOf(template, to);
|
|
21381
|
+
defineOwnKey(resources, to, withNameGenerationReached({
|
|
21382
|
+
...kept,
|
|
21383
|
+
...constructPath !== void 0 && { constructPath }
|
|
21384
|
+
}, to, hasOwnKey(retired, to) ? retired[to] : void 0));
|
|
21385
|
+
}
|
|
21386
|
+
const { skippedOutputs: _dropped, ...rest } = state;
|
|
21387
|
+
return {
|
|
21388
|
+
...rest,
|
|
21389
|
+
...stackInstanceIdCarriedFrom(state),
|
|
21390
|
+
resources,
|
|
21391
|
+
lastModified: Date.now()
|
|
21392
|
+
};
|
|
21393
|
+
}
|
|
21394
|
+
function withoutDependencies(record, drop) {
|
|
21395
|
+
if (record === null || typeof record !== "object" || !Array.isArray(record.dependencies)) return record;
|
|
21396
|
+
return {
|
|
21397
|
+
...record,
|
|
21398
|
+
dependencies: record.dependencies.filter((dep) => typeof dep !== "string" || !drop.has(dep))
|
|
21399
|
+
};
|
|
21400
|
+
}
|
|
21401
|
+
|
|
21402
|
+
//#endregion
|
|
21403
|
+
//#region src/utils/deferred-interrupts.ts
|
|
21404
|
+
async function withDeferredInterrupts(fn, onDeferred) {
|
|
21405
|
+
let held;
|
|
21406
|
+
const hold = (signal) => {
|
|
21407
|
+
if (held === void 0) {
|
|
21408
|
+
held = signal;
|
|
21409
|
+
onDeferred(signal);
|
|
21410
|
+
}
|
|
21411
|
+
};
|
|
21412
|
+
process.on("SIGINT", hold);
|
|
21413
|
+
process.on("SIGTERM", hold);
|
|
21414
|
+
let result;
|
|
21415
|
+
try {
|
|
21416
|
+
result = await fn();
|
|
21417
|
+
} finally {
|
|
21418
|
+
process.removeListener("SIGINT", hold);
|
|
21419
|
+
process.removeListener("SIGTERM", hold);
|
|
21420
|
+
}
|
|
21421
|
+
if (held !== void 0) throw new CdkdError(`Interrupted (${held}) after the state writes finished; they are complete.`, "INTERRUPTED");
|
|
21422
|
+
return result;
|
|
21423
|
+
}
|
|
21424
|
+
|
|
21425
|
+
//#endregion
|
|
21426
|
+
//#region src/cli/commands/refactor-move.ts
|
|
21427
|
+
const rowKey = (stack, region, logicalId) => JSON.stringify([
|
|
21428
|
+
stack,
|
|
21429
|
+
region,
|
|
21430
|
+
logicalId
|
|
21431
|
+
]);
|
|
21432
|
+
const location = (stack, logicalId) => `${stack}.${logicalId}`;
|
|
21433
|
+
async function planMoves(plans, envOf, overrides) {
|
|
21434
|
+
const planAt = (stack, region) => plans.find((p) => p.stackName === stack && p.region === region);
|
|
21435
|
+
const result = {
|
|
21436
|
+
moves: [],
|
|
21437
|
+
differing: [],
|
|
21438
|
+
refused: [],
|
|
21439
|
+
ambiguous: []
|
|
21440
|
+
};
|
|
21441
|
+
const takenFrom = /* @__PURE__ */ new Set();
|
|
21442
|
+
const takenTo = /* @__PURE__ */ new Set();
|
|
21443
|
+
const candidates = [];
|
|
21444
|
+
const move = (source, from, dest, to, resourceType, origin) => ({
|
|
21445
|
+
fromStack: source.stackName,
|
|
21446
|
+
fromRegion: source.region,
|
|
21447
|
+
from,
|
|
21448
|
+
toStack: dest.stackName,
|
|
21449
|
+
toRegion: dest.region,
|
|
21450
|
+
to,
|
|
21451
|
+
resourceType,
|
|
21452
|
+
source: origin
|
|
21453
|
+
});
|
|
21454
|
+
for (const o of overrides) {
|
|
21455
|
+
const source = planAt(o.fromStack, o.region);
|
|
21456
|
+
const dest = planAt(o.toStack, o.region);
|
|
21457
|
+
const refuse = (reason) => {
|
|
21458
|
+
result.refused.push({
|
|
21459
|
+
fromStack: o.fromStack,
|
|
21460
|
+
from: o.from,
|
|
21461
|
+
toStack: o.toStack,
|
|
21462
|
+
to: o.to,
|
|
21463
|
+
reason
|
|
21464
|
+
});
|
|
21465
|
+
};
|
|
21466
|
+
if (source === void 0 || dest === void 0) {
|
|
21467
|
+
refuse("both stacks must be selected and hold cdkd state; a refactor never creates a stack");
|
|
21468
|
+
continue;
|
|
21469
|
+
}
|
|
21470
|
+
if (envOf(source) !== o.env || envOf(dest) !== o.env) {
|
|
21471
|
+
refuse("the two stacks are not both in the mapping environment's account and region");
|
|
21472
|
+
continue;
|
|
21473
|
+
}
|
|
21474
|
+
const d = source.unpaired.deletes.find((x) => x.logicalId === o.from);
|
|
21475
|
+
const c = dest.unpaired.creates.find((x) => x.logicalId === o.to);
|
|
21476
|
+
if (d === void 0) refuse(`${o.from} is not a resource ${o.fromStack}'s state holds and its template no longer declares`);
|
|
21477
|
+
else if (c === void 0) refuse(`${o.to} is not a resource ${o.toStack}'s template declares and its state does not hold`);
|
|
21478
|
+
else if (d.resourceType !== c.resourceType) refuse(`the types differ (${d.resourceType} and ${c.resourceType})`);
|
|
21479
|
+
else if (!isPairableType(d.resourceType)) refuse(`cdkd refactor cannot move a ${d.resourceType} resource`);
|
|
21480
|
+
else if (takenFrom.has(rowKey(o.fromStack, source.region, o.from)) || takenTo.has(rowKey(o.toStack, dest.region, o.to))) refuse("the mapping names one of these locations more than once");
|
|
21481
|
+
else {
|
|
21482
|
+
takenFrom.add(rowKey(o.fromStack, source.region, o.from));
|
|
21483
|
+
takenTo.add(rowKey(o.toStack, dest.region, o.to));
|
|
21484
|
+
candidates.push(move(source, o.from, dest, o.to, d.resourceType, "override"));
|
|
21485
|
+
}
|
|
21486
|
+
}
|
|
21487
|
+
const envs = new Set(plans.map(envOf));
|
|
21488
|
+
for (const env of envs) {
|
|
21489
|
+
const envPlans = plans.filter((p) => envOf(p) === env);
|
|
21490
|
+
const deletes = envPlans.flatMap((plan) => plan.unpaired.deletes.filter((d) => !takenFrom.has(rowKey(plan.stackName, plan.region, d.logicalId))).map((d) => ({
|
|
21491
|
+
plan,
|
|
21492
|
+
row: d
|
|
21493
|
+
})));
|
|
21494
|
+
const creates = envPlans.flatMap((plan) => plan.unpaired.creates.filter((c) => !takenTo.has(rowKey(plan.stackName, plan.region, c.logicalId))).map((c) => ({
|
|
21495
|
+
plan,
|
|
21496
|
+
row: c
|
|
21497
|
+
})));
|
|
21498
|
+
const types = [...new Set(creates.map((c) => c.row.resourceType))].sort();
|
|
21499
|
+
for (const type of types) {
|
|
21500
|
+
if (!isPairableType(type)) continue;
|
|
21501
|
+
const ds = deletes.filter((d) => d.row.resourceType === type);
|
|
21502
|
+
const cs = creates.filter((c) => c.row.resourceType === type);
|
|
21503
|
+
if (ds.length === 0 || cs.length === 0) continue;
|
|
21504
|
+
if (ds.length === 1 && cs.length === 1) {
|
|
21505
|
+
if (ds[0].plan !== cs[0].plan) candidates.push(move(ds[0].plan, ds[0].row.logicalId, cs[0].plan, cs[0].row.logicalId, type, "detected"));
|
|
21506
|
+
continue;
|
|
21507
|
+
}
|
|
21508
|
+
const matchesOf = /* @__PURE__ */ new Map();
|
|
21509
|
+
const claims = /* @__PURE__ */ new Map();
|
|
21510
|
+
for (const c of cs) {
|
|
21511
|
+
const matches = ds.filter((d) => d.plan !== c.plan && looselyEqual(c.row.properties, d.row.properties));
|
|
21512
|
+
matchesOf.set(c, matches);
|
|
21513
|
+
for (const d of matches) claims.set(d, (claims.get(d) ?? 0) + 1);
|
|
21514
|
+
}
|
|
21515
|
+
const groupFrom = /* @__PURE__ */ new Set();
|
|
21516
|
+
const groupTo = /* @__PURE__ */ new Set();
|
|
21517
|
+
let groupPlan;
|
|
21518
|
+
for (const c of cs) {
|
|
21519
|
+
const matches = matchesOf.get(c) ?? [];
|
|
21520
|
+
if (matches.length === 0) continue;
|
|
21521
|
+
if (matches.length === 1 && claims.get(matches[0]) === 1) {
|
|
21522
|
+
const d = matches[0];
|
|
21523
|
+
candidates.push(move(d.plan, d.row.logicalId, c.plan, c.row.logicalId, type, "detected"));
|
|
21524
|
+
continue;
|
|
21525
|
+
}
|
|
21526
|
+
groupTo.add(location(c.plan.stackName, c.row.logicalId));
|
|
21527
|
+
for (const d of matches) {
|
|
21528
|
+
groupFrom.add(location(d.plan.stackName, d.row.logicalId));
|
|
21529
|
+
groupPlan ??= d.plan;
|
|
21530
|
+
}
|
|
21531
|
+
}
|
|
21532
|
+
if (groupTo.size > 0 && groupPlan !== void 0) result.ambiguous.push({
|
|
21533
|
+
resourceType: type,
|
|
21534
|
+
from: [...groupFrom].sort(),
|
|
21535
|
+
to: [...groupTo].sort(),
|
|
21536
|
+
plan: groupPlan
|
|
21537
|
+
});
|
|
21538
|
+
}
|
|
21539
|
+
}
|
|
21540
|
+
let kept = candidates;
|
|
21541
|
+
for (;;) {
|
|
21542
|
+
const failed = [];
|
|
21543
|
+
for (const dest of plans) {
|
|
21544
|
+
const incoming = kept.filter((m) => m.toStack === dest.stackName && m.toRegion === dest.region);
|
|
21545
|
+
if (incoming.length === 0) continue;
|
|
21546
|
+
const renamed = applyLogicalIdRenames(dest.state, dest.renames.map(({ from, to }) => ({
|
|
21547
|
+
from,
|
|
21548
|
+
to
|
|
21549
|
+
})), dest.template);
|
|
21550
|
+
const withMoves = insertMovedRecords(renamed, incoming.map((m) => ({
|
|
21551
|
+
from: m.from,
|
|
21552
|
+
to: m.to,
|
|
21553
|
+
record: planAt(m.fromStack, m.fromRegion).state.resources[m.from],
|
|
21554
|
+
source: producerRecordKey(m.fromStack, m.fromRegion)
|
|
21555
|
+
})), dest.template);
|
|
21556
|
+
const { changes } = await dest.diff(withMoves);
|
|
21557
|
+
for (const m of incoming) {
|
|
21558
|
+
const change = changes.get(m.to);
|
|
21559
|
+
if (change?.changeType === "NO_CHANGE") continue;
|
|
21560
|
+
failed.push(m);
|
|
21561
|
+
const paths = (change?.propertyChanges ?? []).map((p) => p.path);
|
|
21562
|
+
for (const a of change?.attributeChanges ?? []) paths.push(a.attribute);
|
|
21563
|
+
if (paths.length === 0) paths.push(`(${change?.changeType ?? "not in the diff"})`);
|
|
21564
|
+
if (m.source === "override") result.refused.push({
|
|
21565
|
+
fromStack: m.fromStack,
|
|
21566
|
+
from: m.from,
|
|
21567
|
+
toStack: m.toStack,
|
|
21568
|
+
to: m.to,
|
|
21569
|
+
reason: `its properties also changed (${paths.join(", ")}); cdkd refactor keeps only an unchanged resource. Revert the property change, run cdkd refactor, then deploy the change`
|
|
21570
|
+
});
|
|
21571
|
+
else result.differing.push({
|
|
21572
|
+
move: m,
|
|
21573
|
+
paths
|
|
21574
|
+
});
|
|
21575
|
+
}
|
|
21576
|
+
}
|
|
21577
|
+
if (failed.length === 0) break;
|
|
21578
|
+
kept = kept.filter((m) => !failed.includes(m));
|
|
21579
|
+
}
|
|
21580
|
+
for (const m of kept) {
|
|
21581
|
+
planAt(m.fromStack, m.fromRegion).movesOut.push(m);
|
|
21582
|
+
planAt(m.toStack, m.toRegion).movesIn.push(m);
|
|
21583
|
+
}
|
|
21584
|
+
result.moves = kept;
|
|
21585
|
+
const both = plans.filter((p) => p.movesOut.length > 0 && p.movesIn.length > 0);
|
|
21586
|
+
if (both.length > 0) throw new CdkdError(safeMsg`cdkd refactor refused: ${both.map((p) => p.stackName).join(", ")} would both lose ` + "and gain a resource in one run, so nothing was written. Refactor one direction at a time: select only the stacks of one move direction, or name one direction in the mapping file.", "REFACTOR_REFUSED");
|
|
21587
|
+
return result;
|
|
21588
|
+
}
|
|
21589
|
+
function renderMovePlan(plan) {
|
|
21590
|
+
const lines = [];
|
|
21591
|
+
if (plan.moves.length === 0 && plan.differing.length === 0 && plan.refused.length === 0 && plan.ambiguous.length === 0) return lines;
|
|
21592
|
+
lines.push("Moves between stacks");
|
|
21593
|
+
for (const m of plan.moves) lines.push(safeMsg` ${m.fromStack}.${m.from} -> ${m.toStack}.${m.to} (${m.resourceType}${m.source === "override" ? ", from the mapping file" : ""})`);
|
|
21594
|
+
for (const { move: m, paths } of plan.differing) lines.push(safeMsg` Not moved: ${m.fromStack}.${m.from} -> ${m.toStack}.${m.to} (${m.resourceType}) also ` + safeMsg`changes ${paths.join(", ")}. cdkd refactor keeps only an unchanged resource: revert ` + "the property change, run cdkd refactor, then deploy the change.");
|
|
21595
|
+
for (const group of plan.ambiguous) lines.push(safeMsg` Ambiguous (${group.resourceType}): ${group.from.join(", ")} could each be ` + safeMsg`${group.to.join(", ")}. Name the pairs with --override-file.`);
|
|
21596
|
+
for (const r of plan.refused) lines.push(safeMsg` Refused: ${r.fromStack}.${r.from} -> ${r.toStack}.${r.to}: ${r.reason}.`);
|
|
21597
|
+
return lines;
|
|
21598
|
+
}
|
|
21599
|
+
const lockKey = (p) => producerRecordKey(p.stackName, p.region);
|
|
21600
|
+
function marksFor(plan, startedAt) {
|
|
21601
|
+
return plan.movesOut.map((m) => ({
|
|
21602
|
+
from: m.from,
|
|
21603
|
+
toStack: m.toStack,
|
|
21604
|
+
toRegion: m.toRegion,
|
|
21605
|
+
to: m.to,
|
|
21606
|
+
startedAt
|
|
21607
|
+
}));
|
|
21608
|
+
}
|
|
21609
|
+
function withMarks(state, marks) {
|
|
21610
|
+
if (marks.length === 0) return state;
|
|
21611
|
+
const { moves } = pendingMovesOutOf(state);
|
|
21612
|
+
return {
|
|
21613
|
+
...state,
|
|
21614
|
+
...stackInstanceIdCarriedFrom(state),
|
|
21615
|
+
pendingMovesOut: [...moves, ...marks]
|
|
21616
|
+
};
|
|
21617
|
+
}
|
|
21618
|
+
function withoutMarks(state, done) {
|
|
21619
|
+
const { moves } = pendingMovesOutOf(state);
|
|
21620
|
+
const left = moves.filter((m) => !done.has(m.from));
|
|
21621
|
+
const { pendingMovesOut: _dropped, ...rest } = state;
|
|
21622
|
+
const carried = {
|
|
21623
|
+
...rest,
|
|
21624
|
+
...stackInstanceIdCarriedFrom(rest)
|
|
21625
|
+
};
|
|
21626
|
+
return left.length > 0 ? {
|
|
21627
|
+
...carried,
|
|
21628
|
+
pendingMovesOut: left
|
|
21629
|
+
} : carried;
|
|
21630
|
+
}
|
|
21631
|
+
async function applyMoveGroup(input) {
|
|
21632
|
+
const { stateBackend, lockManager, recovery, contentionMessage } = input;
|
|
21633
|
+
const plans = [...input.plans].sort((a, b) => lockKey(a) < lockKey(b) ? -1 : 1);
|
|
21634
|
+
await withLocks(plans, lockManager, contentionMessage, async () => {
|
|
21635
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
21636
|
+
for (const plan of plans) {
|
|
21637
|
+
const stateData = await stateBackend.getState(plan.stackName, plan.region);
|
|
21638
|
+
if (!stateData || stateData.etag !== plan.etag) throw new CdkdError(safeMsg`The state of stack ${plan.stackName} (${plan.region}) changed after cdkd ` + "refactor planned it, so nothing was written. Re-run cdkd refactor.", "REFACTOR_STATE_CHANGED");
|
|
21639
|
+
refuseReadForWrite(stateData.state, plan.stackName, plan.region, recovery);
|
|
21640
|
+
if (await stateBackend.loadRollbackJournal(plan.stackName, plan.region) !== null) throw new CdkdError(safeMsg`cdkd refactor refused stack ${plan.stackName} (${plan.region}): an earlier ` + safeMsg`deploy left a rollback journal, so nothing was written. Run 'cdkd rollback ` + safeMsg`${plan.stackName}' or a successful 'cdkd deploy' first.`, "REFACTOR_REFUSED");
|
|
21641
|
+
loaded.set(plan, {
|
|
21642
|
+
state: stateData.state,
|
|
21643
|
+
...stateData.migrationPending && { migrationPending: true }
|
|
21644
|
+
});
|
|
21645
|
+
}
|
|
21646
|
+
const recordOf = (stack, region, logicalId) => {
|
|
21647
|
+
const plan = plans.find((p) => p.stackName === stack && p.region === region);
|
|
21648
|
+
return loaded.get(plan).state.resources[logicalId];
|
|
21649
|
+
};
|
|
21650
|
+
const renamed = (plan) => applyLogicalIdRenames(loaded.get(plan).state, plan.renames.map(({ from, to }) => ({
|
|
21651
|
+
from,
|
|
21652
|
+
to
|
|
21653
|
+
})), plan.template);
|
|
21654
|
+
const startedAt = (input.now ?? Date.now)();
|
|
21655
|
+
const written = [];
|
|
21656
|
+
const fail = (plan, error) => {
|
|
21657
|
+
const sources = plans.filter((p) => p.movesOut.length > 0).map((p) => p.stackName);
|
|
21658
|
+
return new CdkdError(safeMsg`cdkd refactor could not save stack ${plan.stackName} (${plan.region}): ` + safeMsg`${error.message}.\n` + (written.length > 0 ? safeMsg`Written before the failure: ${written.join(", ")}.\n` : "") + safeMsg`Any move that started is recorded in its source stack (${sources.join(", ")}), ` + "whose 'cdkd deploy' and 'cdkd destroy' refuse until it is finished. Re-run " + safeMsg`'cdkd refactor ${sources.join(" ")}' to finish it.`, "REFACTOR_PARTIAL");
|
|
21659
|
+
};
|
|
21660
|
+
const save = async (plan, state, expectedEtag) => {
|
|
21661
|
+
try {
|
|
21662
|
+
const etag = await stateBackend.saveState(plan.stackName, plan.region, state, {
|
|
21663
|
+
expectedEtag,
|
|
21664
|
+
...loaded.get(plan).migrationPending && { migrateLegacy: true }
|
|
21665
|
+
});
|
|
21666
|
+
written.push(`${plan.stackName} (${plan.region})`);
|
|
21667
|
+
return etag;
|
|
21668
|
+
} catch (error) {
|
|
21669
|
+
throw fail(plan, error);
|
|
21670
|
+
}
|
|
21671
|
+
};
|
|
21672
|
+
const markerMoves = plans.flatMap((p) => p.movesOut.map((m) => ({
|
|
21673
|
+
fromStack: m.fromStack,
|
|
21674
|
+
fromRegion: m.fromRegion,
|
|
21675
|
+
from: m.from,
|
|
21676
|
+
toStack: m.toStack,
|
|
21677
|
+
toRegion: m.toRegion,
|
|
21678
|
+
to: m.to,
|
|
21679
|
+
startedAt
|
|
21680
|
+
})));
|
|
21681
|
+
if (markerMoves.length > 0) await updateRefactorMarker(stateBackend, (moves) => [...moves.filter((m) => !markerMoves.some((n) => sameMove(m, n))), ...markerMoves]);
|
|
21682
|
+
const sourceEtags = /* @__PURE__ */ new Map();
|
|
21683
|
+
const marked = /* @__PURE__ */ new Map();
|
|
21684
|
+
for (const plan of plans.filter((p) => p.movesIn.length === 0)) {
|
|
21685
|
+
const leaving = [...plan.renames.map((r) => r.from), ...plan.movesOut.map((m) => m.from)];
|
|
21686
|
+
if (leaving.length > 0) await stateBackend.rotateCreateTokenNonce(plan.stackName, plan.region, leaving);
|
|
21687
|
+
const state = withMarks(renamed(plan), marksFor(plan, startedAt));
|
|
21688
|
+
marked.set(plan, state);
|
|
21689
|
+
sourceEtags.set(plan, await save(plan, state, plan.etag));
|
|
21690
|
+
}
|
|
21691
|
+
for (const plan of plans.filter((p) => p.movesIn.length > 0)) {
|
|
21692
|
+
if (plan.renames.length > 0) await stateBackend.rotateCreateTokenNonce(plan.stackName, plan.region, plan.renames.map((r) => r.from));
|
|
21693
|
+
await save(plan, insertMovedRecords(renamed(plan), plan.movesIn.map((m) => ({
|
|
21694
|
+
from: m.from,
|
|
21695
|
+
to: m.to,
|
|
21696
|
+
record: recordOf(m.fromStack, m.fromRegion, m.from),
|
|
21697
|
+
source: producerRecordKey(m.fromStack, m.fromRegion)
|
|
21698
|
+
})), plan.template), plan.etag);
|
|
21699
|
+
}
|
|
21700
|
+
for (const plan of plans.filter((p) => p.movesOut.length > 0)) await save(plan, withoutMarks(removeMovedRecords(marked.get(plan), plan.movesOut.map(({ from, to }) => ({
|
|
21701
|
+
from,
|
|
21702
|
+
to
|
|
21703
|
+
}))), new Set(plan.movesOut.map((m) => m.from))), sourceEtags.get(plan));
|
|
21704
|
+
if (markerMoves.length > 0) try {
|
|
21705
|
+
await updateRefactorMarker(stateBackend, (moves) => moves.filter((m) => !markerMoves.some((n) => sameMove(m, n))));
|
|
21706
|
+
} catch (error) {
|
|
21707
|
+
throw new CdkdError(safeMsg`Every record of the refactor was written, but the refactor marker could not ` + safeMsg`be cleared: ${error.message}. Until it is, deploy and destroy of ` + "the moved stacks refuse; re-run cdkd refactor to clear it.", "REFACTOR_PARTIAL");
|
|
21708
|
+
}
|
|
21709
|
+
});
|
|
21710
|
+
}
|
|
21711
|
+
function refuseReadForWrite(state, stackName, region, recovery) {
|
|
21712
|
+
refuseMalformedState(state, stackName, region, recovery);
|
|
21713
|
+
refuseMalformedOrphans(state, stackName, region, recovery);
|
|
21714
|
+
refuseMalformedOrphanRecords(state, stackName, region, recovery);
|
|
21715
|
+
}
|
|
21716
|
+
async function withLocks(plans, lockManager, contentionMessage, fn) {
|
|
21717
|
+
const logger = getLogger();
|
|
21718
|
+
const owner = `${process.env["USER"] || "unknown"}@${process.env["HOSTNAME"] || "host"}:${process.pid}`;
|
|
21719
|
+
const locked = [];
|
|
21720
|
+
try {
|
|
21721
|
+
for (const plan of plans) {
|
|
21722
|
+
if (!await lockManager.acquireLock(plan.stackName, plan.region, owner, "refactor")) throw new Error(await contentionMessage(plan));
|
|
21723
|
+
locked.push(plan);
|
|
21724
|
+
}
|
|
21725
|
+
return await fn();
|
|
21726
|
+
} finally {
|
|
21727
|
+
for (const plan of [...locked].reverse()) await lockManager.releaseLock(plan.stackName, plan.region).catch((err) => {
|
|
21728
|
+
logger.warn(safeMsg`Failed to release lock: ${displayAwsMessage(describeAwsFailure(err).detail)}`);
|
|
21729
|
+
});
|
|
21730
|
+
}
|
|
21731
|
+
}
|
|
21732
|
+
async function finishPendingMoves(input) {
|
|
21733
|
+
const { stackName, region, stateBackend, lockManager, recovery, contentionMessage } = input;
|
|
21734
|
+
const finishedOf = (m) => ({
|
|
21735
|
+
fromStack: stackName,
|
|
21736
|
+
fromRegion: region,
|
|
21737
|
+
from: m.from,
|
|
21738
|
+
toStack: m.toStack,
|
|
21739
|
+
toRegion: m.toRegion,
|
|
21740
|
+
to: m.to
|
|
21741
|
+
});
|
|
21742
|
+
const first = await stateBackend.getState(stackName, region);
|
|
21743
|
+
if (!first) {
|
|
21744
|
+
const finished = [];
|
|
21745
|
+
const stuck = [];
|
|
21746
|
+
for (const m of input.markerMoves ?? []) {
|
|
21747
|
+
const dest = await stateBackend.getState(m.toStack, m.toRegion);
|
|
21748
|
+
if (dest !== null && Object.hasOwn(dest.state.resources, m.to)) finished.push(finishedOf(m));
|
|
21749
|
+
else stuck.push(safeMsg`${stackName}.${m.from} -> ${m.toStack}.${m.to}`);
|
|
21750
|
+
}
|
|
21751
|
+
if (stuck.length > 0) throw new CdkdError(safeMsg`cdkd refactor cannot finish ${stuck.join(", ")}: ${stackName} (${region}) has ` + "no cdkd state any more and the destination does not record the resource. If the resource is still in AWS, adopt it into the destination with cdkd import; then remove the move from the refactor marker in the state bucket.", "REFACTOR_REFUSED");
|
|
21752
|
+
return { finished };
|
|
21753
|
+
}
|
|
21754
|
+
const { moves: marked, malformed } = pendingMovesOutOf(first.state);
|
|
21755
|
+
const moves = [...marked, ...(input.markerMoves ?? []).filter((m) => !marked.some((n) => n.from === m.from))];
|
|
21756
|
+
if (malformed) throw new CdkdError(safeMsg`Stack ${stackName} (${region}) records an unfinished cdkd refactor move this ` + "binary cannot read (its pendingMovesOut field is malformed), so nothing was written. Inspect the record with cdkd state show and repair it by hand.", "REFACTOR_REFUSED");
|
|
21757
|
+
if (moves.length === 0) return { finished: [] };
|
|
21758
|
+
const keyOf = (s) => producerRecordKey(s.stackName, s.region);
|
|
21759
|
+
const stacks = /* @__PURE__ */ new Map();
|
|
21760
|
+
stacks.set(keyOf({
|
|
21761
|
+
stackName,
|
|
21762
|
+
region
|
|
21763
|
+
}), {
|
|
21764
|
+
stackName,
|
|
21765
|
+
region
|
|
21766
|
+
});
|
|
21767
|
+
for (const m of moves) stacks.set(keyOf({
|
|
21768
|
+
stackName: m.toStack,
|
|
21769
|
+
region: m.toRegion
|
|
21770
|
+
}), {
|
|
21771
|
+
stackName: m.toStack,
|
|
21772
|
+
region: m.toRegion
|
|
21773
|
+
});
|
|
21774
|
+
const ordered = [...stacks.values()].sort((a, b) => keyOf(a) < keyOf(b) ? -1 : 1);
|
|
21775
|
+
return withLocks(ordered, lockManager, contentionMessage, async () => {
|
|
21776
|
+
const read = /* @__PURE__ */ new Map();
|
|
21777
|
+
for (const s of ordered) {
|
|
21778
|
+
const data = await stateBackend.getState(s.stackName, s.region);
|
|
21779
|
+
if (data) {
|
|
21780
|
+
refuseReadForWrite(data.state, s.stackName, s.region, recovery);
|
|
21781
|
+
read.set(keyOf(s), {
|
|
21782
|
+
state: data.state,
|
|
21783
|
+
etag: data.etag,
|
|
21784
|
+
...data.migrationPending && { migrationPending: true }
|
|
21785
|
+
});
|
|
21786
|
+
}
|
|
21787
|
+
}
|
|
21788
|
+
const source = read.get(keyOf({
|
|
21789
|
+
stackName,
|
|
21790
|
+
region
|
|
21791
|
+
}));
|
|
21792
|
+
if (!source) return { finished: [] };
|
|
21793
|
+
const currentMarked = pendingMovesOutOf(source.state).moves;
|
|
21794
|
+
const current = [...currentMarked, ...(input.markerMoves ?? []).filter((m) => !currentMarked.some((n) => n.from === m.from))];
|
|
21795
|
+
const refusals = [];
|
|
21796
|
+
const finished = [];
|
|
21797
|
+
const additions = /* @__PURE__ */ new Map();
|
|
21798
|
+
for (const m of current) {
|
|
21799
|
+
const destKey = keyOf({
|
|
21800
|
+
stackName: m.toStack,
|
|
21801
|
+
region: m.toRegion
|
|
21802
|
+
});
|
|
21803
|
+
const dest = read.get(destKey);
|
|
21804
|
+
const record = Object.hasOwn(source.state.resources, m.from) ? source.state.resources[m.from] : void 0;
|
|
21805
|
+
const held = dest !== void 0 && Object.hasOwn(dest.state.resources, m.to) ? dest.state.resources[m.to] : void 0;
|
|
21806
|
+
const describe = safeMsg`${stackName}.${m.from} -> ${m.toStack}.${m.to}`;
|
|
21807
|
+
if (dest === void 0) refusals.push(safeMsg`${describe}: ${m.toStack} (${m.toRegion}) has no cdkd state`);
|
|
21808
|
+
else if (record === void 0) {
|
|
21809
|
+
if (held === void 0) refusals.push(safeMsg`${describe}: neither stack records the resource; adopt it into ${m.toStack} with cdkd import`);
|
|
21810
|
+
else finished.push(finishedOf(m));
|
|
21811
|
+
} else if (held === void 0 && !input.declaresInDestination(m.toStack, m.toRegion, m.to, record.resourceType)) refusals.push(safeMsg`${describe}: ${m.toStack}'s template does not declare ${m.to} as a ` + safeMsg`${record.resourceType}, so the record is not moved there. Check the move, ` + "and the CDK app the run synthesized");
|
|
21812
|
+
else if (held === void 0) {
|
|
21813
|
+
const list = additions.get(destKey) ?? [];
|
|
21814
|
+
list.push({
|
|
21815
|
+
from: m.from,
|
|
21816
|
+
to: m.to,
|
|
21817
|
+
record,
|
|
21818
|
+
source: keyOf({
|
|
21819
|
+
stackName,
|
|
21820
|
+
region
|
|
21821
|
+
})
|
|
21822
|
+
});
|
|
21823
|
+
additions.set(destKey, list);
|
|
21824
|
+
finished.push(finishedOf(m));
|
|
21825
|
+
} else if (held.physicalId !== record.physicalId) refusals.push(safeMsg`${describe}: ${m.toStack} already holds a different resource under ${m.to} ` + "(created by a deploy before the move finished). Delete or orphan that resource in " + safeMsg`${m.toStack}, then re-run cdkd refactor`);
|
|
21826
|
+
else finished.push(finishedOf(m));
|
|
21827
|
+
}
|
|
21828
|
+
for (const [destKey, list] of additions) {
|
|
21829
|
+
const dest = read.get(destKey);
|
|
21830
|
+
const s = stacks.get(destKey);
|
|
21831
|
+
await stateBackend.saveState(s.stackName, s.region, insertMovedRecords(dest.state, list, { Resources: {} }), {
|
|
21832
|
+
expectedEtag: dest.etag,
|
|
21833
|
+
...dest.migrationPending && { migrateLegacy: true }
|
|
21834
|
+
});
|
|
21835
|
+
}
|
|
21836
|
+
if (finished.length > 0) {
|
|
21837
|
+
const done = new Set(finished.map((f) => f.from));
|
|
21838
|
+
const leaving = finished.filter((f) => Object.hasOwn(source.state.resources, f.from)).map(({ from, to }) => ({
|
|
21839
|
+
from,
|
|
21840
|
+
to
|
|
21841
|
+
}));
|
|
21842
|
+
await stateBackend.saveState(stackName, region, withoutMarks(leaving.length > 0 ? removeMovedRecords(source.state, leaving) : source.state, done), {
|
|
21843
|
+
expectedEtag: source.etag,
|
|
21844
|
+
...source.migrationPending && { migrateLegacy: true }
|
|
21845
|
+
});
|
|
21846
|
+
}
|
|
21847
|
+
if (refusals.length > 0) {
|
|
21848
|
+
const error = new CdkdError(safeMsg`cdkd refactor could not finish ${refusals.length} interrupted move(s) out of ` + safeMsg`${stackName} (${region}); they stay recorded, so the deploy and destroy of ` + "both stacks keep refusing" + (finished.length > 0 ? safeMsg` (${finished.length} other move(s) were finished)` : "") + ":\n" + refusals.map((r) => ` ${r}.`).join("\n"), "REFACTOR_REFUSED");
|
|
21849
|
+
Object.assign(error, { finishedMoves: finished });
|
|
21850
|
+
throw error;
|
|
21851
|
+
}
|
|
21852
|
+
return { finished };
|
|
21853
|
+
});
|
|
21854
|
+
}
|
|
21855
|
+
async function finishInterruptedMoves(input) {
|
|
21856
|
+
const { stateBackend } = input;
|
|
21857
|
+
const marker = await stateBackend.getRefactorMarker();
|
|
21858
|
+
const parsed = marker === null ? {
|
|
21859
|
+
moves: [],
|
|
21860
|
+
malformed: false
|
|
21861
|
+
} : refactorMarkerMovesOf(marker.body);
|
|
21862
|
+
if (parsed.malformed) throw new CdkdError(safeMsg`The cdkd refactor marker ${stateBackend.refactorMarkerKey()} cannot be read, so ` + "cdkd refactor cannot tell which moves are unfinished; nothing was written. Inspect it in the state bucket, and delete it once no move is in progress.", "REFACTOR_REFUSED");
|
|
21863
|
+
const keyOf = (stackName, region) => producerRecordKey(stackName, region);
|
|
21864
|
+
const selected = new Set(input.selected.map((s) => keyOf(s.stackName, s.region)));
|
|
21865
|
+
const sources = /* @__PURE__ */ new Map();
|
|
21866
|
+
for (const s of input.selected) {
|
|
21867
|
+
const current = await stateBackend.getState(s.stackName, s.region);
|
|
21868
|
+
const marked = current ? pendingMovesOutOf(current.state).moves : [];
|
|
21869
|
+
if (current && hasPendingMovesOut(current.state)) sources.set(keyOf(s.stackName, s.region), {
|
|
21870
|
+
...s,
|
|
21871
|
+
markerMoves: [],
|
|
21872
|
+
marked
|
|
21873
|
+
});
|
|
21874
|
+
}
|
|
21875
|
+
for (const m of parsed.moves) {
|
|
21876
|
+
if (!selected.has(keyOf(m.fromStack, m.fromRegion)) && !selected.has(keyOf(m.toStack, m.toRegion))) continue;
|
|
21877
|
+
const key = keyOf(m.fromStack, m.fromRegion);
|
|
21878
|
+
const entry = sources.get(key) ?? {
|
|
21879
|
+
stackName: m.fromStack,
|
|
21880
|
+
region: m.fromRegion,
|
|
21881
|
+
markerMoves: [],
|
|
21882
|
+
marked: []
|
|
21883
|
+
};
|
|
21884
|
+
entry.markerMoves.push({
|
|
21885
|
+
from: m.from,
|
|
21886
|
+
toStack: m.toStack,
|
|
21887
|
+
toRegion: m.toRegion,
|
|
21888
|
+
to: m.to,
|
|
21889
|
+
startedAt: m.startedAt
|
|
21890
|
+
});
|
|
21891
|
+
sources.set(key, entry);
|
|
21892
|
+
}
|
|
21893
|
+
const declaresInDestination = (stackName, region, logicalId, resourceType) => {
|
|
21894
|
+
const resources = input.templateOf(stackName, region)?.Resources ?? {};
|
|
21895
|
+
return Object.hasOwn(resources, logicalId) && resources[logicalId].Type === resourceType;
|
|
21896
|
+
};
|
|
21897
|
+
const finished = [];
|
|
21898
|
+
const pending = [];
|
|
21899
|
+
for (const source of sources.values()) {
|
|
21900
|
+
if (input.dryRun) {
|
|
21901
|
+
for (const m of [...source.marked, ...source.markerMoves.filter((x) => !source.marked.some((y) => y.from === x.from))]) pending.push({
|
|
21902
|
+
fromStack: source.stackName,
|
|
21903
|
+
fromRegion: source.region,
|
|
21904
|
+
from: m.from,
|
|
21905
|
+
toStack: m.toStack,
|
|
21906
|
+
toRegion: m.toRegion,
|
|
21907
|
+
to: m.to
|
|
21908
|
+
});
|
|
21909
|
+
continue;
|
|
21910
|
+
}
|
|
21911
|
+
let result;
|
|
21912
|
+
try {
|
|
21913
|
+
result = await finishPendingMoves({
|
|
21914
|
+
stackName: source.stackName,
|
|
21915
|
+
region: source.region,
|
|
21916
|
+
markerMoves: source.markerMoves,
|
|
21917
|
+
declaresInDestination,
|
|
21918
|
+
stateBackend,
|
|
21919
|
+
lockManager: input.lockManager,
|
|
21920
|
+
recovery: input.recovery,
|
|
21921
|
+
contentionMessage: input.contentionMessage
|
|
21922
|
+
});
|
|
21923
|
+
} catch (error) {
|
|
21924
|
+
const done = error.finishedMoves ?? [];
|
|
21925
|
+
if (done.length > 0) await updateRefactorMarker(stateBackend, (moves) => moves.filter((m) => !done.some((f) => sameMove(m, f))));
|
|
21926
|
+
throw error;
|
|
21927
|
+
}
|
|
21928
|
+
if (result.finished.length > 0) await updateRefactorMarker(stateBackend, (moves) => moves.filter((m) => !result.finished.some((f) => sameMove(m, f))));
|
|
21929
|
+
finished.push(...result.finished);
|
|
21930
|
+
}
|
|
21931
|
+
return {
|
|
21932
|
+
finished,
|
|
21933
|
+
pending
|
|
21934
|
+
};
|
|
21935
|
+
}
|
|
21936
|
+
|
|
21937
|
+
//#endregion
|
|
21938
|
+
//#region src/cli/commands/refactor.ts
|
|
21939
|
+
function changedPaths(change) {
|
|
21940
|
+
if (change === void 0) return ["(not in the diff)"];
|
|
21941
|
+
const paths = (change.propertyChanges ?? []).map((p) => p.path);
|
|
21942
|
+
for (const a of change.attributeChanges ?? []) paths.push(a.attribute);
|
|
21943
|
+
return paths.length > 0 ? paths : [`(${change.changeType})`];
|
|
21944
|
+
}
|
|
21945
|
+
async function planStackRefactor(input) {
|
|
21946
|
+
const { stackName, region, state, etag, template, overrides, diff, recovery } = input;
|
|
21947
|
+
refuseMalformedOrphans(state, stackName, region, recovery);
|
|
21948
|
+
refuseMalformedOrphanRecords(state, stackName, region, recovery);
|
|
21949
|
+
const base = await diff(state);
|
|
21950
|
+
if (base.blocking.length > 0) throw new CdkdError(safeMsg`cdkd refactor refused stack ${stackName} (${region}): ` + `'cdkd diff' reports a condition 'cdkd deploy' refuses to start on. Resolve what 'cdkd diff ${stackName}' reports, then re-run cdkd refactor.`, "REFACTOR_REFUSED");
|
|
21951
|
+
const orphanIds = new Set((Array.isArray(state.orphans) ? state.orphans : []).map((o) => o !== null && typeof o === "object" ? o.logicalId : void 0).filter((id) => typeof id === "string"));
|
|
21952
|
+
const creates = [];
|
|
21953
|
+
const deletes = [];
|
|
21954
|
+
for (const change of base.changes.values()) if (change.changeType === "CREATE" && !orphanIds.has(change.logicalId)) creates.push({
|
|
21955
|
+
logicalId: change.logicalId,
|
|
21956
|
+
resourceType: change.resourceType,
|
|
21957
|
+
properties: change.desiredProperties ?? {}
|
|
21958
|
+
});
|
|
21959
|
+
else if (change.changeType === "DELETE") {
|
|
21960
|
+
const record = state.resources[change.logicalId];
|
|
21961
|
+
deletes.push({
|
|
21962
|
+
logicalId: change.logicalId,
|
|
21963
|
+
resourceType: change.resourceType,
|
|
21964
|
+
properties: record !== void 0 && record.properties !== null && typeof record.properties === "object" ? record.properties : {}
|
|
21965
|
+
});
|
|
21966
|
+
}
|
|
21967
|
+
const proposal = proposeRenames(creates, deletes, overrides);
|
|
21968
|
+
const refusedOverrides = [...proposal.refusedOverrides];
|
|
21969
|
+
const differing = [];
|
|
21970
|
+
const skipped = [];
|
|
21971
|
+
const candidates = proposal.renames.filter((r) => {
|
|
21972
|
+
if (!orphanIds.has(r.from)) return true;
|
|
21973
|
+
const reason = `a rollback-orphan record also names ${r.from}; run 'cdkd deploy' first to settle it`;
|
|
21974
|
+
if (r.source === "override") refusedOverrides.push({
|
|
21975
|
+
from: r.from,
|
|
21976
|
+
to: r.to,
|
|
21977
|
+
reason
|
|
21978
|
+
});
|
|
21979
|
+
else skipped.push({
|
|
21980
|
+
rename: r,
|
|
21981
|
+
reason
|
|
21982
|
+
});
|
|
21983
|
+
return false;
|
|
21984
|
+
});
|
|
21985
|
+
const confirm = async (proposed) => {
|
|
21986
|
+
let kept = [...proposed];
|
|
21987
|
+
while (kept.length > 0) {
|
|
21988
|
+
const renamed = applyLogicalIdRenames(state, toLogicalIdRenames(kept), template);
|
|
21989
|
+
const { changes } = await diff(renamed);
|
|
21990
|
+
const failed = kept.filter((r) => changes.get(r.to)?.changeType !== "NO_CHANGE");
|
|
21991
|
+
if (failed.length === 0) return {
|
|
21992
|
+
kept,
|
|
21993
|
+
changes
|
|
21994
|
+
};
|
|
21995
|
+
for (const rename of failed) {
|
|
21996
|
+
const paths = changedPaths(changes.get(rename.to));
|
|
21997
|
+
if (rename.source === "override") refusedOverrides.push({
|
|
21998
|
+
from: rename.from,
|
|
21999
|
+
to: rename.to,
|
|
22000
|
+
reason: `its properties also changed (${paths.join(", ")}); cdkd refactor keeps only an unchanged resource. Revert the property change, run cdkd refactor, then deploy the change`
|
|
22001
|
+
});
|
|
22002
|
+
else differing.push({
|
|
22003
|
+
rename,
|
|
22004
|
+
paths
|
|
22005
|
+
});
|
|
22006
|
+
}
|
|
22007
|
+
kept = kept.filter((r) => !failed.includes(r));
|
|
22008
|
+
}
|
|
22009
|
+
return {
|
|
22010
|
+
kept,
|
|
22011
|
+
changes: void 0
|
|
22012
|
+
};
|
|
22013
|
+
};
|
|
22014
|
+
let { kept: renames, changes: confirmed } = await confirm(candidates);
|
|
22015
|
+
let ambiguous = proposal.ambiguous;
|
|
22016
|
+
let budget = MAX_TRIAL_DIFFS;
|
|
22017
|
+
for (;;) {
|
|
22018
|
+
const takenFrom = new Set(renames.map((r) => r.from));
|
|
22019
|
+
const takenTo = new Set(renames.map((r) => r.to));
|
|
22020
|
+
const accepted = [];
|
|
22021
|
+
const remaining = [];
|
|
22022
|
+
for (const group of ambiguous) {
|
|
22023
|
+
const froms = group.from.filter((id) => !takenFrom.has(id));
|
|
22024
|
+
const tos = group.to.filter((id) => !takenTo.has(id));
|
|
22025
|
+
if (froms.length === 0 || tos.length === 0) continue;
|
|
22026
|
+
if (froms.length * tos.length > budget) {
|
|
22027
|
+
remaining.push({
|
|
22028
|
+
resourceType: group.resourceType,
|
|
22029
|
+
from: froms,
|
|
22030
|
+
to: tos
|
|
22031
|
+
});
|
|
22032
|
+
continue;
|
|
22033
|
+
}
|
|
22034
|
+
const passing = /* @__PURE__ */ new Map();
|
|
22035
|
+
for (const to of tos) {
|
|
22036
|
+
const matches = [];
|
|
22037
|
+
for (const from of froms) {
|
|
22038
|
+
budget -= 1;
|
|
22039
|
+
const { changes } = await diff(applyLogicalIdRenames(state, [...toLogicalIdRenames(renames), {
|
|
22040
|
+
from,
|
|
22041
|
+
to
|
|
22042
|
+
}], template));
|
|
22043
|
+
if (changes.get(to)?.changeType === "NO_CHANGE") matches.push(from);
|
|
22044
|
+
}
|
|
22045
|
+
passing.set(to, matches);
|
|
22046
|
+
}
|
|
22047
|
+
const claims = /* @__PURE__ */ new Map();
|
|
22048
|
+
for (const matches of passing.values()) for (const from of matches) claims.set(from, (claims.get(from) ?? 0) + 1);
|
|
22049
|
+
const leftFrom = /* @__PURE__ */ new Set();
|
|
22050
|
+
const leftTo = [];
|
|
22051
|
+
for (const [to, matches] of passing) {
|
|
22052
|
+
if (matches.length === 0) continue;
|
|
22053
|
+
if (matches.length === 1 && claims.get(matches[0]) === 1) {
|
|
22054
|
+
accepted.push({
|
|
22055
|
+
from: matches[0],
|
|
22056
|
+
to,
|
|
22057
|
+
resourceType: group.resourceType,
|
|
22058
|
+
source: "detected"
|
|
22059
|
+
});
|
|
22060
|
+
continue;
|
|
22061
|
+
}
|
|
22062
|
+
leftTo.push(to);
|
|
22063
|
+
for (const from of matches) leftFrom.add(from);
|
|
22064
|
+
}
|
|
22065
|
+
if (leftTo.length > 0) remaining.push({
|
|
22066
|
+
resourceType: group.resourceType,
|
|
22067
|
+
from: [...leftFrom].sort(),
|
|
22068
|
+
to: leftTo.sort()
|
|
22069
|
+
});
|
|
22070
|
+
}
|
|
22071
|
+
ambiguous = remaining;
|
|
22072
|
+
if (accepted.length === 0) break;
|
|
22073
|
+
({kept: renames, changes: confirmed} = await confirm([...renames, ...accepted]));
|
|
22074
|
+
}
|
|
22075
|
+
const renamedTo = new Set(renames.map((r) => r.to));
|
|
22076
|
+
const stillChanging = confirmed === void 0 ? [] : [...confirmed.values()].filter((change) => (change.changeType === "UPDATE" || change.changeType === "CREATE") && !renamedTo.has(change.logicalId) && [...referencedLogicalIds(template, change.logicalId)].some((id) => renamedTo.has(id))).map((change) => change.logicalId).sort();
|
|
22077
|
+
const takenTo = /* @__PURE__ */ new Set([
|
|
22078
|
+
...renames.map((r) => r.to),
|
|
22079
|
+
...skipped.map((x) => x.rename.to),
|
|
22080
|
+
...ambiguous.flatMap((g) => g.to),
|
|
22081
|
+
...overrides.map((o) => o.to)
|
|
22082
|
+
]);
|
|
22083
|
+
const takenFrom = /* @__PURE__ */ new Set([
|
|
22084
|
+
...renames.map((r) => r.from),
|
|
22085
|
+
...skipped.map((x) => x.rename.from),
|
|
22086
|
+
...ambiguous.flatMap((g) => g.from),
|
|
22087
|
+
...overrides.map((o) => o.from),
|
|
22088
|
+
...orphanIds
|
|
22089
|
+
]);
|
|
22090
|
+
return {
|
|
22091
|
+
stackName,
|
|
22092
|
+
region,
|
|
22093
|
+
etag,
|
|
22094
|
+
renames,
|
|
22095
|
+
differing,
|
|
22096
|
+
skipped,
|
|
22097
|
+
ambiguous,
|
|
22098
|
+
refusedOverrides,
|
|
22099
|
+
stillChanging,
|
|
22100
|
+
template,
|
|
22101
|
+
unpaired: {
|
|
22102
|
+
creates: creates.filter((c) => !takenTo.has(c.logicalId)),
|
|
22103
|
+
deletes: deletes.filter((d) => !takenFrom.has(d.logicalId))
|
|
22104
|
+
},
|
|
22105
|
+
movesOut: [],
|
|
22106
|
+
movesIn: [],
|
|
22107
|
+
state,
|
|
22108
|
+
diff
|
|
22109
|
+
};
|
|
22110
|
+
}
|
|
22111
|
+
const MAX_TRIAL_DIFFS = 64;
|
|
22112
|
+
function referencedLogicalIds(template, logicalId) {
|
|
22113
|
+
const out = /* @__PURE__ */ new Set();
|
|
22114
|
+
const resources = template.Resources ?? {};
|
|
22115
|
+
if (!Object.hasOwn(resources, logicalId)) return out;
|
|
22116
|
+
const resource = resources[logicalId];
|
|
22117
|
+
const dependsOn = resource.DependsOn;
|
|
22118
|
+
for (const id of Array.isArray(dependsOn) ? dependsOn : [dependsOn]) if (typeof id === "string") out.add(id);
|
|
22119
|
+
const walk = (value) => {
|
|
22120
|
+
if (Array.isArray(value)) {
|
|
22121
|
+
value.forEach(walk);
|
|
22122
|
+
return;
|
|
22123
|
+
}
|
|
22124
|
+
if (value === null || typeof value !== "object") return;
|
|
22125
|
+
for (const [key, arg] of Object.entries(value)) {
|
|
22126
|
+
if (key === "Ref" && typeof arg === "string") out.add(arg);
|
|
22127
|
+
else if (key === "Fn::GetAtt") {
|
|
22128
|
+
if (Array.isArray(arg) && typeof arg[0] === "string") out.add(arg[0]);
|
|
22129
|
+
else if (typeof arg === "string") out.add(arg.split(".")[0]);
|
|
22130
|
+
} else if (key === "Fn::Sub") {
|
|
22131
|
+
const text = typeof arg === "string" ? arg : Array.isArray(arg) ? arg[0] : void 0;
|
|
22132
|
+
if (typeof text === "string") for (const match of text.matchAll(/\$\{([^}!][^}.]*)/g)) out.add(match[1]);
|
|
22133
|
+
}
|
|
22134
|
+
walk(arg);
|
|
22135
|
+
}
|
|
22136
|
+
};
|
|
22137
|
+
walk(resource.Properties);
|
|
22138
|
+
return out;
|
|
22139
|
+
}
|
|
22140
|
+
function toLogicalIdRenames(renames) {
|
|
22141
|
+
return renames.map(({ from, to }) => ({
|
|
22142
|
+
from,
|
|
22143
|
+
to
|
|
22144
|
+
}));
|
|
22145
|
+
}
|
|
22146
|
+
async function applyStackRefactor(input) {
|
|
22147
|
+
const { plan, stateBackend, lockManager, contentionMessage } = input;
|
|
22148
|
+
const logger = getLogger();
|
|
22149
|
+
const owner = `${process.env["USER"] || "unknown"}@${process.env["HOSTNAME"] || "host"}:${process.pid}`;
|
|
22150
|
+
if (!await lockManager.acquireLock(plan.stackName, plan.region, owner, "refactor")) throw new Error(await contentionMessage());
|
|
22151
|
+
try {
|
|
22152
|
+
const stateData = await stateBackend.getState(plan.stackName, plan.region);
|
|
22153
|
+
if (!stateData || stateData.etag !== plan.etag) throw new CdkdError(safeMsg`The state of stack ${plan.stackName} (${plan.region}) changed after cdkd ` + `refactor planned it, so nothing was written for this stack. Re-run cdkd refactor.`, "REFACTOR_STATE_CHANGED");
|
|
22154
|
+
refuseMalformedState(stateData.state, plan.stackName, plan.region, input.recovery);
|
|
22155
|
+
refuseMalformedOrphans(stateData.state, plan.stackName, plan.region, input.recovery);
|
|
22156
|
+
refuseMalformedOrphanRecords(stateData.state, plan.stackName, plan.region, input.recovery);
|
|
22157
|
+
if (await stateBackend.loadRollbackJournal(plan.stackName, plan.region) !== null) throw rollbackJournalRefusal(plan.stackName, plan.region);
|
|
22158
|
+
const renamed = applyLogicalIdRenames(stateData.state, toLogicalIdRenames(plan.renames), plan.template);
|
|
22159
|
+
await stateBackend.rotateCreateTokenNonce(plan.stackName, plan.region, plan.renames.map((r) => r.from));
|
|
22160
|
+
await stateBackend.saveState(plan.stackName, plan.region, renamed, {
|
|
22161
|
+
expectedEtag: plan.etag,
|
|
22162
|
+
...stateData.migrationPending && { migrateLegacy: true }
|
|
22163
|
+
});
|
|
22164
|
+
} finally {
|
|
22165
|
+
await lockManager.releaseLock(plan.stackName, plan.region).catch((err) => {
|
|
22166
|
+
logger.warn(safeMsg`Failed to release lock: ${displayAwsMessage(describeAwsFailure(err).detail)}`);
|
|
22167
|
+
});
|
|
22168
|
+
}
|
|
22169
|
+
}
|
|
22170
|
+
function rollbackJournalRefusal(stackName, region) {
|
|
22171
|
+
return new CdkdError(safeMsg`cdkd refactor refused stack ${stackName} (${region}): an earlier deploy left a ` + safeMsg`rollback journal, so nothing was written for this stack. Run 'cdkd rollback ${stackName}' ` + `or a successful 'cdkd deploy' first.`, "REFACTOR_REFUSED");
|
|
22172
|
+
}
|
|
22173
|
+
function renderRefactorPlan(plan) {
|
|
22174
|
+
const lines = [safeMsg`Stack ${plan.stackName} (${plan.region})`];
|
|
22175
|
+
if (plan.renames.length === 0) lines.push(" No resource to refactor.");
|
|
22176
|
+
for (const r of plan.renames) lines.push(safeMsg` ${r.from} -> ${r.to} (${r.resourceType}${r.source === "override" ? ", from the mapping file" : ""})`);
|
|
22177
|
+
for (const { rename, paths } of plan.differing) lines.push(safeMsg` Not refactored: ${rename.from} -> ${rename.to} (${rename.resourceType}) also changes ` + safeMsg`${paths.join(", ")}. cdkd refactor keeps only an unchanged resource: revert the ` + "property change, run cdkd refactor, then deploy the change.");
|
|
22178
|
+
for (const { rename, reason } of plan.skipped) lines.push(safeMsg` Not refactored: ${rename.from} -> ${rename.to} (${rename.resourceType}): ${reason}.`);
|
|
22179
|
+
for (const group of plan.ambiguous) lines.push(safeMsg` Ambiguous (${group.resourceType}): ${group.from.join(", ")} could each be ` + safeMsg`${group.to.join(", ")}. Name the pairs with --override-file.`);
|
|
22180
|
+
for (const refused of plan.refusedOverrides) lines.push(safeMsg` Refused: ${refused.from} -> ${refused.to}: ${refused.reason}.`);
|
|
22181
|
+
if (plan.stillChanging.length > 0) lines.push(safeMsg` Warning: ${plan.stillChanging.join(", ")} reference a renamed resource and still ` + "change on the next deploy. Check that each pair above names the resource you meant; 'cdkd diff' after the refactor shows the change.");
|
|
22182
|
+
return lines;
|
|
22183
|
+
}
|
|
22184
|
+
function sampleMappingFile(plans, moveGroups = []) {
|
|
22185
|
+
const moveEnvironments = moveGroups.map(({ group, account }) => {
|
|
22186
|
+
const resources = {};
|
|
22187
|
+
group.from.forEach((from, i) => {
|
|
22188
|
+
Object.defineProperty(resources, from, {
|
|
22189
|
+
value: group.to[i] ?? group.to[0],
|
|
22190
|
+
enumerable: true,
|
|
22191
|
+
writable: true,
|
|
22192
|
+
configurable: true
|
|
22193
|
+
});
|
|
22194
|
+
});
|
|
22195
|
+
return {
|
|
22196
|
+
account,
|
|
22197
|
+
region: group.plan.region,
|
|
22198
|
+
resources
|
|
22199
|
+
};
|
|
22200
|
+
});
|
|
22201
|
+
const stackEnvironments = plans.filter(({ plan }) => plan.ambiguous.length > 0).map(({ plan, account }) => {
|
|
22202
|
+
const resources = {};
|
|
22203
|
+
for (const group of plan.ambiguous) group.from.forEach((from, i) => {
|
|
22204
|
+
const to = group.to[i] ?? group.to[0];
|
|
22205
|
+
Object.defineProperty(resources, `${plan.stackName}.${from}`, {
|
|
22206
|
+
value: `${plan.stackName}.${to}`,
|
|
22207
|
+
enumerable: true,
|
|
22208
|
+
writable: true,
|
|
22209
|
+
configurable: true
|
|
22210
|
+
});
|
|
22211
|
+
});
|
|
22212
|
+
return {
|
|
22213
|
+
account,
|
|
22214
|
+
region: plan.region,
|
|
22215
|
+
resources
|
|
22216
|
+
};
|
|
22217
|
+
});
|
|
22218
|
+
return JSON.stringify({ environments: [...stackEnvironments, ...moveEnvironments] }, null, 2);
|
|
22219
|
+
}
|
|
22220
|
+
function overridesForStack(environments, stackName, account, region, revert) {
|
|
22221
|
+
const out = [];
|
|
22222
|
+
for (const env of environments) {
|
|
22223
|
+
if (env.account !== account || env.region !== region) continue;
|
|
22224
|
+
for (const pair of mappingPairs(env.resources, revert)) {
|
|
22225
|
+
if (pair.fromStack !== stackName || pair.toStack !== stackName) continue;
|
|
22226
|
+
out.push({
|
|
22227
|
+
from: pair.from,
|
|
22228
|
+
to: pair.to
|
|
22229
|
+
});
|
|
22230
|
+
}
|
|
22231
|
+
}
|
|
22232
|
+
return out;
|
|
22233
|
+
}
|
|
22234
|
+
function moveOverridesOf(environments, accounts, revert) {
|
|
22235
|
+
const out = [];
|
|
22236
|
+
for (const env of environments) {
|
|
22237
|
+
if (!accounts.has(env.account)) continue;
|
|
22238
|
+
for (const pair of mappingPairs(env.resources, revert)) {
|
|
22239
|
+
if (pair.fromStack === pair.toStack) continue;
|
|
22240
|
+
out.push({
|
|
22241
|
+
region: env.region,
|
|
22242
|
+
env: envKey(env.account, env.region),
|
|
22243
|
+
...pair
|
|
22244
|
+
});
|
|
22245
|
+
}
|
|
22246
|
+
}
|
|
22247
|
+
return out;
|
|
22248
|
+
}
|
|
22249
|
+
function envKey(account, region) {
|
|
22250
|
+
return JSON.stringify([account, region]);
|
|
22251
|
+
}
|
|
22252
|
+
async function refactorCommand(stacks, options) {
|
|
22253
|
+
const providerClasses = await loadProviderClasses();
|
|
22254
|
+
const logger = getLogger();
|
|
22255
|
+
const holdNotice = (signal) => {
|
|
22256
|
+
logger.warn(safeMsg`${signal} received: finishing the cdkd state writes already started (a few S3 ` + "writes), then stopping.");
|
|
22257
|
+
};
|
|
22258
|
+
if (options.verbose) logger.setLevel("debug");
|
|
22259
|
+
if (options.revert === true && options.overrideFile === void 0) throw new CdkdError("--revert requires --override-file", "INCOMPATIBLE_OPTIONS");
|
|
22260
|
+
warnIfDeprecatedRegion(options);
|
|
22261
|
+
foldRegionOption(options);
|
|
22262
|
+
await applyRoleArnIfSet({
|
|
22263
|
+
roleArn: options.roleArn,
|
|
22264
|
+
region: options.region
|
|
22265
|
+
});
|
|
22266
|
+
const app = resolveApp(options.app);
|
|
22267
|
+
if (!app) throw new Error("No app command specified. Use --app, set CDKD_APP env var, or add \"app\" to cdk.json");
|
|
22268
|
+
let environments = [];
|
|
22269
|
+
if (options.overrideFile !== void 0) {
|
|
22270
|
+
let text;
|
|
22271
|
+
try {
|
|
22272
|
+
text = readFileSync(options.overrideFile, "utf-8");
|
|
22273
|
+
} catch {
|
|
22274
|
+
throw new CdkdError(safeMsg`Cannot read the mapping file ${options.overrideFile}`, "REFACTOR_MAPPING_FILE");
|
|
22275
|
+
}
|
|
22276
|
+
try {
|
|
22277
|
+
environments = parseMappingFile(text);
|
|
22278
|
+
} catch (error) {
|
|
22279
|
+
throw new CdkdError(safeMsg`Invalid mapping file ${options.overrideFile}: ${error.message}`, "REFACTOR_MAPPING_FILE");
|
|
22280
|
+
}
|
|
22281
|
+
}
|
|
22282
|
+
const effectiveRegion = await resolveEffectiveRegion(options);
|
|
22283
|
+
exportInferredRegionForSdk(effectiveRegion);
|
|
22284
|
+
const region = effectiveRegion.region;
|
|
22285
|
+
const stateBucket = await resolveStateBucketWithDefault(options.stateBucket, region, legacyStateBucketFallbackRegion(effectiveRegion));
|
|
22286
|
+
const awsClients = new AwsClients({
|
|
22287
|
+
region: effectiveRegion.region,
|
|
22288
|
+
...options.profile && { profile: options.profile }
|
|
22289
|
+
});
|
|
22290
|
+
setAwsClients(awsClients);
|
|
22291
|
+
try {
|
|
22292
|
+
logger.info(synthesisStatusMessage(app, "Synthesizing CDK app..."));
|
|
22293
|
+
const synthesizer = new Synthesizer();
|
|
22294
|
+
const context = parseContextOptions(options.context);
|
|
22295
|
+
const synthOptions = {
|
|
22296
|
+
app,
|
|
22297
|
+
output: options.output,
|
|
22298
|
+
...options.region && { region: options.region },
|
|
22299
|
+
...options.profile && { profile: options.profile },
|
|
22300
|
+
...Object.keys(context).length > 0 && { context },
|
|
22301
|
+
stateBucket,
|
|
22302
|
+
...options.profile && { macroExpandS3ClientOpts: { profile: options.profile } },
|
|
22303
|
+
deferMacroExpansion: true
|
|
22304
|
+
};
|
|
22305
|
+
const { stacks: allStacks } = await synthesizer.synthesize(synthOptions);
|
|
22306
|
+
const targetStacks = selectStacks(allStacks, stacks, options);
|
|
22307
|
+
await synthesizer.expandMacrosForStacks(targetStacks, synthOptions);
|
|
22308
|
+
const stateConfig = {
|
|
22309
|
+
bucket: stateBucket,
|
|
22310
|
+
prefix: options.statePrefix
|
|
22311
|
+
};
|
|
22312
|
+
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
22313
|
+
region,
|
|
22314
|
+
...options.profile && { profile: options.profile }
|
|
22315
|
+
});
|
|
22316
|
+
await stateBackend.verifyBucketExists();
|
|
22317
|
+
const lockManager = new LockManager(awsClients.s3, stateConfig);
|
|
22318
|
+
const recovery = {
|
|
22319
|
+
profile: options.profile,
|
|
22320
|
+
stateBucket,
|
|
22321
|
+
statePrefix: options.statePrefix
|
|
22322
|
+
};
|
|
22323
|
+
const regionForStack = stackRegionResolver({
|
|
22324
|
+
effective: effectiveRegion,
|
|
22325
|
+
probe: stateBackend,
|
|
22326
|
+
logger,
|
|
22327
|
+
siblings: allStacks.filter((s) => !s.region).map((s) => s.stackName)
|
|
22328
|
+
});
|
|
22329
|
+
const diffCalculator = new DiffCalculator();
|
|
22330
|
+
const registry = new ProviderRegistry();
|
|
22331
|
+
registerAllProviders(registry, providerClasses);
|
|
22332
|
+
const canonicalizeProperties = makeCanonicalizePropertiesFn(registry);
|
|
22333
|
+
const producerLookup = producerLookupOver(allStacks);
|
|
22334
|
+
const resolveAssetRedirect = createAssetRedirectResolver({
|
|
22335
|
+
stateBackend,
|
|
22336
|
+
stsRegion: region,
|
|
22337
|
+
...options.profile && { profile: options.profile },
|
|
22338
|
+
useCdkBootstrapAssets: resolveUseCdkBootstrapAssets(options.useCdkBootstrapAssets),
|
|
22339
|
+
suppressLegacyNotice: true
|
|
22340
|
+
});
|
|
22341
|
+
let callerAccount;
|
|
22342
|
+
const accountOf = async (stackInfo) => {
|
|
22343
|
+
if (stackInfo.account) return stackInfo.account;
|
|
22344
|
+
callerAccount ??= (await getAccountInfo()).accountId;
|
|
22345
|
+
return callerAccount;
|
|
22346
|
+
};
|
|
22347
|
+
const selectedStacks = [];
|
|
22348
|
+
for (const stackInfo of targetStacks) selectedStacks.push({
|
|
22349
|
+
stackName: stackInfo.stackName,
|
|
22350
|
+
region: await regionForStack(stackInfo.stackName, stackInfo.region)
|
|
22351
|
+
});
|
|
22352
|
+
const templateOf = (stackName, region) => allStacks.find((s) => s.stackName === stackName && (s.region === void 0 || s.region === region))?.template;
|
|
22353
|
+
const { finished: finishedMoves, pending: pendingMoves } = await withDeferredInterrupts(() => finishInterruptedMoves({
|
|
22354
|
+
selected: selectedStacks,
|
|
22355
|
+
templateOf,
|
|
22356
|
+
dryRun: options.dryRun === true,
|
|
22357
|
+
stateBackend,
|
|
22358
|
+
lockManager,
|
|
22359
|
+
recovery,
|
|
22360
|
+
contentionMessage: (st) => buildLockContentionMessage({
|
|
22361
|
+
lockManager,
|
|
22362
|
+
stackName: st.stackName,
|
|
22363
|
+
region: st.region,
|
|
22364
|
+
recovery
|
|
22365
|
+
})
|
|
22366
|
+
}), holdNotice);
|
|
22367
|
+
for (const f of finishedMoves) logger.info(safeMsg`Finished an interrupted move: ${f.fromStack}.${f.from} -> ${f.toStack}.${f.to}`);
|
|
22368
|
+
if (pendingMoves.length > 0) {
|
|
22369
|
+
for (const f of pendingMoves) logger.info(safeMsg`An interrupted move would be finished: ${f.fromStack}.${f.from} -> ${f.toStack}.${f.to}`);
|
|
22370
|
+
logger.info("\n--dry-run: state was NOT written. Re-run without --dry-run to finish these moves; the refactor plan is computed after them.");
|
|
22371
|
+
return;
|
|
22372
|
+
}
|
|
22373
|
+
const plans = [];
|
|
22374
|
+
const planStacks = /* @__PURE__ */ new Map();
|
|
22375
|
+
for (const stackInfo of targetStacks) {
|
|
22376
|
+
const stackRegion = await regionForStack(stackInfo.stackName, stackInfo.region);
|
|
22377
|
+
const stateData = await stateBackend.getState(stackInfo.stackName, stackRegion);
|
|
22378
|
+
if (!stateData) {
|
|
22379
|
+
logger.info(safeMsg`Stack ${stackInfo.stackName} (${stackRegion}) has no cdkd state: nothing to refactor.`);
|
|
22380
|
+
continue;
|
|
22381
|
+
}
|
|
22382
|
+
refuseMalformedState(stateData.state, stackInfo.stackName, stackRegion, recovery);
|
|
22383
|
+
if (await stateBackend.loadRollbackJournal(stackInfo.stackName, stackRegion) !== null) throw rollbackJournalRefusal(stackInfo.stackName, stackRegion);
|
|
22384
|
+
const assetRedirect = await resolveAssetRedirect(stackInfo.assetManifestPath, stackRegion);
|
|
22385
|
+
if (assetRedirect) rewriteTemplateAssetReferences(stackInfo.template, assetRedirect);
|
|
22386
|
+
const overrides = environments.length === 0 ? [] : overridesForStack(environments, stackInfo.stackName, await accountOf(stackInfo), stackRegion, options.revert === true);
|
|
22387
|
+
const diff = async (state) => {
|
|
22388
|
+
const result = await computeStackDiff(state, stackInfo.template, stackRegion, stackInfo.stackName, stateBackend, diffCalculator, {
|
|
22389
|
+
canonicalizeProperties,
|
|
22390
|
+
...options.cfnFallback === false && { cfnFallback: false },
|
|
22391
|
+
producerLookup,
|
|
22392
|
+
...stackInfo.account && { consumerAccount: stackInfo.account },
|
|
22393
|
+
refusalRecovery: recovery,
|
|
22394
|
+
inheritSecretBearingTemplate: false,
|
|
22395
|
+
previewMaskedInputs: true,
|
|
22396
|
+
...stackInfo.nestedTemplates && { nestedTemplates: stackInfo.nestedTemplates }
|
|
22397
|
+
});
|
|
22398
|
+
return {
|
|
22399
|
+
changes: result.changes,
|
|
22400
|
+
blocking: result.blocking
|
|
22401
|
+
};
|
|
22402
|
+
};
|
|
22403
|
+
const plan = await planStackRefactor({
|
|
22404
|
+
stackName: stackInfo.stackName,
|
|
22405
|
+
region: stackRegion,
|
|
22406
|
+
state: stateData.state,
|
|
22407
|
+
etag: stateData.etag,
|
|
22408
|
+
template: stackInfo.template,
|
|
22409
|
+
overrides,
|
|
22410
|
+
diff,
|
|
22411
|
+
recovery
|
|
22412
|
+
});
|
|
22413
|
+
plans.push(plan);
|
|
22414
|
+
planStacks.set(plan, stackInfo);
|
|
22415
|
+
}
|
|
22416
|
+
const accountByPlan = /* @__PURE__ */ new Map();
|
|
22417
|
+
if (plans.length > 1) for (const plan of plans) accountByPlan.set(plan, await accountOf(planStacks.get(plan)));
|
|
22418
|
+
const movePlan = plans.length > 1 ? await planMoves(plans, (plan) => envKey(accountByPlan.get(plan), plan.region), moveOverridesOf(environments, new Set(accountByPlan.values()), options.revert === true)) : {
|
|
22419
|
+
moves: [],
|
|
22420
|
+
differing: [],
|
|
22421
|
+
refused: [],
|
|
22422
|
+
ambiguous: []
|
|
22423
|
+
};
|
|
22424
|
+
if (plans.length <= 1 && environments.length > 0) {
|
|
22425
|
+
const lone = moveOverridesOf(environments, new Set(plans.length === 1 ? [await accountOf(planStacks.get(plans[0]))] : []), options.revert === true);
|
|
22426
|
+
for (const o of lone) movePlan.refused.push({
|
|
22427
|
+
fromStack: o.fromStack,
|
|
22428
|
+
from: o.from,
|
|
22429
|
+
toStack: o.toStack,
|
|
22430
|
+
to: o.to,
|
|
22431
|
+
reason: "a move needs both stacks selected; name both, or pass --all"
|
|
22432
|
+
});
|
|
22433
|
+
}
|
|
22434
|
+
for (const plan of plans) for (const line of renderRefactorPlan(plan)) logger.info(line);
|
|
22435
|
+
for (const line of renderMovePlan(movePlan)) logger.info(line);
|
|
22436
|
+
if (plans.some((p) => p.ambiguous.length > 0) || movePlan.ambiguous.length > 0) {
|
|
22437
|
+
const withAccounts = [];
|
|
22438
|
+
for (const plan of plans) {
|
|
22439
|
+
if (plan.ambiguous.length === 0) continue;
|
|
22440
|
+
withAccounts.push({
|
|
22441
|
+
plan,
|
|
22442
|
+
account: await accountOf(planStacks.get(plan))
|
|
22443
|
+
});
|
|
22444
|
+
}
|
|
22445
|
+
const moveGroups = [];
|
|
22446
|
+
for (const group of movePlan.ambiguous) moveGroups.push({
|
|
22447
|
+
group,
|
|
22448
|
+
account: await accountOf(planStacks.get(group.plan))
|
|
22449
|
+
});
|
|
22450
|
+
logger.info("\nA mapping file naming each ambiguous pair (edit the pairs, then pass it with --override-file):");
|
|
22451
|
+
for (const line of sampleMappingFile(withAccounts, moveGroups).split("\n")) logger.info(displaySafe(line));
|
|
22452
|
+
throw new CdkdError("cdkd refactor found resources it cannot pair on its own; nothing was written. Name each ambiguous pair in a mapping file and re-run with --override-file.", "REFACTOR_AMBIGUOUS");
|
|
22453
|
+
}
|
|
22454
|
+
const refused = [...plans.flatMap((p) => p.refusedOverrides), ...movePlan.refused];
|
|
22455
|
+
if (refused.length > 0) throw new CdkdError(`cdkd refactor refused ${refused.length} pair(s) the mapping file names; nothing was written. Fix or remove each refused pair in the mapping file (the reasons are above), then re-run cdkd refactor.`, "REFACTOR_REFUSED");
|
|
22456
|
+
const toApply = plans.filter((p) => p.renames.length > 0 || p.movesOut.length > 0 || p.movesIn.length > 0);
|
|
22457
|
+
if (toApply.length === 0) {
|
|
22458
|
+
logger.info("\nNothing to refactor.");
|
|
22459
|
+
return;
|
|
22460
|
+
}
|
|
22461
|
+
if (options.dryRun) {
|
|
22462
|
+
logger.info("\n--dry-run: state was NOT written. Re-run without --dry-run to apply.");
|
|
22463
|
+
return;
|
|
22464
|
+
}
|
|
22465
|
+
const count = toApply.reduce((n, p) => n + p.renames.length, 0) + movePlan.moves.length;
|
|
22466
|
+
if (!options.yes && !options.force) {
|
|
22467
|
+
if (!await confirmOrRefuse(`\nRename or move ${count} resource(s) in cdkd state as listed above? AWS resources are not touched.`, { refusal: "The cdkd refactor confirmation prompt cannot run in a non-interactive environment. Pass -y / --yes (or -f / --force) to confirm, or run the command from a real terminal." })) {
|
|
22468
|
+
logger.info("Refactor cancelled.");
|
|
22469
|
+
return;
|
|
22470
|
+
}
|
|
22471
|
+
}
|
|
22472
|
+
if (movePlan.moves.length > 0) {
|
|
22473
|
+
await withDeferredInterrupts(() => applyMoveGroup({
|
|
22474
|
+
plans: toApply,
|
|
22475
|
+
stateBackend,
|
|
22476
|
+
lockManager,
|
|
22477
|
+
recovery,
|
|
22478
|
+
contentionMessage: (plan) => buildLockContentionMessage({
|
|
22479
|
+
lockManager,
|
|
22480
|
+
stackName: plan.stackName,
|
|
22481
|
+
region: plan.region,
|
|
22482
|
+
recovery
|
|
22483
|
+
})
|
|
22484
|
+
}), holdNotice);
|
|
22485
|
+
logger.info(safeMsg`Refactored ${count} resource(s) across ${toApply.length} stack(s): ` + safeMsg`${toApply.map((p) => `${p.stackName} (${p.region})`).join(", ")}.`);
|
|
22486
|
+
logger.info("\nAWS resources were not touched. Run 'cdkd diff' to confirm the refactored resources show no change, and deploy the destination stacks before the stacks that import from them.");
|
|
22487
|
+
return;
|
|
22488
|
+
}
|
|
22489
|
+
const done = [];
|
|
22490
|
+
for (const plan of toApply) {
|
|
22491
|
+
try {
|
|
22492
|
+
await withDeferredInterrupts(() => applyStackRefactor({
|
|
22493
|
+
plan,
|
|
22494
|
+
stateBackend,
|
|
22495
|
+
lockManager,
|
|
22496
|
+
recovery,
|
|
22497
|
+
contentionMessage: () => buildLockContentionMessage({
|
|
22498
|
+
lockManager,
|
|
22499
|
+
stackName: plan.stackName,
|
|
22500
|
+
region: plan.region,
|
|
22501
|
+
recovery
|
|
22502
|
+
})
|
|
22503
|
+
}), holdNotice);
|
|
22504
|
+
} catch (error) {
|
|
22505
|
+
if (done.length > 0) logger.error(safeMsg`Already refactored before this failure, and kept: ${done.join(", ")}.`);
|
|
22506
|
+
throw error;
|
|
22507
|
+
}
|
|
22508
|
+
done.push(`${plan.stackName} (${plan.region})`);
|
|
22509
|
+
logger.info(safeMsg`Refactored ${plan.renames.length} resource(s) in stack ${plan.stackName} (${plan.region}).`);
|
|
22510
|
+
}
|
|
22511
|
+
logger.info("\nAWS resources were not touched. Run 'cdkd diff' to confirm the renamed resources show no change.");
|
|
22512
|
+
} finally {
|
|
22513
|
+
awsClients.destroy();
|
|
22514
|
+
}
|
|
22515
|
+
}
|
|
22516
|
+
function selectStacks(allStacks, stacks, options) {
|
|
22517
|
+
const logger = getLogger();
|
|
22518
|
+
const stackPatterns = stacks.length > 0 ? stacks : options.stack ? [options.stack] : [];
|
|
22519
|
+
if (allStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks));
|
|
22520
|
+
let targetStacks;
|
|
22521
|
+
if (options.all) {
|
|
22522
|
+
const { topLevel, inStages } = partitionTopLevel(allStacks);
|
|
22523
|
+
if (topLevel.length === 0) throw new Error(renderAllNoTopLevelStacks(inStages));
|
|
22524
|
+
if (inStages.length > 0) logger.info(renderAllLeftOutStageStacks(inStages));
|
|
22525
|
+
targetStacks = topLevel;
|
|
22526
|
+
} else if (stackPatterns.length > 0) targetStacks = matchStacks(allStacks, stackPatterns, { dedupe: "identity" });
|
|
22527
|
+
else if (allStacks.length === 1) targetStacks = allStacks;
|
|
22528
|
+
else throw new Error(`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all (top-level stacks; '**' for every stack)`);
|
|
22529
|
+
if (targetStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks));
|
|
22530
|
+
return targetStacks;
|
|
22531
|
+
}
|
|
22532
|
+
function createRefactorCommand() {
|
|
22533
|
+
const cmd = new Command("refactor").description("Keep resources whose logical IDs a construct rename changed, instead of replacing them (mirrors 'cdk refactor'). Rewrites cdkd state only; AWS resources are not touched.").argument("[stacks...]", "Stack name(s) to refactor. Accepts CDK display paths (e.g. 'MyStage/Api') with wildcards, or exact physical CloudFormation names.").option("--all", "Refactor every top-level stack (Stage stacks: '**')", false).option("--dry-run", "Print the refactor plan without writing state", false).option("--override-file <path>", "A JSON mapping file naming pairs the detection finds ambiguous, in the cdk refactor format: {\"environments\":[{\"account\",\"region\",\"resources\":{\"Stack.OldId\":\"Stack.NewId\"}}]}").option("--revert", "Apply the mapping file in reverse (requires --override-file)", false).option("-f, --force", "Skip the confirmation prompt", false).addOption(useCdkBootstrapAssetsOption).addOption(noCfnFallbackOption).action(withErrorHandling(refactorCommand));
|
|
22534
|
+
[
|
|
22535
|
+
...commonOptions,
|
|
22536
|
+
...appOptions,
|
|
22537
|
+
...stateOptions,
|
|
22538
|
+
...stackOptions,
|
|
22539
|
+
...contextOptions
|
|
22540
|
+
].forEach((opt) => cmd.addOption(opt));
|
|
22541
|
+
cmd.addOption(deprecatedRegionOption);
|
|
22542
|
+
return cmd;
|
|
22543
|
+
}
|
|
22544
|
+
|
|
22545
|
+
//#endregion
|
|
22546
|
+
//#region src/cli/commands/publish-assets.ts
|
|
22547
|
+
async function publishAssetsCommand(stacks, options) {
|
|
22548
|
+
const logger = getLogger();
|
|
22549
|
+
if (options.verbose) logger.setLevel("debug");
|
|
22550
|
+
warnIfDeprecatedRegion(options);
|
|
22551
|
+
const rawNamedRegion = rawCliRegion(options.region);
|
|
22552
|
+
foldRegionOption(options);
|
|
22553
|
+
await applyRoleArnIfSet({
|
|
22554
|
+
roleArn: options.roleArn,
|
|
22555
|
+
region: options.region
|
|
22556
|
+
});
|
|
22557
|
+
const app = resolveApp(options.app);
|
|
22558
|
+
if (!app) throw new Error("No app command specified. Use --app, set CDKD_APP env var, or add \"app\" to cdk.json");
|
|
22559
|
+
logger.info(synthesisStatusMessage(app, "Synthesizing CDK app..."));
|
|
22560
|
+
const synthesizer = new Synthesizer();
|
|
22561
|
+
const context = parseContextOptions(options.context);
|
|
22562
|
+
const synthOptions = {
|
|
22563
|
+
app,
|
|
22564
|
+
output: options.output,
|
|
22565
|
+
...options.region && { region: options.region },
|
|
22566
|
+
...options.profile && { profile: options.profile },
|
|
22567
|
+
...Object.keys(context).length > 0 && { context }
|
|
22568
|
+
};
|
|
22569
|
+
const { stacks: allStacks } = await synthesizer.synthesize(synthOptions);
|
|
22570
|
+
logger.debug(`Found ${allStacks.length} stack(s) in assembly`);
|
|
22571
|
+
const stackPatterns = stacks.length > 0 ? stacks : options.stack ? [options.stack] : [];
|
|
22572
|
+
let targetStacks;
|
|
22573
|
+
if (allStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks));
|
|
22574
|
+
if (options.all) targetStacks = allStacks;
|
|
22575
|
+
else if (stackPatterns.length > 0) targetStacks = matchStacks(allStacks, stackPatterns, { dedupe: "identity" });
|
|
22576
|
+
else if (allStacks.length === 1) targetStacks = allStacks;
|
|
22577
|
+
else throw new Error(`Multiple stacks found: ${allStacks.map(describeStack).join(", ")}. Specify stack name(s) or use --all`);
|
|
22578
|
+
if (targetStacks.length === 0) throw new Error(renderNoStackMatch(stackPatterns, allStacks));
|
|
22579
|
+
const effectiveRegion = await resolveEffectiveRegion(options);
|
|
22580
|
+
exportInferredRegionForSdk(effectiveRegion);
|
|
22581
|
+
const baseRegion = effectiveRegion.region;
|
|
22582
|
+
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
|
|
22583
|
+
const stsClient = new STSClient({
|
|
22584
|
+
...ambientClientDefaults(),
|
|
22585
|
+
region: baseRegion
|
|
22586
|
+
});
|
|
22587
|
+
const accountId = (await stsClient.send(new GetCallerIdentityCommand({}))).Account;
|
|
22588
|
+
stsClient.destroy();
|
|
22589
|
+
const useCdkBootstrapAssets = resolveUseCdkBootstrapAssets(options.useCdkBootstrapAssets);
|
|
22590
|
+
let markerAccess;
|
|
22591
|
+
const getModeResolver = async () => {
|
|
22592
|
+
if (markerAccess !== void 0) return markerAccess?.resolver ?? null;
|
|
22593
|
+
let bucket;
|
|
22594
|
+
try {
|
|
22595
|
+
bucket = await resolveStateBucketWithDefault(options.stateBucket, baseRegion, legacyStateBucketFallbackRegion(effectiveRegion));
|
|
22596
|
+
} catch {
|
|
22597
|
+
logger.info("No cdkd state bucket found — publishing to the asset-manifest destinations (CDK bootstrap storage) verbatim. Run 'cdkd bootstrap' to opt into cdkd-owned asset storage that 'cdk gc' never touches.");
|
|
22598
|
+
markerAccess = null;
|
|
22599
|
+
return null;
|
|
22600
|
+
}
|
|
22601
|
+
const markerClients = new AwsClients({
|
|
22602
|
+
region: baseRegion,
|
|
22603
|
+
...options.profile && { profile: options.profile }
|
|
22604
|
+
});
|
|
22605
|
+
const backend = new S3StateBackend(markerClients.s3, {
|
|
22606
|
+
bucket,
|
|
22607
|
+
prefix: options.statePrefix
|
|
22608
|
+
}, {
|
|
22609
|
+
region: baseRegion,
|
|
22610
|
+
...options.profile && { profile: options.profile }
|
|
22611
|
+
});
|
|
22612
|
+
const resolver = new AssetModeResolver(backend, accountId, { ...options.profile && { profile: options.profile } });
|
|
22613
|
+
markerAccess = {
|
|
22614
|
+
resolver,
|
|
22615
|
+
regionFor: stackRegionResolver({
|
|
22616
|
+
effective: effectiveRegion,
|
|
22617
|
+
probe: backend,
|
|
22618
|
+
logger,
|
|
22619
|
+
siblings: allStacks.filter((s) => !s.region).map((s) => s.stackName)
|
|
22620
|
+
}),
|
|
22621
|
+
dispose: () => markerClients.destroy()
|
|
22622
|
+
};
|
|
22623
|
+
return resolver;
|
|
22624
|
+
};
|
|
22625
|
+
const regionForStack = async (stack) => {
|
|
22626
|
+
if (stack.region) return stack.region;
|
|
20931
22627
|
if (!regionNeedsReconciliation(effectiveRegion)) return baseRegion;
|
|
20932
22628
|
await getModeResolver();
|
|
20933
22629
|
return markerAccess ? markerAccess.regionFor(stack.stackName) : baseRegion;
|
|
@@ -21835,7 +23531,8 @@ function parseMappingJson(raw, source) {
|
|
|
21835
23531
|
try {
|
|
21836
23532
|
parsed = JSON.parse(raw);
|
|
21837
23533
|
} catch (err) {
|
|
21838
|
-
|
|
23534
|
+
getLogger().debug(safeMsg`${source} failed to parse: ${parseFailureDebugDetail(err)}`);
|
|
23535
|
+
throw new Error(`Failed to parse ${source} as JSON: ` + describeJsonParseFailure(err, utf8ByteLength(raw)));
|
|
21839
23536
|
}
|
|
21840
23537
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`${source} must be a JSON object {logicalId: physicalId}`);
|
|
21841
23538
|
const out = {};
|
|
@@ -21983,7 +23680,7 @@ function buildStackState(stackName, region, rows, templateParser, template, exis
|
|
|
21983
23680
|
dependencies: deps,
|
|
21984
23681
|
deletionPolicy: tmplResource.DeletionPolicy,
|
|
21985
23682
|
updateReplacePolicy: tmplResource.UpdateReplacePolicy,
|
|
21986
|
-
...constructPathField(constructPathOf(template, row.logicalId)),
|
|
23683
|
+
...constructPathField(constructPathOf$1(template, row.logicalId)),
|
|
21987
23684
|
provisionedBy: row.provisionedBy ?? "sdk",
|
|
21988
23685
|
...prior && prior.physicalId === row.physicalId && (hasUnverifiableParameterRefusal(prior) || hasReasonlessBaselineRefusal(prior) && (namesParameter ??= resourcesNamingDeclaredParameter(template))(row.logicalId)) && {
|
|
21989
23686
|
observedBaselineRefused: true,
|
|
@@ -22508,7 +24205,8 @@ function readNestedChildTemplate(templatePath, childLogicalId) {
|
|
|
22508
24205
|
try {
|
|
22509
24206
|
return JSON.parse(raw);
|
|
22510
24207
|
} catch (err) {
|
|
22511
|
-
|
|
24208
|
+
getLogger().debug(safeMsg`Nested-stack template for '${childLogicalId}' failed to parse: ${parseFailureDebugDetail(err)}`);
|
|
24209
|
+
throw new Error(`Failed to parse nested-stack template for '${childLogicalId}' at ${templatePath}: ${describeJsonParseFailure(err, utf8ByteLength(raw))}`);
|
|
22512
24210
|
}
|
|
22513
24211
|
}
|
|
22514
24212
|
function indexGrandchildTemplatePaths(childTemplate, childTemplatePath) {
|
|
@@ -24764,7 +26462,7 @@ function attachWebSocketServer(opts) {
|
|
|
24764
26462
|
});
|
|
24765
26463
|
});
|
|
24766
26464
|
ws.on("error", (err) => {
|
|
24767
|
-
logger.debug(`WebSocket error for connection ${connectionId}: ${
|
|
26465
|
+
logger.debug(`WebSocket error for connection ${connectionId}: ${describeAwsFailure(err).detail}`);
|
|
24768
26466
|
});
|
|
24769
26467
|
for (const frame of preVerdictFrames) {
|
|
24770
26468
|
const { body, isBase64Encoded } = bufferToBody$1(frame.raw, frame.isBinary);
|
|
@@ -26282,7 +27980,8 @@ function readEnvOverridesFile$3(filePath) {
|
|
|
26282
27980
|
try {
|
|
26283
27981
|
parsed = JSON.parse(raw);
|
|
26284
27982
|
} catch (err) {
|
|
26285
|
-
|
|
27983
|
+
getLogger().debug(safeMsg`--env-vars file failed to parse: ${parseFailureDebugDetail(err)}`);
|
|
27984
|
+
throw new Error(`Failed to parse --env-vars file '${filePath}' as JSON: ${describeJsonParseFailure(err, utf8ByteLength(raw))}`);
|
|
26286
27985
|
}
|
|
26287
27986
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`--env-vars file '${filePath}' must contain a JSON object at the top level.`);
|
|
26288
27987
|
return parsed;
|
|
@@ -27580,7 +29279,7 @@ async function localRunTaskCommand(target, options) {
|
|
|
27580
29279
|
await channels.profileCredsFile.dispose();
|
|
27581
29280
|
credsHostPath = void 0;
|
|
27582
29281
|
} catch (err) {
|
|
27583
|
-
getLogger().debug(`Failed to remove profile credentials tmpdir ${channels.profileCredsFile.hostPath}: ${
|
|
29282
|
+
getLogger().debug(`Failed to remove profile credentials tmpdir ${channels.profileCredsFile.hostPath}: ${describeAwsFailure(err).detail}`);
|
|
27584
29283
|
}
|
|
27585
29284
|
};
|
|
27586
29285
|
try {
|
|
@@ -27589,7 +29288,7 @@ async function localRunTaskCommand(target, options) {
|
|
|
27589
29288
|
afterContainersStopped: disposeCreds
|
|
27590
29289
|
});
|
|
27591
29290
|
} catch (err) {
|
|
27592
|
-
getLogger().debug(`cleanup failed: ${
|
|
29291
|
+
getLogger().debug(`cleanup failed: ${describeAwsFailure(err).detail}`);
|
|
27593
29292
|
}
|
|
27594
29293
|
await disposeCreds();
|
|
27595
29294
|
await flushStdio();
|
|
@@ -27873,13 +29572,14 @@ function readEnvOverridesFile$2(filePath) {
|
|
|
27873
29572
|
try {
|
|
27874
29573
|
raw = readFileSync(filePath, "utf-8");
|
|
27875
29574
|
} catch (err) {
|
|
27876
|
-
throw new Error(`Failed to read --env-vars file '${filePath}': ${
|
|
29575
|
+
throw new Error(`Failed to read --env-vars file '${filePath}': ${describeAwsFailure(err).detail}`);
|
|
27877
29576
|
}
|
|
27878
29577
|
let parsed;
|
|
27879
29578
|
try {
|
|
27880
29579
|
parsed = JSON.parse(raw);
|
|
27881
29580
|
} catch (err) {
|
|
27882
|
-
|
|
29581
|
+
getLogger().debug(safeMsg`--env-vars file failed to parse: ${parseFailureDebugDetail(err)}`);
|
|
29582
|
+
throw new Error(`Failed to parse --env-vars file '${filePath}' as JSON: ${describeJsonParseFailure(err, utf8ByteLength(raw))}`);
|
|
27883
29583
|
}
|
|
27884
29584
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`--env-vars file '${filePath}' must contain a JSON object at the top level.`);
|
|
27885
29585
|
return parsed;
|
|
@@ -28249,7 +29949,7 @@ async function localInvokeAgentCoreCommand(target, options) {
|
|
|
28249
29949
|
const stream = logStream;
|
|
28250
29950
|
logStream = void 0;
|
|
28251
29951
|
drainInFlight = killAndDrainContainerLogs(containerId, stream).catch((err) => {
|
|
28252
|
-
getLogger().debug(safeMsg`container log drain failed: ${
|
|
29952
|
+
getLogger().debug(safeMsg`container log drain failed: ${describeAwsFailure(err).detail}`);
|
|
28253
29953
|
}).finally(() => {
|
|
28254
29954
|
drainInFlight = void 0;
|
|
28255
29955
|
});
|
|
@@ -28269,7 +29969,7 @@ async function localInvokeAgentCoreCommand(target, options) {
|
|
|
28269
29969
|
if (stateProvider) try {
|
|
28270
29970
|
stateProvider.dispose();
|
|
28271
29971
|
} catch (err) {
|
|
28272
|
-
getLogger().debug(`state provider dispose failed: ${
|
|
29972
|
+
getLogger().debug(`state provider dispose failed: ${describeAwsFailure(err).detail}`);
|
|
28273
29973
|
}
|
|
28274
29974
|
if (startPending) getLogger().info("Waiting for the in-flight docker run so its container can be removed...");
|
|
28275
29975
|
if (startInFlight && !await settlesWithin(startInFlight, containerStartWait.ms)) {
|
|
@@ -28283,16 +29983,16 @@ async function localInvokeAgentCoreCommand(target, options) {
|
|
|
28283
29983
|
if (containerId) try {
|
|
28284
29984
|
await removeContainer(containerId);
|
|
28285
29985
|
} catch (err) {
|
|
28286
|
-
getLogger().debug(`removeContainer(${containerId}) failed: ${
|
|
29986
|
+
getLogger().debug(`removeContainer(${containerId}) failed: ${describeAwsFailure(err).detail}`);
|
|
28287
29987
|
}
|
|
28288
29988
|
if (profileCredsFile) try {
|
|
28289
29989
|
await profileCredsFile.dispose();
|
|
28290
29990
|
} catch (err) {
|
|
28291
|
-
getLogger().debug(`Failed to remove profile credentials tmpdir ${profileCredsFile.hostPath}: ${
|
|
29991
|
+
getLogger().debug(`Failed to remove profile credentials tmpdir ${profileCredsFile.hostPath}: ${describeAwsFailure(err).detail}`);
|
|
28292
29992
|
}
|
|
28293
29993
|
await flushStdio();
|
|
28294
29994
|
}, (err) => {
|
|
28295
|
-
getLogger().debug(`cleanup failed: ${
|
|
29995
|
+
getLogger().debug(`cleanup failed: ${describeAwsFailure(err).detail}`);
|
|
28296
29996
|
});
|
|
28297
29997
|
try {
|
|
28298
29998
|
if (typeof options.assumeRole === "string") explicitRoleArnOrThrow("--assume-role", options.assumeRole, (message) => new CdkdError(message, "LOCAL_INVOKE_AGENTCORE_ASSUME_ROLE_INVALID"));
|
|
@@ -29102,7 +30802,7 @@ async function readEvent$1(options) {
|
|
|
29102
30802
|
try {
|
|
29103
30803
|
raw = readFileSync(options.event, "utf-8");
|
|
29104
30804
|
} catch (err) {
|
|
29105
|
-
throw new CdkdError(`Failed to read --event file '${options.event}': ${
|
|
30805
|
+
throw new CdkdError(`Failed to read --event file '${options.event}': ${describeAwsFailure(err).detail}`, "LOCAL_INVOKE_AGENTCORE_EVENT_READ_FAILED");
|
|
29106
30806
|
}
|
|
29107
30807
|
return parseEvent$1(raw, options.event);
|
|
29108
30808
|
}
|
|
@@ -29112,7 +30812,8 @@ function parseEvent$1(raw, source) {
|
|
|
29112
30812
|
try {
|
|
29113
30813
|
return JSON.parse(raw);
|
|
29114
30814
|
} catch (err) {
|
|
29115
|
-
|
|
30815
|
+
getLogger().debug(safeMsg`Event payload failed to parse: ${parseFailureDebugDetail(err)}`);
|
|
30816
|
+
throw new CdkdError(`Failed to parse event payload from ${source} as JSON: ${describeJsonParseFailure(err, utf8ByteLength(raw))}`, "LOCAL_INVOKE_AGENTCORE_EVENT_PARSE_FAILED");
|
|
29116
30817
|
}
|
|
29117
30818
|
}
|
|
29118
30819
|
async function readStdin$1() {
|
|
@@ -29149,13 +30850,14 @@ function readEnvOverridesFile$1(filePath) {
|
|
|
29149
30850
|
try {
|
|
29150
30851
|
raw = readFileSync(filePath, "utf-8");
|
|
29151
30852
|
} catch (err) {
|
|
29152
|
-
throw new CdkdError(`Failed to read --env-vars file '${filePath}': ${
|
|
30853
|
+
throw new CdkdError(`Failed to read --env-vars file '${filePath}': ${describeAwsFailure(err).detail}`, "LOCAL_INVOKE_AGENTCORE_ENV_VARS_READ_FAILED");
|
|
29153
30854
|
}
|
|
29154
30855
|
let parsed;
|
|
29155
30856
|
try {
|
|
29156
30857
|
parsed = JSON.parse(raw);
|
|
29157
30858
|
} catch (err) {
|
|
29158
|
-
|
|
30859
|
+
getLogger().debug(safeMsg`--env-vars file failed to parse: ${parseFailureDebugDetail(err)}`);
|
|
30860
|
+
throw new CdkdError(`Failed to parse --env-vars file '${filePath}' as JSON: ${describeJsonParseFailure(err, utf8ByteLength(raw))}`, "LOCAL_INVOKE_AGENTCORE_ENV_VARS_PARSE_FAILED");
|
|
29159
30861
|
}
|
|
29160
30862
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CdkdError(`--env-vars file '${filePath}' must contain a JSON object at the top level.`, "LOCAL_INVOKE_AGENTCORE_ENV_VARS_NOT_OBJECT");
|
|
29161
30863
|
return parsed;
|
|
@@ -29267,14 +30969,14 @@ async function localInvokeCommand(target, options) {
|
|
|
29267
30969
|
const drainLogs = singleFlight(async () => {
|
|
29268
30970
|
await killAndDrainContainerLogs(containerId, logStream);
|
|
29269
30971
|
}, (err) => {
|
|
29270
|
-
getLogger().debug(safeMsg`container log drain failed: ${
|
|
30972
|
+
getLogger().debug(safeMsg`container log drain failed: ${describeAwsFailure(err).detail}`);
|
|
29271
30973
|
});
|
|
29272
30974
|
const cleanup = singleFlight(async () => {
|
|
29273
30975
|
await drainLogs();
|
|
29274
30976
|
if (containerId) try {
|
|
29275
30977
|
await removeContainer(containerId);
|
|
29276
30978
|
} catch (err) {
|
|
29277
|
-
getLogger().debug(`removeContainer(${containerId}) failed: ${
|
|
30979
|
+
getLogger().debug(`removeContainer(${containerId}) failed: ${describeAwsFailure(err).detail}`);
|
|
29278
30980
|
}
|
|
29279
30981
|
if (imagePlan?.inlineTmpDir) try {
|
|
29280
30982
|
rmSync(imagePlan.inlineTmpDir, {
|
|
@@ -29282,7 +30984,7 @@ async function localInvokeCommand(target, options) {
|
|
|
29282
30984
|
force: true
|
|
29283
30985
|
});
|
|
29284
30986
|
} catch (err) {
|
|
29285
|
-
getLogger().debug(`Failed to remove inline-code tmpdir ${imagePlan.inlineTmpDir}: ${
|
|
30987
|
+
getLogger().debug(`Failed to remove inline-code tmpdir ${imagePlan.inlineTmpDir}: ${describeAwsFailure(err).detail}`);
|
|
29286
30988
|
}
|
|
29287
30989
|
if (imagePlan?.layersTmpDir) try {
|
|
29288
30990
|
rmSync(imagePlan.layersTmpDir, {
|
|
@@ -29290,7 +30992,7 @@ async function localInvokeCommand(target, options) {
|
|
|
29290
30992
|
force: true
|
|
29291
30993
|
});
|
|
29292
30994
|
} catch (err) {
|
|
29293
|
-
getLogger().debug(`Failed to remove merged-layers tmpdir ${imagePlan.layersTmpDir}: ${
|
|
30995
|
+
getLogger().debug(`Failed to remove merged-layers tmpdir ${imagePlan.layersTmpDir}: ${describeAwsFailure(err).detail}`);
|
|
29294
30996
|
}
|
|
29295
30997
|
if (imagePlan?.layerArnTmpDirs) for (const dir of imagePlan.layerArnTmpDirs) try {
|
|
29296
30998
|
rmSync(dir, {
|
|
@@ -29298,16 +31000,16 @@ async function localInvokeCommand(target, options) {
|
|
|
29298
31000
|
force: true
|
|
29299
31001
|
});
|
|
29300
31002
|
} catch (err) {
|
|
29301
|
-
getLogger().debug(`Failed to remove ARN-layer tmpdir ${dir}: ${
|
|
31003
|
+
getLogger().debug(`Failed to remove ARN-layer tmpdir ${dir}: ${describeAwsFailure(err).detail}`);
|
|
29302
31004
|
}
|
|
29303
31005
|
if (profileCredsFile) try {
|
|
29304
31006
|
await profileCredsFile.dispose();
|
|
29305
31007
|
} catch (err) {
|
|
29306
|
-
getLogger().debug(`Failed to remove profile credentials tmpdir ${profileCredsFile.hostPath}: ${
|
|
31008
|
+
getLogger().debug(`Failed to remove profile credentials tmpdir ${profileCredsFile.hostPath}: ${describeAwsFailure(err).detail}`);
|
|
29307
31009
|
}
|
|
29308
31010
|
await flushStdio();
|
|
29309
31011
|
}, (err) => {
|
|
29310
|
-
getLogger().debug(`cleanup failed: ${
|
|
31012
|
+
getLogger().debug(`cleanup failed: ${describeAwsFailure(err).detail}`);
|
|
29311
31013
|
});
|
|
29312
31014
|
try {
|
|
29313
31015
|
if (typeof options.assumeRole === "string") explicitRoleArnOrThrow("--assume-role", options.assumeRole);
|
|
@@ -29695,13 +31397,14 @@ function readEnvOverridesFile(filePath) {
|
|
|
29695
31397
|
try {
|
|
29696
31398
|
raw = readFileSync(filePath, "utf-8");
|
|
29697
31399
|
} catch (err) {
|
|
29698
|
-
throw new Error(`Failed to read --env-vars file '${filePath}': ${
|
|
31400
|
+
throw new Error(`Failed to read --env-vars file '${filePath}': ${describeAwsFailure(err).detail}`);
|
|
29699
31401
|
}
|
|
29700
31402
|
let parsed;
|
|
29701
31403
|
try {
|
|
29702
31404
|
parsed = JSON.parse(raw);
|
|
29703
31405
|
} catch (err) {
|
|
29704
|
-
|
|
31406
|
+
getLogger().debug(safeMsg`--env-vars file failed to parse: ${parseFailureDebugDetail(err)}`);
|
|
31407
|
+
throw new Error(`Failed to parse --env-vars file '${filePath}' as JSON: ${describeJsonParseFailure(err, utf8ByteLength(raw))}`);
|
|
29705
31408
|
}
|
|
29706
31409
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`--env-vars file '${filePath}' must contain a JSON object at the top level.`);
|
|
29707
31410
|
return parsed;
|
|
@@ -29716,7 +31419,8 @@ function parseEvent(raw, source) {
|
|
|
29716
31419
|
try {
|
|
29717
31420
|
return JSON.parse(raw);
|
|
29718
31421
|
} catch (err) {
|
|
29719
|
-
|
|
31422
|
+
getLogger().debug(safeMsg`Event payload failed to parse: ${parseFailureDebugDetail(err)}`);
|
|
31423
|
+
throw new Error(`Failed to parse event payload from ${source} as JSON: ${describeJsonParseFailure(err, utf8ByteLength(raw))}`);
|
|
29720
31424
|
}
|
|
29721
31425
|
}
|
|
29722
31426
|
async function readStdin() {
|
|
@@ -29935,6 +31639,7 @@ function buildProgram() {
|
|
|
29935
31639
|
program.addCommand(createEventsCommand());
|
|
29936
31640
|
program.addCommand(createGcCommand());
|
|
29937
31641
|
program.addCommand(createOrphanCommand());
|
|
31642
|
+
program.addCommand(createRefactorCommand());
|
|
29938
31643
|
program.addCommand(createImportCommand());
|
|
29939
31644
|
program.addCommand(createPublishAssetsCommand());
|
|
29940
31645
|
program.addCommand(createForceUnlockCommand());
|
|
@@ -29960,4 +31665,4 @@ function collectVerbFlags(program) {
|
|
|
29960
31665
|
|
|
29961
31666
|
//#endregion
|
|
29962
31667
|
export { resolveSecretDerivedPrincipals as a, buildProgram, isResolvableSecretPrincipalList as i, runDestroyForStack as n, canonicalizeIpProtocolValue as o, holdsUnprovenSecretPrincipalToken as r, resolvedResourceTimeoutMs as s, canonicalizeIpv4Cidr as t };
|
|
29963
|
-
//# sourceMappingURL=program-
|
|
31668
|
+
//# sourceMappingURL=program-TEYrbsKm.js.map
|