Open fedenusy opened 1 year ago
Hey @fedenusy.
It looks like there is an issue with using secret parameters with the emulator.
I'd recommend making the following change to unblock your setup:
import * as functions from "firebase-functions";
import { defineSecret } from "firebase-functions/params";
--defineSecret('SUPA_SECRET')
export const helloWorld = functions.runWith({ secrets: ['SUPA_SECRET'] }).https.onRequest((request, response) => {
functions.logger.info("Hello logs!", {structuredData: true});
response.send("Hello from Firebase!");
});
In the meantime, I'm going to update the title of this bug to be more generic.
Hey @taeold just to be sure I understood the issue correctly and no longer have to search for errors in my configuration:
As far as I understand it's currently not possible to run the functions emulator on an unauthenticated machine (like CI) when using defineSecret(...)
anywhere in the function's code even if the secrets used in these defineSecret(...)
calls are provided via a .secret.local
file, right?
I ran into the same issue. In-case you want to keep a syntax similar to defineSecret()
, you can do something like this:
const slackWebhookUrl = { name: 'SLACK_WEBHOOK_URL', value: () => process.env.SLACK_WEBHOOK_URL as string }
export const handler = region('europe-west1')
.runWith({ secrets: [slackWebhookUrl].map((secret) => secret.name) })
.https.onCall(async (name, context) => {
// …
await sendSlackMessage(slackWebhookUrl.value(), { /* … */ })
})
Works as expected in emulator with demo-
-project.
I also can't use .secret.local. Using version 12.2.1.
I have tried a lot of different ways to setup that file, (as a dotenv, json, and more), but always get a validation error when emulator are trying to read it.
functions: Failed to read local secrets file /Users/XXXXXXX/functions/.secret.local: Validation failed
Example of file:
secret_pass=1112222 secret_pass_2=344566
How should I can use that file? Now I have workarrounds, but really I need to know how to configure this file.
Same issue here... What is the status? How can we use secrets if we cannot test them? Please solve this ASAP...
@zariweyo, have you tried writing the secret name in uppercase? Like SECRET_PASS=1112222? It worked for me.
Oh my god, it works!!!!. Thanks @coehne
This is happening with v2 functions and the emulator with tools version 12.6.0.
import { https } from 'firebase-functions/v2'
import { defineSecret } from 'firebase-functions/params'
const secretItem = defineSecret('SECRET_ITEM')
https.onCall({secrets: [secretItem]}, () => 200);
Is the advice to not define the secret when emulating? Is there an easy way to branch that logic? It doesn't seem like it...
Ran into the same problem after migrating my code to functions v2 with defineSecret
. The solution is to have a variable defined in .env
too.
// functions/webhook.js
const { onRequest } = require('firebase-functions/v2/https')
const { defineSecret } = require('firebase-functions/params')
const stripeKey = defineSecret('STRIPE_KEY')
exports.webhook = onRequest(
{ region: EUROPE, secrets: [stripeKey] },
async (req, res) => {
res.json({ received: true })
}
)
# functions/.env
# empty value just to have this variable present for the emulator
STRIPE_KEY=
# functions/.secret.local
# actual secret value
STRIPE_KEY=sk_test_***
After that Emulator won't throw that configuration error and run the function as expected
Waiting on this. Now bypassed the block with adding variables to .env
I also just ran into this, but happy to hear that .env works at least! Personally, I use .env.local
and that works too.
If someone is using firebase-functions-test
to test functions and encounters this, the issue is actually that firebase-functions-test
does not use the emulator.
Instead, there is an open issue https://github.com/firebase/firebase-functions-test/issues/196 for firebase-functions-test
to support something like the old mockConfig()
for the new parametrized configuration.
There is also a workaround described in this issue https://github.com/firebase/firebase-functions-test/issues/196#issuecomment-1900541854 until firebase-functions-test
natively supports picking up the .env config.
Just a note that although it is a working workaround to populate .env, which then gets picked up by resolveParams in params.ts, this is potentially dangerous as it risks commands like firebase deploy also using .env.
The proper fix is for .secret.local to be used and for the emulator to pick this up to ignore the secrets manager check. This is being discussed at #7401
For reference, the code that is handling for this issue is as follows (with added comments from me)
/**
* A param defined by the SDK may resolve to:
* - a reference to a secret in Cloud Secret Manager, which we validate the existence of and prompt for if missing
* - a literal value of the same type already defined in one of the .env files with key == param name
* - the value returned by interactively prompting the user
* - it is an error to have params that need to be prompted if the CLI is running in non-interactive mode
* - the default value of the prompt comes from the SDK via param.default, which may be a literal value or a CEL expression
* - if the default CEL expression is not resolvable--it depends on a param whose value is not yet known--we throw an error
* - yes, this means that the same set of params may or may not throw depending on the order the SDK provides them to us in
* - after prompting, the resolved value of the param is written to the most specific .env file available
*/
export async function resolveParams(
params: Param[],
firebaseConfig: FirebaseConfig,
userEnvs: Record<string, ParamValue>,
nonInteractive?: boolean,
): Promise<Record<string, ParamValue>> {
console.info("resolveParams");
const paramValues: Record<string, ParamValue> = populateDefaultParams(firebaseConfig);
// TODO(vsfan@): should we ever reject param values from .env files based on the appearance of the string?
// Use of .env is dangerous. See https://github.com/firebase/firebase-tools/issues/5520#issuecomment-2208329205 (@glorat)
// Better to check for .secret.local (@glorat)
const [resolved, outstanding] = partition(params, (param) => {
return {}.hasOwnProperty.call(userEnvs, param.name);
});
for (const param of resolved) {
paramValues[param.name] = userEnvs[param.name];
}
// TODO: should exclude secrets found in .secret.local too
console.info('handling secrets');
const [needSecret, needPrompt] = partition(outstanding, (param) => param.type === "secret");
for (const param of needSecret) {
// this line requires both internet connection and secrets manager credentials to operate and will fail otherwise - @glorat
await handleSecret(param as SecretParam, firebaseConfig.projectId);
}
...
[REQUIRED] Environment info
firebase-tools: 11.23.1
Platform: ubuntu
[REQUIRED] Test case
helloWorld
function and make itrunWith
any secretfunctions/.secret.local
containing a value for the secret--project
withdemo-
prefixhelloWorld
function from getting loaded.[REQUIRED] Steps to reproduce
mkdir test && cd test
firebase init
> functions > any existing project > typescriptfunctions/src/index.ts
to what's shown below.functions/.secret.local
as shown below.cd functions && npm run build && firebase emulators:start --only functions --project demo-project-id
functions/src/index.ts
:functions/.secret.local
:NB: I also tried placing
.secret.local
at the repo root, and got the same result.[REQUIRED] Expected behavior
Emulators should pick up
.secret.local
.[REQUIRED] Actual behavior