Skip to content

Enable --inspect-functions across multiple codebases #6854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
- Enable dynamic debugger port for functions + support for inspecting multiple codebases (#6854)
- Inject an environment variable in the node functions emulator to tell the google-gax SDK not to look for the metadata service. (#6860)
- Release Firestore Emulator 1.19.3 which fixes ancestor and namespace scope queries for Datastore Mode. This release also fixes internal errors seen across REST API and firebase-js-sdk.
- Inject an environment variable in the node functions emulator to tell the google-gax SDK not to look for the metadata service. (#6860)
1 change: 1 addition & 0 deletions scripts/emulator-tests/functionsEmulator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ describe("FunctionsEmulator", function () {
projectDir: MODULE_ROOT,
emulatableBackends: [TEST_BACKEND],
verbosity: "QUIET",
debugPort: false,
adminSdkConfig: {
projectId: TEST_PROJECT_ID,
databaseURL: `https://${TEST_PROJECT_ID}-default-rtdb.firebaseio.com`,
Expand Down
31 changes: 25 additions & 6 deletions src/emulator/commandUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ export const DEFAULT_CONFIG = new Config(
{},
);

/**
* Utility to be put in the "before" handler for a RTDB or Firestore command
* that supports the emulator. Prints a warning when environment variables
* specify an emulator address.
*/
export function printNoticeIfEmulated(
options: any,
emulator: Emulators.DATABASE | Emulators.FIRESTORE,
Expand All @@ -98,6 +103,11 @@ export function printNoticeIfEmulated(
}
}

/**
* Utility to be put in the "before" handler for a RTDB or Firestore command
* that always talks to production. This warns customers if they've specified
* an emulator port that the command actually talks to production.
*/
export function warnEmulatorNotSupported(
options: any,
emulator: Emulators.DATABASE | Emulators.FIRESTORE,
Expand Down Expand Up @@ -135,6 +145,10 @@ export function warnEmulatorNotSupported(
}
}

/**
* Utility method to be inserted in the "before" function for a command that
* uses the emulator suite.
*/
export async function beforeEmulatorCommand(options: any): Promise<any> {
const optionsWithDefaultConfig = {
...options,
Expand Down Expand Up @@ -170,16 +184,22 @@ export async function beforeEmulatorCommand(options: any): Promise<any> {
}
}

export function parseInspectionPort(options: any): number {
let port = options.inspectFunctions;
if (port === true) {
port = "9229";
/**
* Returns a literal port number if specified or true | false if enabled.
* A true value will later be turned into a dynamic port.
*/
export function parseInspectionPort(options: any): number | boolean {
const port = options.inspectFunctions;
if (typeof port === "undefined") {
return false;
} else if (typeof port === "boolean") {
return port;
}

const parsed = Number(port);
if (isNaN(parsed) || parsed < 1024 || parsed > 65535) {
throw new FirebaseError(
`"${port}" is not a valid port for debugging, please pass an integer between 1024 and 65535.`,
`"${port}" is not a valid port for debugging, please pass an integer between 1024 and 65535 or true for a dynamic port.`,
);
}

Expand Down Expand Up @@ -468,7 +488,6 @@ const JAVA_HINT = "Please make sure Java is installed and on your system PATH.";

/**
* Return whether Java major verion is supported. Throws if Java not available.
*
* @return Java major version (for Java >= 9) or -1 otherwise
*/
export async function checkJavaMajorVersion(): Promise<number> {
Expand Down
8 changes: 3 additions & 5 deletions src/emulator/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,15 +528,13 @@ export async function startAll(
const functionsAddr = legacyGetFirstAddr(Emulators.FUNCTIONS);
const projectId = needProjectId(options);

let inspectFunctions: number | undefined;
if (options.inspectFunctions) {
inspectFunctions = commandUtils.parseInspectionPort(options);

const inspectFunctions = commandUtils.parseInspectionPort(options);
if (inspectFunctions) {
// TODO(samstern): Add a link to documentation
functionsLogger.logLabeled(
"WARN",
"functions",
`You are running the Functions emulator in debug mode (port=${inspectFunctions}). This means that functions will execute in sequence rather than in parallel.`,
`You are running the Functions emulator in debug mode. This means that functions will execute in sequence rather than in parallel.`,
);
}

Expand Down
44 changes: 42 additions & 2 deletions src/emulator/functionsEmulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,12 @@ export interface FunctionsEmulatorArgs {
projectId: string;
projectDir: string;
emulatableBackends: EmulatableBackend[];
debugPort: number | boolean;
account?: Account;
port?: number;
host?: string;
verbosity?: "SILENT" | "QUIET" | "INFO" | "DEBUG";
disabledRuntimeFeatures?: FunctionsRuntimeFeatures;
debugPort?: number;
remoteEmulators?: Record<string, EmulatorInfo>;
adminSdkConfig?: AdminSdkConfig;
projectAlias?: string;
Expand Down Expand Up @@ -224,6 +224,20 @@ export class FunctionsEmulator implements EmulatorInstance {
// When debugging is enabled, the "timeout" feature needs to be disabled so that
// functions don't timeout while a breakpoint is active.
if (this.args.debugPort) {
// N.B. Technically this will create false positives where there is a Node
// and a Python codebase, but there is no good place to check the runtime
// because that may not be present until discovery (e.g. node codebases
// return their runtime based on package.json if not specified in
// firebase.json)
const maybeNodeCodebases = this.args.emulatableBackends.filter(
(b) => !b.runtime || b.runtime.startsWith("node"),
);
if (maybeNodeCodebases.length > 1 && typeof this.args.debugPort === "number") {
throw new FirebaseError(
"Cannot debug on a single port with multiple codebases. " +
"Use --inspect-functions=true to assign dynamic ports to each codebase",
);
}
this.args.disabledRuntimeFeatures = this.args.disabledRuntimeFeatures || {};
this.args.disabledRuntimeFeatures.timeout = true;
this.debugMode = true;
Expand Down Expand Up @@ -1316,8 +1330,34 @@ export class FunctionsEmulator implements EmulatorInstance {
)} --save-dev" in your functions directory`,
);
} else {
let port: number;
if (typeof this.args.debugPort === "number") {
port = this.args.debugPort;
} else {
// Start the search at port 9229 because that is the default node
// inspector port and Chrome et. al. will discover the process without
// additional configuration. Other dynamic ports will need to be added
// manually to the inspector.
port = await portfinder.getPortPromise({ port: 9229 });
if (port === 9229) {
this.logger.logLabeled(
"SUCCESS",
"functions",
`Using debug port 9229 for functions codebase ${backend.codebase}`,
);
} else {
// Give a longer message to warn about non-default ports.
this.logger.logLabeled(
"SUCCESS",
"functions",
`Using debug port ${port} for functions codebase ${backend.codebase}. ` +
"You may need to add manually add this port to your inspector.",
);
}
}

const { host } = this.getInfo();
args.unshift(`--inspect=${connectableHostname(host)}:${this.args.debugPort}`);
args.unshift(`--inspect=${connectableHostname(host)}:${port}`);
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/serve/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Options } from "../options";
import * as projectConfig from "../functions/projectConfig";
import * as utils from "../utils";
import { EmulatorRegistry } from "../emulator/registry";
import { parseInspectionPort } from "../emulator/commandUtils";

export class FunctionsServer {
emulator?: FunctionsEmulator;
Expand Down Expand Up @@ -47,6 +48,8 @@ export class FunctionsServer {
projectAlias: options.projectAlias,
account,
...partialArgs,
// Non-optional; parseInspectionPort will set to false if missing.
debugPort: parseInspectionPort(options),
};

// Normally, these two fields are included in args (and typed as such).
Expand Down