[go: nahoru, domu]

Skip to content

Commit

Permalink
Enable --inspect-functions across multiple codebases (#6854)
Browse files Browse the repository at this point in the history
* Enable --inspect-functions across multiple codebases

* Fix comment and add changelog

* Fix test failures

* Warn customers that non-standard inspector ports might require configuration
  • Loading branch information
inlined committed Mar 13, 2024
1 parent 6c88d82 commit ede7d0b
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 13 deletions.
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

0 comments on commit ede7d0b

Please sign in to comment.