@fjall/components-infrastructure 0.96.0 → 0.99.3

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.
Files changed (209) hide show
  1. package/dist/lib/app.d.ts +68 -1
  2. package/dist/lib/app.js +113 -4
  3. package/dist/lib/config/aws/__t17fixture.d.ts +1 -0
  4. package/dist/lib/config/aws/__t17fixture.js +3 -0
  5. package/dist/lib/config/aws/__t17fixtureType.d.ts +2 -0
  6. package/dist/lib/config/aws/__t17fixtureType.js +1 -0
  7. package/dist/lib/config/aws/alarmTopic.js +8 -4
  8. package/dist/lib/config/aws/cloudTrail.js +1 -1
  9. package/dist/lib/config/aws/disasterRecovery.js +11 -16
  10. package/dist/lib/config/aws/ecrDefaultImage.d.ts +0 -1
  11. package/dist/lib/config/aws/ecrDefaultImage.js +13 -23
  12. package/dist/lib/config/aws/identityCenter.d.ts +10 -3
  13. package/dist/lib/config/aws/identityCenter.js +101 -37
  14. package/dist/lib/config/aws/identityCenterGroupMembership.js +8 -2
  15. package/dist/lib/config/aws/identityCenterMembership.d.ts +11 -0
  16. package/dist/lib/config/aws/identityCenterMembership.js +61 -0
  17. package/dist/lib/config/aws/index.d.ts +1 -1
  18. package/dist/lib/config/aws/index.js +1 -1
  19. package/dist/lib/config/aws/ipam.js +6 -11
  20. package/dist/lib/config/aws/oidcConnector.js +5 -1
  21. package/dist/lib/config/aws/scpPreset.js +4 -1
  22. package/dist/lib/patterns/aws/_eslint_test_tmp/leak.d.ts +1 -0
  23. package/dist/lib/patterns/aws/_eslint_test_tmp/leak.js +4 -0
  24. package/dist/lib/patterns/aws/account.js +2 -4
  25. package/dist/lib/patterns/aws/apexDomainPattern.js +10 -10
  26. package/dist/lib/patterns/aws/bastionFactory.d.ts +10 -0
  27. package/dist/lib/patterns/aws/bastionFactory.js +29 -0
  28. package/dist/lib/patterns/aws/buildkite.d.ts +2 -2
  29. package/dist/lib/patterns/aws/buildkite.js +51 -97
  30. package/dist/lib/patterns/aws/cdn.js +1 -1
  31. package/dist/lib/patterns/aws/clickhouseDatabase.d.ts +173 -0
  32. package/dist/lib/patterns/aws/clickhouseDatabase.js +601 -0
  33. package/dist/lib/patterns/aws/compute.d.ts +4 -6
  34. package/dist/lib/patterns/aws/compute.js +7 -13
  35. package/dist/lib/patterns/aws/computeEcs.d.ts +93 -5
  36. package/dist/lib/patterns/aws/computeEcs.js +867 -37
  37. package/dist/lib/patterns/aws/computeEcsTypes.d.ts +528 -25
  38. package/dist/lib/patterns/aws/computeEcsTypes.js +10 -0
  39. package/dist/lib/patterns/aws/computeLambda.d.ts +0 -5
  40. package/dist/lib/patterns/aws/computeLambda.js +1 -2
  41. package/dist/lib/patterns/aws/database.d.ts +50 -8
  42. package/dist/lib/patterns/aws/database.js +183 -27
  43. package/dist/lib/patterns/aws/domain.js +6 -4
  44. package/dist/lib/patterns/aws/index.d.ts +1 -0
  45. package/dist/lib/patterns/aws/index.js +1 -0
  46. package/dist/lib/patterns/aws/interfaces/compute.d.ts +7 -1
  47. package/dist/lib/patterns/aws/interfaces/database.d.ts +187 -8
  48. package/dist/lib/patterns/aws/interfaces/database.js +17 -3
  49. package/dist/lib/patterns/aws/interfaces/index.d.ts +2 -1
  50. package/dist/lib/patterns/aws/interfaces/index.js +3 -1
  51. package/dist/lib/patterns/aws/interfaces/messaging.d.ts +7 -0
  52. package/dist/lib/patterns/aws/interfaces/migrationContributor.d.ts +47 -0
  53. package/dist/lib/patterns/aws/interfaces/migrationContributor.js +9 -0
  54. package/dist/lib/patterns/aws/messaging.d.ts +66 -10
  55. package/dist/lib/patterns/aws/messaging.js +115 -20
  56. package/dist/lib/patterns/aws/network.js +16 -7
  57. package/dist/lib/patterns/aws/organisation.d.ts +4 -0
  58. package/dist/lib/patterns/aws/organisation.js +22 -4
  59. package/dist/lib/patterns/aws/storage.d.ts +1 -2
  60. package/dist/lib/patterns/aws/storage.js +3 -2
  61. package/dist/lib/patterns/aws/vpcPeer.js +3 -1
  62. package/dist/lib/resources/aws/analytics/clickhouse.js +18 -9
  63. package/dist/lib/resources/aws/analytics/clickhouseAlarms.d.ts +24 -9
  64. package/dist/lib/resources/aws/analytics/clickhouseAlarms.js +61 -10
  65. package/dist/lib/resources/aws/analytics/clickhouseConstants.d.ts +3 -3
  66. package/dist/lib/resources/aws/analytics/clickhouseConstants.js +3 -3
  67. package/dist/lib/resources/aws/analytics/clickhouseTypes.d.ts +7 -1
  68. package/dist/lib/resources/aws/analytics/clickhouseUserData.d.ts +1 -1
  69. package/dist/lib/resources/aws/analytics/clickhouseUserData.js +53 -3
  70. package/dist/lib/resources/aws/base/awsStack.js +4 -2
  71. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.d.ts +2 -0
  72. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.js +11 -0
  73. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.d.ts +52 -0
  74. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.js +60 -0
  75. package/dist/lib/resources/aws/compute/blockDeviceVolume.d.ts +8 -0
  76. package/dist/lib/resources/aws/compute/blockDeviceVolume.js +10 -0
  77. package/dist/lib/resources/aws/compute/ec2.d.ts +132 -12
  78. package/dist/lib/resources/aws/compute/ec2.js +163 -23
  79. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.d.ts +41 -0
  80. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.js +194 -0
  81. package/dist/lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs +458 -0
  82. package/dist/lib/resources/aws/compute/ecs.d.ts +27 -1
  83. package/dist/lib/resources/aws/compute/ecs.js +42 -2
  84. package/dist/lib/resources/aws/compute/ecsConstants.d.ts +9 -0
  85. package/dist/lib/resources/aws/compute/ecsConstants.js +16 -0
  86. package/dist/lib/resources/aws/compute/ecsImages.js +32 -20
  87. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.d.ts +96 -0
  88. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.js +113 -0
  89. package/dist/lib/resources/aws/compute/ecsNetworking.d.ts +2 -1
  90. package/dist/lib/resources/aws/compute/ecsNetworking.js +18 -6
  91. package/dist/lib/resources/aws/compute/ecsServiceFactory.d.ts +13 -4
  92. package/dist/lib/resources/aws/compute/ecsServiceFactory.js +155 -33
  93. package/dist/lib/resources/aws/compute/ecsTaskDefinition.d.ts +31 -1
  94. package/dist/lib/resources/aws/compute/ecsTaskDefinition.js +102 -6
  95. package/dist/lib/resources/aws/compute/ecsTypes.d.ts +173 -13
  96. package/dist/lib/resources/aws/compute/ecsValidation.d.ts +9 -0
  97. package/dist/lib/resources/aws/compute/ecsValidation.js +63 -0
  98. package/dist/lib/resources/aws/compute/index.d.ts +2 -0
  99. package/dist/lib/resources/aws/compute/index.js +2 -0
  100. package/dist/lib/resources/aws/compute/lambda.d.ts +7 -13
  101. package/dist/lib/resources/aws/compute/lambda.js +30 -38
  102. package/dist/lib/resources/aws/compute/lifecycleHookLambda.source.cjs +192 -0
  103. package/dist/lib/resources/aws/compute/persistentDataVolume.d.ts +104 -0
  104. package/dist/lib/resources/aws/compute/persistentDataVolume.js +245 -0
  105. package/dist/lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs +398 -0
  106. package/dist/lib/resources/aws/compute/samApplication.d.ts +15 -0
  107. package/dist/lib/resources/aws/compute/samApplication.js +27 -0
  108. package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +159 -0
  109. package/dist/lib/resources/aws/database/clickhouseConstants.js +181 -0
  110. package/dist/lib/resources/aws/database/clickhouseSchemas.d.ts +71 -0
  111. package/dist/lib/resources/aws/database/clickhouseSchemas.js +160 -0
  112. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.d.ts +14 -0
  113. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.js +23 -0
  114. package/dist/lib/resources/aws/database/clickhouseUserData.d.ts +69 -0
  115. package/dist/lib/resources/aws/database/clickhouseUserData.js +371 -0
  116. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.d.ts +56 -0
  117. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.js +112 -0
  118. package/dist/lib/resources/aws/database/rdsAurora.d.ts +8 -1
  119. package/dist/lib/resources/aws/database/rdsAurora.js +42 -32
  120. package/dist/lib/resources/aws/database/rdsAuroraGlobal.d.ts +15 -2
  121. package/dist/lib/resources/aws/database/rdsAuroraGlobal.js +39 -43
  122. package/dist/lib/resources/aws/database/rdsDefaults.d.ts +6 -0
  123. package/dist/lib/resources/aws/database/rdsDefaults.js +7 -1
  124. package/dist/lib/resources/aws/database/rdsHelpers.d.ts +3 -3
  125. package/dist/lib/resources/aws/database/rdsHelpers.js +1 -0
  126. package/dist/lib/resources/aws/database/rdsInstance.d.ts +8 -1
  127. package/dist/lib/resources/aws/database/rdsInstance.js +51 -34
  128. package/dist/lib/resources/aws/database/rdsProxyOutput.d.ts +1 -1
  129. package/dist/lib/resources/aws/database/rdsProxyOutput.js +1 -1
  130. package/dist/lib/resources/aws/iam/delegationRole.js +1 -1
  131. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.d.ts +9 -0
  132. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.js +12 -0
  133. package/dist/lib/resources/aws/iam/identityCenter/index.d.ts +1 -0
  134. package/dist/lib/resources/aws/iam/identityCenter/index.js +1 -0
  135. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.d.ts +1 -0
  136. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.js +1 -0
  137. package/dist/lib/resources/aws/logging/logGroup.d.ts +0 -8
  138. package/dist/lib/resources/aws/logging/logGroup.js +0 -11
  139. package/dist/lib/resources/aws/messaging/defaultEventBus.d.ts +7 -0
  140. package/dist/lib/resources/aws/messaging/defaultEventBus.js +21 -0
  141. package/dist/lib/resources/aws/messaging/eventBridgeRule.d.ts +96 -0
  142. package/dist/lib/resources/aws/messaging/eventBridgeRule.js +110 -0
  143. package/dist/lib/resources/aws/messaging/eventTargets.d.ts +84 -0
  144. package/dist/lib/resources/aws/messaging/eventTargets.js +152 -0
  145. package/dist/lib/resources/aws/messaging/eventbridge.d.ts +25 -2
  146. package/dist/lib/resources/aws/messaging/eventbridge.js +22 -10
  147. package/dist/lib/resources/aws/messaging/index.d.ts +5 -0
  148. package/dist/lib/resources/aws/messaging/index.js +2 -0
  149. package/dist/lib/resources/aws/messaging/schedule.d.ts +118 -0
  150. package/dist/lib/resources/aws/messaging/schedule.js +64 -0
  151. package/dist/lib/resources/aws/messaging/sns.d.ts +2 -1
  152. package/dist/lib/resources/aws/messaging/sqs.d.ts +2 -1
  153. package/dist/lib/resources/aws/messaging/subscription.d.ts +112 -0
  154. package/dist/lib/resources/aws/messaging/subscription.js +67 -0
  155. package/dist/lib/resources/aws/messaging/utils.d.ts +6 -0
  156. package/dist/lib/resources/aws/messaging/utils.js +10 -0
  157. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.d.ts +60 -0
  158. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.js +139 -0
  159. package/dist/lib/resources/aws/monitoring/index.d.ts +2 -0
  160. package/dist/lib/resources/aws/monitoring/index.js +2 -0
  161. package/dist/lib/resources/aws/monitoring/scheduleAlarms.d.ts +47 -0
  162. package/dist/lib/resources/aws/monitoring/scheduleAlarms.js +106 -0
  163. package/dist/lib/resources/aws/networking/crossAccountDelegationRecord.js +6 -4
  164. package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.js +17 -13
  165. package/dist/lib/resources/aws/networking/dnsRecord/dnsRecordBase.js +7 -5
  166. package/dist/lib/resources/aws/networking/domainCertificate.d.ts +2 -2
  167. package/dist/lib/resources/aws/networking/domainCertificate.js +6 -4
  168. package/dist/lib/resources/aws/networking/hostedZone.js +6 -5
  169. package/dist/lib/resources/aws/networking/serviceDiscovery.d.ts +96 -0
  170. package/dist/lib/resources/aws/networking/serviceDiscovery.js +96 -0
  171. package/dist/lib/resources/aws/networking/vpc.d.ts +4 -1
  172. package/dist/lib/resources/aws/networking/vpc.js +4 -1
  173. package/dist/lib/resources/aws/networking/vpcPeeringConnection.js +21 -3
  174. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.d.ts +16 -5
  175. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.js +17 -3
  176. package/dist/lib/resources/aws/organisation/index.d.ts +1 -1
  177. package/dist/lib/resources/aws/organisation/organisationPolicy.d.ts +2 -0
  178. package/dist/lib/resources/aws/organisation/organisationPolicy.js +3 -2
  179. package/dist/lib/resources/aws/secrets/secret.d.ts +7 -0
  180. package/dist/lib/resources/aws/secrets/secret.js +4 -3
  181. package/dist/lib/resources/aws/storage/bucketDeployment.d.ts +16 -0
  182. package/dist/lib/resources/aws/storage/bucketDeployment.js +17 -0
  183. package/dist/lib/resources/aws/storage/ecr.js +5 -5
  184. package/dist/lib/resources/aws/storage/index.d.ts +1 -0
  185. package/dist/lib/resources/aws/storage/index.js +1 -0
  186. package/dist/lib/resources/aws/storage/s3.js +10 -3
  187. package/dist/lib/resources/aws/utilities/customResource.js +18 -9
  188. package/dist/lib/synth_dump.d.ts +1 -0
  189. package/dist/lib/synth_dump.js +42 -0
  190. package/dist/lib/utils/cdkContext.d.ts +2 -0
  191. package/dist/lib/utils/cdkContext.js +4 -2
  192. package/dist/lib/utils/connections.js +6 -0
  193. package/dist/lib/utils/connector.d.ts +12 -0
  194. package/dist/lib/utils/costAllocationTags.d.ts +9 -0
  195. package/dist/lib/utils/costAllocationTags.js +11 -1
  196. package/dist/lib/utils/databaseTypes.d.ts +14 -0
  197. package/dist/lib/utils/getConfig.d.ts +2 -0
  198. package/dist/lib/utils/getConfig.js +2 -0
  199. package/dist/lib/utils/index.d.ts +1 -0
  200. package/dist/lib/utils/index.js +1 -0
  201. package/dist/lib/utils/manifestWriter.d.ts +6 -89
  202. package/dist/lib/utils/manifestWriter.js +36 -23
  203. package/dist/lib/utils/migrationVersionResolvers.d.ts +2 -0
  204. package/dist/lib/utils/migrationVersionResolvers.js +2 -0
  205. package/dist/lib/utils/orgConfigParser.js +2 -1
  206. package/dist/lib/utils/resolveAlertsTopic.d.ts +14 -0
  207. package/dist/lib/utils/resolveAlertsTopic.js +30 -0
  208. package/dist/lib/utils/validationLogger.js +6 -3
  209. package/package.json +22 -19
@@ -1,11 +1,11 @@
1
- import { AwsLogDriver, FargateTaskDefinition, Ec2TaskDefinition, NetworkMode, CpuArchitecture, OperatingSystemFamily } from "aws-cdk-lib/aws-ecs";
1
+ import { AwsLogDriver, ContainerDependencyCondition, FargateTaskDefinition, Ec2TaskDefinition, NetworkMode, CpuArchitecture, OperatingSystemFamily } from "aws-cdk-lib/aws-ecs";
2
2
  import { Duration } from "aws-cdk-lib";
3
3
  import { Secret as EcsSecret } from "aws-cdk-lib/aws-ecs";
4
4
  import { Secret } from "aws-cdk-lib/aws-secretsmanager";
5
5
  import { StringParameter } from "aws-cdk-lib/aws-ssm";
6
6
  import { resolveOrgId } from "../../../utils/cdkContext.js";
7
7
  import { validateSsmPathComponent } from "./ecsValidation.js";
8
- import { DEFAULT_LOG_RETENTION_DAYS } from "./ecsConstants.js";
8
+ import { DEFAULT_LOG_RETENTION_DAYS, DEFAULT_FARGATE_CPU, DEFAULT_FARGATE_MEMORY_MIB, DEFAULT_EC2_CONTAINER_MEMORY_MIB } from "./ecsConstants.js";
9
9
  import { getContainerImage } from "./ecsImages.js";
10
10
  import { resolveRemoteConnections } from "./ecsRemoteConnections.js";
11
11
  // Re-export extracted functions so existing consumers are not broken
@@ -66,8 +66,8 @@ export function deriveSsmSecretsPath(props, serviceName, explicitPath) {
66
66
  return `/${appName}/${props.clusterName}/${serviceName}`;
67
67
  }
68
68
  export function createTaskDefinition(ctx, serviceName, serviceProps, executionRole, taskRole) {
69
- const cpu = serviceProps.cpu ?? 256;
70
- const memoryLimitMiB = serviceProps.memoryLimitMiB ?? 512;
69
+ const cpu = serviceProps.cpu ?? DEFAULT_FARGATE_CPU;
70
+ const memoryLimitMiB = serviceProps.memoryLimitMiB ?? DEFAULT_FARGATE_MEMORY_MIB;
71
71
  if (isServiceFargate(serviceProps)) {
72
72
  return new FargateTaskDefinition(ctx.scope, `${serviceName}TaskDefinition`, {
73
73
  family: `${ctx.props.clusterName}-${serviceName}`,
@@ -82,16 +82,62 @@ export function createTaskDefinition(ctx, serviceName, serviceProps, executionRo
82
82
  });
83
83
  }
84
84
  else {
85
+ const networkMode = serviceProps.networkMode ??
86
+ (ctx.directAccessEnabled ? NetworkMode.HOST : NetworkMode.AWS_VPC);
85
87
  return new Ec2TaskDefinition(ctx.scope, `${serviceName}TaskDefinition`, {
86
88
  family: `${ctx.props.clusterName}-${serviceName}`,
87
89
  executionRole,
88
90
  taskRole,
89
- ...(ctx.directAccessEnabled && { networkMode: NetworkMode.HOST })
91
+ ...(networkMode !== undefined && { networkMode })
90
92
  });
91
93
  }
92
94
  }
95
+ /**
96
+ * Routing point for `Ec2TaskDefinition`s used by EventBridge-triggered scheduled
97
+ * tasks (per `EcsClusterConfig.scheduledTasks`). Pattern-layer callers MUST
98
+ * reach `Ec2TaskDefinition` through this wrapper rather than instantiating the
99
+ * raw CDK class — a future SOC2-relevant default (encryption, retention,
100
+ * uniform tags) added here propagates to every scheduled-task task def at once.
101
+ *
102
+ * Compare with `createTaskDefinition()` above, which is the per-service path;
103
+ * this is the per-schedule path and intentionally kept narrow (no execution /
104
+ * task roles — scheduled tasks use task-definition defaults).
105
+ */
106
+ export function createScheduledTaskDefinition(scope, id, props) {
107
+ return new Ec2TaskDefinition(scope, id, {
108
+ family: props.family,
109
+ ...(props.networkMode !== undefined && { networkMode: props.networkMode })
110
+ });
111
+ }
112
+ /**
113
+ * Routing point for `FargateTaskDefinition`s used by the lifecycle-hook
114
+ * migration runner when `migrations.separateTaskDef` is set. Synthesises a
115
+ * dedicated Fargate task definition with caller-supplied CPU/memory and the
116
+ * supplied execution + task roles. Runtime platform pinned to ARM64/Linux to
117
+ * match the service task def default.
118
+ */
119
+ export function createMigrationTaskDefinition(scope, id, props) {
120
+ return new FargateTaskDefinition(scope, id, {
121
+ family: props.family,
122
+ cpu: props.cpu,
123
+ memoryLimitMiB: props.memoryLimitMiB,
124
+ executionRole: props.executionRole,
125
+ taskRole: props.taskRole,
126
+ runtimePlatform: {
127
+ cpuArchitecture: CpuArchitecture.ARM64,
128
+ operatingSystemFamily: OperatingSystemFamily.LINUX
129
+ }
130
+ });
131
+ }
132
+ const CONTAINER_DEPENDENCY_CONDITIONS = {
133
+ START: ContainerDependencyCondition.START,
134
+ COMPLETE: ContainerDependencyCondition.COMPLETE,
135
+ SUCCESS: ContainerDependencyCondition.SUCCESS,
136
+ HEALTHY: ContainerDependencyCondition.HEALTHY
137
+ };
93
138
  export function addContainersToTask(ctx, serviceName, serviceProps, taskDefinition) {
94
139
  const containers = [];
140
+ const containerByName = new Map();
95
141
  let primaryContainer;
96
142
  const orgId = resolveOrgId(ctx.scope.node);
97
143
  const remoteEnvByService = resolveRemoteConnections([serviceProps], ctx.scope, orgId);
@@ -158,19 +204,69 @@ export function addContainersToTask(ctx, serviceName, serviceProps, taskDefiniti
158
204
  : undefined
159
205
  }
160
206
  : undefined,
207
+ stopTimeout: containerConfig.stopTimeout !== undefined
208
+ ? Duration.seconds(containerConfig.stopTimeout)
209
+ : undefined,
161
210
  ...(isServiceEc2(serviceProps) && {
162
- memoryLimitMiB: serviceProps.ec2Config?.memoryLimitMiB ?? 1024
211
+ memoryLimitMiB: serviceProps.ec2Config?.memoryLimitMiB ??
212
+ DEFAULT_EC2_CONTAINER_MEMORY_MIB
163
213
  })
164
214
  });
215
+ if (containerConfig.port !== undefined &&
216
+ containerConfig.portMappings !== undefined &&
217
+ containerConfig.portMappings.length > 0) {
218
+ throw new Error(`Container '${containerConfig.name}' in service '${serviceName}': ` +
219
+ `"port" and "portMappings" are mutually exclusive on EcsContainerConfig — supply one or the other, not both.`);
220
+ }
165
221
  if (containerConfig.port) {
166
222
  container.addPortMappings({
167
223
  containerPort: containerConfig.port
168
224
  });
169
225
  }
226
+ else if (containerConfig.portMappings &&
227
+ containerConfig.portMappings.length > 0) {
228
+ container.addPortMappings(...containerConfig.portMappings);
229
+ }
230
+ if (containerConfig.volumes && containerConfig.volumes.length > 0) {
231
+ for (const volume of containerConfig.volumes) {
232
+ taskDefinition.addVolume({
233
+ name: volume.name,
234
+ ...(volume.hostSourcePath !== undefined && {
235
+ host: { sourcePath: volume.hostSourcePath }
236
+ })
237
+ });
238
+ container.addMountPoints({
239
+ sourceVolume: volume.name,
240
+ containerPath: volume.mountPath,
241
+ readOnly: volume.readOnly ?? false
242
+ });
243
+ }
244
+ }
170
245
  if (isFirstWithPort) {
171
246
  primaryContainer = container;
172
247
  }
173
248
  containers.push(container);
249
+ containerByName.set(containerConfig.name, container);
250
+ }
251
+ for (const containerConfig of serviceProps.containers) {
252
+ if (!containerConfig.dependsOn || containerConfig.dependsOn.length === 0) {
253
+ continue;
254
+ }
255
+ const dependent = containerByName.get(containerConfig.name);
256
+ if (!dependent)
257
+ continue;
258
+ for (const dep of containerConfig.dependsOn) {
259
+ const target = containerByName.get(dep.container);
260
+ if (!target) {
261
+ const available = [...containerByName.keys()].join(", ");
262
+ throw new Error(`Service '${serviceName}': container '${containerConfig.name}' dependsOn ` +
263
+ `unknown container '${dep.container}'. Available containers: ${available}.`);
264
+ }
265
+ dependent.addContainerDependencies({
266
+ container: target,
267
+ condition: CONTAINER_DEPENDENCY_CONDITIONS[dep.condition]
268
+ });
269
+ }
174
270
  }
175
271
  return { containers, primaryContainer };
176
272
  }
@@ -1,6 +1,9 @@
1
- import { type ContainerDefinition, type RepositoryImage } from "aws-cdk-lib/aws-ecs";
2
- import { type IVpc } from "aws-cdk-lib/aws-ec2";
1
+ import { type ContainerDefinition, type NetworkMode, type PortMapping, type RepositoryImage } from "aws-cdk-lib/aws-ecs";
2
+ import { type BlockDevice, type IMachineImage, type ISecurityGroup, type IVpc, type UserData } from "aws-cdk-lib/aws-ec2";
3
+ import { type Monitoring } from "aws-cdk-lib/aws-autoscaling";
4
+ import { type IService } from "aws-cdk-lib/aws-servicediscovery";
3
5
  import { type IManagedPolicy, type PolicyDocument } from "aws-cdk-lib/aws-iam";
6
+ import type { DockerBuild } from "@fjall/util/manifest/schemas";
4
7
  import { type TargetTrackingScalingPolicy } from "aws-cdk-lib/aws-applicationautoscaling";
5
8
  import { type GeoLocation } from "aws-cdk-lib/aws-route53";
6
9
  import { type Repository } from "aws-cdk-lib/aws-ecr";
@@ -15,6 +18,7 @@ import { type SecretImport } from "../secrets/index.js";
15
18
  import type { ManagedDomainExports } from "../../../utils/domainTypes.js";
16
19
  import type { ITopic } from "aws-cdk-lib/aws-sns";
17
20
  import type { EcsServiceAlarmThresholds } from "../monitoring/index.js";
21
+ import { type Ec2InstancePersistentDataVolumeConfig } from "./ec2.js";
18
22
  export declare enum Protocol {
19
23
  HTTP = 0,
20
24
  HTTPS = 1
@@ -23,7 +27,8 @@ export declare enum ScalingType {
23
27
  CPU = "ECSServiceAverageCPUUtilization",
24
28
  MEMORY = "ECSServiceAverageMemoryUtilization"
25
29
  }
26
- export type EcsCapacityProvider = "FARGATE" | "FARGATE_SPOT" | "EC2";
30
+ import type { EcsCapacityProvider } from "@fjall/generator";
31
+ export type { EcsCapacityProvider };
27
32
  /**
28
33
  * EC2 capacity configuration for ECS EC2-backed clusters.
29
34
  * Only used when capacityProvider is "EC2".
@@ -47,6 +52,52 @@ export interface Ec2CapacityConfig {
47
52
  /** Return instances to the pool on scale-in instead of terminating. Default: true */
48
53
  reuseOnScaleIn?: boolean;
49
54
  };
55
+ /** CDK `AutoScalingGroupProps.desiredCapacity` — initial instance count. */
56
+ desiredCapacity?: number;
57
+ /**
58
+ * CDK `LaunchTemplateProps.machineImage`. When provided, overrides the
59
+ * default `EcsOptimizedImage.amazonLinux2023(amiHardwareType)`. Use for
60
+ * stateful workloads requiring a custom AMI.
61
+ */
62
+ machineImage?: IMachineImage;
63
+ /**
64
+ * CDK `aws-cdk-lib/aws-autoscaling.Monitoring`. Routes through the
65
+ * LaunchTemplate's `detailedMonitoring` field. Default: `Monitoring.BASIC`.
66
+ */
67
+ instanceMonitoring?: Monitoring;
68
+ /** CDK `LaunchTemplateProps.blockDevices`. Use for EBS attachments. */
69
+ blockDevices?: BlockDevice[];
70
+ /**
71
+ * CDK `LaunchTemplateProps.userData`. When provided, overrides the
72
+ * default empty `UserData.forLinux()`.
73
+ */
74
+ userData?: UserData;
75
+ /** CDK `LaunchTemplateProps.associatePublicIpAddress`. */
76
+ associatePublicIpAddress?: boolean;
77
+ /**
78
+ * Pin the ASG to a specific set of availability zones. When
79
+ * `persistentDataVolume` is set, MUST contain exactly one entry matching
80
+ * `persistentDataVolume.availabilityZone` — the standalone EBS volume is
81
+ * AZ-local and cannot follow a multi-AZ ASG. Merged into `vpcSubnets` at
82
+ * the `Ec2Instance` boundary.
83
+ */
84
+ availabilityZones?: string[];
85
+ /**
86
+ * Pairs the EC2 capacity ASG with a standalone EBS data volume that
87
+ * re-attaches across instance refreshes. Forwarded to `Ec2Instance` which
88
+ * locates and detaches the volume via TERMINATING/LAUNCHING lifecycle
89
+ * hooks. Implies a singleton service — do not share an ASG across
90
+ * services when this is set (`getEc2ConfigKey` adds a discriminator to
91
+ * keep them apart).
92
+ */
93
+ persistentDataVolume?: Ec2InstancePersistentDataVolumeConfig;
94
+ /**
95
+ * Tags applied to the underlying ASG with `applyToLaunchedInstances: true`
96
+ * so every launched EC2 instance carries the tags. Used for tag-based SSM
97
+ * `SendCommand` targeting (`Targets: [{ Key: "tag:<name>", Values: […] }]`).
98
+ * Empty-string keys or values are rejected at the resources layer.
99
+ */
100
+ tags?: Record<string, string>;
50
101
  }
51
102
  /**
52
103
  * Domain configuration for HTTPS and DNS.
@@ -69,6 +120,17 @@ export interface GeoLocationDomainConfig extends DomainBaseConfig {
69
120
  geoLocation: GeoLocation;
70
121
  }
71
122
  export type DomainConfig = DomainBaseConfig | LatencyDomainConfig | WeightedDomainConfig | GeoLocationDomainConfig;
123
+ /**
124
+ * A dependency on another container in the same task definition.
125
+ * Maps directly to ECS `ContainerDependency`. See `ContainerDependency` in
126
+ * the factory layer (`computeEcsTypes.ts`) for the public-facing variant.
127
+ *
128
+ * @internal
129
+ */
130
+ export interface EcsContainerDependency {
131
+ container: string;
132
+ condition: "START" | "COMPLETE" | "SUCCESS" | "HEALTHY";
133
+ }
72
134
  /**
73
135
  * Internal configuration for a container in a multi-container ECS task.
74
136
  *
@@ -136,6 +198,32 @@ export interface EcsClusterContainerConfig {
136
198
  retries?: number;
137
199
  startPeriod?: number;
138
200
  };
201
+ /**
202
+ * Containers in the same service that must reach a given state before this
203
+ * container starts. Resolved at synth time against the service's container names.
204
+ */
205
+ dependsOn?: EcsContainerDependency[];
206
+ /**
207
+ * Multi-port containers (CDK `PortMapping[]`). Mutually exclusive with
208
+ * `port` — supplying both throws at synth (AC30).
209
+ */
210
+ portMappings?: PortMapping[];
211
+ /**
212
+ * Host-bind volumes mounted into this container. Each entry produces a
213
+ * matching `taskDefinition.addVolume(...)` + `container.addMountPoints(...)`
214
+ * pair (AC31).
215
+ */
216
+ volumes?: Array<{
217
+ name: string;
218
+ hostSourcePath?: string;
219
+ mountPath: string;
220
+ readOnly?: boolean;
221
+ }>;
222
+ /**
223
+ * Time (seconds) ECS waits for the container to exit gracefully after
224
+ * SIGTERM before sending SIGKILL. Range 1–120. Default: ECS default (30s).
225
+ */
226
+ stopTimeout?: number;
139
227
  }
140
228
  /**
141
229
  * Cluster-level configuration.
@@ -167,6 +255,12 @@ export interface EcsClusterClusterConfig {
167
255
  * Only used when domain is specified.
168
256
  */
169
257
  domainConfig?: DomainConfig;
258
+ /**
259
+ * Externally-supplied EC2 capacity security group. When provided, the ECS
260
+ * service factory uses this SG instead of constructing its own. Pre-resolved
261
+ * by the patterns layer (AC26 — `EcsClusterConfig.securityGroup`).
262
+ */
263
+ securityGroup?: ISecurityGroup;
170
264
  }
171
265
  /**
172
266
  * Routing configuration for path/host-based routing on the ALB.
@@ -206,11 +300,23 @@ export interface EcsServiceProps {
206
300
  memoryLimitMiB?: number;
207
301
  /** Desired number of tasks. Default: 2 */
208
302
  desiredCount?: number;
209
- /** Scaling type (CPU or MEMORY). Omit to disable auto-scaling. */
303
+ /**
304
+ * Scaling type (CPU or MEMORY). Omit to disable auto-scaling — no
305
+ * `ScalableTarget` is registered, and `minCapacity`/`maxCapacity` below have
306
+ * no effect. The `desiredCount: 0 + minCapacity > 0` validation throw still
307
+ * fires regardless, so operator-intent contradictions surface at synth even
308
+ * when scaling is disabled.
309
+ */
210
310
  scalingType?: ScalingType;
211
- /** Minimum number of tasks for auto-scaling. Default: 2 */
311
+ /**
312
+ * Minimum number of tasks for auto-scaling. Default: tracks `desiredCount`.
313
+ * Only consulted when `scalingType` is set.
314
+ */
212
315
  minCapacity?: number;
213
- /** Maximum number of tasks for auto-scaling. Default: 10 */
316
+ /**
317
+ * Maximum number of tasks for auto-scaling. Default: `Math.max(desiredCount + 1, 3)`.
318
+ * Only consulted when `scalingType` is set.
319
+ */
214
320
  maxCapacity?: number;
215
321
  /**
216
322
  * Routing rules for this service on the cluster's ALB.
@@ -277,14 +383,11 @@ export interface EcsServiceProps {
277
383
  */
278
384
  ssmSecretsPath?: string;
279
385
  /**
280
- * Docker build target stage for multi-stage Dockerfiles.
281
- * When specified, appends `-<target>` to the image tag.
282
- *
283
- * @example
284
- * // With dockerTarget: "api", image tag becomes: myservice-api-latest
285
- * dockerTarget: "api"
386
+ * Dockerfile build configuration for this service. When `target` is set,
387
+ * the image tag suffix becomes `<service>-<target>-latest`.
388
+ * Mutually exclusive with `image` (pre-built URI).
286
389
  */
287
- dockerTarget?: string;
390
+ docker?: DockerBuild;
288
391
  /**
289
392
  * Per-service alarm configuration.
290
393
  * - undefined: use defaults (CPU, memory, running tasks, 5xx if ALB)
@@ -292,6 +395,63 @@ export interface EcsServiceProps {
292
395
  * - object: override specific thresholds
293
396
  */
294
397
  alarms?: EcsServiceAlarmThresholds | false;
398
+ /**
399
+ * Deployment circuit breaker policy.
400
+ * - undefined (default): `{ enable: true, rollback: true }`
401
+ * - `false`: disabled entirely (no breaker)
402
+ * - `{ rollback: boolean }`: override rollback behaviour
403
+ *
404
+ * @see https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-circuit-breaker.html
405
+ */
406
+ circuitBreaker?: false | {
407
+ rollback?: boolean;
408
+ };
409
+ /**
410
+ * Rolling-deploy capacity bounds. Overrides the default
411
+ * `{ minHealthyPercent: 100, maxHealthyPercent: 200 }`. Singletons backed
412
+ * by an EBS volume that only one task can attach to (e.g. ClickHouse) need
413
+ * `{ minHealthyPercent: 0, maxHealthyPercent: 100 }` so the old task
414
+ * detaches before the new one starts.
415
+ *
416
+ * Bounds enforced by `validateEcsClusterProps`: `minHealthyPercent` must be
417
+ * 0–100, `maxHealthyPercent` must be 100–200, and the two cannot both be
418
+ * `100` (no capacity to drain or expand — deploys would never roll forward).
419
+ */
420
+ deployment?: {
421
+ minHealthyPercent?: number;
422
+ maxHealthyPercent?: number;
423
+ };
424
+ /**
425
+ * Pre-registered Cloud Map service. When provided, the underlying
426
+ * `Ec2Service`/`FargateService` calls `associateCloudMapService(...)` after
427
+ * construction. The patterns layer registers the service via
428
+ * `app.getNamespace().registerService({ name })` and threads the resulting
429
+ * `IService` here — keeping the resources layer free of namespace lookup.
430
+ */
431
+ cloudMapService?: IService;
432
+ /**
433
+ * DNS record type registered against `cloudMapService`. Default: `"A"`
434
+ * (matches CDK's default and works under `awsvpc`). Set to `"SRV"` when
435
+ * the service runs under `host` or `bridge` network mode — CDK's
436
+ * `Ec2Service.associateCloudMapService(...)` rejects A records there and
437
+ * requires `containerName` + `containerPort`, derived from the primary
438
+ * container.
439
+ */
440
+ cloudMapDnsRecordType?: "A" | "SRV";
441
+ /**
442
+ * Override the task definition's `NetworkMode`. Default for EC2 services
443
+ * is `AWS_VPC` (or `HOST` when `cluster.directAccess`); set to `BRIDGE`
444
+ * for dynamic-port-mapping ALB integration or when ENI quota is a concern.
445
+ */
446
+ networkMode?: NetworkMode;
447
+ /**
448
+ * Pre-existing security groups to attach to the service's task ENIs (AWS_VPC
449
+ * mode) instead of letting CDK auto-generate a default service SG. Used by
450
+ * stateful consumers (e.g. `ClickHouseDatabase`) that own a wrapper SG and
451
+ * need `this.connections.securityGroups[0]` to be the SG actually arbitrating
452
+ * inbound traffic to the task. Empty/omitted → CDK auto-creates one.
453
+ */
454
+ securityGroups?: ISecurityGroup[];
295
455
  }
296
456
  /**
297
457
  * Props for creating an ECS cluster with multiple services.
@@ -3,6 +3,15 @@ import type { EcsClusterProps } from "./ecsTypes.js";
3
3
  * Validates ECS cluster props before construction.
4
4
  * Pure function — does not depend on class state.
5
5
  *
6
+ * Note: `service.migrations` and `service.migrations.separateTaskDef` are
7
+ * intentionally validated only at the patterns layer (`validateEcsProps`
8
+ * in `lib/patterns/aws/computeEcs.ts`). The migrations sugar is a
9
+ * patterns-layer concept — it is translated into `service.containers`
10
+ * (init-container mode) or a separate Fargate task definition + lifecycle
11
+ * hook (lifecycle-hook mode) BEFORE reaching `EcsClusterProps`. Resources-
12
+ * layer consumers never see a `migrations` field, so duplicating the
13
+ * validation here would be unreachable.
14
+ *
6
15
  * @param props - The cluster props to validate
7
16
  * @throws Error if validation fails
8
17
  */
@@ -1,7 +1,17 @@
1
+ import { NetworkMode } from "aws-cdk-lib/aws-ecs";
1
2
  /**
2
3
  * Validates ECS cluster props before construction.
3
4
  * Pure function — does not depend on class state.
4
5
  *
6
+ * Note: `service.migrations` and `service.migrations.separateTaskDef` are
7
+ * intentionally validated only at the patterns layer (`validateEcsProps`
8
+ * in `lib/patterns/aws/computeEcs.ts`). The migrations sugar is a
9
+ * patterns-layer concept — it is translated into `service.containers`
10
+ * (init-container mode) or a separate Fargate task definition + lifecycle
11
+ * hook (lifecycle-hook mode) BEFORE reaching `EcsClusterProps`. Resources-
12
+ * layer consumers never see a `migrations` field, so duplicating the
13
+ * validation here would be unreachable.
14
+ *
5
15
  * @param props - The cluster props to validate
6
16
  * @throws Error if validation fails
7
17
  */
@@ -47,6 +57,59 @@ export function validateEcsClusterProps(props) {
47
57
  throw new Error(`Service '${service.name}': Duplicate container names: ` +
48
58
  `${[...new Set(duplicateContainers)].join(", ")}`);
49
59
  }
60
+ for (const container of service.containers) {
61
+ if (container.stopTimeout !== undefined) {
62
+ if (!Number.isInteger(container.stopTimeout) ||
63
+ container.stopTimeout < 1 ||
64
+ container.stopTimeout > 120) {
65
+ throw new Error(`Service '${service.name}', container '${container.name ?? "(default)"}': ` +
66
+ `stopTimeout must be an integer between 1 and 120 seconds (got ${container.stopTimeout}).`);
67
+ }
68
+ }
69
+ }
70
+ if (service.capacityProvider === "EC2" && !service.ec2Config) {
71
+ throw new Error(`Service '${service.name}' uses EC2 capacity provider but no ec2Config is defined. ` +
72
+ "Provide ec2Config on the service.");
73
+ }
74
+ if (service.deployment !== undefined) {
75
+ const min = service.deployment.minHealthyPercent;
76
+ const max = service.deployment.maxHealthyPercent;
77
+ if (min !== undefined && (min < 0 || min > 100)) {
78
+ throw new Error(`Service '${service.name}': deployment.minHealthyPercent must be between 0 and 100 (got ${min}).`);
79
+ }
80
+ if (max !== undefined && (max < 100 || max > 200)) {
81
+ throw new Error(`Service '${service.name}': deployment.maxHealthyPercent must be between 100 and 200 (got ${max}).`);
82
+ }
83
+ if (min === 100 && max === 100) {
84
+ throw new Error(`Service '${service.name}': deployment.minHealthyPercent and maxHealthyPercent cannot both be 100 ` +
85
+ "(no capacity to drain or expand — deploys would never roll forward).");
86
+ }
87
+ }
88
+ if (service.cloudMapDnsRecordType !== undefined &&
89
+ service.cloudMapService === undefined) {
90
+ throw new Error(`Service '${service.name}': cloudMapDnsRecordType is set but cloudMapService is not. ` +
91
+ "Service discovery cannot be registered without a Cloud Map namespace.");
92
+ }
93
+ if (service.desiredCount === 0 &&
94
+ service.minCapacity !== undefined &&
95
+ service.minCapacity > 0) {
96
+ throw new Error(`Service '${service.name}': scaling.minCapacity (${service.minCapacity}) cannot exceed desiredCount when desiredCount is 0. ` +
97
+ "Application Auto Scaling would immediately scale the service back up, defeating the desiredCount: 0 toggle. " +
98
+ "Either set scaling.minCapacity to 0 (placeholder service) or raise desiredCount to match scaling.minCapacity.");
99
+ }
100
+ if (service.capacityProvider === "EC2" &&
101
+ service.securityGroups !== undefined &&
102
+ service.securityGroups.length > 0) {
103
+ const directAccessForceHost = props.cluster?.directAccess === true;
104
+ const effectiveMode = service.networkMode ??
105
+ (directAccessForceHost ? NetworkMode.HOST : NetworkMode.AWS_VPC);
106
+ if (effectiveMode !== NetworkMode.AWS_VPC) {
107
+ throw new Error(`Service '${service.name}': securityGroups is only valid with networkMode AWS_VPC ` +
108
+ `(effective networkMode is '${effectiveMode}'). HOST/BRIDGE services share ` +
109
+ `the EC2 instance ENI, which is governed by the cluster-level securityGroup ` +
110
+ `on EcsClusterConfig.`);
111
+ }
112
+ }
50
113
  }
51
114
  }
52
115
  /**
@@ -1,3 +1,5 @@
1
+ export * from "./blockDeviceVolume.js";
1
2
  export * from "./ec2.js";
2
3
  export * from "./ecs.js";
3
4
  export * from "./lambda.js";
5
+ export * from "./samApplication.js";
@@ -1,3 +1,5 @@
1
+ export * from "./blockDeviceVolume.js";
1
2
  export * from "./ec2.js";
2
3
  export * from "./ecs.js";
3
4
  export * from "./lambda.js";
5
+ export * from "./samApplication.js";
@@ -2,7 +2,7 @@ import { SingletonFunction as singletonFunction, Function, Code, type Runtime, A
2
2
  import { type Bucket } from "aws-cdk-lib/aws-s3";
3
3
  import { PolicyStatement, type IRole } from "aws-cdk-lib/aws-iam";
4
4
  import { type IVpc } from "aws-cdk-lib/aws-ec2";
5
- import { Rule, type EventPattern } from "aws-cdk-lib/aws-events";
5
+ import { RetentionDays } from "aws-cdk-lib/aws-logs";
6
6
  import { type IQueue } from "aws-cdk-lib/aws-sqs";
7
7
  import { type ITable } from "aws-cdk-lib/aws-dynamodb";
8
8
  import { type Construct } from "constructs";
@@ -23,6 +23,12 @@ export interface LambdaFunctionProps {
23
23
  memorySize?: number;
24
24
  /** Ephemeral storage size in MiB */
25
25
  ephemeralStorageSize?: number;
26
+ /**
27
+ * Log retention for the auto-created LogGroup. Defaults to one week.
28
+ * Override for Lambdas whose logs back operational debugging beyond the
29
+ * default window (e.g. deployment lifecycle hooks).
30
+ */
31
+ logGroupRetention?: RetentionDays;
26
32
  inlinePolicy: PolicyStatement[];
27
33
  enableFunctionUrl?: boolean;
28
34
  functionUrlAuthType?: FunctionUrlAuthType;
@@ -30,8 +36,6 @@ export interface LambdaFunctionProps {
30
36
  /** Invoke mode for Function URL. Use RESPONSE_STREAM for Lambda streaming. */
31
37
  functionUrlInvokeMode?: InvokeMode;
32
38
  environment?: KeyValue;
33
- tags?: KeyValue;
34
- scheduleExpression?: string;
35
39
  secrets?: string[];
36
40
  ssmSecretsPath?: string;
37
41
  secretsImport?: Record<string, SecretImport>;
@@ -100,16 +104,6 @@ export declare class LambdaFunction extends Function {
100
104
  suffix?: string;
101
105
  }>;
102
106
  }): void;
103
- /**
104
- * Add an EventBridge rule as an event source for this Lambda function.
105
- * This will trigger the Lambda when events matching the pattern are published.
106
- * Useful for scheduled jobs, cross-service event handling, and custom event patterns.
107
- */
108
- addEventBridgeEventSource(ruleId: string, options: {
109
- schedule?: string;
110
- eventPattern?: EventPattern;
111
- description?: string;
112
- }): Rule;
113
107
  /**
114
108
  * Add secrets support using AWS Parameters and Secrets Lambda Extension.
115
109
  *