@composurecdk/ec2 0.5.1 → 0.7.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/README.md +121 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/instance-builder.d.ts +44 -6
- package/dist/instance-builder.d.ts.map +1 -1
- package/dist/instance-builder.js +49 -4
- package/dist/instance-builder.js.map +1 -1
- package/dist/instance-volume-attachment-config.d.ts +34 -0
- package/dist/instance-volume-attachment-config.d.ts.map +1 -0
- package/dist/instance-volume-attachment-config.js +2 -0
- package/dist/instance-volume-attachment-config.js.map +1 -0
- package/dist/instance-volume-attachment-defaults.d.ts +14 -0
- package/dist/instance-volume-attachment-defaults.d.ts.map +1 -0
- package/dist/instance-volume-attachment-defaults.js +24 -0
- package/dist/instance-volume-attachment-defaults.js.map +1 -0
- package/dist/instance-volume-attachments.d.ts +59 -0
- package/dist/instance-volume-attachments.d.ts.map +1 -0
- package/dist/instance-volume-attachments.js +104 -0
- package/dist/instance-volume-attachments.js.map +1 -0
- package/dist/volume-alarm-config.d.ts +35 -0
- package/dist/volume-alarm-config.d.ts.map +1 -0
- package/dist/volume-alarm-config.js +2 -0
- package/dist/volume-alarm-config.js.map +1 -0
- package/dist/volume-alarm-defaults.d.ts +17 -0
- package/dist/volume-alarm-defaults.d.ts.map +1 -0
- package/dist/volume-alarm-defaults.js +27 -0
- package/dist/volume-alarm-defaults.js.map +1 -0
- package/dist/volume-alarms.d.ts +29 -0
- package/dist/volume-alarms.d.ts.map +1 -0
- package/dist/volume-alarms.js +88 -0
- package/dist/volume-alarms.js.map +1 -0
- package/dist/volume-builder.d.ts +171 -0
- package/dist/volume-builder.d.ts.map +1 -0
- package/dist/volume-builder.js +95 -0
- package/dist/volume-builder.js.map +1 -0
- package/dist/volume-defaults.d.ts +15 -0
- package/dist/volume-defaults.d.ts.map +1 -0
- package/dist/volume-defaults.js +47 -0
- package/dist/volume-defaults.js.map +1 -0
- package/dist/vpc-builder.d.ts +3 -2
- package/dist/vpc-builder.d.ts.map +1 -1
- package/dist/vpc-builder.js +2 -2
- package/dist/vpc-builder.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -120,6 +120,51 @@ const server = createInstanceBuilder()
|
|
|
120
120
|
);
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
+
### Attaching persistent volumes
|
|
124
|
+
|
|
125
|
+
`attachVolume(key, volumeRef, opts)` mirrors the call shape of `addAlarm` and produces an `AWS::EC2::VolumeAttachment` for an externally-managed EBS volume. The volume reference accepts either a `Resolvable<VolumeBuilderResult>` (drop a `ref<VolumeBuilderResult>("data")` straight in) or a `Resolvable<IVolume>`.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { compose, ref } from "@composurecdk/core";
|
|
129
|
+
import {
|
|
130
|
+
createInstanceBuilder,
|
|
131
|
+
createVolumeBuilder,
|
|
132
|
+
createVpcBuilder,
|
|
133
|
+
type VolumeBuilderResult,
|
|
134
|
+
type VpcBuilderResult,
|
|
135
|
+
} from "@composurecdk/ec2";
|
|
136
|
+
import { Size } from "aws-cdk-lib";
|
|
137
|
+
import { InstanceClass, InstanceSize, InstanceType, MachineImage } from "aws-cdk-lib/aws-ec2";
|
|
138
|
+
|
|
139
|
+
compose(
|
|
140
|
+
{
|
|
141
|
+
network: createVpcBuilder().maxAzs(2).natGateways(0),
|
|
142
|
+
|
|
143
|
+
data: createVolumeBuilder()
|
|
144
|
+
.availabilityZone(ref<VpcBuilderResult>("network").map((r) => r.vpc.availabilityZones[0]))
|
|
145
|
+
.size(Size.gibibytes(50)),
|
|
146
|
+
|
|
147
|
+
agent: createInstanceBuilder()
|
|
148
|
+
.vpc(ref<VpcBuilderResult>("network").map((r) => r.vpc))
|
|
149
|
+
.instanceType(InstanceType.of(InstanceClass.T3, InstanceSize.MICRO))
|
|
150
|
+
.machineImage(MachineImage.latestAmazonLinux2023())
|
|
151
|
+
.attachVolume("AgentData", ref<VolumeBuilderResult>("data"), { device: "/dev/sdf" }),
|
|
152
|
+
},
|
|
153
|
+
{ network: [], data: ["network"], agent: ["network", "data"] },
|
|
154
|
+
).build(stack, "AgentApp");
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The result exposes the attachment under `result.agent.volumeAttachments.AgentData` and emits a per-attachment `volumeStalledIo` alarm under `result.agent.alarms["AgentData.volumeStalledIo"]`. When both AZs are concrete strings at synth, the builder asserts the instance and volume share an Availability Zone — synth-time failure beats boot-time failure.
|
|
158
|
+
|
|
159
|
+
`VolumeStalledIOCheck` is published only for Nitro-instance attachments. On non-Nitro instances the alarm sits at `INSUFFICIENT_DATA`, which the `treatMissingData: NOT_BREACHING` default makes harmless. To disable the alarm per attachment:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
.attachVolume("AgentData", ref<VolumeBuilderResult>("data"), {
|
|
163
|
+
device: "/dev/sdf",
|
|
164
|
+
recommendedAlarms: false,
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
123
168
|
## VPC Builder
|
|
124
169
|
|
|
125
170
|
```ts
|
|
@@ -175,6 +220,82 @@ createVpcBuilder().flowLogs(false);
|
|
|
175
220
|
|
|
176
221
|
For multiple flow logs against the same VPC, omit this config and create additional `FlowLog` constructs directly against the returned `vpc`.
|
|
177
222
|
|
|
223
|
+
## Volume Builder
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import { createVolumeBuilder } from "@composurecdk/ec2";
|
|
227
|
+
import { Size } from "aws-cdk-lib";
|
|
228
|
+
|
|
229
|
+
const data = createVolumeBuilder()
|
|
230
|
+
.availabilityZone("us-east-1a")
|
|
231
|
+
.size(Size.gibibytes(50))
|
|
232
|
+
.build(stack, "AgentData");
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Every [VolumeProps](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.VolumeProps.html) property is available as a fluent setter on the builder, except `availabilityZone` which is set via the dedicated `.availabilityZone()` method (so it can be wired from a sibling `VpcBuilder` via `ref`) and `encryptionKey` which accepts a `Resolvable<IKey>` for cross-component KMS-key wiring.
|
|
236
|
+
|
|
237
|
+
### Volume Defaults
|
|
238
|
+
|
|
239
|
+
| Property | Default | Rationale |
|
|
240
|
+
| --------------- | ---------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
241
|
+
| `volumeType` | `GP3` | Current-generation general-purpose SSD — cheaper and faster than GP2 at equivalent sizes. |
|
|
242
|
+
| `encrypted` | `true` | Encryption at rest with the account's default EBS KMS key. Pass `encryptionKey` for a CMK. |
|
|
243
|
+
| `autoEnableIo` | `true` | Lets I/O resume on suspected inconsistency so the instance can come up unattended. |
|
|
244
|
+
| `removalPolicy` | `RemovalPolicy.RETAIN` | A destroyed volume is unrecoverable; an orphaned volume is a $/month nuisance. Mirrors `BUCKET_DEFAULTS`. |
|
|
245
|
+
|
|
246
|
+
Three properties intentionally have no default — they are application-specific and must be supplied explicitly:
|
|
247
|
+
|
|
248
|
+
- `availabilityZone` (via the `.availabilityZone()` method)
|
|
249
|
+
- `size`
|
|
250
|
+
- `iops` / `throughput` (only when opting into a volume type that requires them)
|
|
251
|
+
|
|
252
|
+
The defaults are exported as `VOLUME_DEFAULTS` for visibility and testing:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
import { VOLUME_DEFAULTS } from "@composurecdk/ec2";
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Recommended Volume Alarms
|
|
259
|
+
|
|
260
|
+
The builder creates [AWS-recommended CloudWatch alarms](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EBS) by default. No alarm actions are configured — wire actions via `alarmActionsPolicy` from `@composurecdk/cloudwatch`.
|
|
261
|
+
|
|
262
|
+
| Alarm | Metric | Default threshold | Created when |
|
|
263
|
+
| -------------- | ----------------------------- | -------------------- | ----------------------------------------------- |
|
|
264
|
+
| `burstBalance` | BurstBalance (Average, 5 min) | < 20% over 3 × 5 min | `volumeType` is burstable (`gp2`, `st1`, `sc1`) |
|
|
265
|
+
|
|
266
|
+
The defaults are exported as `VOLUME_ALARM_DEFAULTS` for visibility and testing:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import { VOLUME_ALARM_DEFAULTS } from "@composurecdk/ec2";
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
`VolumeQueueLength` and `VolumeIdleTime` are deferred — both need per-`volumeType`/per-workload tuning to be a defensible default. Wire them via `addAlarm` until first-class support lands:
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
import { Metric, Stats } from "aws-cdk-lib/aws-cloudwatch";
|
|
276
|
+
import { Duration } from "aws-cdk-lib";
|
|
277
|
+
|
|
278
|
+
const data = createVolumeBuilder()
|
|
279
|
+
.availabilityZone("us-east-1a")
|
|
280
|
+
.size(Size.gibibytes(50))
|
|
281
|
+
.addAlarm("volumeQueueLength", (alarm) =>
|
|
282
|
+
alarm
|
|
283
|
+
.metric(
|
|
284
|
+
(volume) =>
|
|
285
|
+
new Metric({
|
|
286
|
+
namespace: "AWS/EBS",
|
|
287
|
+
metricName: "VolumeQueueLength",
|
|
288
|
+
dimensionsMap: { VolumeId: volume.volumeId },
|
|
289
|
+
statistic: Stats.AVERAGE,
|
|
290
|
+
period: Duration.minutes(5),
|
|
291
|
+
}),
|
|
292
|
+
)
|
|
293
|
+
.threshold(10)
|
|
294
|
+
.greaterThan()
|
|
295
|
+
.description("EBS volume queue length is high"),
|
|
296
|
+
);
|
|
297
|
+
```
|
|
298
|
+
|
|
178
299
|
## Composing EC2 + VPC
|
|
179
300
|
|
|
180
301
|
Compose the builders into a single system — the instance is wired to the VPC via `ref`:
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,13 @@ export { createInstanceBuilder, type IInstanceBuilder, type InstanceBuilderProps
|
|
|
2
2
|
export { INSTANCE_DEFAULTS } from "./instance-defaults.js";
|
|
3
3
|
export { type InstanceAlarmConfig } from "./instance-alarm-config.js";
|
|
4
4
|
export { INSTANCE_ALARM_DEFAULTS } from "./instance-alarm-defaults.js";
|
|
5
|
+
export { type AttachVolumeOptions } from "./instance-volume-attachments.js";
|
|
6
|
+
export { type VolumeAttachmentAlarmConfig } from "./instance-volume-attachment-config.js";
|
|
7
|
+
export { VOLUME_ATTACHMENT_ALARM_DEFAULTS } from "./instance-volume-attachment-defaults.js";
|
|
8
|
+
export { createVolumeBuilder, type IVolumeBuilder, type VolumeBuilderProps, type VolumeBuilderResult, } from "./volume-builder.js";
|
|
9
|
+
export { VOLUME_DEFAULTS } from "./volume-defaults.js";
|
|
10
|
+
export { type VolumeAlarmConfig } from "./volume-alarm-config.js";
|
|
11
|
+
export { VOLUME_ALARM_DEFAULTS } from "./volume-alarm-defaults.js";
|
|
5
12
|
export { createVpcBuilder, type FlowLogsConfig, type IVpcBuilder, type VpcBuilderProps, type VpcBuilderResult, } from "./vpc-builder.js";
|
|
6
13
|
export { VPC_DEFAULTS } from "./vpc-defaults.js";
|
|
7
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,KAAK,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAC1F,OAAO,EAAE,gCAAgC,EAAE,MAAM,0CAA0C,CAAC;AAE5F,OAAO,EACL,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EACL,gBAAgB,EAChB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export { createInstanceBuilder, } from "./instance-builder.js";
|
|
2
2
|
export { INSTANCE_DEFAULTS } from "./instance-defaults.js";
|
|
3
3
|
export { INSTANCE_ALARM_DEFAULTS } from "./instance-alarm-defaults.js";
|
|
4
|
+
export { VOLUME_ATTACHMENT_ALARM_DEFAULTS } from "./instance-volume-attachment-defaults.js";
|
|
5
|
+
export { createVolumeBuilder, } from "./volume-builder.js";
|
|
6
|
+
export { VOLUME_DEFAULTS } from "./volume-defaults.js";
|
|
7
|
+
export { VOLUME_ALARM_DEFAULTS } from "./volume-alarm-defaults.js";
|
|
4
8
|
export { createVpcBuilder, } from "./vpc-builder.js";
|
|
5
9
|
export { VPC_DEFAULTS } from "./vpc-defaults.js";
|
|
6
10
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GAItB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,GAItB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAAE,gCAAgC,EAAE,MAAM,0CAA0C,CAAC;AAE5F,OAAO,EACL,mBAAmB,GAIpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,OAAO,EACL,gBAAgB,GAKjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type Alarm } from "aws-cdk-lib/aws-cloudwatch";
|
|
2
|
-
import { Instance, type IKeyPair, type ISecurityGroup, type IVpc, type InstanceProps } from "aws-cdk-lib/aws-ec2";
|
|
2
|
+
import { type CfnVolumeAttachment, Instance, type IKeyPair, type ISecurityGroup, type IVpc, type InstanceProps } from "aws-cdk-lib/aws-ec2";
|
|
3
3
|
import { type IRole } from "aws-cdk-lib/aws-iam";
|
|
4
4
|
import { type IConstruct } from "constructs";
|
|
5
|
-
import {
|
|
5
|
+
import { COPY_STATE, type Lifecycle, type Resolvable } from "@composurecdk/core";
|
|
6
|
+
import { type ITaggedBuilder } from "@composurecdk/cloudformation";
|
|
6
7
|
import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
|
|
7
8
|
import type { InstanceAlarmConfig } from "./instance-alarm-config.js";
|
|
9
|
+
import { type AttachVolumeOptions, type AttachVolumeRef } from "./instance-volume-attachments.js";
|
|
8
10
|
/**
|
|
9
11
|
* Configuration properties for the EC2 instance builder.
|
|
10
12
|
*
|
|
@@ -82,9 +84,10 @@ export interface InstanceBuilderResult {
|
|
|
82
84
|
/**
|
|
83
85
|
* CloudWatch alarms created for the instance, keyed by alarm name.
|
|
84
86
|
*
|
|
85
|
-
* Includes
|
|
86
|
-
* via {@link IInstanceBuilder.addAlarm}
|
|
87
|
-
*
|
|
87
|
+
* Includes AWS-recommended instance alarms, any custom alarms added
|
|
88
|
+
* via {@link IInstanceBuilder.addAlarm}, and per-attachment alarms
|
|
89
|
+
* added by {@link IInstanceBuilder.attachVolume} (keyed
|
|
90
|
+
* `${attachmentKey}.${alarmKey}`, e.g. `AgentData.volumeStalledIo`).
|
|
88
91
|
*
|
|
89
92
|
* No alarm actions are configured — apply them via the result or an
|
|
90
93
|
* `afterBuild` hook.
|
|
@@ -92,6 +95,14 @@ export interface InstanceBuilderResult {
|
|
|
92
95
|
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EC2
|
|
93
96
|
*/
|
|
94
97
|
alarms: Record<string, Alarm>;
|
|
98
|
+
/**
|
|
99
|
+
* `AWS::EC2::VolumeAttachment` constructs created via
|
|
100
|
+
* {@link IInstanceBuilder.attachVolume}, keyed by the attachment key
|
|
101
|
+
* supplied in that call.
|
|
102
|
+
*
|
|
103
|
+
* Empty when no volumes were attached.
|
|
104
|
+
*/
|
|
105
|
+
volumeAttachments: Record<string, CfnVolumeAttachment>;
|
|
95
106
|
}
|
|
96
107
|
/**
|
|
97
108
|
* A fluent builder for configuring and creating an AWS EC2 instance.
|
|
@@ -125,7 +136,7 @@ export interface InstanceBuilderResult {
|
|
|
125
136
|
* .machineImage(MachineImage.latestAmazonLinux2023());
|
|
126
137
|
* ```
|
|
127
138
|
*/
|
|
128
|
-
export type IInstanceBuilder =
|
|
139
|
+
export type IInstanceBuilder = ITaggedBuilder<InstanceBuilderProps, InstanceBuilder>;
|
|
129
140
|
declare class InstanceBuilder implements Lifecycle<InstanceBuilderResult> {
|
|
130
141
|
#private;
|
|
131
142
|
props: Partial<InstanceBuilderProps>;
|
|
@@ -150,6 +161,33 @@ declare class InstanceBuilder implements Lifecycle<InstanceBuilderResult> {
|
|
|
150
161
|
* @returns This builder for chaining.
|
|
151
162
|
*/
|
|
152
163
|
addAlarm(key: string, configure: (alarm: AlarmDefinitionBuilder<Instance>) => AlarmDefinitionBuilder<Instance>): this;
|
|
164
|
+
/**
|
|
165
|
+
* Attaches an externally-managed EBS volume to the instance via an
|
|
166
|
+
* `AWS::EC2::VolumeAttachment` resource, mirroring the call shape of
|
|
167
|
+
* {@link addAlarm}.
|
|
168
|
+
*
|
|
169
|
+
* The `volumeRef` accepts either a `Resolvable<VolumeBuilderResult>`
|
|
170
|
+
* (drop a `ref<VolumeBuilderResult>("data")` straight in) or a
|
|
171
|
+
* `Resolvable<IVolume>` for an externally-managed volume — the builder
|
|
172
|
+
* unwraps either at build time.
|
|
173
|
+
*
|
|
174
|
+
* When both AZs are concrete at synth time, the builder asserts the
|
|
175
|
+
* instance and the volume are in the same Availability Zone — synth-
|
|
176
|
+
* time failure beats boot-time failure for AZ mismatches.
|
|
177
|
+
*
|
|
178
|
+
* Per-attachment AWS-recommended alarms (e.g. `volumeStalledIo`) are
|
|
179
|
+
* created by default and merged into the result's `alarms` record
|
|
180
|
+
* under prefixed keys (`${attachmentKey}.${alarmKey}`).
|
|
181
|
+
*
|
|
182
|
+
* @param key - Unique key for the attachment (used as the result-map
|
|
183
|
+
* field name and as the construct id suffix).
|
|
184
|
+
* @param volumeRef - Resolvable to the volume to attach.
|
|
185
|
+
* @param options - Attachment options (`device`, `recommendedAlarms`).
|
|
186
|
+
* @returns This builder for chaining.
|
|
187
|
+
*/
|
|
188
|
+
attachVolume(key: string, volumeRef: AttachVolumeRef, options: AttachVolumeOptions): this;
|
|
189
|
+
/** @internal — see ADR-0005. */
|
|
190
|
+
[COPY_STATE](target: InstanceBuilder): void;
|
|
153
191
|
build(scope: IConstruct, id: string, context?: Record<string, object>): InstanceBuilderResult;
|
|
154
192
|
}
|
|
155
193
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance-builder.d.ts","sourceRoot":"","sources":["../src/instance-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EACL,QAAQ,EACR,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,IAAI,EACT,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"instance-builder.d.ts","sourceRoot":"","sources":["../src/instance-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EACL,KAAK,mBAAmB,EACxB,QAAQ,EACR,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,IAAI,EACT,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,KAAK,SAAS,EAAW,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC1F,OAAO,EAAE,KAAK,cAAc,EAAiB,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAGrB,MAAM,kCAAkC,CAAC;AAE1C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAChD,aAAa,EACb,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,eAAe,CAC7C;IACC;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAEzB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAE/B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;IAE3C;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;CACjD;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,QAAQ,CAAC;IAEnB;;;;;;;;;;;;OAYG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAE9B;;;;;;OAMG;IACH,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CACxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC;AAErF,cAAM,eAAgB,YAAW,SAAS,CAAC,qBAAqB,CAAC;;IAC/D,KAAK,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAM;IAK1C;;;;;;;;;OASG;IACH,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI;IAKhC;;;;;;;;OAQG;IACH,QAAQ,CACN,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,CAAC,KAAK,EAAE,sBAAsB,CAAC,QAAQ,CAAC,KAAK,sBAAsB,CAAC,QAAQ,CAAC,GACvF,IAAI;IAKP;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI;IAQzF,gCAAgC;IAChC,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAM3C,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,qBAAqB;CAoD9F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,qBAAqB,IAAI,gBAAgB,CAExD"}
|
package/dist/instance-builder.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { Instance, } from "aws-cdk-lib/aws-ec2";
|
|
2
|
-
import {
|
|
2
|
+
import { COPY_STATE, resolve } from "@composurecdk/core";
|
|
3
|
+
import { taggedBuilder } from "@composurecdk/cloudformation";
|
|
3
4
|
import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
|
|
4
5
|
import { createInstanceAlarms } from "./instance-alarms.js";
|
|
5
6
|
import { INSTANCE_DEFAULTS } from "./instance-defaults.js";
|
|
7
|
+
import { createVolumeAttachments, } from "./instance-volume-attachments.js";
|
|
6
8
|
class InstanceBuilder {
|
|
7
9
|
props = {};
|
|
8
10
|
#customAlarms = [];
|
|
11
|
+
#volumeAttachments = [];
|
|
9
12
|
#vpc;
|
|
10
13
|
/**
|
|
11
14
|
* Sets the VPC the instance will be launched into.
|
|
@@ -34,6 +37,43 @@ class InstanceBuilder {
|
|
|
34
37
|
this.#customAlarms.push(configure(new AlarmDefinitionBuilder(key)));
|
|
35
38
|
return this;
|
|
36
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Attaches an externally-managed EBS volume to the instance via an
|
|
42
|
+
* `AWS::EC2::VolumeAttachment` resource, mirroring the call shape of
|
|
43
|
+
* {@link addAlarm}.
|
|
44
|
+
*
|
|
45
|
+
* The `volumeRef` accepts either a `Resolvable<VolumeBuilderResult>`
|
|
46
|
+
* (drop a `ref<VolumeBuilderResult>("data")` straight in) or a
|
|
47
|
+
* `Resolvable<IVolume>` for an externally-managed volume — the builder
|
|
48
|
+
* unwraps either at build time.
|
|
49
|
+
*
|
|
50
|
+
* When both AZs are concrete at synth time, the builder asserts the
|
|
51
|
+
* instance and the volume are in the same Availability Zone — synth-
|
|
52
|
+
* time failure beats boot-time failure for AZ mismatches.
|
|
53
|
+
*
|
|
54
|
+
* Per-attachment AWS-recommended alarms (e.g. `volumeStalledIo`) are
|
|
55
|
+
* created by default and merged into the result's `alarms` record
|
|
56
|
+
* under prefixed keys (`${attachmentKey}.${alarmKey}`).
|
|
57
|
+
*
|
|
58
|
+
* @param key - Unique key for the attachment (used as the result-map
|
|
59
|
+
* field name and as the construct id suffix).
|
|
60
|
+
* @param volumeRef - Resolvable to the volume to attach.
|
|
61
|
+
* @param options - Attachment options (`device`, `recommendedAlarms`).
|
|
62
|
+
* @returns This builder for chaining.
|
|
63
|
+
*/
|
|
64
|
+
attachVolume(key, volumeRef, options) {
|
|
65
|
+
if (this.#volumeAttachments.some((a) => a.key === key)) {
|
|
66
|
+
throw new Error(`attachVolume: duplicate attachment key "${key}".`);
|
|
67
|
+
}
|
|
68
|
+
this.#volumeAttachments.push({ key, volumeRef, options });
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
/** @internal — see ADR-0005. */
|
|
72
|
+
[COPY_STATE](target) {
|
|
73
|
+
target.#vpc = this.#vpc;
|
|
74
|
+
target.#customAlarms.push(...this.#customAlarms);
|
|
75
|
+
target.#volumeAttachments.push(...this.#volumeAttachments);
|
|
76
|
+
}
|
|
37
77
|
build(scope, id, context) {
|
|
38
78
|
const resolvedVpc = this.#vpc ? resolve(this.#vpc, context) : undefined;
|
|
39
79
|
if (!resolvedVpc) {
|
|
@@ -49,8 +89,13 @@ class InstanceBuilder {
|
|
|
49
89
|
...(securityGroup !== undefined ? { securityGroup: resolve(securityGroup, context) } : {}),
|
|
50
90
|
};
|
|
51
91
|
const instance = new Instance(scope, id, mergedProps);
|
|
52
|
-
const
|
|
53
|
-
|
|
92
|
+
const instanceAlarms = createInstanceAlarms(scope, id, instance, alarmConfig, mergedProps, this.#customAlarms);
|
|
93
|
+
const { attachments, alarms: attachmentAlarms } = createVolumeAttachments(scope, id, instance, mergedProps, this.#volumeAttachments, context);
|
|
94
|
+
return {
|
|
95
|
+
instance,
|
|
96
|
+
alarms: { ...instanceAlarms, ...attachmentAlarms },
|
|
97
|
+
volumeAttachments: attachments,
|
|
98
|
+
};
|
|
54
99
|
}
|
|
55
100
|
}
|
|
56
101
|
/**
|
|
@@ -82,6 +127,6 @@ class InstanceBuilder {
|
|
|
82
127
|
* ```
|
|
83
128
|
*/
|
|
84
129
|
export function createInstanceBuilder() {
|
|
85
|
-
return
|
|
130
|
+
return taggedBuilder(InstanceBuilder);
|
|
86
131
|
}
|
|
87
132
|
//# sourceMappingURL=instance-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance-builder.js","sourceRoot":"","sources":["../src/instance-builder.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"instance-builder.js","sourceRoot":"","sources":["../src/instance-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AlarmConfig } from "@composurecdk/cloudwatch";
|
|
2
|
+
/**
|
|
3
|
+
* Controls which recommended alarms are created for a per-attachment EBS
|
|
4
|
+
* volume on an EC2 instance.
|
|
5
|
+
*
|
|
6
|
+
* Default thresholds are sourced from the AWS-recommended CloudWatch
|
|
7
|
+
* alarm guide. Set the master switch or individual alarms to `false` to
|
|
8
|
+
* disable them, or provide an {@link AlarmConfig} to tune thresholds.
|
|
9
|
+
*
|
|
10
|
+
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EBS
|
|
11
|
+
*/
|
|
12
|
+
export interface VolumeAttachmentAlarmConfig {
|
|
13
|
+
/**
|
|
14
|
+
* Master switch: set to `false` to disable all per-attachment alarms.
|
|
15
|
+
* @default true
|
|
16
|
+
*/
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Alarm when the per-attachment EBS volume status check reports a
|
|
20
|
+
* stalled I/O condition — typically a host or storage-subsystem issue
|
|
21
|
+
* for that specific attachment.
|
|
22
|
+
*
|
|
23
|
+
* Metric: `AWS/EBS VolumeStalledIOCheck`, statistic Maximum,
|
|
24
|
+
* period 1 minute. Default threshold: >= 1 over 10 consecutive minutes.
|
|
25
|
+
*
|
|
26
|
+
* The metric is published only for Nitro-instance attachments. On
|
|
27
|
+
* non-Nitro instances the alarm sits at `INSUFFICIENT_DATA`, which the
|
|
28
|
+
* `treatMissingData: NOT_BREACHING` default makes harmless.
|
|
29
|
+
*
|
|
30
|
+
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EBS
|
|
31
|
+
*/
|
|
32
|
+
volumeStalledIo?: AlarmConfig | false;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=instance-volume-attachment-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-volume-attachment-config.d.ts","sourceRoot":"","sources":["../src/instance-volume-attachment-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;;;GASG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;CACvC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-volume-attachment-config.js","sourceRoot":"","sources":["../src/instance-volume-attachment-config.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AlarmConfigDefaults } from "@composurecdk/cloudwatch";
|
|
2
|
+
interface VolumeAttachmentAlarmDefaults {
|
|
3
|
+
enabled: true;
|
|
4
|
+
volumeStalledIo: AlarmConfigDefaults;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* AWS-recommended default alarm configuration for per-attachment EBS
|
|
8
|
+
* volumes.
|
|
9
|
+
*
|
|
10
|
+
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EBS
|
|
11
|
+
*/
|
|
12
|
+
export declare const VOLUME_ATTACHMENT_ALARM_DEFAULTS: VolumeAttachmentAlarmDefaults;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=instance-volume-attachment-defaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-volume-attachment-defaults.d.ts","sourceRoot":"","sources":["../src/instance-volume-attachment-defaults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,UAAU,6BAA6B;IACrC,OAAO,EAAE,IAAI,CAAC;IACd,eAAe,EAAE,mBAAmB,CAAC;CACtC;AAED;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,EAAE,6BAgB9C,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TreatMissingData } from "aws-cdk-lib/aws-cloudwatch";
|
|
2
|
+
/**
|
|
3
|
+
* AWS-recommended default alarm configuration for per-attachment EBS
|
|
4
|
+
* volumes.
|
|
5
|
+
*
|
|
6
|
+
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#EBS
|
|
7
|
+
*/
|
|
8
|
+
export const VOLUME_ATTACHMENT_ALARM_DEFAULTS = {
|
|
9
|
+
enabled: true,
|
|
10
|
+
/**
|
|
11
|
+
* The single AWS-recommended EBS alarm. Complements the existing
|
|
12
|
+
* `attachedEbsStatusCheckFailed` on the instance: the EC2-side metric
|
|
13
|
+
* pages on instance-level reachability, this one on per-volume health.
|
|
14
|
+
* The 10-of-10 evaluation window matches AWS guidance — EBS
|
|
15
|
+
* infrastructure usually self-heals within a few minutes.
|
|
16
|
+
*/
|
|
17
|
+
volumeStalledIo: {
|
|
18
|
+
threshold: 1,
|
|
19
|
+
evaluationPeriods: 10,
|
|
20
|
+
datapointsToAlarm: 10,
|
|
21
|
+
treatMissingData: TreatMissingData.NOT_BREACHING,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=instance-volume-attachment-defaults.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-volume-attachment-defaults.js","sourceRoot":"","sources":["../src/instance-volume-attachment-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAQ9D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAkC;IAC7E,OAAO,EAAE,IAAI;IAEb;;;;;;OAMG;IACH,eAAe,EAAE;QACf,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,EAAE;QACrB,iBAAiB,EAAE,EAAE;QACrB,gBAAgB,EAAE,gBAAgB,CAAC,aAAa;KACjD;CACF,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Alarm } from "aws-cdk-lib/aws-cloudwatch";
|
|
2
|
+
import { CfnVolumeAttachment, type Instance, type InstanceProps, type IVolume } from "aws-cdk-lib/aws-ec2";
|
|
3
|
+
import type { IConstruct } from "constructs";
|
|
4
|
+
import { type Resolvable } from "@composurecdk/core";
|
|
5
|
+
import type { VolumeBuilderResult } from "./volume-builder.js";
|
|
6
|
+
import type { VolumeAttachmentAlarmConfig } from "./instance-volume-attachment-config.js";
|
|
7
|
+
/**
|
|
8
|
+
* Reference to the volume to be attached. Either a sibling
|
|
9
|
+
* {@link VolumeBuilderResult} (the common composed-system case) or a
|
|
10
|
+
* concrete {@link IVolume} (for externally-managed volumes).
|
|
11
|
+
*/
|
|
12
|
+
export type AttachVolumeRef = Resolvable<VolumeBuilderResult> | Resolvable<IVolume>;
|
|
13
|
+
/**
|
|
14
|
+
* Configuration for a single `attachVolume` call.
|
|
15
|
+
*/
|
|
16
|
+
export interface AttachVolumeOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Linux device name to attach the volume as (e.g. `/dev/sdf`).
|
|
19
|
+
*
|
|
20
|
+
* The Linux kernel may rename this to `xvdf` etc. on the instance —
|
|
21
|
+
* resolve mounts via UUID rather than the device path.
|
|
22
|
+
*
|
|
23
|
+
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
|
|
24
|
+
*/
|
|
25
|
+
device: string;
|
|
26
|
+
/**
|
|
27
|
+
* Configuration for the per-attachment recommended alarms.
|
|
28
|
+
*
|
|
29
|
+
* @default - alarms enabled with the defaults in
|
|
30
|
+
* {@link VOLUME_ATTACHMENT_ALARM_DEFAULTS}.
|
|
31
|
+
*/
|
|
32
|
+
recommendedAlarms?: VolumeAttachmentAlarmConfig | false;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Internal config captured by {@link IInstanceBuilder.attachVolume} and
|
|
36
|
+
* forwarded to {@link createVolumeAttachments} at build time.
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
export interface PendingVolumeAttachment {
|
|
41
|
+
key: string;
|
|
42
|
+
volumeRef: AttachVolumeRef;
|
|
43
|
+
options: AttachVolumeOptions;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Creates a {@link CfnVolumeAttachment} for each pending attachment and
|
|
47
|
+
* (when configured) the per-attachment recommended alarms. Synth-time AZ
|
|
48
|
+
* alignment is validated when both the volume's AZ and the instance's
|
|
49
|
+
* effective AZ are concrete strings.
|
|
50
|
+
*
|
|
51
|
+
* @returns The created `CfnVolumeAttachment`s keyed by attachment key,
|
|
52
|
+
* plus the per-attachment alarms keyed by `${attachmentKey}.${alarmKey}`
|
|
53
|
+
* so they can be flat-merged into the instance's `alarms` record.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createVolumeAttachments(scope: IConstruct, id: string, instance: Instance, instanceProps: InstanceProps, attachments: PendingVolumeAttachment[], context?: Record<string, object>): {
|
|
56
|
+
attachments: Record<string, CfnVolumeAttachment>;
|
|
57
|
+
alarms: Record<string, Alarm>;
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=instance-volume-attachments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-volume-attachments.d.ts","sourceRoot":"","sources":["../src/instance-volume-attachments.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,KAAK,EAAqC,MAAM,4BAA4B,CAAC;AAC3F,OAAO,EACL,mBAAmB,EACnB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,OAAO,EAEb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAM1F;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,mBAAmB,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;AAEpF;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;OAOG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,2BAA2B,GAAG,KAAK,CAAC;CACzD;AAED;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,eAAe,CAAC;IAC3B,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAsFD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,uBAAuB,EAAE,EACtC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B;IAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;CAAE,CAwCrF"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Duration, Token } from "aws-cdk-lib";
|
|
2
|
+
import { ComparisonOperator, Metric, Stats } from "aws-cdk-lib/aws-cloudwatch";
|
|
3
|
+
import { CfnVolumeAttachment, } from "aws-cdk-lib/aws-ec2";
|
|
4
|
+
import { resolve } from "@composurecdk/core";
|
|
5
|
+
import { createAlarms, resolveAlarmConfig } from "@composurecdk/cloudwatch";
|
|
6
|
+
import { VOLUME_ATTACHMENT_ALARM_DEFAULTS } from "./instance-volume-attachment-defaults.js";
|
|
7
|
+
const STALLED_IO_PERIOD = Duration.minutes(1);
|
|
8
|
+
const STALLED_IO_PERIOD_LABEL = `${String(STALLED_IO_PERIOD.toMinutes())} minute`;
|
|
9
|
+
function resolveInstanceAz(instanceProps) {
|
|
10
|
+
if (instanceProps.availabilityZone && !Token.isUnresolved(instanceProps.availabilityZone)) {
|
|
11
|
+
return instanceProps.availabilityZone;
|
|
12
|
+
}
|
|
13
|
+
let selected;
|
|
14
|
+
try {
|
|
15
|
+
selected = instanceProps.vpc.selectSubnets(instanceProps.vpcSubnets);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// selectSubnets() throws for several unrelated reasons (no matching subnets,
|
|
19
|
+
// unresolved tokens, etc.). The validation is best-effort — if we can't
|
|
20
|
+
// resolve a concrete AZ, fall through and let CFN surface the real failure.
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return selected.availabilityZones.find((az) => !Token.isUnresolved(az));
|
|
24
|
+
}
|
|
25
|
+
function unwrapVolume(resolved) {
|
|
26
|
+
if ("volumeId" in resolved) {
|
|
27
|
+
return resolved;
|
|
28
|
+
}
|
|
29
|
+
return resolved.volume;
|
|
30
|
+
}
|
|
31
|
+
function volumeAttachmentMetric(volume, instance, metricName, statistic, period) {
|
|
32
|
+
return new Metric({
|
|
33
|
+
namespace: "AWS/EBS",
|
|
34
|
+
metricName,
|
|
35
|
+
dimensionsMap: {
|
|
36
|
+
VolumeId: volume.volumeId,
|
|
37
|
+
InstanceId: instance.instanceId,
|
|
38
|
+
},
|
|
39
|
+
statistic,
|
|
40
|
+
period,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function resolveVolumeAttachmentAlarmDefinitions(attachmentKey, volume, instance, config) {
|
|
44
|
+
if (config === false)
|
|
45
|
+
return [];
|
|
46
|
+
const enabled = config?.enabled ?? VOLUME_ATTACHMENT_ALARM_DEFAULTS.enabled;
|
|
47
|
+
if (!enabled)
|
|
48
|
+
return [];
|
|
49
|
+
if (config?.volumeStalledIo === false)
|
|
50
|
+
return [];
|
|
51
|
+
const cfg = resolveAlarmConfig(config?.volumeStalledIo, VOLUME_ATTACHMENT_ALARM_DEFAULTS.volumeStalledIo);
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
key: `${attachmentKey}.volumeStalledIo`,
|
|
55
|
+
alarmName: cfg.alarmName,
|
|
56
|
+
metric: volumeAttachmentMetric(volume, instance, "VolumeStalledIOCheck", Stats.MAXIMUM, STALLED_IO_PERIOD),
|
|
57
|
+
threshold: cfg.threshold,
|
|
58
|
+
comparisonOperator: ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
|
|
59
|
+
evaluationPeriods: cfg.evaluationPeriods,
|
|
60
|
+
datapointsToAlarm: cfg.datapointsToAlarm,
|
|
61
|
+
treatMissingData: cfg.treatMissingData,
|
|
62
|
+
description: `EBS volume attachment "${attachmentKey}" is reporting a stalled I/O condition. ` +
|
|
63
|
+
`Threshold: >= ${String(cfg.threshold)} (max) over ${String(cfg.evaluationPeriods)} x ${STALLED_IO_PERIOD_LABEL}. ` +
|
|
64
|
+
`Note: VolumeStalledIOCheck is published only for Nitro-instance attachments.`,
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Creates a {@link CfnVolumeAttachment} for each pending attachment and
|
|
70
|
+
* (when configured) the per-attachment recommended alarms. Synth-time AZ
|
|
71
|
+
* alignment is validated when both the volume's AZ and the instance's
|
|
72
|
+
* effective AZ are concrete strings.
|
|
73
|
+
*
|
|
74
|
+
* @returns The created `CfnVolumeAttachment`s keyed by attachment key,
|
|
75
|
+
* plus the per-attachment alarms keyed by `${attachmentKey}.${alarmKey}`
|
|
76
|
+
* so they can be flat-merged into the instance's `alarms` record.
|
|
77
|
+
*/
|
|
78
|
+
export function createVolumeAttachments(scope, id, instance, instanceProps, attachments, context) {
|
|
79
|
+
const attachmentRecords = {};
|
|
80
|
+
const alarmDefinitions = [];
|
|
81
|
+
// Resolve the instance AZ once — it is the same for every attachment.
|
|
82
|
+
const instanceAz = attachments.length > 0 ? resolveInstanceAz(instanceProps) : undefined;
|
|
83
|
+
for (const pending of attachments) {
|
|
84
|
+
const resolved = resolve(pending.volumeRef, context);
|
|
85
|
+
const volume = unwrapVolume(resolved);
|
|
86
|
+
if (instanceAz !== undefined && !Token.isUnresolved(volume.availabilityZone)) {
|
|
87
|
+
if (volume.availabilityZone !== instanceAz) {
|
|
88
|
+
throw new Error(`attachVolume "${pending.key}": volume is in availability zone "${volume.availabilityZone}" ` +
|
|
89
|
+
`but the instance is in "${instanceAz}". ` +
|
|
90
|
+
`EBS volumes can only attach to instances in the same AZ.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const attachment = new CfnVolumeAttachment(scope, `${id}${pending.key}Attachment`, {
|
|
94
|
+
device: pending.options.device,
|
|
95
|
+
instanceId: instance.instanceId,
|
|
96
|
+
volumeId: volume.volumeId,
|
|
97
|
+
});
|
|
98
|
+
attachmentRecords[pending.key] = attachment;
|
|
99
|
+
alarmDefinitions.push(...resolveVolumeAttachmentAlarmDefinitions(pending.key, volume, instance, pending.options.recommendedAlarms));
|
|
100
|
+
}
|
|
101
|
+
const alarms = alarmDefinitions.length > 0 ? createAlarms(scope, id, alarmDefinitions) : {};
|
|
102
|
+
return { attachments: attachmentRecords, alarms };
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=instance-volume-attachments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-volume-attachments.js","sourceRoot":"","sources":["../src/instance-volume-attachments.ts"],"names":[],"mappings":"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"}
|