@fjall/deploy-core 2.27.0 → 2.29.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/organisations/backup.d.ts +7 -2
- package/dist/src/aws/organisations/backup.js +2 -2
- package/dist/src/aws/organisations/index.d.ts +1 -1
- package/dist/src/aws/organisations/index.js +1 -1
- package/dist/src/aws/organisations/oidcProvider.d.ts +37 -0
- package/dist/src/aws/organisations/oidcProvider.js +1 -1
- package/dist/src/aws/organisations/organisation.d.ts +21 -0
- package/dist/src/aws/organisations/organisation.js +1 -1
- package/dist/src/aws/utils/cloudformationEventHelpers.d.ts +45 -0
- package/dist/src/aws/utils/cloudformationEventHelpers.js +1 -1
- package/dist/src/aws/utils/cloudformationEventTypes.d.ts +1 -1
- package/dist/src/aws/utils/cloudformationEvents.d.ts +2 -1
- package/dist/src/aws/utils/cloudformationEvents.js +1 -1
- package/dist/src/aws/utils/stackStatus.js +1 -1
- package/dist/src/index.d.ts +12 -1
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/activeDeploymentGuard.d.ts +41 -0
- package/dist/src/orchestration/activeDeploymentGuard.js +8 -4
- package/dist/src/orchestration/application/applicationDeploy.d.ts +9 -0
- package/dist/src/orchestration/application/applicationDeploy.js +1 -1
- package/dist/src/orchestration/application/applicationDeployHelpers.d.ts +20 -3
- package/dist/src/orchestration/application/applicationDeployHelpers.js +3 -3
- package/dist/src/orchestration/application/applicationDestroy.js +1 -1
- package/dist/src/orchestration/application/approvalGate.d.ts +15 -1
- package/dist/src/orchestration/application/approvalGate.js +1 -1
- package/dist/src/orchestration/application/destructionGate.d.ts +164 -0
- package/dist/src/orchestration/application/destructionGate.js +5 -0
- package/dist/src/orchestration/application/leaseGate.d.ts +30 -0
- package/dist/src/orchestration/application/leaseGate.js +1 -0
- package/dist/src/orchestration/application/plan/approvalToken.d.ts +15 -0
- package/dist/src/orchestration/application/plan/approvalToken.js +1 -1
- package/dist/src/orchestration/application/plan/changeSetEscalation.d.ts +46 -0
- package/dist/src/orchestration/application/plan/changeSetEscalation.js +1 -0
- package/dist/src/orchestration/application/plan/computeDeployPlan.d.ts +13 -0
- package/dist/src/orchestration/application/plan/computeDeployPlan.js +1 -1
- package/dist/src/orchestration/application/plan/index.d.ts +4 -1
- package/dist/src/orchestration/application/plan/index.js +1 -1
- package/dist/src/orchestration/application/plan/loadDeployPlan.d.ts +32 -0
- package/dist/src/orchestration/application/plan/loadDeployPlan.js +1 -0
- package/dist/src/orchestration/application/plan/pinnedNames.d.ts +31 -0
- package/dist/src/orchestration/application/plan/pinnedNames.js +1 -0
- package/dist/src/orchestration/application/plan/registryOverlay.d.ts +83 -0
- package/dist/src/orchestration/application/plan/registryOverlay.js +1 -0
- package/dist/src/orchestration/application/plan/renderDeployPlan.d.ts +4 -1
- package/dist/src/orchestration/application/plan/renderDeployPlan.js +1 -1
- package/dist/src/orchestration/application/plan/types.d.ts +31 -0
- package/dist/src/orchestration/application/plan/types.js +1 -0
- package/dist/src/orchestration/contextHelpers.d.ts +37 -1
- package/dist/src/orchestration/contextHelpers.js +1 -1
- package/dist/src/orchestration/drift/classifyDriftFailure.d.ts +31 -0
- package/dist/src/orchestration/drift/classifyDriftFailure.js +1 -0
- package/dist/src/orchestration/drift/detectStackDrift.d.ts +37 -0
- package/dist/src/orchestration/drift/detectStackDrift.js +1 -0
- package/dist/src/orchestration/drift/driftPreFlight.d.ts +73 -0
- package/dist/src/orchestration/drift/driftPreFlight.js +7 -0
- package/dist/src/orchestration/drift/driftProbe.d.ts +113 -0
- package/dist/src/orchestration/drift/driftProbe.js +1 -0
- package/dist/src/orchestration/drift/driftSuspectJournal.d.ts +40 -0
- package/dist/src/orchestration/drift/driftSuspectJournal.js +1 -0
- package/dist/src/orchestration/drift/preFlightGate.d.ts +44 -0
- package/dist/src/orchestration/drift/preFlightGate.js +2 -0
- package/dist/src/orchestration/index.d.ts +19 -0
- package/dist/src/orchestration/index.js +1 -1
- package/dist/src/orchestration/organisation/cascadeDestroyHelpers.d.ts +7 -0
- package/dist/src/orchestration/organisation/cascadeDestroyHelpers.js +1 -1
- package/dist/src/orchestration/organisation/cascadeHelpers.js +1 -1
- package/dist/src/orchestration/organisation/organisationDestroy.js +3 -3
- package/dist/src/orchestration/organisationDeploy/infraSteps.d.ts +8 -1
- package/dist/src/orchestration/organisationDeploy/infraSteps.js +1 -1
- package/dist/src/orchestration/organisationDeploy/orgCascadeDeploy.js +5 -5
- package/dist/src/orchestration/organisationDeploy/orgContext.d.ts +21 -2
- package/dist/src/orchestration/organisationDeploy/orgContext.js +1 -1
- package/dist/src/orchestration/organisationDeploy/singleComponentDeploy.js +1 -1
- package/dist/src/orchestration/remediation/driftRepairGate.d.ts +66 -0
- package/dist/src/orchestration/remediation/driftRepairGate.js +1 -0
- package/dist/src/orchestration/remediation/forensicsCapture.d.ts +49 -0
- package/dist/src/orchestration/remediation/forensicsCapture.js +1 -0
- package/dist/src/orchestration/remediation/forgetResource.d.ts +423 -0
- package/dist/src/orchestration/remediation/forgetResource.js +1 -0
- package/dist/src/orchestration/remediation/pinRemediation.d.ts +121 -0
- package/dist/src/orchestration/remediation/pinRemediation.js +1 -0
- package/dist/src/orchestration/remediation/postFailureRepairOffer.d.ts +77 -0
- package/dist/src/orchestration/remediation/postFailureRepairOffer.js +14 -0
- package/dist/src/orchestration/remediation/recreatePreFlight.d.ts +47 -0
- package/dist/src/orchestration/remediation/recreatePreFlight.js +1 -0
- package/dist/src/orchestration/remediation/recreateResource.d.ts +110 -0
- package/dist/src/orchestration/remediation/recreateResource.js +1 -0
- package/dist/src/orchestration/remediation/remediationJournal.d.ts +88 -0
- package/dist/src/orchestration/remediation/remediationJournal.js +2 -0
- package/dist/src/orchestration/remediation/removalUpdate.d.ts +135 -0
- package/dist/src/orchestration/remediation/removalUpdate.js +1 -0
- package/dist/src/orchestration/remediation/retainFlip.d.ts +294 -0
- package/dist/src/orchestration/remediation/retainFlip.js +1 -0
- package/dist/src/services/application/ApplicationStackService.d.ts +5 -3
- package/dist/src/services/application/ApplicationStackService.js +1 -1
- package/dist/src/services/application/applicationStackHelpers.d.ts +8 -6
- package/dist/src/services/application/applicationStackHelpers.js +3 -3
- package/dist/src/services/index.d.ts +2 -2
- package/dist/src/services/index.js +1 -1
- package/dist/src/services/infrastructure/CdkEventMonitoring.d.ts +15 -0
- package/dist/src/services/infrastructure/CdkEventMonitoring.js +1 -1
- package/dist/src/services/infrastructure/CdkOutputAnalyser.d.ts +8 -0
- package/dist/src/services/infrastructure/CdkOutputAnalyser.js +2 -1
- package/dist/src/services/infrastructure/CdkService.js +3 -3
- package/dist/src/services/infrastructure/CfnRegistryService.d.ts +68 -0
- package/dist/src/services/infrastructure/CfnRegistryService.js +1 -0
- package/dist/src/services/infrastructure/CloudFormationService.d.ts +5 -2
- package/dist/src/services/infrastructure/CloudFormationService.js +1 -1
- package/dist/src/services/infrastructure/EcsServiceResolver.d.ts +23 -3
- package/dist/src/services/infrastructure/EcsServiceResolver.js +1 -1
- package/dist/src/services/infrastructure/cfnRegistrySeed.d.ts +10 -0
- package/dist/src/services/infrastructure/cfnRegistrySeed.js +1 -0
- package/dist/src/services/infrastructure/changeSetProbe.d.ts +88 -0
- package/dist/src/services/infrastructure/changeSetProbe.js +1 -0
- package/dist/src/services/infrastructure/index.d.ts +2 -0
- package/dist/src/services/infrastructure/index.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 +22 -0
- package/dist/src/types/config/entitlements.d.ts +9 -0
- package/dist/src/types/config/orgConfig.d.ts +6 -0
- package/dist/src/types/deploymentEventSchema.d.ts +1 -0
- package/dist/src/types/deploymentEventSchema.js +1 -1
- package/dist/src/types/destruction.d.ts +212 -0
- package/dist/src/types/destruction.js +1 -0
- package/dist/src/types/drift.d.ts +72 -0
- package/dist/src/types/drift.js +1 -0
- package/dist/src/types/index.d.ts +8 -2
- package/dist/src/types/index.js +1 -1
- package/dist/src/types/params.d.ts +142 -0
- package/dist/src/types/remediation.d.ts +126 -0
- package/dist/src/types/remediation.js +1 -0
- package/package.json +5 -4
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent drift-suspect journal (drift-remediation plan § Phase 5 item 4).
|
|
3
|
+
* When a deploy fails with out-of-band-deletion suspects, the engine records
|
|
4
|
+
* them here so the [G2] stateless pre-flight can re-probe on the NEXT deploy
|
|
5
|
+
* without new baseline calls on healthy deploys. One JSON file per
|
|
6
|
+
* account/region/stack under `~/.fjall/drift/`; writes are atomic
|
|
7
|
+
* (tmp + rename) against concurrent deploys sharing the directory.
|
|
8
|
+
*
|
|
9
|
+
* The journal is a hint, never an authority: reads fail soft (a corrupt or
|
|
10
|
+
* schema-drifted file reads as "no suspects" at debug level), and a
|
|
11
|
+
* journalled suspect still goes through the probe before any verdict.
|
|
12
|
+
* Suspect `statusReason`s are pre-masked at construction upstream.
|
|
13
|
+
*/
|
|
14
|
+
import { type DriftJournalRecord, type DriftSuspect } from "../../types/drift.js";
|
|
15
|
+
export interface DriftJournalOptions {
|
|
16
|
+
/** Override the journal root — tests point this at a temp dir. */
|
|
17
|
+
journalDir?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function defaultDriftJournalDir(): string;
|
|
20
|
+
export interface RecordDriftSuspectsParams {
|
|
21
|
+
accountId: string;
|
|
22
|
+
region: string;
|
|
23
|
+
stackName: string;
|
|
24
|
+
/** ISO-8601 timestamp of the failure that produced the suspects. */
|
|
25
|
+
recordedAt: string;
|
|
26
|
+
suspects: readonly DriftSuspect[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Persist a stack's suspects (atomic tmp + rename). Best-effort by contract:
|
|
30
|
+
* failures are logged at debug and swallowed — journalling must never turn a
|
|
31
|
+
* deploy failure into a different failure.
|
|
32
|
+
*/
|
|
33
|
+
export declare function recordDriftSuspects(params: RecordDriftSuspectsParams, options?: DriftJournalOptions): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Read a stack's journalled suspects. Missing, corrupt, or schema-drifted
|
|
36
|
+
* files all read as `null` (debug-logged) — the journal is a hint.
|
|
37
|
+
*/
|
|
38
|
+
export declare function readDriftSuspects(accountId: string, region: string, stackName: string, options?: DriftJournalOptions): Promise<DriftJournalRecord | null>;
|
|
39
|
+
/** Remove a stack's journal entry (after a clean probe or deploy). */
|
|
40
|
+
export declare function clearDriftSuspects(accountId: string, region: string, stackName: string, options?: DriftJournalOptions): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{mkdir as h,readFile as D,rename as S,rm as y,writeFile as w}from"node:fs/promises";import{homedir as b}from"node:os";import{dirname as J,join as m}from"node:path";import{getErrorMessage as f,maskSensitiveOutput as l}from"@fjall/util";import{logger as i}from"@fjall/util/logger";import{DriftJournalRecordSchema as N}from"../../types/drift.js";const c="DriftJournal";function d(){return m(b(),".fjall","drift")}const k=/^[A-Za-z0-9][A-Za-z0-9-]*$/;function p(r,n,e,o){for(const[u,t]of[["accountId",n],["region",e],["stackName",o]])if(!k.test(t))return i.debug(c,"Refusing drift journal path for unsafe key segment",{label:u,value:l(t)}),null;return m(r,n,e,`${o}.json`)}async function $(r,n){const e=n?.journalDir??d(),o=p(e,r.accountId,r.region,r.stackName);if(o===null)return;const u={version:1,accountId:r.accountId,region:r.region,stackName:r.stackName,recordedAt:r.recordedAt,suspects:[...r.suspects]};try{await h(J(o),{recursive:!0});const t=`${o}.tmp-${process.pid}`;await w(t,JSON.stringify(u,null,2),"utf8"),await S(t,o)}catch(t){i.debug(c,"Failed to record drift suspects",{stackName:r.stackName,error:l(f(t))})}}async function I(r,n,e,o){const u=o?.journalDir??d(),t=p(u,r,n,e);if(t===null)return null;let a;try{a=await D(t,"utf8")}catch{return null}let g;try{g=JSON.parse(a)}catch(j){return i.debug(c,"Corrupt drift journal ignored",{stackName:e,error:l(f(j))}),null}const s=N.safeParse(g);return s.success?s.data:(i.debug(c,"Schema-drifted drift journal ignored",{stackName:e,error:s.error.message}),null)}async function P(r,n,e,o){const u=o?.journalDir??d(),t=p(u,r,n,e);t!==null&&await y(t,{force:!0}).catch(a=>{i.debug(c,"Failed to clear drift journal",{stackName:e,error:l(f(a))})})}export{P as clearDriftSuspects,d as defaultDriftJournalDir,I as readDriftSuspects,$ as recordDriftSuspects};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared [G2] pre-flight gate — the deploy-path wrapper around
|
|
3
|
+
* `runDriftPreFlight` + `partitionPreFlightByRemediation`. The application
|
|
4
|
+
* path and the single-component (platform/account) path both run this gate
|
|
5
|
+
* between synth and the CFN update; the converge/recreate warns and the
|
|
6
|
+
* block message are user-facing [G2] copy that must stay byte-identical
|
|
7
|
+
* across the two surfaces, so the wrapper owns them once.
|
|
8
|
+
*
|
|
9
|
+
* Fail-open on probe infrastructure errors (the runDriftPreFlight contract):
|
|
10
|
+
* in the FINDING flow only a CONFIRMED out-of-band deletion with no covering
|
|
11
|
+
* remediation blocks. The flipless-by-necessity absence re-verification
|
|
12
|
+
* (`verifyByNecessityBeforeConverge`) is the exception and fails CLOSED —
|
|
13
|
+
* those targets carry no Retain policy, so a converge that cannot re-prove
|
|
14
|
+
* their absence would issue a real physical delete. Each caller renders the
|
|
15
|
+
* blocked outcome in its own failure idiom — the message is identifier-only
|
|
16
|
+
* (stack, logical ID, resource type, physical ID) and pre-masked by
|
|
17
|
+
* construction.
|
|
18
|
+
*/
|
|
19
|
+
import type { AwsProvider } from "../../aws/AwsProvider.js";
|
|
20
|
+
import type { CloudFormationService } from "../../services/infrastructure/CloudFormationService.js";
|
|
21
|
+
import type { DeployCallbacks } from "../../types/callbacks.js";
|
|
22
|
+
import { type ByNecessityConvergeTarget, type ForgetOrchestrationOptions } from "../remediation/forgetResource.js";
|
|
23
|
+
export interface DriftPreFlightGateParams {
|
|
24
|
+
stackNames: readonly string[];
|
|
25
|
+
/** cdk.out assembly dir for the synth-omission converge check. */
|
|
26
|
+
cdkOutPath: string;
|
|
27
|
+
cfnService: Pick<CloudFormationService, "getStackStatus">;
|
|
28
|
+
callbacks: Pick<DeployCallbacks, "onLog">;
|
|
29
|
+
abortSignal?: AbortSignal;
|
|
30
|
+
/** Journal/clock injection for the remediation partition and the
|
|
31
|
+
* by-necessity re-verification (tests; production uses the defaults). */
|
|
32
|
+
remediationOptions?: ForgetOrchestrationOptions;
|
|
33
|
+
}
|
|
34
|
+
export type DriftPreFlightGateOutcome = {
|
|
35
|
+
kind: "proceed";
|
|
36
|
+
/** Flipless-by-necessity targets re-proven absent against their owning
|
|
37
|
+
* service by THIS pre-flight — the destruction gate consumes this to
|
|
38
|
+
* suppress their already-consented, physically-no-op delete rows. */
|
|
39
|
+
verifiedAbsentByNecessity?: ByNecessityConvergeTarget[];
|
|
40
|
+
} | {
|
|
41
|
+
kind: "blocked";
|
|
42
|
+
message: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function runDriftPreFlightGate(aws: AwsProvider, params: DriftPreFlightGateParams): Promise<DriftPreFlightGateOutcome>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{getErrorMessage as g,maskSensitiveOutput as h}from"@fjall/util";import{logger as u}from"@fjall/util/logger";import{DriftProbe as m}from"./driftProbe.js";import{formatDriftPreFlightBlock as p,runDriftPreFlight as y}from"./driftPreFlight.js";import{formatRecreateInFlightCures as b,partitionPreFlightByRemediation as k,synthTemplateDeclaresResource as v,verifyByNecessityBeforeConverge as w}from"../remediation/forgetResource.js";function N(r){const t=r.map(o=>` - ${o.stackName} \u203A ${o.logicalId} (operation ${o.opId}): ${o.reason}`);return[`Deploy halted: ${r.length} resource(s) from a forget remediation cannot be safely removed by this deploy.`,...t,"Each was verified deleted when the forget was consented, but carries no Retain policy (the policy change was structurally impossible \u2014 flipless by necessity), so this deploy's removal would issue a real physical delete. fjall therefore re-verifies absence against the owning service at every converge, and could not confirm it here. If the resource was recreated deliberately, keep it by re-declaring it in the fjall configuration (CloudFormation keeps tracking it). If it should still be removed, delete it against the owning service first, then re-run the deploy. Nothing was mutated."].join(`
|
|
2
|
+
`)}async function P(r,t){const o=await y({aws:r,probe:new m(r)},{stackNames:t.stackNames,cfnService:t.cfnService,...t.abortSignal!==void 0?{abortSignal:t.abortSignal}:{}}).catch(e=>(u.debug("DriftPreFlightGate","Pre-flight probe threw \u2014 failing open",{error:h(g(e))}),null)),{confirmed:l,unconfirmed:d}=o!==null&&o.success?o.data:{confirmed:[],unconfirmed:[]},c={aws:r,synthDeclaresTarget:(e,i)=>v(t.cdkOutPath,e,i),...t.abortSignal!==void 0?{abortSignal:t.abortSignal}:{}};if(l.length>0){const e=await k(c,l,t.remediationOptions);for(const{finding:i,opId:s}of e.converging)t.callbacks.onLog?.(`Forget remediation ${s} is mid-surgery for ${i.stackName} \u203A ${i.logicalId} \u2014 this deploy is the converge deploy and will complete it`,"warn");for(const{finding:i,opId:s,appName:f}of e.recreateConverging)t.callbacks.onLog?.(`Recreate remediation ${s} covers ${i.stackName} \u203A ${i.logicalId} \u2014 the physical resource was deleted outside CloudFormation. This deploy re-runs the surgery only if the plan still requires the pinned-name replacement (re-consent when prompted). If the deploy completes without re-creating it, the operation stays open \u2014 ${b(f)}`,"warn");if(e.blocked.length>0)return{kind:"blocked",message:p(e.blocked,e.inProgressOpIds,{flipNotLive:e.flipNotLive,flipUnverified:e.flipUnverified,stillDeclared:e.stillDeclared})}}for(const e of d)t.callbacks.onLog?.(`Possible out-of-band deletion (unconfirmed): ${e.stackName} \u203A ${e.logicalId} \u2014 ${e.verdictDetail}`,"warn");const a=new Map;for(const e of l){const i=a.get(e.stackName)??new Set;i.add(e.logicalId),a.set(e.stackName,i)}const n=await w(c,t.stackNames,a,t.remediationOptions);return n.blocked.length>0?{kind:"blocked",message:N(n.blocked)}:{kind:"proceed",...n.verifiedAbsent.length>0?{verifiedAbsentByNecessity:n.verifiedAbsent}:{}}}export{P as runDriftPreFlightGate};
|
|
@@ -49,3 +49,22 @@ export * from "./builders/index.js";
|
|
|
49
49
|
export { buildDeployPlan, computeDeployPlan, computeAssemblyDigest, digestHead, classifyImpact, derivePropertyChanges, isDataLoss, isStatefulResourceType, renderPlanSummary, renderPlanLines, toWirePlanChanges, signApprovalToken, verifyApprovalToken, DEFAULT_APPROVAL_TTL_MS, APPROVAL_TOKEN_PATTERN } from "./application/plan/index.js";
|
|
50
50
|
export type { DeployPlan, DeployPlanResourceChange, StackTemplatePair, CfnTemplateReader, ComputeDeployPlanParams, ComputeDeployPlanStack, ImpactClassification, SignApprovalTokenOptions, SignedApprovalToken, VerifyApprovalTokenOptions, VerifyApprovalTokenResult } from "./application/plan/index.js";
|
|
51
51
|
export { runApprovalGate, type RunApprovalGateParams, type ApprovalGateOutcome } from "./application/approvalGate.js";
|
|
52
|
+
export { runDestructionGate, buildDestructionTicket, evaluateDestructionConsents, computeTicketDigest, legalRemediationVerbs, executableRemediationVerbs, advertisableRemediationVerbs, renderDestructionTicketLines, renderConsentVerdictLines, type RunDestructionGateParams, type DestructionGateOutcome, type EvaluateDestructionConsentsParams } from "./application/destructionGate.js";
|
|
53
|
+
export { loadDeployPlan, createMemoisedPlanLoader, type LoadDeployPlanParams, type DeployPlanLoader } from "./application/plan/loadDeployPlan.js";
|
|
54
|
+
export { classifyDriftFailure, fetchLastOperationEvents } from "./drift/classifyDriftFailure.js";
|
|
55
|
+
export { DriftProbe, type DriftProbeOptions, type DriftProbeRunner, type ProbeDriftSuspectsParams } from "./drift/driftProbe.js";
|
|
56
|
+
export { clearDriftSuspects, defaultDriftJournalDir, readDriftSuspects, recordDriftSuspects, type DriftJournalOptions, type RecordDriftSuspectsParams } from "./drift/driftSuspectJournal.js";
|
|
57
|
+
export { detectStackDrift, type DetectStackDriftDeps, type DetectStackDriftParams, type StackDriftReport } from "./drift/detectStackDrift.js";
|
|
58
|
+
export { runDriftPreFlight, formatDriftPreFlightBlock, DRIFT_PREFLIGHT_TRIGGER_STATUSES, DRIFT_PREFLIGHT_BUDGET_MS, type RunDriftPreFlightParams, type DriftPreFlightBlockExtras, type DriftPreFlightOutcome } from "./drift/driftPreFlight.js";
|
|
59
|
+
export { archiveCompletedRemediationJournal, computeRemediationOpId, defaultRemediationJournalDir, findRemediationJournalByOpId, listActiveRemediationOps, listActiveRemediationOpsForContext, readRemediationJournal, sweepExpiredRemediationJournals, writeRemediationJournal, REMEDIATION_JOURNAL_RETENTION_DAYS, type RemediationJournalContext, type RemediationJournalOptions } from "./remediation/remediationJournal.js";
|
|
60
|
+
export { captureRemediationForensics, defaultRemediationForensicsDir, type CaptureRemediationForensicsParams, type ForensicsCaptureOptions, type RemediationForensicsBundle } from "./remediation/forensicsCapture.js";
|
|
61
|
+
export { applyRetainFlip, composeFlipCapabilities, computeTemplateDigest, deriveRetainFlipTemplate, findMarkedResourcesByOpId, proveFlipMetadataOnly, readRetainFlipState, verifyRetainFlipDiff, FORGET_MARKER_KEY, FORGET_FLIP_SPEC, RECREATE_MARKER_KEY, type ApplyRetainFlipOutcome, type ApplyRetainFlipParams, type DeriveRetainFlipParams, type FlipDeletionPolicy, type FlipProofResult, type FlipSpec, type RetainFlipDerivation, type RetainFlipState } from "./remediation/retainFlip.js";
|
|
62
|
+
export { assessForgetResumability, completeForgetAfterConverge, completeForgetAfterDeploy, formatRecreateInFlightCures, partitionPreFlightByRemediation, runForgetSurgery, synthTemplateDeclaresResource, type CompleteForgetAfterDeployParams, type CompleteForgetOutcome, type CompleteForgetParams, type ForeignMarkerOperation, type ForgetOrchestrationOptions, type ForgetResumeAssessment, type ForgetResumeSource, type ForgetSurgeryParams, type ForgetSurgeryReport, type PreFlightRemediationDeps, type PreFlightRemediationPartition } from "./remediation/forgetResource.js";
|
|
63
|
+
export { runRecreateSurgery, snapshotPolicyForType, SNAPSHOT_CAPABLE_RESOURCE_TYPES, type RecreateOrchestrationOptions, type RecreateSurgeryParams, type RecreateSurgeryReport } from "./remediation/recreateResource.js";
|
|
64
|
+
export { runRecreatePreFlight, type RecreatePreFlightStack, type RunRecreatePreFlightParams } from "./remediation/recreatePreFlight.js";
|
|
65
|
+
export { deriveRemovalTemplate, findRemovalDeletionFailures, proveRemovalTargetsOnly, type ProveRemovalTargetsOnlyParams, type RemovalDeletionFailure, type RemovalDerivation, type RemovalProofResult, type StrippedExport } from "./remediation/removalUpdate.js";
|
|
66
|
+
export { advertisableDriftVerbs, buildDriftRepairTicket, runDriftRepairGate, type DriftRepairGateOutcome, type RunDriftRepairGateParams } from "./remediation/driftRepairGate.js";
|
|
67
|
+
export { formatPinOutcomeDetail, pinProbeCoverage, renderPinReportLines, runPinRemediation, type PinnedProperty, type PinProbeCoverage, type PinRemediationReport, type PinReportOnlyReason, type PinResourceOutcome, type PinTarget, type RunPinRemediationParams } from "./remediation/pinRemediation.js";
|
|
68
|
+
export { composePostFailureRepairOffer, formatDriftRepairOffer, DriftRepairAvailableError, type ComposePostFailureRepairOfferParams } from "./remediation/postFailureRepairOffer.js";
|
|
69
|
+
export { checkDeployPathStackAvailability, isDeployPathBlockingStatus, StackUnavailableError } from "./activeDeploymentGuard.js";
|
|
70
|
+
export { assertLeaseBeforeStack, LeaseDeniedError } from "./application/leaseGate.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{deploy as o}from"./deploy.js";import{destroy as i}from"./destroy.js";import{restart as a,probeSecretsDrift as n}from"./restart/restartApplication.js";import{computeSecretsDrift as l,deriveStaleParameters as m,extractConsumedSsmParameters as u,ssmParameterNameFromValueFrom as p}from"./restart/secretsDrift.js";import{deployOrganisation as d}from"./organisation/organisationDeploy.js";import{destroyOrganisation as A}from"./organisation/organisationDestroy.js";import{cleanupFailedStack as T,emptyS3Bucket as P,preEmptyStackBuckets as _,formatQuarantineSuspectedMessage as y,formatRetainedBucketsMessage as g,isQuarantineDetail as E,isRetainedBucketsDetail as R,PRE_EMPTY_TAG_KEYS as B,isCleanableState as O,SAFE_CLEANUP_STATES as k}from"./stackCleanup.js";import{partitionAccounts as U,buildRegionList as C,buildAccountRegionPairs as L,cascadeHomeRegion as D,cascadeOperationKey as K}from"./organisation/cascadeHelpers.js";import{projectScalarSummary as b,projectAccountRows as F}from"./organisation/cascadeSummary.js";import{reconcileProviderAccounts as M,mergeReconciledProviderAccounts as G}from"./organisation/reconcileProviderAccounts.js";import{decideNextTransition as Q,reconcileTrailMigration as V,ORG_TRAIL_BUCKET_OUTPUT_KEY as h,TRAIL_BUCKET_OUTPUT_KEY as H,TRAIL_KEY_ARN_OUTPUT_KEY as j}from"./trailMigration/trailMigration.js";import{decommissionMemberTrailStorage as W}from"./trailMigration/memberTrailCleanup.js";import{unlockBucket as z}from"./unlock/unlockBucket.js";import{unlockQueue as X}from"./unlock/unlockQueue.js";import{triageBucketPolicy as $,isEnforceSslStatement as ee}from"./unlock/bucketPolicyTriage.js";import{toResourcePolicyStatements as oe}from"./unlock/toResourcePolicyStatements.js";import{restoreBucketPolicy as ie,synthesiseEnforceSslDocument as se,ensureEnforceSsl as ae}from"./unlock/restoreBucketPolicy.js";import{restoreAndReconcileQuarantinedBucket as ce}from"./unlock/restoreAndReconcileQuarantinedBucket.js";import{parseAccountsConfiguration as me,flattenAccountsToEnvironments as ue,extractAllAccountNames as pe,accountsConfigToOUTree as fe,isStringArray as de,isAccountsConfig as Se,isOuOnlyAccountBucket as Ae,OU_ONLY_ACCOUNT_BUCKETS as xe}from"./organisation/accountsConfig.js";import{resolveBuildSecrets as Pe,resolveSecretRefValue as _e,resolveBuildSecretSessionProvider as ye,sourcedRefsFromBuildArgs as ge}from"./application/buildSecretResolver.js";import{buildBuildSecretSessionPolicy as Re,partitionForRegion as Be}from"./application/buildSecretSession.js";import{buildDeploySessionPolicy as ke}from"./application/deploySessionPolicy.js";import{resolveBuildArgs as Ue}from"./application/buildArgResolver.js";import{validateBuildGroup as Le}from"./application/buildGroupValidator.js";import{runOpenNextBuild as Ke}from"./application/openNextBuild.js";import{runOrganisationSetup as be,ORG_SETUP_PHASES as Fe}from"./organisation/organisationSetup.js";export*from"./builders/index.js";import{buildDeployPlan as Ge,computeDeployPlan as Ie,computeAssemblyDigest as Qe,digestHead as Ve,classifyImpact as he,derivePropertyChanges as He,isDataLoss as je,isStatefulResourceType as we,renderPlanSummary as We,renderPlanLines as qe,toWirePlanChanges as ze,signApprovalToken as Je,verifyApprovalToken as Xe,DEFAULT_APPROVAL_TTL_MS as Ze,APPROVAL_TOKEN_PATTERN as $e}from"./application/plan/index.js";import{runApprovalGate as rr}from"./application/approvalGate.js";export{$e as APPROVAL_TOKEN_PATTERN,Ze as DEFAULT_APPROVAL_TTL_MS,Fe as ORG_SETUP_PHASES,h as ORG_TRAIL_BUCKET_OUTPUT_KEY,xe as OU_ONLY_ACCOUNT_BUCKETS,B as PRE_EMPTY_TAG_KEYS,k as SAFE_CLEANUP_STATES,H as TRAIL_BUCKET_OUTPUT_KEY,j as TRAIL_KEY_ARN_OUTPUT_KEY,fe as accountsConfigToOUTree,L as buildAccountRegionPairs,Re as buildBuildSecretSessionPolicy,Ge as buildDeployPlan,ke as buildDeploySessionPolicy,C as buildRegionList,D as cascadeHomeRegion,K as cascadeOperationKey,he as classifyImpact,T as cleanupFailedStack,Qe as computeAssemblyDigest,Ie as computeDeployPlan,l as computeSecretsDrift,Q as decideNextTransition,W as decommissionMemberTrailStorage,o as deploy,d as deployOrganisation,He as derivePropertyChanges,m as deriveStaleParameters,i as destroy,A as destroyOrganisation,Ve as digestHead,P as emptyS3Bucket,ae as ensureEnforceSsl,pe as extractAllAccountNames,u as extractConsumedSsmParameters,ue as flattenAccountsToEnvironments,y as formatQuarantineSuspectedMessage,g as formatRetainedBucketsMessage,Se as isAccountsConfig,O as isCleanableState,je as isDataLoss,ee as isEnforceSslStatement,Ae as isOuOnlyAccountBucket,E as isQuarantineDetail,R as isRetainedBucketsDetail,we as isStatefulResourceType,de as isStringArray,G as mergeReconciledProviderAccounts,me as parseAccountsConfiguration,U as partitionAccounts,Be as partitionForRegion,_ as preEmptyStackBuckets,n as probeSecretsDrift,F as projectAccountRows,b as projectScalarSummary,M as reconcileProviderAccounts,V as reconcileTrailMigration,qe as renderPlanLines,We as renderPlanSummary,Ue as resolveBuildArgs,ye as resolveBuildSecretSessionProvider,Pe as resolveBuildSecrets,_e as resolveSecretRefValue,a as restart,ce as restoreAndReconcileQuarantinedBucket,ie as restoreBucketPolicy,rr as runApprovalGate,Ke as runOpenNextBuild,be as runOrganisationSetup,Je as signApprovalToken,ge as sourcedRefsFromBuildArgs,p as ssmParameterNameFromValueFrom,se as synthesiseEnforceSslDocument,oe as toResourcePolicyStatements,ze as toWirePlanChanges,$ as triageBucketPolicy,z as unlockBucket,X as unlockQueue,Le as validateBuildGroup,Xe as verifyApprovalToken};
|
|
1
|
+
import{deploy as t}from"./deploy.js";import{destroy as i}from"./destroy.js";import{restart as n,probeSecretsDrift as s}from"./restart/restartApplication.js";import{computeSecretsDrift as p,deriveStaleParameters as c,extractConsumedSsmParameters as m,ssmParameterNameFromValueFrom as u}from"./restart/secretsDrift.js";import{deployOrganisation as d}from"./organisation/organisationDeploy.js";import{destroyOrganisation as T}from"./organisation/organisationDestroy.js";import{cleanupFailedStack as x,emptyS3Bucket as P,preEmptyStackBuckets as A,formatQuarantineSuspectedMessage as E,formatRetainedBucketsMessage as D,isQuarantineDetail as y,isRetainedBucketsDetail as _,PRE_EMPTY_TAG_KEYS as g,isCleanableState as O,SAFE_CLEANUP_STATES as F}from"./stackCleanup.js";import{partitionAccounts as k,buildRegionList as B,buildAccountRegionPairs as b,cascadeHomeRegion as C,cascadeOperationKey as L}from"./organisation/cascadeHelpers.js";import{projectScalarSummary as I,projectAccountRows as h}from"./organisation/cascadeSummary.js";import{reconcileProviderAccounts as G,mergeReconciledProviderAccounts as K}from"./organisation/reconcileProviderAccounts.js";import{decideNextTransition as Y,reconcileTrailMigration as V,ORG_TRAIL_BUCKET_OUTPUT_KEY as J,TRAIL_BUCKET_OUTPUT_KEY as H,TRAIL_KEY_ARN_OUTPUT_KEY as Q}from"./trailMigration/trailMigration.js";import{decommissionMemberTrailStorage as j}from"./trailMigration/memberTrailCleanup.js";import{unlockBucket as q}from"./unlock/unlockBucket.js";import{unlockQueue as X}from"./unlock/unlockQueue.js";import{triageBucketPolicy as $,isEnforceSslStatement as ee}from"./unlock/bucketPolicyTriage.js";import{toResourcePolicyStatements as te}from"./unlock/toResourcePolicyStatements.js";import{restoreBucketPolicy as ie,synthesiseEnforceSslDocument as ae,ensureEnforceSsl as ne}from"./unlock/restoreBucketPolicy.js";import{restoreAndReconcileQuarantinedBucket as le}from"./unlock/restoreAndReconcileQuarantinedBucket.js";import{parseAccountsConfiguration as ce,flattenAccountsToEnvironments as me,extractAllAccountNames as ue,accountsConfigToOUTree as fe,isStringArray as de,isAccountsConfig as Re,isOuOnlyAccountBucket as Te,OU_ONLY_ACCOUNT_BUCKETS as Se}from"./organisation/accountsConfig.js";import{resolveBuildSecrets as Pe,resolveSecretRefValue as Ae,resolveBuildSecretSessionProvider as Ee,sourcedRefsFromBuildArgs as De}from"./application/buildSecretResolver.js";import{buildBuildSecretSessionPolicy as _e,partitionForRegion as ge}from"./application/buildSecretSession.js";import{buildDeploySessionPolicy as Fe}from"./application/deploySessionPolicy.js";import{resolveBuildArgs as ke}from"./application/buildArgResolver.js";import{validateBuildGroup as be}from"./application/buildGroupValidator.js";import{runOpenNextBuild as Le}from"./application/openNextBuild.js";import{runOrganisationSetup as Ie,ORG_SETUP_PHASES as he}from"./organisation/organisationSetup.js";export*from"./builders/index.js";import{buildDeployPlan as Ke,computeDeployPlan as Me,computeAssemblyDigest as Ye,digestHead as Ve,classifyImpact as Je,derivePropertyChanges as He,isDataLoss as Qe,isStatefulResourceType as we,renderPlanSummary as je,renderPlanLines as We,toWirePlanChanges as qe,signApprovalToken as ze,verifyApprovalToken as Xe,DEFAULT_APPROVAL_TTL_MS as Ze,APPROVAL_TOKEN_PATTERN as $e}from"./application/plan/index.js";import{runApprovalGate as rr}from"./application/approvalGate.js";import{runDestructionGate as or,buildDestructionTicket as ir,evaluateDestructionConsents as ar,computeTicketDigest as nr,legalRemediationVerbs as sr,executableRemediationVerbs as lr,advertisableRemediationVerbs as pr,renderDestructionTicketLines as cr,renderConsentVerdictLines as mr}from"./application/destructionGate.js";import{loadDeployPlan as fr,createMemoisedPlanLoader as dr}from"./application/plan/loadDeployPlan.js";import{classifyDriftFailure as Tr,fetchLastOperationEvents as Sr}from"./drift/classifyDriftFailure.js";import{DriftProbe as Pr}from"./drift/driftProbe.js";import{clearDriftSuspects as Er,defaultDriftJournalDir as Dr,readDriftSuspects as yr,recordDriftSuspects as _r}from"./drift/driftSuspectJournal.js";import{detectStackDrift as Or}from"./drift/detectStackDrift.js";import{runDriftPreFlight as vr,formatDriftPreFlightBlock as kr,DRIFT_PREFLIGHT_TRIGGER_STATUSES as Br,DRIFT_PREFLIGHT_BUDGET_MS as br}from"./drift/driftPreFlight.js";import{archiveCompletedRemediationJournal as Lr,computeRemediationOpId as Ur,defaultRemediationJournalDir as Ir,findRemediationJournalByOpId as hr,listActiveRemediationOps as Nr,listActiveRemediationOpsForContext as Gr,readRemediationJournal as Kr,sweepExpiredRemediationJournals as Mr,writeRemediationJournal as Yr,REMEDIATION_JOURNAL_RETENTION_DAYS as Vr}from"./remediation/remediationJournal.js";import{captureRemediationForensics as Hr,defaultRemediationForensicsDir as Qr}from"./remediation/forensicsCapture.js";import{applyRetainFlip as jr,composeFlipCapabilities as Wr,computeTemplateDigest as qr,deriveRetainFlipTemplate as zr,findMarkedResourcesByOpId as Xr,proveFlipMetadataOnly as Zr,readRetainFlipState as $r,verifyRetainFlipDiff as et,FORGET_MARKER_KEY as rt,FORGET_FLIP_SPEC as tt,RECREATE_MARKER_KEY as ot}from"./remediation/retainFlip.js";import{assessForgetResumability as at,completeForgetAfterConverge as nt,completeForgetAfterDeploy as st,formatRecreateInFlightCures as lt,partitionPreFlightByRemediation as pt,runForgetSurgery as ct,synthTemplateDeclaresResource as mt}from"./remediation/forgetResource.js";import{runRecreateSurgery as ft,snapshotPolicyForType as dt,SNAPSHOT_CAPABLE_RESOURCE_TYPES as Rt}from"./remediation/recreateResource.js";import{runRecreatePreFlight as St}from"./remediation/recreatePreFlight.js";import{deriveRemovalTemplate as Pt,findRemovalDeletionFailures as At,proveRemovalTargetsOnly as Et}from"./remediation/removalUpdate.js";import{advertisableDriftVerbs as yt,buildDriftRepairTicket as _t,runDriftRepairGate as gt}from"./remediation/driftRepairGate.js";import{formatPinOutcomeDetail as Ft,pinProbeCoverage as vt,renderPinReportLines as kt,runPinRemediation as Bt}from"./remediation/pinRemediation.js";import{composePostFailureRepairOffer as Ct,formatDriftRepairOffer as Lt,DriftRepairAvailableError as Ut}from"./remediation/postFailureRepairOffer.js";import{checkDeployPathStackAvailability as ht,isDeployPathBlockingStatus as Nt,StackUnavailableError as Gt}from"./activeDeploymentGuard.js";import{assertLeaseBeforeStack as Mt,LeaseDeniedError as Yt}from"./application/leaseGate.js";export{$e as APPROVAL_TOKEN_PATTERN,Ze as DEFAULT_APPROVAL_TTL_MS,br as DRIFT_PREFLIGHT_BUDGET_MS,Br as DRIFT_PREFLIGHT_TRIGGER_STATUSES,Pr as DriftProbe,Ut as DriftRepairAvailableError,tt as FORGET_FLIP_SPEC,rt as FORGET_MARKER_KEY,Yt as LeaseDeniedError,he as ORG_SETUP_PHASES,J as ORG_TRAIL_BUCKET_OUTPUT_KEY,Se as OU_ONLY_ACCOUNT_BUCKETS,g as PRE_EMPTY_TAG_KEYS,ot as RECREATE_MARKER_KEY,Vr as REMEDIATION_JOURNAL_RETENTION_DAYS,F as SAFE_CLEANUP_STATES,Rt as SNAPSHOT_CAPABLE_RESOURCE_TYPES,Gt as StackUnavailableError,H as TRAIL_BUCKET_OUTPUT_KEY,Q as TRAIL_KEY_ARN_OUTPUT_KEY,fe as accountsConfigToOUTree,yt as advertisableDriftVerbs,pr as advertisableRemediationVerbs,jr as applyRetainFlip,Lr as archiveCompletedRemediationJournal,Mt as assertLeaseBeforeStack,at as assessForgetResumability,b as buildAccountRegionPairs,_e as buildBuildSecretSessionPolicy,Ke as buildDeployPlan,Fe as buildDeploySessionPolicy,ir as buildDestructionTicket,_t as buildDriftRepairTicket,B as buildRegionList,Hr as captureRemediationForensics,C as cascadeHomeRegion,L as cascadeOperationKey,ht as checkDeployPathStackAvailability,Tr as classifyDriftFailure,Je as classifyImpact,x as cleanupFailedStack,Er as clearDriftSuspects,nt as completeForgetAfterConverge,st as completeForgetAfterDeploy,Wr as composeFlipCapabilities,Ct as composePostFailureRepairOffer,Ye as computeAssemblyDigest,Me as computeDeployPlan,Ur as computeRemediationOpId,p as computeSecretsDrift,qr as computeTemplateDigest,nr as computeTicketDigest,dr as createMemoisedPlanLoader,Y as decideNextTransition,j as decommissionMemberTrailStorage,Dr as defaultDriftJournalDir,Qr as defaultRemediationForensicsDir,Ir as defaultRemediationJournalDir,t as deploy,d as deployOrganisation,He as derivePropertyChanges,Pt as deriveRemovalTemplate,zr as deriveRetainFlipTemplate,c as deriveStaleParameters,i as destroy,T as destroyOrganisation,Or as detectStackDrift,Ve as digestHead,P as emptyS3Bucket,ne as ensureEnforceSsl,ar as evaluateDestructionConsents,lr as executableRemediationVerbs,ue as extractAllAccountNames,m as extractConsumedSsmParameters,Sr as fetchLastOperationEvents,Xr as findMarkedResourcesByOpId,hr as findRemediationJournalByOpId,At as findRemovalDeletionFailures,me as flattenAccountsToEnvironments,kr as formatDriftPreFlightBlock,Lt as formatDriftRepairOffer,Ft as formatPinOutcomeDetail,E as formatQuarantineSuspectedMessage,lt as formatRecreateInFlightCures,D as formatRetainedBucketsMessage,Re as isAccountsConfig,O as isCleanableState,Qe as isDataLoss,Nt as isDeployPathBlockingStatus,ee as isEnforceSslStatement,Te as isOuOnlyAccountBucket,y as isQuarantineDetail,_ as isRetainedBucketsDetail,we as isStatefulResourceType,de as isStringArray,sr as legalRemediationVerbs,Nr as listActiveRemediationOps,Gr as listActiveRemediationOpsForContext,fr as loadDeployPlan,K as mergeReconciledProviderAccounts,ce as parseAccountsConfiguration,k as partitionAccounts,ge as partitionForRegion,pt as partitionPreFlightByRemediation,vt as pinProbeCoverage,A as preEmptyStackBuckets,s as probeSecretsDrift,h as projectAccountRows,I as projectScalarSummary,Zr as proveFlipMetadataOnly,Et as proveRemovalTargetsOnly,yr as readDriftSuspects,Kr as readRemediationJournal,$r as readRetainFlipState,G as reconcileProviderAccounts,V as reconcileTrailMigration,_r as recordDriftSuspects,mr as renderConsentVerdictLines,cr as renderDestructionTicketLines,kt as renderPinReportLines,We as renderPlanLines,je as renderPlanSummary,ke as resolveBuildArgs,Ee as resolveBuildSecretSessionProvider,Pe as resolveBuildSecrets,Ae as resolveSecretRefValue,n as restart,le as restoreAndReconcileQuarantinedBucket,ie as restoreBucketPolicy,rr as runApprovalGate,or as runDestructionGate,vr as runDriftPreFlight,gt as runDriftRepairGate,ct as runForgetSurgery,Le as runOpenNextBuild,Ie as runOrganisationSetup,Bt as runPinRemediation,St as runRecreatePreFlight,ft as runRecreateSurgery,ze as signApprovalToken,dt as snapshotPolicyForType,De as sourcedRefsFromBuildArgs,u as ssmParameterNameFromValueFrom,Mr as sweepExpiredRemediationJournals,mt as synthTemplateDeclaresResource,ae as synthesiseEnforceSslDocument,te as toResourcePolicyStatements,qe as toWirePlanChanges,$ as triageBucketPolicy,q as unlockBucket,X as unlockQueue,be as validateBuildGroup,Xe as verifyApprovalToken,et as verifyRetainFlipDiff,Yr as writeRemediationJournal};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* for destroy operations. No bootstrap step (unlike deploy).
|
|
6
6
|
*/
|
|
7
7
|
import type { DestroyParams } from "../../types/params.js";
|
|
8
|
+
import { type LeaseDeniedError } from "../application/leaseGate.js";
|
|
8
9
|
import type { DeployCallbacks } from "../../types/callbacks.js";
|
|
9
10
|
import type { OrganisationOperation } from "../../types/operations.js";
|
|
10
11
|
import type { DeployServices } from "../serviceFactory.js";
|
|
@@ -16,6 +17,12 @@ export interface CascadeDestroyAccountResult {
|
|
|
16
17
|
duration: number;
|
|
17
18
|
error?: string;
|
|
18
19
|
skipped?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Set when the failure was a §5.3 lease denial. The aggregator must
|
|
22
|
+
* propagate it as a typed overall failure — a denial is a cancel/supersede
|
|
23
|
+
* signal, not a partial-failure to fold into the skip-root warnings path.
|
|
24
|
+
*/
|
|
25
|
+
leaseDenied?: LeaseDeniedError;
|
|
19
26
|
}
|
|
20
27
|
export interface DestroyCascadeAccountOptions {
|
|
21
28
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as $}from"path";import{logger as l}from"@fjall/util/logger";import{maskSensitiveOutput as s,getErrorMessage as w}from"@fjall/util";import{stubCallerIdentity as F}from"../../types/deployment/index.js";import{CloudFormationClient as E,DescribeStacksCommand as _}from"@aws-sdk/client-cloudformation";import{S3Client as k}from"@aws-sdk/client-s3";import{BackupClient as x}from"@aws-sdk/client-backup";import{IAMClient as L}from"@aws-sdk/client-iam";import{accountHasDisasterRecovery as M,describeSurvivingBackupVault as K,formatSurvivingVaultWarning as V}from"../../aws/organisations/backup.js";import{cleanupOrphanedOidcProvider as j}from"../../aws/organisations/oidcProvider.js";import{composeSdkAbortSignal as U}from"../../aws/organisations/types.js";import{assertLeaseBeforeStack as G}from"../application/leaseGate.js";import{ORGANISATION_TYPES as N,getOrganisationStackName as H}from"../../types/operations.js";import{CdkContextBuilder as W}from"../../services/supporting/CdkContextBuilder.js";import{regionSuffix as Y}from"../../types/config/FjallState.js";import{buildParamsContext as q,assumeCascadeRole as z,forwardOutput as O}from"../contextHelpers.js";import{cascadeOperationKey as J}from"./cascadeHelpers.js";import{capturedSweepBuckets as Q,cleanupFailedStack as X,preEmptyStackBuckets as Z,sweepOrphanedDestroyBuckets as T}from"../stackCleanup.js";import{STACK_NOT_FOUND_PATTERN as ee,STACK_FAILED_STATE_PATTERN as te}from"../../types/constants.js";async function De(a,n,c,e,t,r,o,S){const y=Date.now(),m=J(e.name,r),f=(i,p=i)=>(o.onCascadeAccountComplete?.(m,!1,p,r),{accountName:e.name,accountId:e.id,region:r,success:!1,duration:Date.now()-y,error:i});o.onCascadeAccountStart?.(m,e.id,r,t);const g=await z(n.awsProvider,e.id,r,`fjall-cascade-destroy-${e.name}`);if(!g.success){const i=s(g.error.message);return f(`AssumeRole failed: ${i}`,i)}const{provider:d,credentials:A}=g.data,b=$(c.path,`cdk.out.${e.id}.${Y(r)}`),D=W.buildDeploymentContext({deployType:t,target:c.target,path:c.path,assemblyDir:b,region:r,accountName:e.name,callerIdentity:F(e.id),...q({orgConfig:a.orgConfig,identity:a.identity,region:r,primaryRegion:S?.primaryRegion})},{verbose:a.options?.verbose},a.orgConfig);o.onCascadeAccountPhaseChange?.(m,"synth",r);const h=await n.cdkService.runCdkSynth(D,O(o));if(!h.success)return f(s(`Synth failed: ${h.error}`));o.onCascadeAccountPhaseChange?.(m,"destroy",r);const u=H(t==="platform"?N.PLATFORM:N.ACCOUNT),v=await G(a.assertLeaseForStack,u,a.abortSignal,"destroy");if(v!==void 0)return{...f(v.message),leaseDenied:v};M(e.environment,a.orgConfig?.disasterRecoveryRegion)&&await re(d.getClient(x),r,o,a.abortSignal);const B=await Z(d.getClient(E),d.getClient(k),u,o,a.abortSignal),C=Q(B),R=await n.cdkService.runCdkDestroy(D,u,O(o),i=>o.onCascadeAccountResourceProgress?.(m,i,r),d,!0,A);if(!R.success){const i=R.error;if(i.includes(te)){l.warn("cascadeDestroy",`CDK destroy failed on ${u} in failed state, retrying via CloudFormation API`,{region:r,account:e.name});try{await X(u,r,A,{accountId:e.id,abortSignal:a.abortSignal},o)}catch(I){const P=`cleanupFailedStack threw for ${u}: ${s(w(I))}`;l.warn("cascadeDestroy",P),o.onLog?.(P,"warn")}const p=await ae(u,d.getClient(E),a.abortSignal);return p.deleted?(C.length>0&&await T(d.getClient(k),C,o,a.abortSignal),o.onCascadeAccountComplete?.(m,!0,void 0,r),{accountName:e.name,accountId:e.id,region:r,success:!0,duration:Date.now()-y}):p.error?f(s(p.error)):f(s(`Stack ${u} cleanup attempted but stack still exists in ${r}`))}return f(s(i))}return C.length>0&&await T(d.getClient(k),C,o,a.abortSignal),(S?.primaryRegion===void 0||r===S.primaryRegion)&&await j(d.getClient(L),o,a.abortSignal),o.onCascadeAccountComplete?.(m,!0,void 0,r),{accountName:e.name,accountId:e.id,region:r,success:!0,duration:Date.now()-y}}async function re(a,n,c,e){try{const t=await K(a,n,e);if(!t.success){l.debug("cascadeDestroy","Backup-vault survival probe failed",{region:n,error:s(t.error.message)});return}if(t.data===null)return;c.onProgress?.({type:"warning",message:V(t.data),metadata:{source:"backup-vault-survival"}}),l.warn("cascadeDestroy","Backup vault survives destroy",{region:n,vaultName:t.data.vaultName,recoveryPointCount:t.data.recoveryPointCount,lockPermanent:t.data.lockPermanent})}catch(t){l.debug("cascadeDestroy","Backup-vault survival probe threw",{region:n,error:s(w(t))})}}async function ae(a,n,c){try{const t=(await n.send(new _({StackName:a}),{abortSignal:U(c)})).Stacks?.[0]?.StackStatus;return!t||t==="DELETE_COMPLETE"?{deleted:!0}:{deleted:!1,error:`Stack still in ${t} after cleanup attempt`}}catch(e){if(e instanceof Error&&e.message?.includes(ee))return{deleted:!0};const t=s(w(e));return l.debug("cascadeDestroy","Stack verification failed",{error:t}),{deleted:!1,error:`Stack verification failed: ${t}`}}}export{De as destroyCascadeAccount};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as
|
|
1
|
+
import{join as b}from"path";import{success as j,failure as C}from"@fjall/generator";import{logger as R}from"@fjall/util/logger";import{maskSensitiveOutput as l,mapSettledWithConcurrency as K}from"@fjall/util";import{ORGANISATION_TYPES as S,getOrganisationStackName as H}from"../../types/operations.js";import{CdkContextBuilder as z}from"../../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as Y}from"../../types/deployment/index.js";import{CloudFormationService as M}from"../../services/infrastructure/CloudFormationService.js";import{getCascadeStateFilePath as W,regionSuffix as X}from"../../types/config/FjallState.js";import{BackupClient as q}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as J,describeBackupVaultExists as Q}from"../../aws/organisations/backup.js";import{buildParamsContext as Z,collectStackOutputs as T,assumeCascadeRole as k,forwardOutput as E,reconcileOidcProviderIfCarried as V}from"../contextHelpers.js";import{assertLeaseBeforeStack as ee}from"../application/leaseGate.js";import{runDriftPreFlightGate as te}from"../drift/preFlightGate.js";import{accountTier as G}from"@fjall/util";import{DEFAULT_REGION as U}from"../../aws/utils/regions.js";const oe=4;function we(e){const a=e.find(t=>G(t)==="platform"),c=e.filter(t=>G(t)==="account");return{platformAccount:a,memberAccounts:c}}function re(e){const a=e?.primaryRegion??U,c=e?.secondaryRegions??[],t=new Set([a,...c]),i=e?.disasterRecoveryRegion;return i&&t.add(i),[...t]}function Pe(e){return re(e)[0]??U}function ne(e,a){return`${e} (${a})`}function $e(e,a){const c=[];for(const t of a)for(const i of e)c.push({account:i,region:t});return c}import{buildCascadeRoleArn as Ee}from"../contextHelpers.js";async function Ae(e,a,c,t){c.onLog?.(`Verifying cascade role access for ${a.length} account(s)\u2026`,"info");const i=await K(a,oe,async s=>k(e.awsProvider,s.id,s.region??e.awsProvider.getRegion(),`fjall-preflight-${s.name}`,t)),r=[];return i.forEach((s,o)=>{const n=a[o];if(n){if(s.status==="rejected"){r.push({accountId:n.id,accountName:n.name,error:s.reason instanceof Error?s.reason.message:String(s.reason)});return}s.value.success||r.push({accountId:n.id,accountName:n.name,error:s.value.error.message})}}),r.length===0&&c.onLog?.(`Cascade role access verified for ${a.length} account(s)`,"info"),r}async function Oe(e,a,c,t,i,r,s){const o=s?.region??t.region??a.awsProvider.getRegion(),n=ne(t.name,o),u=s?.orgConfig??e.orgConfig,g=s?.ipamPoolId;r.onCascadeAccountStart?.(n,t.id,o,i);const f=await k(a.awsProvider,t.id,o,`fjall-cascade-${t.name}`,e.abortSignal);if(!f.success)return r.onCascadeAccountComplete?.(n,!1,l(f.error.message),o),C(new Error(`Failed to assume role for ${t.name}: ${l(f.error.message)}`));const{provider:m,credentials:y}=f.data,P=b(e.workingDirectory,"fjall",i==="platform"?S.PLATFORM:S.ACCOUNT),$=b(P,`cdk.out.${t.id}.${X(o)}`),h=z.buildDeploymentContext({deployType:i,target:c.target,path:P,assemblyDir:$,environment:t.environment??void 0,region:o,accountName:t.name,callerIdentity:Y(t.id),ipamPoolId:g,...Z({orgConfig:u,identity:e.identity,skipOidc:e.options?.skipOidc,region:o,primaryRegion:s?.primaryRegion,trailLifecycle:t.trailLifecycle})},{verbose:e.options?.verbose},u);if(J(t.environment,u?.disasterRecoveryRegion)){const p=await Q(m.getClient(q),e.abortSignal);if(!p.success)return r.onCascadeAccountComplete?.(n,!1,l(p.error.message),o),C(new Error(`Backup vault probe failed for ${t.name}: ${l(p.error.message)}`));h.fjallAdoptBackupVault=p.data}r.onCascadeAccountPhaseChange?.(n,"synth",o);const A=await a.cdkService.runCdkSynth(h,E(r),y);if(!A.success)return r.onCascadeAccountComplete?.(n,!1,l(`Synth failed: ${A.error}`),o),C(new Error(`Synth failed for ${t.name}: ${l(A.error)}`));const d=H(i==="platform"?S.PLATFORM:S.ACCOUNT),I=W(P,t.id,o),w=new M(m),{changed:_,currentHash:F}=await a.hashService.compareCascadeStack($,d,I);if(!_&&e.options?.force!==!0&&await w.stackExists(d)){const p=await w.getStackOutputs(d);p.success||R.debug("cascadeHelpers","Failed to read outputs for skipped cascade account (non-critical)",{stackName:d,account:t.name});const B=T(p);return await V(h,m,r,e.abortSignal),r.onLog?.(`${t.name}: no infrastructure changes \u2014 skipping deploy`,"info"),r.onCascadeAccountComplete?.(n,!0,void 0,o,B,!0),j({outputs:B,skipped:!0})}const O=await te(m,{stackNames:[d],cdkOutPath:$,cfnService:w,callbacks:r,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(O.kind==="blocked")return r.onCascadeAccountComplete?.(n,!1,O.message,o),C(new Error(O.message));const v=await ee(e.assertLeaseForStack,d,e.abortSignal);if(v!==void 0)return r.onCascadeAccountComplete?.(n,!1,v.message,o),C(v);r.onCascadeAccountPhaseChange?.(n,"bootstrap",o);const x=await a.cdkService.runCdkBootstrap(h,E(r),y);if(!x.success)return r.onCascadeAccountComplete?.(n,!1,l(`Bootstrap failed: ${x.error}`),o),C(new Error(`Bootstrap failed for ${t.name}: ${l(x.error)}`));r.onCascadeAccountPhaseChange?.(n,"deploy",o);const D=await a.cdkService.runCdkDeploy(h,d,E(r),p=>r.onCascadeAccountResourceProgress?.(n,p,o),m,y);if(!D.success)return r.onCascadeAccountComplete?.(n,!1,l(D.error),o),C(new Error(l(D.error)));const N=await w.getStackOutputs(d);N.success||R.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:d,account:t.name});const L=T(N);return await V(h,m,r,e.abortSignal),F!==void 0&&((await a.hashService.persistCascadeStack(I,d,F)).success||R.debug("cascadeHelpers","Failed to persist cascade hash state (non-critical)",{stackName:d,account:t.name})),r.onCascadeAccountComplete?.(n,!0,void 0,o,L,!1),j({outputs:L,skipped:!1})}async function ve(e,a,c,t){const i=new Map,r=e.awsProvider.getRegion(),s=await k(e.awsProvider,a.id,r,`fjall-ipam-read-${a.name}`,t);if(!s.success)return R.debug("organisationDeploy",`Cannot read Platform outputs: ${s.error.message}`),i;const o=new M(s.data.provider),n=H(S.PLATFORM),u=await o.getStackOutputs(n);if(!u.success)return R.debug("organisationDeploy",`Failed to read Platform stack outputs: ${u.error.message}`),i;const g=/^IpamPoolId(\d{12})(\w+)$/;for(const f of u.data){const m=f.OutputKey?.match(g);if(m&&f.OutputValue){const y=`${m[1]}-${m[2]}`;i.set(y,f.OutputValue)}}return i.size>0&&c.onLog?.(`Read ${i.size} IPAM pool ID(s) from Platform stack`,"info"),i}async function xe(e,a){const c=e.getDomains();if(c.length===0)return{domainsDeployed:0,errors:[]};a.onCascadePhaseStart?.("domains");const t=c.filter(o=>o.type==="apex"),i=c.filter(o=>o.type==="delegated");let r=0;const s=[];for(const o of t){const n=await e.deployDomain(o.name,a);n.success?r++:s.push(`${o.name}: ${n.error.message}`)}if(i.length>0){const o=await Promise.allSettled(i.map(n=>e.deployDomain(n.name,a)));for(let n=0;n<o.length;n++){const u=o[n],g=i[n];if(!(!u||!g))if(u.status==="fulfilled")u.value.success?r++:s.push(`${g.name}: ${u.value.error.message}`);else{const f=u.reason instanceof Error?u.reason.message:String(u.reason);s.push(`${g.name}: ${f}`)}}}return a.onCascadePhaseComplete?.("domains"),{domainsDeployed:r,errors:s}}export{oe as CASCADE_MAX_CONCURRENCY,$e as buildAccountRegionPairs,Ee as buildCascadeRoleArn,re as buildRegionList,Pe as cascadeHomeRegion,ne as cascadeOperationKey,Oe as deployCascadeAccount,xe as deployDomains,we as partitionAccounts,Ae as probeCascadeRoles,ve as readPlatformIpamPoolIds};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{CloudFormationClient as
|
|
2
|
-
`),
|
|
3
|
-
${a}`);t.onError?.(
|
|
1
|
+
import{CloudFormationClient as B}from"@aws-sdk/client-cloudformation";import{S3Client as L}from"@aws-sdk/client-s3";import{IAMClient as G}from"@aws-sdk/client-iam";import{success as k,failure as D}from"@fjall/generator";import{maskSensitiveOutput as E,getErrorMessage as x,mapSettledWithConcurrency as Y}from"@fjall/util";import{stubCallerIdentity as F}from"../../types/deployment/index.js";import{ORGANISATION_TYPES as N,getOrganisationStackName as T}from"../../types/operations.js";import{CdkContextBuilder as U}from"../../services/supporting/CdkContextBuilder.js";import{buildParamsContext as j,synthOrFail as H,forwardOutput as W,forwardResourceProgress as X}from"../contextHelpers.js";import{destroyCascadeAccount as v}from"./cascadeDestroyHelpers.js";import{emitOrgStackTargets as q}from"../organisationDeploy/infraSteps.js";import{assertLeaseBeforeStack as z}from"../application/leaseGate.js";import{cleanupOrphanedOidcProvider as J}from"../../aws/organisations/oidcProvider.js";import{capturedSweepBuckets as K,preEmptyStackBuckets as Q,sweepOrphanedDestroyBuckets as V}from"../stackCleanup.js";import{partitionAccounts as Z,buildRegionList as tt,buildAccountRegionPairs as b,cascadeHomeRegion as ot,CASCADE_MAX_CONCURRENCY as et}from"./cascadeHelpers.js";import{projectScalarSummary as rt}from"./cascadeSummary.js";import{STEP_IDS as nt}from"../../types/stepDefinitions.js";const M=nt.ORG_DESTROY,$="Destroying organisation infrastructure";async function It(o,e,u){const R=Date.now(),{callbacks:t}=o,p=o.orgConfig?.providerAccounts??[],d=ot(o.orgConfig),m=tt(o.orgConfig),{platformAccount:n,memberAccounts:C}=Z(p),f=o.options?.cascade!==!1;t.onLog?.(`Destroying organisation infrastructure (${p.length} accounts, ${m.length} region(s))`,"info");const S=e.awsProvider.getAccountId(),y=e.awsProvider.getRegion();q(t,[{stackName:T(N.ORGANISATION),...S!==""?{accountId:S}:{},...y!==""?{region:y}:{}},...f&&n!==void 0?[{stackName:T(N.PLATFORM),accountId:n.id,region:n.region??d}]:[],...f?b(C,m).map(({account:P,region:l})=>({stackName:T(N.ACCOUNT),accountId:P.id,region:l})):[]]);const s=[],w=[],I=[];let A,h;if(f){t.onCascadeStart?.();const P=Date.now();if(C.length>0){t.onCascadePhaseStart?.("accounts");const a=b(C,m),g=await Y(a,et,({account:c,region:i})=>v(o,e,u,c,"account",i,t,{primaryRegion:d}));for(let c=0;c<g.length;c++){const i=g[c],r=a[c];if(!(!i||!r))if(i.status==="fulfilled")if(i.value.success)w.push(`Account-${r.account.name}-${r.region}`),I.push({accountId:r.account.id,accountName:r.account.name,region:r.region,result:"succeeded",durationMs:i.value.duration});else{const O=E(i.value.error??"Unknown error");I.push({accountId:r.account.id,accountName:r.account.name,region:r.region,result:"failed",durationMs:i.value.duration,error:O}),s.push({accountId:r.account.id,error:O}),h??=i.value.leaseDenied}else{const O=E(x(i.reason));I.push({accountId:r.account.id,accountName:r.account.name,region:r.region,result:"failed",durationMs:0,error:O}),s.push({accountId:r.account.id,error:O})}}t.onCascadePhaseComplete?.("accounts")}if(n){t.onCascadePhaseStart?.("platform");const a=await v(o,e,u,n,"platform",n.region??d,t);if(a.success)w.push("Platform"),A={accountId:n.id,result:"succeeded",durationMs:a.duration};else{const g=E(a.error??"Platform destroy failed");s.push({accountId:n.id,error:g}),h??=a.leaseDenied,A={accountId:n.id,result:"failed",durationMs:a.duration,error:g}}t.onCascadePhaseComplete?.("platform")}const l={members:I,...A!==void 0?{platform:A}:{},domainsDeployed:!1,errors:s,totalDurationMs:Date.now()-P};if(t.onCascadeComplete?.(rt(l)),t.onCascadeLedger?.(l),s.length>0){const a=s.map(c=>` ${c.accountId}: ${c.error}`).join(`
|
|
2
|
+
`),g=new Error(`Cascade destroy completed with ${s.length} failure(s):
|
|
3
|
+
${a}`);t.onError?.(g),t.onLog?.(g.message,"warn")}}if(h!==void 0)return t.onError?.(h),D(h);if(s.length>0)return t.onLog?.("Skipping organisation root stack destroy due to cascade failures","warn"),k({target:u.target,deploymentType:"organisation",stacksDestroyed:w,durationMs:Date.now()-R,warnings:s.map(l=>`${l.accountId}: ${l.error}`)});t.onStepStart?.(M,$,0,1);const _=await at(o,e,u,e.awsProvider.getRegion(),t);if(_.success)w.push("Organisation"),t.onStepComplete?.(M,$,"completed",0,1);else return t.onStepComplete?.(M,$,"error",0,1),D(_.error);return k({target:u.target,deploymentType:"organisation",stacksDestroyed:w,durationMs:Date.now()-R})}async function at(o,e,u,R,t){const p=U.buildDeploymentContext({deployType:"organisation",target:u.target,path:u.path,region:R,callerIdentity:F(e.awsProvider.getAccountId()),...j({orgConfig:o.orgConfig,identity:o.identity})},{verbose:o.options?.verbose},o.orgConfig),d=T(N.ORGANISATION);t.onLog?.("Synthesising organisation infrastructure\u2026","info");const m=await H(e,p,t,"Organisation synth failed");if(!m.success)return m;const n=await z(o.assertLeaseForStack,d,o.abortSignal,"destroy");if(n!==void 0)return t.onError?.(n),D(n);const C=await Q(e.awsProvider.getClient(B),e.awsProvider.getClient(L),d,t,o.abortSignal),f=K(C);t.onLog?.(`Destroying ${d} stack\u2026`,"info");const S=await e.cdkService.runCdkDestroy(p,d,W(t),X(t),e.awsProvider,!0);if(!S.success){const y=new Error(E(`Organisation destroy failed: ${S.error}`));return t.onError?.(y),D(y)}return f.length>0&&await V(e.awsProvider.getClient(L),f,t,o.abortSignal),await J(e.awsProvider.getClient(G),t,o.abortSignal),k(void 0)}export{It as destroyOrganisation};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Result } from "@fjall/generator";
|
|
2
|
-
import type { DeployCallbacks } from "../../types/callbacks.js";
|
|
2
|
+
import type { DeployCallbacks, StackTarget } from "../../types/callbacks.js";
|
|
3
3
|
import type { DeployServices } from "../serviceFactory.js";
|
|
4
4
|
export declare const INFRA_STEPS: {
|
|
5
5
|
readonly CONNECT: {
|
|
@@ -32,6 +32,13 @@ export declare const INFRA_STEPS: {
|
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
export declare const INFRA_STEP_TOTAL = 4;
|
|
35
|
+
/**
|
|
36
|
+
* Emit §5.9 reconciler coordinates for the org path — post-synth, before the
|
|
37
|
+
* first mutating CFN op — so a crashed run's in-flight stacks can be located
|
|
38
|
+
* by name/account/region. No-ops when no consumer wired the callback or the
|
|
39
|
+
* operation touches no stacks.
|
|
40
|
+
*/
|
|
41
|
+
export declare function emitOrgStackTargets(callbacks: DeployCallbacks, targets: readonly StackTarget[]): void;
|
|
35
42
|
/** Complete the single-component PREPARE step as errored. */
|
|
36
43
|
export declare function failPrepareStep(callbacks: DeployCallbacks): void;
|
|
37
44
|
/** Mask a failure message, surface it via onError, and return the failure. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{failure as
|
|
1
|
+
import{failure as S}from"@fjall/generator";import{collectStackOutputs as C}from"../contextHelpers.js";import{maskSensitiveOutput as P}from"@fjall/util";import{INFRA_STEP_NAME as n,STEP_IDS as e,STEP_NAMES as o}from"../../types/stepDefinitions.js";const A={CONNECT:{id:e.CONNECT,name:n.CONNECT},PREPARE:{id:e.PREPARE_ENVIRONMENT,name:n.PREPARE},DEPLOY:{id:e.DEPLOY,name:n.DEPLOY},MONITORING:{id:e.MONITORING,name:n.MONITORING},ORG_DEPLOY:{id:e.ORG_DEPLOY,name:o.ORG_DEPLOY},CASCADE_PLATFORM:{id:e.CASCADE_PLATFORM,name:o.CASCADE_PLATFORM},CASCADE_ACCOUNTS:{id:e.CASCADE_ACCOUNTS,name:o.CASCADE_ACCOUNTS}},a=4;function m(t,r){t.onStackTargets===void 0||r.length===0||t.onStackTargets(r)}function c(t){t.onStepComplete?.(A.PREPARE.id,A.PREPARE.name,"error",1,a)}function p(t,r){const E=new Error(P(r));return t.onError?.(E),S(E)}async function s(t,r,E,i){const O=await t.cfnService.getStackOutputs(E);return O.success||r.onLog?.(i,"debug"),C(O)}export{A as INFRA_STEPS,a as INFRA_STEP_TOTAL,m as emitOrgStackTargets,c as failPrepareStep,p as maskAndFail,s as readStackOutputsBestEffort};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{join as
|
|
2
|
-
`);t.onLog?.(
|
|
3
|
-
${
|
|
4
|
-
`),
|
|
5
|
-
${o}`);return t.onError?.(
|
|
1
|
+
import{join as _}from"node:path";import{OrganizationsClient as Q}from"@aws-sdk/client-organizations";import{success as V,failure as v}from"@fjall/generator";import{ORGANISATION_TYPES as A,getOrganisationStackName as R}from"../../types/operations.js";import{synthOrFail as X,bootstrapOrFail as Z,forwardOutput as tt,forwardResourceProgress as et}from"../contextHelpers.js";import{partitionAccounts as ot,probeCascadeRoles as at,buildRegionList as rt,buildAccountRegionPairs as nt}from"../organisation/cascadeHelpers.js";import{assertLeaseBeforeStack as ct}from"../application/leaseGate.js";import{accountTier as it,maskSensitiveOutput as g}from"@fjall/util";import{findDevelopmentOuId as st}from"../../aws/organisations/organisationalUnits.js";import{buildOrgContext as dt,resolveOrgDetails as ut}from"./orgContext.js";import{INFRA_STEPS as B,emitOrgStackTargets as lt,maskAndFail as O,readStackOutputsBestEffort as Y}from"./infraSteps.js";import{resolveCascadeAccounts as gt}from"./resolveCascadeAccounts.js";import{executeCascade as ft}from"./cascadeExecution.js";import{reconcileOrgTrailOutputs as pt}from"./trailReconciliation.js";import{runDriftPreFlightGate as mt}from"../drift/preFlightGate.js";async function Et(n,e,I,z){const{callbacks:t,options:D}=n,{providerAccounts:f,effectiveOrgConfig:K,defaultRegion:U}=await gt(n,e),p=await ut(e,n.abortSignal);if(!p.success)return O(t,p.error.message);const w=await st(e.awsProvider.getClient(Q),p.data.rootId,n.abortSignal);if(!w.success)return O(t,w.error.message);const q={...p.data,...w.data!==void 0?{devOuId:w.data}:{}},J=f.find(o=>it(o)==="organisation"),d=dt(n,e,I,"organisation",q,void 0,J?.trailLifecycle),l=D?.cascade!==!1,{platformAccount:s,memberAccounts:P}=ot(f),E=l&&s!==void 0?1:0,L=l&&P.length>0?1:0,c=2+E+L,T=l?[...s!==void 0?[s]:[],...P]:[];if(T.length>0){const o=await at(e,T,t,n.abortSignal),a=s!==void 0?o.find(r=>r.accountId===s.id):void 0;if(a!==void 0)return O(t,`Pre-flight cascade role check failed for the platform account ${a.accountName} (${a.accountId}): ${a.error}`);for(const r of o)t.onProgress?.({type:"warning",message:g(`Pre-flight cascade role check failed for ${r.accountName} (${r.accountId}) \u2014 its cascade deploy is expected to fail: ${r.error}`)})}t.onCascadeAccountsReconciled?.({hasPlatformAccount:E>0,hasMemberAccounts:L>0});const{id:C,name:y}=B.PREPARE;t.onStepStart?.(C,y,0,c),t.onLog?.("Synthesising organisation infrastructure\u2026","info");const F=await X(e,d,t,"CDK synthesis failed");if(!F.success)return t.onStepComplete?.(C,y,"error",0,c),F;const x=e.awsProvider.getAccountId(),N=e.awsProvider.getRegion();lt(t,[{stackName:R(A.ORGANISATION),...x!==""?{accountId:x}:{},...N!==""?{region:N}:{}},...l&&s!==void 0?[{stackName:R(A.PLATFORM),accountId:s.id,region:s.region??N}]:[],...l?nt(P,rt(n.orgConfig)).map(({account:o,region:a})=>({stackName:R(A.ACCOUNT),accountId:o.id,region:a})):[]]);const G=await Z(e,d,t);if(!G.success)return t.onStepComplete?.(C,y,"error",0,c),G;t.onStepComplete?.(C,y,"completed",0,c);const{id:m,name:h}=B.ORG_DEPLOY,i=R(A.ORGANISATION);let H=!0;const S=await e.hashService.getTemplateHashes(_(d.path,"cdk.out"));if(S.success){const o=await e.hashService.compareWithState(S.data,d.path);o.success?H=o.data.stackChanges.get(i)??!0:t.onLog?.(g(`Org root change detection failed \u2014 deploying to be safe: ${o.error.message}`),"warn")}else t.onLog?.(g(`Org root template hashing failed \u2014 deploying to be safe: ${S.error.message}`),"warn");const $=H||D?.force===!0||!await e.cfnService.stackExists(i);t.onOrgChangesDetected?.({hasOrgChanges:$});let k;if($){t.onStepStart?.(m,h,1,c);const o=await mt(e.awsProvider,{stackNames:[i],cdkOutPath:_(d.path,"cdk.out"),cfnService:e.cfnService,callbacks:t,...n.abortSignal!==void 0?{abortSignal:n.abortSignal}:{}});if(o.kind==="blocked")return t.onStepComplete?.(m,h,"error",1,c),O(t,o.message);const a=await ct(n.assertLeaseForStack,i,n.abortSignal);if(a!==void 0)return t.onStepComplete?.(m,h,"error",1,c),t.onError?.(a),v(a);const r=await e.cdkService.runCdkDeploy(d,i,tt(t),et(t),e.awsProvider);if(!r.success)return t.onStepComplete?.(m,h,"error",1,c),O(t,r.error);k=await Y(e,t,i,"Failed to read org stack outputs (non-critical)");const j=S.success?S.data.get(i):void 0;if(j!==void 0){const W=await e.hashService.updateStateAfterDeploy(d.path,new Map([[i,j]]));W.success||t.onLog?.(`Warning: failed to update state file \u2014 next deploy may re-deploy the org root: ${g(W.error.message)}`,"warn")}t.onStepComplete?.(m,h,"completed",1,c)}else t.onLog?.("Organisation root: no infrastructure changes \u2014 skipping deploy","info"),k=await Y(e,t,i,"Failed to read org stack outputs (non-critical)");const u=[],b=[];let M=$;if(l&&f.length>0){const{anyCascadeDeployHappened:o}=await ft(n,e,I,{providerAccounts:f,effectiveOrgConfig:K,totalSteps:c,cascadeErrors:u,allCascadeOutputs:b});if(o&&(M=!0),await pt(n,e,{orgOutputs:k,allCascadeOutputs:b,orgDetails:p.data,providerAccounts:f,defaultRegion:U}),u.length>0){const a=u.map(r=>` ${r.accountId}: ${r.error}`).join(`
|
|
2
|
+
`);t.onLog?.(g(`Cascade failed for ${u.length} target(s):
|
|
3
|
+
${a}`),"warn")}}if(u.length>0){const o=u.map(r=>g(`${r.accountId}: ${r.error}`)).join(`
|
|
4
|
+
`),a=new Error(`Organisation root deployed, but the cascade failed for ${u.length} target(s):
|
|
5
|
+
${o}`);return t.onError?.(a),v(a)}return V({target:I.target,deploymentType:"organisation",outputs:k,artefacts:[],...b.length>0?{cascadeOutputs:b}:{},...M?{}:{noChanges:!0},durationMs:Date.now()-z})}export{Et as deployOrgWithCascade};
|
|
@@ -5,7 +5,11 @@ import type { OrganisationOperation } from "../../types/operations.js";
|
|
|
5
5
|
import type { DeployServices } from "../serviceFactory.js";
|
|
6
6
|
export interface OrgDetailsForSynth {
|
|
7
7
|
orgId: string;
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Absent when the deploy credentials cannot list roots — a member-account
|
|
10
|
+
* single-component deploy. Only the Organisation stack consumes it.
|
|
11
|
+
*/
|
|
12
|
+
rootId?: string;
|
|
9
13
|
managementAccountId: string;
|
|
10
14
|
/**
|
|
11
15
|
* Development OU id, resolved only for organisation-tier synths (the dev
|
|
@@ -16,7 +20,22 @@ export interface OrgDetailsForSynth {
|
|
|
16
20
|
devOuId?: string;
|
|
17
21
|
}
|
|
18
22
|
export declare function buildOrgContext(params: DeployParams, services: DeployServices, operation: OrganisationOperation, deployType: "organisation" | "platform" | "account", orgDetails: OrgDetailsForSynth | undefined, accountName?: string, trailLifecycle?: ProviderAccount["trailLifecycle"], targetIsOrganisationTier?: boolean): import("../../types/deployment/DeploymentTypes.js").DeploymentContext;
|
|
19
|
-
export declare function resolveOrgDetails(services: DeployServices, abortSignal?: AbortSignal): Promise<Result<OrgDetailsForSynth
|
|
23
|
+
export declare function resolveOrgDetails(services: DeployServices, abortSignal?: AbortSignal): Promise<Result<OrgDetailsForSynth & {
|
|
24
|
+
rootId: string;
|
|
25
|
+
}>>;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve org details for a single-component (account/platform) deploy in an
|
|
28
|
+
* org-managed estate. Read-only and member-safe: DescribeOrganization is
|
|
29
|
+
* callable from any account in the organisation, while ListRoots is
|
|
30
|
+
* management-account-only — the cascade path resolves full details under
|
|
31
|
+
* management credentials, but a standalone `deploy account` runs under the
|
|
32
|
+
* target account's own credentials and must not require (or create)
|
|
33
|
+
* organisation-level access. The Account and Platform stacks consume only
|
|
34
|
+
* orgId (the exported OrganisationId output), so a best-effort rootId is
|
|
35
|
+
* sufficient here. Resolves undefined when no organisation exists yet — the
|
|
36
|
+
* component synthesises standalone, exactly like the solo path.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveOrgDetailsForMemberComponent(services: DeployServices, abortSignal?: AbortSignal): Promise<Result<OrgDetailsForSynth | undefined>>;
|
|
20
39
|
/**
|
|
21
40
|
* Resolve org details for a SOLO-estate single-component deploy. Read-only — a
|
|
22
41
|
* standalone account deploy must NEVER create an organisation (the mutating
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{success as i,failure as c}from"@fjall/generator";import{OrganizationsClient as d}from"@aws-sdk/client-organizations";import{CdkContextBuilder as l}from"../../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as I}from"../../types/deployment/index.js";import{ensureOrganisationExists as m,describeOrganisation as f,describeOrganisationAsMember as C}from"../../aws/organisations/organisation.js";import{buildParamsContext as O,resolveAccountBootstrapSkipOidc as v}from"../contextHelpers.js";function P(o,n,e,t,r,s,u,g){const a=n.awsProvider.getRegion();return l.buildDeploymentContext({deployType:t,target:e.target,path:e.path,region:a,accountName:s,callerIdentity:I(n.awsProvider.getAccountId()),orgId:r?.orgId,rootId:r?.rootId,managementAccountId:r?.managementAccountId,devOuId:r?.devOuId,...O({orgConfig:o.orgConfig,identity:o.identity,skipOidc:v(t,o.orgConfig,o.options?.skipOidc,g),...t!=="organisation"?{region:a,primaryRegion:o.orgConfig?.primaryRegion}:{},trailLifecycle:u})},{verbose:o.options?.verbose,infraOnly:o.options?.infraOnly},o.orgConfig)}async function k(o,n){const e=o.awsProvider.getClient(d),t=await m(e,n);return t.success?i({orgId:t.data.orgId,rootId:t.data.rootId,managementAccountId:t.data.managementAccountId}):c(t.error)}async function R(o,n){const e=o.awsProvider.getClient(d),t=await C(e,n);return t.success?t.data===null?i(void 0):i({orgId:t.data.orgId,managementAccountId:t.data.managementAccountId,...t.data.rootId!==void 0?{rootId:t.data.rootId}:{}}):c(t.error)}async function h(o,n){const e=o.awsProvider.getClient(d),t=await f(e,n,{tolerateAccessDenied:!0});if(!(!t.success||t.data===null))return{orgId:t.data.orgId,rootId:t.data.rootId,managementAccountId:t.data.managementAccountId}}export{P as buildOrgContext,k as resolveOrgDetails,R as resolveOrgDetailsForMemberComponent,h as resolveOrgDetailsForSolo};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{success as
|
|
1
|
+
import{join as h}from"path";import{failure as A,success as v}from"@fjall/generator";import{BackupClient as y}from"@aws-sdk/client-backup";import{IAMClient as T}from"@aws-sdk/client-iam";import{logger as M}from"@fjall/util/logger";import{getOrganisationStackName as G}from"../../types/operations.js";import{accountHasDisasterRecovery as Y,describeBackupVaultExists as x}from"../../aws/organisations/backup.js";import{describeAccountGlobalsExist as B,buildMissingAccountGlobalsAdvisory as V}from"../../aws/organisations/accountGlobals.js";import{targetsNonPrimaryRegion as _,reconcileOidcProviderIfCarried as j,synthOrFail as $,bootstrapOrFail as H,forwardOutput as K,forwardResourceProgress as q}from"../contextHelpers.js";import{accountTier as k,getErrorMessage as z,maskSensitiveOutput as N}from"@fjall/util";import{runDriftPreFlightGate as J}from"../drift/preFlightGate.js";import{completeForgetAfterDeploy as Q}from"../remediation/forgetResource.js";import{composePostFailureRepairOffer as U}from"../remediation/postFailureRepairOffer.js";import{buildOrgContext as W,resolveOrgDetailsForMemberComponent as X,resolveOrgDetailsForSolo as Z}from"./orgContext.js";import{hasOrganisationTierAccount as tt}from"../../aws/organisations/accountGlobals.js";import{INFRA_STEPS as n,INFRA_STEP_TOTAL as a,emitOrgStackTargets as ot,failPrepareStep as c,maskAndFail as l,readStackOutputsBestEffort as et}from"./infraSteps.js";import{assertLeaseBeforeStack as rt}from"../application/leaseGate.js";async function Et(o,r,d,g,D){const{callbacks:t}=o;t.onStepComplete?.(n.CONNECT.id,n.CONNECT.name,"completed",0,a),t.onStepStart?.(n.PREPARE.id,n.PREPARE.name,1,a);const P=r.awsProvider.getRegion(),f=o.orgConfig?.primaryRegion;if(f!==void 0&&_(P,f)){const e=await B(r.awsProvider.getClient(T),o.abortSignal);if(!e.success)return c(t),l(t,e.error.message);if(!e.data)return c(t),l(t,V({target:d.target,deployRegion:P,primaryRegion:f,...o.orgConfig!==void 0?{providerAccounts:o.orgConfig.providerAccounts}:{}}))}let m;if(tt(o.orgConfig?.providerAccounts)){const e=await X(r,o.abortSignal);if(!e.success)return c(t),l(t,e.error.message);m=e.data}else m=await Z(r,o.abortSignal);const s=g==="account"?o.orgConfig?.providerAccounts.find(e=>e.id===r.awsProvider.getAccountId()):o.orgConfig?.providerAccounts.find(e=>k(e)==="platform"),I=g==="account"&&s!==void 0&&k(s)==="organisation",u=W(o,r,d,g,m,g==="account"?s?.name??d.target:void 0,s?.trailLifecycle,I),S=o.orgConfig?.disasterRecoveryRegion;if(g==="account"&&(S!==void 0&&S!=="")&&(s===void 0||Y(s.environment,S))){const e=await x(r.awsProvider.getClient(y),o.abortSignal);if(!e.success)return c(t),l(t,e.error.message);u.fjallAdoptBackupVault=e.data}t.onLog?.(`Synthesising ${g} infrastructure\u2026`,"info");const R=await $(r,u,t,"CDK synthesis failed");if(!R.success)return c(t),R;const i=G(d.type),b=r.awsProvider.getAccountId(),O=r.awsProvider.getRegion();ot(t,[{stackName:i,...b!==""?{accountId:b}:{},...O!==""?{region:O}:{}}]);const w=await J(r.awsProvider,{stackNames:[i],cdkOutPath:h(d.path,"cdk.out"),cfnService:r.cfnService,callbacks:t,...o.abortSignal!==void 0?{abortSignal:o.abortSignal}:{}});if(w.kind==="blocked")return c(t),l(t,w.message);const C=await H(r,u,t);if(!C.success)return c(t),C;t.onStepComplete?.(n.PREPARE.id,n.PREPARE.name,"completed",1,a);const p=await rt(o.assertLeaseForStack,i,o.abortSignal);if(p!==void 0)return t.onError?.(p),A(p);t.onStepStart?.(n.DEPLOY.id,n.DEPLOY.name,2,a);const E=await r.cdkService.runCdkDeploy(u,i,K(t),q(t),r.awsProvider);if(!E.success){t.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"error",2,a);const e=await U(r.awsProvider,{stackNames:[i],repairTarget:`--stack ${i}`,originalError:new Error(N(E.error)),cfnService:r.cfnService,...o.abortSignal!==void 0?{abortSignal:o.abortSignal}:{}});return t.onError?.(e),A(e)}t.onStepComplete?.(n.DEPLOY.id,n.DEPLOY.name,"completed",2,a);const F=await et(r,t,i,"Failed to read stack outputs (non-critical)");return await j(u,r.awsProvider,t,o.abortSignal),t.onStepStart?.(n.MONITORING.id,n.MONITORING.name,3,a),t.onStepComplete?.(n.MONITORING.id,n.MONITORING.name,"completed",3,a),await Q(r.awsProvider,{stackNames:[i],onLog:(e,L)=>t.onLog?.(e,L),...o.abortSignal!==void 0?{abortSignal:o.abortSignal}:{}}).catch(e=>{M.debug("SingleComponentDeploy","Forget completion sweep failed",{error:N(z(e))})}),v({target:d.target,deploymentType:"organisation",outputs:F,artefacts:[],durationMs:Date.now()-D})}export{Et as deploySingleComponent};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `fjall drift repair` ceremony — the drift-flavoured twin of the deploy
|
|
3
|
+
* destruction gate. Builds a `DestructionTicket` from CONFIRMED drift
|
|
4
|
+
* findings (never unconfirmed — fail-closed verdicts refuse remediation) and
|
|
5
|
+
* evaluates `--remediate` consents through the SAME engine primitives
|
|
6
|
+
* (`evaluateDestructionConsents`, digest TOCTOU, [G6] verdict table,
|
|
7
|
+
* `executableRemediationVerbs`) so the two ceremonies cannot drift apart.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately no plan loader and no interactive resolver: repair consents
|
|
10
|
+
* arrive as flags ([G5] — the agent surface teaches the flag SHAPE, the
|
|
11
|
+
* operator types the physical name). All output lines are identifier-only
|
|
12
|
+
* (physical names, verbs) — pre-masked by construction.
|
|
13
|
+
*/
|
|
14
|
+
import { type DriftFinding } from "../../types/drift.js";
|
|
15
|
+
import type { DestructionConsent, DestructionConsentReport, DestructionOutcome, DestructionTicket, RemediationVerb } from "../../types/destruction.js";
|
|
16
|
+
/**
|
|
17
|
+
* Build the repair ticket from confirmed-deleted findings. Returns undefined
|
|
18
|
+
* when nothing is confirmed (unconfirmed/stack-missing stay repair-only and
|
|
19
|
+
* never ticket). `physicalName` is the probed live physical id ([G6] — the
|
|
20
|
+
* exact string the operator must type); findings without one (membership
|
|
21
|
+
* listing could not resolve an id) fall back to the
|
|
22
|
+
* `remediationPlaceholderPhysicalId` address, same as the plan gate — the
|
|
23
|
+
* shared builder is what keeps this [G6] address byte-agreed with the
|
|
24
|
+
* journal target's physicalId, so the consent covers the target operated on.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildDriftRepairTicket(findings: readonly DriftFinding[]): DestructionTicket | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* [I1] The verbs a drift-DETECT surface may ADVERTISE for one finding — the
|
|
29
|
+
* verdict vocabulary reduced to what this gate would let execute. The
|
|
30
|
+
* drift-detect twin of `advertisableRemediationVerbs`: a confirmed-deleted
|
|
31
|
+
* finding tickets as `resource-deleted` (see `buildDriftRepairTicket` above),
|
|
32
|
+
* whose only execution arm is forget, so advertising recreate sells a consent
|
|
33
|
+
* the execution check withholds (consent-not-executable). Non-ticketing
|
|
34
|
+
* verdicts (unconfirmed, stack-missing, present) keep their vocabulary
|
|
35
|
+
* unchanged — they never enter the consent ceremony, so there is no gate to
|
|
36
|
+
* disagree with and `repair` stays the honest report-only teach.
|
|
37
|
+
* Consumers: the CLI drift-detect agent block rows + confirmed-verbs help
|
|
38
|
+
* line and the human `formatVerbList` line.
|
|
39
|
+
*/
|
|
40
|
+
export declare function advertisableDriftVerbs(finding: Pick<DriftFinding, "verdict" | "verbs">): RemediationVerb[];
|
|
41
|
+
export interface RunDriftRepairGateParams {
|
|
42
|
+
ticket: DestructionTicket;
|
|
43
|
+
consents: readonly DestructionConsent[];
|
|
44
|
+
/** Digest the consents were minted against (agent two-step) — TOCTOU. */
|
|
45
|
+
consentDigest?: string;
|
|
46
|
+
}
|
|
47
|
+
export type DriftRepairGateOutcome =
|
|
48
|
+
/** Every resource consented with an executable verb — run the surgery. */
|
|
49
|
+
{
|
|
50
|
+
kind: "proceed";
|
|
51
|
+
/** The consented verb per exact physical name. */
|
|
52
|
+
verbsByPhysicalName: ReadonlyMap<string, RemediationVerb>;
|
|
53
|
+
report: DestructionConsentReport;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "withheld";
|
|
56
|
+
outcome: DestructionOutcome;
|
|
57
|
+
report: DestructionConsentReport;
|
|
58
|
+
reasons: string[];
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Evaluate repair consents — the exact fail-closed semantics of the deploy
|
|
62
|
+
* gate (matched/unmatched/wrong-verb rows, uncovered fails closed, digest
|
|
63
|
+
* mismatch stales everything, abort verb = operator decline, non-executable
|
|
64
|
+
* verb withholds truthfully).
|
|
65
|
+
*/
|
|
66
|
+
export declare function runDriftRepairGate(params: RunDriftRepairGateParams): DriftRepairGateOutcome;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{remediationPlaceholderPhysicalId as u}from"../../types/remediation.js";import{computeTicketDigest as f,evaluateDestructionConsents as p,executableRemediationVerbs as n,legalRemediationVerbs as m}from"../application/destructionGate.js";const b="drift-repair";function g(c){const i=c.filter(e=>e.verdict==="confirmed-deleted").map(e=>({stack:e.stackName,logicalId:e.logicalId,resourceType:e.resourceType,physicalName:e.physicalId??u(e.stackName,e.logicalId),finding:"resource-deleted",cause:`deleted outside CloudFormation \u2014 ${e.verdictDetail}`,offendingProperties:[],verdictSource:"drift-probe",dataLossRisk:!1,verbs:[...m("resource-deleted")]}));if(i.length!==0)return{ticketDigest:f(b,i),resources:i}}function D(c){if(c.verdict!=="confirmed-deleted")return[...c.verbs];const i=n("resource-deleted");return c.verbs.filter(e=>i.includes(e))}function N(c){const{ticket:i,consents:e,consentDigest:d}=c,o=p({ticket:i,consents:e,...d!==void 0?{consentDigest:d}:{}}),l=new Map(i.resources.map(t=>[t.physicalName,t])),s=[],a=[];if(o.satisfied)for(const t of o.rows){if(t.verdict!=="matched")continue;const r=l.get(t.physicalName);if(r!==void 0){if(t.verb==="abort"){s.push(`${t.physicalName}: aborted by operator consent`);continue}n(r.finding).includes(t.verb)||a.push(`${t.physicalName}: verb '${t.verb}' is accepted vocabulary but not yet executable via 'fjall drift repair' for ${r.finding} \u2014 executable today: ${n(r.finding).join(", ")||"none (abort only)"}`)}}return o.satisfied&&s.length===0&&a.length===0?{kind:"proceed",verbsByPhysicalName:new Map(o.rows.filter(r=>r.verdict==="matched").map(r=>[r.physicalName,r.verb])),report:o}:{kind:"withheld",outcome:s.length>0?"declined":a.length>0?"consent-not-executable":o.rows.length>0||o.digestMismatch?"consents-invalid":"awaiting-consent",report:o,reasons:[...s,...a]}}export{D as advertisableDriftVerbs,g as buildDriftRepairTicket,N as runDriftRepairGate};
|