@fjall/components-infrastructure 0.95.0 → 0.99.1

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 (234) hide show
  1. package/dist/lib/app.d.ts +90 -107
  2. package/dist/lib/app.js +149 -139
  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 +7 -8
  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 +172 -0
  32. package/dist/lib/patterns/aws/clickhouseDatabase.js +600 -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 +95 -396
  36. package/dist/lib/patterns/aws/computeEcs.js +880 -46
  37. package/dist/lib/patterns/aws/computeEcsTypes.d.ts +889 -0
  38. package/dist/lib/patterns/aws/computeEcsTypes.js +12 -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 +8 -7
  44. package/dist/lib/patterns/aws/index.d.ts +3 -0
  45. package/dist/lib/patterns/aws/index.js +3 -0
  46. package/dist/lib/patterns/aws/interfaces/compute.d.ts +13 -1
  47. package/dist/lib/patterns/aws/interfaces/connector.d.ts +1 -1
  48. package/dist/lib/patterns/aws/interfaces/connector.js +1 -1
  49. package/dist/lib/patterns/aws/interfaces/database.d.ts +187 -8
  50. package/dist/lib/patterns/aws/interfaces/database.js +17 -3
  51. package/dist/lib/patterns/aws/interfaces/index.d.ts +4 -2
  52. package/dist/lib/patterns/aws/interfaces/index.js +4 -2
  53. package/dist/lib/patterns/aws/interfaces/messaging.d.ts +7 -0
  54. package/dist/lib/patterns/aws/interfaces/migrationContributor.d.ts +47 -0
  55. package/dist/lib/patterns/aws/interfaces/migrationContributor.js +9 -0
  56. package/dist/lib/patterns/aws/interfaces/vpcPeer.d.ts +7 -0
  57. package/dist/lib/patterns/aws/interfaces/vpcPeer.js +1 -0
  58. package/dist/lib/patterns/aws/messaging.d.ts +66 -10
  59. package/dist/lib/patterns/aws/messaging.js +115 -20
  60. package/dist/lib/patterns/aws/network.js +16 -7
  61. package/dist/lib/patterns/aws/organisation.d.ts +4 -0
  62. package/dist/lib/patterns/aws/organisation.js +24 -5
  63. package/dist/lib/patterns/aws/storage.d.ts +1 -2
  64. package/dist/lib/patterns/aws/storage.js +3 -2
  65. package/dist/lib/patterns/aws/vpcPeer.d.ts +34 -0
  66. package/dist/lib/patterns/aws/vpcPeer.js +38 -0
  67. package/dist/lib/patterns/aws/vpcPeerAccepter.d.ts +29 -0
  68. package/dist/lib/patterns/aws/vpcPeerAccepter.js +196 -0
  69. package/dist/lib/resources/aws/analytics/clickhouse.js +25 -7
  70. package/dist/lib/resources/aws/analytics/clickhouseAlarms.d.ts +49 -0
  71. package/dist/lib/resources/aws/analytics/clickhouseAlarms.js +140 -0
  72. package/dist/lib/resources/aws/analytics/clickhouseConstants.d.ts +4 -4
  73. package/dist/lib/resources/aws/analytics/clickhouseConstants.js +6 -4
  74. package/dist/lib/resources/aws/analytics/clickhouseTypes.d.ts +12 -0
  75. package/dist/lib/resources/aws/analytics/clickhouseUserData.d.ts +1 -0
  76. package/dist/lib/resources/aws/analytics/clickhouseUserData.js +56 -5
  77. package/dist/lib/resources/aws/analytics/index.d.ts +2 -0
  78. package/dist/lib/resources/aws/analytics/index.js +1 -0
  79. package/dist/lib/resources/aws/base/awsStack.js +4 -2
  80. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.d.ts +2 -0
  81. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.js +11 -0
  82. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.d.ts +52 -0
  83. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.js +60 -0
  84. package/dist/lib/resources/aws/compute/blockDeviceVolume.d.ts +8 -0
  85. package/dist/lib/resources/aws/compute/blockDeviceVolume.js +10 -0
  86. package/dist/lib/resources/aws/compute/ec2.d.ts +132 -12
  87. package/dist/lib/resources/aws/compute/ec2.js +163 -23
  88. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.d.ts +41 -0
  89. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.js +194 -0
  90. package/dist/lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs +458 -0
  91. package/dist/lib/resources/aws/compute/ecs.d.ts +27 -1
  92. package/dist/lib/resources/aws/compute/ecs.js +42 -2
  93. package/dist/lib/resources/aws/compute/ecsConstants.d.ts +9 -0
  94. package/dist/lib/resources/aws/compute/ecsConstants.js +16 -0
  95. package/dist/lib/resources/aws/compute/ecsImages.js +32 -20
  96. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.d.ts +96 -0
  97. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.js +113 -0
  98. package/dist/lib/resources/aws/compute/ecsNetworking.d.ts +2 -1
  99. package/dist/lib/resources/aws/compute/ecsNetworking.js +18 -6
  100. package/dist/lib/resources/aws/compute/ecsRemoteConnections.d.ts +38 -0
  101. package/dist/lib/resources/aws/compute/ecsRemoteConnections.js +80 -0
  102. package/dist/lib/resources/aws/compute/ecsServiceFactory.d.ts +13 -4
  103. package/dist/lib/resources/aws/compute/ecsServiceFactory.js +155 -33
  104. package/dist/lib/resources/aws/compute/ecsTaskDefinition.d.ts +31 -1
  105. package/dist/lib/resources/aws/compute/ecsTaskDefinition.js +110 -6
  106. package/dist/lib/resources/aws/compute/ecsTypes.d.ts +180 -13
  107. package/dist/lib/resources/aws/compute/ecsValidation.d.ts +9 -0
  108. package/dist/lib/resources/aws/compute/ecsValidation.js +63 -0
  109. package/dist/lib/resources/aws/compute/index.d.ts +2 -0
  110. package/dist/lib/resources/aws/compute/index.js +2 -0
  111. package/dist/lib/resources/aws/compute/lambda.d.ts +7 -13
  112. package/dist/lib/resources/aws/compute/lambda.js +30 -38
  113. package/dist/lib/resources/aws/compute/lifecycleHookLambda.source.cjs +192 -0
  114. package/dist/lib/resources/aws/compute/persistentDataVolume.d.ts +104 -0
  115. package/dist/lib/resources/aws/compute/persistentDataVolume.js +245 -0
  116. package/dist/lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs +398 -0
  117. package/dist/lib/resources/aws/compute/samApplication.d.ts +15 -0
  118. package/dist/lib/resources/aws/compute/samApplication.js +27 -0
  119. package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +159 -0
  120. package/dist/lib/resources/aws/database/clickhouseConstants.js +181 -0
  121. package/dist/lib/resources/aws/database/clickhouseSchemas.d.ts +71 -0
  122. package/dist/lib/resources/aws/database/clickhouseSchemas.js +157 -0
  123. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.d.ts +14 -0
  124. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.js +23 -0
  125. package/dist/lib/resources/aws/database/clickhouseUserData.d.ts +69 -0
  126. package/dist/lib/resources/aws/database/clickhouseUserData.js +371 -0
  127. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.d.ts +56 -0
  128. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.js +112 -0
  129. package/dist/lib/resources/aws/database/rdsAurora.d.ts +8 -1
  130. package/dist/lib/resources/aws/database/rdsAurora.js +42 -32
  131. package/dist/lib/resources/aws/database/rdsAuroraGlobal.d.ts +15 -2
  132. package/dist/lib/resources/aws/database/rdsAuroraGlobal.js +39 -43
  133. package/dist/lib/resources/aws/database/rdsDefaults.d.ts +6 -0
  134. package/dist/lib/resources/aws/database/rdsDefaults.js +7 -1
  135. package/dist/lib/resources/aws/database/rdsHelpers.d.ts +3 -3
  136. package/dist/lib/resources/aws/database/rdsHelpers.js +1 -0
  137. package/dist/lib/resources/aws/database/rdsInstance.d.ts +8 -1
  138. package/dist/lib/resources/aws/database/rdsInstance.js +51 -34
  139. package/dist/lib/resources/aws/database/rdsProxyOutput.d.ts +1 -1
  140. package/dist/lib/resources/aws/database/rdsProxyOutput.js +1 -1
  141. package/dist/lib/resources/aws/iam/delegationRole.js +12 -5
  142. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.d.ts +9 -0
  143. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.js +12 -0
  144. package/dist/lib/resources/aws/iam/identityCenter/index.d.ts +1 -0
  145. package/dist/lib/resources/aws/iam/identityCenter/index.js +1 -0
  146. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.d.ts +1 -0
  147. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.js +1 -0
  148. package/dist/lib/resources/aws/logging/logGroup.d.ts +0 -8
  149. package/dist/lib/resources/aws/logging/logGroup.js +0 -11
  150. package/dist/lib/resources/aws/messaging/defaultEventBus.d.ts +7 -0
  151. package/dist/lib/resources/aws/messaging/defaultEventBus.js +21 -0
  152. package/dist/lib/resources/aws/messaging/eventBridgeRule.d.ts +96 -0
  153. package/dist/lib/resources/aws/messaging/eventBridgeRule.js +110 -0
  154. package/dist/lib/resources/aws/messaging/eventTargets.d.ts +84 -0
  155. package/dist/lib/resources/aws/messaging/eventTargets.js +152 -0
  156. package/dist/lib/resources/aws/messaging/eventbridge.d.ts +25 -2
  157. package/dist/lib/resources/aws/messaging/eventbridge.js +22 -10
  158. package/dist/lib/resources/aws/messaging/index.d.ts +5 -0
  159. package/dist/lib/resources/aws/messaging/index.js +2 -0
  160. package/dist/lib/resources/aws/messaging/schedule.d.ts +118 -0
  161. package/dist/lib/resources/aws/messaging/schedule.js +64 -0
  162. package/dist/lib/resources/aws/messaging/sns.d.ts +2 -1
  163. package/dist/lib/resources/aws/messaging/sqs.d.ts +2 -1
  164. package/dist/lib/resources/aws/messaging/subscription.d.ts +112 -0
  165. package/dist/lib/resources/aws/messaging/subscription.js +67 -0
  166. package/dist/lib/resources/aws/messaging/utils.d.ts +6 -0
  167. package/dist/lib/resources/aws/messaging/utils.js +10 -0
  168. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.d.ts +60 -0
  169. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.js +139 -0
  170. package/dist/lib/resources/aws/monitoring/index.d.ts +2 -0
  171. package/dist/lib/resources/aws/monitoring/index.js +2 -0
  172. package/dist/lib/resources/aws/monitoring/scheduleAlarms.d.ts +47 -0
  173. package/dist/lib/resources/aws/monitoring/scheduleAlarms.js +106 -0
  174. package/dist/lib/resources/aws/networking/crossAccountDelegationRecord.js +6 -3
  175. package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.d.ts +40 -0
  176. package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.js +158 -0
  177. package/dist/lib/resources/aws/networking/dnsRecord/dnsRecordBase.js +7 -4
  178. package/dist/lib/resources/aws/networking/domainCertificate.d.ts +2 -2
  179. package/dist/lib/resources/aws/networking/domainCertificate.js +6 -3
  180. package/dist/lib/resources/aws/networking/hostedZone.js +6 -4
  181. package/dist/lib/resources/aws/networking/index.d.ts +3 -0
  182. package/dist/lib/resources/aws/networking/index.js +3 -0
  183. package/dist/lib/resources/aws/networking/serviceDiscovery.d.ts +96 -0
  184. package/dist/lib/resources/aws/networking/serviceDiscovery.js +96 -0
  185. package/dist/lib/resources/aws/networking/vpc.d.ts +4 -1
  186. package/dist/lib/resources/aws/networking/vpc.js +10 -3
  187. package/dist/lib/resources/aws/networking/vpcPeeringAccepterRole.d.ts +18 -0
  188. package/dist/lib/resources/aws/networking/vpcPeeringAccepterRole.js +61 -0
  189. package/dist/lib/resources/aws/networking/vpcPeeringConnection.d.ts +49 -0
  190. package/dist/lib/resources/aws/networking/vpcPeeringConnection.js +106 -0
  191. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.d.ts +16 -5
  192. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.js +17 -3
  193. package/dist/lib/resources/aws/organisation/index.d.ts +1 -1
  194. package/dist/lib/resources/aws/organisation/organisationPolicy.d.ts +2 -0
  195. package/dist/lib/resources/aws/organisation/organisationPolicy.js +3 -2
  196. package/dist/lib/resources/aws/secrets/secret.d.ts +7 -0
  197. package/dist/lib/resources/aws/secrets/secret.js +4 -3
  198. package/dist/lib/resources/aws/storage/bucketDeployment.d.ts +16 -0
  199. package/dist/lib/resources/aws/storage/bucketDeployment.js +17 -0
  200. package/dist/lib/resources/aws/storage/ecr.js +5 -5
  201. package/dist/lib/resources/aws/storage/index.d.ts +1 -0
  202. package/dist/lib/resources/aws/storage/index.js +1 -0
  203. package/dist/lib/resources/aws/storage/s3.js +10 -3
  204. package/dist/lib/resources/aws/utilities/customResource.js +18 -9
  205. package/dist/lib/synth_dump.d.ts +1 -0
  206. package/dist/lib/synth_dump.js +42 -0
  207. package/dist/lib/utils/bastionFactory.d.ts +10 -0
  208. package/dist/lib/utils/bastionFactory.js +29 -0
  209. package/dist/lib/utils/capitaliseString.d.ts +1 -1
  210. package/dist/lib/utils/capitaliseString.js +1 -1
  211. package/dist/lib/utils/cdkContext.d.ts +10 -0
  212. package/dist/lib/utils/cdkContext.js +13 -0
  213. package/dist/lib/utils/connections.d.ts +7 -1
  214. package/dist/lib/utils/connections.js +21 -0
  215. package/dist/lib/utils/connector.d.ts +30 -2
  216. package/dist/lib/utils/connector.js +6 -1
  217. package/dist/lib/utils/costAllocationTags.d.ts +15 -0
  218. package/dist/lib/utils/costAllocationTags.js +16 -0
  219. package/dist/lib/utils/databaseTypes.d.ts +14 -0
  220. package/dist/lib/utils/getConfig.d.ts +2 -0
  221. package/dist/lib/utils/getConfig.js +2 -0
  222. package/dist/lib/utils/index.d.ts +4 -0
  223. package/dist/lib/utils/index.js +4 -0
  224. package/dist/lib/utils/manifestWriter.d.ts +6 -89
  225. package/dist/lib/utils/manifestWriter.js +36 -23
  226. package/dist/lib/utils/migrationVersionResolvers.d.ts +2 -0
  227. package/dist/lib/utils/migrationVersionResolvers.js +2 -0
  228. package/dist/lib/utils/orgConfigParser.js +2 -1
  229. package/dist/lib/utils/resolveAlertsTopic.d.ts +14 -0
  230. package/dist/lib/utils/resolveAlertsTopic.js +30 -0
  231. package/dist/lib/utils/validationLogger.js +6 -3
  232. package/dist/lib/utils/vpcPeerInterface.d.ts +22 -0
  233. package/dist/lib/utils/vpcPeerInterface.js +1 -0
  234. package/package.json +22 -18
@@ -1,14 +1,29 @@
1
- import { FargateService, Ec2Service, PropagatedTagSource, PlacementStrategy, AsgCapacityProvider, EcsOptimizedImage, AmiHardwareType } from "aws-cdk-lib/aws-ecs";
2
- import { InstanceType, Peer, Port, SubnetType } from "aws-cdk-lib/aws-ec2";
3
- import { CfnOutput, Duration } from "aws-cdk-lib";
1
+ import { FargateService, Ec2Service, PropagatedTagSource, PlacementStrategy, AsgCapacityProvider, EcsOptimizedImage, AmiHardwareType, NetworkMode } from "aws-cdk-lib/aws-ecs";
2
+ import { Peer, Port, SubnetType, UserData } from "aws-cdk-lib/aws-ec2";
3
+ import { ServicePrincipal } from "aws-cdk-lib/aws-iam";
4
+ import { Role } from "../iam/role.js";
5
+ import { CfnOutput, Duration, Token } from "aws-cdk-lib";
4
6
  import { PredefinedMetric, ScalableTarget, ServiceNamespace, TargetTrackingScalingPolicy } from "aws-cdk-lib/aws-applicationautoscaling";
5
- import { AutoScalingGroup, Monitoring } from "aws-cdk-lib/aws-autoscaling";
7
+ import { Monitoring } from "aws-cdk-lib/aws-autoscaling";
6
8
  import { SecurityGroup } from "../networking/securityGroup.js";
9
+ import { Ec2Instance } from "./ec2.js";
7
10
  import { vpcHasNatGateways } from "../../../utils/vpcUtils.js";
8
11
  import { toPascalCase } from "../../../utils/capitaliseString.js";
9
- import { DEFAULT_EC2_INSTANCE_TYPE, DEFAULT_WARM_POOL_MIN_SIZE, DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN, inferAmiHardwareType } from "./ecsConstants.js";
12
+ import { DEFAULT_EC2_INSTANCE_TYPE, DEFAULT_WARM_POOL_MIN_SIZE, DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN, DEFAULT_HEALTH_CHECK_GRACE_SECONDS, DEFAULT_MIN_HEALTHY_PERCENT, DEFAULT_MAX_HEALTHY_PERCENT, DEFAULT_DESIRED_COUNT, inferAmiHardwareType } from "./ecsConstants.js";
10
13
  import { ScalingType } from "./ecsTypes.js";
11
14
  import { isServiceFargate, isServiceEc2 } from "./ecsTaskDefinition.js";
15
+ /**
16
+ * Resolves the user's `circuitBreaker` config to the CDK
17
+ * `DeploymentCircuitBreaker` shape. `undefined` resolves to the safe default
18
+ * `{ enable: true, rollback: true }`; `false` resolves to `undefined` (CDK
19
+ * omits the breaker block from CFN output entirely).
20
+ */
21
+ export function resolveCircuitBreaker(config) {
22
+ if (config === false)
23
+ return undefined;
24
+ const rollback = config?.rollback ?? true;
25
+ return { enable: true, rollback };
26
+ }
12
27
  /**
13
28
  * Generates a unique key for EC2 config so services with matching
14
29
  * configurations share an ASG.
@@ -22,7 +37,26 @@ export function getEc2ConfigKey(ec2Config) {
22
37
  const warmPoolKey = ec2Config.warmPool
23
38
  ? `wp${ec2Config.warmPool.minSize ?? DEFAULT_WARM_POOL_MIN_SIZE}-${ec2Config.warmPool.reuseOnScaleIn ?? DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN}`
24
39
  : "nowp";
25
- return `${instanceType}-${amiHardwareType}-${warmPoolKey}`;
40
+ const baseKey = `${instanceType}-${amiHardwareType}-${warmPoolKey}`;
41
+ // PDV services are implicit singletons — sharing an ASG would let two tasks race for the same EBS volume.
42
+ const pdv = ec2Config.persistentDataVolume;
43
+ const extras = [];
44
+ if (pdv !== undefined) {
45
+ extras.push(`pdv-${pdv.deviceName}-${pdv.sizeGb}-${stableAzSegment(pdv.availabilityZone)}`);
46
+ }
47
+ // Serialise actual AZ names — `az${length}` would let services pinned to different AZs collide on `az1`.
48
+ // CDK Tokens (env-agnostic stacks) substitute a stable sentinel to keep logical IDs deterministic.
49
+ if (ec2Config.availabilityZones !== undefined) {
50
+ const azKey = [...ec2Config.availabilityZones]
51
+ .map(stableAzSegment)
52
+ .sort()
53
+ .join(",");
54
+ extras.push(`az-${azKey}`);
55
+ }
56
+ return extras.length === 0 ? baseKey : `${baseKey}-${extras.join("-")}`;
57
+ }
58
+ function stableAzSegment(az) {
59
+ return Token.isUnresolved(az) ? "synthAz" : az;
26
60
  }
27
61
  /**
28
62
  * Gets or creates an ASG capacity provider for an EC2-backed service.
@@ -46,10 +80,11 @@ export function getOrCreateAsgCapacityProvider(ctx, serviceProps, state) {
46
80
  : inferAmiHardwareType(instanceType);
47
81
  const minCapacity = ec2Config.minCapacity ?? 2;
48
82
  const maxCapacity = ec2Config.maxCapacity ?? 3;
49
- const asgSecurityGroup = new SecurityGroup(ctx.scope, `${safeKey}AsgSecurityGroup`, {
50
- vpc: ctx.cluster.vpc,
51
- description: `Security group for ${key} auto scaling group`
52
- });
83
+ const asgSecurityGroup = ctx.props.cluster?.securityGroup ??
84
+ new SecurityGroup(ctx.scope, `${safeKey}AsgSecurityGroup`, {
85
+ vpc: ctx.cluster.vpc,
86
+ description: `Security group for ${key} auto scaling group`
87
+ });
53
88
  if (ctx.directAccessEnabled) {
54
89
  for (const service of ctx.props.services) {
55
90
  if (isServiceEc2(service)) {
@@ -62,29 +97,61 @@ export function getOrCreateAsgCapacityProvider(ctx, serviceProps, state) {
62
97
  }
63
98
  }
64
99
  const hasNat = vpcHasNatGateways(ctx.cluster.vpc);
65
- const asg = new AutoScalingGroup(ctx.scope, `${safeKey}AutoScalingGroup`, {
66
- autoScalingGroupName: `${ctx.props.clusterName}-${safeKey}-Asg`,
100
+ const resolvedWarmPool = ec2Config.warmPool
101
+ ? {
102
+ minSize: ec2Config.warmPool.minSize ?? DEFAULT_WARM_POOL_MIN_SIZE,
103
+ reuseOnScaleIn: ec2Config.warmPool.reuseOnScaleIn ??
104
+ DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN
105
+ }
106
+ : undefined;
107
+ // userData + role must be set on the LaunchTemplate or AsgCapacityProvider
108
+ // throws at synth (ProvidedLaunchTemplateExposeUser/ExposeDefine).
109
+ const instanceRole = new Role(ctx.scope, `${safeKey}InstanceRole`, {
110
+ assumedBy: new ServicePrincipal("ec2.amazonaws.com"),
111
+ description: `EC2 instance role for ${key} ECS capacity`
112
+ });
113
+ const ec2Instance = new Ec2Instance(ctx.scope, `${safeKey}Ec2Instance`, {
114
+ serviceName: `${ctx.props.clusterName}${safeKey}`,
67
115
  vpc: ctx.cluster.vpc,
68
116
  vpcSubnets: {
69
- subnetType: hasNat ? SubnetType.PRIVATE_WITH_EGRESS : SubnetType.PUBLIC
117
+ subnetType: hasNat ? SubnetType.PRIVATE_WITH_EGRESS : SubnetType.PUBLIC,
118
+ ...(ec2Config.availabilityZones !== undefined && {
119
+ availabilityZones: ec2Config.availabilityZones
120
+ })
70
121
  },
71
122
  securityGroup: asgSecurityGroup,
72
123
  minCapacity,
73
124
  maxCapacity,
74
- instanceType: new InstanceType(instanceType),
125
+ instanceType,
126
+ machineImage: ec2Config.machineImage ??
127
+ EcsOptimizedImage.amazonLinux2023(amiHardwareType),
128
+ userData: ec2Config.userData ?? UserData.forLinux(),
129
+ role: instanceRole,
130
+ instanceMonitoring: ec2Config.instanceMonitoring ?? Monitoring.BASIC,
75
131
  capacityRebalance: true,
76
- instanceMonitoring: Monitoring.BASIC,
77
- machineImage: EcsOptimizedImage.amazonLinux2023(amiHardwareType)
132
+ ecsClusterArn: ctx.cluster.clusterArn,
133
+ ...(ec2Config.desiredCapacity !== undefined && {
134
+ desiredCapacity: ec2Config.desiredCapacity
135
+ }),
136
+ ...(ec2Config.blockDevices !== undefined && {
137
+ blockDevices: ec2Config.blockDevices
138
+ }),
139
+ ...(ec2Config.associatePublicIpAddress !== undefined && {
140
+ associatePublicIpAddress: ec2Config.associatePublicIpAddress
141
+ }),
142
+ ...(resolvedWarmPool !== undefined && { warmPool: resolvedWarmPool }),
143
+ ...(ec2Config.persistentDataVolume !== undefined && {
144
+ persistentDataVolume: ec2Config.persistentDataVolume
145
+ }),
146
+ ...(ec2Config.tags !== undefined && { tags: ec2Config.tags })
78
147
  });
79
- if (ec2Config.warmPool) {
80
- asg.addWarmPool({
81
- minSize: ec2Config.warmPool.minSize ?? DEFAULT_WARM_POOL_MIN_SIZE,
82
- reuseOnScaleIn: ec2Config.warmPool.reuseOnScaleIn ?? DEFAULT_WARM_POOL_REUSE_ON_SCALE_IN
83
- });
84
- }
148
+ const asg = ec2Instance.getAutoScalingGroup();
85
149
  const provider = new AsgCapacityProvider(ctx.scope, `${safeKey}AsgCapacityProvider`, {
86
150
  autoScalingGroup: asg,
87
151
  enableManagedDraining: true,
152
+ // MTP's ProtectedFromScaleIn flag does NOT clear on CP deletion, so
153
+ // CFN rollback wedges on a stranded protected instance. The drain
154
+ // path is Ec2GracefulTerminationHandler instead.
88
155
  enableManagedTerminationProtection: false
89
156
  });
90
157
  ctx.cluster.addAsgCapacityProvider(provider);
@@ -101,8 +168,13 @@ export function getOrCreateAsgCapacityProvider(ctx, serviceProps, state) {
101
168
  * Creates a Fargate or EC2 service and emits a CfnOutput for its ARN.
102
169
  */
103
170
  export function createService(ctx, serviceName, serviceProps, taskDefinition, asgState) {
104
- const desiredCount = serviceProps.desiredCount ?? 2;
171
+ const desiredCount = serviceProps.desiredCount ?? DEFAULT_DESIRED_COUNT;
105
172
  let service;
173
+ // CDK's Ec2Service rejects `securityGroups` unless networkMode is AWS_VPC
174
+ // (HOST/BRIDGE share the instance ENI). FargateService is always awsvpc.
175
+ const explicitSecurityGroups = serviceProps.securityGroups && serviceProps.securityGroups.length > 0
176
+ ? serviceProps.securityGroups
177
+ : undefined;
106
178
  if (isServiceFargate(serviceProps)) {
107
179
  const hasNat = vpcHasNatGateways(ctx.cluster.vpc);
108
180
  service = new FargateService(ctx.scope, `${serviceName}Service`, {
@@ -121,12 +193,17 @@ export function createService(ctx, serviceName, serviceProps, taskDefinition, as
121
193
  }
122
194
  ],
123
195
  propagateTags: PropagatedTagSource.SERVICE,
124
- circuitBreaker: { enable: true, rollback: true },
196
+ circuitBreaker: resolveCircuitBreaker(serviceProps.circuitBreaker),
125
197
  enableECSManagedTags: true,
126
198
  enableExecuteCommand: true,
127
- healthCheckGracePeriod: Duration.seconds(120),
128
- minHealthyPercent: 100,
129
- maxHealthyPercent: 200
199
+ healthCheckGracePeriod: Duration.seconds(DEFAULT_HEALTH_CHECK_GRACE_SECONDS),
200
+ minHealthyPercent: serviceProps.deployment?.minHealthyPercent ??
201
+ DEFAULT_MIN_HEALTHY_PERCENT,
202
+ maxHealthyPercent: serviceProps.deployment?.maxHealthyPercent ??
203
+ DEFAULT_MAX_HEALTHY_PERCENT,
204
+ ...(explicitSecurityGroups !== undefined && {
205
+ securityGroups: explicitSecurityGroups
206
+ })
130
207
  });
131
208
  }
132
209
  else {
@@ -143,15 +220,59 @@ export function createService(ctx, serviceName, serviceProps, taskDefinition, as
143
220
  }
144
221
  ],
145
222
  propagateTags: PropagatedTagSource.SERVICE,
146
- circuitBreaker: { enable: true, rollback: true },
223
+ circuitBreaker: resolveCircuitBreaker(serviceProps.circuitBreaker),
147
224
  placementStrategies: [PlacementStrategy.spreadAcrossInstances()],
148
225
  enableECSManagedTags: true,
149
226
  enableExecuteCommand: true,
150
- healthCheckGracePeriod: Duration.seconds(120),
151
- minHealthyPercent: 100,
152
- maxHealthyPercent: 200
227
+ healthCheckGracePeriod: Duration.seconds(DEFAULT_HEALTH_CHECK_GRACE_SECONDS),
228
+ minHealthyPercent: serviceProps.deployment?.minHealthyPercent ??
229
+ DEFAULT_MIN_HEALTHY_PERCENT,
230
+ maxHealthyPercent: serviceProps.deployment?.maxHealthyPercent ??
231
+ DEFAULT_MAX_HEALTHY_PERCENT,
232
+ ...(explicitSecurityGroups !== undefined && {
233
+ securityGroups: explicitSecurityGroups
234
+ })
153
235
  });
154
236
  }
237
+ if (serviceProps.cloudMapService !== undefined) {
238
+ const isSrv = serviceProps.cloudMapDnsRecordType === "SRV";
239
+ const isHostOrBridge = taskDefinition.networkMode === NetworkMode.HOST ||
240
+ taskDefinition.networkMode === NetworkMode.BRIDGE;
241
+ // AWS forbids A-typed serviceRegistries under HOST/BRIDGE networkMode.
242
+ if (isHostOrBridge && !isSrv) {
243
+ throw new Error(`Service '${serviceName}': cloudMapDnsRecordType '${serviceProps.cloudMapDnsRecordType ?? "A"}' ` +
244
+ `is not supported with networkMode '${taskDefinition.networkMode}'. ` +
245
+ 'Use dnsRecordType: "SRV" or change networkMode to AWS_VPC.');
246
+ }
247
+ const primaryWithPort = serviceProps.containers.find((c) => c.port !== undefined);
248
+ const primaryWithMappings = serviceProps.containers.find((c) => c.portMappings !== undefined &&
249
+ c.portMappings[0]?.containerPort !== undefined);
250
+ const primary = primaryWithPort ?? primaryWithMappings;
251
+ const primaryPort = primaryWithPort?.port ??
252
+ primaryWithMappings?.portMappings?.[0]?.containerPort;
253
+ if (isSrv && (primary === undefined || primaryPort === undefined)) {
254
+ throw new Error(`Service '${serviceName}': cloudMapDnsRecordType: "SRV" requires at ` +
255
+ "least one container with a port — SRV records carry the published " +
256
+ "port and cannot be registered without one.");
257
+ }
258
+ if (primary !== undefined && primaryPort !== undefined) {
259
+ const containerDef = taskDefinition.findContainer(primary.name);
260
+ if (containerDef === undefined) {
261
+ throw new Error(`Service '${serviceName}': could not resolve container '${primary.name}' ` +
262
+ "from task definition for Cloud Map registration.");
263
+ }
264
+ service.associateCloudMapService({
265
+ service: serviceProps.cloudMapService,
266
+ container: containerDef,
267
+ containerPort: primaryPort
268
+ });
269
+ }
270
+ else {
271
+ service.associateCloudMapService({
272
+ service: serviceProps.cloudMapService
273
+ });
274
+ }
275
+ }
155
276
  new CfnOutput(ctx.scope, `${ctx.outputName}${toPascalCase(serviceName)}ServiceArn`, {
156
277
  key: `${ctx.outputName}${toPascalCase(serviceName)}ServiceArn`,
157
278
  exportName: `${ctx.props.clusterName}${serviceName}ServiceArn`,
@@ -164,12 +285,13 @@ export function createService(ctx, serviceName, serviceProps, taskDefinition, as
164
285
  * Adds auto-scaling to an ECS service based on CPU or memory utilisation.
165
286
  */
166
287
  export function addServiceScaling(ctx, serviceName, serviceProps, service) {
288
+ const desiredCount = serviceProps.desiredCount ?? DEFAULT_DESIRED_COUNT;
167
289
  const scalableTarget = new ScalableTarget(ctx.scope, `${serviceName}ScalableTarget`, {
168
290
  serviceNamespace: ServiceNamespace.ECS,
169
291
  resourceId: `service/${ctx.cluster.clusterName}/${service.serviceName}`,
170
292
  scalableDimension: "ecs:service:DesiredCount",
171
- minCapacity: serviceProps.minCapacity ?? 2,
172
- maxCapacity: serviceProps.maxCapacity ?? 10
293
+ minCapacity: serviceProps.minCapacity ?? desiredCount,
294
+ maxCapacity: serviceProps.maxCapacity ?? Math.max(desiredCount + 1, 3)
173
295
  });
174
296
  return new TargetTrackingScalingPolicy(ctx.scope, `${serviceName}ScalingPolicy`, {
175
297
  scalingTarget: scalableTarget,
@@ -1,4 +1,5 @@
1
- import { FargateTaskDefinition, Ec2TaskDefinition, type ContainerDefinition } from "aws-cdk-lib/aws-ecs";
1
+ import { FargateTaskDefinition, Ec2TaskDefinition, NetworkMode, type ContainerDefinition } from "aws-cdk-lib/aws-ecs";
2
+ import type { Construct } from "constructs";
2
3
  import { type Role } from "aws-cdk-lib/aws-iam";
3
4
  import type { EcsConstructContext } from "./ecsContext.js";
4
5
  import type { EcsClusterProps, EcsServiceProps, EcsCapacityProvider } from "./ecsTypes.js";
@@ -24,6 +25,35 @@ export declare function collectSecretsManagerSecretNames(props: EcsClusterProps,
24
25
  */
25
26
  export declare function deriveSsmSecretsPath(props: EcsClusterProps, serviceName: string, explicitPath?: string): string;
26
27
  export declare function createTaskDefinition(ctx: EcsConstructContext, serviceName: string, serviceProps: EcsServiceProps, executionRole: Role, taskRole: Role): FargateTaskDefinition | Ec2TaskDefinition;
28
+ /**
29
+ * Routing point for `Ec2TaskDefinition`s used by EventBridge-triggered scheduled
30
+ * tasks (per `EcsClusterConfig.scheduledTasks`). Pattern-layer callers MUST
31
+ * reach `Ec2TaskDefinition` through this wrapper rather than instantiating the
32
+ * raw CDK class — a future SOC2-relevant default (encryption, retention,
33
+ * uniform tags) added here propagates to every scheduled-task task def at once.
34
+ *
35
+ * Compare with `createTaskDefinition()` above, which is the per-service path;
36
+ * this is the per-schedule path and intentionally kept narrow (no execution /
37
+ * task roles — scheduled tasks use task-definition defaults).
38
+ */
39
+ export declare function createScheduledTaskDefinition(scope: Construct, id: string, props: {
40
+ family: string;
41
+ networkMode?: NetworkMode;
42
+ }): Ec2TaskDefinition;
43
+ /**
44
+ * Routing point for `FargateTaskDefinition`s used by the lifecycle-hook
45
+ * migration runner when `migrations.separateTaskDef` is set. Synthesises a
46
+ * dedicated Fargate task definition with caller-supplied CPU/memory and the
47
+ * supplied execution + task roles. Runtime platform pinned to ARM64/Linux to
48
+ * match the service task def default.
49
+ */
50
+ export declare function createMigrationTaskDefinition(scope: Construct, id: string, props: {
51
+ family: string;
52
+ cpu: number;
53
+ memoryLimitMiB: number;
54
+ executionRole: Role;
55
+ taskRole: Role;
56
+ }): FargateTaskDefinition;
27
57
  export declare function addContainersToTask(ctx: EcsConstructContext, serviceName: string, serviceProps: EcsServiceProps, taskDefinition: FargateTaskDefinition | Ec2TaskDefinition): {
28
58
  containers: ContainerDefinition[];
29
59
  primaryContainer?: ContainerDefinition;
@@ -1,11 +1,13 @@
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
+ import { resolveOrgId } from "../../../utils/cdkContext.js";
6
7
  import { validateSsmPathComponent } from "./ecsValidation.js";
7
- 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";
8
9
  import { getContainerImage } from "./ecsImages.js";
10
+ import { resolveRemoteConnections } from "./ecsRemoteConnections.js";
9
11
  // Re-export extracted functions so existing consumers are not broken
10
12
  export { createExecutionRole, createTaskRole } from "./ecsRoles.js";
11
13
  export { getContainerImage } from "./ecsImages.js";
@@ -64,8 +66,8 @@ export function deriveSsmSecretsPath(props, serviceName, explicitPath) {
64
66
  return `/${appName}/${props.clusterName}/${serviceName}`;
65
67
  }
66
68
  export function createTaskDefinition(ctx, serviceName, serviceProps, executionRole, taskRole) {
67
- const cpu = serviceProps.cpu ?? 256;
68
- const memoryLimitMiB = serviceProps.memoryLimitMiB ?? 512;
69
+ const cpu = serviceProps.cpu ?? DEFAULT_FARGATE_CPU;
70
+ const memoryLimitMiB = serviceProps.memoryLimitMiB ?? DEFAULT_FARGATE_MEMORY_MIB;
69
71
  if (isServiceFargate(serviceProps)) {
70
72
  return new FargateTaskDefinition(ctx.scope, `${serviceName}TaskDefinition`, {
71
73
  family: `${ctx.props.clusterName}-${serviceName}`,
@@ -80,17 +82,66 @@ export function createTaskDefinition(ctx, serviceName, serviceProps, executionRo
80
82
  });
81
83
  }
82
84
  else {
85
+ const networkMode = serviceProps.networkMode ??
86
+ (ctx.directAccessEnabled ? NetworkMode.HOST : NetworkMode.AWS_VPC);
83
87
  return new Ec2TaskDefinition(ctx.scope, `${serviceName}TaskDefinition`, {
84
88
  family: `${ctx.props.clusterName}-${serviceName}`,
85
89
  executionRole,
86
90
  taskRole,
87
- ...(ctx.directAccessEnabled && { networkMode: NetworkMode.HOST })
91
+ ...(networkMode !== undefined && { networkMode })
88
92
  });
89
93
  }
90
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
+ };
91
138
  export function addContainersToTask(ctx, serviceName, serviceProps, taskDefinition) {
92
139
  const containers = [];
140
+ const containerByName = new Map();
93
141
  let primaryContainer;
142
+ const orgId = resolveOrgId(ctx.scope.node);
143
+ const remoteEnvByService = resolveRemoteConnections([serviceProps], ctx.scope, orgId);
144
+ const remoteEnv = remoteEnvByService[serviceName] ?? {};
94
145
  for (const containerConfig of serviceProps.containers) {
95
146
  const image = getContainerImage(ctx, serviceName, containerConfig, serviceProps);
96
147
  const isFirstWithPort = !primaryContainer && containerConfig.port !== undefined;
@@ -125,8 +176,11 @@ export function addContainersToTask(ctx, serviceName, serviceProps, taskDefiniti
125
176
  streamPrefix: `/ecs/${ctx.props.clusterName}/${serviceName}`,
126
177
  logRetention: DEFAULT_LOG_RETENTION_DAYS
127
178
  }),
179
+ // remoteEnv (cross-app `${PREFIX}_HOST/_PORT`) intentionally overrides user
180
+ // values — a stale manual setting must not mask the resolved peer.
128
181
  environment: {
129
182
  ...containerConfig.environment,
183
+ ...remoteEnv,
130
184
  ...(containerConfig.port
131
185
  ? { PORT: String(containerConfig.port) }
132
186
  : {})
@@ -150,19 +204,69 @@ export function addContainersToTask(ctx, serviceName, serviceProps, taskDefiniti
150
204
  : undefined
151
205
  }
152
206
  : undefined,
207
+ stopTimeout: containerConfig.stopTimeout !== undefined
208
+ ? Duration.seconds(containerConfig.stopTimeout)
209
+ : undefined,
153
210
  ...(isServiceEc2(serviceProps) && {
154
- memoryLimitMiB: serviceProps.ec2Config?.memoryLimitMiB ?? 1024
211
+ memoryLimitMiB: serviceProps.ec2Config?.memoryLimitMiB ??
212
+ DEFAULT_EC2_CONTAINER_MEMORY_MIB
155
213
  })
156
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
+ }
157
221
  if (containerConfig.port) {
158
222
  container.addPortMappings({
159
223
  containerPort: containerConfig.port
160
224
  });
161
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
+ }
162
245
  if (isFirstWithPort) {
163
246
  primaryContainer = container;
164
247
  }
165
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
+ }
166
270
  }
167
271
  return { containers, primaryContainer };
168
272
  }