firebase / firebase-tools

The Firebase Command Line Tools
MIT License
4.02k stars 943 forks source link

Emulator fails to load functions with nested `TernalyExpression` for a runWith parameter #7755

Open tsuburin opened 1 month ago

tsuburin commented 1 month ago

[REQUIRED] Environment info

firebase-tools: 13.20.0

Platform: Debian on WSL2

[REQUIRED] Test case

// functions/index.js
const { runWith } = require("firebase-functions/v1");
const { params } = require("firebase-functions");

exports.f = runWith({
  serviceAccount: params.projectID.equals("xxx").thenElse(
    "aaa",
    params.projectID.equals("yyy").thenElse("bbb", "ccc")
  )
}).https.onCall(() => { })

[REQUIRED] Steps to reproduce

  1. Create a firebase project with functions enabled by firebase init
  2. Replace functions/index.js with the test case above
  3. Run firebase emulators:start --project demo-xxx

[REQUIRED] Expected behavior

The function f is loaded successfully.

[REQUIRED] Actual behavior

Emulator fails to load the function.

⬢  functions: Failed to load function definition from source: FirebaseError: CEL tried to evaluate param."xxx" ? "aaa" : params.PROJECT_ID == "yyy" in a context which only permits literal values
firebase-debug.log [debug] [2024-09-27T02:04:52.227Z] ---------------------------------------------------------------------- [debug] [2024-09-27T02:04:52.228Z] Command: /home/tsubu/.asdf/installs/nodejs/18.20.4/bin/node /home/tsubu/.asdf/installs/nodejs/18.20.4/bin/firebase --debug emulators:start --project demo-xxx [debug] [2024-09-27T02:04:52.228Z] CLI Version: 13.20.0 [debug] [2024-09-27T02:04:52.229Z] Platform: linux [debug] [2024-09-27T02:04:52.229Z] Node Version: v18.20.4 [debug] [2024-09-27T02:04:52.229Z] Time: Fri Sep 27 2024 11:04:52 GMT+0900 (Japan Standard Time) [debug] [2024-09-27T02:04:52.229Z] ---------------------------------------------------------------------- [debug] [debug] [2024-09-27T02:04:52.269Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"] [debug] Failed to authenticate, have you run firebase login? [warn] ⚠ emulators: You are not currently authenticated so some features may not work correctly. Please run firebase login to authenticate the CLI. [info] i emulators: Starting emulators: functions {"metadata":{"emulator":{"name":"hub"},"message":"Starting emulators: functions"}} [info] i emulators: Detected demo project ID "demo-xxx", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail. {"metadata":{"emulator":{"name":"hub"},"message":"Detected demo project ID \"demo-xxx\", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail."}} [debug] [2024-09-27T02:04:52.300Z] assigned listening specs for emulators {"user":{"hub":[{"address":"127.0.0.1","family":"IPv4","port":4400}],"ui":[{"address":"127.0.0.1","family":"IPv4","port":4000}],"logging":[{"address":"127.0.0.1","family":"IPv4","port":4500}]},"metadata":{"message":"assigned listening specs for emulators"}} [debug] [2024-09-27T02:04:52.303Z] [hub] writing locator at /tmp/hub-demo-xxx.json [debug] [2024-09-27T02:04:52.306Z] late-assigned ports for functions and eventarc emulators {"user":{"hub":[{"address":"127.0.0.1","family":"IPv4","port":4400}],"ui":[{"address":"127.0.0.1","family":"IPv4","port":4000}],"logging":[{"address":"127.0.0.1","family":"IPv4","port":4500}],"functions":[{"address":"127.0.0.1","family":"IPv4","port":5001}],"eventarc":[{"address":"127.0.0.1","family":"IPv4","port":9299}],"tasks":[{"address":"127.0.0.1","family":"IPv4","port":9499}]},"metadata":{"message":"late-assigned ports for functions and eventarc emulators"}} [warn] ⚠ functions: You are not signed in to the Firebase CLI. If you have authorized this machine using gcloud application-default credentials those may be discovered and used to access production services. {"metadata":{"emulator":{"name":"functions"},"message":"You are not signed in to the Firebase CLI. If you have authorized this machine using gcloud application-default credentials those may be discovered and used to access production services."}} [debug] [2024-09-27T02:04:52.313Z] Ignoring unsupported arg: auto_download {"metadata":{"emulator":{"name":"ui"},"message":"Ignoring unsupported arg: auto_download"}} [debug] [2024-09-27T02:04:52.314Z] Ignoring unsupported arg: port {"metadata":{"emulator":{"name":"ui"},"message":"Ignoring unsupported arg: port"}} [debug] [2024-09-27T02:04:52.314Z] Starting Emulator UI with command {"binary":"node","args":["/home/tsubu/.cache/firebase/emulators/ui-v1.13.0/server/server.mjs"],"optionalArgs":[],"joinArgs":false,"shell":false,"port":4000} {"metadata":{"emulator":{"name":"ui"},"message":"Starting Emulator UI with command {\"binary\":\"node\",\"args\":[\"/home/tsubu/.cache/firebase/emulators/ui-v1.13.0/server/server.mjs\"],\"optionalArgs\":[],\"joinArgs\":false,\"shell\":false,\"port\":4000}"}} [info] i ui: Emulator UI logging to ui-debug.log {"metadata":{"emulator":{"name":"ui"},"message":"Emulator UI logging to \u001b[1mui-debug.log\u001b[22m"}} [debug] [2024-09-27T02:04:52.392Z] Web / API server started at 127.0.0.1:4000 {"metadata":{"emulator":{"name":"ui"},"message":"Web / API server started at 127.0.0.1:4000\n"}} [info] i functions: Watching "/home/tsubu/test/functions" for Cloud Functions... {"metadata":{"emulator":{"name":"functions"},"message":"Watching \"/home/tsubu/test/functions\" for Cloud Functions..."}} [debug] [2024-09-27T02:04:52.537Z] Validating nodejs source [warn] ⚠ functions: package.json indicates an outdated version of firebase-functions. Please upgrade using npm install --save firebase-functions@latest in your functions directory. [warn] ⚠ functions: Please note that there will be breaking changes when you upgrade. [debug] [2024-09-27T02:04:53.004Z] > [functions] package.json contents: { "name": "functions", "description": "Cloud Functions for Firebase", "scripts": { "serve": "firebase emulators:start --only functions", "shell": "firebase functions:shell", "start": "npm run shell", "deploy": "firebase deploy --only functions", "logs": "firebase functions:log" }, "engines": { "node": "18" }, "main": "index.js", "dependencies": { "firebase-admin": "^12.1.0", "firebase-functions": "^5.0.0" }, "devDependencies": { "firebase-functions-test": "^3.1.0" }, "private": true } [debug] [2024-09-27T02:04:53.004Z] Building nodejs source [debug] [2024-09-27T02:04:53.005Z] Failed to find version of module node: reached end of search path /home/tsubu/test/functions/node_modules [info] ✔ functions: Using node@18 from host. [debug] [2024-09-27T02:04:53.007Z] Could not find functions.yaml. Must use http discovery [debug] [2024-09-27T02:04:53.010Z] Found firebase-functions binary at '/home/tsubu/test/functions/node_modules/.bin/firebase-functions' [info] Serving at port 8682 [debug] [2024-09-27T02:04:53.201Z] Got response from /__/functions.yaml {"endpoints":{"f":{"platform":"gcfv1","labels":{},"availableMemoryMb":null,"timeoutSeconds":null,"minInstances":null,"maxInstances":null,"ingressSettings":null,"serviceAccountEmail":"{{ params.PROJECT_ID == \"xxx\" ? \"aaa\" : params.PROJECT_ID == \"yyy\" ? \"bbb\" : \"ccc\" }}","vpc":null,"callableTrigger":{},"entryPoint":"f"}},"specVersion":"v1alpha1","requiredAPIs":[],"extensions":{}} [error] ⬢ functions: Failed to load function definition from source: FirebaseError: CEL tried to evaluate param."xxx" ? "aaa" : params.PROJECT_ID == "yyy" in a context which only permits literal values {"metadata":{"emulator":{"name":"functions"},"message":"Failed to load function definition from source: FirebaseError: CEL tried to evaluate param.\"xxx\" ? \"aaa\" : params.PROJECT_ID == \"yyy\" in a context which only permits literal values"}} [debug] [2024-09-27T02:04:53.218Z] Could not find VSCode notification endpoint: FetchError: request to http://localhost:40001/vscode/notify failed, reason: connect ECONNREFUSED 127.0.0.1:40001 [info] ┌─────────────────────────────────────────────────────────────┐ │ ✔ All emulators ready! It is now safe to connect your app. │ │ i View Emulator UI at http://127.0.0.1:4000/ │ └─────────────────────────────────────────────────────────────┘ ┌───────────┬────────────────┬─────────────────────────────────┐ │ Emulator │ Host:Port │ View in Emulator UI │ ├───────────┼────────────────┼─────────────────────────────────┤ │ Functions │ 127.0.0.1:5001 │ http://127.0.0.1:4000/functions │ └───────────┴────────────────┴─────────────────────────────────┘ Emulator Hub running at 127.0.0.1:4400 Other reserved ports: 4500 Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files. [debug] [2024-09-27T02:04:55.246Z] Received signal SIGINT (Ctrl-C) 1 [info] [info] i emulators: Received SIGINT (Ctrl-C) for the first time. Starting a clean shutdown. [info] i emulators: Please wait for a clean shutdown or send the SIGINT (Ctrl-C) signal again to stop right now. [info] i emulators: Shutting down emulators. {"metadata":{"emulator":{"name":"hub"},"message":"Shutting down emulators."}} [info] i ui: Stopping Emulator UI {"metadata":{"emulator":{"name":"ui"},"message":"Stopping Emulator UI"}} [warn] ⚠ Emulator UI has exited upon receiving signal: SIGINT [info] i functions: Stopping Functions Emulator {"metadata":{"emulator":{"name":"functions"},"message":"Stopping Functions Emulator"}} [info] i eventarc: Stopping Eventarc Emulator {"metadata":{"emulator":{"name":"eventarc"},"message":"Stopping Eventarc Emulator"}} [info] i tasks: Stopping Cloud Tasks Emulator {"metadata":{"emulator":{"name":"tasks"},"message":"Stopping Cloud Tasks Emulator"}} [info] i hub: Stopping emulator hub {"metadata":{"emulator":{"name":"hub"},"message":"Stopping emulator hub"}} [info] i logging: Stopping Logging Emulator {"metadata":{"emulator":{"name":"logging"},"message":"Stopping Logging Emulator"}} [debug] [2024-09-27T02:04:55.255Z] Could not find VSCode notification endpoint: FetchError: request to http://localhost:40001/vscode/notify failed, reason: connect ECONNREFUSED 127.0.0.1:40001
tsuburin commented 1 month ago

I also confirmed the following.

tsuburin commented 1 month ago

I also confirmed that not only string but number behaves the same.

// functions/index.js
const { runWith } = require("firebase-functions/v1");
const { params } = require("firebase-functions");

exports.f = runWith({
  maxInstances: params.projectID.equals("xxx").thenElse(
    1,
    params.projectID.equals("yyy").thenElse(2, 3)
  ),
}).https.onCall(() => { })
tsuburin commented 1 month ago

I suspect that ternaryRegexp causes the problem. Since it uses greedy quantifier (.+), it regards the last '?' and ':' as the delimiters. Thus, if the actual else expression contains '?' or ':' in itself, the structure is interpreted incorrectly. For example,

params.projectID == "xxx" ? 1 : params.projectId == "yyy" ? 2 : 3

seems to be interpreted as

( params.projectID == "xxx" ? 1 : params.projectId == "yyy" ) ? 2 : 3

I guess even if we used non-greedy quantifier, e.g. (.+?), the similar problem would happen.

aalej commented 1 month ago

Hey @tsuburin, sorry we weren’t able to respond to this sooner, and thanks for creating a detailed report and for sharing your observations. I’m able to reproduce the error you mentioned, I’ll notify our engineering team so they can take a look.