Skip to content

Commit 363d3f8

Browse files
authored
fix: make runtime and buildConfig optional to prevent crash on parallel deploys (#9455)
Make runtime and buildConfig optional for cloud functions. This prevents the TypeError: Cannot read properties of undefined (reading 'runtime') crash that occurred during parallel deployments when the API returned a function object without buildConfig - which could happen during early part of function creation.
1 parent 5965754 commit 363d3f8

File tree

9 files changed

+35
-15
lines changed

9 files changed

+35
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
- Fixed a crash during parallel deployments when buildConfig is empty (#9455)
12
- [Added] support for new google-genai plugin during `init genkit` (#8957)
23
- Updated to v2.17.1 of the Data Connect emulator, which fixes an admin SDK bug for operation without argument #9449 (#9454).
34
- Fixed "Precondition failed" error when updating GCFv2 functions in a FAILED state without code changes.

src/deploy/functions/backend.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ describe("Backend", () => {
281281
cpu: 1,
282282
httpsTrigger: {},
283283
runServiceId: HAVE_CLOUD_FUNCTION_V2.serviceConfig?.service,
284-
source: HAVE_CLOUD_FUNCTION_V2.buildConfig.source,
284+
source: HAVE_CLOUD_FUNCTION_V2.buildConfig?.source,
285285
uri: HAVE_CLOUD_FUNCTION_V2.serviceConfig?.uri,
286286
}),
287287
);

src/deploy/functions/backend.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ export type Endpoint = TargetIds &
354354
Triggered & {
355355
entryPoint: string;
356356
platform: FunctionsPlatform;
357-
runtime: Runtime;
357+
runtime?: Runtime;
358358

359359
// Output only
360360
// "Codebase" is not part of the container contract. Instead, it's value is provided by firebase.json or derived

src/deploy/functions/release/fabricator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,7 @@ export class Fabricator {
759759
}
760760

761761
logOpStart(op: string, endpoint: backend.Endpoint): void {
762-
const runtime = RUNTIMES[endpoint.runtime].friendly;
762+
const runtime = endpoint.runtime ? RUNTIMES[endpoint.runtime].friendly : "unknown";
763763
const platform = getHumanFriendlyPlatformName(endpoint.platform);
764764
const label = helper.getFunctionLabel(endpoint);
765765
utils.logLabeledBullet(

src/functions/secrets.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ describe("functions/secret", () => {
556556
};
557557
const fn: Omit<gcf.CloudFunction, gcf.OutputOnlyFields> = {
558558
name: `projects/${endpoint.project}/locations/${endpoint.region}/functions/${endpoint.id}`,
559-
runtime: endpoint.runtime,
559+
runtime: endpoint.runtime!,
560560
entryPoint: endpoint.entryPoint,
561561
secretEnvironmentVariables: [{ ...sev, version: "2" }],
562562
};

src/gcp/cloudfunctions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ export function functionFromEndpoint(
622622
);
623623
}
624624

625-
if (!supported.isRuntime(endpoint.runtime)) {
625+
if (!endpoint.runtime || !supported.isRuntime(endpoint.runtime)) {
626626
throw new FirebaseError(
627627
"Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
628628
" This should never happen",

src/gcp/cloudfunctionsv2.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,24 @@ describe("cloudfunctionsv2", () => {
798798
}),
799799
).to.deep.equal(expectedEndpoint);
800800
});
801+
802+
it("should convert function without buildConfig", () => {
803+
const expectedEndpoint = {
804+
...ENDPOINT,
805+
platform: "gcfv2",
806+
httpsTrigger: {},
807+
uri: GCF_URL,
808+
entryPoint: "",
809+
runtime: undefined,
810+
source: undefined,
811+
};
812+
expect(
813+
cloudfunctionsv2.endpointFromFunction({
814+
...HAVE_CLOUD_FUNCTION_V2,
815+
buildConfig: undefined,
816+
}),
817+
).to.deep.equal(expectedEndpoint);
818+
});
801819
});
802820

803821
describe("createFunction", () => {

src/gcp/cloudfunctionsv2.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export type RetryPolicy =
4646

4747
/** Settings for building a container out of the customer source. */
4848
export interface BuildConfig {
49-
runtime: supported.Runtime;
49+
runtime?: supported.Runtime;
5050
entryPoint: string;
5151
source: Source;
5252
sourceToken?: string;
@@ -158,7 +158,7 @@ export interface EventTrigger {
158158
interface CloudFunctionBase {
159159
name: string;
160160
description?: string;
161-
buildConfig: BuildConfig;
161+
buildConfig?: BuildConfig;
162162
serviceConfig?: ServiceConfig;
163163
eventTrigger?: EventTrigger;
164164
labels?: Record<string, string> | null;
@@ -172,6 +172,7 @@ export type OutputCloudFunction = CloudFunctionBase & {
172172
};
173173

174174
export type InputCloudFunction = CloudFunctionBase & {
175+
buildConfig: BuildConfig;
175176
// serviceConfig is required.
176177
serviceConfig: ServiceConfig;
177178
};
@@ -228,7 +229,7 @@ function functionsOpLogReject(func: InputCloudFunction, type: string, err: any):
228229
"Either reduce this function's maximum instances, or request a quota increase on the underlying Cloud Run service " +
229230
"at https://cloud.google.com/run/quotas.",
230231
);
231-
const suggestedFix = func.buildConfig.runtime.startsWith("python")
232+
const suggestedFix = func.buildConfig.runtime?.startsWith("python")
232233
? "firebase_functions.options.set_global_options(max_instances=10)"
233234
: "setGlobalOptions({maxInstances: 10})";
234235
utils.logLabeledWarning(
@@ -436,7 +437,7 @@ export function functionFromEndpoint(endpoint: backend.Endpoint): InputCloudFunc
436437
);
437438
}
438439

439-
if (!supported.isRuntime(endpoint.runtime)) {
440+
if (endpoint.runtime && !supported.isRuntime(endpoint.runtime)) {
440441
throw new FirebaseError(
441442
"Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
442443
" This should never happen",
@@ -446,7 +447,7 @@ export function functionFromEndpoint(endpoint: backend.Endpoint): InputCloudFunc
446447
const gcfFunction: InputCloudFunction = {
447448
name: backend.functionName(endpoint),
448449
buildConfig: {
449-
runtime: endpoint.runtime,
450+
runtime: endpoint.runtime || undefined,
450451
entryPoint: endpoint.entryPoint,
451452
source: {
452453
storageSource: endpoint.source?.storageSource,
@@ -664,7 +665,7 @@ export function endpointFromFunction(gcfFunction: OutputCloudFunction): backend.
664665
trigger = { httpsTrigger: {} };
665666
}
666667

667-
if (!supported.isRuntime(gcfFunction.buildConfig.runtime)) {
668+
if (gcfFunction.buildConfig?.runtime && !supported.isRuntime(gcfFunction.buildConfig.runtime)) {
668669
logger.debug("GCFv2 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
669670
}
670671

@@ -674,9 +675,9 @@ export function endpointFromFunction(gcfFunction: OutputCloudFunction): backend.
674675
project,
675676
region,
676677
...trigger,
677-
entryPoint: gcfFunction.buildConfig.entryPoint,
678-
runtime: gcfFunction.buildConfig.runtime,
679-
source: gcfFunction.buildConfig.source,
678+
entryPoint: gcfFunction.buildConfig?.entryPoint || "",
679+
runtime: gcfFunction.buildConfig?.runtime || undefined,
680+
source: gcfFunction.buildConfig?.source,
680681
};
681682
if (gcfFunction.serviceConfig) {
682683
proto.copyIfPresent(

src/gcp/runv2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ export function serviceFromEndpoint(
519519
): Omit<Service, ServiceOutputFields> {
520520
const labels: Record<string, string> = {
521521
...endpoint.labels,
522-
[RUNTIME_LABEL]: endpoint.runtime,
522+
...(endpoint.runtime ? { [RUNTIME_LABEL]: endpoint.runtime } : {}),
523523
[CLIENT_NAME_LABEL]: "firebase-functions",
524524
};
525525

0 commit comments

Comments
 (0)