Skip to content

Commit 3b19c99

Browse files
add check to prevent IPv6 connection issue with localhost on MacOS (#79)
Co-authored-by: Mathias Kron <[email protected]>
1 parent 63040a7 commit 3b19c99

File tree

3 files changed

+40
-15
lines changed

3 files changed

+40
-15
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ $ awslocal sns list-topics
6767

6868
## Change Log
6969

70+
* 2.16.0: Add check to prevent IPv6 connection issue with `localhost` on MacOS
7071
* 2.15.0: Fix issue with undefined BUCKET_NAME_OUTPUT variable; add CI build and eslint config
7172
* 2.14.0: Add switches in patches to accommodate new esbuild packaging mechanism in CDK v2.14.0+
7273
* 1.65.7: Add switch that checks for asset existence before symlinking assets; fix parsing fetched template body for JSON/YAML formats; add missing dependency to "diff" package

bin/cdklocal

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const path = require("path");
77
const http = require("http");
88
const https = require("https");
99
const crypto = require("crypto");
10+
const net = require('net');
1011

1112
// config constants
1213

@@ -19,13 +20,36 @@ const LAMBDA_MOUNT_CODE = ["1", "true"].includes(process.env.LAMBDA_MOUNT_CODE);
1920
// UTIL FUNCTIONS
2021
//----------------
2122

22-
const getLocalEndpoint = () => `http://${getLocalHost()}`;
23+
const getLocalEndpoint = async () => `http://${await getLocalHost()}`;
2324

2425
const port = process.env.EDGE_PORT || DEFAULT_EDGE_PORT;
2526

26-
const getLocalHost = () => {
27-
const host = process.env.LOCALSTACK_HOSTNAME || DEFAULT_HOSTNAME;
28-
return `${host}:${port}`;
27+
var resolvedHostname = undefined;
28+
29+
const getLocalHost = async () => {
30+
if (resolvedHostname) {
31+
// early exit to not resolve again
32+
return `${resolvedHostname}:${port}`;
33+
}
34+
35+
var hostname = process.env.LOCALSTACK_HOSTNAME || DEFAULT_HOSTNAME;
36+
// attempt to connect to the given host/port
37+
const socket = new net.Socket();
38+
try {
39+
await socket.connect({ host: hostname, port });
40+
} catch (e) {
41+
if (hostname === "localhost") {
42+
// fall back to using local IPv4 address - to fix IPv6 issue on MacOS
43+
// see, https://github.com/localstack/serverless-localstack/issues/125
44+
// and https://github.com/localstack/aws-cdk-local/issues/78
45+
hostname = "127.0.0.1";
46+
}
47+
} finally {
48+
socket.destroy();
49+
}
50+
51+
resolvedHostname = hostname;
52+
return `${hostname}:${port}`;
2953
};
3054

3155
const useLocal = () => {
@@ -85,14 +109,14 @@ const importLib = function importLib (libPath) {
85109
}
86110
};
87111

88-
const setSdkOptions = (options, setHttpOptions) => {
112+
const setSdkOptions = async (options, setHttpOptions) => {
89113
if (!useLocal(options)) {
90114
return;
91115
}
92116
if (setHttpOptions) {
93117
options = options.httpOptions = options.httpOptions || {};
94118
}
95-
options.endpoint = getLocalEndpoint();
119+
options.endpoint = await getLocalEndpoint();
96120
options.s3ForcePathStyle = true;
97121
options.accessKeyId = "test";
98122
options.secretAccessKey = "test";
@@ -101,10 +125,10 @@ const setSdkOptions = (options, setHttpOptions) => {
101125
const patchProviderCredentials = (provider) => {
102126
const origConstr = provider.SdkProvider.withAwsCliCompatibleDefaults;
103127
provider.SdkProvider.withAwsCliCompatibleDefaults = async (options = {}) => {
104-
setSdkOptions(options, true);
128+
await setSdkOptions(options, true);
105129
const result = await origConstr(options);
106130
result.sdkOptions = result.sdkOptions || {};
107-
setSdkOptions(result.sdkOptions);
131+
await setSdkOptions(result.sdkOptions);
108132
return result;
109133
};
110134

@@ -118,18 +142,18 @@ const patchCdkToolkit = (CdkToolkit) => {
118142
const CdkToolkitClass = CdkToolkit.ToolkitInfo || CdkToolkit;
119143
getMethods(CdkToolkitClass.prototype).forEach((meth) => {
120144
const original = CdkToolkitClass.prototype[meth];
121-
CdkToolkitClass.prototype[meth] = function methFunc (...args) {
122-
setSdkOptions(this.props.sdkProvider.sdkOptions);
145+
CdkToolkitClass.prototype[meth] = async function methFunc (...args) {
146+
await setSdkOptions(this.props.sdkProvider.sdkOptions);
123147
return original.bind(this).apply(this, args);
124148
};
125149
});
126150
};
127151

128152
const patchCurrentAccount = (SDK) => {
129153
const currentAccountOrig = SDK.prototype.currentAccount;
130-
SDK.prototype.currentAccount = function currentAccount () {
154+
SDK.prototype.currentAccount = async function currentAccount () {
131155
const {config} = this;
132-
setSdkOptions(config);
156+
await setSdkOptions(config);
133157
return currentAccountOrig.bind(this)();
134158
};
135159

@@ -149,9 +173,9 @@ const patchToolkitInfo = (ToolkitInfo) => {
149173

150174
const setBucketUrl = function setBucketUrl (object) {
151175
Object.defineProperty(object, "bucketUrl", {
152-
get () {
176+
async get () {
153177
const bucket = this.requireOutput(BUCKET_NAME_OUTPUT);
154-
const domain = this.requireOutput(BUCKET_DOMAIN_NAME_OUTPUT) || getLocalHost();
178+
const domain = this.requireOutput(BUCKET_DOMAIN_NAME_OUTPUT) || await getLocalHost();
155179
return `https://${domain.replace(`${bucket}.`, "")}:${port}/${bucket}`;
156180
}
157181
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "aws-cdk-local",
33
"description": "CDK Toolkit for use with LocalStack",
4-
"version": "2.15.0",
4+
"version": "2.16.0",
55
"bin": {
66
"cdklocal": "bin/cdklocal"
77
},

0 commit comments

Comments
 (0)