@intentius/chant-lexicon-aws 0.1.1 → 0.1.8
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/integrity.json +40 -34
- package/dist/manifest.json +1 -1
- package/dist/meta.json +3755 -690
- package/dist/rules/waw032.ts +52 -0
- package/dist/rules/waw033.ts +86 -0
- package/dist/rules/waw034.ts +63 -0
- package/dist/rules/waw035.ts +71 -0
- package/dist/rules/waw036.ts +88 -0
- package/dist/rules/waw037.ts +81 -0
- package/dist/types/index.d.ts +5043 -483
- package/package.json +7 -7
- package/src/actions/actions.test.ts +1 -1
- package/src/codegen/__snapshots__/snapshot.test.ts.snap +45 -45
- package/src/codegen/docs-links.test.ts +3 -3
- package/src/codegen/docs.ts +15 -10
- package/src/codegen/generate-cli.ts +2 -2
- package/src/codegen/generate.test.ts +1 -1
- package/src/codegen/idempotency.test.ts +1 -1
- package/src/codegen/package.test.ts +1 -1
- package/src/codegen/package.ts +2 -7
- package/src/codegen/snapshot.test.ts +1 -1
- package/src/codegen/typecheck.test.ts +1 -1
- package/src/composites/composites.test.ts +66 -1
- package/src/composites/ec2-instance-role.ts +39 -0
- package/src/composites/fargate-service.ts +9 -0
- package/src/composites/index.ts +6 -0
- package/src/composites/lambda-function.ts +2 -1
- package/src/composites/minimal-vpc.ts +71 -0
- package/src/composites/solr-fargate-service.ts +42 -0
- package/src/coverage.test.ts +1 -1
- package/src/default-tags.test.ts +1 -1
- package/src/generated/index.d.ts +5043 -483
- package/src/generated/index.ts +392 -46
- package/src/generated/lexicon-aws.json +3755 -690
- package/src/import/generator.test.ts +1 -1
- package/src/import/generator.ts +1 -1
- package/src/import/parser.test.ts +1 -1
- package/src/import/roundtrip-fixtures.test.ts +4 -4
- package/src/import/roundtrip.test.ts +1 -1
- package/src/index.ts +2 -0
- package/src/integration.test.ts +1 -1
- package/src/intrinsics.test.ts +1 -1
- package/src/lint/post-synth/ext001.test.ts +1 -1
- package/src/lint/post-synth/post-synth.test.ts +1 -1
- package/src/lint/post-synth/waw013.test.ts +1 -1
- package/src/lint/post-synth/waw014.test.ts +1 -1
- package/src/lint/post-synth/waw015.test.ts +1 -1
- package/src/lint/post-synth/waw016.test.ts +1 -1
- package/src/lint/post-synth/waw017.test.ts +1 -1
- package/src/lint/post-synth/waw018.test.ts +1 -1
- package/src/lint/post-synth/waw019.test.ts +1 -1
- package/src/lint/post-synth/waw020.test.ts +1 -1
- package/src/lint/post-synth/waw021.test.ts +1 -1
- package/src/lint/post-synth/waw022.test.ts +1 -1
- package/src/lint/post-synth/waw023.test.ts +1 -1
- package/src/lint/post-synth/waw024.test.ts +1 -1
- package/src/lint/post-synth/waw025.test.ts +1 -1
- package/src/lint/post-synth/waw026.test.ts +1 -1
- package/src/lint/post-synth/waw027.test.ts +1 -1
- package/src/lint/post-synth/waw028.test.ts +1 -1
- package/src/lint/post-synth/waw029.test.ts +1 -1
- package/src/lint/post-synth/waw030.test.ts +1 -1
- package/src/lint/post-synth/waw031.test.ts +1 -1
- package/src/lint/post-synth/waw032.test.ts +83 -0
- package/src/lint/post-synth/waw032.ts +52 -0
- package/src/lint/post-synth/waw033.test.ts +68 -0
- package/src/lint/post-synth/waw033.ts +86 -0
- package/src/lint/post-synth/waw034.test.ts +54 -0
- package/src/lint/post-synth/waw034.ts +63 -0
- package/src/lint/post-synth/waw035.test.ts +74 -0
- package/src/lint/post-synth/waw035.ts +71 -0
- package/src/lint/post-synth/waw036.test.ts +217 -0
- package/src/lint/post-synth/waw036.ts +88 -0
- package/src/lint/post-synth/waw037.test.ts +155 -0
- package/src/lint/post-synth/waw037.ts +81 -0
- package/src/lint/rules/rules.test.ts +1 -1
- package/src/lsp/completions.test.ts +1 -1
- package/src/lsp/hover.test.ts +1 -1
- package/src/nested-stack-integration.test.ts +2 -2
- package/src/nested-stack.test.ts +1 -1
- package/src/package-cli.ts +3 -3
- package/src/plugin.test.ts +5 -5
- package/src/plugin.ts +6 -6
- package/src/pseudo.test.ts +1 -1
- package/src/serializer.test.ts +1 -1
- package/src/spec/fetch.test.ts +1 -1
- package/src/spec/parse.test.ts +1 -1
- package/src/spec/parse.ts +6 -0
- package/src/validate-cli.ts +2 -2
- package/src/validate.test.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-aws",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "AWS CloudFormation lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -36,18 +36,18 @@
|
|
|
36
36
|
"./types": "./dist/types/index.d.ts"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
|
-
"generate": "
|
|
40
|
-
"bundle": "
|
|
41
|
-
"validate": "
|
|
42
|
-
"docs": "
|
|
43
|
-
"prepack": "
|
|
39
|
+
"generate": "tsx src/codegen/generate-cli.ts",
|
|
40
|
+
"bundle": "tsx src/package-cli.ts",
|
|
41
|
+
"validate": "tsx src/validate-cli.ts",
|
|
42
|
+
"docs": "tsx src/codegen/docs-cli.ts",
|
|
43
|
+
"prepack": "npm run generate && npm run bundle && npm run validate"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"fflate": "^0.8.2",
|
|
47
47
|
"js-yaml": "^4.1.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@intentius/chant": "
|
|
50
|
+
"@intentius/chant": "*",
|
|
51
51
|
"typescript": "^5.9.3"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
@@ -1,6 +1,24 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`snapshot tests Bucket
|
|
3
|
+
exports[`snapshot tests > Bucket .d.ts class declaration 1`] = `
|
|
4
|
+
"export declare class Bucket {
|
|
5
|
+
constructor(props: {
|
|
6
|
+
/** A name for the bucket. */
|
|
7
|
+
BucketName: string;
|
|
8
|
+
/** The Amazon Resource Name (ARN) of the bucket. */
|
|
9
|
+
Arn?: string;
|
|
10
|
+
/** Specifies default encryption for a bucket. */
|
|
11
|
+
BucketEncryption?: Bucket_BucketEncryption;
|
|
12
|
+
/** An arbitrary set of tags (key-value pairs) for this S3 bucket. */
|
|
13
|
+
Tags?: Bucket_Tag[];
|
|
14
|
+
/** Enables multiple versions of all objects in this bucket. */
|
|
15
|
+
VersioningConfiguration?: Bucket_VersioningConfiguration;
|
|
16
|
+
}, attributes?: CFResourceAttributes);
|
|
17
|
+
readonly Arn: string;
|
|
18
|
+
}"
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
exports[`snapshot tests > Bucket lexicon entry 1`] = `
|
|
4
22
|
{
|
|
5
23
|
"attrs": {
|
|
6
24
|
"Arn": "Arn",
|
|
@@ -24,7 +42,29 @@ exports[`snapshot tests Bucket lexicon entry 1`] = `
|
|
|
24
42
|
}
|
|
25
43
|
`;
|
|
26
44
|
|
|
27
|
-
exports[`snapshot tests Function
|
|
45
|
+
exports[`snapshot tests > Function .d.ts class declaration 1`] = `
|
|
46
|
+
"export declare class Function {
|
|
47
|
+
constructor(props: {
|
|
48
|
+
/** The code for the function. */
|
|
49
|
+
Code: Function_Code;
|
|
50
|
+
/** The ARN of the function's execution role. */
|
|
51
|
+
Role: string;
|
|
52
|
+
/** The ARN of the function. */
|
|
53
|
+
Arn?: string;
|
|
54
|
+
/** The name of the Lambda function. */
|
|
55
|
+
FunctionName?: string;
|
|
56
|
+
/** The name of the method within your code that Lambda calls to run your function. */
|
|
57
|
+
Handler?: string;
|
|
58
|
+
/** The amount of memory available to the function at runtime. */
|
|
59
|
+
MemorySize?: number;
|
|
60
|
+
/** The identifier of the function's runtime. */
|
|
61
|
+
Runtime?: "java17" | "java21" | "nodejs18.x" | "nodejs20.x" | "python3.11" | "python3.12";
|
|
62
|
+
}, attributes?: CFResourceAttributes);
|
|
63
|
+
readonly Arn: string;
|
|
64
|
+
}"
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
exports[`snapshot tests > Function lexicon entry 1`] = `
|
|
28
68
|
{
|
|
29
69
|
"attrs": {
|
|
30
70
|
"Arn": "Arn",
|
|
@@ -76,7 +116,7 @@ exports[`snapshot tests Function lexicon entry 1`] = `
|
|
|
76
116
|
}
|
|
77
117
|
`;
|
|
78
118
|
|
|
79
|
-
exports[`snapshot tests Role lexicon entry 1`] = `
|
|
119
|
+
exports[`snapshot tests > Role lexicon entry 1`] = `
|
|
80
120
|
{
|
|
81
121
|
"attrs": {
|
|
82
122
|
"Arn": "Arn",
|
|
@@ -98,7 +138,7 @@ exports[`snapshot tests Role lexicon entry 1`] = `
|
|
|
98
138
|
}
|
|
99
139
|
`;
|
|
100
140
|
|
|
101
|
-
exports[`snapshot tests generated resource names 1`] = `
|
|
141
|
+
exports[`snapshot tests > generated resource names 1`] = `
|
|
102
142
|
[
|
|
103
143
|
"Api",
|
|
104
144
|
"Api_Auth",
|
|
@@ -155,43 +195,3 @@ exports[`snapshot tests generated resource names 1`] = `
|
|
|
155
195
|
"VpcConfig",
|
|
156
196
|
]
|
|
157
197
|
`;
|
|
158
|
-
|
|
159
|
-
exports[`snapshot tests Bucket .d.ts class declaration 1`] = `
|
|
160
|
-
"export declare class Bucket {
|
|
161
|
-
constructor(props: {
|
|
162
|
-
/** A name for the bucket. */
|
|
163
|
-
BucketName: string;
|
|
164
|
-
/** The Amazon Resource Name (ARN) of the bucket. */
|
|
165
|
-
Arn?: string;
|
|
166
|
-
/** Specifies default encryption for a bucket. */
|
|
167
|
-
BucketEncryption?: Bucket_BucketEncryption;
|
|
168
|
-
/** An arbitrary set of tags (key-value pairs) for this S3 bucket. */
|
|
169
|
-
Tags?: Bucket_Tag[];
|
|
170
|
-
/** Enables multiple versions of all objects in this bucket. */
|
|
171
|
-
VersioningConfiguration?: Bucket_VersioningConfiguration;
|
|
172
|
-
}, attributes?: CFResourceAttributes);
|
|
173
|
-
readonly Arn: string;
|
|
174
|
-
}"
|
|
175
|
-
`;
|
|
176
|
-
|
|
177
|
-
exports[`snapshot tests Function .d.ts class declaration 1`] = `
|
|
178
|
-
"export declare class Function {
|
|
179
|
-
constructor(props: {
|
|
180
|
-
/** The code for the function. */
|
|
181
|
-
Code: Function_Code;
|
|
182
|
-
/** The ARN of the function's execution role. */
|
|
183
|
-
Role: string;
|
|
184
|
-
/** The ARN of the function. */
|
|
185
|
-
Arn?: string;
|
|
186
|
-
/** The name of the Lambda function. */
|
|
187
|
-
FunctionName?: string;
|
|
188
|
-
/** The name of the method within your code that Lambda calls to run your function. */
|
|
189
|
-
Handler?: string;
|
|
190
|
-
/** The amount of memory available to the function at runtime. */
|
|
191
|
-
MemorySize?: number;
|
|
192
|
-
/** The identifier of the function's runtime. */
|
|
193
|
-
Runtime?: "java17" | "java21" | "nodejs18.x" | "nodejs20.x" | "python3.11" | "python3.12";
|
|
194
|
-
}, attributes?: CFResourceAttributes);
|
|
195
|
-
readonly Arn: string;
|
|
196
|
-
}"
|
|
197
|
-
`;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { describe, test, expect } from "
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
3
3
|
import { join, basename } from "path";
|
|
4
4
|
|
|
5
|
-
const docsDir = join(import.meta.
|
|
6
|
-
const docsSource = join(import.meta.
|
|
5
|
+
const docsDir = join(import.meta.dirname, "..", "..", "docs", "src", "content", "docs");
|
|
6
|
+
const docsSource = join(import.meta.dirname, "docs.ts");
|
|
7
7
|
const docsExist = existsSync(docsDir);
|
|
8
8
|
|
|
9
9
|
/**
|
package/src/codegen/docs.ts
CHANGED
|
@@ -488,12 +488,16 @@ When you need to split resources into a separate CloudFormation template, the le
|
|
|
488
488
|
title: "Nested Stacks",
|
|
489
489
|
sidebar: false,
|
|
490
490
|
description: "Splitting resources into child CloudFormation templates with automatic cross-stack reference wiring",
|
|
491
|
-
content: `
|
|
491
|
+
content: `import Diagram from '../../components/Diagram.astro';
|
|
492
|
+
|
|
493
|
+
CloudFormation nested stacks (\`AWS::CloudFormation::Stack\`) let you decompose large templates into smaller, reusable child templates. The AWS lexicon's \`nestedStack()\` function references a **child project directory** — a subdirectory that builds independently to a valid CloudFormation template.
|
|
492
494
|
|
|
493
495
|
:::caution[Consider alternatives first]
|
|
494
|
-
Nested stacks add deployment complexity: child templates must be uploaded to S3, rollbacks are all-or-nothing at the parent level, drift detection doesn't recurse into children, and debugging failures requires drilling into child stack events. For
|
|
496
|
+
Nested stacks add deployment complexity: child templates must be uploaded to S3, rollbacks are all-or-nothing at the parent level, drift detection doesn't recurse into children, and debugging failures requires drilling into child stack events. For splitting a large project into separately-deployed pieces, prefer [Multi-Stack Projects](/chant/guide/multi-stack/) — each \`src/\` subdirectory becomes an independent stack with no parent orchestration and no S3 upload step. Nested stacks are supported for specific cases — exceeding CloudFormation's 500-resource limit inside a single deployable unit, or packaging reusable infrastructure for other teams to deploy as a black box — but are not the recommended default.
|
|
495
497
|
:::
|
|
496
498
|
|
|
499
|
+
<Diagram name="nested-stacks" alt="Parent and child project source directories producing separate CloudFormation templates, both uploaded to S3" caption="Nested stack source layout and output" />
|
|
500
|
+
|
|
497
501
|
## Project structure
|
|
498
502
|
|
|
499
503
|
A nested stack is a child project — a subdirectory with its own resource files and explicit \`stackOutput()\` declarations:
|
|
@@ -611,14 +615,15 @@ Three lint rules help catch common nested stack issues:
|
|
|
611
615
|
|
|
612
616
|
## When to use nested stacks
|
|
613
617
|
|
|
614
|
-
|
|
618
|
+
Most splitting use cases are better served by other mechanisms:
|
|
615
619
|
|
|
616
|
-
**
|
|
617
|
-
-
|
|
618
|
-
- You're packaging reusable infrastructure for other teams to deploy as a black box
|
|
619
|
-
- You need independent update/rollback boundaries (rare — this usually means the resources should be separate stacks entirely)
|
|
620
|
+
- **Splitting a large project into separately-deployed pieces** → [Multi-Stack Projects](/chant/guide/multi-stack/). Organize \`src/\` into subdirectories — each becomes an independent stack, deployed on its own, with cross-stack references resolved via standard CloudFormation \`Export\`/\`ImportValue\`. No parent template, no S3 upload of children, no \`TemplateBasePath\` parameter.
|
|
621
|
+
- **Reusing resource patterns within a single template** → [Composites](../composites/). Composites expand into the same template and deploy atomically — they're about DRY, not splitting.
|
|
620
622
|
|
|
621
|
-
|
|
623
|
+
**Use nested stacks only when:**
|
|
624
|
+
- A single deployable unit exceeds CloudFormation's 500-resource limit
|
|
625
|
+
- You're packaging reusable infrastructure for other teams to consume as a black box (the parent-child relationship is load-bearing)
|
|
626
|
+
- You need an orchestrator parent that coordinates child lifecycles via CloudFormation Parameters and Outputs specifically`,
|
|
622
627
|
},
|
|
623
628
|
{
|
|
624
629
|
slug: "lint-rules",
|
|
@@ -902,10 +907,10 @@ The \`plugins\` array accepts relative paths. Each plugin module should export a
|
|
|
902
907
|
|
|
903
908
|
\`\`\`bash
|
|
904
909
|
cd examples/lambda-function
|
|
905
|
-
|
|
910
|
+
npm install
|
|
906
911
|
chant build # produces CloudFormation JSON
|
|
907
912
|
chant lint # runs lint rules
|
|
908
|
-
|
|
913
|
+
npx vitest run # run the tests
|
|
909
914
|
\`\`\`
|
|
910
915
|
|
|
911
916
|
## Lambda Function
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
2
|
/**
|
|
3
|
-
* Thin entry point for `
|
|
3
|
+
* Thin entry point for `npm run generate` in lexicon-aws.
|
|
4
4
|
*/
|
|
5
5
|
import { generate, writeGeneratedFiles } from "./generate";
|
|
6
6
|
import { dirname } from "path";
|
package/src/codegen/package.ts
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* with AWS-specific manifest building and skill collection.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createRequire } from "module";
|
|
7
6
|
import { readFileSync } from "fs";
|
|
8
|
-
|
|
7
|
+
import { awsPlugin } from "../plugin";
|
|
9
8
|
import { join, dirname } from "path";
|
|
10
9
|
import { fileURLToPath } from "url";
|
|
11
10
|
import type { IntrinsicDef } from "@intentius/chant/lexicon";
|
|
@@ -43,8 +42,6 @@ export async function packageLexicon(opts: PackageOptions = {}): Promise<Package
|
|
|
43
42
|
|
|
44
43
|
buildManifest: (_genResult) => {
|
|
45
44
|
// Lazy-import to avoid circular dependency
|
|
46
|
-
const { awsPlugin } = require("../plugin");
|
|
47
|
-
|
|
48
45
|
const intrinsics: IntrinsicDef[] = (awsPlugin.intrinsics?.() ?? []).map(
|
|
49
46
|
(i: { name: string; description: string }) => ({
|
|
50
47
|
name: i.name,
|
|
@@ -73,9 +70,7 @@ export async function packageLexicon(opts: PackageOptions = {}): Promise<Package
|
|
|
73
70
|
|
|
74
71
|
srcDir: pkgDir,
|
|
75
72
|
|
|
76
|
-
collectSkills: () => {
|
|
77
|
-
const { awsPlugin } = require("../plugin");
|
|
78
|
-
const skillDefs = awsPlugin.skills?.() ?? [];
|
|
73
|
+
collectSkills: () => { const skillDefs = awsPlugin.skills?.() ?? [];
|
|
79
74
|
return collectSkills(skillDefs);
|
|
80
75
|
},
|
|
81
76
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach } from "
|
|
1
|
+
import { describe, test, expect, beforeEach } from "vitest";
|
|
2
2
|
import { expandComposite, CompositeRegistry, isCompositeInstance } from "@intentius/chant";
|
|
3
3
|
import { AttrRef } from "@intentius/chant/attrref";
|
|
4
4
|
import { LambdaFunction, LambdaNode, LambdaPython, NodeLambda, PythonLambda } from "./lambda-function";
|
|
@@ -14,6 +14,8 @@ import { FargateAlb } from "./fargate-alb";
|
|
|
14
14
|
import { AlbShared } from "./alb-shared";
|
|
15
15
|
import { FargateService } from "./fargate-service";
|
|
16
16
|
import { RdsInstance } from "./rds-instance";
|
|
17
|
+
import { Ec2InstanceRole } from "./ec2-instance-role";
|
|
18
|
+
import { MinimalVpc } from "./minimal-vpc";
|
|
17
19
|
|
|
18
20
|
const baseProps = {
|
|
19
21
|
name: "TestFunc",
|
|
@@ -865,3 +867,66 @@ describe("per-member defaults", () => {
|
|
|
865
867
|
expect(funcPropsA.Timeout).toBe(funcPropsB.Timeout);
|
|
866
868
|
});
|
|
867
869
|
});
|
|
870
|
+
|
|
871
|
+
describe("Ec2InstanceRole", () => {
|
|
872
|
+
test("returns role and instanceProfile members", () => {
|
|
873
|
+
const instance = Ec2InstanceRole({});
|
|
874
|
+
expect(instance.role).toBeDefined();
|
|
875
|
+
expect(instance.instanceProfile).toBeDefined();
|
|
876
|
+
expect(Object.keys(instance.members)).toEqual(["role", "instanceProfile"]);
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
test("role has EC2 trust policy", () => {
|
|
880
|
+
const instance = Ec2InstanceRole({});
|
|
881
|
+
const roleProps = (instance.role as any).props;
|
|
882
|
+
expect(roleProps.AssumeRolePolicyDocument.Statement[0].Principal.Service).toBe("ec2.amazonaws.com");
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
test("ManagedPolicyArns are passed through", () => {
|
|
886
|
+
const arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore";
|
|
887
|
+
const instance = Ec2InstanceRole({ ManagedPolicyArns: [arn] });
|
|
888
|
+
const roleProps = (instance.role as any).props;
|
|
889
|
+
expect(roleProps.ManagedPolicyArns).toContain(arn);
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
test("expandComposite produces 2 entries", () => {
|
|
893
|
+
const expanded = expandComposite("myRole", Ec2InstanceRole({}));
|
|
894
|
+
expect(expanded.size).toBe(2);
|
|
895
|
+
expect(expanded.has("myRoleRole")).toBe(true);
|
|
896
|
+
expect(expanded.has("myRoleInstanceProfile")).toBe(true);
|
|
897
|
+
});
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
describe("MinimalVpc", () => {
|
|
901
|
+
test("returns 8 members", () => {
|
|
902
|
+
const instance = MinimalVpc({});
|
|
903
|
+
expect(Object.keys(instance.members)).toEqual([
|
|
904
|
+
"vpc", "subnet", "igw", "igwAttachment", "routeTable", "defaultRoute", "subnetRta", "securityGroup",
|
|
905
|
+
]);
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
test("subnet uses Select/GetAZs for AZ (not a plain string)", () => {
|
|
909
|
+
const instance = MinimalVpc({});
|
|
910
|
+
const subnetProps = (instance.subnet as any).props;
|
|
911
|
+
expect(typeof subnetProps.AvailabilityZone).not.toBe("string");
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
test("vpc cidr defaults to 10.0.0.0/24", () => {
|
|
915
|
+
const instance = MinimalVpc({});
|
|
916
|
+
const vpcProps = (instance.vpc as any).props;
|
|
917
|
+
expect(vpcProps.CidrBlock).toBe("10.0.0.0/24");
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
test("custom cidr is respected", () => {
|
|
921
|
+
const instance = MinimalVpc({ cidr: "192.168.0.0/16", subnetCidr: "192.168.1.0/24" });
|
|
922
|
+
const vpcProps = (instance.vpc as any).props;
|
|
923
|
+
const subnetProps = (instance.subnet as any).props;
|
|
924
|
+
expect(vpcProps.CidrBlock).toBe("192.168.0.0/16");
|
|
925
|
+
expect(subnetProps.CidrBlock).toBe("192.168.1.0/24");
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
test("expandComposite produces 8 entries", () => {
|
|
929
|
+
const expanded = expandComposite("net", MinimalVpc({}));
|
|
930
|
+
expect(expanded.size).toBe(8);
|
|
931
|
+
});
|
|
932
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
2
|
+
import { Role, InstanceProfile } from "../generated";
|
|
3
|
+
import { Ref } from "../intrinsics";
|
|
4
|
+
|
|
5
|
+
export interface Ec2InstanceRoleProps {
|
|
6
|
+
ManagedPolicyArns?: string[];
|
|
7
|
+
Policies?: ConstructorParameters<typeof Role>[0]["Policies"];
|
|
8
|
+
defaults?: {
|
|
9
|
+
role?: Partial<ConstructorParameters<typeof Role>[0]>;
|
|
10
|
+
instanceProfile?: Partial<ConstructorParameters<typeof InstanceProfile>[0]>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const EC2_ASSUME_ROLE = {
|
|
15
|
+
Version: "2012-10-17" as const,
|
|
16
|
+
Statement: [
|
|
17
|
+
{
|
|
18
|
+
Effect: "Allow" as const,
|
|
19
|
+
Principal: { Service: "ec2.amazonaws.com" },
|
|
20
|
+
Action: "sts:AssumeRole",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const Ec2InstanceRole = Composite<Ec2InstanceRoleProps>((props) => {
|
|
26
|
+
const { defaults } = props;
|
|
27
|
+
|
|
28
|
+
const role = new Role(mergeDefaults({
|
|
29
|
+
AssumeRolePolicyDocument: EC2_ASSUME_ROLE,
|
|
30
|
+
ManagedPolicyArns: props.ManagedPolicyArns ?? [],
|
|
31
|
+
Policies: props.Policies ?? [],
|
|
32
|
+
}, defaults?.role));
|
|
33
|
+
|
|
34
|
+
const instanceProfile = new InstanceProfile(mergeDefaults({
|
|
35
|
+
Roles: [Ref(role)],
|
|
36
|
+
}, defaults?.instanceProfile));
|
|
37
|
+
|
|
38
|
+
return { role, instanceProfile };
|
|
39
|
+
}, "Ec2InstanceRole");
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
TaskDefinition_KeyValuePair,
|
|
13
13
|
TaskDefinition_EFSVolumeConfiguration,
|
|
14
14
|
TaskDefinition_Volume,
|
|
15
|
+
TaskDefinition_Ulimit,
|
|
15
16
|
TargetGroup,
|
|
16
17
|
ListenerRule,
|
|
17
18
|
ListenerRule_Action,
|
|
@@ -74,6 +75,9 @@ export interface FargateServiceProps {
|
|
|
74
75
|
privateSubnetIds: string[];
|
|
75
76
|
healthCheckPath?: string;
|
|
76
77
|
|
|
78
|
+
// Ulimits (container-level)
|
|
79
|
+
ulimits?: Array<{ name: string; softLimit: number; hardLimit: number }>;
|
|
80
|
+
|
|
77
81
|
// IAM
|
|
78
82
|
ManagedPolicyArns?: string[];
|
|
79
83
|
Policies?: InstanceType<typeof Role_Policy>[];
|
|
@@ -175,6 +179,11 @@ export const FargateService = Composite<FargateServiceProps>((props) => {
|
|
|
175
179
|
Environment: environmentVars.length > 0 ? environmentVars : undefined,
|
|
176
180
|
Command: props.command,
|
|
177
181
|
MountPoints: allMountPoints.length > 0 ? allMountPoints : undefined,
|
|
182
|
+
Ulimits: props.ulimits?.map(u => new TaskDefinition_Ulimit({
|
|
183
|
+
Name: u.name,
|
|
184
|
+
SoftLimit: u.softLimit,
|
|
185
|
+
HardLimit: u.hardLimit,
|
|
186
|
+
})),
|
|
178
187
|
});
|
|
179
188
|
|
|
180
189
|
// Task definition
|
package/src/composites/index.ts
CHANGED
|
@@ -26,3 +26,9 @@ export { RdsInstance, RdsInstance as RdsPostgres } from "./rds-instance";
|
|
|
26
26
|
export type { RdsInstanceProps, RdsInstanceProps as RdsPostgresProps } from "./rds-instance";
|
|
27
27
|
export { EfsWithAccessPoint } from "./efs-with-access-point";
|
|
28
28
|
export type { EfsWithAccessPointProps } from "./efs-with-access-point";
|
|
29
|
+
export { SolrFargateService } from "./solr-fargate-service";
|
|
30
|
+
export type { SolrFargateServiceProps } from "./solr-fargate-service";
|
|
31
|
+
export { Ec2InstanceRole } from "./ec2-instance-role";
|
|
32
|
+
export type { Ec2InstanceRoleProps } from "./ec2-instance-role";
|
|
33
|
+
export { MinimalVpc } from "./minimal-vpc";
|
|
34
|
+
export type { MinimalVpcProps } from "./minimal-vpc";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Composite, withDefaults, mergeDefaults } from "@intentius/chant";
|
|
2
2
|
import { Role, Function, Function_VpcConfig, Role_Policy } from "../generated";
|
|
3
|
+
import { Sub } from "../intrinsics";
|
|
3
4
|
|
|
4
5
|
const lambdaTrustPolicy = {
|
|
5
6
|
Version: "2012-10-17" as const,
|
|
@@ -18,7 +19,7 @@ const VPC_ACCESS_ARN =
|
|
|
18
19
|
"arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole";
|
|
19
20
|
|
|
20
21
|
export interface LambdaFunctionProps {
|
|
21
|
-
name: string
|
|
22
|
+
name: string | ReturnType<typeof Sub>;
|
|
22
23
|
Runtime: string;
|
|
23
24
|
Handler: string;
|
|
24
25
|
Code: ConstructorParameters<typeof Function>[0]["Code"];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
2
|
+
import {
|
|
3
|
+
Vpc,
|
|
4
|
+
Subnet,
|
|
5
|
+
InternetGateway,
|
|
6
|
+
VPCGatewayAttachment,
|
|
7
|
+
RouteTable,
|
|
8
|
+
EC2Route,
|
|
9
|
+
SubnetRouteTableAssociation,
|
|
10
|
+
SecurityGroup,
|
|
11
|
+
} from "../generated";
|
|
12
|
+
import { Select, GetAZs } from "../intrinsics";
|
|
13
|
+
|
|
14
|
+
export interface MinimalVpcProps {
|
|
15
|
+
cidr?: string;
|
|
16
|
+
subnetCidr?: string;
|
|
17
|
+
defaults?: {
|
|
18
|
+
vpc?: Partial<ConstructorParameters<typeof Vpc>[0]>;
|
|
19
|
+
subnet?: Partial<ConstructorParameters<typeof Subnet>[0]>;
|
|
20
|
+
securityGroup?: Partial<ConstructorParameters<typeof SecurityGroup>[0]>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const MinimalVpc = Composite<MinimalVpcProps>((props) => {
|
|
25
|
+
const { defaults } = props;
|
|
26
|
+
const cidr = props.cidr ?? "10.0.0.0/24";
|
|
27
|
+
const subnetCidr = props.subnetCidr ?? "10.0.0.0/25";
|
|
28
|
+
|
|
29
|
+
const vpc = new Vpc(mergeDefaults({
|
|
30
|
+
CidrBlock: cidr,
|
|
31
|
+
EnableDnsHostnames: true,
|
|
32
|
+
EnableDnsSupport: true,
|
|
33
|
+
}, defaults?.vpc));
|
|
34
|
+
|
|
35
|
+
const subnet = new Subnet(mergeDefaults({
|
|
36
|
+
VpcId: vpc.VpcId,
|
|
37
|
+
CidrBlock: subnetCidr,
|
|
38
|
+
AvailabilityZone: Select(0, GetAZs("")),
|
|
39
|
+
MapPublicIpOnLaunch: true,
|
|
40
|
+
}, defaults?.subnet));
|
|
41
|
+
|
|
42
|
+
const igw = new InternetGateway({});
|
|
43
|
+
|
|
44
|
+
const igwAttachment = new VPCGatewayAttachment({
|
|
45
|
+
VpcId: vpc.VpcId,
|
|
46
|
+
InternetGatewayId: igw.InternetGatewayId,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const routeTable = new RouteTable({ VpcId: vpc.VpcId });
|
|
50
|
+
|
|
51
|
+
const defaultRoute = new EC2Route(
|
|
52
|
+
{
|
|
53
|
+
RouteTableId: routeTable.RouteTableId,
|
|
54
|
+
DestinationCidrBlock: "0.0.0.0/0",
|
|
55
|
+
GatewayId: igw.InternetGatewayId,
|
|
56
|
+
},
|
|
57
|
+
{ DependsOn: [igwAttachment] },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const subnetRta = new SubnetRouteTableAssociation({
|
|
61
|
+
SubnetId: subnet.SubnetId,
|
|
62
|
+
RouteTableId: routeTable.RouteTableId,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const securityGroup = new SecurityGroup(mergeDefaults({
|
|
66
|
+
GroupDescription: "MinimalVpc default security group",
|
|
67
|
+
VpcId: vpc.VpcId,
|
|
68
|
+
}, defaults?.securityGroup));
|
|
69
|
+
|
|
70
|
+
return { vpc, subnet, igw, igwAttachment, routeTable, defaultRoute, subnetRta, securityGroup };
|
|
71
|
+
}, "MinimalVpc");
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Composite } from "@intentius/chant";
|
|
2
|
+
import { FargateService, FargateServiceProps } from "./fargate-service";
|
|
3
|
+
|
|
4
|
+
export interface SolrFargateServiceProps extends FargateServiceProps {
|
|
5
|
+
/**
|
|
6
|
+
* JVM heap size passed as SOLR_HEAP. Defaults to 45% of task memory.
|
|
7
|
+
* Examples: "1843m", "4g". Must not exceed 50% of task memory.
|
|
8
|
+
*/
|
|
9
|
+
solrHeap?: string;
|
|
10
|
+
/**
|
|
11
|
+
* GC tuning string passed as GC_TUNE.
|
|
12
|
+
* Default: "-XX:+UseG1GC -XX:MaxGCPauseMillis=200"
|
|
13
|
+
*/
|
|
14
|
+
gcOpts?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const SolrFargateService = Composite<SolrFargateServiceProps>((props) => {
|
|
18
|
+
const memoryMb = parseInt(props.memory ?? "4096");
|
|
19
|
+
const solrHeap = props.solrHeap ?? `${Math.floor(memoryMb * 0.45)}m`;
|
|
20
|
+
const gcOpts = props.gcOpts ?? "-XX:+UseG1GC -XX:MaxGCPauseMillis=200";
|
|
21
|
+
|
|
22
|
+
// Solr env defaults — user-supplied environment entries override these
|
|
23
|
+
const solrEnv: Record<string, string> = {
|
|
24
|
+
SOLR_HEAP: solrHeap,
|
|
25
|
+
GC_TUNE: gcOpts,
|
|
26
|
+
SOLR_OPTS: "-XX:-UseLargePages",
|
|
27
|
+
...props.environment,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Solr-specific ulimit default — user-supplied ulimits override
|
|
31
|
+
const solrUlimits = props.ulimits ?? [
|
|
32
|
+
{ name: "nofile", softLimit: 65535, hardLimit: 65535 },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
return FargateService({
|
|
36
|
+
containerPort: 8983,
|
|
37
|
+
healthCheckPath: "/solr/admin/info/health",
|
|
38
|
+
...props,
|
|
39
|
+
environment: solrEnv,
|
|
40
|
+
ulimits: solrUlimits,
|
|
41
|
+
});
|
|
42
|
+
}, "SolrFargateService");
|
package/src/coverage.test.ts
CHANGED
package/src/default-tags.test.ts
CHANGED