@fjall/components-infrastructure 2.34.0 → 3.1.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/lib/app.d.ts +36 -2
- package/dist/lib/app.js +78 -3
- package/dist/lib/patterns/aws/apexDomainPattern.d.ts +11 -12
- package/dist/lib/patterns/aws/apexDomainPattern.js +11 -31
- package/dist/lib/patterns/aws/buildkite/alarms.d.ts +20 -0
- package/dist/lib/patterns/aws/buildkite/alarms.js +78 -0
- package/dist/lib/patterns/aws/buildkite/buildkite.d.ts +46 -0
- package/dist/lib/patterns/aws/buildkite/buildkite.js +233 -0
- package/dist/lib/patterns/aws/buildkite/iam.d.ts +35 -0
- package/dist/lib/patterns/aws/buildkite/iam.js +176 -0
- package/dist/lib/patterns/aws/buildkite/pins.d.ts +69 -0
- package/dist/lib/patterns/aws/buildkite/pins.js +80 -0
- package/dist/lib/patterns/aws/buildkite/schema.d.ts +80 -0
- package/dist/lib/patterns/aws/buildkite/schema.js +148 -0
- package/dist/lib/patterns/aws/buildkite/userData.d.ts +58 -0
- package/dist/lib/patterns/aws/buildkite/userData.js +226 -0
- package/dist/lib/patterns/aws/buildkite.d.ts +12 -59
- package/dist/lib/patterns/aws/buildkite.js +12 -310
- package/dist/lib/patterns/aws/cdn.js +25 -10
- package/dist/lib/patterns/aws/computeEcs.js +6 -1
- package/dist/lib/patterns/aws/computeEcsTypes.d.ts +8 -3
- package/dist/lib/patterns/aws/delegatedDomainPattern.d.ts +13 -4
- package/dist/lib/patterns/aws/delegatedDomainPattern.js +57 -8
- package/dist/lib/patterns/aws/devSubstrate.d.ts +2 -1
- package/dist/lib/patterns/aws/devSubstrate.js +18 -8
- package/dist/lib/patterns/aws/dnsRecordComposer.d.ts +2 -9
- package/dist/lib/patterns/aws/dnsRecordComposer.js +5 -97
- package/dist/lib/patterns/aws/domain.d.ts +3 -2
- package/dist/lib/patterns/aws/domain.js +3 -2
- package/dist/lib/patterns/aws/domainValidation.js +71 -46
- package/dist/lib/patterns/aws/externalRecordsPattern.js +2 -11
- package/dist/lib/patterns/aws/index.d.ts +1 -3
- package/dist/lib/patterns/aws/index.js +2 -3
- package/dist/lib/patterns/aws/interfaces/domain.d.ts +22 -8
- package/dist/lib/patterns/aws/interfaces/index.d.ts +0 -1
- package/dist/lib/patterns/aws/interfaces/index.js +3 -0
- package/dist/lib/patterns/aws/interfaces/pattern.d.ts +55 -10
- package/dist/lib/patterns/aws/patternDomain.d.ts +77 -0
- package/dist/lib/patterns/aws/patternDomain.js +198 -0
- package/dist/lib/patterns/aws/payload.js +23 -30
- package/dist/lib/patterns/aws/staticSite.d.ts +1 -0
- package/dist/lib/patterns/aws/staticSite.js +28 -33
- package/dist/lib/resources/aws/base/awsStack.d.ts +8 -0
- package/dist/lib/resources/aws/base/awsStack.js +20 -17
- package/dist/lib/resources/aws/compute/ec2.d.ts +15 -0
- package/dist/lib/resources/aws/compute/ec2.js +20 -1
- package/dist/lib/resources/aws/compute/ecs.d.ts +1 -1
- package/dist/lib/resources/aws/compute/ecs.js +1 -1
- package/dist/lib/resources/aws/compute/ecsNetworking.d.ts +1 -1
- package/dist/lib/resources/aws/compute/ecsNetworking.js +212 -47
- package/dist/lib/resources/aws/compute/ecsTypes.d.ts +60 -9
- package/dist/lib/resources/aws/compute/ecsValidation.d.ts +14 -1
- package/dist/lib/resources/aws/compute/ecsValidation.js +51 -0
- package/dist/lib/resources/aws/networking/dnsRecord/aRecord.js +3 -2
- package/dist/lib/resources/aws/networking/dnsRecord/aaaaRecord.js +3 -2
- package/dist/lib/resources/aws/networking/dnsRecord/aliasRecord.d.ts +23 -1
- package/dist/lib/resources/aws/networking/dnsRecord/aliasRecord.js +14 -3
- package/dist/lib/resources/aws/networking/dnsRecord/caaRecord.js +3 -2
- package/dist/lib/resources/aws/networking/dnsRecord/cnameRecord.js +3 -2
- package/dist/lib/resources/aws/networking/dnsRecord/dnsRecordBase.d.ts +10 -1
- package/dist/lib/resources/aws/networking/dnsRecord/dnsRecordBase.js +21 -4
- package/dist/lib/resources/aws/networking/dnsRecord/mxRecord.js +3 -2
- package/dist/lib/resources/aws/networking/dnsRecord/nsRecord.js +3 -2
- package/dist/lib/resources/aws/networking/dnsRecord/srvRecord.js +3 -2
- package/dist/lib/resources/aws/networking/dnsRecord/txtRecord.js +3 -2
- package/dist/lib/resources/aws/networking/domainCertificate.d.ts +28 -4
- package/dist/lib/resources/aws/networking/domainCertificate.js +24 -10
- package/dist/lib/resources/aws/networking/hostedZone.d.ts +11 -0
- package/dist/lib/resources/aws/networking/hostedZone.js +6 -0
- package/dist/lib/utils/costAllocationTags.d.ts +9 -0
- package/dist/lib/utils/costAllocationTags.js +6 -1
- package/dist/lib/utils/dnsRecordRegistry.d.ts +52 -0
- package/dist/lib/utils/dnsRecordRegistry.js +64 -0
- package/dist/lib/utils/domainTypes.d.ts +43 -36
- package/dist/lib/utils/domainTypes.js +65 -2
- package/dist/lib/utils/managedDomainContext.d.ts +37 -0
- package/dist/lib/utils/managedDomainContext.js +111 -0
- package/package.json +4 -4
|
@@ -1,310 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import { applyCostAllocationTags } from "../../utils/costAllocationTags.js";
|
|
14
|
-
export var AgentRelease;
|
|
15
|
-
(function (AgentRelease) {
|
|
16
|
-
AgentRelease["STABLE"] = "stable";
|
|
17
|
-
AgentRelease["BETA"] = "beta";
|
|
18
|
-
AgentRelease["EDGE"] = "edge";
|
|
19
|
-
})(AgentRelease || (AgentRelease = {}));
|
|
20
|
-
export const BuildkiteDefaultProps = {
|
|
21
|
-
// Buildkite Agent Configuration
|
|
22
|
-
elasticStackVersion: "v6.30.0",
|
|
23
|
-
keyName: "",
|
|
24
|
-
buildkiteAgentRelease: AgentRelease.STABLE,
|
|
25
|
-
buildkiteAgentTags: "",
|
|
26
|
-
buildkiteAgentTimestampLines: false,
|
|
27
|
-
buildkiteAgentExperiments: "",
|
|
28
|
-
buildkiteAgentScalerVersion: "1.8.0",
|
|
29
|
-
logRetentionDays: 1,
|
|
30
|
-
buildkiteAgentTracingBackend: "",
|
|
31
|
-
buildkiteAgentCancelGracePeriod: 60,
|
|
32
|
-
buildkiteTerminateInstanceAfterJob: false,
|
|
33
|
-
buildkiteAdditionalSudoPermissions: "",
|
|
34
|
-
buildkiteQueue: "default",
|
|
35
|
-
agentsPerInstance: 1,
|
|
36
|
-
bootstrapScriptUrl: "",
|
|
37
|
-
agentEnvFileUrl: "",
|
|
38
|
-
authorizedUsersUrl: "",
|
|
39
|
-
// Infrastructure
|
|
40
|
-
agentInstanceType: InstanceType.of(InstanceClass.M5, InstanceSize.LARGE),
|
|
41
|
-
agentMinInstances: 0,
|
|
42
|
-
agentMaxInstances: 10,
|
|
43
|
-
agentVolumeSize: 250,
|
|
44
|
-
scalerEventSchedulePeriod: "1 minute",
|
|
45
|
-
scalerMinPollInterval: "10s",
|
|
46
|
-
spotCapacityPercentage: 75,
|
|
47
|
-
scaleOutFactor: "1.0",
|
|
48
|
-
scaleInIdlePeriod: 600,
|
|
49
|
-
scaleOutWaitingForJobs: false,
|
|
50
|
-
ecrAccessPolicy: "",
|
|
51
|
-
dockerNetworkingProtocol: "ipv4",
|
|
52
|
-
enableSecretsPlugin: true,
|
|
53
|
-
enableEcrPlugin: true,
|
|
54
|
-
enableDockerLoginPlugin: true,
|
|
55
|
-
enableDockerUserNamespaceRemap: true,
|
|
56
|
-
enableDockerExperimental: false,
|
|
57
|
-
enableInstanceStorage: false,
|
|
58
|
-
mountTmpfsAtTmp: true,
|
|
59
|
-
buildkiteAgentEnableGitMirrors: false,
|
|
60
|
-
pipelineSigningKMSKey: "",
|
|
61
|
-
pipelineSigningVerificationFailureBehavior: "block",
|
|
62
|
-
// Tags
|
|
63
|
-
environment: "platform",
|
|
64
|
-
owner: "engineer"
|
|
65
|
-
};
|
|
66
|
-
export class Buildkite extends Stack {
|
|
67
|
-
constructor(scope, id, props) {
|
|
68
|
-
super(scope, id, props);
|
|
69
|
-
applyCostAllocationTags(this, {
|
|
70
|
-
service: "buildkite",
|
|
71
|
-
domain: "platform",
|
|
72
|
-
environment: props.environment || "platform",
|
|
73
|
-
owner: props.owner ?? "engineer"
|
|
74
|
-
});
|
|
75
|
-
const tags = { ...props.tags };
|
|
76
|
-
// VPC
|
|
77
|
-
const vpc = new Vpc(this, `${id}Vpc`, {
|
|
78
|
-
accountId: props.accountId,
|
|
79
|
-
subnetConfiguration: [
|
|
80
|
-
{
|
|
81
|
-
name: `${id}`,
|
|
82
|
-
subnetType: SubnetType.PUBLIC
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
});
|
|
86
|
-
// Artifacts Bucket
|
|
87
|
-
const artifactBucket = new S3Bucket(this, `${id}ArtifactBucket`);
|
|
88
|
-
const managedSecretsBucket = new S3Bucket(this, `${id}ManagedSecretsBucket`);
|
|
89
|
-
const buildkitePrivateSshKey = process.env.BUILDKITE_PRIVATE_SSH_KEY;
|
|
90
|
-
const hasBuildkitePrivateSshKey = buildkitePrivateSshKey !== undefined && buildkitePrivateSshKey !== "";
|
|
91
|
-
if (hasBuildkitePrivateSshKey) {
|
|
92
|
-
// Synth-time secret bake: Source.data() inlines the env var into the
|
|
93
|
-
// CDK asset bundle uploaded to the bootstrap S3 staging bucket. The
|
|
94
|
-
// synth host MUST hold the secret in env, and the CDK staging bucket
|
|
95
|
-
// inherits the secret's blast radius. Treat as Buildkite-only opt-in.
|
|
96
|
-
new BucketDeployment(this, `${id}SshKeyDeployment`, {
|
|
97
|
-
sources: [Source.data("private_ssh_key", buildkitePrivateSshKey)],
|
|
98
|
-
destinationBucket: managedSecretsBucket
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
FjallLogger.warn("'BUILDKITE_PRIVATE_SSH_KEY' environment variable not declared. Skipping upload of private SSH key.");
|
|
103
|
-
}
|
|
104
|
-
const buildkiteAgentToken = process.env.BUILDKITE_AGENT_TOKEN;
|
|
105
|
-
const hasBuildkiteAgentToken = buildkiteAgentToken !== undefined && buildkiteAgentToken !== "";
|
|
106
|
-
const agentToken = new SecureStringParameter(this, `${id}AgentToken`, {
|
|
107
|
-
name: `/buildkite/agentToken`,
|
|
108
|
-
aliasName: `buildkiteAgentToken`,
|
|
109
|
-
value: buildkiteAgentToken,
|
|
110
|
-
overwrite: hasBuildkiteAgentToken,
|
|
111
|
-
tags: tags,
|
|
112
|
-
accountId: this.account,
|
|
113
|
-
region: this.region
|
|
114
|
-
});
|
|
115
|
-
// Buildkite Agent Execution Role
|
|
116
|
-
const agentRole = new Role(this, `${id}AgentRole`, {
|
|
117
|
-
roleName: `${id}AgentRole`,
|
|
118
|
-
description: "Buildkite Agent IAM Role",
|
|
119
|
-
inlinePolicies: {
|
|
120
|
-
["decryptBuildkiteAgentToken"]: new PolicyDocument({
|
|
121
|
-
statements: [
|
|
122
|
-
new PolicyStatement({
|
|
123
|
-
actions: ["kms:Decrypt"],
|
|
124
|
-
resources: [agentToken.cmk.key.keyArn]
|
|
125
|
-
})
|
|
126
|
-
]
|
|
127
|
-
}),
|
|
128
|
-
["readBuildkiteAgentToken"]: new PolicyDocument({
|
|
129
|
-
statements: [
|
|
130
|
-
new PolicyStatement({
|
|
131
|
-
actions: ["ssm:GetParameter"],
|
|
132
|
-
resources: [
|
|
133
|
-
`arn:${this.partition}:ssm:${this.region}:${this.account}:parameter${agentToken.name}`
|
|
134
|
-
]
|
|
135
|
-
})
|
|
136
|
-
]
|
|
137
|
-
}),
|
|
138
|
-
["readManagedSecretsBucket"]: new PolicyDocument({
|
|
139
|
-
statements: [
|
|
140
|
-
new PolicyStatement({
|
|
141
|
-
actions: ["s3:Get*", "s3:List*"],
|
|
142
|
-
resources: [
|
|
143
|
-
managedSecretsBucket.bucketArn,
|
|
144
|
-
`${managedSecretsBucket.bucketArn}/*`
|
|
145
|
-
]
|
|
146
|
-
})
|
|
147
|
-
]
|
|
148
|
-
}),
|
|
149
|
-
["writeArtifactBucket"]: new PolicyDocument({
|
|
150
|
-
statements: [
|
|
151
|
-
new PolicyStatement({
|
|
152
|
-
actions: [
|
|
153
|
-
"s3:GetObject",
|
|
154
|
-
"s3:GetObjectAcl",
|
|
155
|
-
"s3:GetObjectVersion",
|
|
156
|
-
"s3:GetObjectVersionAcl",
|
|
157
|
-
"s3:ListBucket",
|
|
158
|
-
"s3:PutObject",
|
|
159
|
-
"s3:PutObjectAcl",
|
|
160
|
-
"s3:PutObjectVersionAcl"
|
|
161
|
-
],
|
|
162
|
-
resources: [
|
|
163
|
-
artifactBucket.bucketArn,
|
|
164
|
-
`${artifactBucket.bucketArn}/*`
|
|
165
|
-
]
|
|
166
|
-
})
|
|
167
|
-
]
|
|
168
|
-
}),
|
|
169
|
-
["describeInstance"]: new PolicyDocument({
|
|
170
|
-
statements: [
|
|
171
|
-
new PolicyStatement({
|
|
172
|
-
actions: [
|
|
173
|
-
"autoscaling:DescribeAutoScalingInstances",
|
|
174
|
-
"cloudwatch:PutMetricData",
|
|
175
|
-
"cloudformation:DescribeStackResource",
|
|
176
|
-
"ec2:DescribeTags"
|
|
177
|
-
],
|
|
178
|
-
resources: ["*"]
|
|
179
|
-
})
|
|
180
|
-
]
|
|
181
|
-
}),
|
|
182
|
-
["logging"]: new PolicyDocument({
|
|
183
|
-
statements: [
|
|
184
|
-
new PolicyStatement({
|
|
185
|
-
actions: [
|
|
186
|
-
"logs:CreateLogGroup",
|
|
187
|
-
"logs:CreateLogStream",
|
|
188
|
-
"logs:PutLogEvents",
|
|
189
|
-
"logs:DescribeLogGroups",
|
|
190
|
-
"logs:DescribeLogStreams"
|
|
191
|
-
],
|
|
192
|
-
resources: ["*"]
|
|
193
|
-
})
|
|
194
|
-
]
|
|
195
|
-
}),
|
|
196
|
-
["ssm"]: new PolicyDocument({
|
|
197
|
-
statements: [
|
|
198
|
-
new PolicyStatement({
|
|
199
|
-
actions: [
|
|
200
|
-
"ssm:DescribeInstanceProperties",
|
|
201
|
-
"ssm:ListAssociations",
|
|
202
|
-
"ssm:PutInventory",
|
|
203
|
-
"ssm:UpdateInstanceInformation",
|
|
204
|
-
"ssmmessages:CreateControlChannel",
|
|
205
|
-
"ssmmessages:CreateDataChannel",
|
|
206
|
-
"ssmmessages:OpenControlChannel",
|
|
207
|
-
"ssmmessages:OpenDataChannel",
|
|
208
|
-
"ec2messages:AcknowledgeMessage",
|
|
209
|
-
"ec2messages:DeleteMessage",
|
|
210
|
-
"ec2messages:FailMessage",
|
|
211
|
-
"ec2messages:GetEndpoint",
|
|
212
|
-
"ec2messages:GetMessages",
|
|
213
|
-
"ec2messages:SendReply"
|
|
214
|
-
],
|
|
215
|
-
resources: ["*"]
|
|
216
|
-
})
|
|
217
|
-
]
|
|
218
|
-
})
|
|
219
|
-
},
|
|
220
|
-
assumedBy: new CompositePrincipal(new ServicePrincipal("autoscaling.amazonaws.com"), new ServicePrincipal("ec2.amazonaws.com"))
|
|
221
|
-
});
|
|
222
|
-
// Buildkite Agent EC2 User Data
|
|
223
|
-
const multipartUserData = new MultipartUserData();
|
|
224
|
-
// Cloud Config
|
|
225
|
-
const cloudConfigUserData = UserData.forLinux({
|
|
226
|
-
shebang: "#cloud-config"
|
|
227
|
-
});
|
|
228
|
-
cloudConfigUserData.addCommands("cloud_final_modules:", " - [scripts-user, always]");
|
|
229
|
-
multipartUserData.addPart(MultipartBody.fromUserData(cloudConfigUserData, "text/cloud-config; charset='us-ascii'"));
|
|
230
|
-
// Mount Storage
|
|
231
|
-
const mountStorageUserData = UserData.forLinux({
|
|
232
|
-
shebang: "#!/bin/bash -v"
|
|
233
|
-
});
|
|
234
|
-
mountStorageUserData.addCommands(`BUILDKITE_ENABLE_INSTANCE_STORAGE='${props.enableInstanceStorage}' \\`, `BUILDKITE_MOUNT_TMPFS_AT_TMP='${props.mountTmpfsAtTmp}' \\`, "/usr/local/bin/bk-mount-instance-storage.sh");
|
|
235
|
-
multipartUserData.addPart(MultipartBody.fromUserData(mountStorageUserData, "text/x-shellscript; charset='us-ascii'"));
|
|
236
|
-
// Configure Docker
|
|
237
|
-
const configureDockerUserData = UserData.forLinux({
|
|
238
|
-
shebang: "#!/bin/bash -v"
|
|
239
|
-
});
|
|
240
|
-
configureDockerUserData.addCommands(`DOCKER_USERNS_REMAP='${props.enableDockerUserNamespaceRemap}' \\`, `DOCKER_EXPERIMENTAL='${props.enableDockerExperimental}' \\`, `DOCKER_NETWORKING_PROTOCOL='${props.dockerNetworkingProtocol}' \\`, `BUILDKITE_ENABLE_INSTANCE_STORAGE='${props.enableInstanceStorage}' \\`, "/usr/local/bin/bk-configure-docker.sh");
|
|
241
|
-
multipartUserData.addPart(MultipartBody.fromUserData(configureDockerUserData, "text/x-shellscript; charset='us-ascii'"));
|
|
242
|
-
const installElasticStackUserData = UserData.forLinux({
|
|
243
|
-
shebang: "#!/bin/bash -v"
|
|
244
|
-
});
|
|
245
|
-
installElasticStackUserData.addCommands(`BUILDKITE_STACK_NAME='${id}' \\`, `BUILDKITE_STACK_VERSION='${props.elasticStackVersion}' \\`, `BUILDKITE_SCALE_IN_IDLE_PERIOD='${props.scaleInIdlePeriod}' \\`, `BUILDKITE_SECRETS_BUCKET='${managedSecretsBucket.bucketName}' \\`, `BUILDKITE_SECRETS_BUCKET_REGION='${props.env?.region}' \\`, `BUILDKITE_AGENT_TOKEN_PATH='${agentToken.name}' \\`, `BUILDKITE_AGENTS_PER_INSTANCE='${props.agentsPerInstance}' \\`, `BUILDKITE_AGENT_TAGS='${props.buildkiteAgentTags}' \\`, `BUILDKITE_AGENT_TIMESTAMP_LINES='${props.buildkiteAgentTimestampLines}' \\`, `BUILDKITE_AGENT_EXPERIMENTS='${props.buildkiteAgentExperiments}' \\`, `BUILDKITE_AGENT_TRACING_BACKEND='${props.buildkiteAgentTracingBackend}' \\`, `BUILDKITE_AGENT_RELEASE='${props.buildkiteAgentRelease}' \\`, `BUILDKITE_AGENT_CANCEL_GRACE_PERIOD='${props.buildkiteAgentCancelGracePeriod}' \\`, `BUILDKITE_AGENT_SIGNING_KMS_KEY='${props.pipelineSigningKMSKey}' \\`, `BUILDKITE_AGENT_SIGNING_FAILURE_BEHAVIOR='${props.pipelineSigningVerificationFailureBehavior}' \\`, `BUILDKITE_QUEUE='${props.buildkiteQueue}' \\`, `BUILDKITE_AGENT_ENABLE_GIT_MIRRORS='${props.buildkiteAgentEnableGitMirrors}' \\`, `BUILDKITE_ELASTIC_BOOTSTRAP_SCRIPT='${props.bootstrapScriptUrl}' \\`, `BUILDKITE_ENV_FILE_URL='${props.agentEnvFileUrl}' \\`, `BUILDKITE_ENABLE_INSTANCE_STORAGE='${props.enableInstanceStorage}' \\`, `BUILDKITE_AUTHORIZED_USERS_URL='${props.authorizedUsersUrl}' \\`, `BUILDKITE_ECR_POLICY='${props.ecrAccessPolicy}' \\`, `BUILDKITE_TERMINATE_INSTANCE_AFTER_JOB='${props.buildkiteTerminateInstanceAfterJob}' \\`, `BUILDKITE_ADDITIONAL_SUDO_PERMISSIONS='${props.buildkiteAdditionalSudoPermissions}' \\`, `AWS_DEFAULT_REGION='${props.env?.region}' \\`, `SECRETS_PLUGIN_ENABLED='${props.enableSecretsPlugin}' \\`, `ECR_PLUGIN_ENABLED='${props.enableEcrPlugin}' \\`, `DOCKER_LOGIN_PLUGIN_ENABLED='${props.enableDockerLoginPlugin}' \\`, `DOCKER_EXPERIMENTAL='${props.enableDockerExperimental}' \\`, `DOCKER_USERNS_REMAP='${props.enableDockerUserNamespaceRemap}' \\`, `AWS_REGION='${props.env?.region}' \\`, "/usr/local/bin/bk-install-elastic-stack.sh");
|
|
246
|
-
// Merge User Data
|
|
247
|
-
multipartUserData.addPart(MultipartBody.fromUserData(installElasticStackUserData, "text/x-shellscript; charset='us-ascii'"));
|
|
248
|
-
const ec2Instance = new Ec2Instance(this, `${id}Agent`, {
|
|
249
|
-
serviceName: `${id}Agent`,
|
|
250
|
-
vpc,
|
|
251
|
-
vpcSubnets: { subnetType: SubnetType.PUBLIC },
|
|
252
|
-
instanceType: props.agentInstanceType.toString(),
|
|
253
|
-
machineImage: MachineImage.lookup({
|
|
254
|
-
name: "buildkite-stack-linux-x86_64-*",
|
|
255
|
-
owners: ["172840064832"],
|
|
256
|
-
filters: {
|
|
257
|
-
["block-device-mapping.volume-type"]: ["gp3"]
|
|
258
|
-
}
|
|
259
|
-
}),
|
|
260
|
-
userData: multipartUserData,
|
|
261
|
-
role: agentRole,
|
|
262
|
-
blockDevices: [
|
|
263
|
-
{
|
|
264
|
-
deviceName: "/dev/xvda",
|
|
265
|
-
volume: safeEbs(props.agentVolumeSize)
|
|
266
|
-
}
|
|
267
|
-
],
|
|
268
|
-
minCapacity: props.agentMinInstances,
|
|
269
|
-
maxCapacity: props.agentMaxInstances,
|
|
270
|
-
spotCapacityPercentage: props.spotCapacityPercentage
|
|
271
|
-
});
|
|
272
|
-
Tags.of(ec2Instance).add("Role", "buildkiteAgent");
|
|
273
|
-
Tags.of(ec2Instance).add("BuildkiteAgentRelease", props.buildkiteAgentRelease);
|
|
274
|
-
Tags.of(ec2Instance).add("BuildkiteQueue", props.buildkiteQueue);
|
|
275
|
-
const autoScalingGroup = ec2Instance.getAutoScalingGroup();
|
|
276
|
-
agentRole.attachInlinePolicy(new Policy(this, `${id}UpdateAutoScalingGroupPolicy`, {
|
|
277
|
-
statements: [
|
|
278
|
-
new PolicyStatement({
|
|
279
|
-
actions: [
|
|
280
|
-
"autoscaling:SetInstanceHealth",
|
|
281
|
-
"autoscaling:TerminateInstanceInAutoScalingGroup"
|
|
282
|
-
],
|
|
283
|
-
resources: [`${autoScalingGroup.autoScalingGroupArn}`]
|
|
284
|
-
})
|
|
285
|
-
]
|
|
286
|
-
}));
|
|
287
|
-
new SamApplication(this, `${id}AgentScaler`, {
|
|
288
|
-
applicationId: "arn:aws:serverlessrepo:us-east-1:172840064832:applications/buildkite-agent-scaler",
|
|
289
|
-
semanticVersion: props.buildkiteAgentScalerVersion,
|
|
290
|
-
parameters: {
|
|
291
|
-
BuildkiteAgentTokenParameter: `${agentToken.name}`,
|
|
292
|
-
BuildkiteAgentTokenParameterStoreKMSKey: agentToken.cmk.key.keyId,
|
|
293
|
-
RolePermissionsBoundaryARN: "",
|
|
294
|
-
BuildkiteQueue: props.buildkiteQueue,
|
|
295
|
-
AgentsPerInstance: `${props.agentsPerInstance}`,
|
|
296
|
-
MinSize: `${props.agentMinInstances}`,
|
|
297
|
-
MaxSize: `${props.agentMaxInstances}`,
|
|
298
|
-
AgentAutoScaleGroup: autoScalingGroup.autoScalingGroupName,
|
|
299
|
-
ScaleOutFactor: props.scaleOutFactor,
|
|
300
|
-
ScaleOutForWaitingJobs: `${props.scaleOutWaitingForJobs}`,
|
|
301
|
-
EventSchedulePeriod: props.scalerEventSchedulePeriod,
|
|
302
|
-
MinPollInterval: props.scalerMinPollInterval,
|
|
303
|
-
LogRetentionDays: `${props.logRetentionDays}`
|
|
304
|
-
},
|
|
305
|
-
costAllocationService: "buildkite",
|
|
306
|
-
costAllocationDomain: "buildkite-agent-scaler"
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
export default Buildkite;
|
|
1
|
+
/**
|
|
2
|
+
* Self-hosted Buildkite agent fleet — barrel for the `buildkite/` modules.
|
|
3
|
+
* The pre-2026-07 `Buildkite extends Stack` implementation is replaced by a
|
|
4
|
+
* Construct instantiated via `App.addBuildkite()`; design:
|
|
5
|
+
* `aiDocs/designs/2026-07-18-buildkite-selfhosted-agents.md`.
|
|
6
|
+
*/
|
|
7
|
+
export { Buildkite, FJALL_ENV_HOOK_OBJECT_KEY, buildFjallApiKeyEnvHookScript } from "./buildkite/buildkite.js";
|
|
8
|
+
export { BUILDKITE_AMI_OWNER_ACCOUNT_ID, BUILDKITE_CPU_ARCHITECTURES, BUILDKITE_STACK_PINS, resolvePinnedAmiId, resolveScalerSarApplicationArn } from "./buildkite/pins.js";
|
|
9
|
+
export { BuildkitePropsSchema, validateBuildkiteProps } from "./buildkite/schema.js";
|
|
10
|
+
export { BUILDKITE_AGENT_IAM_ACTION_ALLOWLIST, buildAgentRole } from "./buildkite/iam.js";
|
|
11
|
+
export { BUILDKITE_DOCKER_PART_ENV_KEYS, BUILDKITE_MOUNT_PART_ENV_KEYS, BUILDKITE_USER_DATA_ENV_KEYS, buildBuildkiteDockerCommands, buildBuildkiteInstallCommands, buildBuildkiteMountCommands, buildBuildkiteUserData } from "./buildkite/userData.js";
|
|
12
|
+
export { addBuildkiteAlarms } from "./buildkite/alarms.js";
|
|
@@ -1,23 +1,38 @@
|
|
|
1
|
-
import { Fn } from "aws-cdk-lib";
|
|
1
|
+
import { Fn, Token } from "aws-cdk-lib";
|
|
2
2
|
import { Certificate } from "aws-cdk-lib/aws-certificatemanager";
|
|
3
3
|
import { CloudFrontDistribution } from "../../resources/aws/cdn/index.js";
|
|
4
4
|
import { isStorage } from "./storage.js";
|
|
5
5
|
import { isCompute, isEcsCompute, isLambdaCompute } from "./compute.js";
|
|
6
|
-
import { FjallLogger } from "../../utils/validationLogger.js";
|
|
7
6
|
/**
|
|
8
|
-
* Validates CDN props
|
|
7
|
+
* Validates CDN props — synth-time hard errors for shapes CloudFormation
|
|
8
|
+
* would only reject at deploy.
|
|
9
9
|
*/
|
|
10
10
|
function validateCdnProps(props) {
|
|
11
11
|
const hasCertificate = !!(props.certificate || props.certificateArn);
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
// CloudFront accepts viewer certificates from us-east-1 only. A literal
|
|
13
|
+
// ARN is inspectable at synth (design H3/D3 — the old advisory log is
|
|
14
|
+
// replaced by real us-east-1 provisioning in the patterns; this assert
|
|
15
|
+
// covers the BYO-ARN escape hatch), so fail here with the cure rather
|
|
16
|
+
// than at CloudFormation with InvalidViewerCertificate.
|
|
17
|
+
if (props.certificateArn !== undefined &&
|
|
18
|
+
!Token.isUnresolved(props.certificateArn)) {
|
|
19
|
+
const certificateRegion = props.certificateArn.split(":")[3];
|
|
20
|
+
if (certificateRegion !== "us-east-1") {
|
|
21
|
+
throw new Error(`CDN 'certificateArn' is in region '${certificateRegion}' ` +
|
|
22
|
+
`(got '${props.certificateArn}'). CloudFront accepts viewer ` +
|
|
23
|
+
"certificates from us-east-1 only — pass the ARN of a us-east-1 " +
|
|
24
|
+
"certificate, or set the pattern's domain zone identity so it " +
|
|
25
|
+
"provisions one.");
|
|
26
|
+
}
|
|
16
27
|
}
|
|
17
|
-
//
|
|
28
|
+
// Aliases without a viewer certificate fail at CloudFormation deploy; fail
|
|
29
|
+
// at synth with the cure instead.
|
|
18
30
|
if (props.domainNames && props.domainNames.length > 0 && !hasCertificate) {
|
|
19
|
-
|
|
20
|
-
"
|
|
31
|
+
throw new Error(`CDN 'domainNames' [${props.domainNames.join(", ")}] were provided ` +
|
|
32
|
+
"without a certificate. CloudFront serves custom domain names only " +
|
|
33
|
+
"with a us-east-1 ACM certificate — pass 'certificate' or " +
|
|
34
|
+
"'certificateArn', or configure the pattern's 'domain' with a zone " +
|
|
35
|
+
"identity so it provisions one.");
|
|
21
36
|
}
|
|
22
37
|
// Validate ALB origin
|
|
23
38
|
if (props.originType === "alb") {
|
|
@@ -25,7 +25,7 @@ import { FjallLogger } from "../../utils/validationLogger.js";
|
|
|
25
25
|
import { VALIDATION_PATTERNS } from "@fjall/generator";
|
|
26
26
|
import { evaluateBakeGuard } from "@fjall/util/docker";
|
|
27
27
|
import { toKebab, buildParameterPath } from "@fjall/util";
|
|
28
|
-
import { validateSecretName } from "../../resources/aws/compute/ecsValidation.js";
|
|
28
|
+
import { validateEcsDomainConfig, validateSecretName } from "../../resources/aws/compute/ecsValidation.js";
|
|
29
29
|
import { COMPUTE_DEFAULTS, collectImportedSecretNames } from "./compute.js";
|
|
30
30
|
import { isHookMigrations } from "./computeEcsTypes.js";
|
|
31
31
|
export { ScalingType } from "./computeEcsTypes.js";
|
|
@@ -81,6 +81,11 @@ function validateSeparateTaskDef(serviceName, separateTaskDef) {
|
|
|
81
81
|
* Extracted for clarity and detail parity with database/network patterns.
|
|
82
82
|
*/
|
|
83
83
|
export function validateEcsProps(props) {
|
|
84
|
+
// Mirror of the resources-layer domainConfig checks (H8 legacy `region`
|
|
85
|
+
// rejection, routingPolicy shape) — defence-in-depth per generator-standards
|
|
86
|
+
// § "Validate at the Lowest Layer the Field Belongs To". The cluster name is
|
|
87
|
+
// the construct id, unavailable here, so errors prefix with the domain name.
|
|
88
|
+
validateEcsDomainConfig(props.cluster?.domainConfig);
|
|
84
89
|
// Validate services array exists and is not empty
|
|
85
90
|
if (!props.services || props.services.length === 0) {
|
|
86
91
|
throw new Error("At least one service must be specified in 'services' array.");
|
|
@@ -522,6 +522,10 @@ export interface EcsClusterConfig {
|
|
|
522
522
|
* Domain for HTTPS access.
|
|
523
523
|
* - Omit: ALB created with default DNS (*.elb.amazonaws.com)
|
|
524
524
|
* - Specified: Creates ACM certificate + Route53 DNS A record
|
|
525
|
+
*
|
|
526
|
+
* Requires `domainConfig` to name a zone source (`managedDomain` or
|
|
527
|
+
* `hostedZone`) — Fjall never creates a hosted zone inside an app stack
|
|
528
|
+
* (design D1/H1), so a bare `domain` with no zone source throws at synth.
|
|
525
529
|
*/
|
|
526
530
|
domain?: string;
|
|
527
531
|
/**
|
|
@@ -538,9 +542,10 @@ export interface EcsClusterConfig {
|
|
|
538
542
|
*/
|
|
539
543
|
directAccess?: boolean;
|
|
540
544
|
/**
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
545
|
+
* Domain configuration: zone source (managed domain or imported zone),
|
|
546
|
+
* certificate, and routing policy (latency via `routingPolicy`, weighted,
|
|
547
|
+
* geo). Required for any custom domain; enables multi-region deployments
|
|
548
|
+
* with advanced DNS routing.
|
|
544
549
|
*/
|
|
545
550
|
domainConfig?: DomainConfig;
|
|
546
551
|
/**
|
|
@@ -9,9 +9,18 @@ export interface DelegatedDomainPatternResult {
|
|
|
9
9
|
readonly manualRecords: ManualRecord[];
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* Composition for `registrar: "external-delegated"
|
|
13
|
-
* for `{delegatedSubdomain}.{zoneName}`
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* Composition for `registrar: "external-delegated"` — the delegated-child
|
|
13
|
+
* topology. Creates a hosted zone for `{delegatedSubdomain}.{zoneName}` and,
|
|
14
|
+
* when `parentDelegationRoleArn` is present, UPSERTs the child NS into the
|
|
15
|
+
* parent zone via `CrossAccountZoneDelegationRecord` (D8 child-writes
|
|
16
|
+
* delegation; same shape as devSubstrate Case 1). The role ARN MUST be a
|
|
17
|
+
* literal — validated in `validateDomainProps` (VD2-a). When absent, the
|
|
18
|
+
* user delegates out of band (external registrar / manual NS) and a synth
|
|
19
|
+
* warning flags that the zone will not resolve until they do.
|
|
20
|
+
*
|
|
21
|
+
* `phase: "zone"` early-returns before certificate issuance (R2 cert-hang
|
|
22
|
+
* guard, shared `DomainDeployPhase` contract with `DevSubstrate`): issuing a
|
|
23
|
+
* DNS-validated certificate before the delegation has propagated hangs ACM
|
|
24
|
+
* until CloudFormation rolls back (~1 h).
|
|
16
25
|
*/
|
|
17
26
|
export declare function composeDelegatedDomain(scope: Construct, props: ExternalDelegatedProps): DelegatedDomainPatternResult;
|
|
@@ -1,22 +1,75 @@
|
|
|
1
|
+
import { Annotations } from "aws-cdk-lib";
|
|
2
|
+
import { Role } from "../../resources/aws/iam/index.js";
|
|
1
3
|
import { HostedZone } from "../../resources/aws/networking/hostedZone.js";
|
|
4
|
+
import { CrossAccountDelegationRecord } from "../../resources/aws/networking/crossAccountDelegationRecord.js";
|
|
2
5
|
import { DomainCertificate } from "../../resources/aws/networking/domainCertificate.js";
|
|
3
6
|
import { composeTypedDnsRecords } from "./dnsRecordComposer.js";
|
|
4
7
|
import { toPascalCase, getSafeZoneName } from "../../utils/capitaliseString.js";
|
|
8
|
+
import { DOMAIN_DEPLOY_DEFAULT_PHASE } from "../../utils/domainTypes.js";
|
|
5
9
|
/**
|
|
6
|
-
* Composition for `registrar: "external-delegated"
|
|
7
|
-
* for `{delegatedSubdomain}.{zoneName}`
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
+
* Composition for `registrar: "external-delegated"` — the delegated-child
|
|
11
|
+
* topology. Creates a hosted zone for `{delegatedSubdomain}.{zoneName}` and,
|
|
12
|
+
* when `parentDelegationRoleArn` is present, UPSERTs the child NS into the
|
|
13
|
+
* parent zone via `CrossAccountZoneDelegationRecord` (D8 child-writes
|
|
14
|
+
* delegation; same shape as devSubstrate Case 1). The role ARN MUST be a
|
|
15
|
+
* literal — validated in `validateDomainProps` (VD2-a). When absent, the
|
|
16
|
+
* user delegates out of band (external registrar / manual NS) and a synth
|
|
17
|
+
* warning flags that the zone will not resolve until they do.
|
|
18
|
+
*
|
|
19
|
+
* `phase: "zone"` early-returns before certificate issuance (R2 cert-hang
|
|
20
|
+
* guard, shared `DomainDeployPhase` contract with `DevSubstrate`): issuing a
|
|
21
|
+
* DNS-validated certificate before the delegation has propagated hangs ACM
|
|
22
|
+
* until CloudFormation rolls back (~1 h).
|
|
10
23
|
*/
|
|
11
24
|
export function composeDelegatedDomain(scope, props) {
|
|
12
25
|
const effectiveZone = `${props.delegatedSubdomain}.${props.zoneName}`;
|
|
13
26
|
const safeZone = toPascalCase(getSafeZoneName(effectiveZone));
|
|
14
27
|
const hostedZoneConstruct = new HostedZone(scope, `${safeZone}HostedZone`, {
|
|
15
28
|
zoneName: effectiveZone,
|
|
29
|
+
// The PARENT account owns the delegation role — this is the child.
|
|
16
30
|
createDelegationRole: false,
|
|
17
31
|
costAllocationEnvironment: props.costAllocationEnvironment,
|
|
18
32
|
costAllocationDomain: props.zoneName
|
|
19
33
|
});
|
|
34
|
+
// D8 child-writes delegation. The in-stack custom resource assumes the
|
|
35
|
+
// parent account's DelegationRole by LITERAL ARN (Role.fromRoleArn) — NOT
|
|
36
|
+
// Fn.importValue, which resolves same-account only (VD2-a). The parent
|
|
37
|
+
// DelegationRole trusts the whole org (OrganizationPrincipal), so a child
|
|
38
|
+
// account can assume it. The CLI injects the ARN via
|
|
39
|
+
// ManagedDomainBinding.delegationRoleArn at deploy time.
|
|
40
|
+
if (props.parentDelegationRoleArn !== undefined) {
|
|
41
|
+
const parentRole = Role.fromRoleArn(scope, `${safeZone}ParentDelegationRole`, props.parentDelegationRoleArn);
|
|
42
|
+
new CrossAccountDelegationRecord(scope, `${safeZone}DelegationRecord`, {
|
|
43
|
+
delegationRole: parentRole,
|
|
44
|
+
delegatedZone: hostedZoneConstruct.hostedZone,
|
|
45
|
+
delegatedZoneName: effectiveZone,
|
|
46
|
+
parentHostedZoneName: props.zoneName,
|
|
47
|
+
costAllocationEnvironment: props.costAllocationEnvironment,
|
|
48
|
+
costAllocationDomain: props.zoneName
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
Annotations.of(scope).addWarning(`Zone '${effectiveZone}' will not resolve until it is delegated: no ` +
|
|
53
|
+
`parentDelegationRoleArn was provided, so no NS records are written ` +
|
|
54
|
+
`into '${props.zoneName}'. Deploy via the Fjall CLI (which injects ` +
|
|
55
|
+
`the parent DelegationRole ARN from the managed-domain binding) or ` +
|
|
56
|
+
`add the zone's NS records at the parent registrar manually.`);
|
|
57
|
+
}
|
|
58
|
+
const nameServers = hostedZoneConstruct.nameServers ?? [];
|
|
59
|
+
const records = props.records ?? [];
|
|
60
|
+
if (records.length > 0) {
|
|
61
|
+
composeTypedDnsRecords(scope, hostedZoneConstruct.hostedZone, effectiveZone, records);
|
|
62
|
+
}
|
|
63
|
+
// Step 2 of the R2 two-step gate: issuing the cert before the delegation
|
|
64
|
+
// NS has propagated hangs ACM DNS-validation, so "zone" stops here.
|
|
65
|
+
if ((props.phase ?? DOMAIN_DEPLOY_DEFAULT_PHASE) !== "full") {
|
|
66
|
+
return {
|
|
67
|
+
hostedZone: hostedZoneConstruct.hostedZone,
|
|
68
|
+
certificates: new Map(),
|
|
69
|
+
nameServers,
|
|
70
|
+
manualRecords: []
|
|
71
|
+
};
|
|
72
|
+
}
|
|
20
73
|
const certificates = new Map();
|
|
21
74
|
(props.certificates ?? []).forEach((cert, index) => {
|
|
22
75
|
const normalised = normaliseCertificate(cert);
|
|
@@ -31,10 +84,6 @@ export function composeDelegatedDomain(scope, props) {
|
|
|
31
84
|
});
|
|
32
85
|
certificates.set(normalised.domainName, dc.certificate);
|
|
33
86
|
});
|
|
34
|
-
if (props.records && props.records.length > 0) {
|
|
35
|
-
composeTypedDnsRecords(scope, hostedZoneConstruct.hostedZone, effectiveZone, props.records);
|
|
36
|
-
}
|
|
37
|
-
const nameServers = hostedZoneConstruct.nameServers ?? [];
|
|
38
87
|
return {
|
|
39
88
|
hostedZone: hostedZoneConstruct.hostedZone,
|
|
40
89
|
certificates,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
2
|
import type App from "../../app.js";
|
|
3
|
+
import { type DomainDeployPhase } from "../../utils/domainTypes.js";
|
|
3
4
|
/**
|
|
4
5
|
* Props for the internal `DevSubstrate` construct.
|
|
5
6
|
*
|
|
@@ -44,7 +45,7 @@ export interface IDevSubstrateProps {
|
|
|
44
45
|
* only after the delegated NS has propagated, else ACM DNS-validation hangs.
|
|
45
46
|
* Defaults to `"full"`. Inert unless `domain` is set.
|
|
46
47
|
*/
|
|
47
|
-
readonly phase?:
|
|
48
|
+
readonly phase?: DomainDeployPhase;
|
|
48
49
|
/**
|
|
49
50
|
* Public domain the substrate hangs a `dev.<appDomain>` child zone under. When
|
|
50
51
|
* omitted, the substrate stands up no zone/cert (early milestone — G2 reaches
|
|
@@ -24,6 +24,7 @@ import { CrossAccountDelegationRecord } from "../../resources/aws/networking/cro
|
|
|
24
24
|
import { DomainCertificate } from "../../resources/aws/networking/domainCertificate.js";
|
|
25
25
|
import { Role } from "../../resources/aws/iam/role.js";
|
|
26
26
|
import { toPascalCase } from "../../utils/capitaliseString.js";
|
|
27
|
+
import { DOMAIN_DEPLOY_DEFAULT_PHASE } from "../../utils/domainTypes.js";
|
|
27
28
|
import { DEV_AURORA_DEFAULTS } from "../../resources/aws/database/rdsDefaults.js";
|
|
28
29
|
import { DEV_APP_TAG_KEY, DEV_TAG_KEY, DEV_TAG_VALUE } from "../../utils/devSubstrateTags.js";
|
|
29
30
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -54,12 +55,11 @@ const DEV_SUBSTRATE_PARENT_ROLE_ID = "DevSubstrateParentDelegationRole";
|
|
|
54
55
|
/** Construct ids for the substrate's wildcard cert + :443 HTTPS listener (A6). */
|
|
55
56
|
const DEV_SUBSTRATE_CERT_ID = "DevSubstrateCertificate";
|
|
56
57
|
const DEV_SUBSTRATE_HTTPS_LISTENER_ID = "DevSubstrateHttpsListener";
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const DEV_SUBSTRATE_DEFAULT_PHASE = "full";
|
|
58
|
+
// Default deploy phase when `phase` is omitted — the cert + :443 listener
|
|
59
|
+
// build. Shared with the delegated `Domain` topology via
|
|
60
|
+
// `DOMAIN_DEPLOY_DEFAULT_PHASE` (utils/domainTypes.ts): the R2 two-step guard
|
|
61
|
+
// sets `"zone"` explicitly for step 1, so the safe default for a single-shot
|
|
62
|
+
// deploy is the complete substrate.
|
|
63
63
|
/**
|
|
64
64
|
* The substrate's database mode, surfaced to slot-ops as an export so the
|
|
65
65
|
* hand-created G2 slot knows whether to wire Aurora Data-API creds or a
|
|
@@ -142,6 +142,11 @@ export class DevSubstrate extends Construct {
|
|
|
142
142
|
// exposes for the cluster ARN; it returns databaseCluster.clusterArn.
|
|
143
143
|
dbClusterArn: database.getSnapshotTarget().arn,
|
|
144
144
|
dbSecretArn: database.getCredentials().secret.secretArn,
|
|
145
|
+
// Proxy-less substrate ⇒ getHostEndpoint() is the cluster writer endpoint;
|
|
146
|
+
// it + the port ride the exports because the dev role cannot rds:Describe*
|
|
147
|
+
// at runtime to resolve them.
|
|
148
|
+
dbWriterEndpoint: database.getHostEndpoint(),
|
|
149
|
+
dbPort: database.getHostPort(),
|
|
145
150
|
slotExecutionRoleArn: slotExecutionRole.roleArn,
|
|
146
151
|
slotRepositoryUri: slotRepository.repositoryUri,
|
|
147
152
|
wakerTargetGroupArn: wakerTargetGroup.targetGroupArn,
|
|
@@ -244,7 +249,10 @@ export class DevSubstrate extends Construct {
|
|
|
244
249
|
// fence-pinned name — the reaper finds it by the subtree fence tags. A pinned
|
|
245
250
|
// name would only reintroduce the 32-char ALB-name truncation dance.
|
|
246
251
|
const loadBalancer = createApplicationLoadBalancer(this, DEV_SUBSTRATE_ALB_ID, { vpc, internetFacing: true, securityGroup: albSecurityGroup });
|
|
247
|
-
slotSecurityGroup.connections.allowFrom(albSecurityGroup, Port.tcp(DEV_SUBSTRATE_SLOT_PORT),
|
|
252
|
+
slotSecurityGroup.connections.allowFrom(albSecurityGroup, Port.tcp(DEV_SUBSTRATE_SLOT_PORT),
|
|
253
|
+
// EC2 SG-rule descriptions reject non-ASCII (allowed set is
|
|
254
|
+
// `a-zA-Z0-9. _-:/()#,@[]+=&;{}!$*` — no arrow, em-dash, or even `>`).
|
|
255
|
+
"Dev substrate ALB to slot tasks");
|
|
248
256
|
// Unconditional 404 default — the substrate's only routes are per-slot
|
|
249
257
|
// host-header rules, so an unmatched host must 404, never fall through (the ECS
|
|
250
258
|
// path's 404 is conditional on >=2 routes). The :443 listener + wildcard cert
|
|
@@ -327,7 +335,7 @@ export class DevSubstrate extends Construct {
|
|
|
327
335
|
}
|
|
328
336
|
// Step 2 of the R2 two-step gate: synthesising the cert before the step-1 NS
|
|
329
337
|
// has propagated hangs ACM DNS-validation for hours, so "zone" stops here.
|
|
330
|
-
if ((props.phase ??
|
|
338
|
+
if ((props.phase ?? DOMAIN_DEPLOY_DEFAULT_PHASE) !== "full")
|
|
331
339
|
return { devDomain: childZoneName };
|
|
332
340
|
// exportCertificateArn:false — consumed in-stack by the :443 listener (slots
|
|
333
341
|
// reference the exported LISTENER arn, not the cert), and the wildcard `*.`
|
|
@@ -375,6 +383,8 @@ export class DevSubstrate extends Construct {
|
|
|
375
383
|
emit(this, "AlbCloudWatchName", values.albCloudWatchName);
|
|
376
384
|
emit(databaseScope, "DbClusterArn", values.dbClusterArn);
|
|
377
385
|
emit(databaseScope, "DbSecretArn", values.dbSecretArn);
|
|
386
|
+
emit(databaseScope, "DbWriterEndpoint", values.dbWriterEndpoint);
|
|
387
|
+
emit(databaseScope, "DbPort", values.dbPort);
|
|
378
388
|
emit(this, "SlotExecutionRoleArn", values.slotExecutionRoleArn);
|
|
379
389
|
emit(this, "SlotRepositoryUri", values.slotRepositoryUri);
|
|
380
390
|
emit(this, "WakerTargetGroupArn", values.wakerTargetGroupArn);
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import type { Construct } from "constructs";
|
|
2
2
|
import type { IHostedZone } from "aws-cdk-lib/aws-route53";
|
|
3
|
-
import type { DnsRecord
|
|
3
|
+
import type { DnsRecord } from "./interfaces/domain.js";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* the `DnsRecordInput` shape from the BIND-primary era where ALIAS sentinels
|
|
7
|
-
* live in `value`. Typed targets are not supported here; use
|
|
8
|
-
* `composeTypedDnsRecords` instead.
|
|
9
|
-
*/
|
|
10
|
-
export declare function composeDnsRecords(scope: Construct, zone: IHostedZone, zoneName: string, records: DnsRecordInput[]): void;
|
|
11
|
-
/**
|
|
12
|
-
* Typed composer consumed by the new `Domain` construct. Accepts the
|
|
5
|
+
* Typed composer consumed by the `Domain` construct. Accepts the
|
|
13
6
|
* discriminated `DnsRecord` union (`StandardRecord | AliasRecord`).
|
|
14
7
|
*
|
|
15
8
|
* `AliasRecord` entries MUST carry a `target` produced by `fjallApp()`,
|