Skip to content

Commit 5463aad

Browse files
authored
Fix data connect emulator listen address. (#7211)
* Fix data connect emulator listen address. * Add PR link to CHANGELOG.md.
1 parent c24a3d9 commit 5463aad

File tree

7 files changed

+58
-47
lines changed

7 files changed

+58
-47
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
- Fixes an issue where dataconnect:sql:migrate still prompts for confirmation even with `--force`. (#7208)
22
- Update to Firebase Data Connect Emulator version 1.1.18 which contains code generation bug fixes, surfacing schema migration errors when a diff remains after migration, and a fix to allow the local connection string to be empty at startup.
3+
- Fixes an issue where the dataconnect emulator listens on all addresses by default instead of just localhost (#7211).

src/commands/dataconnect-sdk-generate.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as path from "path";
22

33
import { Command } from "../command";
44
import { Options } from "../options";
5-
import { DataConnectEmulator, DataConnectEmulatorArgs } from "../emulator/dataconnectEmulator";
5+
import { DataConnectEmulator } from "../emulator/dataconnectEmulator";
66
import { needProjectId } from "../projectUtils";
77
import { load } from "../dataconnect/load";
88
import { readFirebaseJson } from "../dataconnect/fileUtils";
@@ -21,16 +21,12 @@ export const command = new Command("dataconnect:sdk:generate")
2121
configDir = path.resolve(path.join(cwd), configDir);
2222
}
2323
const serviceInfo = await load(projectId, service.location, configDir);
24-
const args: DataConnectEmulatorArgs = {
25-
projectId,
26-
configDir,
27-
auto_download: true,
28-
rc: options.rc,
29-
locationId: service.location,
30-
};
31-
const dataconnectEmulator = new DataConnectEmulator(args);
3224
for (const conn of serviceInfo.connectorInfo) {
33-
const output = await dataconnectEmulator.generate(conn.connectorYaml.connectorId);
25+
const output = await DataConnectEmulator.generate({
26+
configDir,
27+
locationId: service.location,
28+
connectorId: conn.connectorYaml.connectorId,
29+
});
3430
logger.info(output);
3531
logger.info(`Generated SDKs for ${conn.connectorYaml.connectorId}`);
3632
}

src/dataconnect/build.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
1-
import { DataConnectEmulator, DataConnectEmulatorArgs } from "../emulator/dataconnectEmulator";
1+
import { DataConnectEmulator } from "../emulator/dataconnectEmulator";
22
import { Options } from "../options";
33
import { FirebaseError } from "../error";
44
import { prettify } from "./graphqlError";
55
import { DeploymentMetadata } from "./types";
66

77
export async function build(options: Options, configDir: string): Promise<DeploymentMetadata> {
8-
// We can build even if there is no project declared.
9-
const projectId = options.project ?? "demo-test";
10-
const args: DataConnectEmulatorArgs = {
11-
projectId,
12-
configDir,
13-
auto_download: true,
14-
rc: options.rc,
15-
};
16-
const dataconnectEmulator = new DataConnectEmulator(args);
17-
const buildResult = await dataconnectEmulator.build();
8+
const buildResult = await DataConnectEmulator.build({ configDir });
189
if (buildResult?.errors?.length) {
1910
throw new FirebaseError(
2011
`There are errors in your schema and connector files:\n${buildResult.errors.map(prettify).join("\n")}`,

src/emulator/controller.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,6 @@ export async function startAll(
821821
}
822822

823823
if (listenForEmulator.dataconnect) {
824-
const dataConnectAddr = legacyGetFirstAddr(Emulators.DATACONNECT);
825824
const config = readFirebaseJson(options.config);
826825
if (!config.length) {
827826
throw new FirebaseError("No Data Connect service found in firebase.json");
@@ -836,8 +835,7 @@ export async function startAll(
836835
configDir = path.resolve(path.join(cwd), configDir);
837836
}
838837
const dataConnectEmulator = new DataConnectEmulator({
839-
host: dataConnectAddr.host,
840-
port: dataConnectAddr.port,
838+
listen: listenForEmulator.dataconnect,
841839
projectId,
842840
auto_download: true,
843841
configDir,

src/emulator/dataconnectEmulator.ts

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,39 @@ import * as childProcess from "child_process";
33
import { dataConnectLocalConnString } from "../api";
44
import { Constants } from "./constants";
55
import { getPID, start, stop, downloadIfNecessary } from "./downloadableEmulators";
6-
import { EmulatorInfo, EmulatorInstance, Emulators } from "./types";
6+
import { EmulatorInfo, EmulatorInstance, Emulators, ListenSpec } from "./types";
77
import { FirebaseError } from "../error";
88
import { EmulatorLogger } from "./emulatorLogger";
99
import { RC } from "../rc";
1010
import { BuildResult, requiresVector } from "../dataconnect/types";
11+
import { listenSpecsToString } from "./portUtils";
1112

1213
export interface DataConnectEmulatorArgs {
1314
projectId?: string;
14-
port?: number;
15-
host?: string;
16-
configDir?: string;
15+
listen: ListenSpec[];
16+
configDir: string;
1717
locationId?: string;
1818
auto_download?: boolean;
1919
rc: RC;
2020
}
2121

22-
const grpcDefaultPort = 9510;
22+
export interface DataConnectGenerateArgs {
23+
configDir: string;
24+
locationId: string;
25+
connectorId: string;
26+
}
27+
28+
export interface DataConnectBuildArgs {
29+
configDir: string;
30+
}
2331

2432
export class DataConnectEmulator implements EmulatorInstance {
2533
constructor(private args: DataConnectEmulatorArgs) {}
2634
private logger = EmulatorLogger.forEmulator(Emulators.DATACONNECT);
2735

2836
async start(): Promise<void> {
29-
const port = this.args.port || Constants.getDefaultPort(Emulators.DATACONNECT);
3037
this.logger.log("DEBUG", `Using Postgres connection string: ${this.getLocalConectionString()}`);
31-
const info = await this.build();
38+
const info = await DataConnectEmulator.build({ configDir: this.args.configDir });
3239
if (requiresVector(info.metadata)) {
3340
if (Constants.isDemoProject(this.args.projectId)) {
3441
this.logger.logLabeled(
@@ -46,8 +53,7 @@ export class DataConnectEmulator implements EmulatorInstance {
4653
}
4754
return start(Emulators.DATACONNECT, {
4855
...this.args,
49-
http_port: port,
50-
grpc_port: grpcDefaultPort,
56+
listen: listenSpecsToString(this.args.listen),
5157
config_dir: this.args.configDir,
5258
local_connection_string: this.getLocalConectionString(),
5359
project_id: this.args.projectId,
@@ -65,13 +71,11 @@ export class DataConnectEmulator implements EmulatorInstance {
6571
}
6672

6773
getInfo(): EmulatorInfo {
68-
const host = this.args.host || Constants.getDefaultHost();
69-
const port = this.args.port || Constants.getDefaultPort(Emulators.DATACONNECT);
70-
7174
return {
7275
name: this.getName(),
73-
host,
74-
port,
76+
listen: this.args.listen,
77+
host: this.args.listen[0].address,
78+
port: this.args.listen[0].port,
7579
pid: getPID(Emulators.DATACONNECT),
7680
timeout: 10_000,
7781
};
@@ -80,26 +84,33 @@ export class DataConnectEmulator implements EmulatorInstance {
8084
return Emulators.DATACONNECT;
8185
}
8286

83-
async generate(connectorId: string): Promise<string> {
87+
static async generate(args: DataConnectGenerateArgs): Promise<string> {
8488
const commandInfo = await downloadIfNecessary(Emulators.DATACONNECT);
8589
const cmd = [
8690
"generate",
87-
`--service_location=${this.args.locationId}`,
88-
`--config_dir=${this.args.configDir}`,
89-
`--connector_id=${connectorId}`,
91+
`--service_location=${args.locationId}`,
92+
`--config_dir=${args.configDir}`,
93+
`--connector_id=${args.connectorId}`,
9094
];
9195
const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8" });
9296
if (res.error) {
93-
throw new FirebaseError(`Error starting up Data Connect emulator: ${res.error}`);
97+
throw new FirebaseError(`Error starting up Data Connect generate: ${res.error.message}`, {
98+
original: res.error,
99+
});
94100
}
95101
return res.stdout;
96102
}
97103

98-
async build(): Promise<BuildResult> {
104+
static async build(args: DataConnectBuildArgs): Promise<BuildResult> {
99105
const commandInfo = await downloadIfNecessary(Emulators.DATACONNECT);
100-
const cmd = ["build", `--config_dir=${this.args.configDir}`];
106+
const cmd = ["build", `--config_dir=${args.configDir}`];
101107

102108
const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8" });
109+
if (res.error) {
110+
throw new FirebaseError(`Error starting up Data Connect build: ${res.error.message}`, {
111+
original: res.error,
112+
});
113+
}
103114
if (res.stderr) {
104115
throw new FirebaseError(
105116
`Unable to build your Data Connect schema and connectors: ${res.stderr}`,

src/emulator/downloadableEmulators.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ const Commands: { [s in DownloadableEmulators]: DownloadableEmulatorCommand } =
281281
dataconnect: {
282282
binary: getExecPath(Emulators.DATACONNECT),
283283
args: ["dev"],
284-
optionalArgs: ["http_port", "grpc_port", "config_dir", "local_connection_string", "project_id"],
284+
optionalArgs: ["listen", "config_dir", "local_connection_string", "project_id"],
285285
joinArgs: true,
286286
shell: true,
287287
},

src/emulator/portUtils.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,9 @@ const EMULATOR_CAN_LISTEN_ON_PRIMARY_ONLY: Record<PortName, boolean> = {
184184
firestore: true,
185185
"firestore.websocket": true,
186186
pubsub: true,
187-
dataconnect: true,
187+
188+
// External processes that accepts multiple listen specs.
189+
dataconnect: false,
188190

189191
// Listening on multiple addresses to maximize the chance of discovery.
190192
hub: false,
@@ -463,3 +465,15 @@ function listenSpec(lookup: dns.LookupAddress, port: number): ListenSpec {
463465
port: port,
464466
};
465467
}
468+
469+
/**
470+
* Return a comma-separated list of host:port from specs.
471+
*/
472+
export function listenSpecsToString(specs: ListenSpec[]): string {
473+
return specs
474+
.map((spec) => {
475+
const host = spec.family === "IPv4" ? spec.address : `[${spec.address}]`;
476+
return `${host}:${spec.port}`;
477+
})
478+
.join(",");
479+
}

0 commit comments

Comments
 (0)