@fjall/deploy-core 2.12.0 → 2.14.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/.minified +1 -1
- package/dist/src/aws/cloudtrail/orgTrailDelivery.d.ts +44 -0
- package/dist/src/aws/cloudtrail/orgTrailDelivery.js +1 -0
- package/dist/src/aws/index.d.ts +6 -2
- package/dist/src/aws/index.js +1 -1
- package/dist/src/aws/organisations/accountGlobals.d.ts +40 -0
- package/dist/src/aws/organisations/accountGlobals.js +1 -0
- package/dist/src/aws/organisations/accounts.d.ts +3 -1
- package/dist/src/aws/organisations/accounts.js +1 -1
- package/dist/src/aws/organisations/backup.d.ts +2 -1
- package/dist/src/aws/organisations/backup.js +2 -2
- package/dist/src/aws/organisations/importedAccounts.d.ts +16 -0
- package/dist/src/aws/organisations/importedAccounts.js +1 -0
- package/dist/src/aws/organisations/index.d.ts +3 -1
- package/dist/src/aws/organisations/index.js +1 -1
- package/dist/src/aws/organisations/organisationalUnits.d.ts +1 -1
- package/dist/src/aws/organisations/policies.js +1 -1
- package/dist/src/aws/organisations/rootAccess.d.ts +27 -0
- package/dist/src/aws/organisations/rootAccess.js +3 -0
- package/dist/src/aws/organisations/serviceAccess.d.ts +6 -0
- package/dist/src/aws/organisations/serviceAccess.js +1 -1
- package/dist/src/aws/organisations/types.d.ts +18 -0
- package/dist/src/aws/organisations/types.js +1 -1
- package/dist/src/aws/sts/assumeRoot.d.ts +46 -0
- package/dist/src/aws/sts/assumeRoot.js +1 -0
- package/dist/src/aws/targetReadiness.d.ts +70 -0
- package/dist/src/aws/targetReadiness.js +1 -0
- package/dist/src/aws/targetSetAdvisory.d.ts +24 -0
- package/dist/src/aws/targetSetAdvisory.js +1 -0
- package/dist/src/events/index.d.ts +2 -0
- package/dist/src/events/index.js +1 -1
- package/dist/src/index.d.ts +18 -14
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/accountsConfig.d.ts +11 -0
- package/dist/src/orchestration/accountsConfig.js +1 -1
- package/dist/src/orchestration/applicationDeploy.js +1 -1
- package/dist/src/orchestration/applicationDestroy.js +1 -1
- package/dist/src/orchestration/cascadeDestroyHelpers.d.ts +12 -1
- package/dist/src/orchestration/cascadeDestroyHelpers.js +1 -1
- package/dist/src/orchestration/cascadeHelpers.d.ts +36 -5
- package/dist/src/orchestration/cascadeHelpers.js +1 -1
- package/dist/src/orchestration/contextHelpers.d.ts +20 -2
- package/dist/src/orchestration/contextHelpers.js +1 -1
- package/dist/src/orchestration/index.d.ts +13 -4
- package/dist/src/orchestration/index.js +1 -1
- package/dist/src/orchestration/organisationDeploy/cascadeExecution.d.ts +28 -0
- package/dist/src/orchestration/organisationDeploy/cascadeExecution.js +1 -0
- package/dist/src/orchestration/organisationDeploy/infraSteps.d.ts +40 -0
- package/dist/src/orchestration/organisationDeploy/infraSteps.js +1 -0
- package/dist/src/orchestration/organisationDeploy/orgCascadeDeploy.d.ts +8 -0
- package/dist/src/orchestration/organisationDeploy/orgCascadeDeploy.js +5 -0
- package/dist/src/orchestration/organisationDeploy/orgContext.d.ts +12 -0
- package/dist/src/orchestration/organisationDeploy/orgContext.js +1 -0
- package/dist/src/orchestration/organisationDeploy/resolveCascadeAccounts.d.ts +15 -0
- package/dist/src/orchestration/organisationDeploy/resolveCascadeAccounts.js +1 -0
- package/dist/src/orchestration/organisationDeploy/singleComponentDeploy.d.ts +11 -0
- package/dist/src/orchestration/organisationDeploy/singleComponentDeploy.js +1 -0
- package/dist/src/orchestration/organisationDeploy/trailReconciliation.d.ts +21 -0
- package/dist/src/orchestration/organisationDeploy/trailReconciliation.js +1 -0
- package/dist/src/orchestration/organisationDeploy.d.ts +1 -5
- package/dist/src/orchestration/organisationDeploy.js +1 -5
- package/dist/src/orchestration/organisationDestroy.d.ts +1 -1
- package/dist/src/orchestration/organisationDestroy.js +2 -2
- package/dist/src/orchestration/organisationSetup.d.ts +23 -3
- package/dist/src/orchestration/organisationSetup.js +1 -1
- package/dist/src/orchestration/reconcileProviderAccounts.js +1 -1
- package/dist/src/orchestration/stackCleanup/bucketOps.d.ts +54 -0
- package/dist/src/orchestration/stackCleanup/bucketOps.js +1 -0
- package/dist/src/orchestration/stackCleanup/failedStack.d.ts +34 -0
- package/dist/src/orchestration/stackCleanup/failedStack.js +1 -0
- package/dist/src/orchestration/stackCleanup/logging.d.ts +9 -0
- package/dist/src/orchestration/stackCleanup/logging.js +1 -0
- package/dist/src/orchestration/stackCleanup/messages.d.ts +16 -0
- package/dist/src/orchestration/stackCleanup/messages.js +1 -0
- package/dist/src/orchestration/stackCleanup/orphanSweep.d.ts +25 -0
- package/dist/src/orchestration/stackCleanup/orphanSweep.js +1 -0
- package/dist/src/orchestration/stackCleanup/preEmpty.d.ts +35 -0
- package/dist/src/orchestration/stackCleanup/preEmpty.js +1 -0
- package/dist/src/orchestration/stackCleanup/stackResources.d.ts +9 -0
- package/dist/src/orchestration/stackCleanup/stackResources.js +1 -0
- package/dist/src/orchestration/stackCleanup.d.ts +13 -33
- package/dist/src/orchestration/stackCleanup.js +1 -1
- package/dist/src/orchestration/trailMigration/memberTrailCleanup.d.ts +43 -0
- package/dist/src/orchestration/trailMigration/memberTrailCleanup.js +1 -0
- package/dist/src/orchestration/trailMigration/trailMigration.d.ts +64 -0
- package/dist/src/orchestration/trailMigration/trailMigration.js +1 -0
- package/dist/src/orchestration/unlock/scpRemediation.d.ts +15 -0
- package/dist/src/orchestration/unlock/scpRemediation.js +1 -0
- package/dist/src/orchestration/unlock/unlockBucket.d.ts +37 -0
- package/dist/src/orchestration/unlock/unlockBucket.js +1 -0
- package/dist/src/orchestration/unlock/unlockQueue.d.ts +43 -0
- package/dist/src/orchestration/unlock/unlockQueue.js +1 -0
- package/dist/src/services/application/ApplicationStackService.d.ts +9 -10
- package/dist/src/services/application/ApplicationStackService.js +1 -1
- package/dist/src/services/application/applicationStackHelpers.d.ts +13 -8
- package/dist/src/services/application/applicationStackHelpers.js +3 -3
- package/dist/src/services/infrastructure/CdkArgumentBuilder.js +1 -1
- package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +1 -0
- package/dist/src/services/infrastructure/cdkServiceHelpers.js +1 -1
- package/dist/src/services/supporting/CdkContextBuilder.d.ts +1 -0
- package/dist/src/services/supporting/CdkContextBuilder.js +1 -1
- package/dist/src/steps/stepRegistry.js +1 -1
- package/dist/src/types/FjallState.d.ts +7 -0
- package/dist/src/types/FjallState.js +1 -1
- package/dist/src/types/callbackKeys.d.ts +1 -1
- package/dist/src/types/callbackKeys.js +1 -1
- package/dist/src/types/callbacks.d.ts +58 -3
- package/dist/src/types/callbacks.js +1 -0
- package/dist/src/types/deployment/DeploymentTypes.d.ts +1 -0
- package/dist/src/types/deploymentEventSchema.d.ts +28 -3
- package/dist/src/types/deploymentEventSchema.js +1 -1
- package/dist/src/types/events.d.ts +12 -0
- package/dist/src/types/events.js +1 -0
- package/dist/src/types/index.d.ts +7 -11
- package/dist/src/types/index.js +1 -1
- package/dist/src/types/orgConfig.d.ts +8 -2
- package/dist/src/types/params.d.ts +18 -0
- package/dist/src/types/patternDetection.d.ts +0 -25
- package/dist/src/types/patternDetection.js +1 -1
- package/dist/src/types/stepDefinitions.d.ts +2 -0
- package/dist/src/types/stepDefinitions.js +1 -1
- package/dist/src/util/index.d.ts +1 -0
- package/dist/src/util/index.js +1 -1
- package/dist/src/util/sleepAbortable.d.ts +8 -0
- package/dist/src/util/sleepAbortable.js +1 -0
- package/package.json +8 -4
package/dist/src/index.d.ts
CHANGED
|
@@ -11,23 +11,19 @@
|
|
|
11
11
|
* responsibility. deploy-core receives credentials and a working
|
|
12
12
|
* directory; it never reads credential files or writes to the terminal.
|
|
13
13
|
*/
|
|
14
|
-
export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURCE_CATEGORIES, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES } from "./types/index.js";
|
|
14
|
+
export { DeploymentEventSchema, DEPLOYMENT_EVENT_TYPES, DEPLOYMENT_EVENT_RESOURCE_CATEGORIES, DEPLOYMENT_EVENT_STATUS_REASON_MAX, DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX, DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX, CASCADE_PHASES, CASCADE_ACCOUNT_STATUSES } from "./types/index.js";
|
|
15
15
|
export type { DeploymentEvent, DeploymentEventType, DeploymentEventResourceCategory, DeploymentEventCascadePhase, DeploymentEventCascadeAccountStatus } from "./types/index.js";
|
|
16
|
-
export type { AwsCredentials, DeployIdentity, DeployCallbacks, StepCompleteStatus, ProgressEvent, ProgressEventType, ResourceEvent, AwsAuthResult, CascadeDeploymentResult, CascadePhase, BuildPushStartEvent, BuildPushProgressEvent, BuildPushCompleteEvent, TaskDefRegisteredEvent, ECSCompleteEvent, MigrationsStartEvent, MigrationsCompleteEvent, ApiClientInterface, EntitlementsData, DeployParams, DeployOptions, DeploymentType, DeployResult, DestroyParams, DestroyOptions, DestroyResult, OrgConfig, ProviderAccount, SSOSession, Entitlements } from "./types/index.js";
|
|
16
|
+
export type { AwsCredentials, DeployIdentity, DeployCallbacks, StepCompleteStatus, StackCleanupQuarantineDetail, StackCleanupRetainedBucketsDetail, ProgressEvent, ProgressEventType, ResourceEvent, AwsAuthResult, CascadeDeploymentResult, CascadePhase, BuildPushStartEvent, BuildPushProgressEvent, BuildPushCompleteEvent, TaskDefRegisteredEvent, ECSCompleteEvent, MigrationsStartEvent, MigrationsCompleteEvent, TrailMigrationPhase, TrailMigrationStatus, TrailMigrationPhaseEvent, ApiClientInterface, EntitlementsData, DeployParams, DeployOptions, DeploymentType, DeployResult, DestroyParams, DestroyOptions, DestroyResult, OrgConfig, ProviderAccount, RootAccessManagementMode, SSOSession, Entitlements } from "./types/index.js";
|
|
17
17
|
export type { AwsProvider, AwsProviderCredentials, AwsSdkClientConstructor } from "./aws/index.js";
|
|
18
18
|
export { SimpleAwsProvider } from "./aws/index.js";
|
|
19
|
-
export {
|
|
20
|
-
export type {
|
|
19
|
+
export { checkTargetReadiness, describeTargetReadinessReason, buildTargetUnreadyAdvisory, buildTargetSetUnreadyAdvisory } from "./aws/index.js";
|
|
20
|
+
export type { TargetReadinessAccount, TargetReadinessClients, TargetReadinessReason, TargetReadinessVerdict, TargetSetUnreadyAdvisoryParams } from "./aws/index.js";
|
|
21
|
+
export { ensureOrganisationExists, describeOrganisation, enablePolicyTypes, enableServiceAccess, SERVICE_PRINCIPALS, enableRamSharing, activateTrustedAccess, enableIpamDelegatedAdmin, updateBackupGlobalSettings, listAccounts, findAccount, createAccount, ensureOrganisationalUnitsExist, placeAccountsInOUs, buildAccountToOUMap, activateCostAllocationTags, checkIdentityCentreStatus, extractErrorName, isOULeaf, registerSecurityDelegates, SECURITY_SERVICE_PRINCIPALS, enableCentralisedRootAccess, ROOT_ACCESS_FEATURES, RootAccessEnablementError } from "./aws/index.js";
|
|
22
|
+
export type { OrgDetails, OUMap, OUTree, AccountPlacementResult, AccountInfo, IdentityCentreStatus, BackupGlobalSettings, CostAllocationTag, CreateAccountResult, RootAccessFeature, RootAccessEnablementSummary } from "./aws/index.js";
|
|
21
23
|
export { STEP_IDS, STEP_NAMES, INFRASTRUCTURE_STEP_NAMES, INFRA_STEP_NAME } from "./types/index.js";
|
|
22
24
|
export type { StepId, InfrastructureStepName, StepDefinition, StepDeploymentType, Operation, StepContext } from "./types/index.js";
|
|
23
25
|
export type { DeploymentContext, StepOutput, ApplicationDeploymentContext, CallerIdentity, StackOutput, StackOutputsRecord, ValidationResult, DetectionResult, ProgressCallbacks, ApplicationStack, OrganisationType, DeploymentOperation, ApplicationOperation, OrganisationOperation, DeployOrderOptions, ParallelDeployGroupKey, ParallelDestroyGroupKey, OpenNextPattern, AppResourceFlags, ParallelDeploymentResult, ParallelDeployGroup, ParallelOperation, ParallelOperationType, ParallelOperationState, ApplicationErrorType, StackDeploymentData, StackDeploymentOptions, ApplicationDeploymentData, ApplicationDestructionData, FjallStateFile, TemplateHashEntry } from "./types/index.js";
|
|
24
|
-
export { ProgressReporter, APPLICATION_STACKS, ORGANISATION_TYPES, APPLICATION_DEPLOY_ORDER, APPLICATION_DESTROY_ORDER, OPENNEXT_DEPLOY_ORDER, OPENNEXT_DESTROY_ORDER, PARALLEL_DEPLOY_GROUPS, PARALLEL_DESTROY_GROUPS, OPENNEXT_PARALLEL_GROUPS, PARALLEL_OPERATION_TYPES, isApplicationOperation, isOrganisationOperation, getParallelDeployGroups, getParallelDestroyGroups, getApplicationDeployOrder, getApplicationDestroyOrder, getApplicationStackName, getOrganisationStackName, isApplicationStack, getApplicationStepName, getApplicationStepId, toPascalCase, isOpenNextPattern, OPENNEXT_PATTERNS, deriveResourcesFromManifestStacks, STACK_NOT_FOUND_PATTERN, STACK_FAILED_STATE_PATTERN, CDK_NO_STACKS_MATCH, INFRASTRUCTURE_FILENAME, ApplicationError, wrapApplicationError, FjallStateFileSchema, readStateFile, writeStateFile, createEmptyState, deleteStateFile, updateTemplateHash, getStateFilePath, stubCallerIdentity } from "./types/index.js";
|
|
25
|
-
/** @deprecated Use FrameworkRegistry.resolve() instead */
|
|
26
|
-
export { detectPattern } from "./types/index.js";
|
|
27
|
-
/** @deprecated Use FrameworkRegistry.resolve() instead */
|
|
28
|
-
export { detectPayloadPattern } from "./types/index.js";
|
|
29
|
-
/** @deprecated Use FrameworkRegistry.resolve() instead */
|
|
30
|
-
export { detectDatabase } from "./types/index.js";
|
|
26
|
+
export { ProgressReporter, APPLICATION_STACKS, ORGANISATION_TYPES, APPLICATION_DEPLOY_ORDER, APPLICATION_DESTROY_ORDER, OPENNEXT_DEPLOY_ORDER, OPENNEXT_DESTROY_ORDER, PARALLEL_DEPLOY_GROUPS, PARALLEL_DESTROY_GROUPS, OPENNEXT_PARALLEL_GROUPS, PARALLEL_OPERATION_TYPES, isApplicationOperation, isOrganisationOperation, getParallelDeployGroups, getParallelDestroyGroups, getApplicationDeployOrder, getApplicationDestroyOrder, getApplicationStackName, getOrganisationStackName, isApplicationStack, isQuarantineDetail, isRetainedBucketsDetail, getApplicationStepName, getApplicationStepId, toPascalCase, isOpenNextPattern, OPENNEXT_PATTERNS, deriveResourcesFromManifestStacks, STACK_NOT_FOUND_PATTERN, STACK_FAILED_STATE_PATTERN, CDK_NO_STACKS_MATCH, INFRASTRUCTURE_FILENAME, ApplicationError, wrapApplicationError, FjallStateFileSchema, readStateFile, writeStateFile, createEmptyState, deleteStateFile, updateTemplateHash, getStateFilePath, stubCallerIdentity } from "./types/index.js";
|
|
31
27
|
export { CloudFormationEventMonitor } from "./aws/index.js";
|
|
32
28
|
export type { AwsClientLike, EventLogWriter, EventFailureAnalyser, EventLogWriterFactory, EventMonitorDeps } from "./aws/index.js";
|
|
33
29
|
export { CdkService, CdkArgumentBuilder, CdkProcessManager, CdkEventMonitor, startStackMonitoring, DEFAULT_DEPLOY_TIMEOUT_MS, isCdkError, formatInfrastructureError, getStructuralHint, getSourceContext, hasCdkDifferences, parseDiffOutput, CloudFormationService, CloudFormationError, EcsService, EcsError, EcsServiceResolver, type CfnExportsClient, TemplateHashService, TemplateHashError, type TemplateComparisonResult, CdkContextBuilder, emitProgress, PROGRESS_MESSAGES, parseBuildPhase, buildStepContextBuildConfig, convertCloudFormationOutputsToRecord, type CloudFormationOutput, ApplicationStackService } from "./services/index.js";
|
|
@@ -43,16 +39,24 @@ export { type Result, success, failure, isSuccess, isFailure } from "@fjall/gene
|
|
|
43
39
|
export { deploy } from "./orchestration/index.js";
|
|
44
40
|
export { destroy } from "./orchestration/index.js";
|
|
45
41
|
export { partitionAccounts } from "./orchestration/index.js";
|
|
46
|
-
export { buildRegionList, buildAccountRegionPairs } from "./orchestration/index.js";
|
|
42
|
+
export { buildRegionList, buildAccountRegionPairs, cascadeHomeRegion, cascadeOperationKey } from "./orchestration/index.js";
|
|
47
43
|
export type { AccountRegionPair } from "./orchestration/index.js";
|
|
48
44
|
export { projectScalarSummary, projectAccountRows } from "./orchestration/index.js";
|
|
49
45
|
export type { CascadeOutcomeResult, CascadeMemberOutcome, CascadePlatformOutcome, CascadeLedger, CascadeAccountRow, CascadePlatformRow, CascadeAccountProjection } from "./orchestration/index.js";
|
|
50
46
|
export { reconcileProviderAccounts, mergeReconciledProviderAccounts } from "./orchestration/index.js";
|
|
51
47
|
export type { ReconcileResult } from "./orchestration/index.js";
|
|
52
|
-
export {
|
|
48
|
+
export { decideNextTransition, reconcileTrailMigration, decommissionMemberTrailStorage, ORG_TRAIL_BUCKET_OUTPUT_KEY, TRAIL_BUCKET_OUTPUT_KEY, TRAIL_KEY_ARN_OUTPUT_KEY } from "./orchestration/index.js";
|
|
49
|
+
export type { MemberTrailFacts, TrailMigrationTransition, TrailMigrationOutcome, DecommissionClients, DecommissionInput, DecommissionOutcome } from "./orchestration/index.js";
|
|
50
|
+
export { unlockBucket, unlockQueue } from "./orchestration/index.js";
|
|
51
|
+
export type { UnlockBucketInput, UnlockBucketReport, UnlockQueueInput, UnlockQueueReport } from "./orchestration/index.js";
|
|
52
|
+
export { verifyOrgTrailDelivery } from "./aws/cloudtrail/orgTrailDelivery.js";
|
|
53
|
+
export type { OrgTrailDeliveryReport, OrgTrailAccountDelivery } from "./aws/cloudtrail/orgTrailDelivery.js";
|
|
54
|
+
export { assumeRootForTask, isRootTaskPolicyArn, ROOT_TASK_POLICY_ARNS, MAX_ROOT_SESSION_SECONDS } from "./aws/sts/assumeRoot.js";
|
|
55
|
+
export type { RootTaskPolicyArn, AssumeRootOptions, RootTaskSession } from "./aws/sts/assumeRoot.js";
|
|
56
|
+
export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig, isOuOnlyAccountBucket, OU_ONLY_ACCOUNT_BUCKETS } from "./orchestration/index.js";
|
|
53
57
|
export type { AccountsConfig } from "./orchestration/index.js";
|
|
54
58
|
export { runOpenNextBuild } from "./orchestration/index.js";
|
|
55
|
-
export { runOrganisationSetup } from "./orchestration/index.js";
|
|
59
|
+
export { runOrganisationSetup, ORG_SETUP_PHASES } from "./orchestration/index.js";
|
|
56
60
|
export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult, DockerProvider, DockerProgressCallback, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams, DomainDeployProvider, DomainConfig, DomainDeployResult, DeployServices } from "./orchestration/index.js";
|
|
57
61
|
export type { FrameworkBuilder, FrameworkDetection, BuildPlan, BuildCommand, BuildCallbacks, DetectionContext, BuildOptions } from "./types/index.js";
|
|
58
62
|
export { FrameworkRegistry, type ResolvedBuilder } from "./orchestration/index.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as a,CASCADE_PHASES as
|
|
1
|
+
import{DeploymentEventSchema as t,DEPLOYMENT_EVENT_TYPES as o,DEPLOYMENT_EVENT_RESOURCE_CATEGORIES as i,DEPLOYMENT_EVENT_STATUS_REASON_MAX as a,DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX as s,DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX as E,CASCADE_PHASES as n,CASCADE_ACCOUNT_STATUSES as c}from"./types/index.js";import{SimpleAwsProvider as A}from"./aws/index.js";import{checkTargetReadiness as T,describeTargetReadinessReason as p,buildTargetUnreadyAdvisory as _,buildTargetSetUnreadyAdvisory as O}from"./aws/index.js";import{ensureOrganisationExists as R,describeOrganisation as m,enablePolicyTypes as d,enableServiceAccess as P,SERVICE_PRINCIPALS as C,enableRamSharing as N,activateTrustedAccess as f,enableIpamDelegatedAdmin as g,updateBackupGlobalSettings as x,listAccounts as D,findAccount as I,createAccount as L,ensureOrganisationalUnitsExist as U,placeAccountsInOUs as k,buildAccountToOUMap as y,activateCostAllocationTags as v,checkIdentityCentreStatus as M,extractErrorName as F,isOULeaf as Y,registerSecurityDelegates as b,SECURITY_SERVICE_PRINCIPALS as B,enableCentralisedRootAccess as h,ROOT_ACCESS_FEATURES as K,RootAccessEnablementError as G}from"./aws/index.js";import{STEP_IDS as H,STEP_NAMES as X,INFRASTRUCTURE_STEP_NAMES as w,INFRA_STEP_NAME as j}from"./types/index.js";import{ProgressReporter as q,APPLICATION_STACKS as z,ORGANISATION_TYPES as J,APPLICATION_DEPLOY_ORDER as W,APPLICATION_DESTROY_ORDER as Z,OPENNEXT_DEPLOY_ORDER as $,OPENNEXT_DESTROY_ORDER as ee,PARALLEL_DEPLOY_GROUPS as re,PARALLEL_DESTROY_GROUPS as te,OPENNEXT_PARALLEL_GROUPS as oe,PARALLEL_OPERATION_TYPES as ie,isApplicationOperation as ae,isOrganisationOperation as se,getParallelDeployGroups as Ee,getParallelDestroyGroups as ne,getApplicationDeployOrder as ce,getApplicationDestroyOrder as Se,getApplicationStackName as Ae,getOrganisationStackName as le,isApplicationStack as Te,isQuarantineDetail as pe,isRetainedBucketsDetail as _e,getApplicationStepName as Oe,getApplicationStepId as ue,toPascalCase as Re,isOpenNextPattern as me,OPENNEXT_PATTERNS as de,deriveResourcesFromManifestStacks as Pe,STACK_NOT_FOUND_PATTERN as Ce,STACK_FAILED_STATE_PATTERN as Ne,CDK_NO_STACKS_MATCH as fe,INFRASTRUCTURE_FILENAME as ge,ApplicationError as xe,wrapApplicationError as De,FjallStateFileSchema as Ie,readStateFile as Le,writeStateFile as Ue,createEmptyState as ke,deleteStateFile as ye,updateTemplateHash as ve,getStateFilePath as Me,stubCallerIdentity as Fe}from"./types/index.js";import{CloudFormationEventMonitor as be}from"./aws/index.js";import{CdkService as he,CdkArgumentBuilder as Ke,CdkProcessManager as Ge,CdkEventMonitor as Ve,startStackMonitoring as He,DEFAULT_DEPLOY_TIMEOUT_MS as Xe,isCdkError as we,formatInfrastructureError as je,getStructuralHint as Qe,getSourceContext as qe,hasCdkDifferences as ze,parseDiffOutput as Je,CloudFormationService as We,CloudFormationError as Ze,EcsService as $e,EcsError as er,EcsServiceResolver as rr,TemplateHashService as tr,TemplateHashError as or,CdkContextBuilder as ir,emitProgress as ar,PROGRESS_MESSAGES as sr,parseBuildPhase as Er,buildStepContextBuildConfig as nr,convertCloudFormationOutputsToRecord as cr,ApplicationStackService as Sr}from"./services/index.js";import{CdkError as lr}from"./types/errors/index.js";import{BaseServiceError as pr,ValidationError as _r,AuthError as Or,AwsError as ur,DeploymentError as Rr,NetworkError as mr,FileSystemError as dr,ConfigError as Pr,toServiceError as Cr}from"./types/errors/index.js";import{filterDangerousEnvVars as fr,maskSensitiveOutput as gr,parseShellArgs as xr,sleep as Dr}from"@fjall/util";import{hasDockerfile as Lr}from"./util/dockerfileDetection.js";import{createSequencedCallbacks as kr}from"./util/sequencedCallbacks.js";import{fileExists as vr}from"@fjall/util/fsHelpers";import{success as Fr,failure as Yr,isSuccess as br,isFailure as Br}from"@fjall/generator";import{deploy as Kr}from"./orchestration/index.js";import{destroy as Vr}from"./orchestration/index.js";import{partitionAccounts as Xr}from"./orchestration/index.js";import{buildRegionList as jr,buildAccountRegionPairs as Qr,cascadeHomeRegion as qr,cascadeOperationKey as zr}from"./orchestration/index.js";import{projectScalarSummary as Wr,projectAccountRows as Zr}from"./orchestration/index.js";import{reconcileProviderAccounts as et,mergeReconciledProviderAccounts as rt}from"./orchestration/index.js";import{decideNextTransition as ot,reconcileTrailMigration as it,decommissionMemberTrailStorage as at,ORG_TRAIL_BUCKET_OUTPUT_KEY as st,TRAIL_BUCKET_OUTPUT_KEY as Et,TRAIL_KEY_ARN_OUTPUT_KEY as nt}from"./orchestration/index.js";import{unlockBucket as St,unlockQueue as At}from"./orchestration/index.js";import{verifyOrgTrailDelivery as Tt}from"./aws/cloudtrail/orgTrailDelivery.js";import{assumeRootForTask as _t,isRootTaskPolicyArn as Ot,ROOT_TASK_POLICY_ARNS as ut,MAX_ROOT_SESSION_SECONDS as Rt}from"./aws/sts/assumeRoot.js";import{parseAccountsConfiguration as dt,flattenAccountsToEnvironments as Pt,extractAllAccountNames as Ct,accountsConfigToOUTree as Nt,isStringArray as ft,isAccountsConfig as gt,isOuOnlyAccountBucket as xt,OU_ONLY_ACCOUNT_BUCKETS as Dt}from"./orchestration/index.js";import{runOpenNextBuild as Lt}from"./orchestration/index.js";import{runOrganisationSetup as kt,ORG_SETUP_PHASES as yt}from"./orchestration/index.js";import{FrameworkRegistry as Mt}from"./orchestration/index.js";import{openNextBuilder as Yt,dockerBuilder as bt}from"./orchestration/index.js";import{StepRegistry as ht,getDestroyStepId as Kt}from"./steps/index.js";export{W as APPLICATION_DEPLOY_ORDER,Z as APPLICATION_DESTROY_ORDER,z as APPLICATION_STACKS,xe as ApplicationError,Sr as ApplicationStackService,Or as AuthError,ur as AwsError,pr as BaseServiceError,c as CASCADE_ACCOUNT_STATUSES,n as CASCADE_PHASES,fe as CDK_NO_STACKS_MATCH,Ke as CdkArgumentBuilder,ir as CdkContextBuilder,lr as CdkError,Ve as CdkEventMonitor,Ge as CdkProcessManager,he as CdkService,Ze as CloudFormationError,be as CloudFormationEventMonitor,We as CloudFormationService,Pr as ConfigError,Xe as DEFAULT_DEPLOY_TIMEOUT_MS,s as DEPLOYMENT_EVENT_ERROR_MESSAGE_MAX,i as DEPLOYMENT_EVENT_RESOURCE_CATEGORIES,a as DEPLOYMENT_EVENT_STATUS_REASON_MAX,E as DEPLOYMENT_EVENT_TRAIL_DETAIL_MAX,o as DEPLOYMENT_EVENT_TYPES,Rr as DeploymentError,t as DeploymentEventSchema,er as EcsError,$e as EcsService,rr as EcsServiceResolver,dr as FileSystemError,Ie as FjallStateFileSchema,Mt as FrameworkRegistry,ge as INFRASTRUCTURE_FILENAME,w as INFRASTRUCTURE_STEP_NAMES,j as INFRA_STEP_NAME,Rt as MAX_ROOT_SESSION_SECONDS,mr as NetworkError,$ as OPENNEXT_DEPLOY_ORDER,ee as OPENNEXT_DESTROY_ORDER,oe as OPENNEXT_PARALLEL_GROUPS,de as OPENNEXT_PATTERNS,J as ORGANISATION_TYPES,yt as ORG_SETUP_PHASES,st as ORG_TRAIL_BUCKET_OUTPUT_KEY,Dt as OU_ONLY_ACCOUNT_BUCKETS,re as PARALLEL_DEPLOY_GROUPS,te as PARALLEL_DESTROY_GROUPS,ie as PARALLEL_OPERATION_TYPES,sr as PROGRESS_MESSAGES,q as ProgressReporter,K as ROOT_ACCESS_FEATURES,ut as ROOT_TASK_POLICY_ARNS,G as RootAccessEnablementError,B as SECURITY_SERVICE_PRINCIPALS,C as SERVICE_PRINCIPALS,Ne as STACK_FAILED_STATE_PATTERN,Ce as STACK_NOT_FOUND_PATTERN,H as STEP_IDS,X as STEP_NAMES,A as SimpleAwsProvider,ht as StepRegistry,Et as TRAIL_BUCKET_OUTPUT_KEY,nt as TRAIL_KEY_ARN_OUTPUT_KEY,or as TemplateHashError,tr as TemplateHashService,_r as ValidationError,Nt as accountsConfigToOUTree,v as activateCostAllocationTags,f as activateTrustedAccess,_t as assumeRootForTask,Qr as buildAccountRegionPairs,y as buildAccountToOUMap,jr as buildRegionList,nr as buildStepContextBuildConfig,O as buildTargetSetUnreadyAdvisory,_ as buildTargetUnreadyAdvisory,qr as cascadeHomeRegion,zr as cascadeOperationKey,M as checkIdentityCentreStatus,T as checkTargetReadiness,cr as convertCloudFormationOutputsToRecord,L as createAccount,ke as createEmptyState,kr as createSequencedCallbacks,ot as decideNextTransition,at as decommissionMemberTrailStorage,ye as deleteStateFile,Kr as deploy,Pe as deriveResourcesFromManifestStacks,m as describeOrganisation,p as describeTargetReadinessReason,Vr as destroy,bt as dockerBuilder,ar as emitProgress,h as enableCentralisedRootAccess,g as enableIpamDelegatedAdmin,d as enablePolicyTypes,N as enableRamSharing,P as enableServiceAccess,R as ensureOrganisationExists,U as ensureOrganisationalUnitsExist,Ct as extractAllAccountNames,F as extractErrorName,Yr as failure,vr as fileExists,fr as filterDangerousEnvVars,I as findAccount,Pt as flattenAccountsToEnvironments,je as formatInfrastructureError,ce as getApplicationDeployOrder,Se as getApplicationDestroyOrder,Ae as getApplicationStackName,ue as getApplicationStepId,Oe as getApplicationStepName,Kt as getDestroyStepId,le as getOrganisationStackName,Ee as getParallelDeployGroups,ne as getParallelDestroyGroups,qe as getSourceContext,Me as getStateFilePath,Qe as getStructuralHint,ze as hasCdkDifferences,Lr as hasDockerfile,gt as isAccountsConfig,ae as isApplicationOperation,Te as isApplicationStack,we as isCdkError,Br as isFailure,Y as isOULeaf,me as isOpenNextPattern,se as isOrganisationOperation,xt as isOuOnlyAccountBucket,pe as isQuarantineDetail,_e as isRetainedBucketsDetail,Ot as isRootTaskPolicyArn,ft as isStringArray,br as isSuccess,D as listAccounts,gr as maskSensitiveOutput,rt as mergeReconciledProviderAccounts,Yt as openNextBuilder,dt as parseAccountsConfiguration,Er as parseBuildPhase,Je as parseDiffOutput,xr as parseShellArgs,Xr as partitionAccounts,k as placeAccountsInOUs,Zr as projectAccountRows,Wr as projectScalarSummary,Le as readStateFile,et as reconcileProviderAccounts,it as reconcileTrailMigration,b as registerSecurityDelegates,Lt as runOpenNextBuild,kt as runOrganisationSetup,Dr as sleep,He as startStackMonitoring,Fe as stubCallerIdentity,Fr as success,Re as toPascalCase,Cr as toServiceError,St as unlockBucket,At as unlockQueue,x as updateBackupGlobalSettings,ve as updateTemplateHash,Tt as verifyOrgTrailDelivery,De as wrapApplicationError,Ue as writeStateFile};
|
|
@@ -4,6 +4,17 @@ import type { OUTree } from "../aws/organisations/types.js";
|
|
|
4
4
|
export type AccountsConfig = {
|
|
5
5
|
readonly [key: string]: string | readonly string[] | AccountsConfig;
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* Scaffold buckets that are OU structure ONLY — accounts placed here get an
|
|
9
|
+
* organisational unit but no workload stage (ADR
|
|
10
|
+
* 2026-04-07-nested-ou-accounts-structure: environments resolve solely from
|
|
11
|
+
* the five stage leaf keys). Producers mapping leaf keys to wire
|
|
12
|
+
* `environment` values must decode these buckets to a null stage rather than
|
|
13
|
+
* forwarding the key verbatim — the org-config ingress enum rejects them.
|
|
14
|
+
*/
|
|
15
|
+
export declare const OU_ONLY_ACCOUNT_BUCKETS: readonly ["security", "suspended"];
|
|
16
|
+
/** Type guard: checks whether an ACCOUNTS leaf key is an OU-only bucket. */
|
|
17
|
+
export declare function isOuOnlyAccountBucket(key: string): boolean;
|
|
7
18
|
export declare function isStringArray(value: unknown): value is string[];
|
|
8
19
|
export declare function isAccountsConfig(value: unknown): value is AccountsConfig;
|
|
9
20
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{success as f,failure as c}from"@fjall/generator";import{maskSensitiveOutput as C,getErrorMessage as m}from"@fjall/util";import{ConfigError as u}from"../types/errors/ServiceError.js";const a="infrastructure.ts",O=["security","suspended"],g=new Set(O);function x(n){return g.has(n)}function T(n){return Array.isArray(n)&&n.every(t=>typeof t=="string")}function p(n){return typeof n!="object"||n===null?!1:Object.entries(n).every(([t,e])=>typeof t=="string"&&(typeof e=="string"||T(e)||p(e)))}function U(n){const t=[];for(const[e,r]of Object.entries(n))if(typeof r=="string")t.push({accountName:r,environment:e});else if(Array.isArray(r))for(const o of r)t.push({accountName:o,environment:e});else typeof r=="object"&&r!==null&&t.push(...U(r));return t}function N(n){const t=[];for(const e of Object.values(n))typeof e=="string"?t.push(e):Array.isArray(e)?t.push(...e):typeof e=="object"&&e!==null&&t.push(...N(e));return t}function d(n){const t={};for(const[e,r]of Object.entries(n))typeof r=="string"?t[e]=[r]:Array.isArray(r)?t[e]=[...r]:typeof r=="object"&&r!==null&&(t[e]=d(r));return t}async function E(n){try{const t=await import("path"),e=await import("fs"),{pathToFileURL:r}=await import("url"),o=t.join(n,"fjall","organisation",a);if(!e.existsSync(o))return f(null);const{tsImport:l}=await import("tsx/esm/api"),y=r(o).href,s=await l(o,y),A=s.default,i=s.ACCOUNTS||A?.ACCOUNTS;return i?p(i)?f(i):c(new u("ACCOUNTS configuration has invalid structure","ACCOUNTS",o,{providedValue:i})):c(new u(`ACCOUNTS constant not found in organisation/${a}`,"ACCOUNTS",o,{moduleKeys:Object.keys(s),hasDefault:s.default!==void 0}))}catch(t){const e=C(m(t));return c(new u(`Failed to import ACCOUNTS from organisation/${a}: ${e}`,"ACCOUNTS",void 0,{originalError:t}))}}export{O as OU_ONLY_ACCOUNT_BUCKETS,d as accountsConfigToOUTree,N as extractAllAccountNames,U as flattenAccountsToEnvironments,p as isAccountsConfig,x as isOuOnlyAccountBucket,T as isStringArray,E as parseAccountsConfiguration};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as $}from"path";import{CloudFormationClient as q}from"@aws-sdk/client-cloudformation";import{EC2Client as z}from"@aws-sdk/client-ec2";import{success as U,failure as m}from"@fjall/generator";import{logger as E}from"@fjall/util/logger";import{maskSensitiveOutput as S,toPascalCase as Y}from"@fjall/util";import{parseDockerServicesFromManifest as J}from"@fjall/util/manifest";import{stubCallerIdentity as Q}from"../types/deployment/index.js";import{getApplicationDeployOrder as V,getApplicationStackName as X,getApplicationStepName as F,getApplicationStepId as H,APPLICATION_STACKS as j}from"../types/operations.js";import{CdkContextBuilder as Z}from"../services/supporting/CdkContextBuilder.js";import{buildParamsContext as ee,bootstrapOrFail as te}from"./contextHelpers.js";import{runDetectionPipeline as oe}from"./detectionPipeline.js";import{STEP_IDS as O,STEP_NAMES as re}from"../types/stepDefinitions.js";import{StepRegistry as ne}from"../steps/stepRegistry.js";import{checkTargetReadiness as ae}from"../aws/targetReadiness.js";import{cascadeHomeRegion as ie}from"./cascadeHelpers.js";import{withStepLifecycle as se}from"./stepLifecycle.js";import{DOCKER_BUILD_STEP_NAME as G,runDockerBuild as ce}from"./dockerBuildHelper.js";import{getParallelPhase2Stacks as de,deployParallelPhase as le,deployStackSequential as pe,runDockerPreCompute as ue,createBuildCallbacks as fe}from"./applicationDeployHelpers.js";import{deployCodeOnly as ge}from"./codeOnlyDeploy.js";const R="applicationDeploy";function me(i){const a=Object.entries(i);if(a.length===0)return;const r={};for(const[t,s]of a){const b=`${Y(t)}ImageTag`;r[b]=s}return r}async function Me(i,a,r){const{callbacks:t,options:s}=i,b=Date.now(),D=Z.buildDeploymentContext({deployType:"application",target:r.appName,path:r.path,region:a.awsProvider.getRegion(),callerIdentity:Q(a.awsProvider.getAccountId()),...ee({orgConfig:i.orgConfig,identity:i.identity,skipOidc:i.options?.skipOidc})},{verbose:s?.verbose,infraOnly:s?.infraOnly},i.orgConfig),p=a.frameworkRegistry.resolve({appPath:r.path});let w;const k=!!s?.imageTag;if(p&&!k){w=p.builder.plan({appPath:r.path},p.detection);const e=fe(t),o=await p.builder.build(r.path,w,e,{skipBuild:s?.skipBuild,infraOnly:s?.infraOnly});if(!o.success){const n=new Error(S(o.error.message));return t.onError?.(n),m(n)}}if(s?.deployOnly||k){t.onLog?.(k?`Rollback mode \u2014 rolling to image tag ${s?.imageTag}`:"Deploy-only mode \u2014 skipping infrastructure pipeline","info");const e=$(r.path,"cdk.out"),o=J(e),n=o.length>0,l=p?.detection.hasDockerfile===!0,d=n||l;E.debug(R,"Deploy-only branch entered",{isRollback:k,imageTag:s?.imageTag,appName:r.appName,appPath:r.path,cdkOutPath:e,dockerProviderAvailable:i.dockerProvider!==void 0,builderName:p?.builder.name,hasDockerfileFromManifest:n,hasDockerfileFromDisk:l,hasDockerfile:d,manifestDockerServiceCount:o.length,manifestDockerPaths:o.map(u=>u.docker.path)}),!n&&!l&&t.onLog?.("No Dockerfile detected via manifest or appPath \u2014 skipping Docker build. If this app uses a cross-repo Dockerfile, ensure a full deploy has run first to populate cdk.out/fjall-manifest.json.","warn");let f={};if(!k&&i.dockerProvider!==void 0&&d){E.debug(R,"Running Docker build before code-only deploy",{source:n?"manifest":"disk"});const u=await ce(i,a,r,t);if(!u.success)return m(u.error);f=u.data}else E.debug(R,"Skipping Docker build",{reason:k?"rollback":i.dockerProvider===void 0?"no dockerProvider":"no Dockerfile detected"});return ge(i,a,r,f)}t.onLog?.("Analysing infrastructure\u2026","info");const T=await oe(r,a,D,t);if(!T.success){const e=new Error(S(T.error.message));return t.onError?.(e),m(e)}const c=T.data;try{await t.onDetectionComplete?.({...c,builderName:p?.builder.name??"unknown"})}catch(e){const o=e instanceof Error?e.message:String(e),n=new Error(S(o));return t.onError?.(n),m(n)}const K={deploymentType:"application",operation:"deploy",deployOnly:!1,infraOnly:s?.infraOnly??!1,hasDockerfile:c.hasDockerfile,pattern:c.pattern,resources:c.resources,...p&&{builderName:p.builder.name}},N=ne.getSteps(K),A=N.findIndex(e=>e.id===O.TARGET_READINESS),I=await se(t,{stepId:O.TARGET_READINESS,stepName:re.TARGET_READINESS,...A>=0&&{stepIndex:A,totalSteps:N.length}},async()=>{if(s?.skipReadinessCheck)return t.onLog?.("Skipping target readiness check (--skip-readiness-check)","warn"),{kind:"skipped",data:void 0};const e=a.awsProvider.getAccountId(),o=i.orgConfig?.providerAccounts.find(f=>f.id===e)?.name,n=a.awsProvider.getCredentials(),l=ie(i.orgConfig),d=await ae({cloudFormation:a.awsProvider.getClient(q),ec2:new z({region:l,...n!==void 0&&{credentials:n}})},{id:e,...o!==void 0&&{name:o}},a.awsProvider.getRegion(),i.orgConfig,i.abortSignal);return d.success?d.data.ready?{kind:"completed",data:void 0}:{kind:"error",error:new Error(S(d.data.advisory))}:{kind:"error",error:new Error(S(d.error.message))}});if(!I.success)return I;const g=w?w.deployOrder:V({pattern:c.pattern,resources:c.resources}),h=g.length;if(!c.hasDifferences&&!s?.force){t.onLog?.("No infrastructure changes detected","info");const e=c.hasDockerfile&&i.dockerProvider!==void 0&&g.includes(j.COMPUTE);for(let n=0;n<g.length;n++){const l=g[n];e&&l===j.COMPUTE&&(t.onStepStart?.(O.DOCKER_OPERATIONS,G),t.onStepComplete?.(O.DOCKER_OPERATIONS,G,"skipped"));const d=H(l,"deploy"),f=F(l,"deploy");t.onStepStart?.(d,f,n,h),t.onStepComplete?.(d,f,"skipped",n,h)}const o=await a.stackService.resolveWebsiteUrl(r.appName);return U({target:r.appName,deploymentType:"application",outputs:o?{websiteUrl:o}:void 0,noChanges:!0,durationMs:Date.now()-b})}const v=await te(a,D,t);if(!v.success)return v;const C={},P=new Map;for(let e=0;e<g.length;e++){const o=g[e],n=X(r.appName,o),l=H(o,"deploy"),d=F(o,"deploy");if(!(c.stackChanges.get(n)??!0)&&!s?.force){t.onStepStart?.(l,d,e,h),t.onLog?.(`Skipping ${o} \u2014 no changes detected`,"info"),t.onStepComplete?.(l,d,"skipped",e,h);continue}const u=de(g,e,c.stackChanges,r.appName,s?.force);if(u.length>=2){const B=await le(u,r,a,D,t,e,h,c,C,P);if(!B.success)return m(B.error);e+=u.length-1;continue}const y=await ue(o,i,a,r,t,c.hasDockerfile);if(y!==null&&!y.success)return m(y.error);const W=y!==null&&y.success?y.data.contentHashTagsByService:{},L=me(W),x=await pe(o,a,D,t,e,h,C,L!==void 0?{parameters:L}:void 0);if(!x.success)return m(x.error);const M=c.currentHashes.get(n);M&&P.set(n,M)}if(P.size>0){const e=await a.hashService.updateStateAfterDeploy(r.path,P);if(!e.success){const o=S(e.error.message);E.debug(R,"Failed to update state file",{error:o}),t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy unchanged stacks: ${o}`,"warn")}}const _=await a.stackService.resolveWebsiteUrl(r.appName);return _&&(C.websiteUrl=_),U({target:r.appName,deploymentType:"application",outputs:Object.keys(C).length>0?C:void 0,durationMs:Date.now()-b})}export{Me as deployApplication};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as w,failure as b}from"@fjall/generator";import{getErrorMessage as y}from"@fjall/util";import{logger as
|
|
1
|
+
import{success as w,failure as b}from"@fjall/generator";import{getErrorMessage as y}from"@fjall/util";import{logger as S}from"@fjall/util/logger";import{getApplicationDestroyOrder as N,getApplicationStepName as h,getApplicationStepId as P}from"../types/operations.js";import{stubCallerIdentity as x}from"../types/deployment/index.js";import{deriveResourcesFromManifestStacks as D}from"../types/patternDetection.js";import{CdkContextBuilder as R}from"../services/supporting/CdkContextBuilder.js";import{buildParamsContext as I}from"./contextHelpers.js";import{deleteStateFile as L,readStateFile as O}from"../types/FjallState.js";async function q(n,c,r){const{callbacks:t}=n,C=Date.now(),m=c.frameworkRegistry.resolve({appPath:r.path})?.detection.pattern??null;let p;try{const e=await O(r.path);if(e!==null){const o=Object.keys(e.templateHashes);o.length>0&&(p=D(o))}}catch(e){S.debug("applicationDestroy","Could not read state file for resource detection",{error:y(e)}),t.onLog?.("Could not read state file for resource detection \u2014 falling back to pattern detection","warn")}const k=R.buildDeploymentContext({deployType:"application",target:r.appName,path:r.path,region:c.awsProvider.getRegion(),callerIdentity:x(c.awsProvider.getAccountId()),...I({orgConfig:n.orgConfig,identity:n.identity})},{verbose:n.options?.verbose},n.orgConfig),d=N({pattern:m,resources:p}),s=d.length;t.onLog?.(`Destroying ${r.appName} (${s} stacks, ${m??"standard"} pattern)`,"info");const f=[],u=[],g=await c.stackService.destroyAllStacks(k,{onOutput:e=>{t.onOutput?.(e)},onLog:t.onLog,onStackCleanupProgress:t.onStackCleanupProgress,onResourceProgress:(e,o)=>{t.onResourceProgress?.(e),o&&t.onParallelStackResourceProgress?.(o,e)},onStackStart:(e,o)=>{const a=P(e,"destroy"),l=h(e,"destroy"),i=d.indexOf(e);t.onStepStart?.(a,l,i,s)},onStackComplete:async(e,o)=>{const a=P(e,"destroy"),l=h(e,"destroy"),i=d.indexOf(e);o.success?o.data?.skipped?(u.push(o.data.stackName||e),t.onStepComplete?.(a,l,"skipped",i,s)):(f.push(o.data?.stackName||e),t.onStepComplete?.(a,l,"completed",i,s)):(t.onStepComplete?.(a,l,"error",i,s),t.onError?.(o.error))},onParallelPhaseStart:(e,o)=>{t.onLog?.(`Parallel phase: ${o}`,"info"),t.onParallelPhaseStart?.(e,o)},onParallelPhaseComplete:e=>{const o=e.filter(a=>!a.success);o.length>0&&t.onLog?.(`Parallel phase completed with ${o.length} failure(s)`,"warn"),t.onParallelPhaseComplete?.(e)}},p,n.abortSignal);if(!g.success)return t.onError?.(g.error),b(g.error);try{await L(r.path)}catch(e){S.debug("applicationDestroy","Failed to delete state file (non-critical)",{error:y(e)}),t.onLog?.("Failed to delete state file (non-critical)","warn")}return w({target:r.appName,deploymentType:"application",stacksDestroyed:f,skippedStacks:u,durationMs:Date.now()-C})}export{q as destroyApplication};
|
|
@@ -17,6 +17,17 @@ export interface CascadeDestroyAccountResult {
|
|
|
17
17
|
error?: string;
|
|
18
18
|
skipped?: boolean;
|
|
19
19
|
}
|
|
20
|
+
export interface DestroyCascadeAccountOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Resolved primary (home) region of the cascade. buildParamsContext derives
|
|
23
|
+
* the account-globals skip from it, mirroring deployCascadeAccount: a
|
|
24
|
+
* non-home (secondary/DR) region destroy synthesises WITHOUT the fixed-name
|
|
25
|
+
* org-global IAM resources, matching the deployed stack shape so the destroy
|
|
26
|
+
* can never identify the primary's globals for teardown. Omitted ⇒ the
|
|
27
|
+
* globals stay in the synthesised template (home-region semantics).
|
|
28
|
+
*/
|
|
29
|
+
primaryRegion?: string;
|
|
30
|
+
}
|
|
20
31
|
/**
|
|
21
32
|
* Destroy a single cascade account (platform or member) in a specific region.
|
|
22
33
|
*
|
|
@@ -27,4 +38,4 @@ export declare function destroyCascadeAccount(params: DestroyParams, services: D
|
|
|
27
38
|
id: string;
|
|
28
39
|
name: string;
|
|
29
40
|
environment: string | null;
|
|
30
|
-
}, deployType: "platform" | "account", region: string, callbacks: DeployCallbacks): Promise<CascadeDestroyAccountResult>;
|
|
41
|
+
}, deployType: "platform" | "account", region: string, callbacks: DeployCallbacks, options?: DestroyCascadeAccountOptions): Promise<CascadeDestroyAccountResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{logger as f}from"@fjall/util/logger";import{maskSensitiveOutput as
|
|
1
|
+
import{join as I}from"path";import{logger as f}from"@fjall/util/logger";import{maskSensitiveOutput as i,getErrorMessage as v}from"@fjall/util";import{stubCallerIdentity as b}from"../types/deployment/index.js";import{CloudFormationClient as P,DescribeStacksCommand as _}from"@aws-sdk/client-cloudformation";import{S3Client as g}from"@aws-sdk/client-s3";import{BackupClient as x}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as F,describeSurvivingBackupVault as M,formatSurvivingVaultWarning as K}from"../aws/organisations/backup.js";import{composeSdkAbortSignal as L}from"../aws/organisations/types.js";import{ORGANISATION_TYPES as R,getOrganisationStackName as V}from"../types/operations.js";import{CdkContextBuilder as j}from"../services/supporting/CdkContextBuilder.js";import{regionSuffix as U}from"../types/FjallState.js";import{buildParamsContext as G,assumeCascadeRole as H,forwardOutput as E}from"./contextHelpers.js";import{cascadeOperationKey as W}from"./cascadeHelpers.js";import{capturedSweepBuckets as Y,cleanupFailedStack as q,preEmptyStackBuckets as z,sweepOrphanedDestroyBuckets as N}from"./stackCleanup.js";import{STACK_NOT_FOUND_PATTERN as J,STACK_FAILED_STATE_PATTERN as Q}from"../types/constants.js";async function ye(a,o,c,e,n,t,r,T){const y=Date.now(),u=W(e.name,t),l=(s,p=s)=>(r.onCascadeAccountComplete?.(u,!1,p,t),{accountName:e.name,accountId:e.id,region:t,success:!1,duration:Date.now()-y,error:s});r.onCascadeAccountStart?.(u,e.id,t,n);const S=await H(o.awsProvider,e.id,t,`fjall-cascade-destroy-${e.name}`);if(!S.success){const s=i(S.error.message);return l(`AssumeRole failed: ${s}`,s)}const{provider:d,credentials:w}=S.data,O=I(c.path,`cdk.out.${e.id}.${U(t)}`),k=j.buildDeploymentContext({deployType:n,target:c.target,path:c.path,assemblyDir:O,region:t,accountName:e.name,callerIdentity:b(e.id),...G({orgConfig:a.orgConfig,identity:a.identity,region:t,primaryRegion:T?.primaryRegion})},{verbose:a.options?.verbose},a.orgConfig);r.onCascadeAccountPhaseChange?.(u,"synth",t);const A=await o.cdkService.runCdkSynth(k,E(r));if(!A.success)return l(i(`Synth failed: ${A.error}`));r.onCascadeAccountPhaseChange?.(u,"destroy",t);const m=V(n==="platform"?R.PLATFORM:R.ACCOUNT);F(e.environment,a.orgConfig?.disasterRecoveryRegion)&&await X(d.getClient(x),t,r);const $=await z(d.getClient(P),d.getClient(g),m,r,a.abortSignal),C=Y($),D=await o.cdkService.runCdkDestroy(k,m,E(r),s=>r.onCascadeAccountResourceProgress?.(u,s,t),d,!0,w);if(!D.success){const s=D.error;if(s.includes(Q)){f.warn("cascadeDestroy",`CDK destroy failed on ${m} in failed state, retrying via CloudFormation API`,{region:t,account:e.name});try{await q(m,t,w,{accountId:e.id,abortSignal:a.abortSignal},r)}catch(B){const h=`cleanupFailedStack threw for ${m}: ${i(v(B))}`;f.warn("cascadeDestroy",h),r.onLog?.(h,"warn")}const p=await Z(m,d.getClient(P),a.abortSignal);return p.deleted?(C.length>0&&await N(d.getClient(g),C,r,a.abortSignal),r.onCascadeAccountComplete?.(u,!0,void 0,t),{accountName:e.name,accountId:e.id,region:t,success:!0,duration:Date.now()-y}):p.error?l(i(p.error)):l(i(`Stack ${m} cleanup attempted but stack still exists in ${t}`))}return l(i(s))}return C.length>0&&await N(d.getClient(g),C,r,a.abortSignal),r.onCascadeAccountComplete?.(u,!0,void 0,t),{accountName:e.name,accountId:e.id,region:t,success:!0,duration:Date.now()-y}}async function X(a,o,c){try{const e=await M(a,o);if(!e.success){f.debug("cascadeDestroy","Backup-vault survival probe failed",{region:o,error:i(e.error.message)});return}if(e.data===null)return;c.onProgress?.({type:"warning",message:K(e.data),metadata:{source:"backup-vault-survival"}}),f.warn("cascadeDestroy","Backup vault survives destroy",{region:o,vaultName:e.data.vaultName,recoveryPointCount:e.data.recoveryPointCount,lockPermanent:e.data.lockPermanent})}catch(e){f.debug("cascadeDestroy","Backup-vault survival probe threw",{region:o,error:i(v(e))})}}async function Z(a,o,c){try{const n=(await o.send(new _({StackName:a}),{abortSignal:L(c)})).Stacks?.[0]?.StackStatus;return!n||n==="DELETE_COMPLETE"?{deleted:!0}:{deleted:!1,error:`Stack still in ${n} after cleanup attempt`}}catch(e){if(e instanceof Error&&e.message?.includes(J))return{deleted:!0};const n=i(v(e));return f.debug("cascadeDestroy","Stack verification failed",{error:n}),{deleted:!1,error:`Stack verification failed: ${n}`}}}export{ye as destroyCascadeAccount};
|
|
@@ -34,11 +34,41 @@ export interface AccountRegionPair {
|
|
|
34
34
|
* on this set, else destroy sweeps regions deploy never created (or vice versa).
|
|
35
35
|
*/
|
|
36
36
|
export declare function buildRegionList(orgConfig: OrgConfig | undefined): string[];
|
|
37
|
+
/**
|
|
38
|
+
* The cascade's home (anchor) region — first entry of buildRegionList, i.e.
|
|
39
|
+
* the configured primary (or DEFAULT_REGION when none is set). Deploy and
|
|
40
|
+
* destroy fan-outs MUST share this derivation so a destroy synth mirrors the
|
|
41
|
+
* account-globals decision of the deploy that created each region's stack.
|
|
42
|
+
*/
|
|
43
|
+
export declare function cascadeHomeRegion(orgConfig: OrgConfig | undefined): string;
|
|
44
|
+
/**
|
|
45
|
+
* Operation key identifying one account x region task across the cascade
|
|
46
|
+
* lifecycle. Deploy events, destroy events, and the Ink pre-seeded pills
|
|
47
|
+
* (CLI `stackOperationUtils.fetchCascadeAccounts`) MUST share this shape so
|
|
48
|
+
* every pill matches its engine events one-for-one.
|
|
49
|
+
*/
|
|
50
|
+
export declare function cascadeOperationKey(accountName: string, region: string): string;
|
|
37
51
|
/**
|
|
38
52
|
* Build all account x region pairs for parallel cascade fan-out.
|
|
39
53
|
*/
|
|
40
54
|
export declare function buildAccountRegionPairs(accounts: ProviderAccount[], regions: string[]): AccountRegionPair[];
|
|
41
55
|
export { buildCascadeRoleArn } from "./contextHelpers.js";
|
|
56
|
+
/** A single pre-flight probe failure: the cascade role could not be assumed. */
|
|
57
|
+
export interface CascadeRoleProbeFailure {
|
|
58
|
+
accountId: string;
|
|
59
|
+
accountName: string;
|
|
60
|
+
error: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Pre-flight check that OrganizationAccountAccessRole is assumable in every
|
|
64
|
+
* cascade target, run BEFORE any synth/bootstrap/deploy work. An imported
|
|
65
|
+
* (invite-joined) member never gets the role auto-created, so without this
|
|
66
|
+
* probe a broken role only surfaces mid-cascade — after the org root has
|
|
67
|
+
* already deployed — one account at a time. Read-only: the assumed
|
|
68
|
+
* credentials are discarded. Inherits assumeCascadeRole's retry budget, so
|
|
69
|
+
* role propagation on freshly created accounts does not false-fail.
|
|
70
|
+
*/
|
|
71
|
+
export declare function probeCascadeRoles(services: DeployServices, accounts: ProviderAccount[], callbacks: DeployCallbacks, signal?: AbortSignal): Promise<CascadeRoleProbeFailure[]>;
|
|
42
72
|
/**
|
|
43
73
|
* Deploy a single cascade account (platform or member).
|
|
44
74
|
* Assumes the target account's role, sets env credentials, and deploys.
|
|
@@ -62,11 +92,12 @@ export interface DeployCascadeAccountOptions {
|
|
|
62
92
|
/** Region to deploy in; falls back to account.region then the provider region. */
|
|
63
93
|
region?: string;
|
|
64
94
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
95
|
+
* Resolved primary (home) region of the cascade. buildParamsContext derives
|
|
96
|
+
* the account-globals skip from it: non-home (secondary/DR) regions skip the
|
|
97
|
+
* fixed-name org-global IAM resources that would collide across regions.
|
|
98
|
+
* Omitted ⇒ globals are never skipped.
|
|
68
99
|
*/
|
|
69
|
-
|
|
100
|
+
primaryRegion?: string;
|
|
70
101
|
}
|
|
71
102
|
export declare function deployCascadeAccount(params: DeployParams, services: DeployServices, operation: OrganisationOperation, account: ProviderAccount, deployType: "platform" | "account", callbacks: DeployCallbacks, options?: DeployCascadeAccountOptions): Promise<Result<CascadeAccountResult>>;
|
|
72
103
|
/**
|
|
@@ -78,7 +109,7 @@ export declare function deployCascadeAccount(params: DeployParams, services: Dep
|
|
|
78
109
|
export declare function readPlatformIpamPoolIds(services: DeployServices, platformAccount: {
|
|
79
110
|
id: string;
|
|
80
111
|
name: string;
|
|
81
|
-
}, callbacks: DeployCallbacks): Promise<Map<string, string>>;
|
|
112
|
+
}, callbacks: DeployCallbacks, signal?: AbortSignal): Promise<Map<string, string>>;
|
|
82
113
|
/**
|
|
83
114
|
* Deploy configured domains: apex domains sequentially, then delegated
|
|
84
115
|
* domains in parallel. Delegates to the caller-provided DomainDeployProvider.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as L}from"path";import{success as B,failure as h}from"@fjall/generator";import{logger as y}from"@fjall/util/logger";import{maskSensitiveOutput as p,mapSettledWithConcurrency as U}from"@fjall/util";import{ORGANISATION_TYPES as R,getOrganisationStackName as j}from"../types/operations.js";import{CdkContextBuilder as _}from"../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as K}from"../types/deployment/index.js";import{CloudFormationService as H}from"../services/infrastructure/CloudFormationService.js";import{getCascadeStateFilePath as z,regionSuffix as G}from"../types/FjallState.js";import{BackupClient as Y}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as W,describeBackupVaultExists as X}from"../aws/organisations/backup.js";import{buildParamsContext as q,collectStackOutputs as M,assumeCascadeRole as v,forwardOutput as x}from"./contextHelpers.js";import{accountTier as T}from"@fjall/util";import{DEFAULT_REGION as V}from"../aws/utils/regions.js";const J=4;function le(o){const n=o.find(e=>T(e)==="platform"),c=o.filter(e=>T(e)==="account");return{platformAccount:n,memberAccounts:c}}function Q(o){const n=o?.primaryRegion??V,c=o?.secondaryRegions??[],e=new Set([n,...c]),i=o?.disasterRecoveryRegion;return i&&e.add(i),[...e]}function ge(o){return Q(o)[0]??V}function Z(o,n){return`${o} (${n})`}function Ce(o,n){const c=[];for(const e of n)for(const i of o)c.push({account:i,region:e});return c}import{buildCascadeRoleArn as Se}from"./contextHelpers.js";async function he(o,n,c,e){c.onLog?.(`Verifying cascade role access for ${n.length} account(s)\u2026`,"info");const i=await U(n,J,async a=>v(o.awsProvider,a.id,a.region??o.awsProvider.getRegion(),`fjall-preflight-${a.name}`,e)),s=[];return i.forEach((a,t)=>{const r=n[t];if(r){if(a.status==="rejected"){s.push({accountId:r.id,accountName:r.name,error:a.reason instanceof Error?a.reason.message:String(a.reason)});return}a.value.success||s.push({accountId:r.id,accountName:r.name,error:a.value.error.message})}}),s.length===0&&c.onLog?.(`Cascade role access verified for ${n.length} account(s)`,"info"),s}async function ye(o,n,c,e,i,s,a){const t=a?.region??e.region??n.awsProvider.getRegion(),r=Z(e.name,t),u=a?.orgConfig??o.orgConfig,l=a?.ipamPoolId;s.onCascadeAccountStart?.(r,e.id,t,i);const d=await v(n.awsProvider,e.id,t,`fjall-cascade-${e.name}`,o.abortSignal);if(!d.success)return s.onCascadeAccountComplete?.(r,!1,p(d.error.message),t),h(new Error(`Failed to assume role for ${e.name}: ${p(d.error.message)}`));const{provider:g,credentials:C}=d.data,P=L(o.workingDirectory,"fjall",i==="platform"?R.PLATFORM:R.ACCOUNT),D=L(P,`cdk.out.${e.id}.${G(t)}`),w=_.buildDeploymentContext({deployType:i,target:c.target,path:P,assemblyDir:D,environment:e.environment??void 0,region:t,accountName:e.name,callerIdentity:K(e.id),ipamPoolId:l,...q({orgConfig:u,identity:o.identity,skipOidc:o.options?.skipOidc,region:t,primaryRegion:a?.primaryRegion,trailLifecycle:e.trailLifecycle})},{verbose:o.options?.verbose},u);if(W(e.environment,u?.disasterRecoveryRegion)){const f=await X(g.getClient(Y));if(!f.success)return s.onCascadeAccountComplete?.(r,!1,p(f.error.message),t),h(new Error(`Backup vault probe failed for ${e.name}: ${p(f.error.message)}`));w.fjallAdoptBackupVault=f.data}s.onCascadeAccountPhaseChange?.(r,"synth",t);const $=await n.cdkService.runCdkSynth(w,x(s),C);if(!$.success)return s.onCascadeAccountComplete?.(r,!1,p(`Synth failed: ${$.error}`),t),h(new Error(`Synth failed for ${e.name}: ${p($.error)}`));const m=j(i==="platform"?R.PLATFORM:R.ACCOUNT),k=z(P,e.id,t),S=new H(g),{changed:b,currentHash:I}=await n.hashService.compareCascadeStack(D,m,k);if(!b&&o.options?.force!==!0&&await S.stackExists(m)){const f=await S.getStackOutputs(m);f.success||y.debug("cascadeHelpers","Failed to read outputs for skipped cascade account (non-critical)",{stackName:m,account:e.name});const F=M(f);return s.onLog?.(`${e.name}: no infrastructure changes \u2014 skipping deploy`,"info"),s.onCascadeAccountComplete?.(r,!0,void 0,t,F,!0),B({outputs:F,skipped:!0})}s.onCascadeAccountPhaseChange?.(r,"bootstrap",t);const A=await n.cdkService.runCdkBootstrap(w,x(s),C);if(!A.success)return s.onCascadeAccountComplete?.(r,!1,p(`Bootstrap failed: ${A.error}`),t),h(new Error(`Bootstrap failed for ${e.name}: ${p(A.error)}`));s.onCascadeAccountPhaseChange?.(r,"deploy",t);const O=await n.cdkService.runCdkDeploy(w,m,x(s),f=>s.onCascadeAccountResourceProgress?.(r,f,t),g,C);if(!O.success)return s.onCascadeAccountComplete?.(r,!1,p(O.error),t),h(new Error(p(O.error)));const E=await S.getStackOutputs(m);E.success||y.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:m,account:e.name});const N=M(E);return I!==void 0&&((await n.hashService.persistCascadeStack(k,m,I)).success||y.debug("cascadeHelpers","Failed to persist cascade hash state (non-critical)",{stackName:m,account:e.name})),s.onCascadeAccountComplete?.(r,!0,void 0,t,N,!1),B({outputs:N,skipped:!1})}async function Re(o,n,c,e){const i=new Map,s=o.awsProvider.getRegion(),a=await v(o.awsProvider,n.id,s,`fjall-ipam-read-${n.name}`,e);if(!a.success)return y.debug("organisationDeploy",`Cannot read Platform outputs: ${a.error.message}`),i;const t=new H(a.data.provider),r=j(R.PLATFORM),u=await t.getStackOutputs(r);if(!u.success)return y.debug("organisationDeploy",`Failed to read Platform stack outputs: ${u.error.message}`),i;const l=/^IpamPoolId(\d{12})(\w+)$/;for(const d of u.data){const g=d.OutputKey?.match(l);if(g&&d.OutputValue){const C=`${g[1]}-${g[2]}`;i.set(C,d.OutputValue)}}return i.size>0&&c.onLog?.(`Read ${i.size} IPAM pool ID(s) from Platform stack`,"info"),i}async function we(o,n){const c=o.getDomains();if(c.length===0)return{domainsDeployed:0,errors:[]};n.onCascadePhaseStart?.("domains");const e=c.filter(t=>t.type==="apex"),i=c.filter(t=>t.type==="delegated");let s=0;const a=[];for(const t of e){const r=await o.deployDomain(t.name,n);r.success?s++:a.push(`${t.name}: ${r.error.message}`)}if(i.length>0){const t=await Promise.allSettled(i.map(r=>o.deployDomain(r.name,n)));for(let r=0;r<t.length;r++){const u=t[r],l=i[r];if(!(!u||!l))if(u.status==="fulfilled")u.value.success?s++:a.push(`${l.name}: ${u.value.error.message}`);else{const d=u.reason instanceof Error?u.reason.message:String(u.reason);a.push(`${l.name}: ${d}`)}}}return n.onCascadePhaseComplete?.("domains"),{domainsDeployed:s,errors:a}}export{J as CASCADE_MAX_CONCURRENCY,Ce as buildAccountRegionPairs,Se as buildCascadeRoleArn,Q as buildRegionList,ge as cascadeHomeRegion,Z as cascadeOperationKey,ye as deployCascadeAccount,we as deployDomains,le as partitionAccounts,he as probeCascadeRoles,Re as readPlatformIpamPoolIds};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Result } from "@fjall/generator";
|
|
2
|
+
import type { TrailLifecycleState } from "@fjall/util/config";
|
|
2
3
|
import type { ResourceEvent } from "@fjall/util/aws";
|
|
3
4
|
import type { OrgConfig } from "../types/orgConfig.js";
|
|
4
5
|
import type { DeployIdentity } from "../types/credentials.js";
|
|
@@ -7,20 +8,37 @@ import { SimpleAwsProvider } from "../aws/SimpleAwsProvider.js";
|
|
|
7
8
|
import type { DeploymentContext } from "../types/deployment/DeploymentTypes.js";
|
|
8
9
|
import type { DeployCallbacks } from "../types/callbacks.js";
|
|
9
10
|
import type { DeployServices } from "./serviceFactory.js";
|
|
11
|
+
/**
|
|
12
|
+
* Whether a deploy targets a region other than the resolved primary. Shared
|
|
13
|
+
* predicate for the synth-time globals skip (buildParamsContext) AND the
|
|
14
|
+
* pre-deploy globals existence probe (deploySingleComponent) — the two MUST
|
|
15
|
+
* agree or a skip can pass unprobed. No primary configured (config never
|
|
16
|
+
* synced) ⇒ false: the deploy region anchors a first-ever deploy.
|
|
17
|
+
*/
|
|
18
|
+
export declare function targetsNonPrimaryRegion(region: string | undefined, primaryRegion: string | undefined): boolean;
|
|
10
19
|
/**
|
|
11
20
|
* Build the orgConfig/identity context fields shared by all deployment paths.
|
|
12
21
|
* CdkContextBuilder expects orgConfig as a JSON string and fjallOrgId as a string.
|
|
22
|
+
*
|
|
23
|
+
* Sole derivation site for `fjallAccountGlobalsConfigured`: a deploy skips the
|
|
24
|
+
* fixed-name account globals iff it targets a region other than the resolved
|
|
25
|
+
* primary (targetsNonPrimaryRegion above).
|
|
13
26
|
*/
|
|
14
27
|
export declare function buildParamsContext(params: {
|
|
15
28
|
orgConfig?: OrgConfig;
|
|
16
29
|
identity?: DeployIdentity;
|
|
17
30
|
skipOidc?: boolean;
|
|
18
|
-
|
|
31
|
+
/** Region this synth targets. Omitted ⇒ no globals flag. */
|
|
32
|
+
region?: string;
|
|
33
|
+
/** Resolved primary (home) region; callers pass their resolved anchor, never re-derive. */
|
|
34
|
+
primaryRegion?: string;
|
|
35
|
+
trailLifecycle?: TrailLifecycleState;
|
|
19
36
|
}): {
|
|
20
37
|
orgConfig?: string;
|
|
21
38
|
fjallOrgId?: string;
|
|
22
39
|
fjallOidcConfigured?: boolean;
|
|
23
40
|
fjallAccountGlobalsConfigured?: boolean;
|
|
41
|
+
fjallAccountTrailState?: string;
|
|
24
42
|
};
|
|
25
43
|
/** Forward onOutput callback — reduces lambda repetition across orchestration files. */
|
|
26
44
|
export declare function forwardOutput(callbacks: DeployCallbacks): (chunk: string) => void;
|
|
@@ -42,7 +60,7 @@ export interface CascadeAssumedRole {
|
|
|
42
60
|
* Callers handle failure (callbacks, logging, return type) — this helper
|
|
43
61
|
* only owns the assume + provider construction.
|
|
44
62
|
*/
|
|
45
|
-
export declare function assumeCascadeRole(awsProvider: SimpleAwsProvider, accountId: string, region: string, sessionName: string): Promise<Result<CascadeAssumedRole, Error>>;
|
|
63
|
+
export declare function assumeCascadeRole(awsProvider: SimpleAwsProvider, accountId: string, region: string, sessionName: string, signal?: AbortSignal): Promise<Result<CascadeAssumedRole, Error>>;
|
|
46
64
|
/**
|
|
47
65
|
* Run CDK synth and return failure with masked error if it fails.
|
|
48
66
|
* Calls `onError` on the callbacks so callers only need to handle
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as f,failure as
|
|
1
|
+
import{success as f,failure as i}from"@fjall/generator";import{getErrorMessage as m,maskSensitiveOutput as d,sleep as y}from"@fjall/util";import{logger as O}from"@fjall/util/logger";import{SimpleAwsProvider as R}from"../aws/SimpleAwsProvider.js";const S={account:"active",draining:"draining",org:"removed"};function w(e,r){return e!==void 0&&e!==""&&r!==void 0&&r!==""&&e!==r}function M(e){const r=w(e.region,e.primaryRegion);return{...e.orgConfig!==void 0?{orgConfig:JSON.stringify(e.orgConfig)}:{},...e.identity!==void 0?{fjallOrgId:e.identity.fjallOrgId}:{},...e.skipOidc?{fjallOidcConfigured:!0}:{},...r?{fjallAccountGlobalsConfigured:!0}:{},...e.trailLifecycle!==void 0?{fjallAccountTrailState:S[e.trailLifecycle]}:{}}}function _(e){return r=>e.onOutput?.(r)}function L(e){return r=>e.onResourceProgress?.(r)}function D(e){if(!e.success||e.data.length===0)return;const r={};for(const t of e.data)t.OutputKey&&t.OutputValue!==void 0&&(r[t.OutputKey]=t.OutputValue);return Object.keys(r).length>0?r:void 0}const E="OrganizationAccountAccessRole",l=5,$=5e3,b=3e4;function K(e){return`arn:aws:iam::${e}:role/${E}`}function T(e,r){return r===void 0?y(e):r.aborted?Promise.resolve():new Promise(t=>{const n=()=>{t()};r.addEventListener("abort",n,{once:!0}),y(e).then(()=>{r.removeEventListener("abort",n),t()})})}async function P(e,r,t,n,o){if(!e.assumeRole)return i(new Error("AwsProvider does not support assumeRole"));const u=K(r),g=e.assumeRole.bind(e);let s;for(let c=0;c<=l;c++)try{s=await g(u,n);break}catch(a){const p=a instanceof Error?a.name:void 0;if(p==="AccessDenied"||p==="AccessDeniedException")return i(new Error(`Access denied assuming ${E} in account ${r}. The role may not exist or may not trust the management account.`));if(c<l){const A=Math.min($*2**c,b);if(O.debug("assumeCascadeRole",`Attempt ${c+1} failed for account ${r}, retrying in ${Math.round(A/1e3)}s`,{error:d(m(a))}),await T(A,o),o?.aborted)return i(new Error(`Aborted while retrying assume-role for account ${r}`));continue}return i(new Error(`Failed to assume role in account ${r} after ${l+1} attempts: ${d(m(a))}`))}if(!s)return i(new Error(`Failed to assume role in account ${r}`));const C=new R({accessKeyId:s.accessKeyId,secretAccessKey:s.secretAccessKey,sessionToken:s.sessionToken,region:t,accountId:r});return f({provider:C,credentials:{accessKeyId:s.accessKeyId,secretAccessKey:s.secretAccessKey,sessionToken:s.sessionToken}})}async function j(e,r,t,n){const o=await e.cdkService.runCdkSynth(r,u=>t.onCdkOutput?.(u,"synth"));if(!o.success){const u=new Error(d(`${n}: ${o.error}`));return t.onError?.(u),i(u)}return f(void 0)}async function B(e,r,t){t.onCDKBootstrap?.("bootstrapping");const n=await e.cdkService.runCdkBootstrap(r,_(t));if(!n.success){t.onCDKBootstrap?.("failed");const o=new Error(d(`Bootstrap failed: ${n.error}`));return t.onError?.(o),i(o)}return t.onCDKBootstrap?.("complete"),f(void 0)}export{P as assumeCascadeRole,B as bootstrapOrFail,K as buildCascadeRoleArn,M as buildParamsContext,D as collectStackOutputs,_ as forwardOutput,L as forwardResourceProgress,j as synthOrFail,w as targetsNonPrimaryRegion};
|
|
@@ -2,21 +2,30 @@ export { deploy } from "./deploy.js";
|
|
|
2
2
|
export { destroy } from "./destroy.js";
|
|
3
3
|
export { deployOrganisation } from "./organisationDeploy.js";
|
|
4
4
|
export { destroyOrganisation } from "./organisationDestroy.js";
|
|
5
|
-
export { cleanupFailedStack, isCleanableState, SAFE_CLEANUP_STATES } from "./stackCleanup.js";
|
|
5
|
+
export { cleanupFailedStack, emptyS3Bucket, preEmptyStackBuckets, formatQuarantineSuspectedMessage, formatRetainedBucketsMessage, isQuarantineDetail, isRetainedBucketsDetail, PRE_EMPTY_TAG_KEYS, isCleanableState, SAFE_CLEANUP_STATES } from "./stackCleanup.js";
|
|
6
|
+
export type { PreEmptyBucketsSummary, EmptyBucketOutcome } from "./stackCleanup.js";
|
|
6
7
|
export type { CascadeDestroyAccountResult } from "./cascadeDestroyHelpers.js";
|
|
7
|
-
export { partitionAccounts, buildRegionList, buildAccountRegionPairs } from "./cascadeHelpers.js";
|
|
8
|
+
export { partitionAccounts, buildRegionList, buildAccountRegionPairs, cascadeHomeRegion, cascadeOperationKey } from "./cascadeHelpers.js";
|
|
8
9
|
export type { AccountRegionPair } from "./cascadeHelpers.js";
|
|
9
10
|
export { projectScalarSummary, projectAccountRows } from "./cascadeSummary.js";
|
|
10
11
|
export type { CascadeOutcomeResult, CascadeMemberOutcome, CascadePlatformOutcome, CascadeLedger, CascadeAccountRow, CascadePlatformRow, CascadeAccountProjection } from "./cascadeSummary.js";
|
|
11
12
|
export { reconcileProviderAccounts, mergeReconciledProviderAccounts } from "./reconcileProviderAccounts.js";
|
|
12
13
|
export type { ReconcileResult } from "./reconcileProviderAccounts.js";
|
|
13
|
-
export {
|
|
14
|
+
export { decideNextTransition, reconcileTrailMigration, ORG_TRAIL_BUCKET_OUTPUT_KEY, TRAIL_BUCKET_OUTPUT_KEY, TRAIL_KEY_ARN_OUTPUT_KEY } from "./trailMigration/trailMigration.js";
|
|
15
|
+
export type { MemberTrailFacts, TrailMigrationTransition, TrailMigrationOutcome } from "./trailMigration/trailMigration.js";
|
|
16
|
+
export { decommissionMemberTrailStorage } from "./trailMigration/memberTrailCleanup.js";
|
|
17
|
+
export type { DecommissionClients, DecommissionInput, DecommissionOutcome } from "./trailMigration/memberTrailCleanup.js";
|
|
18
|
+
export { unlockBucket } from "./unlock/unlockBucket.js";
|
|
19
|
+
export type { UnlockBucketInput, UnlockBucketReport } from "./unlock/unlockBucket.js";
|
|
20
|
+
export { unlockQueue } from "./unlock/unlockQueue.js";
|
|
21
|
+
export type { UnlockQueueInput, UnlockQueueReport } from "./unlock/unlockQueue.js";
|
|
22
|
+
export { parseAccountsConfiguration, flattenAccountsToEnvironments, extractAllAccountNames, accountsConfigToOUTree, isStringArray, isAccountsConfig, isOuOnlyAccountBucket, OU_ONLY_ACCOUNT_BUCKETS } from "./accountsConfig.js";
|
|
14
23
|
export type { AccountsConfig } from "./accountsConfig.js";
|
|
15
24
|
export type { DockerProvider, DockerProgressCallback, DockerServiceConfig, DockerBuildParams, DockerBuildResult, ECRInitParams, ECRInitResult, TagImagesParams, TagImagesResult, TagByDigestParams } from "./dockerInterface.js";
|
|
16
25
|
export type { DomainDeployProvider, DomainConfig, DomainDeployResult } from "./domainInterface.js";
|
|
17
26
|
export type { DeployServices } from "./serviceFactory.js";
|
|
18
27
|
export type { DetectionResult } from "./detectionPipeline.js";
|
|
19
28
|
export { runOpenNextBuild } from "./openNextBuild.js";
|
|
20
|
-
export { runOrganisationSetup } from "./organisationSetup.js";
|
|
29
|
+
export { runOrganisationSetup, ORG_SETUP_PHASES } from "./organisationSetup.js";
|
|
21
30
|
export type { OrgSetupPhase, OrgSetupCallbacks, OrgSetupConfig, OrgSetupResult } from "./organisationSetup.js";
|
|
22
31
|
export * from "./builders/index.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{deploy as
|
|
1
|
+
import{deploy as r}from"./deploy.js";import{destroy as n}from"./destroy.js";import{deployOrganisation as i}from"./organisationDeploy.js";import{destroyOrganisation as s}from"./organisationDestroy.js";import{cleanupFailedStack as p,emptyS3Bucket as m,preEmptyStackBuckets as T,formatQuarantineSuspectedMessage as l,formatRetainedBucketsMessage as f,isQuarantineDetail as A,isRetainedBucketsDetail as _,PRE_EMPTY_TAG_KEYS as d,isCleanableState as x,SAFE_CLEANUP_STATES as S}from"./stackCleanup.js";import{partitionAccounts as O,buildRegionList as g,buildAccountRegionPairs as R,cascadeHomeRegion as U,cascadeOperationKey as P}from"./cascadeHelpers.js";import{projectScalarSummary as y,projectAccountRows as B}from"./cascadeSummary.js";import{reconcileProviderAccounts as K,mergeReconciledProviderAccounts as N}from"./reconcileProviderAccounts.js";import{decideNextTransition as L,reconcileTrailMigration as M,ORG_TRAIL_BUCKET_OUTPUT_KEY as b,TRAIL_BUCKET_OUTPUT_KEY as v,TRAIL_KEY_ARN_OUTPUT_KEY as G}from"./trailMigration/trailMigration.js";import{decommissionMemberTrailStorage as Q}from"./trailMigration/memberTrailCleanup.js";import{unlockBucket as D}from"./unlock/unlockBucket.js";import{unlockQueue as H}from"./unlock/unlockQueue.js";import{parseAccountsConfiguration as h,flattenAccountsToEnvironments as q,extractAllAccountNames as z,accountsConfigToOUTree as J,isStringArray as V,isAccountsConfig as W,isOuOnlyAccountBucket as X,OU_ONLY_ACCOUNT_BUCKETS as Z}from"./accountsConfig.js";import{runOpenNextBuild as ee}from"./openNextBuild.js";import{runOrganisationSetup as re,ORG_SETUP_PHASES as te}from"./organisationSetup.js";export*from"./builders/index.js";export{te as ORG_SETUP_PHASES,b as ORG_TRAIL_BUCKET_OUTPUT_KEY,Z as OU_ONLY_ACCOUNT_BUCKETS,d as PRE_EMPTY_TAG_KEYS,S as SAFE_CLEANUP_STATES,v as TRAIL_BUCKET_OUTPUT_KEY,G as TRAIL_KEY_ARN_OUTPUT_KEY,J as accountsConfigToOUTree,R as buildAccountRegionPairs,g as buildRegionList,U as cascadeHomeRegion,P as cascadeOperationKey,p as cleanupFailedStack,L as decideNextTransition,Q as decommissionMemberTrailStorage,r as deploy,i as deployOrganisation,n as destroy,s as destroyOrganisation,m as emptyS3Bucket,z as extractAllAccountNames,q as flattenAccountsToEnvironments,l as formatQuarantineSuspectedMessage,f as formatRetainedBucketsMessage,W as isAccountsConfig,x as isCleanableState,X as isOuOnlyAccountBucket,A as isQuarantineDetail,_ as isRetainedBucketsDetail,V as isStringArray,N as mergeReconciledProviderAccounts,h as parseAccountsConfiguration,O as partitionAccounts,T as preEmptyStackBuckets,B as projectAccountRows,y as projectScalarSummary,K as reconcileProviderAccounts,M as reconcileTrailMigration,ee as runOpenNextBuild,re as runOrganisationSetup,D as unlockBucket,H as unlockQueue};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ProviderAccount } from "@fjall/util/config";
|
|
2
|
+
import type { DeployParams } from "../../types/params.js";
|
|
3
|
+
import type { OrgConfig } from "../../types/orgConfig.js";
|
|
4
|
+
import type { OrganisationOperation } from "../../types/operations.js";
|
|
5
|
+
import type { DeployServices } from "../serviceFactory.js";
|
|
6
|
+
/**
|
|
7
|
+
* Execute the cascade phases: platform deploy, IPAM pool read, domains, and
|
|
8
|
+
* the member-account fan-out, finishing with the ledger callbacks.
|
|
9
|
+
*
|
|
10
|
+
* Pushes per-target failures onto `cascadeErrors` and captured stack outputs
|
|
11
|
+
* onto `allCascadeOutputs` (both caller-owned). Returns whether any cascade
|
|
12
|
+
* target actually deployed (vs every target skipping as unchanged).
|
|
13
|
+
*/
|
|
14
|
+
export declare function executeCascade(params: DeployParams, services: DeployServices, operation: OrganisationOperation, args: {
|
|
15
|
+
providerAccounts: ProviderAccount[];
|
|
16
|
+
effectiveOrgConfig: OrgConfig | undefined;
|
|
17
|
+
totalSteps: number;
|
|
18
|
+
cascadeErrors: Array<{
|
|
19
|
+
accountId: string;
|
|
20
|
+
error: string;
|
|
21
|
+
}>;
|
|
22
|
+
allCascadeOutputs: Array<{
|
|
23
|
+
accountId: string;
|
|
24
|
+
outputs: Record<string, string>;
|
|
25
|
+
}>;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
anyCascadeDeployHappened: boolean;
|
|
28
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{failure as U}from"@fjall/generator";import{regionSuffix as j}from"../../types/FjallState.js";import{partitionAccounts as W,deployCascadeAccount as v,readPlatformIpamPoolIds as $,deployDomains as X,buildRegionList as Y,buildAccountRegionPairs as z,cascadeHomeRegion as B,CASCADE_MAX_CONCURRENCY as G}from"../cascadeHelpers.js";import{projectScalarSummary as x}from"../cascadeSummary.js";import{getErrorMessage as J,maskSensitiveOutput as C,mapSettledWithConcurrency as K}from"@fjall/util";import{INFRA_STEPS as E}from"./infraSteps.js";async function re(d,D,N,T){const{callbacks:e}=d,{providerAccounts:q,effectiveOrgConfig:R,totalSteps:p,cascadeErrors:l,allCascadeOutputs:b}=T;let S=!1;e.onCascadeStart?.();const H=Date.now();let i=2,P=!1,g,w=!1;const h=[],M=t=>({members:h,...g!==void 0?{platform:g}:{},domainsDeployed:w,errors:l,totalDurationMs:t}),{platformAccount:a,memberAccounts:A}=W(q),O=B(d.orgConfig);if(a){const{id:t,name:o}=E.CASCADE_PLATFORM;e.onStepStart?.(t,o,i,p),e.onCascadePhaseStart?.("platform");let s;const I=Date.now();try{s=await v(d,D,N,a,"platform",e,{orgConfig:R,primaryRegion:O})}catch(c){const r=C(J(c));l.push({accountId:a.id,error:r}),e.onCascadePhaseComplete?.("platform"),e.onStepComplete?.(t,o,"error",i,p),s=U(new Error(r))}const k=Date.now()-I;if(s.success){P=!0;const c=s.data.skipped===!0;c||(S=!0),s.data.outputs&&b.push({accountId:a.id,outputs:s.data.outputs}),e.onCascadePhaseComplete?.("platform"),e.onStepComplete?.(t,o,c?"skipped":"completed",i,p),g={accountId:a.id,result:c?"skipped":"succeeded",durationMs:k}}else l.some(c=>c.accountId===a.id)||(l.push({accountId:a.id,error:C(s.error.message)}),e.onCascadePhaseComplete?.("platform"),e.onStepComplete?.(t,o,"error",i,p)),g={accountId:a.id,result:"failed",durationMs:k,error:C(s.error.message)};i++}let _=new Map;if(P&&a&&(_=await $(D,a,e,d.abortSignal)),d.domainProvider){const t=await X(d.domainProvider,e);w=t.domainsDeployed>0,w&&(S=!0);for(const o of t.errors)l.push({accountId:"domains",error:C(o)})}if(a!==void 0&&!P&&A.length>0){const{id:t,name:o}=E.CASCADE_ACCOUNTS;e.onStepStart?.(t,o,i,p),e.onStepComplete?.(t,o,"skipped",i,p),e.onLog?.("Skipping account cascade \u2014 platform deployment failed; platform is a prerequisite for member accounts.","warn")}else if(A.length>0){const{id:t,name:o}=E.CASCADE_ACCOUNTS;e.onStepStart?.(t,o,i,p),e.onCascadePhaseStart?.("accounts");const s=Y(d.orgConfig),I=z(A,s);(await K(I,G,async({account:r,region:y})=>{const m=_.get(`${r.id}-${j(y)}`),n=Date.now();return{result:await v(d,D,N,r,"account",e,{ipamPoolId:m,orgConfig:R,region:y,primaryRegion:O}),durationMs:Date.now()-n}})).forEach((r,y)=>{const m=I[y];if(!m)return;const n=m.account;if(r.status==="rejected"){const u=C(r.reason instanceof Error?r.reason.message:String(r.reason));h.push({accountId:n.id,accountName:n.name,region:m.region,result:"failed",durationMs:0,error:u}),l.push({accountId:n.id,error:u});return}const{result:f,durationMs:L}=r.value;if(f.success){const u=f.data.skipped===!0;u||(S=!0),f.data.outputs&&b.push({accountId:n.id,outputs:f.data.outputs}),h.push({accountId:n.id,accountName:n.name,region:m.region,result:u?"skipped":"succeeded",durationMs:L})}else{const u=C(f.error.message);h.push({accountId:n.id,accountName:n.name,region:m.region,result:"failed",durationMs:L,error:u}),l.push({accountId:n.id,error:u})}});const c=x(M(0));e.onCascadePhaseComplete?.("accounts"),e.onStepComplete?.(t,o,c.accountsFailed>0?"error":c.accountsSkipped===A.length?"skipped":"completed",i,p)}const F=M(Date.now()-H);return e.onCascadeComplete?.(x(F)),e.onCascadeLedger?.(F),{anyCascadeDeployHappened:S}}export{re as executeCascade};
|