Skip to content
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

IS_FIREBASE_CLI missing on deploy functions @9.13.1 ? #3517

Closed
maganap opened this issue Jun 20, 2021 · 11 comments
Closed

IS_FIREBASE_CLI missing on deploy functions @9.13.1 ? #3517

maganap opened this issue Jun 20, 2021 · 11 comments

Comments

@maganap
Copy link

maganap commented Jun 20, 2021

[REQUIRED] Environment info

firebase-tools: 9.13.1
node: v12.22.1
Platform: macOS 10.15.7

[REQUIRED] Test case

To reduce cold start time in functions, I use to work with a structure like this:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

if ( process.env.IS_FIREBASE_CLI
  || process.env.K_SERVICE == 'test'
) {
  exports.test = functions.https.onCall( (data, context) => {
    console.log(`IS_FIREBASE_CLI: >>${process.env.IS_FIREBASE_CLI}<<`);
  });
}

By using IS_FIREBASE_CLI, I could run the functions shell and deploy functions without issues, until I updated to firebase-tools@9.13.1. Now, I can run the shell but I can't deploy.
I'm getting different errors (today different from yesterday, I can still see it in the terminal but I don't have the firebase-debug.log from yesterday).

Today:

% firebase deploy --only functions:test

=== Deploying to 'xxx'...

i  deploying functions
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
✔  functions: required API cloudbuild.googleapis.com is enabled
✔  functions: required API cloudfunctions.googleapis.com is enabled

Error: An unexpected error has occurred.

Yesterday, it was like the deploy command ran successfully, but it didn't deploy anything.

 % firebase deploy --only functions:onWriteListings

=== Deploying to 'xxx'...

i  deploying functions
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
✔  functions: required API cloudbuild.googleapis.com is enabled
✔  functions: required API cloudfunctions.googleapis.com is enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (85.53 KB) for uploading
✔  functions: functions folder uploaded successfully

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/xxx/overview
maganap@Magas-Mac-mini functions % firebase --version
9.13.1

If I remove the if() statement, it does deploy properly.

I know this use of IS_FIREBASE_CLI is not documented but, I'm still wondering what's happened since IS_FIREBASE_CLI still exists and it seems to be used in other cases.

Any other suggestions to be able to deploy using if ( process.env.K_SERVICE == 'test' ) { ? (which of course won't allow me to deploy since process.env.K_SERVICE is not set while deploying).

Thanks in advance.

[REQUIRED] Steps to reproduce

Just try to deploy the piece of code above with:
firebase deploy --only functions:test

[REQUIRED] Expected behavior

The function test deploys as it used to with earlier versions, including 9.12.1.

[REQUIRED] Actual behavior

The function won't deploy. It is still found by the functions shell, though.

The log below is exactly the same as if I ran the deploy command with if ( false ) { to enclose the function to be deployed.

[debug] [2021-06-20T17:39:29.092Z] ----------------------------------------------------------------------
[debug] [2021-06-20T17:39:29.096Z] Command:       /usr/local/bin/node /usr/local/bin/firebase deploy --only functions:test
[debug] [2021-06-20T17:39:29.096Z] CLI Version:   9.13.1
[debug] [2021-06-20T17:39:29.097Z] Platform:      darwin
[debug] [2021-06-20T17:39:29.097Z] Node Version:  v12.22.1
[debug] [2021-06-20T17:39:29.098Z] Time:          Sun Jun 20 2021 19:39:29 GMT+0200 (Central European Summer Time)
[debug] [2021-06-20T17:39:29.098Z] ----------------------------------------------------------------------
[debug] 
[debug] [2021-06-20T17:39:29.109Z] > 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] [2021-06-20T17:39:29.109Z] > authorizing via signed-in user (xxx@gmail.com)
[debug] [2021-06-20T17:39:29.109Z] [iam] checking project xxx for permissions ["cloudfunctions.functions.create","cloudfunctions.functions.delete","cloudfunctions.functions.get","cloudfunctions.functions.list","cloudfunctions.functions.update","cloudfunctions.operations.get","firebase.projects.get"]
[debug] [2021-06-20T17:39:29.112Z] >>> HTTP REQUEST POST https://cloudresourcemanager.googleapis.com/v1/projects/xxx:testIamPermissions  
 {"permissions":["cloudfunctions.functions.create","cloudfunctions.functions.delete","cloudfunctions.functions.get","cloudfunctions.functions.list","cloudfunctions.functions.update","cloudfunctions.operations.get","firebase.projects.get"]}
[debug] [2021-06-20T17:39:29.855Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:29 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","server-timing":"gfet4t7; dur=637","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:29.857Z] >>> HTTP REQUEST POST https://iam.googleapis.com/v1/projects/xxx/serviceAccounts/xxx@appspot.gserviceaccount.com:testIamPermissions  
 {"permissions":["iam.serviceAccounts.actAs"]}
[debug] [2021-06-20T17:39:30.515Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:30 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[info] 
[info] === Deploying to 'xxx'...
[info] 
[info] i  deploying functions 
[debug] [2021-06-20T17:39:30.523Z] Validating nodejs source
[debug] [2021-06-20T17:39:32.880Z] > [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": "12"
  },
  "main": "index.js",
  "dependencies": {
    "@elastic/elasticsearch": "^7.12.0",
    "@sendgrid/mail": "^7.4.4",
    "firebase-admin": "^9.2.0",
    "firebase-dynamic-links": "^1.1.0",
    "firebase-functions": "^3.14.1",
    "handlebars": "^4.7.7",
    "lodash.snakecase": "^4.1.1",
    "mkdirp": "^1.0.4",
    "sharp": "^0.28.1",
    "twilio": "^3.63.0",
    "uuid": "^8.3.2"
  },
  "devDependencies": {
    "firebase-functions-test": "^0.2.0"
  },
  "private": true
}
[debug] [2021-06-20T17:39:32.880Z] Building nodejs source
[info] i  functions: ensuring required API cloudfunctions.googleapis.com is enabled... 
[info] i  functions: ensuring required API cloudbuild.googleapis.com is enabled... 
[debug] [2021-06-20T17:39:32.882Z] >>> HTTP REQUEST GET https://serviceusage.googleapis.com/v1/projects/xxx/services/cloudfunctions.googleapis.com  
 
[debug] [2021-06-20T17:39:32.883Z] >>> HTTP REQUEST GET https://serviceusage.googleapis.com/v1/projects/xxx/services/runtimeconfig.googleapis.com  
 
[debug] [2021-06-20T17:39:32.883Z] >>> HTTP REQUEST GET https://serviceusage.googleapis.com/v1/projects/xxx/services/cloudbuild.googleapis.com  
 
[debug] [2021-06-20T17:39:33.895Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:33 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:33.913Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:33 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[info] ✔  functions: required API cloudbuild.googleapis.com is enabled 
[debug] [2021-06-20T17:39:33.935Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:33 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[info] ✔  functions: required API cloudfunctions.googleapis.com is enabled 
[debug] [2021-06-20T17:39:33.937Z] >>> HTTP REQUEST GET https://firebase.googleapis.com/v1beta1/projects/xxx/adminSdkConfig  
 
[debug] [2021-06-20T17:39:34.407Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:34 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:34.409Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs  
 
[debug] [2021-06-20T17:39:34.894Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:34 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:34.896Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/sendgrid/variables  
 
[debug] [2021-06-20T17:39:34.896Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/server/variables  
 
[debug] [2021-06-20T17:39:34.897Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/storage/variables  
 
[debug] [2021-06-20T17:39:34.897Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/twilio/variables  
 
[debug] [2021-06-20T17:39:34.897Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/elasticsearch/variables  
 
[debug] [2021-06-20T17:39:35.150Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.151Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/twilio/variables/accountsid  
 
[debug] [2021-06-20T17:39:35.152Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/twilio/variables/authtoken  
 
[debug] [2021-06-20T17:39:35.156Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.156Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/storage/variables/bucket  
 
[debug] [2021-06-20T17:39:35.222Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.223Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/sendgrid/variables/apikey  
 
[debug] [2021-06-20T17:39:35.261Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.266Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/server/variables/apikey  
 
[debug] [2021-06-20T17:39:35.358Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.359Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/elasticsearch/variables/apikey  
 
[debug] [2021-06-20T17:39:35.359Z] >>> HTTP REQUEST GET https://runtimeconfig.googleapis.com/v1beta1/projects/xxx/configs/elasticsearch/variables/node  
 
[debug] [2021-06-20T17:39:35.370Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.461Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.499Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.562Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.575Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.595Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.657Z] <<< HTTP RESPONSE 200 {"content-type":"application/json; charset=UTF-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","date":"Sun, 20 Jun 2021 17:39:35 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
[debug] [2021-06-20T17:39:35.657Z] Analyzing nodejs backend spec
[debug] [2021-06-20T17:39:35.913Z] TypeError: Cannot read property 'backend' of undefined
    at Object.checkHttpIam (/usr/local/lib/node_modules/firebase-tools/lib/deploy/functions/checkIam.js:31:41)
    at deploy (/usr/local/lib/node_modules/firebase-tools/lib/deploy/functions/deploy.js:36:22)
    at _chain (/usr/local/lib/node_modules/firebase-tools/lib/deploy/index.js:23:40)
    at /usr/local/lib/node_modules/firebase-tools/lib/deploy/index.js:67:16
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
[error] 
[error] Error: An unexpected error has occurred.

@maganap
Copy link
Author

maganap commented Jun 20, 2021

Same error log as in #3512 , same firebase-tools version. Not sure it's related.

@taeold
Copy link
Contributor

taeold commented Jun 22, 2021

Looks like there are 2 issues here:

  1. Deploy fails when there aren't any functions defined in the user's project (Fixed in Fix crash when deploying zero functions. #3520)

  2. process.env is no longer piped when parsing .js files to discover firebase functions
    (Make environment variables on function instances be accessible during trigger parsing #3422).

We expected the process for parsing user code to discover user-defined functions to undergo some changes in the coming months and wanted to start tightening up the contract for what environment variables are available during the parsing process (the exact contract is still being worked out).

Apologies for breaking your setup - I knew the above change may possible to break someone's workflow in theory, but didn't think it will affect anyone in practice. I was wrong.

Do you mind sharing your setup in more detail? I'd love to understand your use case better. In particular, I noticed that you wrote:

To reduce cold start time in functions, I use to work with a structure like this:

How are you using local-only function to reduce cold start time (in prod I assume?).

@taeold taeold added the Needs: Author Feedback Issues awaiting author feedback label Jun 22, 2021
@maganap
Copy link
Author

maganap commented Jun 22, 2021

Apologies for breaking your setup

No prob. It's an undocumented feature so... I knew this day would come 😅

In older/smaller projects we used to work like this:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
      admin.initializeApp();

const lib1 = require('./lib1.js');  // <== related functionality together for various functions
const lib2 = require('./lib2.js'); 
const lib3 = require('./lib3.js');
// ...

const f = functions.region('europe-west1');

exports.onCreateCol1 = f.firestore.document('col1/{id}')
  .onCreate(async (snap, context) => await lib1.onCreateCol1(snap, context));

exports.onWriteCol2 = f.firestore.document('col2/{id}')
  .onWrite(async (change, context) => await lib2.onWriteCol2(change, context));
// ...

Then on larger projects where cold start time was an issue for very specific functions, we followed suggestions from Firebase and Doug and came up with a mix of our own by using K_SERVICE and IS_FIREBASE_CLI. We ended up with something like this:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
      admin.initializeApp();

const f = functions.region('europe-west1');

if ( process.env.IS_FIREBASE_CLI
  || process.env.K_SERVICE == 'onCreateCol1'
) {
  const libCol1 = require('./minimalLibForCol1.js'); // <== as specific as possible, short start time is important

  exports.onCreateCol1 = f.firestore.document('col1/{id}')
    .onCreate(async (snap, context) => await libCol1.onCreateCol1(snap, context));
}

if ( process.env.IS_FIREBASE_CLI
  || process.env.K_SERVICE == 'onWriteCol2'
  || process.env.K_SERVICE == 'onWriteCol3'
  || process.env.K_SERVICE == 'onWriteCol4'
) {
  const otherLib = require('./otherLib.js'); // related stuff together, time is not that important
  
  exports.onWriteCol2 = f.firestore.document('col2/{id}')
    .onWrite(async (change, context) => await otherLib.onWriteCol2(change, context));
  exports.onWriteCol3 = f.firestore.document('col3/{id}')
    .onWrite(async (change, context) => await otherLib.onWriteCol3(change, context));
  exports.onWriteCol4 = f.firestore.document('col4/{id}')
    .onWrite(async (change, context) => await otherLib.onWriteCol4(change, context));
}

// if ( process.env.IS_FIREBASE_CLI ...

So:

  • By using K_SERVICE the running instance will only load what it needs to serve that specific function (once deployed, I mean). To achieve this, I had to add IS_FIREBASE_CLI to allow firebase-tools to find the function while being deployed.
  • Using IS_FIREBASE_CLI also allowed firebase-tools to find the function while running the functions:shell, so I could easily test locally while developing. Useful specially with https.onCall() functions. To test the Firestore triggers I work with the emulators.

The use case comes about the need to auto-scale resources very easily without us having to work with GKE or similar.

We've grown much with a particular project, and during the daytime there's traffic enough to always have some instances running for a particular function. But there's always that user that uploads 20 files at a time and those 20 triggers seem to create many new instances, leading to cold start time for many of the 20 triggers. The user ended up with list of mixed-up "Ready" and "Processing..." items for several seconds.

So... either we migrated everything to GKE, or tried to fix it on firebase functions. We went for the latter.

We also had to "cheat" by triggering the empty function (run but without actually processing anything, ends up quickly) when we could anticipate the user would need it. That allowed us to create some instances in advance to avoid cold start time most of the times.

For other functions (mostly https.onCall()) where we have very low traffic (so there may be no instance ready to wake up for that specific function), but we do need a very quick reply, we ended up installing our own server on a small VM.

Hope it helps.

@google-oss-bot google-oss-bot added Needs: Attention and removed Needs: Author Feedback Issues awaiting author feedback labels Jun 22, 2021
@taeold
Copy link
Contributor

taeold commented Jun 23, 2021

@maganap Thanks for sharing your setup in great detail. I learned a lot.

As for immediate workaround to your problem, my suggestion is to replace the condition process.env.IS_FIREBASE_CLI with !process.env.K_SERVICE. In GCF runtime, process.env.K_SERVICE will always be set whereas it won't be in local environments.

Give it a try and let us know how it goes (and if it's good enough, feel free to close the issue).

Tangent - the way you dealt with your cold start issue is pretty fantastic. One more lead you might want to follow https://issuetracker.google.com/issues/158014637. It's a very long thread, but the summary of it is that gRPC client used in the Firestore SDK (which is in turn used in the Admin SDK) may be a big contributor to the cold-start time. I know some Googlers are working to improve the situation - though you might want to keep it under your radar.

@samtstern
Copy link
Contributor

@taeold actually K_SERVICE is defined within the Functions emulator:

process.env.K_SERVICE = service;

@maganap I use the same workflow as you, where I have some callable functions I only want to test in the shell / emulator. For that I use the FUNCTIONS_EMULATOR environment variable:

process.env.FUNCTIONS_EMULATOR = "true";

However maybe the same changes that broke IS_FIREBASE_CLI broke that as well? To be honest I am not sure, I have not updated my personal projects to the latest version yet.

@taeold
Copy link
Contributor

taeold commented Jun 23, 2021

@samtstern Thanks for chiming in. I didn't realize K_SERVICE is available in the Functions Emulator (and hence also in functions:shell command).

To be precise, there are 2 different environments I have in mind:

  1. Trigger Parsing - environment we used to load user code in order to parse function triggers.
  2. Function Runtime - environment at which user functions are executed.

This issue is about removal of IS_FIREBASE_CLI environment variable in (1); #3422 intentionally stopped including parent process's environment variables when forking a process that's responsible for parsing function triggers.

To further complicate the picture, we have to distinguish trigger parsing that's done during firebase deploy vs during firebase emulators:start (or firebase functions:shell) - they don't seem to share much code unfortunately, and right now environment variables available during deploy isn't the same as the environment variable available during function emulation. It's confusing, but okay... except that I totally discounted it when I started to tighten things down.

I'm now coming to realize all these complications, and in hindsight #3422 feels a bit immature. I will try to come up with a more mature proposal w.r.t. "during trigger parsing, these environment variables are available to you. Similarly function runtime, these environment variables are available to you..." similar in style of what Cloud Functions documentation discusses in https://cloud.google.com/functions/docs/env-var#using_build_environment_variables. We should apply whatever we agree upon to both function deploy process and emulator setup process.

taeold referenced this issue Jun 29, 2021
… trigger parsing (#3422)

We move the logic for setting up environment variables for function instances from post-trigger parsing to pre-trigger parsing. This makes environment variables accessible during trigger parsing, e.g.

```
const myFunc = functions.runWith({minInstances: process.env.TIER == "PROD" ? 20 : 0})...
```

(Of course, there isn't a way for users to specify environment variables for now. That may change soon.)

This change simply refactors existing logic - nothing new is introduced and function deploys should work exactly as it is done today.

*Warning* There is technically a breaking change where we no longer pass caller's `process.env` when forking a process to parse function triggers in user's source code. We think this is removing what was previously an undefined behavior but recognize we may be breaking someone's workflow. We will soon propose a support for setting project-level environment variables more easily.
@maganap
Copy link
Author

maganap commented Jul 14, 2021

To be able to move on from @9.12, as ugly as it looks, I ended up with something like this to minimize cold start time... at least until I find a more elegant solution.

Note:

  • process.env.NODE_ENV = 'production' once deployed, but it's not defined while running locally and while deploying.
  • process.env.K_SERVICE does exist locally, but only while executing, not while parsing.

This covers the cases:

  • Local parsing (shell init and function deployment): process.env.NODE_ENV !== 'production'
  • Local exec (shell): process.env.K_SERVICE == fn
  • Deployed parsing and exec: process.env.K_SERVICE == fn
const functions = require('firebase-functions');

const reqPath = `./fn/${process.env.K_SERVICE}`;
const isFn = fn => process.env.K_SERVICE == fn || process.env.NODE_ENV !== 'production';

exports.test2 = isFn('test2') && functions.firestore.document('col2/{id}')
  .onWrite( async (change, context) => await require(reqPath)(change, context) );

exports.test3 = isFn('test3') && functions.firestore.document('col3/{id}')
  .onWrite( async (change, context) => await require(reqPath)(change, context) );

With single function files like this:

- index.js
- fn/
  - test2.js
  - test3.js

And test2.js being:

async function test2(change, context) {
  // ...
}
module.exports = test2;

The rest of functions that don't require a short cold start time are just grouped in common files for simplicity.

@samtstern
Copy link
Contributor

@maganap thanks for explaining! Your cold start trick looks a lot like the one that this library uses:
https://github.com/gramstr/better-firebase-functions

@maganap
Copy link
Author

maganap commented Jul 14, 2021

@samtstern Nice reference! I'll take a deeper look at it. Thanks!

@VictorLNoCapInc
Copy link

VictorLNoCapInc commented Jun 16, 2022

Hey, bump for the access of process.env during Trigger Parsing (environment we used to load user code in order to parse function triggers).
It messes up a lot of architectures actually.

For instance, when you have a staging env and you want to listen for changes on different buckets in staging env...

@taeold
Copy link
Contributor

taeold commented Jun 30, 2022

This FR seems related to firebase/firebase-functions#1084.

tl;dr - process.env will probably never be available during trigger discovery. Params will.

@taeold taeold closed this as completed Jun 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants