@kensio/yulin 1.21.6 → 1.21.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/README.md +22 -2
- package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-creator.js +1 -0
- package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.d.ts +4 -0
- package/dist/service/eventbridge/cfn/bus/sim-cfn-event-bus-properties.js +26 -1
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-creator.js +3 -1
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.d.ts +0 -7
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-properties.js +0 -20
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.d.ts +13 -0
- package/dist/service/eventbridge/cfn/rule/sim-cfn-event-rule-unsimulated-properties.js +59 -0
- package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.d.ts +1 -1
- package/dist/service/eventbridge/cfn/sim-cfn-event-bridge-resource-error.js +1 -1
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +1 -1
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.d.ts +3 -2
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +25 -3
- package/dist/service/ses/command/authorize/sim-ses-authorizer.d.ts +9 -0
- package/dist/service/ses/command/authorize/sim-ses-authorizer.js +18 -2
- package/dist/service/ses/command/send/sim-ses-send-email.js +2 -1
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-creator.js +1 -0
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.d.ts +9 -4
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-properties.js +18 -6
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.d.ts +10 -0
- package/dist/service/sns/cfn/topic/sim-cfn-sns-topic-property-names.js +16 -1
- package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-creator.js +1 -1
- package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.d.ts +11 -6
- package/dist/service/ssm/cfn/parameter/sim-cfn-ssm-parameter-properties.js +18 -9
- package/docs/README.md +40 -4
- package/docs/ai-skill/README.md +57 -54
- package/docs/cli/README.md +84 -94
- package/docs/factories/README.md +42 -54
- package/docs/lint/README.md +41 -67
- package/docs/non-aws-dependencies/README.md +72 -168
- package/docs/sdk/README.md +109 -95
- package/docs/serve/README.md +192 -898
- package/docs/services/acm/README.md +24 -40
- package/docs/services/apigateway/README.md +52 -71
- package/docs/services/apigatewayv2/README.md +55 -74
- package/docs/services/athena/README.md +17 -26
- package/docs/services/backup/README.md +29 -39
- package/docs/services/bedrock/README.md +38 -52
- package/docs/services/cloudformation/README.md +43 -55
- package/docs/services/cloudfront/README.md +69 -95
- package/docs/services/cloudwatch/README.md +40 -54
- package/docs/services/cognito/README.md +30 -45
- package/docs/services/dynamodb/README.md +34 -51
- package/docs/services/ecr/README.md +36 -77
- package/docs/services/ecs/README.md +26 -46
- package/docs/services/elbv2/README.md +19 -29
- package/docs/services/eventbridge/README.md +24 -20
- package/docs/services/firehose/README.md +24 -32
- package/docs/services/glue/README.md +41 -78
- package/docs/services/iam/README.md +13 -15
- package/docs/services/kinesis/README.md +53 -93
- package/docs/services/kms/README.md +22 -32
- package/docs/services/lambda/README.md +60 -80
- package/docs/services/logs/README.md +41 -50
- package/docs/services/organizations/README.md +50 -85
- package/docs/services/personalize/README.md +28 -44
- package/docs/services/rekognition/README.md +26 -38
- package/docs/services/route53/README.md +17 -17
- package/docs/services/s3/README.md +47 -51
- package/docs/services/scheduler/README.md +41 -52
- package/docs/services/secretsmanager/README.md +27 -42
- package/docs/services/ses/README.md +24 -34
- package/docs/services/sns/README.md +31 -33
- package/docs/services/sqs/README.md +14 -14
- package/docs/services/ssm/README.md +19 -21
- package/docs/services/stepfunctions/README.md +18 -20
- package/docs/services/sts/README.md +32 -45
- package/docs/services/wafv2/README.md +12 -17
- package/docs/terraform/README.md +108 -126
- package/docs/testing/README.md +228 -0
- package/docs/time/README.md +87 -117
- package/llms.txt +2 -1
- package/package.json +1 -1
package/docs/lint/README.md
CHANGED
|
@@ -1,24 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Lint CloudFront Functions code
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
absent and there is no network to reach. Both are a long way from where the code was written, and
|
|
7
|
-
Yulin publishes lint configs that refuse the same things in the editor.
|
|
3
|
+
Yulin provides ESLint and Oxlint configs for the CloudFront Functions JavaScript 2.0 runtime. Both
|
|
4
|
+
configs apply to `**/*.cff.js`, the filename pattern used by
|
|
5
|
+
[simulated CloudFront](https://yulinsim.dev/services/cloudfront/ "Simulated CloudFront usage docs").
|
|
8
6
|
|
|
9
|
-
The
|
|
10
|
-
[
|
|
11
|
-
CloudFront
|
|
12
|
-
|
|
13
|
-
Every restriction comes from the runtime's
|
|
14
|
-
[own feature list](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html),
|
|
15
|
-
an allow-list, and anything outside it is unsupported. None of it is house style. A rule
|
|
16
|
-
banning syntax JS2 accepts would send you away from code that works.
|
|
7
|
+
The runtime is ECMAScript 5.1 with selected features from ES 6 to 12. The lint rules follow AWS's
|
|
8
|
+
[runtime feature list](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html).
|
|
9
|
+
They report unsupported syntax and globals before CloudFront rejects the function.
|
|
17
10
|
|
|
18
11
|
## Setting it up with ESLint
|
|
19
12
|
|
|
20
|
-
`@kensio/yulin/eslint` exports a flat config
|
|
21
|
-
|
|
13
|
+
`@kensio/yulin/eslint` exports a flat config. Spread it into your config after any general rules it
|
|
14
|
+
needs to override:
|
|
22
15
|
|
|
23
16
|
```typescript cff-js2-eslint-config
|
|
24
17
|
/**
|
|
@@ -38,18 +31,15 @@ export default defineConfig(
|
|
|
38
31
|
);
|
|
39
32
|
```
|
|
40
33
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
names are ES 6 literal syntax the runtime lacks.
|
|
34
|
+
The config keeps rules for syntax that JS2 supports, including `const`, `let`, template literals,
|
|
35
|
+
arrow functions, rest parameters, and `async`/`await`. It switches off `object-shorthand` because
|
|
36
|
+
JS2 does not support shorthand property names.
|
|
45
37
|
|
|
46
|
-
`eslint` and `typescript-eslint`
|
|
38
|
+
Install the optional `eslint` and `typescript-eslint` peer dependencies when you use this export.
|
|
47
39
|
|
|
48
40
|
## Setting it up with Oxlint
|
|
49
41
|
|
|
50
|
-
|
|
51
|
-
themselves are one plugin loaded by both linters. The two configs report the same things in the same
|
|
52
|
-
places.
|
|
42
|
+
Extend Yulin's generated Oxlint config by its path in `node_modules`:
|
|
53
43
|
|
|
54
44
|
```json
|
|
55
45
|
{
|
|
@@ -60,16 +50,14 @@ places.
|
|
|
60
50
|
}
|
|
61
51
|
```
|
|
62
52
|
|
|
63
|
-
Oxlint's `extends` takes a file path
|
|
64
|
-
|
|
65
|
-
`dist/` leaves the path alone. The fragment brings its own `overrides` entry scoped to `**/*.cff.js`
|
|
66
|
-
and the JS plugin the rules live in, and leaves every other file to your own rules.
|
|
53
|
+
Oxlint's `extends` setting takes a file path. The fragment loads Yulin's JavaScript plugin and scopes
|
|
54
|
+
its rules to `**/*.cff.js`. Your own rules continue to apply to other files.
|
|
67
55
|
|
|
68
|
-
|
|
56
|
+
This config requires Oxlint 1.77 or later because it uses JavaScript plugin support.
|
|
69
57
|
|
|
70
58
|
## What is reported
|
|
71
59
|
|
|
72
|
-
|
|
60
|
+
Both configs load the same rules under the `cff-js2` name:
|
|
73
61
|
|
|
74
62
|
| Rule | What it refuses |
|
|
75
63
|
| ------------------------------- | -------------------------------------------------------- |
|
|
@@ -83,20 +71,16 @@ Each restriction is its own rule, under the `cff-js2` name in both linters.
|
|
|
83
71
|
| `cff-js2/no-for-of` | `for...of`, in favour of an index-based loop |
|
|
84
72
|
| `cff-js2/no-unavailable-global` | Globals the runtime does not have, such as `fetch` |
|
|
85
73
|
|
|
86
|
-
`cff-js2/no-unavailable-global`
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
rather than matching them as text, and a local variable named `fetch` or a property named
|
|
90
|
-
`event.fetch` is left alone. Each report says why the global is missing, because `fetch` being
|
|
91
|
-
absent for want of a network and `setTimeout` for want of an event loop call for different rewrites.
|
|
74
|
+
`cff-js2/no-unavailable-global` reports browser network APIs such as `fetch`, Node.js globals such as
|
|
75
|
+
`process`, and timer functions such as `setTimeout`. It resolves variables through JavaScript scope.
|
|
76
|
+
A local variable named `fetch` and a property such as `event.fetch` are allowed.
|
|
92
77
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
entry point CloudFront calls.
|
|
78
|
+
The configs also enable `no-eval`, `no-new-func`, and `no-implied-eval`. Their `no-unused-vars`
|
|
79
|
+
setting ignores the exported `handler` function because CloudFront calls it as the entry point.
|
|
96
80
|
|
|
97
|
-
##
|
|
81
|
+
## Supported JS2 syntax
|
|
98
82
|
|
|
99
|
-
|
|
83
|
+
The configs allow these JS2 features:
|
|
100
84
|
|
|
101
85
|
- Template literals, including interpolation and nesting
|
|
102
86
|
- Arrow functions and rest parameters
|
|
@@ -110,7 +94,7 @@ These all work in JS2 and no rule here objects to them:
|
|
|
110
94
|
|
|
111
95
|
## Turning one restriction off
|
|
112
96
|
|
|
113
|
-
|
|
97
|
+
You can switch off one restriction without disabling the rest. For ESLint:
|
|
114
98
|
|
|
115
99
|
```typescript cff-js2-eslint-relax
|
|
116
100
|
/**
|
|
@@ -129,7 +113,7 @@ export default defineConfig(...cloudFrontFunctionsJs2, {
|
|
|
129
113
|
});
|
|
130
114
|
```
|
|
131
115
|
|
|
132
|
-
|
|
116
|
+
For Oxlint, add an `overrides` entry after `extends`:
|
|
133
117
|
|
|
134
118
|
```json
|
|
135
119
|
{
|
|
@@ -147,30 +131,20 @@ In Oxlint, the same thing goes in an `overrides` entry after the `extends`:
|
|
|
147
131
|
|
|
148
132
|
## Available functionality
|
|
149
133
|
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
|
|
153
|
-
-
|
|
154
|
-
- Scoping to `**/*.cff.js`, leaving a repository's own rules untouched elsewhere
|
|
134
|
+
- `@kensio/yulin/eslint` exports the `cloudFrontFunctionsJs2` flat config.
|
|
135
|
+
- `cffjs2.oxlintrc.json` in the package root contains the Oxlint config. The source object is exported
|
|
136
|
+
as `cloudFrontFunctionsJs2Oxlint` from `@kensio/yulin/oxlint`.
|
|
137
|
+
- Both configs enable nine `cff-js2` rules and scope them to `**/*.cff.js`.
|
|
155
138
|
|
|
156
139
|
## Limitations
|
|
157
140
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
-
|
|
165
|
-
|
|
166
|
-
-
|
|
167
|
-
|
|
168
|
-
too slow. No rule checks which methods of a supported built-in you call either, and the runtime
|
|
169
|
-
supports only some of them.
|
|
170
|
-
- **Only the `cff-js2` rules are shared between the linters.** ESLint and Oxlint each bring their own
|
|
171
|
-
built-in rules, and their own defaults for which are on. A `.cff.js` file linted by both may pick
|
|
172
|
-
up findings from one that the other lacks.
|
|
173
|
-
- **Oxlint's JS plugin support is in alpha.** It is what loads these rules into Oxlint, and its API
|
|
174
|
-
is still moving upstream.
|
|
175
|
-
- **The Oxlint fragment is extended by path.** Oxlint has no package-name resolution in `extends`.
|
|
176
|
-
The written-out path into `node_modules` depends on your installer's layout.
|
|
141
|
+
- The rules do not check the JS2 restrictions on `async` arguments and closures. They also do not
|
|
142
|
+
check that `await` appears only inside an `async` function.
|
|
143
|
+
- `cff-js2/no-unavailable-global` covers common browser, Node.js, and timer globals. Other globals
|
|
144
|
+
missing from JS2 may go unreported.
|
|
145
|
+
- The rules do not check CloudFront's function size or CPU limits. They also do not validate every
|
|
146
|
+
method on each supported built-in.
|
|
147
|
+
- ESLint and Oxlint have different built-in rules. Only the `cff-js2` rules are shared.
|
|
148
|
+
- Oxlint's JavaScript plugin support is in alpha and may change upstream.
|
|
149
|
+
- Oxlint resolves the config through its path in `node_modules`. That path depends on the package
|
|
150
|
+
manager's installation layout.
|
|
@@ -1,33 +1,20 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Use non-AWS dependencies
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
what happens to those when the application runs under Yulin.
|
|
3
|
+
Yulin leaves databases, caches, and other non-AWS dependencies under your control.
|
|
5
4
|
|
|
6
|
-
##
|
|
5
|
+
## Pass connection details through environment variables
|
|
7
6
|
|
|
8
|
-
A simulated Lambda function
|
|
9
|
-
|
|
7
|
+
A simulated Lambda function or ECS container runs in the same Node.js process as your test. AWS
|
|
8
|
+
SDK calls can stay inside Yulin through [SDK interception](https://yulinsim.dev/sdk/). Calls to
|
|
9
|
+
other libraries work normally.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Everything else is yours to provide, and connects the way it normally would. Yulin leaves it alone
|
|
16
|
-
entirely, with no simulation and no interception. Code that opens a Redis connection opens a real
|
|
17
|
-
one, to whatever address it was given.
|
|
18
|
-
|
|
19
|
-
## Pointing the code at your own dependency
|
|
20
|
-
|
|
21
|
-
A deployed Lambda function or ECS container reads its connection details from environment variables.
|
|
22
|
-
The simulated ones do the same. A function's `Environment.Variables` and a container definition's
|
|
23
|
-
`environment` are visible through `process.env` while the code runs, so pointing the application
|
|
24
|
-
somewhere else means setting the value it already reads. No Yulin feature is involved, and the code
|
|
25
|
-
under test stays as it is.
|
|
11
|
+
Configure those libraries through the same environment variables that you use in production. For
|
|
12
|
+
example, a deployed function could receive an ElastiCache URL while a test receives a localhost
|
|
13
|
+
URL:
|
|
26
14
|
|
|
27
15
|
```typescript non-aws-dependency-lambda
|
|
28
16
|
/**
|
|
29
|
-
*
|
|
30
|
-
* simulate, alongside one it does.
|
|
17
|
+
* Giving a simulated Lambda function the address of an external dependency.
|
|
31
18
|
*/
|
|
32
19
|
|
|
33
20
|
import { CreateFunctionCommand, InvokeCommand } from "@aws-sdk/client-lambda";
|
|
@@ -44,17 +31,12 @@ await lambda.createFunction(
|
|
|
44
31
|
Role: "arn:aws:iam::111111111111:role/RatesRole",
|
|
45
32
|
Environment: {
|
|
46
33
|
Variables: {
|
|
47
|
-
// Simulated by Yulin, reached with no network involved.
|
|
48
34
|
TABLE_NAME: "rates",
|
|
49
|
-
// Yours. A deployment points this at ElastiCache. A test points it
|
|
50
|
-
// at whatever it wants the code to talk to instead.
|
|
51
35
|
CACHE_URL: "redis://127.0.0.1:6379",
|
|
52
36
|
},
|
|
53
37
|
},
|
|
54
38
|
Code: {
|
|
55
39
|
ZipFile: makeLambdaZipFileInput(() => ({
|
|
56
|
-
// Building a Redis client from the second value involves nothing of
|
|
57
|
-
// Yulin's. It is an ordinary environment variable read.
|
|
58
40
|
tableName: process.env["TABLE_NAME"],
|
|
59
41
|
cacheUrl: process.env["CACHE_URL"],
|
|
60
42
|
})),
|
|
@@ -66,141 +48,56 @@ const output = await lambda.invoke(
|
|
|
66
48
|
new InvokeCommand({ FunctionName: "rates" }),
|
|
67
49
|
);
|
|
68
50
|
|
|
69
|
-
if (output.Payload === undefined) throw new Error("No invoke
|
|
70
|
-
|
|
51
|
+
if (output.Payload === undefined) throw new Error("No invoke payload");
|
|
52
|
+
|
|
71
53
|
console.log(Buffer.from(output.Payload).toString());
|
|
54
|
+
// {"tableName":"rates","cacheUrl":"redis://127.0.0.1:6379"}
|
|
72
55
|
```
|
|
73
56
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
## A container reading from both
|
|
57
|
+
`TABLE_NAME` identifies a simulated AWS resource. `CACHE_URL` points to a dependency supplied by
|
|
58
|
+
the test. That dependency could be a local process, a test container, or an in-process
|
|
59
|
+
implementation of the interface your application uses.
|
|
79
60
|
|
|
80
|
-
|
|
81
|
-
and reads from a cache of its own. The two categories sit next to each other in the same handler and
|
|
82
|
-
are configured the same way, through the container definition's `environment`.
|
|
61
|
+
## Configure an ECS container in the same way
|
|
83
62
|
|
|
84
|
-
|
|
85
|
-
|
|
63
|
+
An ECS container binding reads the container definition's `environment` entries through
|
|
64
|
+
`process.env` while its handler runs:
|
|
86
65
|
|
|
87
66
|
```typescript non-aws-dependency-ecs
|
|
88
67
|
/**
|
|
89
|
-
*
|
|
90
|
-
* from a cache of its own.
|
|
68
|
+
* Passing a cache URL to a simulated ECS container.
|
|
91
69
|
*/
|
|
92
70
|
|
|
93
|
-
import {
|
|
94
|
-
CreateTableCommand,
|
|
95
|
-
DynamoDBClient,
|
|
96
|
-
GetItemCommand,
|
|
97
|
-
PutItemCommand,
|
|
98
|
-
} from "@aws-sdk/client-dynamodb";
|
|
99
71
|
import {
|
|
100
72
|
CreateClusterCommand,
|
|
101
73
|
RegisterTaskDefinitionCommand,
|
|
102
74
|
RunTaskCommand,
|
|
103
75
|
} from "@aws-sdk/client-ecs";
|
|
104
|
-
import { CreateRoleCommand, PutRolePolicyCommand } from "@aws-sdk/client-iam";
|
|
105
76
|
|
|
106
|
-
import {
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Where the worker reads exchange rates from.
|
|
110
|
-
*
|
|
111
|
-
* A deployment builds a Redis client from the URL. This example stands one in,
|
|
112
|
-
* so nothing has to be running for it to work.
|
|
113
|
-
*/
|
|
114
|
-
class RateCache {
|
|
115
|
-
constructor(private readonly url: string) {}
|
|
116
|
-
|
|
117
|
-
rate(currency: string): Promise<string> {
|
|
118
|
-
console.log(`reading ${currency} from ${this.url}`);
|
|
119
|
-
// reading GBP from redis://127.0.0.1:6379
|
|
120
|
-
return Promise.resolve("1.27");
|
|
121
|
-
}
|
|
122
|
-
}
|
|
77
|
+
import { SimAws } from "@kensio/yulin";
|
|
123
78
|
|
|
124
|
-
|
|
125
|
-
const { simAws } = simSdk;
|
|
79
|
+
const simAws = new SimAws();
|
|
126
80
|
const ecs = simAws.ecs();
|
|
127
|
-
|
|
128
|
-
simSdk.intercept(DynamoDBClient);
|
|
129
|
-
|
|
130
|
-
await simAws.dynamoDb().createTable(
|
|
131
|
-
new CreateTableCommand({
|
|
132
|
-
TableName: "rates",
|
|
133
|
-
KeySchema: [{ AttributeName: "currency", KeyType: "HASH" }],
|
|
134
|
-
AttributeDefinitions: [{ AttributeName: "currency", AttributeType: "S" }],
|
|
135
|
-
BillingMode: "PAY_PER_REQUEST",
|
|
136
|
-
}),
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
const taskRole = await simAws.iam().createRole(
|
|
140
|
-
new CreateRoleCommand({
|
|
141
|
-
RoleName: "RatesTaskRole",
|
|
142
|
-
AssumeRolePolicyDocument: JSON.stringify({
|
|
143
|
-
Version: "2012-10-17",
|
|
144
|
-
Statement: {
|
|
145
|
-
Effect: "Allow",
|
|
146
|
-
Principal: { Service: "ecs-tasks.amazonaws.com" },
|
|
147
|
-
Action: "sts:AssumeRole",
|
|
148
|
-
},
|
|
149
|
-
}),
|
|
150
|
-
}),
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
await simAws.iam().putRolePolicy(
|
|
154
|
-
new PutRolePolicyCommand({
|
|
155
|
-
RoleName: "RatesTaskRole",
|
|
156
|
-
PolicyName: "WriteRates",
|
|
157
|
-
PolicyDocument: JSON.stringify({
|
|
158
|
-
Version: "2012-10-17",
|
|
159
|
-
Statement: {
|
|
160
|
-
Effect: "Allow",
|
|
161
|
-
Action: "dynamodb:PutItem",
|
|
162
|
-
Resource:
|
|
163
|
-
`arn:aws:dynamodb:${simAws.defaultRegionName}:` +
|
|
164
|
-
`${simAws.defaultAccountId}:table/rates`,
|
|
165
|
-
},
|
|
166
|
-
}),
|
|
167
|
-
}),
|
|
168
|
-
);
|
|
81
|
+
const connections: string[] = [];
|
|
169
82
|
|
|
170
83
|
await ecs.createCluster(new CreateClusterCommand({}));
|
|
171
84
|
|
|
172
85
|
ecs.bindContainer({
|
|
173
86
|
family: "rates-worker",
|
|
174
87
|
containerName: "app",
|
|
175
|
-
run:
|
|
176
|
-
|
|
177
|
-
// variables rather than the test process's.
|
|
178
|
-
const cache = new RateCache(process.env["CACHE_URL"] ?? "");
|
|
179
|
-
const rate = await cache.rate("GBP");
|
|
180
|
-
|
|
181
|
-
await new DynamoDBClient({}).send(
|
|
182
|
-
new PutItemCommand({
|
|
183
|
-
TableName: process.env["TABLE_NAME"],
|
|
184
|
-
Item: { currency: { S: "GBP" }, rate: { S: rate } },
|
|
185
|
-
}),
|
|
186
|
-
);
|
|
88
|
+
run: () => {
|
|
89
|
+
connections.push(process.env["CACHE_URL"] ?? "");
|
|
187
90
|
},
|
|
188
91
|
});
|
|
189
92
|
|
|
190
93
|
await ecs.registerTaskDefinition(
|
|
191
94
|
new RegisterTaskDefinitionCommand({
|
|
192
95
|
family: "rates-worker",
|
|
193
|
-
taskRoleArn: taskRole.Role.Arn,
|
|
194
96
|
containerDefinitions: [
|
|
195
97
|
{
|
|
196
98
|
name: "app",
|
|
197
99
|
image: "rates-worker:1",
|
|
198
|
-
environment: [
|
|
199
|
-
// Simulated by Yulin, reached with no network involved.
|
|
200
|
-
{ name: "TABLE_NAME", value: "rates" },
|
|
201
|
-
// Yours, connected to the way it normally would be.
|
|
202
|
-
{ name: "CACHE_URL", value: "redis://127.0.0.1:6379" },
|
|
203
|
-
],
|
|
100
|
+
environment: [{ name: "CACHE_URL", value: "redis://127.0.0.1:6379" }],
|
|
204
101
|
},
|
|
205
102
|
],
|
|
206
103
|
}),
|
|
@@ -209,53 +106,60 @@ await ecs.registerTaskDefinition(
|
|
|
209
106
|
await ecs.runTask(new RunTaskCommand({ taskDefinition: "rates-worker" }));
|
|
210
107
|
await simAws.backgroundTasksComplete();
|
|
211
108
|
|
|
212
|
-
|
|
213
|
-
new GetItemCommand({
|
|
214
|
-
TableName: "rates",
|
|
215
|
-
Key: { currency: { S: "GBP" } },
|
|
216
|
-
}),
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
console.log(stored.Item?.["rate"]?.S); // "1.27"
|
|
109
|
+
console.log(connections); // ["redis://127.0.0.1:6379"]
|
|
220
110
|
```
|
|
221
111
|
|
|
222
|
-
The
|
|
223
|
-
|
|
112
|
+
The host environment remains available unless the container overrides a variable. ECS task
|
|
113
|
+
overrides and simulated Secrets Manager or SSM values are also applied before the handler runs.
|
|
114
|
+
See the [ECS guide](https://yulinsim.dev/services/ecs/) for the full precedence rules.
|
|
224
115
|
|
|
225
|
-
##
|
|
116
|
+
## Run the dependency yourself
|
|
226
117
|
|
|
227
|
-
|
|
228
|
-
running next to the application in the same task. Yulin never looks inside a container image, and
|
|
229
|
-
the only thing it can run is JavaScript or TypeScript in its own process. An image holding a Redis
|
|
230
|
-
server is beyond it.
|
|
118
|
+
Your test or development script starts and stops the server named by a connection string.
|
|
231
119
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
run yourself or a stand-in, in the same way as for any other dependency of your own.
|
|
120
|
+
This also applies to ECS sidecars. Yulin stores every container definition, but it only runs a
|
|
121
|
+
container that has a JavaScript or TypeScript binding. It cannot run a Redis or database image. An
|
|
122
|
+
unbound sidecar is recorded as unsimulated when the task runs.
|
|
236
123
|
|
|
237
|
-
|
|
124
|
+
Point the application container at a dependency that is available to the host process. A localhost
|
|
125
|
+
address often works because the simulated container runs in that process, without a separate
|
|
126
|
+
container network.
|
|
238
127
|
|
|
239
|
-
|
|
240
|
-
process environment otherwise. A read at module scope, as in `const url = process.env.CACHE_URL` at
|
|
241
|
-
the top of a file, happens when the test imports that file rather than when the code runs. It sees
|
|
242
|
-
the host value.
|
|
128
|
+
## Read environment variables while the handler runs
|
|
243
129
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
difference changes what the code sees, which is covered under
|
|
247
|
-
[environment variables](https://yulinsim.dev/services/lambda/#environment-variables) on the simulated Lambda
|
|
248
|
-
page. Zip code running in the vm runtime is unaffected, because it is imported during an invocation.
|
|
130
|
+
Referenced Lambda handlers and ECS bindings come from modules already imported by the test. A
|
|
131
|
+
module-level read happens during that import:
|
|
249
132
|
|
|
250
|
-
|
|
133
|
+
```typescript
|
|
134
|
+
const cacheUrl = process.env["CACHE_URL"];
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
That value comes from the host environment. Read the variable inside the handler when it must come
|
|
138
|
+
from the simulated function or container configuration:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
async function handler(): Promise<void> {
|
|
142
|
+
const cacheUrl = process.env["CACHE_URL"];
|
|
143
|
+
// Build or call the dependency here.
|
|
144
|
+
}
|
|
145
|
+
```
|
|
251
146
|
|
|
252
|
-
|
|
147
|
+
Simulated Lambda prints a warning when a declared value conflicts with a value that module-level
|
|
148
|
+
code could have read. Lambda code loaded from a zip archive is imported during its first invocation
|
|
149
|
+
and sees the function environment at module scope.
|
|
150
|
+
|
|
151
|
+
## Available functionality
|
|
152
|
+
|
|
153
|
+
- Lambda `Environment.Variables` are applied during an invocation.
|
|
154
|
+
- ECS container `environment`, `secrets`, and task overrides are applied during a container run.
|
|
155
|
+
- Host environment variables remain available for names left out of the workload configuration.
|
|
156
|
+
- AWS SDK interception continues to work from code that also uses external dependencies.
|
|
157
|
+
|
|
158
|
+
## Limitations
|
|
253
159
|
|
|
254
|
-
-
|
|
255
|
-
|
|
256
|
-
-
|
|
257
|
-
|
|
258
|
-
-
|
|
259
|
-
|
|
260
|
-
- There is no interception point for a non-AWS dependency. The way to send the code somewhere else
|
|
261
|
-
is the environment variable it already reads, or an injection point in the code itself.
|
|
160
|
+
- Yulin does not create, start, reset, or stop non-AWS dependencies.
|
|
161
|
+
- Unbound ECS sidecar images are stored but never run.
|
|
162
|
+
- Referenced handlers and container bindings read the host value when they access `process.env` at
|
|
163
|
+
module scope.
|
|
164
|
+
- Yulin has no interception API for non-AWS clients. Configure the client through your application's
|
|
165
|
+
existing environment variables or dependency injection point.
|