Skip to content

Commit 4a89536

Browse files
authoredApr 1, 2025··
Major: rework environment configuration of CDK (#109)
1 parent 7400aa3 commit 4a89536

13 files changed

+7216
-682
lines changed
 

‎.github/workflows/build.yml

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ on:
1010
branches:
1111
- main
1212

13+
# Only one pull-request triggered run should be executed at a time
14+
# (head_ref is only set for PR events, otherwise fallback to run_id which differs for every run).
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
17+
cancel-in-progress: true
18+
1319
jobs:
1420
build-and-lint:
1521
runs-on: ubuntu-latest

‎.github/workflows/integrations.go.yml

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ on:
1414
upstream-version:
1515
description: Upstream aws-cdk version to use in tests
1616
required: false
17+
18+
# Only one pull-request triggered run should be executed at a time
19+
# (head_ref is only set for PR events, otherwise fallback to run_id which differs for every run).
20+
concurrency:
21+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
22+
cancel-in-progress: true
23+
1724
env:
1825
AWS_ACCESS_KEY_ID: test
1926
AWS_SECRET_ACCESS_KEY: test
@@ -35,6 +42,7 @@ jobs:
3542
env:
3643
AWS_REGION: ${{ matrix.region }}
3744
AWS_DEFAULT_REGION: ${{ matrix.region }}
45+
AWS_ENVAR_ALLOWLIST: AWS_REGION,AWS_DEFAULT_REGION
3846

3947
steps:
4048
- uses: actions/checkout@v2

‎.github/workflows/integrations.node.yml

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ on:
1414
upstream-version:
1515
description: Upstream aws-cdk version to use in tests
1616
required: false
17+
18+
# Only one pull-request triggered run should be executed at a time
19+
# (head_ref is only set for PR events, otherwise fallback to run_id which differs for every run).
20+
concurrency:
21+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
22+
cancel-in-progress: true
23+
1724
env:
1825
AWS_ACCESS_KEY_ID: test
1926
AWS_SECRET_ACCESS_KEY: test
@@ -33,6 +40,7 @@ jobs:
3340
env:
3441
AWS_REGION: ${{ matrix.region }}
3542
AWS_DEFAULT_REGION: ${{ matrix.region }}
43+
AWS_ENVAR_ALLOWLIST: AWS_REGION,AWS_DEFAULT_REGION
3644

3745
steps:
3846
- uses: actions/checkout@v2

‎.github/workflows/integrations.python.yml

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ on:
1414
upstream-version:
1515
description: Upstream aws-cdk version to use in tests
1616
required: false
17+
18+
# Only one pull-request triggered run should be executed at a time
19+
# (head_ref is only set for PR events, otherwise fallback to run_id which differs for every run).
20+
concurrency:
21+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
22+
cancel-in-progress: true
23+
1724
env:
1825
AWS_ACCESS_KEY_ID: test
1926
AWS_SECRET_ACCESS_KEY: test
@@ -33,6 +40,7 @@ jobs:
3340
env:
3441
AWS_REGION: ${{ matrix.region }}
3542
AWS_DEFAULT_REGION: ${{ matrix.region }}
43+
AWS_ENVAR_ALLOWLIST: AWS_REGION,AWS_DEFAULT_REGION
3644

3745
steps:
3846
- uses: actions/checkout@v2

‎.github/workflows/test.yml

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ on:
2020
type: boolean
2121
default: false
2222

23+
# Only one pull-request triggered run should be executed at a time
24+
# (head_ref is only set for PR events, otherwise fallback to run_id which differs for every run).
25+
concurrency:
26+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
27+
cancel-in-progress: true
28+
2329
env:
2430
AWS_ACCESS_KEY_ID: test
2531
AWS_SECRET_ACCESS_KEY: test

‎.github/workflows/unit.yml

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Unit tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
workflow_dispatch:
9+
inputs:
10+
node-version:
11+
required: false
12+
default: "22.x"
13+
14+
# Only one pull-request triggered run should be executed at a time
15+
# (head_ref is only set for PR events, otherwise fallback to run_id which differs for every run).
16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
18+
cancel-in-progress: true
19+
20+
jobs:
21+
unit-tests:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- name: Use Node.js ${{ inputs.node-version }}
27+
uses: actions/setup-node@v4
28+
with:
29+
node-version: ${{ inputs.node-version }}
30+
31+
- name: Install project
32+
run: |
33+
npm install
34+
35+
- name: Run tests
36+
run: |
37+
npm run test
38+
39+

‎README.md

+11
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,22 @@ To resolve this, set the `NODE_PATH` variable pointing to your AWS CDK's `node_m
2828
$ export NODE_PATH=$NODE_PATH:/opt/homebrew/Cellar/aws-cdk/<CDK_VERSION>/libexec/lib/node_modules
2929
```
3030

31+
## Version support
32+
33+
`cdklocal` supports all installed versions of the node `aws-cdk` package, however some complications are present for `aws-cdk >= 2.177.0`.
34+
35+
For these CDK versions, we remove AWS configuration environment variables like `AWS_PROFILE` from the shell environment before invoking the `cdk` command, and explicitly set `AWS_ENDPOINT_URL` and `AWS_ENDPOINT_URL_S3` to target LocalStack.
36+
37+
1. We do this because other environment variables may lead to a conflicting set of configuration options, where the wrong region is used to target LocalStack, or `cdklocal` tries to deploy into upstream AWS by mistake. If individual configuration variables are needed for the deploy process (e.g. `AWS_REGION`) these configuration variables can be propagated to the `cdk` command by configuring `AWS_ENVAR_ALLOWLIST`, for example: `AWS_ENVAR_ALLOWLIST=AWS_REGION,AWS_DEFAULT_REGION AWS_DEFAULT_REGION=eu-central-1 AWS_REGION=eu-central-1 cdklocal ...`.
38+
2. If you are manually setting `AWS_ENDPOINT_URL`, the new value will continue to be read from the environment, however `AWS_ENDPOINT_URL_S3` _must_ also be set and should include a `.s3.` component to ensure we correctly detect S3 requests.
39+
3140
## Configurations
3241

3342
The following environment variables can be configured:
3443

3544
* `AWS_ENDPOINT_URL`: The endpoint URL to connect to (combination of `USE_SSL`/`LOCALSTACK_HOSTNAME`/`EDGE_PORT` below)
3645
* `AWS_ENDPOINT_URL_S3`: The endpoint URL to connect to (combination of `USE_SSL`/`LOCALSTACK_HOSTNAME`/`EDGE_PORT` below) for S3 requests
46+
* `AWS_ENVAR_ALLOWLIST`: Allow specific `AWS_*` environment variables to be used by the CDK
3747
* `EDGE_PORT` (deprecated): Port under which LocalStack edge service is accessible (default: `4566`)
3848
* `LOCALSTACK_HOSTNAME` (deprecated): Target host under which LocalStack edge service is accessible (default: `localhost`)
3949
* `USE_SSL` (deprecated): Whether to use SSL to connect to the LocalStack endpoint, i.e., connect via HTTPS.
@@ -78,6 +88,7 @@ $ awslocal sns list-topics
7888

7989
## Change Log
8090

91+
* 3.0.0: Sanitise environment of configuration environment variables before deployment
8192
* 2.19.2: Fix SDK compatibility with aws-cdk versions >= 2.177.0
8293
* 2.19.1: Fix SDK compatibility with older CDK versions; Fix patched bucket location in TemplateURL
8394
* 2.19.0: Add support for aws-cdk versions >= `2.167.0`

‎__tests__/index.test.js

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
const { configureEnvironment, EnvironmentMisconfigurationError } = require("../src");
2+
3+
describe("configureEnvironment", () => {
4+
test("empty environment", () => {
5+
const env = {};
6+
const allowListStr = "";
7+
configureEnvironment(env, allowListStr);
8+
expect(env).toEqual({
9+
AWS_ACCESS_KEY_ID: "test",
10+
AWS_SECRET_ACCESS_KEY: "test",
11+
AWS_REGION: "us-east-1",
12+
AWS_DEFAULT_REGION: "us-east-1",
13+
AWS_ENDPOINT_URL: "http://localhost.localstack.cloud:4566",
14+
AWS_ENDPOINT_URL_S3: "http://s3.localhost.localstack.cloud:4566",
15+
});
16+
});
17+
18+
test("custom endpoint url", () => {
19+
const env = {
20+
AWS_ENDPOINT_URL: "http://foo.bar:4567",
21+
AWS_ENDPOINT_URL_S3: "http://foo.bar:4567",
22+
};
23+
const allowListStr = "";
24+
configureEnvironment(env, allowListStr);
25+
expect(env).toEqual({
26+
AWS_ACCESS_KEY_ID: "test",
27+
AWS_SECRET_ACCESS_KEY: "test",
28+
AWS_REGION: "us-east-1",
29+
AWS_DEFAULT_REGION: "us-east-1",
30+
AWS_ENDPOINT_URL: "http://foo.bar:4567",
31+
AWS_ENDPOINT_URL_S3: "http://foo.bar:4567",
32+
});
33+
});
34+
35+
test("custom endpoint url without specifying s3 url", () => {
36+
const env = {
37+
AWS_ENDPOINT_URL: "http://foo.bar:4567",
38+
};
39+
const allowListStr = "";
40+
expect(() => configureEnvironment(env, allowListStr)).toThrow(EnvironmentMisconfigurationError);
41+
});
42+
43+
test("strip extra configuration envars", () => {
44+
const env = {
45+
AWS_PROFILE: "my-profile",
46+
};
47+
const allowListStr = "";
48+
configureEnvironment(env, allowListStr);
49+
expect(env).toEqual({
50+
AWS_ACCESS_KEY_ID: "test",
51+
AWS_SECRET_ACCESS_KEY: "test",
52+
AWS_REGION: "us-east-1",
53+
AWS_DEFAULT_REGION: "us-east-1",
54+
AWS_ENDPOINT_URL: "http://localhost.localstack.cloud:4566",
55+
AWS_ENDPOINT_URL_S3: "http://s3.localhost.localstack.cloud:4566",
56+
});
57+
});
58+
59+
test("allowlist of profile", () => {
60+
const env = {
61+
AWS_PROFILE: "my-profile",
62+
};
63+
const allowListStr = "AWS_PROFILE";
64+
configureEnvironment(env, allowListStr);
65+
expect(env).toEqual({
66+
AWS_ACCESS_KEY_ID: "test",
67+
AWS_SECRET_ACCESS_KEY: "test",
68+
AWS_PROFILE: "my-profile",
69+
AWS_REGION: "us-east-1",
70+
AWS_DEFAULT_REGION: "us-east-1",
71+
AWS_ENDPOINT_URL: "http://localhost.localstack.cloud:4566",
72+
AWS_ENDPOINT_URL_S3: "http://s3.localhost.localstack.cloud:4566",
73+
});
74+
});
75+
76+
test("credentials overriding", () => {
77+
const env = {
78+
AWS_ACCESS_KEY_ID: "something",
79+
AWS_SECRET_ACCESS_KEY: "else",
80+
};
81+
const allowListStr = "AWS_PROFILE,AWS_SECRET_ACCESS_KEY,AWS_ACCESS_KEY_ID";
82+
configureEnvironment(env, allowListStr);
83+
expect(env).toEqual({
84+
AWS_ACCESS_KEY_ID: "something",
85+
AWS_SECRET_ACCESS_KEY: "else",
86+
AWS_REGION: "us-east-1",
87+
AWS_DEFAULT_REGION: "us-east-1",
88+
AWS_ENDPOINT_URL: "http://localhost.localstack.cloud:4566",
89+
AWS_ENDPOINT_URL_S3: "http://s3.localhost.localstack.cloud:4566",
90+
});
91+
});
92+
93+
test("region overriding", () => {
94+
const env = {
95+
AWS_REGION: "eu-central-1",
96+
AWS_DEFAULT_REGION: "eu-central-1",
97+
};
98+
const allowListStr = "AWS_REGION,AWS_DEFAULT_REGION";
99+
configureEnvironment(env, allowListStr);
100+
expect(env).toEqual({
101+
AWS_ACCESS_KEY_ID: "test",
102+
AWS_SECRET_ACCESS_KEY: "test",
103+
AWS_REGION: "eu-central-1",
104+
AWS_DEFAULT_REGION: "eu-central-1",
105+
AWS_ENDPOINT_URL: "http://localhost.localstack.cloud:4566",
106+
AWS_ENDPOINT_URL_S3: "http://s3.localhost.localstack.cloud:4566",
107+
});
108+
});
109+
});

‎bin/cdklocal

+4-13
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@ const https = require("https");
99
const crypto = require("crypto");
1010
const net = require('net');
1111

12-
// constants and custom config values
12+
const { isEnvTrue, EDGE_PORT, PROTOCOL, configureEnvironment } = require("../src");
1313

14-
const isEnvTrue = (envName) => ["1", "true"].includes(process.env[envName]);
14+
// constants and custom config values
1515

16-
const DEFAULT_EDGE_PORT = 4566;
17-
const EDGE_PORT = process.env.EDGE_PORT || DEFAULT_EDGE_PORT;
1816
const DEFAULT_HOSTNAME = "localhost";
1917
const LAMBDA_MOUNT_CODE = isEnvTrue("LAMBDA_MOUNT_CODE");
20-
const PROTOCOL = isEnvTrue("USE_SSL") ? "https" : "http";
18+
const AWS_ENVAR_ALLOWLIST = process.env.AWS_ENVAR_ALLOWLIST || "";
2119

2220

2321
//----------------
@@ -451,13 +449,6 @@ const patchPre_2_14 = () => {
451449
applyPatches(provider, CdkToolkit, SDK, ToolkitInfo);
452450
};
453451

454-
const configureEnvironment = () => {
455-
// This _must_ use localhost.localstack.cloud as we require valid subdomains of these paths to
456-
// resolve. Unfortunately though `curl` seems to support subdomains of localhost, the CDK does not.
457-
process.env.AWS_ENDPOINT_URL_S3 = process.env.AWS_ENDPOINT_URL_S3 || `${PROTOCOL}://s3.localhost.localstack.cloud:${EDGE_PORT}`;
458-
process.env.AWS_ENDPOINT_URL = process.env.AWS_ENDPOINT_URL || `${PROTOCOL}://localhost.localstack.cloud:${EDGE_PORT}`;
459-
};
460-
461452
const patchPost_2_14 = () => {
462453
var lib = null;
463454
try {
@@ -484,7 +475,7 @@ const patchPost_2_14 = () => {
484475
break;
485476
case "ERR_PACKAGE_PATH_NOT_EXPORTED":
486477
// post 2.177
487-
configureEnvironment();
478+
configureEnvironment(process.env, AWS_ENVAR_ALLOWLIST);
488479
break;
489480
default:
490481
// a different error

‎jest.config.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
testEnvironment: "node",
3+
verbose: true,
4+
coverageDirectory: "coverage",
5+
collectCoverageFrom: ["src/**/*.{js,jsx,ts,tsx}"],
6+
testMatch: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)"],
7+
};
8+

0 commit comments

Comments
 (0)
Please sign in to comment.