Repository: firebase/firebase-tools
Pull request #5491 (2023-02-07 01:07:21)

Make Hosting's Run rewrite for v2 functions use API responses
GCF is looking at allowing non-Run compatible names and converting them in the backend. This makes the CLI forwards compatible with this change (though further changes will be necessary to relax the function ID regex)


Patch:
diff --git a/src/deploy/functions/backend.ts b/src/deploy/functions/backend.ts
index cb7d0d65b..464e06b85 100644
--- a/src/deploy/functions/backend.ts
+++ b/src/deploy/functions/backend.ts
@@ -366,6 +366,11 @@ export type Endpoint = TargetIds &
 
     // Marked as true if a user specifically called this function or codebase with the --only flag.
     targetedByOnly?: boolean;
+
+    // Output only. For v2 functions, this is the run service ID.
+    // This may eventually be different than id because GCF is going to start
+    // doing name translations
+    runServiceId?: string;
   };
 
 export interface RequiredAPI {
diff --git a/src/deploy/hosting/convertConfig.ts b/src/deploy/hosting/convertConfig.ts
index f6669343b..58bffa53b 100644
--- a/src/deploy/hosting/convertConfig.ts
+++ b/src/deploy/hosting/convertConfig.ts
@@ -213,7 +213,7 @@ export async function convertConfig(
       const apiRewrite: api.Rewrite = {
         ...target,
         run: {
-          serviceId: endpoint.id,
+          serviceId: endpoint.runServiceId ?? endpoint.id,
           region: endpoint.region,
         },
       };
diff --git a/src/gcp/cloudfunctionsv2.ts b/src/gcp/cloudfunctionsv2.ts
index e50aeca52..cf924e571 100644
--- a/src/gcp/cloudfunctionsv2.ts
+++ b/src/gcp/cloudfunctionsv2.ts
@@ -691,5 +691,14 @@ export function endpointFromFunction(gcfFunction: CloudFunction): backend.Endpoi
   if (gcfFunction.labels?.[HASH_LABEL]) {
     endpoint.hash = gcfFunction.labels[HASH_LABEL];
   }
+  const serviceName = gcfFunction.serviceConfig.service;
+  if (!serviceName) {
+    logger.debug(
+      "Got a v2 function without a service name." +
+        "Maybe we've migrated to using the v2 API everywhere and missed this code"
+    );
+  } else {
+    endpoint.runServiceId = utils.last(serviceName.split("/"));
+  }
   return endpoint;
 }


Test patch:
diff --git a/src/test/deploy/hosting/convertConfig.spec.ts b/src/test/deploy/hosting/convertConfig.spec.ts
index 665656bf3..317b0924a 100644
--- a/src/test/deploy/hosting/convertConfig.spec.ts
+++ b/src/test/deploy/hosting/convertConfig.spec.ts
@@ -9,7 +9,8 @@ import * as api from "../../../hosting/api";
 import { FirebaseError } from "../../../error";
 import { Payload } from "../../../deploy/functions/args";
 
-const FUNCTION_ID = "function";
+const FUNCTION_ID = "functionId";
+const SERVICE_ID = "function-id";
 const PROJECT_ID = "project";
 const REGION = "region";
 
@@ -36,6 +37,9 @@ function endpoint(opts?: Partial<backend.Endpoint>): backend.Endpoint {
   ) {
     ret.httpsTrigger = {};
   }
+  if (opts?.platform === "gcfv2") {
+    ret.runServiceId = opts?.id ?? SERVICE_ID;
+  }
   return ret as backend.Endpoint;
 }
 
@@ -178,7 +182,7 @@ describe("convertConfig", () => {
       name: "defaults to a us-central1 rewrite if one is avaiable, v2 edition",
       input: { rewrites: [{ glob: "/foo", function: { functionId: FUNCTION_ID } }] },
       want: {
-        rewrites: [{ glob: "/foo", run: { region: "us-central1", serviceId: FUNCTION_ID } }],
+        rewrites: [{ glob: "/foo", run: { region: "us-central1", serviceId: SERVICE_ID } }],
       },
       functionsPayload: {
         functions: {
@@ -192,6 +196,7 @@ describe("convertConfig", () => {
                 region: "europe-west2",
                 platform: "gcfv2",
                 httpsTrigger: {},
+                runServiceId: SERVICE_ID,
               },
               {
                 id: FUNCTION_ID,
@@ -201,6 +206,7 @@ describe("convertConfig", () => {
                 region: "us-central1",
                 platform: "gcfv2",
                 httpsTrigger: {},
+                runServiceId: SERVICE_ID,
               }
             ),
             haveBackend: backend.empty(),
@@ -236,7 +242,7 @@ describe("convertConfig", () => {
     {
       name: "rewrites referencing CF3v2 functions being deployed are changed to Cloud Run (during release)",
       input: { rewrites: [{ regex: "/foo$", function: { functionId: FUNCTION_ID } }] },
-      want: { rewrites: [{ regex: "/foo$", run: { serviceId: FUNCTION_ID, region: REGION } }] },
+      want: { rewrites: [{ regex: "/foo$", run: { serviceId: SERVICE_ID, region: REGION } }] },
       functionsPayload: {
         functions: {
           default: {
@@ -248,6 +254,7 @@ describe("convertConfig", () => {
               region: REGION,
               platform: "gcfv2",
               httpsTrigger: {},
+              runServiceId: SERVICE_ID,
             }),
             haveBackend: backend.empty(),
           },
@@ -262,7 +269,7 @@ describe("convertConfig", () => {
         ],
       },
       want: {
-        rewrites: [{ regex: "/foo$", run: { serviceId: FUNCTION_ID, region: "us-central1" } }],
+        rewrites: [{ regex: "/foo$", run: { serviceId: SERVICE_ID, region: "us-central1" } }],
       },
       existingBackend: backend.of(endpoint({ platform: "gcfv2", region: "us-central1" })),
     },
@@ -275,7 +282,7 @@ describe("convertConfig", () => {
       },
       existingBackend: backend.of(endpoint({ platform: "gcfv2", region: "us-central1" })),
       want: {
-        rewrites: [{ regex: "/foo$", run: { serviceId: FUNCTION_ID, region: "us-central1" } }],
+        rewrites: [{ regex: "/foo$", run: { serviceId: SERVICE_ID, region: "us-central1" } }],
       },
     },
     {
diff --git a/src/test/gcp/cloudfunctionsv2.spec.ts b/src/test/gcp/cloudfunctionsv2.spec.ts
index 54c4f6236..16e05e778 100644
--- a/src/test/gcp/cloudfunctionsv2.spec.ts
+++ b/src/test/gcp/cloudfunctionsv2.spec.ts
@@ -382,6 +382,23 @@ describe("cloudfunctionsv2", () => {
       });
     });
 
+    it("should copy run service IDs", () => {
+      const fn: cloudfunctionsv2.CloudFunction = {
+        ...HAVE_CLOUD_FUNCTION_V2,
+        serviceConfig: {
+          ...HAVE_CLOUD_FUNCTION_V2.serviceConfig,
+          service: "projects/p/locations/l/services/service-id",
+        },
+      };
+      expect(cloudfunctionsv2.endpointFromFunction(fn)).to.deep.equal({
+        ...ENDPOINT,
+        httpsTrigger: {},
+        platform: "gcfv2",
+        uri: RUN_URI,
+        runServiceId: "service-id",
+      });
+    });
+
     it("should translate event triggers", () => {
       let want: backend.Endpoint = {
         ...ENDPOINT,