@fjall/deploy-core 14.1.0 → 14.3.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/.build-source-hash +21 -19
- package/dist/.minified +1 -1
- package/dist/src/index.d.ts +9 -7
- package/dist/src/index.js +1 -1
- package/dist/src/orchestration/application/applicationDeploy.js +1 -1
- package/dist/src/orchestration/domain/WorkerDomainDeployProvider.d.ts +64 -5
- package/dist/src/orchestration/domain/WorkerDomainDeployProvider.js +1 -1
- package/dist/src/orchestration/domain/cascadeDomainProviderFactory.d.ts +61 -0
- package/dist/src/orchestration/domain/cascadeDomainProviderFactory.js +1 -0
- package/dist/src/orchestration/domain/delegationResolution.d.ts +46 -0
- package/dist/src/orchestration/domain/delegationResolution.js +1 -0
- package/dist/src/orchestration/domain/domainTakeoverGate.d.ts +45 -2
- package/dist/src/orchestration/domain/domainTakeoverGate.js +2 -2
- package/dist/src/orchestration/domain/index.d.ts +3 -1
- package/dist/src/orchestration/domain/index.js +1 -1
- package/dist/src/orchestration/domainInterface.d.ts +32 -0
- package/dist/src/orchestration/index.d.ts +2 -2
- package/dist/src/orchestration/index.js +1 -1
- package/dist/src/orchestration/organisation/cascadeHelpers.d.ts +20 -1
- package/dist/src/orchestration/organisation/cascadeHelpers.js +1 -1
- package/dist/src/orchestration/organisationDeploy/cascadeExecution.js +1 -1
- package/dist/src/services/infrastructure/CdkArgumentBuilder.js +1 -1
- package/dist/src/services/infrastructure/CdkServiceTypes.d.ts +11 -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/types/deployment/DeploymentTypes.d.ts +1 -0
- package/dist/src/types/params.d.ts +22 -1
- package/package.json +3 -3
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { CloudFormationClient } from "@aws-sdk/client-cloudformation";
|
|
2
|
+
import { type Result } from "@fjall/generator";
|
|
3
|
+
import type { AwsCredentials } from "../../types/credentials.js";
|
|
4
|
+
import type { DomainHome, DomainProviderFactory, DomainDeployProvider } from "../domainInterface.js";
|
|
5
|
+
import { WorkerDomainDeployProvider } from "./WorkerDomainDeployProvider.js";
|
|
6
|
+
/**
|
|
7
|
+
* Per-home credential strategy for `createCascadeDomainProviderFactory`. The
|
|
8
|
+
* returned credentials MUST be bound to the home they were minted for
|
|
9
|
+
* (`accountId`/`region` echo the home) — the factory refuses a mismatch
|
|
10
|
+
* fail-closed rather than deploy one home under another home's identity.
|
|
11
|
+
*
|
|
12
|
+
* CLI: assumes the home's CascadeDeploymentRole from the ambient management
|
|
13
|
+
* identity (`assumeCascadeRole`). Worker: mints a per-home OIDC session.
|
|
14
|
+
*/
|
|
15
|
+
export type HomeCredentialsMinter = (home: DomainHome) => Promise<Result<AwsCredentials>>;
|
|
16
|
+
export interface CascadeDomainProviderFactoryParams {
|
|
17
|
+
/** Injected credential strategy — the only surface-specific ingredient. */
|
|
18
|
+
mintHomeCredentials: HomeCredentialsMinter;
|
|
19
|
+
/** Absolute path of the checkout the cascade deploys domain components from. */
|
|
20
|
+
workingDirectory: string;
|
|
21
|
+
/** Repo-relative boundary directory (e.g. `fjall`) holding fjall-config.json. */
|
|
22
|
+
boundaryDir: string;
|
|
23
|
+
/** Serialised org config threaded into each provider's CDK context. */
|
|
24
|
+
orgConfigSerialised?: string;
|
|
25
|
+
/**
|
|
26
|
+
* The EXECUTING identity's CloudFormation reader, seeded into the
|
|
27
|
+
* foreign-parent registry. The executing home never passes through the
|
|
28
|
+
* factory, yet it is the most common PARENT home of all: a routed child
|
|
29
|
+
* whose apex lives with the management account resolves the delegation-role
|
|
30
|
+
* export through this seed.
|
|
31
|
+
*/
|
|
32
|
+
executingParentReader?: {
|
|
33
|
+
accountId: string;
|
|
34
|
+
cfn: Pick<CloudFormationClient, "send">;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Surface-specific wrap of each minted provider — e.g. the CLI's
|
|
38
|
+
* deploy-state + P6 reporting decorator. The concrete worker type is
|
|
39
|
+
* deliberate: decorators may rely on every provider seam being present.
|
|
40
|
+
*/
|
|
41
|
+
decorateProvider?: (provider: WorkerDomainDeployProvider, home: DomainHome) => DomainDeployProvider;
|
|
42
|
+
abortSignal?: AbortSignal;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* T2 account routing, shared assembly: build a `DomainProviderFactory` whose
|
|
46
|
+
* groups deploy through `WorkerDomainDeployProvider` under per-home
|
|
47
|
+
* credentials. The credential strategy is injected; everything
|
|
48
|
+
* strategy-independent lives here once — provider construction, the group's
|
|
49
|
+
* AwsProvider, and the shared foreign-parent reader registry.
|
|
50
|
+
*
|
|
51
|
+
* The registry is the subtle part: a routed child whose PARENT zone lives in
|
|
52
|
+
* another home needs a CloudFormation reader for the parent's account to read
|
|
53
|
+
* the delegation-role export there (`resolveForeignParentCfn` rung). Every
|
|
54
|
+
* minted home registers its client; resolution happens at DEPLOY time, after
|
|
55
|
+
* the engine's pass-1 minting loop has registered every home, so a group
|
|
56
|
+
* minted early still sees clients registered later. First-mint-wins keeps the
|
|
57
|
+
* registry deterministic (homes mint in config first-appearance order) and
|
|
58
|
+
* keeps the executing seed authoritative when a foreign home shares the
|
|
59
|
+
* executing ACCOUNT under another region.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createCascadeDomainProviderFactory(params: CascadeDomainProviderFactoryParams): DomainProviderFactory;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var u=Object.defineProperty;var i=(e,t)=>u(e,"name",{value:t,configurable:!0});import{CloudFormationClient as g}from"@aws-sdk/client-cloudformation";import{success as s,failure as a}from"@fjall/generator";import{SimpleAwsProvider as f}from"../../aws/SimpleAwsProvider.js";import{WorkerDomainDeployProvider as l}from"./WorkerDomainDeployProvider.js";function D(e){const t=new Map;e.executingParentReader!==void 0&&t.set(e.executingParentReader.accountId,e.executingParentReader.cfn);const c=i(r=>t.get(r),"resolveForeignParentCfn");return async r=>{const n=await e.mintHomeCredentials(r);if(!n.success)return a(n.error);if(n.data.accountId!==r.accountId||n.data.region!==r.region)return a(new Error(`account routing refused: credentials minted for home ${r.accountId}/${r.region} report ${n.data.accountId}/${n.data.region} \u2014 deploying a home under another home's identity is not an option.`));const d=new f(n.data),o=await l.create({workingDirectory:e.workingDirectory,boundaryDir:e.boundaryDir,credentials:n.data,...e.orgConfigSerialised!==void 0?{orgConfigSerialised:e.orgConfigSerialised}:{},resolveForeignParentCfn:c,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});return o.success?(t.has(r.accountId)||t.set(r.accountId,d.getClient(g)),s({provider:e.decorateProvider?.(o.data,r)??o.data,aws:d})):a(o.error)}}i(D,"createCascadeDomainProviderFactory");export{D as createCascadeDomainProviderFactory};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Result, type ParsedDomainProps } from "@fjall/generator";
|
|
2
|
+
import type { DelegatedZonePhaseState } from "./delegatedDomainDeploy.js";
|
|
3
|
+
import { DomainDelegationError } from "./types.js";
|
|
4
|
+
import type { AwsSdkClientLike } from "./zoneClassifier.js";
|
|
5
|
+
/** Parsed identity of a delegated child domain component. */
|
|
6
|
+
export interface DelegatedDomainTopology {
|
|
7
|
+
/** The parent zone the NS delegation lives in, e.g. `example.com`. */
|
|
8
|
+
readonly parentZoneName: string;
|
|
9
|
+
/** The effective child zone, e.g. `dev.example.com`. */
|
|
10
|
+
readonly childZoneName: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Derive a delegated child's topology from its parsed Domain props.
|
|
14
|
+
* `undefined` means the component is NOT a delegated child (registrar
|
|
15
|
+
* `route53` / `external-records`) — callers route those down the
|
|
16
|
+
* single-phase path, which is then CORRECT, not a downgrade.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readDelegatedTopologyFromProps(parsed: ParsedDomainProps): DelegatedDomainTopology | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* The typed fail-closed stop for a parent DelegationRole that cannot be
|
|
21
|
+
* used: proceeding would deploy a child zone whose NS delegation can never
|
|
22
|
+
* be written, stalling at the NS-propagation gate with a worse message.
|
|
23
|
+
* `cures` is host-specific — which remedies exist depends on where the
|
|
24
|
+
* deploy runs (the CLI can mint parent-account credentials; the worker
|
|
25
|
+
* cannot) — so each host names its own.
|
|
26
|
+
*/
|
|
27
|
+
export declare function delegationRoleUnusableError(parentZoneName: string, detail: string, cures: string): DomainDelegationError;
|
|
28
|
+
/**
|
|
29
|
+
* DescribeStacks on a domain's stack, reduced to a Map keyed by EXPORT
|
|
30
|
+
* name. `success(undefined)` is the stack-not-found state; callers own its
|
|
31
|
+
* meaning (fail-closed for a managed parent, not-applicable for an audit).
|
|
32
|
+
*/
|
|
33
|
+
export declare function readDomainStackOutputsByExport(cloudFormation: AwsSdkClientLike, domainName: string, abortSignal?: AbortSignal): Promise<Result<Map<string, string> | undefined, Error>>;
|
|
34
|
+
/**
|
|
35
|
+
* Extract the parent stack's delegation-role export from its output map.
|
|
36
|
+
* `undefined` covers both an absent export and an empty value — a stack
|
|
37
|
+
* predating the export contract publishes neither usably.
|
|
38
|
+
*/
|
|
39
|
+
export declare function extractDelegationRoleArn(outputs: Map<string, string>, parentZoneName: string): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Derive the delegated child's zone-phase state from its stack outputs:
|
|
42
|
+
* the zone phase is complete when the hosted-zone-id export is present; the
|
|
43
|
+
* nameservers export feeds the propagation gate's expected-NS check.
|
|
44
|
+
* `undefined` outputs (stack not deployed) is an incomplete zone phase.
|
|
45
|
+
*/
|
|
46
|
+
export declare function deriveZonePhaseState(outputs: Map<string, string> | undefined, childZoneName: string): DelegatedZonePhaseState;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var m=Object.defineProperty;var s=(e,t)=>m(e,"name",{value:t,configurable:!0});import{DescribeStacksCommand as c}from"@aws-sdk/client-cloudformation";import{success as d,failure as l}from"@fjall/generator";import{getDomainExportNames as u,getDomainStackName as f,getErrorMessage as p}from"@fjall/util";import{STACK_NOT_FOUND_PATTERN as g}from"../../types/constants.js";import{DomainDelegationError as N}from"./types.js";function $(e){if(e.registrar==="external-delegated")return{parentZoneName:e.zoneName,childZoneName:`${e.delegatedSubdomain}.${e.zoneName}`}}s($,"readDelegatedTopologyFromProps");function b(e,t,n){return new N(`Parent zone ${e}: its DelegationRole could not be resolved (${t}). Nothing was deployed. Cures: ${n}`,"delegation_role_unusable",{parentZoneName:e})}s(b,"delegationRoleUnusableError");async function w(e,t,n){const o=f(t);try{const a=await e.send(new c({StackName:o}),n!==void 0?{abortSignal:n}:{}),i=new Map;for(const r of a.Stacks?.[0]?.Outputs??[])r.ExportName!==void 0&&r.OutputValue!==void 0&&i.set(r.ExportName,r.OutputValue);return d(i)}catch(a){return a instanceof Error&&a.message.includes(g)?d(void 0):l(new Error(`DescribeStacks on '${o}' failed: ${p(a)}`))}}s(w,"readDomainStackOutputsByExport");function y(e,t){const n=u(t).delegationRoleArn,o=e.get(n);return o!==void 0&&o!==""?o:void 0}s(y,"extractDelegationRoleArn");function O(e,t){if(e===void 0)return{complete:!1};const n=u(t),o=e.get(n.hostedZoneId),a=o!==void 0&&o!=="",i=e.get(n.nameservers)?.split(",").map(r=>r.trim()).filter(r=>r!=="");return{complete:a,...i!==void 0&&i.length>0?{nameServers:i}:{}}}s(O,"deriveZonePhaseState");export{b as delegationRoleUnusableError,O as deriveZonePhaseState,y as extractDelegationRoleArn,$ as readDelegatedTopologyFromProps,w as readDomainStackOutputsByExport};
|
|
@@ -24,7 +24,12 @@ import { type DeletedRecordSet } from "./route53Release.js";
|
|
|
24
24
|
* keeps its richer per-domain interactive ceremony; this gate is the floor,
|
|
25
25
|
* not a replacement.
|
|
26
26
|
*/
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Everything the gate needs from the cascade's execution context. This is the
|
|
29
|
+
* EXECUTING identity's context (plus the cascade-scoped consent inputs) —
|
|
30
|
+
* `deployDomains` widens it into one `DomainsTakeoverGroup` per home when a
|
|
31
|
+
* `DomainProviderFactory` routes foreign-pinned domains (T2).
|
|
32
|
+
*/
|
|
28
33
|
export interface DomainsTakeoverContext {
|
|
29
34
|
aws: AwsProvider;
|
|
30
35
|
cfnService: Pick<CloudFormationService, "getTemplate">;
|
|
@@ -38,6 +43,36 @@ export interface DomainsTakeoverContext {
|
|
|
38
43
|
route53Client?: Pick<Route53Client, "send">;
|
|
39
44
|
abortSignal?: AbortSignal;
|
|
40
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* One home's slice of the gate (T2 account routing): its domains are synthed
|
|
48
|
+
* by ITS provider, their deployed templates read through ITS CloudFormation
|
|
49
|
+
* service, and consented releases deleted through ITS Route53 credentials —
|
|
50
|
+
* a foreign zone's records exist only in the foreign account, so running any
|
|
51
|
+
* arm through the executing identity would probe/mutate the wrong account.
|
|
52
|
+
* Consent stays cascade-scoped (`CascadeTakeoverConsentContext`): the
|
|
53
|
+
* operator answers ONE ticket for the whole cascade, however many homes it
|
|
54
|
+
* spans.
|
|
55
|
+
*/
|
|
56
|
+
export interface DomainsTakeoverGroup {
|
|
57
|
+
/** The home this group deploys into — for diagnostics grammar. */
|
|
58
|
+
readonly home: {
|
|
59
|
+
readonly accountId?: string;
|
|
60
|
+
readonly region?: string;
|
|
61
|
+
};
|
|
62
|
+
readonly provider: DomainDeployProvider;
|
|
63
|
+
readonly aws: AwsProvider;
|
|
64
|
+
readonly cfnService: Pick<CloudFormationService, "getTemplate">;
|
|
65
|
+
/** Injectable Route53 client for this group's release arm (tests). */
|
|
66
|
+
readonly route53Client?: Pick<Route53Client, "send">;
|
|
67
|
+
readonly domains: readonly DomainConfig[];
|
|
68
|
+
}
|
|
69
|
+
/** The cascade-scoped consent inputs — shared across every group. */
|
|
70
|
+
export interface CascadeTakeoverConsentContext {
|
|
71
|
+
readonly consents?: readonly DestructionConsent[];
|
|
72
|
+
readonly consentDigest?: string;
|
|
73
|
+
readonly consentGate?: DestructionConsentGate;
|
|
74
|
+
readonly abortSignal?: AbortSignal;
|
|
75
|
+
}
|
|
41
76
|
export interface DomainsTakeoverWithheld {
|
|
42
77
|
readonly ticket: DestructionTicket;
|
|
43
78
|
readonly outcome: DestructionOutcome;
|
|
@@ -67,5 +102,13 @@ export declare function buildCascadeTakeoverTicket(zoneNames: readonly string[],
|
|
|
67
102
|
* `abort` verb is an operator withhold, no blanket flag exists, and every
|
|
68
103
|
* withheld path renders the full ticket + verdict table through the warning
|
|
69
104
|
* seam so non-interactive runs stop naming the exact consent spelling.
|
|
105
|
+
*
|
|
106
|
+
* T2 account routing: assessment and release run PER GROUP through each
|
|
107
|
+
* group's own credentials, but the ticket unions every group's collisions
|
|
108
|
+
* and consent is evaluated ONCE against it — one cascade, one ceremony,
|
|
109
|
+
* however many homes the domains span. Groups assess sequentially (in the
|
|
110
|
+
* caller's order) so warning output stays deterministic, and one group's
|
|
111
|
+
* fail-closed assessment refuses the WHOLE phase: a cascade that cannot
|
|
112
|
+
* assess every eligible domain must not deploy any of them.
|
|
70
113
|
*/
|
|
71
|
-
export declare function runCascadeTakeoverGate(
|
|
114
|
+
export declare function runCascadeTakeoverGate(groups: readonly DomainsTakeoverGroup[], callbacks: DeployCallbacks, context: CascadeTakeoverConsentContext): Promise<Result<DomainsTakeoverGateOutcome>>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)))}
|
|
1
|
+
var N=Object.defineProperty;var g=(s,r)=>N(s,"name",{value:r,configurable:!0});import{Route53Client as R}from"@aws-sdk/client-route-53";import{success as w,failure as v}from"@fjall/generator";import{maskSensitiveOutput as C}from"@fjall/util";import{computeTicketDigest as E,evaluateDestructionConsents as T,renderConsentVerdictLines as I,renderDestructionTicketLines as P}from"../application/destructionGate.js";import{emitWarningEvent as u}from"../contextHelpers.js";import{runRoute53RecordPreflight as z}from"../drift/route53RecordPreflight.js";import{recordPhysicalName as $}from"./recordConsentSpelling.js";import{deleteRecordSetsByKey as L,recordSetKey as F}from"./route53Release.js";function M(s){return`domain-deploy-takeover:${[...s].sort().join(",")}`}g(M,"cascadeTakeoverDigestSeed");function G(s,r){const n=r.map(t=>{const c=$(t.canonicalName,t.type,t.liveSetIdentifier);return{stack:`zone/${t.zoneId}`,logicalId:`Takeover ${c}`,resourceType:"AWS::Route53::RecordSet",physicalName:c,finding:"data-loss-delete",cause:C(`live record in zone ${t.zoneId} (${t.liveDescription}) is not owned by this stack's previous template \u2014 consenting RELEASES it: the live record is deleted so this deploy's CloudFormation stack recreates '${t.declaredName}' (${t.type}) from the declaration, a short DNS gap bounded by the deploy; consent with --remediate '${c}'=recreate`),offendingProperties:[],verdictSource:"drift-probe",dataLossRisk:!0,verbs:["abort","recreate"]}});return{ticketDigest:E(M(s),n),resources:n}}g(G,"buildCascadeTakeoverTicket");async function j(s,r,n){const{provider:t}=s;if(t.synthForTakeover===void 0)return v(new Error("domains phase refused: this DomainDeployProvider does not implement synthForTakeover, so the record-takeover gate cannot assess collisions \u2014 deploying ungated is not an option. Cure: implement synthForTakeover on the provider (CliDomainDeployProvider and WorkerDomainDeployProvider both do)."));const c=[];for(const p of s.domains){const d=await t.synthForTakeover(p.name,r);if(!d.success)return v(new Error(`[${p.name}] takeover assessment synth failed: ${C(d.error.message)}`));const h=await z(s.aws,{stackNames:d.data.stackNames,cdkOutPath:d.data.cdkOutPath,cfnService:s.cfnService,onLog:g(l=>u(r,l),"onLog"),onUnreadableSynthTemplate:"block",...n!==void 0?{abortSignal:n}:{}});if(h.kind==="proceed")continue;const o=h.collisions,f=(o??[]).filter(l=>!l.consentable);if(o===void 0||f.length>0){const l=f.map(m=>` - ${$(m.canonicalName,m.type,m.liveSetIdentifier)}: ${m.nonConsentableReason??"not consentable"}`);return v(new Error([`[${p.name}] ${h.message}`,...l].join(`
|
|
2
|
+
`)))}c.push({group:s,domain:p,collisions:o})}return w(c)}g(j,"assessGroupDomains");async function q(s,r,n){const t=[];for(const e of s){const i=await j(e,r,n.abortSignal);if(!i.success)return v(i.error);t.push(...i.data)}if(t.length===0)return n.consents!==void 0&&n.consents.length>0&&u(r,"no record collisions \u2014 supplied consents were not needed"),w({kind:"clear",released:[]});const c=t.map(e=>e.domain.name),p=t.flatMap(e=>e.collisions),d=G(c,p),h=n.consents??[];let o=T({ticket:d,consents:h,...n.consentDigest!==void 0?{consentDigest:n.consentDigest}:{}});const f=[];if(!o.satisfied&&n.consentGate!==void 0&&h.length===0&&n.consentDigest===void 0){const e=await n.consentGate.resolve({target:`${c.join(", ")} record takeover`,ticket:d});e.kind==="abort"?f.push("declined at the interactive destruction ceremony \u2014 nothing was deleted, nothing was deployed"):o=T({ticket:d,consents:e.consents,consentDigest:e.ticketDigest})}const l=o.satisfied&&o.rows.some(e=>e.verdict==="matched"&&e.verb==="abort");if(!o.satisfied||l||f.length>0){const e=f.length>0||l?"declined":o.rows.length>0||o.digestMismatch?"consents-invalid":"awaiting-consent";for(const i of P(d))u(r,i);for(const i of I(o))u(r,i);for(const i of f)u(r,i);return e!=="declined"&&u(r,`Consent with --remediate '<name>/<TYPE>[@<setIdentifier>]'=recreate for EVERY row on the ticket (e.g. --remediate '${d.resources[0]?.physicalName??"<name>/<TYPE>"}'=recreate) (exact, case-sensitive). No blanket flag exists: --auto-approve, --skip-confirmation and approval tokens never satisfy this gate.`),w({kind:"withheld",withheld:{ticket:d,outcome:e,...o.rows.length>0||o.digestMismatch?{report:o}:{},reasons:f}})}const m=[];for(const e of s){const i=t.filter(a=>a.group===e).flatMap(a=>a.collisions);if(i.length===0)continue;const b=e.route53Client??e.aws.getClient(R),D=new Map;for(const a of i){const y=D.get(a.zoneId)??new Set;y.add(F(a.canonicalName,a.type,a.liveSetIdentifier)),D.set(a.zoneId,y)}for(const[a,y]of D){const k=await L(b,a,y,n.abortSignal);if(!k.success)return v(k.error);for(const S of k.data)u(r,`Released live record ${$(S.name,S.type,S.setIdentifier)} from zone ${a} \u2014 this deploy's stack recreates it from the declaration`);m.push(...k.data)}}return w({kind:"clear",released:m})}g(q,"runCascadeTakeoverGate");export{G as buildCascadeTakeoverTicket,M as cascadeTakeoverDigestSeed,q as runCascadeTakeoverGate};
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
export { DOMAIN_DELEGATION_ERROR_TYPES, DomainDelegationError, DEFAULT_PUBLIC_RESOLVER_ADDRESSES, PUBLIC_DNS_QUERY_TIMEOUT_MS, createPublicResolvers, normaliseDnsName, isDnsEmptyAnswer, type DomainDeployPhase, type DomainDelegationErrorType, type PublicCaaRecord, type PublicDnsResolver } from "./types.js";
|
|
7
7
|
export { NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS, NS_PROPAGATION_DEFAULT_INTERVAL_MS, waitForNsPropagation, type NsPropagationGateParams, type NsPropagationVerdict } from "./nsPropagationGate.js";
|
|
8
8
|
export { ACM_CAA_ISSUERS, caaAncestorChain, formatCaaRecord, runCaaPreflight, type CaaPreflightParams, type CaaPreflightVerdict } from "./caaPreflight.js";
|
|
9
|
+
export { delegationRoleUnusableError, deriveZonePhaseState, extractDelegationRoleArn, readDelegatedTopologyFromProps, readDomainStackOutputsByExport, type DelegatedDomainTopology } from "./delegationResolution.js";
|
|
9
10
|
export { deployDelegatedDomain, type DelegatedDomainDeployer, type DelegatedZonePhaseState, type DeployDelegatedDomainParams, type DelegatedDomainDeployOutcome } from "./delegatedDomainDeploy.js";
|
|
10
11
|
export { CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE, templateOwnsDelegationRecord, deleteChildNsFromParent, nsDelegationPhysicalName, runDelegatedDomainDestroy, type NsDeleteOutcome, type DeleteChildNsParams, type DelegationDestroyNsOutcome, type DelegatedDomainDestroyOutcome, type DelegatedDomainDestroyParams } from "./delegationDestroyMirror.js";
|
|
11
12
|
export { classifyZoneRecords, buildSatelliteRecordIndex, createSystemDnsNameProbe, systemDnsNameProbe, normaliseRecordName, type RecordClassification, type ClassifiedRecord, type ZoneClassificationReport, type ZoneClassifierClients, type ZoneClassifierPhase, type ClassifyZoneOptions, type SatelliteRecordIndex, type DelegatedChildDomain, type ChildAccountClients, type ChildAccountClientFactory, type AwsSdkClientLike, type DnsProbeVerdict, type DnsNameProbe } from "./zoneClassifier.js";
|
|
12
13
|
export { isRoute53NameServer, isDelegatedToRoute53 } from "./route53Delegation.js";
|
|
13
14
|
export { recordPhysicalName, parseRecordPhysicalName, type RecordConsentIdentity } from "./recordConsentSpelling.js";
|
|
14
15
|
export { recordSetKey, deleteRecordSetsByKey, type DeletedRecordSet } from "./route53Release.js";
|
|
15
|
-
export { cascadeTakeoverDigestSeed, buildCascadeTakeoverTicket, runCascadeTakeoverGate, type DomainsTakeoverContext, type DomainsTakeoverWithheld, type DomainsTakeoverGateOutcome } from "./domainTakeoverGate.js";
|
|
16
|
+
export { cascadeTakeoverDigestSeed, buildCascadeTakeoverTicket, runCascadeTakeoverGate, type DomainsTakeoverContext, type DomainsTakeoverGroup, type CascadeTakeoverConsentContext, type DomainsTakeoverWithheld, type DomainsTakeoverGateOutcome } from "./domainTakeoverGate.js";
|
|
16
17
|
export { WorkerDomainDeployProvider, createWorkerDomainDeployProvider, type WorkerDomainDeployProviderParams } from "./WorkerDomainDeployProvider.js";
|
|
18
|
+
export { createCascadeDomainProviderFactory, type HomeCredentialsMinter, type CascadeDomainProviderFactoryParams } from "./cascadeDomainProviderFactory.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{DOMAIN_DELEGATION_ERROR_TYPES as o,DomainDelegationError as a,DEFAULT_PUBLIC_RESOLVER_ADDRESSES as t,PUBLIC_DNS_QUERY_TIMEOUT_MS as s,createPublicResolvers as m,normaliseDnsName as i,isDnsEmptyAnswer as
|
|
1
|
+
import{DOMAIN_DELEGATION_ERROR_TYPES as o,DomainDelegationError as a,DEFAULT_PUBLIC_RESOLVER_ADDRESSES as t,PUBLIC_DNS_QUERY_TIMEOUT_MS as s,createPublicResolvers as m,normaliseDnsName as i,isDnsEmptyAnswer as l}from"./types.js";import{NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS as d,NS_PROPAGATION_DEFAULT_INTERVAL_MS as D,waitForNsPropagation as c}from"./nsPropagationGate.js";import{ACM_CAA_ISSUERS as E,caaAncestorChain as S,formatCaaRecord as p,runCaaPreflight as _}from"./caaPreflight.js";import{delegationRoleUnusableError as T,deriveZonePhaseState as A,extractDelegationRoleArn as N,readDelegatedTopologyFromProps as y,readDomainStackOutputsByExport as f}from"./delegationResolution.js";import{deployDelegatedDomain as C}from"./delegatedDomainDeploy.js";import{CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE as g,templateOwnsDelegationRecord as u,deleteChildNsFromParent as I,nsDelegationPhysicalName as U,runDelegatedDomainDestroy as v}from"./delegationDestroyMirror.js";import{classifyZoneRecords as h,buildSatelliteRecordIndex as k,createSystemDnsNameProbe as F,systemDnsNameProbe as M,normaliseRecordName as b}from"./zoneClassifier.js";import{isRoute53NameServer as B,isDelegatedToRoute53 as w}from"./route53Delegation.js";import{recordPhysicalName as K,parseRecordPhysicalName as V}from"./recordConsentSpelling.js";import{recordSetKey as Z,deleteRecordSetsByKey as Q}from"./route53Release.js";import{cascadeTakeoverDigestSeed as j,buildCascadeTakeoverTicket as q,runCascadeTakeoverGate as z}from"./domainTakeoverGate.js";import{WorkerDomainDeployProvider as J,createWorkerDomainDeployProvider as $}from"./WorkerDomainDeployProvider.js";import{createCascadeDomainProviderFactory as re}from"./cascadeDomainProviderFactory.js";export{E as ACM_CAA_ISSUERS,g as CROSS_ACCOUNT_DELEGATION_RESOURCE_TYPE,t as DEFAULT_PUBLIC_RESOLVER_ADDRESSES,o as DOMAIN_DELEGATION_ERROR_TYPES,a as DomainDelegationError,D as NS_PROPAGATION_DEFAULT_INTERVAL_MS,d as NS_PROPAGATION_DEFAULT_MAX_ATTEMPTS,s as PUBLIC_DNS_QUERY_TIMEOUT_MS,J as WorkerDomainDeployProvider,q as buildCascadeTakeoverTicket,k as buildSatelliteRecordIndex,S as caaAncestorChain,j as cascadeTakeoverDigestSeed,h as classifyZoneRecords,re as createCascadeDomainProviderFactory,m as createPublicResolvers,F as createSystemDnsNameProbe,$ as createWorkerDomainDeployProvider,T as delegationRoleUnusableError,I as deleteChildNsFromParent,Q as deleteRecordSetsByKey,C as deployDelegatedDomain,A as deriveZonePhaseState,N as extractDelegationRoleArn,p as formatCaaRecord,w as isDelegatedToRoute53,l as isDnsEmptyAnswer,B as isRoute53NameServer,i as normaliseDnsName,b as normaliseRecordName,U as nsDelegationPhysicalName,V as parseRecordPhysicalName,y as readDelegatedTopologyFromProps,f as readDomainStackOutputsByExport,K as recordPhysicalName,Z as recordSetKey,_ as runCaaPreflight,z as runCascadeTakeoverGate,v as runDelegatedDomainDestroy,M as systemDnsNameProbe,u as templateOwnsDelegationRecord,c as waitForNsPropagation};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Result } from "@fjall/generator";
|
|
2
|
+
import type { AwsProvider } from "../aws/AwsProvider.js";
|
|
2
3
|
import type { DeployCallbacks } from "../types/callbacks.js";
|
|
3
4
|
import type { DelegatedDomainDeployer } from "./domain/delegatedDomainDeploy.js";
|
|
4
5
|
import type { DomainsTakeoverWithheld } from "./domain/domainTakeoverGate.js";
|
|
@@ -93,6 +94,37 @@ export interface DomainDeployProvider {
|
|
|
93
94
|
*/
|
|
94
95
|
synthForTakeover?(domainName: string, callbacks: DeployCallbacks): Promise<Result<DomainTakeoverSynth>>;
|
|
95
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* The resolved home of a domain group: each pin key defaulted to the
|
|
99
|
+
* cascade's executing identity, so two entries pinning only `account` to the
|
|
100
|
+
* same value land in one group. Always fully resolved — the factory never
|
|
101
|
+
* sees a partial pin.
|
|
102
|
+
*/
|
|
103
|
+
export interface DomainHome {
|
|
104
|
+
readonly accountId: string;
|
|
105
|
+
readonly region: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Group-scoped execution context minted by a `DomainProviderFactory`: the
|
|
109
|
+
* provider deploys INTO the home, and the AWS provider holds credentials FOR
|
|
110
|
+
* it (the takeover gate's template reads and record releases run through this,
|
|
111
|
+
* never through the cascade's executing identity). Derived services
|
|
112
|
+
* (CloudFormationService) are constructed engine-side from `aws`.
|
|
113
|
+
*/
|
|
114
|
+
export interface DomainProviderGroup {
|
|
115
|
+
readonly provider: DomainDeployProvider;
|
|
116
|
+
readonly aws: AwsProvider;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* T2 account routing: mint a provider context for a home OTHER than the one
|
|
120
|
+
* the cascade executes under. `deployDomains` calls this once per distinct
|
|
121
|
+
* foreign `(account, region)` pin among the configured domains — the caller
|
|
122
|
+
* decides HOW to reach the home (worker: per-account OIDC role; CLI:
|
|
123
|
+
* OrganizationAccountAccessRole from a management identity). A failure
|
|
124
|
+
* degrades that home's domains to the pre-routing skip-with-warning, never to
|
|
125
|
+
* a phase error: routing is an upgrade over the skip, not a new way to fail.
|
|
126
|
+
*/
|
|
127
|
+
export type DomainProviderFactory = (home: DomainHome) => Promise<Result<DomainProviderGroup>>;
|
|
96
128
|
/** Assembly facts the takeover preflight reads for one domain. */
|
|
97
129
|
export interface DomainTakeoverSynth {
|
|
98
130
|
/** Stack artifact names whose templates claim Route53 record sets. */
|
|
@@ -39,7 +39,7 @@ export { buildBuildSecretSessionPolicy, partitionForRegion, type BuildSecretRef,
|
|
|
39
39
|
export { buildDeploySessionPolicy, type DeploySessionContext } from "./application/deploySessionPolicy.js";
|
|
40
40
|
export { resolveBuildArgs, type ResolveBuildArgsContext, type ResolveBuildArgsResult, type SourcedBuildArgResolver } from "./application/buildArgResolver.js";
|
|
41
41
|
export { validateBuildGroup, type ValidateBuildGroupInput, type BuildGroupMemberInputs } from "./application/buildGroupValidator.js";
|
|
42
|
-
export type { DomainDeployProvider, DomainConfig, DomainDeployResult, DomainTakeoverSynth } from "./domainInterface.js";
|
|
42
|
+
export type { DomainDeployProvider, DomainConfig, DomainDeployResult, DomainTakeoverSynth, DomainHome, DomainProviderGroup, DomainProviderFactory } from "./domainInterface.js";
|
|
43
43
|
export * from "./domain/index.js";
|
|
44
44
|
export type { DeployServices } from "./serviceFactory.js";
|
|
45
45
|
export type { DetectionResult } from "./application/detectionPipeline.js";
|
|
@@ -53,7 +53,7 @@ export { runApprovalGate, approvalRefusalReason, type RunApprovalGateParams, typ
|
|
|
53
53
|
export { runDestructionGate, buildDestructionTicket, evaluateDestructionConsents, computeTicketDigest, legalRemediationVerbs, executableRemediationVerbs, advertisableRemediationVerbs, renderDestructionTicketLines, renderConsentVerdictLines, type RunDestructionGateParams, type DestructionGateOutcome, type EvaluateDestructionConsentsParams } from "./application/destructionGate.js";
|
|
54
54
|
export { loadDeployPlan, createMemoisedPlanLoader, type LoadDeployPlanParams, type DeployPlanLoader } from "./application/plan/loadDeployPlan.js";
|
|
55
55
|
export { classifyDriftFailure, fetchLastOperationEvents } from "./drift/classifyDriftFailure.js";
|
|
56
|
-
export { DriftProbe, type DriftProbeOptions, type DriftProbeRunner, type ProbeDriftSuspectsParams } from "./drift/driftProbe.js";
|
|
56
|
+
export { DriftProbe, probePhysicalPresence, type DriftProbeOptions, type DriftProbeRunner, type PhysicalPresenceOutcome, type ProbeDriftSuspectsParams } from "./drift/driftProbe.js";
|
|
57
57
|
export { clearDriftSuspects, defaultDriftJournalDir, readDriftSuspects, recordDriftSuspects, type DriftJournalOptions, type RecordDriftSuspectsParams } from "./drift/driftSuspectJournal.js";
|
|
58
58
|
export { detectStackDrift, type DetectStackDriftDeps, type DetectStackDriftParams, type StackDriftReport } from "./drift/detectStackDrift.js";
|
|
59
59
|
export { runDriftPreFlight, formatDriftPreFlightBlock, DRIFT_PREFLIGHT_TRIGGER_STATUSES, DRIFT_PREFLIGHT_BUDGET_MS, type RunDriftPreFlightParams, type DriftPreFlightBlockExtras, type DriftPreFlightOutcome } from "./drift/driftPreFlight.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{deploy as t}from"./deploy.js";import{destroy as i}from"./destroy.js";import{restart as n,probeSecretsDrift as s,probeServiceDeploymentStatus as c}from"./restart/restartApplication.js";import{computeSecretsDrift as p,deriveStaleParameters as m,extractConsumedSsmParameters as u,ssmParameterNameFromValueFrom as f}from"./restart/secretsDrift.js";import{deployOrganisation as R}from"./organisation/organisationDeploy.js";import{destroyOrganisation as x}from"./organisation/organisationDestroy.js";import{cleanupFailedStack as T,emptyS3Bucket as y,preEmptyStackBuckets as A,formatQuarantineSuspectedMessage as E,formatRetainedBucketsMessage as D,isQuarantineDetail as g,isRetainedBucketsDetail as _,PRE_EMPTY_TAG_KEYS as O,isCleanableState as v,SAFE_CLEANUP_STATES as F}from"./stackCleanup.js";import{partitionAccounts as B,buildRegionList as L,buildAccountRegionPairs as b,cascadeHomeRegion as C,cascadeOperationKey as I}from"./organisation/cascadeHelpers.js";import{projectScalarSummary as h,projectAccountRows as G}from"./organisation/cascadeSummary.js";import{reconcileProviderAccounts as K,mergeReconciledProviderAccounts as M}from"./organisation/reconcileProviderAccounts.js";import{decideNextTransition as V,reconcileTrailMigration as J,ORG_TRAIL_BUCKET_OUTPUT_KEY as H,TRAIL_BUCKET_OUTPUT_KEY as Q,TRAIL_KEY_ARN_OUTPUT_KEY as w}from"./trailMigration/trailMigration.js";import{decommissionMemberTrailStorage as W}from"./trailMigration/memberTrailCleanup.js";import{unlockBucket as z}from"./unlock/unlockBucket.js";import{unlockQueue as Z}from"./unlock/unlockQueue.js";import{triageBucketPolicy as ee,isEnforceSslStatement as re}from"./unlock/bucketPolicyTriage.js";import{toResourcePolicyStatements as oe}from"./unlock/toResourcePolicyStatements.js";import{restoreBucketPolicy as ae,synthesiseEnforceSslDocument as ne,ensureEnforceSsl as se}from"./unlock/restoreBucketPolicy.js";import{restoreAndReconcileQuarantinedBucket as le}from"./unlock/restoreAndReconcileQuarantinedBucket.js";import{parseAccountsConfiguration as me,flattenAccountsToEnvironments as ue,extractAllAccountNames as fe,accountsConfigToOUTree as de,isStringArray as Re,isAccountsConfig as Se,isOuOnlyAccountBucket as xe,OU_ONLY_ACCOUNT_BUCKETS as Pe}from"./organisation/accountsConfig.js";import{resolveBuildSecrets as ye,resolveSecretRefValue as Ae,resolveBuildSecretSessionProvider as Ee,sourcedRefsFromBuildArgs as De}from"./application/buildSecretResolver.js";import{buildBuildSecretSessionPolicy as _e,partitionForRegion as Oe}from"./application/buildSecretSession.js";import{buildDeploySessionPolicy as Fe}from"./application/deploySessionPolicy.js";import{resolveBuildArgs as Be}from"./application/buildArgResolver.js";import{validateBuildGroup as be}from"./application/buildGroupValidator.js";export*from"./domain/index.js";import{runOpenNextBuild as Ue}from"./application/openNextBuild.js";import{runOrganisationSetup as Ge,ORG_SETUP_PHASES as Ne}from"./organisation/organisationSetup.js";export*from"./builders/index.js";import{buildDeployPlan as Ye,computeDeployPlan as Ve,computeAssemblyDigest as Je,digestHead as He,classifyImpact as Qe,derivePropertyChanges as we,isDataLoss as je,isStatefulResourceType as We,renderPlanSummary as qe,renderPlanLines as ze,toWirePlanChanges as Xe,signApprovalToken as Ze,verifyApprovalToken as $e,DEFAULT_APPROVAL_TTL_MS as er,APPROVAL_TOKEN_PATTERN as rr}from"./application/plan/index.js";import{runApprovalGate as or,approvalRefusalReason as ir}from"./application/approvalGate.js";import{runDestructionGate as nr,buildDestructionTicket as sr,evaluateDestructionConsents as cr,computeTicketDigest as lr,legalRemediationVerbs as pr,executableRemediationVerbs as mr,advertisableRemediationVerbs as ur,renderDestructionTicketLines as fr,renderConsentVerdictLines as dr}from"./application/destructionGate.js";import{loadDeployPlan as Sr,createMemoisedPlanLoader as xr}from"./application/plan/loadDeployPlan.js";import{classifyDriftFailure as Tr,fetchLastOperationEvents as yr}from"./drift/classifyDriftFailure.js";import{DriftProbe as Er}from"./drift/driftProbe.js";import{clearDriftSuspects as gr,defaultDriftJournalDir as _r,readDriftSuspects as Or,recordDriftSuspects as vr}from"./drift/driftSuspectJournal.js";import{detectStackDrift as kr}from"./drift/detectStackDrift.js";import{runDriftPreFlight as Lr,formatDriftPreFlightBlock as br,DRIFT_PREFLIGHT_TRIGGER_STATUSES as Cr,DRIFT_PREFLIGHT_BUDGET_MS as Ir}from"./drift/driftPreFlight.js";import{runRoute53RecordPreflight as hr}from"./drift/route53RecordPreflight.js";import{archiveCompletedRemediationJournal as Nr,computeRemediationOpId as Kr,defaultRemediationJournalDir as Mr,findRemediationJournalByOpId as Yr,listActiveRemediationOps as Vr,listActiveRemediationOpsForContext as Jr,readRemediationJournal as Hr,sweepExpiredRemediationJournals as Qr,writeRemediationJournal as wr,REMEDIATION_JOURNAL_RETENTION_DAYS as jr}from"./remediation/remediationJournal.js";import{captureRemediationForensics as qr,defaultRemediationForensicsDir as zr}from"./remediation/forensicsCapture.js";import{applyRetainFlip as Zr,composeFlipCapabilities as $r,computeTemplateDigest as et,deriveRetainFlipTemplate as rt,findMarkedResourcesByOpId as tt,proveFlipMetadataOnly as ot,readRetainFlipState as it,verifyRetainFlipDiff as at,FORGET_MARKER_KEY as nt,FORGET_FLIP_SPEC as st,RECREATE_MARKER_KEY as ct}from"./remediation/retainFlip.js";import{assessForgetResumability as pt,completeForgetAfterConverge as mt,completeForgetAfterDeploy as ut,formatRecreateInFlightCures as ft,partitionPreFlightByRemediation as dt,runForgetSurgery as Rt,synthTemplateDeclaresResource as St}from"./remediation/forgetResource.js";import{runRecreateSurgery as Pt,snapshotPolicyForType as Tt,SNAPSHOT_CAPABLE_RESOURCE_TYPES as yt}from"./remediation/recreateResource.js";import{runRecreatePreFlight as Et}from"./remediation/recreatePreFlight.js";import{deriveRemovalTemplate as gt,findRemovalDeletionFailures as _t,findResourcesPresent as Ot,proveRemovalTargetsOnly as vt}from"./remediation/removalUpdate.js";import{advertisableDriftVerbs as kt,buildDriftRepairTicket as Bt,runDriftRepairGate as Lt}from"./remediation/driftRepairGate.js";import{formatPinOutcomeDetail as Ct,pinProbeCoverage as It,renderPinReportLines as Ut,runPinRemediation as ht}from"./remediation/pinRemediation.js";import{composePostFailureRepairOffer as Nt,formatDriftRepairOffer as Kt,DriftRepairAvailableError as Mt}from"./remediation/postFailureRepairOffer.js";import{captureLegacySurfaces as Vt,decodeLegacyAnchor as Jt,runIdentitySurfaceGate as Ht}from"./remediation/pinIdentity.js";import{collectIdentityPinCandidates as wt,degradedIdentityPinOutcomes as jt,renderIdentityPinReportLines as Wt,runIdentityPinMigration as qt}from"./remediation/migrateIdentity.js";import{checkDeployPathStackAvailability as Xt,isDeployPathBlockingStatus as Zt,StackUnavailableError as $t}from"./activeDeploymentGuard.js";import{assertLeaseBeforeStack as ro,LeaseDeniedError as to}from"./application/leaseGate.js";export{rr as APPROVAL_TOKEN_PATTERN,er as DEFAULT_APPROVAL_TTL_MS,Ir as DRIFT_PREFLIGHT_BUDGET_MS,Cr as DRIFT_PREFLIGHT_TRIGGER_STATUSES,Er as DriftProbe,Mt as DriftRepairAvailableError,st as FORGET_FLIP_SPEC,nt as FORGET_MARKER_KEY,to as LeaseDeniedError,Ne as ORG_SETUP_PHASES,H as ORG_TRAIL_BUCKET_OUTPUT_KEY,Pe as OU_ONLY_ACCOUNT_BUCKETS,O as PRE_EMPTY_TAG_KEYS,ct as RECREATE_MARKER_KEY,jr as REMEDIATION_JOURNAL_RETENTION_DAYS,F as SAFE_CLEANUP_STATES,yt as SNAPSHOT_CAPABLE_RESOURCE_TYPES,$t as StackUnavailableError,Q as TRAIL_BUCKET_OUTPUT_KEY,w as TRAIL_KEY_ARN_OUTPUT_KEY,de as accountsConfigToOUTree,kt as advertisableDriftVerbs,ur as advertisableRemediationVerbs,Zr as applyRetainFlip,ir as approvalRefusalReason,Nr as archiveCompletedRemediationJournal,ro as assertLeaseBeforeStack,pt as assessForgetResumability,b as buildAccountRegionPairs,_e as buildBuildSecretSessionPolicy,Ye as buildDeployPlan,Fe as buildDeploySessionPolicy,sr as buildDestructionTicket,Bt as buildDriftRepairTicket,L as buildRegionList,Vt as captureLegacySurfaces,qr as captureRemediationForensics,C as cascadeHomeRegion,I as cascadeOperationKey,Xt as checkDeployPathStackAvailability,Tr as classifyDriftFailure,Qe as classifyImpact,T as cleanupFailedStack,gr as clearDriftSuspects,wt as collectIdentityPinCandidates,mt as completeForgetAfterConverge,ut as completeForgetAfterDeploy,$r as composeFlipCapabilities,Nt as composePostFailureRepairOffer,Je as computeAssemblyDigest,Ve as computeDeployPlan,Kr as computeRemediationOpId,p as computeSecretsDrift,et as computeTemplateDigest,lr as computeTicketDigest,xr as createMemoisedPlanLoader,V as decideNextTransition,Jt as decodeLegacyAnchor,W as decommissionMemberTrailStorage,_r as defaultDriftJournalDir,zr as defaultRemediationForensicsDir,Mr as defaultRemediationJournalDir,jt as degradedIdentityPinOutcomes,t as deploy,R as deployOrganisation,we as derivePropertyChanges,gt as deriveRemovalTemplate,rt as deriveRetainFlipTemplate,m as deriveStaleParameters,i as destroy,x as destroyOrganisation,kr as detectStackDrift,He as digestHead,y as emptyS3Bucket,se as ensureEnforceSsl,cr as evaluateDestructionConsents,mr as executableRemediationVerbs,fe as extractAllAccountNames,u as extractConsumedSsmParameters,yr as fetchLastOperationEvents,tt as findMarkedResourcesByOpId,Yr as findRemediationJournalByOpId,_t as findRemovalDeletionFailures,Ot as findResourcesPresent,ue as flattenAccountsToEnvironments,br as formatDriftPreFlightBlock,Kt as formatDriftRepairOffer,Ct as formatPinOutcomeDetail,E as formatQuarantineSuspectedMessage,ft as formatRecreateInFlightCures,D as formatRetainedBucketsMessage,Se as isAccountsConfig,v as isCleanableState,je as isDataLoss,Zt as isDeployPathBlockingStatus,re as isEnforceSslStatement,xe as isOuOnlyAccountBucket,g as isQuarantineDetail,_ as isRetainedBucketsDetail,We as isStatefulResourceType,Re as isStringArray,pr as legalRemediationVerbs,Vr as listActiveRemediationOps,Jr as listActiveRemediationOpsForContext,Sr as loadDeployPlan,M as mergeReconciledProviderAccounts,me as parseAccountsConfiguration,B as partitionAccounts,Oe as partitionForRegion,dt as partitionPreFlightByRemediation,It as pinProbeCoverage,A as preEmptyStackBuckets,s as probeSecretsDrift,c as probeServiceDeploymentStatus,G as projectAccountRows,h as projectScalarSummary,ot as proveFlipMetadataOnly,vt as proveRemovalTargetsOnly,Or as readDriftSuspects,Hr as readRemediationJournal,it as readRetainFlipState,K as reconcileProviderAccounts,J as reconcileTrailMigration,vr as recordDriftSuspects,dr as renderConsentVerdictLines,fr as renderDestructionTicketLines,Wt as renderIdentityPinReportLines,Ut as renderPinReportLines,ze as renderPlanLines,qe as renderPlanSummary,Be as resolveBuildArgs,Ee as resolveBuildSecretSessionProvider,ye as resolveBuildSecrets,Ae as resolveSecretRefValue,n as restart,le as restoreAndReconcileQuarantinedBucket,ae as restoreBucketPolicy,or as runApprovalGate,nr as runDestructionGate,Lr as runDriftPreFlight,Lt as runDriftRepairGate,Rt as runForgetSurgery,qt as runIdentityPinMigration,Ht as runIdentitySurfaceGate,Ue as runOpenNextBuild,Ge as runOrganisationSetup,ht as runPinRemediation,Et as runRecreatePreFlight,Pt as runRecreateSurgery,hr as runRoute53RecordPreflight,Ze as signApprovalToken,Tt as snapshotPolicyForType,De as sourcedRefsFromBuildArgs,f as ssmParameterNameFromValueFrom,Qr as sweepExpiredRemediationJournals,St as synthTemplateDeclaresResource,ne as synthesiseEnforceSslDocument,oe as toResourcePolicyStatements,Xe as toWirePlanChanges,ee as triageBucketPolicy,z as unlockBucket,Z as unlockQueue,be as validateBuildGroup,$e as verifyApprovalToken,at as verifyRetainFlipDiff,wr as writeRemediationJournal};
|
|
1
|
+
import{deploy as t}from"./deploy.js";import{destroy as i}from"./destroy.js";import{restart as n,probeSecretsDrift as s,probeServiceDeploymentStatus as c}from"./restart/restartApplication.js";import{computeSecretsDrift as p,deriveStaleParameters as m,extractConsumedSsmParameters as u,ssmParameterNameFromValueFrom as f}from"./restart/secretsDrift.js";import{deployOrganisation as R}from"./organisation/organisationDeploy.js";import{destroyOrganisation as P}from"./organisation/organisationDestroy.js";import{cleanupFailedStack as T,emptyS3Bucket as y,preEmptyStackBuckets as A,formatQuarantineSuspectedMessage as E,formatRetainedBucketsMessage as D,isQuarantineDetail as g,isRetainedBucketsDetail as _,PRE_EMPTY_TAG_KEYS as O,isCleanableState as v,SAFE_CLEANUP_STATES as F}from"./stackCleanup.js";import{partitionAccounts as k,buildRegionList as B,buildAccountRegionPairs as L,cascadeHomeRegion as C,cascadeOperationKey as I}from"./organisation/cascadeHelpers.js";import{projectScalarSummary as h,projectAccountRows as G}from"./organisation/cascadeSummary.js";import{reconcileProviderAccounts as K,mergeReconciledProviderAccounts as M}from"./organisation/reconcileProviderAccounts.js";import{decideNextTransition as V,reconcileTrailMigration as J,ORG_TRAIL_BUCKET_OUTPUT_KEY as H,TRAIL_BUCKET_OUTPUT_KEY as Q,TRAIL_KEY_ARN_OUTPUT_KEY as w}from"./trailMigration/trailMigration.js";import{decommissionMemberTrailStorage as W}from"./trailMigration/memberTrailCleanup.js";import{unlockBucket as z}from"./unlock/unlockBucket.js";import{unlockQueue as Z}from"./unlock/unlockQueue.js";import{triageBucketPolicy as ee,isEnforceSslStatement as re}from"./unlock/bucketPolicyTriage.js";import{toResourcePolicyStatements as oe}from"./unlock/toResourcePolicyStatements.js";import{restoreBucketPolicy as ae,synthesiseEnforceSslDocument as ne,ensureEnforceSsl as se}from"./unlock/restoreBucketPolicy.js";import{restoreAndReconcileQuarantinedBucket as le}from"./unlock/restoreAndReconcileQuarantinedBucket.js";import{parseAccountsConfiguration as me,flattenAccountsToEnvironments as ue,extractAllAccountNames as fe,accountsConfigToOUTree as de,isStringArray as Re,isAccountsConfig as Se,isOuOnlyAccountBucket as Pe,OU_ONLY_ACCOUNT_BUCKETS as xe}from"./organisation/accountsConfig.js";import{resolveBuildSecrets as ye,resolveSecretRefValue as Ae,resolveBuildSecretSessionProvider as Ee,sourcedRefsFromBuildArgs as De}from"./application/buildSecretResolver.js";import{buildBuildSecretSessionPolicy as _e,partitionForRegion as Oe}from"./application/buildSecretSession.js";import{buildDeploySessionPolicy as Fe}from"./application/deploySessionPolicy.js";import{resolveBuildArgs as ke}from"./application/buildArgResolver.js";import{validateBuildGroup as Le}from"./application/buildGroupValidator.js";export*from"./domain/index.js";import{runOpenNextBuild as Ue}from"./application/openNextBuild.js";import{runOrganisationSetup as Ge,ORG_SETUP_PHASES as Ne}from"./organisation/organisationSetup.js";export*from"./builders/index.js";import{buildDeployPlan as Ye,computeDeployPlan as Ve,computeAssemblyDigest as Je,digestHead as He,classifyImpact as Qe,derivePropertyChanges as we,isDataLoss as je,isStatefulResourceType as We,renderPlanSummary as qe,renderPlanLines as ze,toWirePlanChanges as Xe,signApprovalToken as Ze,verifyApprovalToken as $e,DEFAULT_APPROVAL_TTL_MS as er,APPROVAL_TOKEN_PATTERN as rr}from"./application/plan/index.js";import{runApprovalGate as or,approvalRefusalReason as ir}from"./application/approvalGate.js";import{runDestructionGate as nr,buildDestructionTicket as sr,evaluateDestructionConsents as cr,computeTicketDigest as lr,legalRemediationVerbs as pr,executableRemediationVerbs as mr,advertisableRemediationVerbs as ur,renderDestructionTicketLines as fr,renderConsentVerdictLines as dr}from"./application/destructionGate.js";import{loadDeployPlan as Sr,createMemoisedPlanLoader as Pr}from"./application/plan/loadDeployPlan.js";import{classifyDriftFailure as Tr,fetchLastOperationEvents as yr}from"./drift/classifyDriftFailure.js";import{DriftProbe as Er,probePhysicalPresence as Dr}from"./drift/driftProbe.js";import{clearDriftSuspects as _r,defaultDriftJournalDir as Or,readDriftSuspects as vr,recordDriftSuspects as Fr}from"./drift/driftSuspectJournal.js";import{detectStackDrift as kr}from"./drift/detectStackDrift.js";import{runDriftPreFlight as Lr,formatDriftPreFlightBlock as Cr,DRIFT_PREFLIGHT_TRIGGER_STATUSES as Ir,DRIFT_PREFLIGHT_BUDGET_MS as Ur}from"./drift/driftPreFlight.js";import{runRoute53RecordPreflight as Gr}from"./drift/route53RecordPreflight.js";import{archiveCompletedRemediationJournal as Kr,computeRemediationOpId as Mr,defaultRemediationJournalDir as Yr,findRemediationJournalByOpId as Vr,listActiveRemediationOps as Jr,listActiveRemediationOpsForContext as Hr,readRemediationJournal as Qr,sweepExpiredRemediationJournals as wr,writeRemediationJournal as jr,REMEDIATION_JOURNAL_RETENTION_DAYS as Wr}from"./remediation/remediationJournal.js";import{captureRemediationForensics as zr,defaultRemediationForensicsDir as Xr}from"./remediation/forensicsCapture.js";import{applyRetainFlip as $r,composeFlipCapabilities as et,computeTemplateDigest as rt,deriveRetainFlipTemplate as tt,findMarkedResourcesByOpId as ot,proveFlipMetadataOnly as it,readRetainFlipState as at,verifyRetainFlipDiff as nt,FORGET_MARKER_KEY as st,FORGET_FLIP_SPEC as ct,RECREATE_MARKER_KEY as lt}from"./remediation/retainFlip.js";import{assessForgetResumability as mt,completeForgetAfterConverge as ut,completeForgetAfterDeploy as ft,formatRecreateInFlightCures as dt,partitionPreFlightByRemediation as Rt,runForgetSurgery as St,synthTemplateDeclaresResource as Pt}from"./remediation/forgetResource.js";import{runRecreateSurgery as Tt,snapshotPolicyForType as yt,SNAPSHOT_CAPABLE_RESOURCE_TYPES as At}from"./remediation/recreateResource.js";import{runRecreatePreFlight as Dt}from"./remediation/recreatePreFlight.js";import{deriveRemovalTemplate as _t,findRemovalDeletionFailures as Ot,findResourcesPresent as vt,proveRemovalTargetsOnly as Ft}from"./remediation/removalUpdate.js";import{advertisableDriftVerbs as kt,buildDriftRepairTicket as Bt,runDriftRepairGate as Lt}from"./remediation/driftRepairGate.js";import{formatPinOutcomeDetail as It,pinProbeCoverage as Ut,renderPinReportLines as ht,runPinRemediation as Gt}from"./remediation/pinRemediation.js";import{composePostFailureRepairOffer as Kt,formatDriftRepairOffer as Mt,DriftRepairAvailableError as Yt}from"./remediation/postFailureRepairOffer.js";import{captureLegacySurfaces as Jt,decodeLegacyAnchor as Ht,runIdentitySurfaceGate as Qt}from"./remediation/pinIdentity.js";import{collectIdentityPinCandidates as jt,degradedIdentityPinOutcomes as Wt,renderIdentityPinReportLines as qt,runIdentityPinMigration as zt}from"./remediation/migrateIdentity.js";import{checkDeployPathStackAvailability as Zt,isDeployPathBlockingStatus as $t,StackUnavailableError as eo}from"./activeDeploymentGuard.js";import{assertLeaseBeforeStack as to,LeaseDeniedError as oo}from"./application/leaseGate.js";export{rr as APPROVAL_TOKEN_PATTERN,er as DEFAULT_APPROVAL_TTL_MS,Ur as DRIFT_PREFLIGHT_BUDGET_MS,Ir as DRIFT_PREFLIGHT_TRIGGER_STATUSES,Er as DriftProbe,Yt as DriftRepairAvailableError,ct as FORGET_FLIP_SPEC,st as FORGET_MARKER_KEY,oo as LeaseDeniedError,Ne as ORG_SETUP_PHASES,H as ORG_TRAIL_BUCKET_OUTPUT_KEY,xe as OU_ONLY_ACCOUNT_BUCKETS,O as PRE_EMPTY_TAG_KEYS,lt as RECREATE_MARKER_KEY,Wr as REMEDIATION_JOURNAL_RETENTION_DAYS,F as SAFE_CLEANUP_STATES,At as SNAPSHOT_CAPABLE_RESOURCE_TYPES,eo as StackUnavailableError,Q as TRAIL_BUCKET_OUTPUT_KEY,w as TRAIL_KEY_ARN_OUTPUT_KEY,de as accountsConfigToOUTree,kt as advertisableDriftVerbs,ur as advertisableRemediationVerbs,$r as applyRetainFlip,ir as approvalRefusalReason,Kr as archiveCompletedRemediationJournal,to as assertLeaseBeforeStack,mt as assessForgetResumability,L as buildAccountRegionPairs,_e as buildBuildSecretSessionPolicy,Ye as buildDeployPlan,Fe as buildDeploySessionPolicy,sr as buildDestructionTicket,Bt as buildDriftRepairTicket,B as buildRegionList,Jt as captureLegacySurfaces,zr as captureRemediationForensics,C as cascadeHomeRegion,I as cascadeOperationKey,Zt as checkDeployPathStackAvailability,Tr as classifyDriftFailure,Qe as classifyImpact,T as cleanupFailedStack,_r as clearDriftSuspects,jt as collectIdentityPinCandidates,ut as completeForgetAfterConverge,ft as completeForgetAfterDeploy,et as composeFlipCapabilities,Kt as composePostFailureRepairOffer,Je as computeAssemblyDigest,Ve as computeDeployPlan,Mr as computeRemediationOpId,p as computeSecretsDrift,rt as computeTemplateDigest,lr as computeTicketDigest,Pr as createMemoisedPlanLoader,V as decideNextTransition,Ht as decodeLegacyAnchor,W as decommissionMemberTrailStorage,Or as defaultDriftJournalDir,Xr as defaultRemediationForensicsDir,Yr as defaultRemediationJournalDir,Wt as degradedIdentityPinOutcomes,t as deploy,R as deployOrganisation,we as derivePropertyChanges,_t as deriveRemovalTemplate,tt as deriveRetainFlipTemplate,m as deriveStaleParameters,i as destroy,P as destroyOrganisation,kr as detectStackDrift,He as digestHead,y as emptyS3Bucket,se as ensureEnforceSsl,cr as evaluateDestructionConsents,mr as executableRemediationVerbs,fe as extractAllAccountNames,u as extractConsumedSsmParameters,yr as fetchLastOperationEvents,ot as findMarkedResourcesByOpId,Vr as findRemediationJournalByOpId,Ot as findRemovalDeletionFailures,vt as findResourcesPresent,ue as flattenAccountsToEnvironments,Cr as formatDriftPreFlightBlock,Mt as formatDriftRepairOffer,It as formatPinOutcomeDetail,E as formatQuarantineSuspectedMessage,dt as formatRecreateInFlightCures,D as formatRetainedBucketsMessage,Se as isAccountsConfig,v as isCleanableState,je as isDataLoss,$t as isDeployPathBlockingStatus,re as isEnforceSslStatement,Pe as isOuOnlyAccountBucket,g as isQuarantineDetail,_ as isRetainedBucketsDetail,We as isStatefulResourceType,Re as isStringArray,pr as legalRemediationVerbs,Jr as listActiveRemediationOps,Hr as listActiveRemediationOpsForContext,Sr as loadDeployPlan,M as mergeReconciledProviderAccounts,me as parseAccountsConfiguration,k as partitionAccounts,Oe as partitionForRegion,Rt as partitionPreFlightByRemediation,Ut as pinProbeCoverage,A as preEmptyStackBuckets,Dr as probePhysicalPresence,s as probeSecretsDrift,c as probeServiceDeploymentStatus,G as projectAccountRows,h as projectScalarSummary,it as proveFlipMetadataOnly,Ft as proveRemovalTargetsOnly,vr as readDriftSuspects,Qr as readRemediationJournal,at as readRetainFlipState,K as reconcileProviderAccounts,J as reconcileTrailMigration,Fr as recordDriftSuspects,dr as renderConsentVerdictLines,fr as renderDestructionTicketLines,qt as renderIdentityPinReportLines,ht as renderPinReportLines,ze as renderPlanLines,qe as renderPlanSummary,ke as resolveBuildArgs,Ee as resolveBuildSecretSessionProvider,ye as resolveBuildSecrets,Ae as resolveSecretRefValue,n as restart,le as restoreAndReconcileQuarantinedBucket,ae as restoreBucketPolicy,or as runApprovalGate,nr as runDestructionGate,Lr as runDriftPreFlight,Lt as runDriftRepairGate,St as runForgetSurgery,zt as runIdentityPinMigration,Qt as runIdentitySurfaceGate,Ue as runOpenNextBuild,Ge as runOrganisationSetup,Gt as runPinRemediation,Dt as runRecreatePreFlight,Tt as runRecreateSurgery,Gr as runRoute53RecordPreflight,Ze as signApprovalToken,yt as snapshotPolicyForType,De as sourcedRefsFromBuildArgs,f as ssmParameterNameFromValueFrom,wr as sweepExpiredRemediationJournals,Pt as synthTemplateDeclaresResource,ne as synthesiseEnforceSslDocument,oe as toResourcePolicyStatements,Xe as toWirePlanChanges,ee as triageBucketPolicy,z as unlockBucket,Z as unlockQueue,Le as validateBuildGroup,$e as verifyApprovalToken,nt as verifyRetainFlipDiff,jr as writeRemediationJournal};
|
|
@@ -4,7 +4,7 @@ import type { OrgConfig } from "../../types/config/orgConfig.js";
|
|
|
4
4
|
import type { DeployCallbacks } from "../../types/callbacks.js";
|
|
5
5
|
import type { OrganisationOperation } from "../../types/operations.js";
|
|
6
6
|
import type { DeployServices } from "../serviceFactory.js";
|
|
7
|
-
import type { DomainConfig, DomainDeployProvider, DomainDeployResult } from "../domainInterface.js";
|
|
7
|
+
import type { DomainConfig, DomainDeployProvider, DomainDeployResult, DomainProviderFactory } from "../domainInterface.js";
|
|
8
8
|
import { type DomainsTakeoverContext } from "../domain/domainTakeoverGate.js";
|
|
9
9
|
import type { ProviderAccount } from "@fjall/util/config";
|
|
10
10
|
/**
|
|
@@ -156,6 +156,17 @@ export interface DeployDomainsOptions {
|
|
|
156
156
|
* every provider, CLI and worker alike).
|
|
157
157
|
*/
|
|
158
158
|
takeover?: DomainsTakeoverContext;
|
|
159
|
+
/**
|
|
160
|
+
* T2 account routing: mints a group-scoped provider context per distinct
|
|
161
|
+
* foreign `(account, region)` pin, so pinned domains DEPLOY into their
|
|
162
|
+
* recorded homes instead of being skipped — the "<accountname>.<domain>"
|
|
163
|
+
* children the registry knows about stop depending on which account the
|
|
164
|
+
* operator happened to run the cascade from. Absent: today's
|
|
165
|
+
* skip-with-warning partition, byte for byte. A factory failure for one
|
|
166
|
+
* home degrades THAT home's domains to the same skip — routing is an
|
|
167
|
+
* upgrade over the skip, never a new way to fail the phase.
|
|
168
|
+
*/
|
|
169
|
+
domainProviderFactory?: DomainProviderFactory;
|
|
159
170
|
}
|
|
160
171
|
/**
|
|
161
172
|
* Order delegated domains into parent-first topological waves keyed on
|
|
@@ -180,5 +191,13 @@ export declare function planDelegatedDomainWaves(delegatedDomains: readonly Doma
|
|
|
180
191
|
* ceremony deploys nothing (assess-first mirrors the application path's
|
|
181
192
|
* withhold-before-mutation semantics). `deployedZones` names exactly the
|
|
182
193
|
* zones that deployed — the only honest per-zone retirement signal.
|
|
194
|
+
*
|
|
195
|
+
* T2 account routing: with `options.domainProviderFactory`, domains pinned to
|
|
196
|
+
* a foreign `(account, region)` home deploy through a home-scoped provider
|
|
197
|
+
* context instead of being skipped. The partition, the takeover gate and the
|
|
198
|
+
* wave plan all stay GLOBAL — one pin check, one consent ceremony, one
|
|
199
|
+
* parent-first order across every home — while synth, template reads, record
|
|
200
|
+
* releases and deploys run through each domain's own group. No factory (or a
|
|
201
|
+
* factory failure for a home) keeps the skip-with-warning behaviour.
|
|
183
202
|
*/
|
|
184
203
|
export declare function deployDomains(provider: DomainDeployProvider, callbacks: DeployCallbacks, options?: DeployDomainsOptions): Promise<DomainDeployResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Y=Object.defineProperty;var g=(e,n)=>Y(e,"name",{value:n,configurable:!0});import{join as H}from"path";import{success as E,failure as $}from"@fjall/generator";import{logger as P}from"@fjall/util/logger";import{maskSensitiveOutput as S,mapSettledWithConcurrency as Z,resolveSynthEnvironment as X}from"@fjall/util";import{ORGANISATION_TYPES as A,getOrganisationStackName as G}from"../../types/operations.js";import{CdkContextBuilder as q}from"../../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as J}from"../../types/deployment/index.js";import{CloudFormationService as V}from"../../services/infrastructure/CloudFormationService.js";import{getCascadeStateFilePath as Q,regionSuffix as ee}from"../../types/config/FjallState.js";import{BackupClient as te}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as ne,describeBackupVaultExists as oe}from"../../aws/organisations/backup.js";import{assertAssemblyEngineCompat as re,buildParamsContext as ae,collectStackOutputs as W,assumeCascadeRole as N,emitStepWarnings as se,emitWarningEvent as ie,forwardOutput as j,reconcileOidcProviderIfCarried as U}from"../contextHelpers.js";import{assertLeaseBeforeStack as ce}from"../application/leaseGate.js";import{runCascadeTakeoverGate as de}from"../domain/domainTakeoverGate.js";import{deployDelegatedDomain as ue}from"../domain/delegatedDomainDeploy.js";import{normaliseRecordName as F}from"../domain/zoneClassifier.js";import{runDriftPreFlightGate as fe}from"../drift/preFlightGate.js";import{accountTier as _}from"@fjall/util";import{buildBootstrapTagsIfResolved as le}from"../bootstrapTags.js";import{hasOrganisationTierAccount as pe}from"../../aws/organisations/accountGlobals.js";import{DEFAULT_REGION as K}from"../../aws/utils/regions.js";const me=4;function Ge(e){const n=e.find(o=>_(o)==="platform"),c=e.filter(o=>_(o)==="account");return{platformAccount:n,memberAccounts:c}}g(Ge,"partitionAccounts");function ge(e){const n=e?.primaryRegion??K,c=e?.secondaryRegions??[],o=new Set([n,...c]),i=e?.disasterRecoveryRegion;return i&&o.add(i),[...o]}g(ge,"buildRegionList");function Ve(e){return ge(e)[0]??K}g(Ve,"cascadeHomeRegion");function he(e,n){return`${e} (${n})`}g(he,"cascadeOperationKey");function We(e,n){const c=[];for(const o of n)for(const i of e)c.push({account:i,region:o});return c}g(We,"buildAccountRegionPairs");import{buildCascadeRoleArn as Xe}from"../contextHelpers.js";async function Ue(e,n,c,o){c.onLog?.(`Verifying cascade role access for ${n.length} account(s)\u2026`,"info");const i=await Z(n,me,async a=>N(e.awsProvider,a.id,a.region??e.awsProvider.getRegion(),`fjall-preflight-${a.name}`,o)),t=[];return i.forEach((a,s)=>{const r=n[s];if(r){if(a.status==="rejected"){t.push({accountId:r.id,accountName:r.name,error:a.reason instanceof Error?a.reason.message:String(a.reason)});return}a.value.success||t.push({accountId:r.id,accountName:r.name,error:a.value.error.message})}}),t.length===0&&c.onLog?.(`Cascade role access verified for ${n.length} account(s)`,"info"),t}g(Ue,"probeCascadeRoles");async function _e(e,n,c,o,i,t,a){const s=a?.region??o.region??n.awsProvider.getRegion(),r=he(o.name,s),u=a?.orgConfig??e.orgConfig,l=a?.ipamPoolId;t.onCascadeAccountStart?.(r,o.id,s,i);const p=await N(n.awsProvider,o.id,s,`fjall-cascade-${o.name}`,e.abortSignal);if(!p.success)return t.onCascadeAccountComplete?.(r,!1,S(p.error.message),s),$(new Error(`Failed to assume role for ${o.name}: ${S(p.error.message)}`));const{provider:h,credentials:d}=p.data,C=H(e.workingDirectory,"fjall",i==="platform"?A.PLATFORM:A.ACCOUNT),y=H(C,`cdk.out.${o.id}.${ee(s)}`),f=q.buildDeploymentContext({deployType:i,target:c.target,path:C,assemblyDir:y,environment:X(o),region:s,accountName:o.name,callerIdentity:J(o.id),ipamPoolId:l,...ae({orgConfig:u,identity:e.identity,skipOidc:e.options?.skipOidc,region:s,primaryRegion:a?.primaryRegion,trailLifecycle:o.trailLifecycle})},{verbose:e.options?.verbose},u);if(ne(o.environment,u?.disasterRecoveryRegion)){const m=await oe(h.getClient(te),e.abortSignal);if(!m.success)return t.onCascadeAccountComplete?.(r,!1,S(m.error.message),s),$(new Error(`Backup vault probe failed for ${o.name}: ${S(m.error.message)}`));f.fjallAdoptBackupVault=m.data}t.onCascadeAccountPhaseChange?.(r,"synth",s);const R=await n.cdkService.runCdkSynth(f,j(t),d);if(!R.success)return t.onCascadeAccountComplete?.(r,!1,S(`Synth failed: ${R.error}`),s),$(new Error(`Synth failed for ${o.name}: ${S(R.error)}`));se(t,R.data);const v=re(y,t);if(!v.success){const m=S(v.error.message);return t.onCascadeAccountComplete?.(r,!1,m,s),$(new Error(`Engine compatibility check failed for ${o.name}: ${m}`))}const w=G(i==="platform"?A.PLATFORM:A.ACCOUNT),L=Q(C,o.id,s),D=new V(h),{changed:z,currentHash:b}=await n.hashService.compareCascadeStack(y,w,L);if(!z&&e.options?.force!==!0&&await D.stackExists(w,void 0,e.abortSignal)){const m=await D.getStackOutputs(w,void 0,e.abortSignal);m.success||P.debug("cascadeHelpers","Failed to read outputs for skipped cascade account (non-critical)",{stackName:w,account:o.name});const T=W(m);return await U(f,h,t,e.abortSignal),t.onLog?.(`${o.name}: no infrastructure changes \u2014 skipping deploy`,"info"),t.onCascadeAccountComplete?.(r,!0,void 0,s,T,!0),E({outputs:T,skipped:!0})}const x=await fe(h,{stackNames:[w],cdkOutPath:y,cfnService:D,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(x.kind==="blocked")return t.onCascadeAccountComplete?.(r,!1,x.message,s),$(new Error(x.message));const O=await ce(e.assertLeaseForStack,w,e.abortSignal);if(O!==void 0)return t.onCascadeAccountComplete?.(r,!1,O.message,s),$(O);t.onCascadeAccountPhaseChange?.(r,"bootstrap",s);const k=await n.cdkService.runCdkBootstrap(f,j(t),d,void 0,le({account:o,estate:pe(u?.providerAccounts)?"org":"solo",...a?.orgId!==void 0?{orgId:a.orgId}:{}}));if(!k.success)return t.onCascadeAccountComplete?.(r,!1,S(`Bootstrap failed: ${k.error}`),s),$(new Error(`Bootstrap failed for ${o.name}: ${S(k.error)}`));t.onCascadeAccountPhaseChange?.(r,"deploy",s);const I=await n.cdkService.runCdkDeploy(f,w,j(t),m=>t.onCascadeAccountResourceProgress?.(r,m,s),h,d);if(!I.success)return t.onCascadeAccountComplete?.(r,!1,S(I.error),s),$(new Error(S(I.error)));const M=await D.getStackOutputs(w,void 0,e.abortSignal);M.success||P.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:w,account:o.name});const B=W(M);return await U(f,h,t,e.abortSignal),b!==void 0&&((await n.hashService.persistCascadeStack(L,w,b)).success||P.debug("cascadeHelpers","Failed to persist cascade hash state (non-critical)",{stackName:w,account:o.name})),t.onCascadeAccountComplete?.(r,!0,void 0,s,B,!1),E({outputs:B,skipped:!1})}g(_e,"deployCascadeAccount");async function Ke(e,n,c,o){const i=new Map,t=e.awsProvider.getRegion(),a=await N(e.awsProvider,n.id,t,`fjall-ipam-read-${n.name}`,o);if(!a.success)return P.debug("organisationDeploy",`Cannot read Platform outputs: ${a.error.message}`),i;const s=new V(a.data.provider),r=G(A.PLATFORM),u=await s.getStackOutputs(r);if(!u.success)return P.debug("organisationDeploy",`Failed to read Platform stack outputs: ${u.error.message}`),i;const l=/^IpamPoolId(\d{12})(\w+)$/;for(const p of u.data){const h=p.OutputKey?.match(l);if(h&&p.OutputValue){const d=`${h[1]}-${h[2]}`;i.set(d,p.OutputValue)}}return i.size>0&&c.onLog?.(`Read ${i.size} IPAM pool ID(s) from Platform stack`,"info"),i}g(Ke,"readPlatformIpamPoolIds");function Ce(e,n){const c=e.account!==void 0&&n?.executingAccountId!==void 0&&e.account!==n.executingAccountId,o=e.region!==void 0&&n?.executingRegion!==void 0&&e.region!==n.executingRegion;if(c)return`[${e.name}] skipped: its fjall-config.json entry records account ${e.account} as its home, but this cascade runs in account ${n?.executingAccountId} - deploy it separately with 'fjall domain deploy ${e.name} -t <target in account ${e.account}>' or run the cascade under that account.`;if(o)return`[${e.name}] skipped: its fjall-config.json entry records region ${e.region} as its home, but this cascade runs in region ${n?.executingRegion} - deploy it separately with 'fjall domain deploy ${e.name} --region ${e.region}', or update the entry's "region" key if this move is intentional.`}g(Ce,"foreignDomainSkipReason");function ye(e){const n=new Map;for(const t of e)n.set(F(t.name),t);const c=new Map,o=g((t,a)=>{const s=c.get(t);if(s!==void 0)return s;if(a.includes(t))return[...a.slice(a.indexOf(t)),t];const r=n.get(t);if(r===void 0)return-1;const u=r.parentDomain!==void 0?F(r.parentDomain):void 0,l=u===void 0?-1:o(u,[...a,t]);if(Array.isArray(l))return l;const p=l+1;return c.set(t,p),p},"resolveDepth"),i=[];for(const t of e){const a=o(F(t.name),[]);if(Array.isArray(a)){const s=a.map(r=>n.get(r)?.name??r).join(" -> ");return $(new Error(`Delegated domain ordering: fjall-config.json declares a parentDomain cycle (${s}) - no deploy order satisfies it. Nothing was deployed. Cure: correct the "parentDomain" of one entry in that cycle so parents form a chain, then re-run.`))}(i[a]??=[]).push(t)}return E(i.filter(t=>t.length>0))}g(ye,"planDelegatedDomainWaves");async function ze(e,n,c){const o=e.getDomains();if(o.length===0)return{domainsDeployed:0,domainsSkipped:0,errors:[],deployedZones:[]};n.onCascadePhaseStart?.("domains");const i=[];let t=0;for(const d of o){const C=Ce(d,c);if(C!==void 0){t++,ie(n,C);continue}i.push(d)}const a=i.filter(d=>d.type==="apex"),s=i.filter(d=>d.type==="delegated");let r=0;const u=[],l=[],p=ye(s);if(!p.success)return u.push(p.error.message),n.onCascadePhaseComplete?.("domains"),{domainsDeployed:r,domainsSkipped:t,errors:u,deployedZones:l};const h=p.data;if(i.length>0){if(c?.takeover===void 0)return u.push("domains phase refused: the cascade executed without a takeover-gate context, so record collisions cannot be assessed \u2014 deploying ungated is not an option. This is a caller wiring defect (executeCascade threads services + consent inputs into DeployDomainsOptions.takeover)."),n.onCascadePhaseComplete?.("domains"),{domainsDeployed:r,domainsSkipped:t,errors:u,deployedZones:l};const d=await de(e,i,n,c.takeover);if(!d.success)return u.push(d.error.message),n.onCascadePhaseComplete?.("domains"),{domainsDeployed:r,domainsSkipped:t,errors:u,deployedZones:l};if(d.data.kind==="withheld")return n.onCascadePhaseComplete?.("domains"),{domainsDeployed:r,domainsSkipped:t,errors:u,deployedZones:l,takeover:d.data.withheld}}for(const d of a){const C=await e.deployDomain(d.name,n);C.success?(r++,l.push(d.name)):u.push(`${d.name}: ${C.error.message}`)}for(const d of h){const C=await Promise.allSettled(d.map(y=>{const f=e.delegatedDeployer?.(y.name);return f!==void 0?ue({deployer:f,callbacks:n,...c?.abortSignal!==void 0?{abortSignal:c.abortSignal}:{},...c?.skipCaaPreflight!==void 0?{skipCaaPreflight:c.skipCaaPreflight}:{}}):e.deployDomain(y.name,n)}));for(let y=0;y<C.length;y++){const f=C[y],R=d[y];if(!(!f||!R))if(f.status==="fulfilled")f.value.success?(r++,l.push(R.name)):u.push(`${R.name}: ${f.value.error.message}`);else{const v=f.reason instanceof Error?f.reason.message:String(f.reason);u.push(`${R.name}: ${v}`)}}}return n.onCascadePhaseComplete?.("domains"),{domainsDeployed:r,domainsSkipped:t,errors:u,deployedZones:l}}g(ze,"deployDomains");export{me as CASCADE_MAX_CONCURRENCY,We as buildAccountRegionPairs,Xe as buildCascadeRoleArn,ge as buildRegionList,Ve as cascadeHomeRegion,he as cascadeOperationKey,_e as deployCascadeAccount,ze as deployDomains,Ge as partitionAccounts,ye as planDelegatedDomainWaves,Ue as probeCascadeRoles,Ke as readPlatformIpamPoolIds};
|
|
1
|
+
var X=Object.defineProperty;var C=(e,n)=>X(e,"name",{value:n,configurable:!0});import{join as K}from"path";import{success as b,failure as D}from"@fjall/generator";import{logger as N}from"@fjall/util/logger";import{maskSensitiveOutput as P,mapSettledWithConcurrency as q,resolveSynthEnvironment as J}from"@fjall/util";import{ORGANISATION_TYPES as j,getOrganisationStackName as V}from"../../types/operations.js";import{CdkContextBuilder as Q}from"../../services/supporting/CdkContextBuilder.js";import{stubCallerIdentity as ee}from"../../types/deployment/index.js";import{CloudFormationService as F}from"../../services/infrastructure/CloudFormationService.js";import{getCascadeStateFilePath as te,regionSuffix as ne}from"../../types/config/FjallState.js";import{BackupClient as oe}from"@aws-sdk/client-backup";import{accountHasDisasterRecovery as re,describeBackupVaultExists as se}from"../../aws/organisations/backup.js";import{assertAssemblyEngineCompat as ie,buildParamsContext as ae,collectStackOutputs as W,assumeCascadeRole as M,emitStepWarnings as ce,emitWarningEvent as U,forwardOutput as L,reconcileOidcProviderIfCarried as _}from"../contextHelpers.js";import{assertLeaseBeforeStack as de}from"../application/leaseGate.js";import{runCascadeTakeoverGate as ue}from"../domain/domainTakeoverGate.js";import{deployDelegatedDomain as fe}from"../domain/delegatedDomainDeploy.js";import{normaliseRecordName as B}from"../domain/zoneClassifier.js";import{runDriftPreFlightGate as ge}from"../drift/preFlightGate.js";import{accountTier as z}from"@fjall/util";import{buildBootstrapTagsIfResolved as me}from"../bootstrapTags.js";import{hasOrganisationTierAccount as le}from"../../aws/organisations/accountGlobals.js";import{DEFAULT_REGION as Y}from"../../aws/utils/regions.js";const pe=4;function He(e){const n=e.find(r=>z(r)==="platform"),i=e.filter(r=>z(r)==="account");return{platformAccount:n,memberAccounts:i}}C(He,"partitionAccounts");function he(e){const n=e?.primaryRegion??Y,i=e?.secondaryRegions??[],r=new Set([n,...i]),f=e?.disasterRecoveryRegion;return f&&r.add(f),[...r]}C(he,"buildRegionList");function Ke(e){return he(e)[0]??Y}C(Ke,"cascadeHomeRegion");function ye(e,n){return`${e} (${n})`}C(ye,"cascadeOperationKey");function Ve(e,n){const i=[];for(const r of n)for(const f of e)i.push({account:f,region:r});return i}C(Ve,"buildAccountRegionPairs");import{buildCascadeRoleArn as Xe}from"../contextHelpers.js";async function We(e,n,i,r){i.onLog?.(`Verifying cascade role access for ${n.length} account(s)\u2026`,"info");const f=await q(n,pe,async c=>M(e.awsProvider,c.id,c.region??e.awsProvider.getRegion(),`fjall-preflight-${c.name}`,r)),t=[];return f.forEach((c,s)=>{const a=n[s];if(a){if(c.status==="rejected"){t.push({accountId:a.id,accountName:a.name,error:c.reason instanceof Error?c.reason.message:String(c.reason)});return}c.value.success||t.push({accountId:a.id,accountName:a.name,error:c.value.error.message})}}),t.length===0&&i.onLog?.(`Cascade role access verified for ${n.length} account(s)`,"info"),t}C(We,"probeCascadeRoles");async function Ue(e,n,i,r,f,t,c){const s=c?.region??r.region??n.awsProvider.getRegion(),a=ye(r.name,s),l=c?.orgConfig??e.orgConfig,v=c?.ipamPoolId;t.onCascadeAccountStart?.(a,r.id,s,f);const y=await M(n.awsProvider,r.id,s,`fjall-cascade-${r.name}`,e.abortSignal);if(!y.success)return t.onCascadeAccountComplete?.(a,!1,P(y.error.message),s),D(new Error(`Failed to assume role for ${r.name}: ${P(y.error.message)}`));const{provider:h,credentials:k}=y.data,E=K(e.workingDirectory,"fjall",f==="platform"?j.PLATFORM:j.ACCOUNT),R=K(E,`cdk.out.${r.id}.${ne(s)}`),p=Q.buildDeploymentContext({deployType:f,target:i.target,path:E,assemblyDir:R,environment:J(r),region:s,accountName:r.name,callerIdentity:ee(r.id),ipamPoolId:v,...ae({orgConfig:l,identity:e.identity,skipOidc:e.options?.skipOidc,region:s,primaryRegion:c?.primaryRegion,trailLifecycle:r.trailLifecycle})},{verbose:e.options?.verbose},l);if(re(r.environment,l?.disasterRecoveryRegion)){const S=await se(h.getClient(oe),e.abortSignal);if(!S.success)return t.onCascadeAccountComplete?.(a,!1,P(S.error.message),s),D(new Error(`Backup vault probe failed for ${r.name}: ${P(S.error.message)}`));p.fjallAdoptBackupVault=S.data}t.onCascadeAccountPhaseChange?.(a,"synth",s);const $=await n.cdkService.runCdkSynth(p,L(t),k);if(!$.success)return t.onCascadeAccountComplete?.(a,!1,P(`Synth failed: ${$.error}`),s),D(new Error(`Synth failed for ${r.name}: ${P($.error)}`));ce(t,$.data);const I=ie(R,t);if(!I.success){const S=P(I.error.message);return t.onCascadeAccountComplete?.(a,!1,S,s),D(new Error(`Engine compatibility check failed for ${r.name}: ${S}`))}const w=V(f==="platform"?j.PLATFORM:j.ACCOUNT),o=te(E,r.id,s),u=new F(h),{changed:d,currentHash:g}=await n.hashService.compareCascadeStack(R,w,o);if(!d&&e.options?.force!==!0&&await u.stackExists(w,void 0,e.abortSignal)){const S=await u.getStackOutputs(w,void 0,e.abortSignal);S.success||N.debug("cascadeHelpers","Failed to read outputs for skipped cascade account (non-critical)",{stackName:w,account:r.name});const H=W(S);return await _(p,h,t,e.abortSignal),t.onLog?.(`${r.name}: no infrastructure changes \u2014 skipping deploy`,"info"),t.onCascadeAccountComplete?.(a,!0,void 0,s,H,!0),b({outputs:H,skipped:!0})}const m=await ge(h,{stackNames:[w],cdkOutPath:R,cfnService:u,callbacks:t,...e.abortSignal!==void 0?{abortSignal:e.abortSignal}:{}});if(m.kind==="blocked")return t.onCascadeAccountComplete?.(a,!1,m.message,s),D(new Error(m.message));const A=await de(e.assertLeaseForStack,w,e.abortSignal);if(A!==void 0)return t.onCascadeAccountComplete?.(a,!1,A.message,s),D(A);t.onCascadeAccountPhaseChange?.(a,"bootstrap",s);const x=await n.cdkService.runCdkBootstrap(p,L(t),k,void 0,me({account:r,estate:le(l?.providerAccounts)?"org":"solo",...c?.orgId!==void 0?{orgId:c.orgId}:{}}));if(!x.success)return t.onCascadeAccountComplete?.(a,!1,P(`Bootstrap failed: ${x.error}`),s),D(new Error(`Bootstrap failed for ${r.name}: ${P(x.error)}`));t.onCascadeAccountPhaseChange?.(a,"deploy",s);const O=await n.cdkService.runCdkDeploy(p,w,L(t),S=>t.onCascadeAccountResourceProgress?.(a,S,s),h,k);if(!O.success)return t.onCascadeAccountComplete?.(a,!1,P(O.error),s),D(new Error(P(O.error)));const G=await u.getStackOutputs(w,void 0,e.abortSignal);G.success||N.debug("cascadeHelpers","Failed to read cascade account stack outputs (non-critical)",{stackName:w,account:r.name});const T=W(G);return await _(p,h,t,e.abortSignal),g!==void 0&&((await n.hashService.persistCascadeStack(o,w,g)).success||N.debug("cascadeHelpers","Failed to persist cascade hash state (non-critical)",{stackName:w,account:r.name})),t.onCascadeAccountComplete?.(a,!0,void 0,s,T,!1),b({outputs:T,skipped:!1})}C(Ue,"deployCascadeAccount");async function _e(e,n,i,r){const f=new Map,t=e.awsProvider.getRegion(),c=await M(e.awsProvider,n.id,t,`fjall-ipam-read-${n.name}`,r);if(!c.success)return N.debug("organisationDeploy",`Cannot read Platform outputs: ${c.error.message}`),f;const s=new F(c.data.provider),a=V(j.PLATFORM),l=await s.getStackOutputs(a);if(!l.success)return N.debug("organisationDeploy",`Failed to read Platform stack outputs: ${l.error.message}`),f;const v=/^IpamPoolId(\d{12})(\w+)$/;for(const y of l.data){const h=y.OutputKey?.match(v);if(h&&y.OutputValue){const k=`${h[1]}-${h[2]}`;f.set(k,y.OutputValue)}}return f.size>0&&i.onLog?.(`Read ${f.size} IPAM pool ID(s) from Platform stack`,"info"),f}C(_e,"readPlatformIpamPoolIds");function Z(e,n){const i=e.account!==void 0&&n?.executingAccountId!==void 0&&e.account!==n.executingAccountId,r=e.region!==void 0&&n?.executingRegion!==void 0&&e.region!==n.executingRegion;if(i)return`[${e.name}] skipped: its fjall-config.json entry records account ${e.account} as its home, but this cascade runs in account ${n?.executingAccountId} - deploy it separately with 'fjall domain deploy ${e.name} -t <target in account ${e.account}>' or run the cascade under that account.`;if(r)return`[${e.name}] skipped: its fjall-config.json entry records region ${e.region} as its home, but this cascade runs in region ${n?.executingRegion} - deploy it separately with 'fjall domain deploy ${e.name} --region ${e.region}', or update the entry's "region" key if this move is intentional.`}C(Z,"foreignDomainSkipReason");function Ce(e){const n=new Map;for(const t of e)n.set(B(t.name),t);const i=new Map,r=C((t,c)=>{const s=i.get(t);if(s!==void 0)return s;if(c.includes(t))return[...c.slice(c.indexOf(t)),t];const a=n.get(t);if(a===void 0)return-1;const l=a.parentDomain!==void 0?B(a.parentDomain):void 0,v=l===void 0?-1:r(l,[...c,t]);if(Array.isArray(v))return v;const y=v+1;return i.set(t,y),y},"resolveDepth"),f=[];for(const t of e){const c=r(B(t.name),[]);if(Array.isArray(c)){const s=c.map(a=>n.get(a)?.name??a).join(" -> ");return D(new Error(`Delegated domain ordering: fjall-config.json declares a parentDomain cycle (${s}) - no deploy order satisfies it. Nothing was deployed. Cure: correct the "parentDomain" of one entry in that cycle so parents form a chain, then re-run.`))}(f[c]??=[]).push(t)}return b(f.filter(t=>t.length>0))}C(Ce,"planDelegatedDomainWaves");async function ze(e,n,i){const r=e.getDomains();if(r.length===0)return{domainsDeployed:0,domainsSkipped:0,errors:[],deployedZones:[]};n.onCascadePhaseStart?.("domains");const f=C(o=>{const u=o.account??i?.executingAccountId,d=o.region??i?.executingRegion;if(!(u===void 0||d===void 0))return{accountId:u,region:d}},"resolveHome"),t=C(o=>`${o.accountId}\0${o.region}`,"homeKey"),c=i?.domainProviderFactory,s=new Map;if(c!==void 0)for(const o of r){if(Z(o,i)===void 0)continue;const u=f(o);if(u===void 0)continue;const d=t(u);s.has(d)||s.set(d,u)}const a=new Map;for(const[o,u]of s){const d=await c?.(u);d!==void 0&&(d.success?a.set(o,d.data):U(n,`[domains] account routing for home ${u.accountId}/${u.region} unavailable (${P(d.error.message)}) \u2014 its domains are skipped exactly as they were before routing existed.`))}const l=[],v=new Map,y=new Map;let h=0;for(const o of r){const u=Z(o,i);if(u===void 0){l.push(o);continue}const d=f(o),g=d!==void 0?a.get(t(d)):void 0;if(g!==void 0&&d!==void 0){l.push(o),v.set(o.name,g.provider);const m=t(d),A=y.get(m)??[];A.push(o),y.set(m,A);continue}h++,U(n,u)}const k=l.filter(o=>o.type==="apex"),E=l.filter(o=>o.type==="delegated");let R=0;const p=[],$=[],I=Ce(E);if(!I.success)return p.push(I.error.message),n.onCascadePhaseComplete?.("domains"),{domainsDeployed:R,domainsSkipped:h,errors:p,deployedZones:$};const w=I.data;if(l.length>0){if(i?.takeover===void 0)return p.push("domains phase refused: the cascade executed without a takeover-gate context, so record collisions cannot be assessed \u2014 deploying ungated is not an option. This is a caller wiring defect (executeCascade threads services + consent inputs into DeployDomainsOptions.takeover)."),n.onCascadePhaseComplete?.("domains"),{domainsDeployed:R,domainsSkipped:h,errors:p,deployedZones:$};const o=i.takeover,u=l.filter(m=>!v.has(m.name)),d=[];u.length>0&&d.push({home:{...i.executingAccountId!==void 0?{accountId:i.executingAccountId}:{},...i.executingRegion!==void 0?{region:i.executingRegion}:{}},provider:e,aws:o.aws,cfnService:o.cfnService,...o.route53Client!==void 0?{route53Client:o.route53Client}:{},domains:u});for(const[m,A]of s){const x=a.get(m),O=y.get(m);x===void 0||O===void 0||d.push({home:A,provider:x.provider,aws:x.aws,cfnService:new F(x.aws),domains:O})}const g=await ue(d,n,{...o.consents!==void 0?{consents:o.consents}:{},...o.consentDigest!==void 0?{consentDigest:o.consentDigest}:{},...o.consentGate!==void 0?{consentGate:o.consentGate}:{},...o.abortSignal!==void 0?{abortSignal:o.abortSignal}:{}});if(!g.success)return p.push(g.error.message),n.onCascadePhaseComplete?.("domains"),{domainsDeployed:R,domainsSkipped:h,errors:p,deployedZones:$};if(g.data.kind==="withheld")return n.onCascadePhaseComplete?.("domains"),{domainsDeployed:R,domainsSkipped:h,errors:p,deployedZones:$,takeover:g.data.withheld}}for(const o of k){const d=await(v.get(o.name)??e).deployDomain(o.name,n);d.success?(R++,$.push(o.name)):p.push(`${o.name}: ${d.error.message}`)}for(const o of w){const u=await Promise.allSettled(o.map(d=>{const g=v.get(d.name)??e,m=g.delegatedDeployer?.(d.name);return m!==void 0?fe({deployer:m,callbacks:n,...i?.abortSignal!==void 0?{abortSignal:i.abortSignal}:{},...i?.skipCaaPreflight!==void 0?{skipCaaPreflight:i.skipCaaPreflight}:{}}):g.deployDomain(d.name,n)}));for(let d=0;d<u.length;d++){const g=u[d],m=o[d];if(!(!g||!m))if(g.status==="fulfilled")g.value.success?(R++,$.push(m.name)):p.push(`${m.name}: ${g.value.error.message}`);else{const A=g.reason instanceof Error?g.reason.message:String(g.reason);p.push(`${m.name}: ${A}`)}}}return n.onCascadePhaseComplete?.("domains"),{domainsDeployed:R,domainsSkipped:h,errors:p,deployedZones:$}}C(ze,"deployDomains");export{pe as CASCADE_MAX_CONCURRENCY,Ve as buildAccountRegionPairs,Xe as buildCascadeRoleArn,he as buildRegionList,Ke as cascadeHomeRegion,ye as cascadeOperationKey,Ue as deployCascadeAccount,ze as deployDomains,He as partitionAccounts,Ce as planDelegatedDomainWaves,We as probeCascadeRoles,_e as readPlatformIpamPoolIds};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Z=Object.defineProperty;var
|
|
1
|
+
var Z=Object.defineProperty;var R=(t,d)=>Z(t,"name",{value:d,configurable:!0});import{failure as $}from"@fjall/generator";import{regionSuffix as X}from"../../types/config/FjallState.js";import{partitionAccounts as Y,deployCascadeAccount as U,readPlatformIpamPoolIds as z,deployDomains as B,buildRegionList as J,buildAccountRegionPairs as K,cascadeHomeRegion as Q,CASCADE_MAX_CONCURRENCY as V}from"../organisation/cascadeHelpers.js";import{projectScalarSummary as E}from"../organisation/cascadeSummary.js";import{getErrorMessage as ee,maskSensitiveOutput as C,mapSettledWithConcurrency as te}from"@fjall/util";import{INFRA_STEPS as M}from"./infraSteps.js";async function ue(t,d,N,j){const{callbacks:e}=t,{providerAccounts:W,effectiveOrgConfig:F,totalSteps:p,cascadeErrors:f,allCascadeOutputs:O,orgId:L}=j;let S=!1;e.onCascadeStart?.();const x=Date.now();let u=2,A=!1,h,D=!1;const P=[],w=R(o=>({members:P,...h!==void 0?{platform:h}:{},domainsDeployed:D,errors:f,totalDurationMs:o}),"buildLedger"),{platformAccount:a,memberAccounts:y}=Y(W),_=Q(t.orgConfig);if(a){const{id:o,name:n}=M.CASCADE_PLATFORM;e.onStepStart?.(o,n,u,p),e.onCascadePhaseStart?.("platform");let i;const I=Date.now();try{i=await U(t,d,N,a,"platform",e,{orgConfig:F,primaryRegion:_,orgId:L})}catch(c){const r=C(ee(c));f.push({accountId:a.id,error:r}),e.onCascadePhaseComplete?.("platform"),e.onStepComplete?.(o,n,"error",u,p),i=$(new Error(r))}const v=Date.now()-I;if(i.success){A=!0;const c=i.data.skipped===!0;c||(S=!0),i.data.outputs&&O.push({accountId:a.id,outputs:i.data.outputs}),e.onCascadePhaseComplete?.("platform"),e.onStepComplete?.(o,n,c?"skipped":"completed",u,p),h={accountId:a.id,result:c?"skipped":"succeeded",durationMs:v}}else f.some(c=>c.accountId===a.id)||(f.push({accountId:a.id,error:C(i.error.message)}),e.onCascadePhaseComplete?.("platform"),e.onStepComplete?.(o,n,"error",u,p)),h={accountId:a.id,result:"failed",durationMs:v,error:C(i.error.message)};u++}let T=new Map;A&&a&&(T=await z(d,a,e,t.abortSignal));let b=[],H;if(t.domainProvider){const o=await B(t.domainProvider,e,{executingAccountId:d.awsProvider.getAccountId(),executingRegion:d.awsProvider.getRegion(),takeover:{aws:d.awsProvider,cfnService:d.cfnService,...t.options?.destructionConsents!==void 0?{consents:t.options.destructionConsents}:{},...t.options?.destructionConsentDigest!==void 0?{consentDigest:t.options.destructionConsentDigest}:{},...t.destructionConsentGate!==void 0?{consentGate:t.destructionConsentGate}:{},...t.abortSignal!==void 0?{abortSignal:t.abortSignal}:{}},...t.abortSignal!==void 0?{abortSignal:t.abortSignal}:{},...t.options?.skipCaaPreflight!==void 0?{skipCaaPreflight:t.options.skipCaaPreflight}:{},...t.domainProviderFactory!==void 0?{domainProviderFactory:t.domainProviderFactory}:{}});D=o.domainsDeployed>0,D&&(S=!0),b=o.deployedZones;for(const n of o.errors)f.push({accountId:"domains",error:C(n)});if(o.takeover!==void 0){H=o.takeover,e.onLog?.("Member-account cascade skipped \u2014 domain record takeover consent is pending. Consent to the ticket and re-run; nothing was deployed in the domains phase.","warn");const n=w(Date.now()-x);return e.onCascadeComplete?.(E(n)),e.onCascadeLedger?.(n),{anyCascadeDeployHappened:S,deployedDomainZones:b,domainsTakeover:H}}}if(a!==void 0&&!A&&y.length>0){const{id:o,name:n}=M.CASCADE_ACCOUNTS;e.onStepStart?.(o,n,u,p),e.onStepComplete?.(o,n,"skipped",u,p),e.onLog?.("Skipping account cascade \u2014 platform deployment failed; platform is a prerequisite for member accounts.","warn")}else if(y.length>0){const{id:o,name:n}=M.CASCADE_ACCOUNTS;e.onStepStart?.(o,n,u,p),e.onCascadePhaseStart?.("accounts");const i=J(t.orgConfig),I=K(y,i);(await te(I,V,async({account:r,region:k})=>{const m=T.get(`${r.id}-${X(k)}`),s=Date.now();return{result:await U(t,d,N,r,"account",e,{ipamPoolId:m,orgConfig:F,region:k,primaryRegion:_,orgId:L}),durationMs:Date.now()-s}})).forEach((r,k)=>{const m=I[k];if(!m)return;const s=m.account;if(r.status==="rejected"){const l=C(r.reason instanceof Error?r.reason.message:String(r.reason));P.push({accountId:s.id,accountName:s.name,region:m.region,result:"failed",durationMs:0,error:l}),f.push({accountId:s.id,error:l});return}const{result:g,durationMs:G}=r.value;if(g.success){const l=g.data.skipped===!0;l||(S=!0),g.data.outputs&&O.push({accountId:s.id,outputs:g.data.outputs}),P.push({accountId:s.id,accountName:s.name,region:m.region,result:l?"skipped":"succeeded",durationMs:G})}else{const l=C(g.error.message);P.push({accountId:s.id,accountName:s.name,region:m.region,result:"failed",durationMs:G,error:l}),f.push({accountId:s.id,error:l})}});const c=E(w(0));e.onCascadePhaseComplete?.("accounts"),e.onStepComplete?.(o,n,c.accountsFailed>0?"error":c.accountsSkipped===y.length?"skipped":"completed",u,p)}const q=w(Date.now()-x);return e.onCascadeComplete?.(E(q)),e.onCascadeLedger?.(q),{anyCascadeDeployHappened:S,deployedDomainZones:b}}R(ue,"executeCascade");export{ue as executeCascade};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var s=Object.defineProperty;var d=(
|
|
1
|
+
var s=Object.defineProperty;var d=(l,a)=>s(l,"name",{value:a,configurable:!0});import{CAPACITY_IDENTITY_CONTEXT_KEY as f,filterDangerousEnvVars as c}from"@fjall/util";class t{static{d(this,"CdkArgumentBuilder")}buildContextArgs(a){const e=[];if(a?.accountId&&e.push("-c",`accountId=${a.accountId}`),a?.environment&&e.push("-c",`environment=${a.environment}`),a?.managedAccount&&e.push("-c","managedAccount=true"),a?.accountName&&e.push("-c",`accountName=${a.accountName}`),a?.orgId&&e.push("-c",`orgId=${a.orgId}`),a?.rootId&&e.push("-c",`rootId=${a.rootId}`),a?.managementAccountId&&e.push("-c",`managementAccountId=${a.managementAccountId}`),a?.devOuId&&e.push("-c",`fjallDevOuId=${a.devOuId}`),a?.ipamPoolId&&e.push("-c",`ipamPoolId=${a.ipamPoolId}`),a?.fjallOrgId&&e.push("-c",`fjallOrgId=${a.fjallOrgId}`),a?.fjallOidcConfigured&&e.push("-c",`fjallOidcConfigured=${a.fjallOidcConfigured}`),a?.fjallAccountGlobalsConfigured&&e.push("-c",`fjallAccountGlobalsConfigured=${a.fjallAccountGlobalsConfigured}`),a?.fjallAccountTrailState&&e.push("-c",`fjallAccountTrailState=${a.fjallAccountTrailState}`),a?.orgConfig&&e.push("-c",`orgConfig=${a.orgConfig}`),a?.fjallAdoptBackupVault&&e.push("-c","fjallAdoptBackupVault=true"),a?.resolvedSecretArns&&e.push("-c",`fjallResolvedSecretArns=${a.resolvedSecretArns}`),a?.managedDomainBindings)for(const[n,r]of Object.entries(a.managedDomainBindings))r!==""&&e.push("-c",`fjall:managedDomain:${n}=${r}`);if(a?.managedDomainCoverage)for(const[n,r]of Object.entries(a.managedDomainCoverage))r!==""&&e.push("-c",`fjall:managedDomainCoverage:${n}=${r}`);return a?.domainDeployPhase&&e.push("-c",`fjall:domainDeployPhase=${a.domainDeployPhase}`),a?.parentDelegationRoleArn&&e.push("-c",`fjall:parentDelegationRoleArn=${a.parentDelegationRoleArn}`),a?.capacityIdentity&&e.push("-c",`${f}=${a.capacityIdentity}`),e}buildParameterArgs(a,e){if(a===void 0)return[];const n=Object.entries(a);if(n.length===0)return[];const r=e!==void 0&&e!==""?`${e}:`:"",u=[];for(const[i,o]of n){if(!/^[A-Za-z][A-Za-z0-9]*$/.test(i))throw new Error(`Invalid CloudFormation parameter name "${i}": must match /^[A-Za-z][A-Za-z0-9]*$/ (alphanumeric, leading letter, no separators).`);if(o==="")throw new Error(`CloudFormation parameter "${i}" has an empty value.`);if(/[,\n\r]/.test(o))throw new Error(`CloudFormation parameter "${i}" value contains "," or newline \u2014 cdk's --parameters splits on "," so the deploy would silently fragment.`);u.push("--parameters",`${r}${i}=${o}`)}return u}injectCascadeCredentials(a,e){e&&(a.AWS_ACCESS_KEY_ID=e.accessKeyId,a.AWS_SECRET_ACCESS_KEY=e.secretAccessKey,delete a.AWS_SESSION_TOKEN,e.sessionToken&&(a.AWS_SESSION_TOKEN=e.sessionToken))}buildCdkEnv(a){const e={...c(process.env),CI:"true",FORCE_COLOR:"0",CDK_DISABLE_VERSION_CHECK:"1",NODE_NO_WARNINGS:"1"};return a?.context?.region&&(e.AWS_REGION=a.context.region,e.AWS_DEFAULT_REGION=a.context.region,e.CDK_DEFAULT_REGION=a.context.region),a?.context?.accountId&&(e.CDK_DEFAULT_ACCOUNT=a.context.accountId),a?.context?.environment&&(e.ENVIRONMENT=a.context.environment),this.injectCascadeCredentials(e,a?.credentials),e}}export{t as CdkArgumentBuilder};
|
|
@@ -42,6 +42,17 @@ export interface CdkContext {
|
|
|
42
42
|
* (MANAGED_DOMAIN_CONTEXT_PREFIX).
|
|
43
43
|
*/
|
|
44
44
|
managedDomainBindings?: Record<string, string>;
|
|
45
|
+
/**
|
|
46
|
+
* Record<zoneName, JSON ManagedDomainCoverage> of deploy-time-resolved
|
|
47
|
+
* certificate SAN host lists (D5). Emitted as one
|
|
48
|
+
* `-c fjall:managedDomainCoverage:<zoneName>=<json>` context arg per entry
|
|
49
|
+
* and consumed by `readInjectedManagedDomainCoverage` in
|
|
50
|
+
* @fjall/components-infrastructure (`utils/managedDomainContext.ts`). Keep
|
|
51
|
+
* the key prefix literal in sync with that reader
|
|
52
|
+
* (MANAGED_DOMAIN_COVERAGE_CONTEXT_PREFIX). SEPARATE from the binding key
|
|
53
|
+
* because the binding parser fails closed on unknown fields.
|
|
54
|
+
*/
|
|
55
|
+
managedDomainCoverage?: Record<string, string>;
|
|
45
56
|
/**
|
|
46
57
|
* Two-phase delegated-domain deploy gate (D8): `"zone"` deploys the child
|
|
47
58
|
* zone + NS delegation only, `"full"` additionally issues certificates.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var d=Object.defineProperty;var o=(a,e)=>d(a,"name",{value:e,configurable:!0});import{getApplicationStackName as l,getOrganisationStackName as u,isApplicationStack as
|
|
1
|
+
var d=Object.defineProperty;var o=(a,e)=>d(a,"name",{value:e,configurable:!0});import{getApplicationStackName as l,getOrganisationStackName as u,isApplicationStack as c}from"../../types/operations.js";const p=5e3;function t(a,e){if(a&&!a.includes("*"))return a;if(a){const n=a.match(/\*?(\w+)\*?/);if(n?.[1]){const i=n[1],r=e.target;return c(i)?l(r,i):`${r}${i}`}return a}}o(t,"resolveStackName");function f(a){const e=a.deployType;return e==="organisation"||e==="platform"||e==="account"?u(e):`${a.target}Network`}o(f,"getFallbackStackName");function s(a,e,n){return{accountId:e,region:n,environment:a.environment,managedAccount:a.isManagedAccount,accountName:a.accountName,orgId:a.orgId,rootId:a.rootId,managementAccountId:a.managementAccountId,devOuId:a.devOuId,ipamPoolId:a.ipamPoolId,fjallOrgId:a.fjallOrgId,fjallOidcConfigured:a.fjallOidcConfigured?"true":void 0,fjallAccountGlobalsConfigured:a.fjallAccountGlobalsConfigured?"true":void 0,fjallAccountTrailState:a.fjallAccountTrailState,orgConfig:a.orgConfig,fjallAdoptBackupVault:a.fjallAdoptBackupVault?"true":void 0,resolvedSecretArns:a.resolvedSecretArns,managedDomainBindings:a.managedDomainBindings,managedDomainCoverage:a.managedDomainCoverage,capacityIdentity:a.capacityIdentity}}o(s,"buildDeploymentCdkContext");export{p as STACK_DETECTION_FALLBACK_MS,s as buildDeploymentCdkContext,f as getFallbackStackName,t as resolveStackName};
|
|
@@ -37,6 +37,7 @@ export declare class CdkContextBuilder {
|
|
|
37
37
|
fjallAccountTrailState?: string;
|
|
38
38
|
orgConfig?: string;
|
|
39
39
|
managedDomainBindings?: Record<string, string>;
|
|
40
|
+
managedDomainCoverage?: Record<string, string>;
|
|
40
41
|
capacityIdentity?: string;
|
|
41
42
|
}, options: {
|
|
42
43
|
verbose?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var r=Object.defineProperty;var d=(o,a)=>r(o,"name",{value:a,configurable:!0});import{DEFAULT_REGION as t}from"@fjall/generator";class g{static{d(this,"CdkContextBuilder")}static buildDeploymentContext(a,e,l){return{deployType:a.deployType,target:a.target,path:a.path,...a.assemblyDir!==void 0?{assemblyDir:a.assemblyDir}:{},...a.environment!==void 0?{environment:a.environment}:{},options:e,stackOutputs:a.stackOutputs||{},callerIdentity:a.callerIdentity,region:a.region||l?.primaryRegion||t,isManagedAccount:a.isManagedAccount,accountName:a.accountName,logPath:a.logPath,orgId:a.orgId,rootId:a.rootId,managementAccountId:a.managementAccountId,devOuId:a.devOuId,ipamPoolId:a.ipamPoolId,fjallOrgId:a.fjallOrgId,fjallOidcConfigured:a.fjallOidcConfigured,fjallAccountGlobalsConfigured:a.fjallAccountGlobalsConfigured,fjallAccountTrailState:a.fjallAccountTrailState,orgConfig:a.orgConfig,managedDomainBindings:a.managedDomainBindings,managedDomainCoverage:a.managedDomainCoverage,capacityIdentity:a.capacityIdentity}}static updateContext(a,e){return{...a,...e}}}export{g as CdkContextBuilder};
|