firebase-tools 10.5.0 → 10.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/command.js +4 -4
- package/lib/commands/deploy.js +1 -1
- package/lib/commands/emulators-start.js +7 -2
- package/lib/commands/ext-configure.js +15 -5
- package/lib/commands/ext-export.js +6 -5
- package/lib/commands/ext-install.js +28 -44
- package/lib/commands/ext-update.js +9 -1
- package/lib/commands/functions-delete.js +7 -3
- package/lib/commands/functions-secrets-destroy.js +23 -3
- package/lib/commands/functions-secrets-prune.js +15 -12
- package/lib/commands/functions-secrets-set.js +51 -4
- package/lib/commands/hosting-channel-deploy.js +2 -2
- package/lib/deploy/database/deploy.js +4 -0
- package/lib/deploy/database/index.js +1 -0
- package/lib/deploy/extensions/deploy.js +4 -4
- package/lib/deploy/extensions/deploymentSummary.js +8 -5
- package/lib/deploy/extensions/planner.js +36 -9
- package/lib/deploy/extensions/prepare.js +1 -1
- package/lib/deploy/extensions/secrets.js +2 -2
- package/lib/deploy/extensions/tasks.js +60 -21
- package/lib/deploy/functions/backend.js +37 -6
- package/lib/deploy/functions/build.js +162 -0
- package/lib/deploy/functions/checkIam.js +10 -6
- package/lib/deploy/functions/deploy.js +49 -28
- package/lib/deploy/functions/ensure.js +4 -4
- package/lib/deploy/functions/functionsDeployHelper.js +99 -24
- package/lib/deploy/functions/prepare.js +130 -62
- package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
- package/lib/deploy/functions/pricing.js +6 -3
- package/lib/deploy/functions/prompts.js +1 -7
- package/lib/deploy/functions/release/fabricator.js +70 -28
- package/lib/deploy/functions/release/index.js +41 -6
- package/lib/deploy/functions/release/planner.js +19 -12
- package/lib/deploy/functions/release/reporter.js +14 -11
- package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +61 -13
- package/lib/deploy/functions/runtimes/node/index.js +1 -1
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +29 -24
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/services/auth.js +95 -0
- package/lib/deploy/functions/services/index.js +41 -21
- package/lib/deploy/functions/services/storage.js +1 -6
- package/lib/deploy/functions/validate.js +32 -6
- package/lib/deploy/hosting/args.js +2 -0
- package/lib/deploy/hosting/convertConfig.js +39 -8
- package/lib/deploy/hosting/deploy.js +3 -3
- package/lib/deploy/hosting/prepare.js +2 -2
- package/lib/deploy/hosting/release.js +6 -2
- package/lib/deploy/index.js +82 -93
- package/lib/deploy/remoteconfig/deploy.js +4 -0
- package/lib/deploy/remoteconfig/index.js +3 -1
- package/lib/emulator/auth/operations.js +5 -0
- package/lib/emulator/auth/utils.js +3 -25
- package/lib/emulator/controller.js +17 -14
- package/lib/emulator/downloadableEmulators.js +39 -23
- package/lib/emulator/extensions/postinstall.js +41 -0
- package/lib/emulator/extensions/validation.js +2 -2
- package/lib/emulator/extensionsEmulator.js +85 -21
- package/lib/emulator/functionsEmulator.js +88 -10
- package/lib/emulator/functionsEmulatorShared.js +37 -21
- package/lib/emulator/functionsEmulatorShell.js +2 -3
- package/lib/emulator/pubsubEmulator.js +13 -9
- package/lib/emulator/registry.js +34 -12
- package/lib/emulator/storage/apis/firebase.js +13 -8
- package/lib/emulator/storage/apis/gcloud.js +15 -9
- package/lib/emulator/storage/files.js +14 -3
- package/lib/emulator/storage/index.js +9 -1
- package/lib/emulator/storage/metadata.js +18 -8
- package/lib/emulator/storage/rules/manager.js +7 -17
- package/lib/emulator/storage/server.js +38 -12
- package/lib/ensureApiEnabled.js +8 -4
- package/lib/extensions/askUserForParam.js +14 -11
- package/lib/extensions/changelog.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +9 -10
- package/lib/extensions/emulator/specHelper.js +7 -1
- package/lib/extensions/emulator/triggerHelper.js +11 -14
- package/lib/extensions/extensionsApi.js +2 -1
- package/lib/extensions/extensionsHelper.js +30 -24
- package/lib/extensions/manifest.js +28 -8
- package/lib/extensions/paramHelper.js +19 -13
- package/lib/extensions/provisioningHelper.js +2 -2
- package/lib/extensions/warnings.js +3 -3
- package/lib/functions/env.js +10 -2
- package/lib/functions/events/index.js +7 -0
- package/lib/functions/events/v1.js +6 -0
- package/lib/functions/projectConfig.js +32 -6
- package/lib/functions/runtimeConfigExport.js +10 -6
- package/lib/functions/secrets.js +99 -6
- package/lib/functionsShellCommandAction.js +1 -1
- package/lib/gcp/cloudfunctions.js +44 -18
- package/lib/gcp/cloudfunctionsv2.js +48 -25
- package/lib/gcp/cloudtasks.js +5 -3
- package/lib/gcp/identityPlatform.js +44 -0
- package/lib/gcp/secretManager.js +2 -2
- package/lib/metaprogramming.js +2 -0
- package/lib/previews.js +1 -1
- package/lib/serve/functions.js +16 -19
- package/lib/serve/hosting.js +25 -12
- package/lib/serve/index.js +6 -0
- package/lib/track.js +15 -21
- package/lib/utils.js +30 -1
- package/npm-shrinkwrap.json +44 -2
- package/package.json +4 -1
- package/schema/firebase-config.json +6 -0
|
@@ -21,6 +21,8 @@ const reporter = require("./reporter");
|
|
|
21
21
|
const run = require("../../../gcp/run");
|
|
22
22
|
const scheduler = require("../../../gcp/cloudscheduler");
|
|
23
23
|
const utils = require("../../../utils");
|
|
24
|
+
const services = require("../services");
|
|
25
|
+
const v1_1 = require("../../../functions/events/v1");
|
|
24
26
|
const gcfV1PollerOptions = {
|
|
25
27
|
apiOrigin: api_1.functionsOrigin,
|
|
26
28
|
apiVersion: gcf.API_VERSION,
|
|
@@ -35,14 +37,14 @@ const gcfV2PollerOptions = {
|
|
|
35
37
|
};
|
|
36
38
|
const DEFAULT_GCFV2_CONCURRENCY = 80;
|
|
37
39
|
const rethrowAs = (endpoint, op) => (err) => {
|
|
40
|
+
logger_1.logger.error(err.message);
|
|
38
41
|
throw new reporter.DeploymentError(endpoint, op, err);
|
|
39
42
|
};
|
|
40
43
|
class Fabricator {
|
|
41
44
|
constructor(args) {
|
|
42
45
|
this.executor = args.executor;
|
|
43
46
|
this.functionExecutor = args.functionExecutor;
|
|
44
|
-
this.
|
|
45
|
-
this.storage = args.storage;
|
|
47
|
+
this.sources = args.sources;
|
|
46
48
|
this.appEngineLocation = args.appEngineLocation;
|
|
47
49
|
}
|
|
48
50
|
async applyPlan(plan) {
|
|
@@ -124,9 +126,7 @@ class Fabricator {
|
|
|
124
126
|
await this.setTrigger(endpoint);
|
|
125
127
|
}
|
|
126
128
|
async updateEndpoint(update, scraper) {
|
|
127
|
-
|
|
128
|
-
update.endpoint.labels = Object.assign(Object.assign({}, update.endpoint.labels), deploymentTool.labels());
|
|
129
|
-
}
|
|
129
|
+
update.endpoint.labels = Object.assign(Object.assign({}, update.endpoint.labels), deploymentTool.labels());
|
|
130
130
|
if (update.deleteAndRecreate) {
|
|
131
131
|
await this.deleteEndpoint(update.deleteAndRecreate);
|
|
132
132
|
await this.createEndpoint(update.endpoint, scraper);
|
|
@@ -153,12 +153,13 @@ class Fabricator {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
async createV1Function(endpoint, scraper) {
|
|
156
|
-
var _a;
|
|
157
|
-
|
|
156
|
+
var _a, _b;
|
|
157
|
+
const sourceUrl = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.sourceUrl;
|
|
158
|
+
if (!sourceUrl) {
|
|
158
159
|
logger_1.logger.debug("Precondition failed. Cannot create a GCF function without sourceUrl");
|
|
159
160
|
throw new Error("Precondition failed");
|
|
160
161
|
}
|
|
161
|
-
const apiFunction = gcf.functionFromEndpoint(endpoint,
|
|
162
|
+
const apiFunction = gcf.functionFromEndpoint(endpoint, sourceUrl);
|
|
162
163
|
if (apiFunction.httpsTrigger) {
|
|
163
164
|
apiFunction.httpsTrigger.securityLevel = "SECURE_ALWAYS";
|
|
164
165
|
}
|
|
@@ -166,10 +167,10 @@ class Fabricator {
|
|
|
166
167
|
const resultFunction = await this.functionExecutor
|
|
167
168
|
.run(async () => {
|
|
168
169
|
const op = await gcf.createFunction(apiFunction);
|
|
169
|
-
return poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `create-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
|
|
170
|
+
return poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `create-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
|
|
170
171
|
})
|
|
171
172
|
.catch(rethrowAs(endpoint, "create"));
|
|
172
|
-
endpoint.uri = (
|
|
173
|
+
endpoint.uri = (_b = resultFunction === null || resultFunction === void 0 ? void 0 : resultFunction.httpsTrigger) === null || _b === void 0 ? void 0 : _b.url;
|
|
173
174
|
if (backend.isHttpsTriggered(endpoint)) {
|
|
174
175
|
const invoker = endpoint.httpsTrigger.invoker || ["public"];
|
|
175
176
|
if (!invoker.includes("private")) {
|
|
@@ -197,15 +198,24 @@ class Fabricator {
|
|
|
197
198
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
198
199
|
}
|
|
199
200
|
}
|
|
201
|
+
else if (backend.isBlockingTriggered(endpoint) &&
|
|
202
|
+
v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
|
|
203
|
+
await this.executor
|
|
204
|
+
.run(async () => {
|
|
205
|
+
await gcf.setInvokerCreate(endpoint.project, backend.functionName(endpoint), ["public"]);
|
|
206
|
+
})
|
|
207
|
+
.catch(rethrowAs(endpoint, "set invoker"));
|
|
208
|
+
}
|
|
200
209
|
}
|
|
201
210
|
async createV2Function(endpoint) {
|
|
202
|
-
var _a;
|
|
203
|
-
|
|
211
|
+
var _a, _b;
|
|
212
|
+
const storage = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
|
|
213
|
+
if (!storage) {
|
|
204
214
|
logger_1.logger.debug("Precondition failed. Cannot create a GCFv2 function without storage");
|
|
205
215
|
throw new Error("Precondition failed");
|
|
206
216
|
}
|
|
207
|
-
const apiFunction = gcfV2.functionFromEndpoint(endpoint,
|
|
208
|
-
const topic = (
|
|
217
|
+
const apiFunction = gcfV2.functionFromEndpoint(endpoint, storage);
|
|
218
|
+
const topic = (_b = apiFunction.eventTrigger) === null || _b === void 0 ? void 0 : _b.pubsubTopic;
|
|
209
219
|
if (topic) {
|
|
210
220
|
await this.executor
|
|
211
221
|
.run(async () => {
|
|
@@ -226,7 +236,7 @@ class Fabricator {
|
|
|
226
236
|
const resultFunction = await this.functionExecutor
|
|
227
237
|
.run(async () => {
|
|
228
238
|
const op = await gcfV2.createFunction(apiFunction);
|
|
229
|
-
return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `create-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
|
|
239
|
+
return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `create-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
|
|
230
240
|
})
|
|
231
241
|
.catch(rethrowAs(endpoint, "create"));
|
|
232
242
|
endpoint.uri = resultFunction.serviceConfig.uri;
|
|
@@ -254,26 +264,33 @@ class Fabricator {
|
|
|
254
264
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
255
265
|
}
|
|
256
266
|
}
|
|
267
|
+
else if (backend.isBlockingTriggered(endpoint) &&
|
|
268
|
+
v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
|
|
269
|
+
await this.executor
|
|
270
|
+
.run(() => run.setInvokerCreate(endpoint.project, serviceName, ["public"]))
|
|
271
|
+
.catch(rethrowAs(endpoint, "set invoker"));
|
|
272
|
+
}
|
|
257
273
|
const mem = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
|
|
258
274
|
if (mem >= backend.MIN_MEMORY_FOR_CONCURRENCY && endpoint.concurrency !== 1) {
|
|
259
275
|
await this.setConcurrency(endpoint, serviceName, endpoint.concurrency || DEFAULT_GCFV2_CONCURRENCY);
|
|
260
276
|
}
|
|
261
277
|
}
|
|
262
278
|
async updateV1Function(endpoint, scraper) {
|
|
263
|
-
var _a;
|
|
264
|
-
|
|
279
|
+
var _a, _b;
|
|
280
|
+
const sourceUrl = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.sourceUrl;
|
|
281
|
+
if (!sourceUrl) {
|
|
265
282
|
logger_1.logger.debug("Precondition failed. Cannot update a GCF function without sourceUrl");
|
|
266
283
|
throw new Error("Precondition failed");
|
|
267
284
|
}
|
|
268
|
-
const apiFunction = gcf.functionFromEndpoint(endpoint,
|
|
285
|
+
const apiFunction = gcf.functionFromEndpoint(endpoint, sourceUrl);
|
|
269
286
|
apiFunction.sourceToken = await scraper.tokenPromise();
|
|
270
287
|
const resultFunction = await this.functionExecutor
|
|
271
288
|
.run(async () => {
|
|
272
289
|
const op = await gcf.updateFunction(apiFunction);
|
|
273
|
-
return await poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `update-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
|
|
290
|
+
return await poller.pollOperation(Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `update-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name, onPoll: scraper.poller }));
|
|
274
291
|
})
|
|
275
292
|
.catch(rethrowAs(endpoint, "update"));
|
|
276
|
-
endpoint.uri = (
|
|
293
|
+
endpoint.uri = (_b = resultFunction === null || resultFunction === void 0 ? void 0 : resultFunction.httpsTrigger) === null || _b === void 0 ? void 0 : _b.url;
|
|
277
294
|
let invoker;
|
|
278
295
|
if (backend.isHttpsTriggered(endpoint)) {
|
|
279
296
|
invoker = endpoint.httpsTrigger.invoker;
|
|
@@ -281,6 +298,10 @@ class Fabricator {
|
|
|
281
298
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
282
299
|
invoker = endpoint.taskQueueTrigger.invoker;
|
|
283
300
|
}
|
|
301
|
+
else if (backend.isBlockingTriggered(endpoint) &&
|
|
302
|
+
v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
|
|
303
|
+
invoker = ["public"];
|
|
304
|
+
}
|
|
284
305
|
if (invoker) {
|
|
285
306
|
await this.executor
|
|
286
307
|
.run(() => gcf.setInvokerUpdate(endpoint.project, backend.functionName(endpoint), invoker))
|
|
@@ -288,19 +309,20 @@ class Fabricator {
|
|
|
288
309
|
}
|
|
289
310
|
}
|
|
290
311
|
async updateV2Function(endpoint) {
|
|
291
|
-
var _a;
|
|
292
|
-
|
|
312
|
+
var _a, _b;
|
|
313
|
+
const storage = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
|
|
314
|
+
if (!storage) {
|
|
293
315
|
logger_1.logger.debug("Precondition failed. Cannot update a GCFv2 function without storage");
|
|
294
316
|
throw new Error("Precondition failed");
|
|
295
317
|
}
|
|
296
|
-
const apiFunction = gcfV2.functionFromEndpoint(endpoint,
|
|
297
|
-
if ((
|
|
318
|
+
const apiFunction = gcfV2.functionFromEndpoint(endpoint, storage);
|
|
319
|
+
if ((_b = apiFunction.eventTrigger) === null || _b === void 0 ? void 0 : _b.pubsubTopic) {
|
|
298
320
|
delete apiFunction.eventTrigger.pubsubTopic;
|
|
299
321
|
}
|
|
300
322
|
const resultFunction = await this.functionExecutor
|
|
301
323
|
.run(async () => {
|
|
302
324
|
const op = await gcfV2.updateFunction(apiFunction);
|
|
303
|
-
return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `update-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
|
|
325
|
+
return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `update-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
|
|
304
326
|
})
|
|
305
327
|
.catch(rethrowAs(endpoint, "update"));
|
|
306
328
|
endpoint.uri = resultFunction.serviceConfig.uri;
|
|
@@ -312,6 +334,10 @@ class Fabricator {
|
|
|
312
334
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
313
335
|
invoker = endpoint.taskQueueTrigger.invoker;
|
|
314
336
|
}
|
|
337
|
+
else if (backend.isBlockingTriggered(endpoint) &&
|
|
338
|
+
v1_1.AUTH_BLOCKING_EVENTS.includes(endpoint.blockingTrigger.eventType)) {
|
|
339
|
+
invoker = ["public"];
|
|
340
|
+
}
|
|
315
341
|
if (invoker) {
|
|
316
342
|
await this.executor
|
|
317
343
|
.run(() => run.setInvokerUpdate(endpoint.project, serviceName, invoker))
|
|
@@ -326,7 +352,7 @@ class Fabricator {
|
|
|
326
352
|
await this.functionExecutor
|
|
327
353
|
.run(async () => {
|
|
328
354
|
const op = await gcf.deleteFunction(fnName);
|
|
329
|
-
const pollerOptions = Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `delete-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name });
|
|
355
|
+
const pollerOptions = Object.assign(Object.assign({}, gcfV1PollerOptions), { pollerName: `delete-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name });
|
|
330
356
|
await poller.pollOperation(pollerOptions);
|
|
331
357
|
})
|
|
332
358
|
.catch(rethrowAs(endpoint, "delete"));
|
|
@@ -336,7 +362,7 @@ class Fabricator {
|
|
|
336
362
|
await this.functionExecutor
|
|
337
363
|
.run(async () => {
|
|
338
364
|
const op = await gcfV2.deleteFunction(fnName);
|
|
339
|
-
const pollerOptions = Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `delete-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name });
|
|
365
|
+
const pollerOptions = Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `delete-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name });
|
|
340
366
|
await poller.pollOperation(pollerOptions);
|
|
341
367
|
})
|
|
342
368
|
.catch(rethrowAs(endpoint, "delete"));
|
|
@@ -371,6 +397,9 @@ class Fabricator {
|
|
|
371
397
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
372
398
|
await this.upsertTaskQueue(endpoint);
|
|
373
399
|
}
|
|
400
|
+
else if (backend.isBlockingTriggered(endpoint)) {
|
|
401
|
+
await this.registerBlockingTrigger(endpoint);
|
|
402
|
+
}
|
|
374
403
|
}
|
|
375
404
|
async deleteTrigger(endpoint) {
|
|
376
405
|
if (backend.isScheduleTriggered(endpoint)) {
|
|
@@ -387,6 +416,9 @@ class Fabricator {
|
|
|
387
416
|
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
388
417
|
await this.disableTaskQueue(endpoint);
|
|
389
418
|
}
|
|
419
|
+
else if (backend.isBlockingTriggered(endpoint)) {
|
|
420
|
+
await this.unregisterBlockingTrigger(endpoint);
|
|
421
|
+
}
|
|
390
422
|
}
|
|
391
423
|
async upsertScheduleV1(endpoint) {
|
|
392
424
|
const job = scheduler.jobFromEndpoint(endpoint, this.appEngineLocation);
|
|
@@ -408,6 +440,11 @@ class Fabricator {
|
|
|
408
440
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
409
441
|
}
|
|
410
442
|
}
|
|
443
|
+
async registerBlockingTrigger(endpoint) {
|
|
444
|
+
await this.executor
|
|
445
|
+
.run(() => services.serviceForEndpoint(endpoint).registerTrigger(endpoint))
|
|
446
|
+
.catch(rethrowAs(endpoint, "register blocking trigger"));
|
|
447
|
+
}
|
|
411
448
|
async deleteScheduleV1(endpoint) {
|
|
412
449
|
const job = scheduler.jobFromEndpoint(endpoint, this.appEngineLocation);
|
|
413
450
|
await this.executor
|
|
@@ -429,10 +466,15 @@ class Fabricator {
|
|
|
429
466
|
.run(() => cloudtasks.updateQueue(update))
|
|
430
467
|
.catch(rethrowAs(endpoint, "disable task queue"));
|
|
431
468
|
}
|
|
469
|
+
async unregisterBlockingTrigger(endpoint) {
|
|
470
|
+
await this.executor
|
|
471
|
+
.run(() => services.serviceForEndpoint(endpoint).unregisterTrigger(endpoint))
|
|
472
|
+
.catch(rethrowAs(endpoint, "unregister blocking trigger"));
|
|
473
|
+
}
|
|
432
474
|
logOpStart(op, endpoint) {
|
|
433
475
|
const runtime = (0, runtimes_1.getHumanFriendlyRuntimeName)(endpoint.runtime);
|
|
434
476
|
const label = helper.getFunctionLabel(endpoint);
|
|
435
|
-
utils.
|
|
477
|
+
utils.logLabeledBullet("functions", `${op} ${runtime} function ${clc.bold(label)}...`);
|
|
436
478
|
}
|
|
437
479
|
logOpSuccess(op, endpoint) {
|
|
438
480
|
const label = helper.getFunctionLabel(endpoint);
|
|
@@ -11,14 +11,31 @@ const fabricator = require("./fabricator");
|
|
|
11
11
|
const reporter = require("./reporter");
|
|
12
12
|
const executor = require("./executor");
|
|
13
13
|
const prompts = require("../prompts");
|
|
14
|
+
const secrets = require("../../../functions/secrets");
|
|
14
15
|
const functionsConfig_1 = require("../../../functionsConfig");
|
|
15
16
|
const functionsDeployHelper_1 = require("../functionsDeployHelper");
|
|
16
17
|
const error_1 = require("../../../error");
|
|
18
|
+
const projectUtils_1 = require("../../../projectUtils");
|
|
19
|
+
const utils_1 = require("../../../utils");
|
|
17
20
|
async function release(context, options, payload) {
|
|
18
21
|
if (!context.config) {
|
|
19
22
|
return;
|
|
20
23
|
}
|
|
21
|
-
|
|
24
|
+
if (!payload.functions) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!context.sources) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
let plan = {};
|
|
31
|
+
for (const [codebase, { wantBackend, haveBackend }] of Object.entries(payload.functions)) {
|
|
32
|
+
plan = Object.assign(Object.assign({}, plan), planner.createDeploymentPlan({
|
|
33
|
+
codebase,
|
|
34
|
+
wantBackend,
|
|
35
|
+
haveBackend,
|
|
36
|
+
filters: context.filters,
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
22
39
|
const fnsToDelete = Object.values(plan)
|
|
23
40
|
.map((regionalChanges) => regionalChanges.endpointsToDelete)
|
|
24
41
|
.reduce(functional_1.reduceFlat, []);
|
|
@@ -37,15 +54,15 @@ async function release(context, options, payload) {
|
|
|
37
54
|
const fab = new fabricator.Fabricator({
|
|
38
55
|
functionExecutor,
|
|
39
56
|
executor: new executor.QueueExecutor({}),
|
|
40
|
-
|
|
41
|
-
storage: context.storage,
|
|
57
|
+
sources: context.sources,
|
|
42
58
|
appEngineLocation: (0, functionsConfig_1.getAppEngineLocation)(context.firebaseConfig),
|
|
43
59
|
});
|
|
44
60
|
const summary = await fab.applyPlan(plan);
|
|
45
61
|
await reporter.logAndTrackDeployStats(summary);
|
|
46
62
|
reporter.printErrors(summary);
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
const wantBackend = backend.merge(...Object.values(payload.functions).map((p) => p.wantBackend));
|
|
64
|
+
printTriggerUrls(wantBackend);
|
|
65
|
+
const haveEndpoints = backend.allEndpoints(wantBackend);
|
|
49
66
|
const deletedEndpoints = Object.values(plan)
|
|
50
67
|
.map((r) => r.endpointsToDelete)
|
|
51
68
|
.reduce(functional_1.reduceFlat, []);
|
|
@@ -59,6 +76,24 @@ async function release(context, options, payload) {
|
|
|
59
76
|
const opts = allErrors.length === 1 ? { original: allErrors[0] } : { children: allErrors };
|
|
60
77
|
throw new error_1.FirebaseError("There was an error deploying functions", Object.assign(Object.assign({}, opts), { exit: 2 }));
|
|
61
78
|
}
|
|
79
|
+
else {
|
|
80
|
+
if (secrets.of(haveEndpoints).length > 0) {
|
|
81
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
82
|
+
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
83
|
+
const reloadedBackend = await backend.existingBackend({ projectId });
|
|
84
|
+
const prunedResult = await secrets.pruneAndDestroySecrets({ projectId, projectNumber }, backend.allEndpoints(reloadedBackend));
|
|
85
|
+
if (prunedResult.destroyed.length > 0) {
|
|
86
|
+
(0, utils_1.logLabeledBullet)("functions", `Destroyed unused secret versions: ${prunedResult.destroyed
|
|
87
|
+
.map((s) => `${s.secret}@${s.version}`)
|
|
88
|
+
.join(", ")}`);
|
|
89
|
+
}
|
|
90
|
+
if (prunedResult.erred.length > 0) {
|
|
91
|
+
(0, utils_1.logLabeledWarning)("functions", `Failed to destroy unused secret versions:\n\t${prunedResult.erred
|
|
92
|
+
.map((err) => err.message)
|
|
93
|
+
.join("\n\t")}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
62
97
|
}
|
|
63
98
|
exports.release = release;
|
|
64
99
|
function printTriggerUrls(results) {
|
|
@@ -68,7 +103,7 @@ function printTriggerUrls(results) {
|
|
|
68
103
|
}
|
|
69
104
|
for (const httpsFunc of httpsFunctions) {
|
|
70
105
|
if (!httpsFunc.uri) {
|
|
71
|
-
logger_1.logger.debug("
|
|
106
|
+
logger_1.logger.debug("Not printing URL for HTTPS function. Typically this means it didn't match a filter or we failed deployment");
|
|
72
107
|
continue;
|
|
73
108
|
}
|
|
74
109
|
logger_1.logger.info(clc.bold("Function URL"), `(${(0, functionsDeployHelper_1.getFunctionLabel)(httpsFunc)}):`, httpsFunc.uri);
|
|
@@ -7,13 +7,13 @@ const error_1 = require("../../../error");
|
|
|
7
7
|
const utils = require("../../../utils");
|
|
8
8
|
const backend = require("../backend");
|
|
9
9
|
const v2events = require("../../../functions/events/v2");
|
|
10
|
-
function calculateChangesets(want, have, keyFn,
|
|
10
|
+
function calculateChangesets(want, have, keyFn, deleteAll) {
|
|
11
11
|
const toCreate = utils.groupBy(Object.keys(want)
|
|
12
12
|
.filter((id) => !have[id])
|
|
13
13
|
.map((id) => want[id]), keyFn);
|
|
14
14
|
const toDelete = utils.groupBy(Object.keys(have)
|
|
15
15
|
.filter((id) => !want[id])
|
|
16
|
-
.filter((id) =>
|
|
16
|
+
.filter((id) => deleteAll || (0, deploymentTool_1.isFirebaseManaged)(have[id].labels || {}))
|
|
17
17
|
.map((id) => have[id]), keyFn);
|
|
18
18
|
const toUpdate = utils.groupBy(Object.keys(want)
|
|
19
19
|
.filter((id) => have[id])
|
|
@@ -48,20 +48,25 @@ function calculateUpdate(want, have) {
|
|
|
48
48
|
return update;
|
|
49
49
|
}
|
|
50
50
|
exports.calculateUpdate = calculateUpdate;
|
|
51
|
-
function createDeploymentPlan(
|
|
51
|
+
function createDeploymentPlan(args) {
|
|
52
|
+
let { wantBackend, haveBackend, codebase, filters, deleteAll } = args;
|
|
52
53
|
let deployment = {};
|
|
53
|
-
|
|
54
|
-
return (0, functionsDeployHelper_1.
|
|
54
|
+
wantBackend = backend.matchingBackend(wantBackend, (endpoint) => {
|
|
55
|
+
return (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, filters);
|
|
55
56
|
});
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
const wantedEndpoint = backend.hasEndpoint(wantBackend);
|
|
58
|
+
haveBackend = backend.matchingBackend(haveBackend, (endpoint) => {
|
|
59
|
+
return wantedEndpoint(endpoint) || (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, filters);
|
|
58
60
|
});
|
|
59
|
-
const regions = new Set([
|
|
61
|
+
const regions = new Set([
|
|
62
|
+
...Object.keys(wantBackend.endpoints),
|
|
63
|
+
...Object.keys(haveBackend.endpoints),
|
|
64
|
+
]);
|
|
60
65
|
for (const region of regions) {
|
|
61
|
-
const changesets = calculateChangesets(
|
|
66
|
+
const changesets = calculateChangesets(wantBackend.endpoints[region] || {}, haveBackend.endpoints[region] || {}, (e) => `${codebase}-${e.region}-${e.availableMemoryMb || "default"}`, deleteAll);
|
|
62
67
|
deployment = Object.assign(Object.assign({}, deployment), changesets);
|
|
63
68
|
}
|
|
64
|
-
if (upgradedToGCFv2WithoutSettingConcurrency(
|
|
69
|
+
if (upgradedToGCFv2WithoutSettingConcurrency(wantBackend, haveBackend)) {
|
|
65
70
|
utils.logLabeledBullet("functions", "You are updating one or more functions to Google Cloud Functions v2, " +
|
|
66
71
|
"which introduces support for concurrent execution. New functions " +
|
|
67
72
|
"default to 80 concurrent executions, but existing functions keep the " +
|
|
@@ -103,7 +108,6 @@ function changedTriggerRegion(want, have) {
|
|
|
103
108
|
}
|
|
104
109
|
exports.changedTriggerRegion = changedTriggerRegion;
|
|
105
110
|
function changedV2PubSubTopic(want, have) {
|
|
106
|
-
var _a, _b;
|
|
107
111
|
if (want.platform !== "gcfv2") {
|
|
108
112
|
return false;
|
|
109
113
|
}
|
|
@@ -122,7 +126,7 @@ function changedV2PubSubTopic(want, have) {
|
|
|
122
126
|
if (have.eventTrigger.eventType !== v2events.PUBSUB_PUBLISH_EVENT) {
|
|
123
127
|
return false;
|
|
124
128
|
}
|
|
125
|
-
return
|
|
129
|
+
return have.eventTrigger.eventFilters.topic !== want.eventTrigger.eventFilters.topic;
|
|
126
130
|
}
|
|
127
131
|
exports.changedV2PubSubTopic = changedV2PubSubTopic;
|
|
128
132
|
function upgradedScheduleFromV1ToV2(want, have) {
|
|
@@ -158,6 +162,9 @@ function checkForIllegalUpdate(want, have) {
|
|
|
158
162
|
else if (backend.isTaskQueueTriggered(e)) {
|
|
159
163
|
return "a task queue";
|
|
160
164
|
}
|
|
165
|
+
else if (backend.isBlockingTriggered(e)) {
|
|
166
|
+
return e.blockingTrigger.eventType;
|
|
167
|
+
}
|
|
161
168
|
throw Error("Functions release planner is not able to handle an unknown trigger type");
|
|
162
169
|
};
|
|
163
170
|
const wantType = triggerType(want);
|
|
@@ -4,7 +4,7 @@ exports.triggerTag = exports.printAbortedErrors = exports.printErrors = exports.
|
|
|
4
4
|
const backend = require("../backend");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const logger_1 = require("../../../logger");
|
|
7
|
-
const
|
|
7
|
+
const track_1 = require("../../../track");
|
|
8
8
|
const utils = require("../../../utils");
|
|
9
9
|
const functionsDeployHelper_1 = require("../functionsDeployHelper");
|
|
10
10
|
class DeploymentError extends Error {
|
|
@@ -36,23 +36,23 @@ async function logAndTrackDeployStats(summary) {
|
|
|
36
36
|
totalTime += result.durationMs;
|
|
37
37
|
if (!result.error) {
|
|
38
38
|
totalSuccesses++;
|
|
39
|
-
reports.push(
|
|
39
|
+
reports.push((0, track_1.track)("function_deploy_success", tag, result.durationMs));
|
|
40
40
|
}
|
|
41
41
|
else if (result.error instanceof AbortedDeploymentError) {
|
|
42
42
|
totalAborts++;
|
|
43
|
-
reports.push(
|
|
43
|
+
reports.push((0, track_1.track)("function_deploy_abort", tag, result.durationMs));
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
46
|
totalErrors++;
|
|
47
|
-
reports.push(
|
|
47
|
+
reports.push((0, track_1.track)("function_deploy_failure", tag, result.durationMs));
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
const regionCountTag = regions.size < 5 ? regions.size.toString() : ">=5";
|
|
51
|
-
reports.push(
|
|
51
|
+
reports.push((0, track_1.track)("functions_region_count", regionCountTag, 1));
|
|
52
52
|
const gcfv1 = summary.results.find((r) => r.endpoint.platform === "gcfv1");
|
|
53
53
|
const gcfv2 = summary.results.find((r) => r.endpoint.platform === "gcfv2");
|
|
54
54
|
const tag = gcfv1 && gcfv2 ? "v1+v2" : gcfv1 ? "v1" : "v2";
|
|
55
|
-
reports.push(
|
|
55
|
+
reports.push((0, track_1.track)("functions_codebase_deploy", tag, summary.results.length));
|
|
56
56
|
const avgTime = totalTime / (totalSuccesses + totalErrors);
|
|
57
57
|
logger_1.logger.debug(`Total Function Deployment time: ${summary.totalTime}`);
|
|
58
58
|
logger_1.logger.debug(`${totalErrors + totalSuccesses + totalAborts} Functions Deployed`);
|
|
@@ -61,15 +61,15 @@ async function logAndTrackDeployStats(summary) {
|
|
|
61
61
|
logger_1.logger.debug(`Average Function Deployment time: ${avgTime}`);
|
|
62
62
|
if (totalErrors + totalSuccesses > 0) {
|
|
63
63
|
if (totalErrors === 0) {
|
|
64
|
-
reports.push(
|
|
64
|
+
reports.push((0, track_1.track)("functions_deploy_result", "success", totalSuccesses));
|
|
65
65
|
}
|
|
66
66
|
else if (totalSuccesses > 0) {
|
|
67
|
-
reports.push(
|
|
68
|
-
reports.push(
|
|
69
|
-
reports.push(
|
|
67
|
+
reports.push((0, track_1.track)("functions_deploy_result", "partial_success", totalSuccesses));
|
|
68
|
+
reports.push((0, track_1.track)("functions_deploy_result", "partial_failure", totalErrors));
|
|
69
|
+
reports.push((0, track_1.track)("functions_deploy_result", "partial_error_ratio", totalErrors / (totalSuccesses + totalErrors)));
|
|
70
70
|
}
|
|
71
71
|
else {
|
|
72
|
-
reports.push(
|
|
72
|
+
reports.push((0, track_1.track)("functions_deploy_result", "failure", totalErrors));
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
await utils.allSettled(reports);
|
|
@@ -171,6 +171,9 @@ function triggerTag(endpoint) {
|
|
|
171
171
|
}
|
|
172
172
|
return `${prefix}.https`;
|
|
173
173
|
}
|
|
174
|
+
if (backend.isBlockingTriggered(endpoint)) {
|
|
175
|
+
return `${prefix}.blocking`;
|
|
176
|
+
}
|
|
174
177
|
return endpoint.eventTrigger.eventType;
|
|
175
178
|
}
|
|
176
179
|
exports.triggerTag = triggerTag;
|
|
@@ -19,31 +19,37 @@ function assertKeyTypes(prefix, yaml, schema) {
|
|
|
19
19
|
}
|
|
20
20
|
for (const [keyAsString, value] of Object.entries(yaml)) {
|
|
21
21
|
const key = keyAsString;
|
|
22
|
-
const fullKey = prefix ? prefix
|
|
22
|
+
const fullKey = prefix ? `${prefix}.${keyAsString}` : keyAsString;
|
|
23
23
|
if (!schema[key] || schema[key] === "omit") {
|
|
24
24
|
throw new error_1.FirebaseError(`Unexpected key ${fullKey}. You may need to install a newer version of the Firebase CLI.`);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
const schemaType = schema[key];
|
|
27
|
+
if (typeof schemaType === "function") {
|
|
28
|
+
if (!schemaType(value)) {
|
|
29
|
+
throw new error_1.FirebaseError(`${Array.isArray(value) ? "array" : typeof value} ${fullKey} failed validation`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else if (schemaType === "string") {
|
|
27
33
|
if (typeof value !== "string") {
|
|
28
34
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be string; was ${typeof value}`);
|
|
29
35
|
}
|
|
30
36
|
}
|
|
31
|
-
else if (
|
|
37
|
+
else if (schemaType === "number") {
|
|
32
38
|
if (typeof value !== "number") {
|
|
33
39
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be a number; was ${typeof value}`);
|
|
34
40
|
}
|
|
35
41
|
}
|
|
36
|
-
else if (
|
|
42
|
+
else if (schemaType === "boolean") {
|
|
37
43
|
if (typeof value !== "boolean") {
|
|
38
44
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be a boolean; was ${typeof value}`);
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
|
-
else if (
|
|
47
|
+
else if (schemaType === "array") {
|
|
42
48
|
if (!Array.isArray(value)) {
|
|
43
49
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be an array; was ${typeof value}`);
|
|
44
50
|
}
|
|
45
51
|
}
|
|
46
|
-
else if (
|
|
52
|
+
else if (schemaType === "object") {
|
|
47
53
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
48
54
|
throw new error_1.FirebaseError(`Expected ${fullKey} to be an object; was ${typeof value}`);
|
|
49
55
|
}
|