firebase-tools 9.20.0 → 9.23.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/CHANGELOG.md +5 -1
- package/lib/api.js +2 -0
- package/lib/apiv2.js +7 -4
- package/lib/commands/crashlytics-symbols-upload.js +2 -2
- package/lib/commands/deploy.js +9 -1
- package/lib/commands/ext-configure.js +1 -1
- package/lib/commands/ext-dev-deprecate.js +63 -0
- package/lib/commands/ext-dev-undeprecate.js +56 -0
- package/lib/commands/ext-dev-unpublish.js +10 -3
- package/lib/commands/ext-export.js +44 -0
- package/lib/commands/ext-install.js +1 -1
- package/lib/commands/ext-update.js +1 -1
- package/lib/commands/functions-delete.js +55 -42
- package/lib/commands/functions-list.js +11 -11
- package/lib/commands/index.js +6 -5
- package/lib/commands/init.js +3 -0
- package/lib/config.js +3 -2
- package/lib/deploy/extensions/args.js +2 -0
- package/lib/deploy/extensions/deploy.js +49 -0
- package/lib/deploy/extensions/deploymentSummary.js +52 -0
- package/lib/deploy/extensions/errors.js +31 -0
- package/lib/deploy/extensions/index.js +8 -0
- package/lib/deploy/extensions/params.js +39 -0
- package/lib/deploy/extensions/planner.js +94 -0
- package/lib/deploy/extensions/prepare.js +111 -0
- package/lib/deploy/extensions/release.js +43 -0
- package/lib/deploy/extensions/secrets.js +150 -0
- package/lib/deploy/extensions/tasks.js +98 -0
- package/lib/deploy/extensions/validate.js +17 -0
- package/lib/deploy/functions/backend.js +84 -115
- package/lib/deploy/functions/checkIam.js +73 -12
- package/lib/deploy/functions/containerCleaner.js +97 -50
- package/lib/deploy/functions/deploy.js +4 -10
- package/lib/deploy/functions/eventTypes.js +10 -0
- package/lib/deploy/functions/functionsDeployHelper.js +3 -68
- package/lib/deploy/functions/prepare.js +72 -29
- package/lib/deploy/functions/pricing.js +17 -17
- package/lib/deploy/functions/prompts.js +22 -21
- package/lib/deploy/functions/release/executor.js +39 -0
- package/lib/deploy/functions/release/fabricator.js +425 -0
- package/lib/deploy/functions/release/index.js +73 -0
- package/lib/deploy/functions/release/planner.js +162 -0
- package/lib/deploy/functions/release/reporter.js +165 -0
- package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
- package/lib/deploy/functions/release/timer.js +14 -0
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +129 -126
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +32 -54
- package/lib/deploy/functions/services/index.js +38 -0
- package/lib/deploy/functions/services/storage.js +43 -0
- package/lib/deploy/functions/triggerRegionHelper.js +9 -25
- package/lib/deploy/functions/validate.js +1 -24
- package/lib/deploy/index.js +10 -1
- package/lib/emulator/auth/apiSpec.js +37 -6
- package/lib/emulator/auth/operations.js +45 -17
- package/lib/emulator/auth/server.js +16 -2
- package/lib/emulator/auth/state.js +34 -15
- package/lib/emulator/auth/widget_ui.js +14 -0
- package/lib/emulator/downloadableEmulators.js +7 -7
- package/lib/emulator/functionsEmulator.js +18 -4
- package/lib/emulator/storage/cloudFunctions.js +37 -7
- package/lib/ensureApiEnabled.js +10 -12
- package/lib/extensions/askUserForParam.js +14 -6
- package/lib/extensions/checkProjectBilling.js +7 -7
- package/lib/extensions/export.js +107 -0
- package/lib/extensions/extensionsApi.js +103 -21
- package/lib/extensions/extensionsHelper.js +5 -2
- package/lib/extensions/listExtensions.js +16 -11
- package/lib/extensions/paramHelper.js +6 -4
- package/lib/extensions/provisioningHelper.js +16 -3
- package/lib/extensions/refs.js +9 -1
- package/lib/extensions/secretsUtils.js +10 -9
- package/lib/extensions/updateHelper.js +12 -2
- package/lib/extensions/versionHelper.js +14 -0
- package/lib/extensions/warnings.js +33 -1
- package/lib/gcp/artifactregistry.js +16 -0
- package/lib/gcp/cloudfunctions.js +25 -72
- package/lib/gcp/cloudfunctionsv2.js +46 -98
- package/lib/gcp/cloudscheduler.js +22 -16
- package/lib/gcp/cloudtasks.js +143 -0
- package/lib/gcp/docker.js +36 -2
- package/lib/gcp/location.js +44 -0
- package/lib/gcp/proto.js +2 -2
- package/lib/gcp/pubsub.js +1 -9
- package/lib/gcp/secretManager.js +27 -6
- package/lib/gcp/storage.js +48 -32
- package/lib/init/features/project.js +2 -1
- package/lib/previews.js +1 -1
- package/lib/projectUtils.js +10 -1
- package/lib/utils.js +30 -1
- package/package.json +5 -4
- package/schema/firebase-config.json +9 -0
- package/lib/deploy/functions/deploymentPlanner.js +0 -113
- package/lib/deploy/functions/deploymentTimer.js +0 -23
- package/lib/deploy/functions/errorHandler.js +0 -75
- package/lib/deploy/functions/release.js +0 -116
- package/lib/deploy/functions/tasks.js +0 -324
- package/lib/functions/listFunctions.js +0 -10
- package/lib/functionsDelete.js +0 -60
|
@@ -11,6 +11,7 @@ const gcs = require("../../gcp/storage");
|
|
|
11
11
|
const gcf = require("../../gcp/cloudfunctions");
|
|
12
12
|
const gcfv2 = require("../../gcp/cloudfunctionsv2");
|
|
13
13
|
const utils = require("../../utils");
|
|
14
|
+
const backend = require("./backend");
|
|
14
15
|
const GCP_REGION = api_1.functionsUploadRegion;
|
|
15
16
|
tmp_1.setGracefulCleanup();
|
|
16
17
|
async function uploadSourceV1(context) {
|
|
@@ -44,18 +45,11 @@ async function deploy(context, options, payload) {
|
|
|
44
45
|
try {
|
|
45
46
|
const want = payload.functions.backend;
|
|
46
47
|
const uploads = [];
|
|
47
|
-
if (want.
|
|
48
|
+
if (backend.allEndpoints(want).some((endpoint) => endpoint.platform === "gcfv1")) {
|
|
48
49
|
uploads.push(uploadSourceV1(context));
|
|
49
50
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const regions = [];
|
|
53
|
-
for (const func of functions) {
|
|
54
|
-
if (func.platform === "gcfv2" && -1 === regions.indexOf(func.region)) {
|
|
55
|
-
regions.push(func.region);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
for (const region of regions) {
|
|
51
|
+
for (const region of Object.keys(want.endpoints)) {
|
|
52
|
+
if (backend.regionalEndpoints(want, region).some((e) => e.platform === "gcfv2")) {
|
|
59
53
|
uploads.push(uploadSourceV2(context, region));
|
|
60
54
|
}
|
|
61
55
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PUBSUB_V2_EVENT = exports.STORAGE_V2_EVENTS = void 0;
|
|
4
|
+
exports.STORAGE_V2_EVENTS = [
|
|
5
|
+
"google.cloud.storage.object.v1.finalized",
|
|
6
|
+
"google.cloud.storage.object.v1.archived",
|
|
7
|
+
"google.cloud.storage.object.v1.deleted",
|
|
8
|
+
"google.cloud.storage.object.v1.metadataUpdated",
|
|
9
|
+
];
|
|
10
|
+
exports.PUBSUB_V2_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const clc = require("cli-color");
|
|
5
|
-
const logger_1 = require("../../logger");
|
|
6
|
-
const backend = require("./backend");
|
|
7
|
-
const track = require("../../track");
|
|
8
|
-
const utils = require("../../utils");
|
|
3
|
+
exports.getFunctionLabel = exports.getFilterGroups = exports.functionMatchesGroup = exports.functionMatchesAnyGroup = void 0;
|
|
9
4
|
function functionMatchesAnyGroup(func, filterGroups) {
|
|
10
5
|
if (!filterGroups.length) {
|
|
11
6
|
return true;
|
|
@@ -40,67 +35,7 @@ function getFilterGroups(options) {
|
|
|
40
35
|
});
|
|
41
36
|
}
|
|
42
37
|
exports.getFilterGroups = getFilterGroups;
|
|
43
|
-
function
|
|
44
|
-
return
|
|
45
|
-
}
|
|
46
|
-
exports.getFunctionId = getFunctionId;
|
|
47
|
-
function getRegion(fullName) {
|
|
48
|
-
return fullName.split("/")[3];
|
|
49
|
-
}
|
|
50
|
-
function getFunctionLabel(fnOrName) {
|
|
51
|
-
if (typeof fnOrName === "string") {
|
|
52
|
-
return getFunctionId(fnOrName) + "(" + getRegion(fnOrName) + ")";
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
return `${fnOrName.id}(${fnOrName.region})`;
|
|
56
|
-
}
|
|
38
|
+
function getFunctionLabel(fn) {
|
|
39
|
+
return `${fn.id}(${fn.region})`;
|
|
57
40
|
}
|
|
58
41
|
exports.getFunctionLabel = getFunctionLabel;
|
|
59
|
-
function logAndTrackDeployStats(queue, errorHandler) {
|
|
60
|
-
const stats = queue.stats();
|
|
61
|
-
logger_1.logger.debug(`Total Function Deployment time: ${stats.elapsed}`);
|
|
62
|
-
logger_1.logger.debug(`${stats.total} Functions Deployed`);
|
|
63
|
-
logger_1.logger.debug(`${errorHandler.errors.length} Functions Errored`);
|
|
64
|
-
logger_1.logger.debug(`Average Function Deployment time: ${stats.avg}`);
|
|
65
|
-
if (stats.total > 0) {
|
|
66
|
-
if (errorHandler.errors.length === 0) {
|
|
67
|
-
track("functions_deploy_result", "success", stats.total);
|
|
68
|
-
}
|
|
69
|
-
else if (errorHandler.errors.length < stats.total) {
|
|
70
|
-
track("functions_deploy_result", "partial_success", stats.total - errorHandler.errors.length);
|
|
71
|
-
track("functions_deploy_result", "partial_failure", errorHandler.errors.length);
|
|
72
|
-
track("functions_deploy_result", "partial_error_ratio", errorHandler.errors.length / stats.total);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
track("functions_deploy_result", "failure", stats.total);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
exports.logAndTrackDeployStats = logAndTrackDeployStats;
|
|
80
|
-
function printSuccess(func, type) {
|
|
81
|
-
utils.logSuccess(clc.bold.green("functions[" + getFunctionLabel(func) + "]: ") +
|
|
82
|
-
"Successful " +
|
|
83
|
-
type +
|
|
84
|
-
" operation. ");
|
|
85
|
-
}
|
|
86
|
-
exports.printSuccess = printSuccess;
|
|
87
|
-
async function printTriggerUrls(context, want) {
|
|
88
|
-
const have = await backend.existingBackend(context, true);
|
|
89
|
-
const httpsFunctions = have.cloudFunctions.filter((fn) => {
|
|
90
|
-
if (backend.isEventTrigger(fn.trigger)) {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
return want.cloudFunctions.some(backend.sameFunctionName(fn));
|
|
94
|
-
});
|
|
95
|
-
if (httpsFunctions.length === 0) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
for (const httpsFunc of httpsFunctions) {
|
|
99
|
-
if (!httpsFunc.uri) {
|
|
100
|
-
logger_1.logger.debug("Missing URI for HTTPS function in printTriggerUrls. This shouldn't happen");
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
logger_1.logger.info(clc.bold("Function URL"), `(${getFunctionLabel(httpsFunc)}):`, httpsFunc.uri);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
exports.printTriggerUrls = printTriggerUrls;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.prepare = void 0;
|
|
3
|
+
exports.inferDetailsFromExisting = exports.prepare = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const ensureCloudBuildEnabled_1 = require("./ensureCloudBuildEnabled");
|
|
6
6
|
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
@@ -19,12 +19,20 @@ const validate = require("./validate");
|
|
|
19
19
|
const utils = require("../../utils");
|
|
20
20
|
const logger_1 = require("../../logger");
|
|
21
21
|
const triggerRegionHelper_1 = require("./triggerRegionHelper");
|
|
22
|
+
const checkIam_1 = require("./checkIam");
|
|
22
23
|
function hasUserConfig(config) {
|
|
23
24
|
return Object.keys(config).length > 1;
|
|
24
25
|
}
|
|
25
26
|
function hasDotenv(opts) {
|
|
26
27
|
return previews_1.previews.dotenv && functionsEnv.hasUserEnvs(opts);
|
|
27
28
|
}
|
|
29
|
+
async function maybeEnableAR(projectId) {
|
|
30
|
+
if (previews_1.previews.artifactregistry) {
|
|
31
|
+
return ensureApiEnabled.check(projectId, "artifactregistry.googleapis.com", "functions", true);
|
|
32
|
+
}
|
|
33
|
+
await ensureApiEnabled.ensure(projectId, "artifactregistry.googleapis.com", "functions");
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
28
36
|
async function prepare(context, options, payload) {
|
|
29
37
|
if (!options.config.src.functions) {
|
|
30
38
|
return;
|
|
@@ -39,8 +47,10 @@ async function prepare(context, options, payload) {
|
|
|
39
47
|
ensureApiEnabled.ensure(projectId, "cloudfunctions.googleapis.com", "functions"),
|
|
40
48
|
ensureApiEnabled.check(projectId, "runtimeconfig.googleapis.com", "runtimeconfig", true),
|
|
41
49
|
ensureCloudBuildEnabled_1.ensureCloudBuildEnabled(projectId),
|
|
50
|
+
maybeEnableAR(projectId),
|
|
42
51
|
]);
|
|
43
52
|
context.runtimeConfigEnabled = checkAPIsEnabled[1];
|
|
53
|
+
context.artifactRegistryEnabled = checkAPIsEnabled[3];
|
|
44
54
|
const firebaseConfig = await functionsConfig.getFirebaseConfig(options);
|
|
45
55
|
context.firebaseConfig = firebaseConfig;
|
|
46
56
|
const runtimeConfig = await prepareFunctionsUpload_1.getFunctionsConfig(context);
|
|
@@ -53,58 +63,91 @@ async function prepare(context, options, payload) {
|
|
|
53
63
|
projectAlias: options.projectAlias,
|
|
54
64
|
};
|
|
55
65
|
const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
|
|
66
|
+
const usedDotenv = hasDotenv(userEnvOpt);
|
|
56
67
|
const tag = hasUserConfig(runtimeConfig)
|
|
57
|
-
?
|
|
68
|
+
? usedDotenv
|
|
58
69
|
? "mixed"
|
|
59
70
|
: "runtime_config"
|
|
60
|
-
:
|
|
71
|
+
: usedDotenv
|
|
61
72
|
? "dotenv"
|
|
62
73
|
: "none";
|
|
63
|
-
track_1.track("functions_codebase_deploy_env_method", tag);
|
|
74
|
+
await track_1.track("functions_codebase_deploy_env_method", tag);
|
|
64
75
|
logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
|
|
65
76
|
const wantBackend = await runtimeDelegate.discoverSpec(runtimeConfig, firebaseEnvs);
|
|
66
77
|
wantBackend.environmentVariables = Object.assign(Object.assign({}, userEnvs), firebaseEnvs);
|
|
67
78
|
payload.functions = { backend: wantBackend };
|
|
68
|
-
if (
|
|
69
|
-
const V2_APIS =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const enablements =
|
|
77
|
-
return ensureApiEnabled.ensure(context.projectId, api,
|
|
79
|
+
if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
|
|
80
|
+
const V2_APIS = [
|
|
81
|
+
"artifactregistry.googleapis.com",
|
|
82
|
+
"run.googleapis.com",
|
|
83
|
+
"eventarc.googleapis.com",
|
|
84
|
+
"pubsub.googleapis.com",
|
|
85
|
+
"storage.googleapis.com",
|
|
86
|
+
];
|
|
87
|
+
const enablements = V2_APIS.map((api) => {
|
|
88
|
+
return ensureApiEnabled.ensure(context.projectId, api, "functions");
|
|
78
89
|
});
|
|
79
90
|
await Promise.all(enablements);
|
|
80
91
|
}
|
|
81
|
-
if (wantBackend
|
|
92
|
+
if (backend.someEndpoint(wantBackend, () => true)) {
|
|
82
93
|
utils_1.logBullet(clc.cyan.bold("functions:") +
|
|
83
94
|
" preparing " +
|
|
84
95
|
clc.bold(options.config.src.functions.source) +
|
|
85
96
|
" directory for uploading...");
|
|
86
97
|
}
|
|
87
|
-
if (
|
|
98
|
+
if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv1")) {
|
|
88
99
|
context.functionsSourceV1 = await prepareFunctionsUpload_1.prepareFunctionsUpload(runtimeConfig, options);
|
|
89
100
|
}
|
|
90
|
-
if (
|
|
101
|
+
if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
|
|
91
102
|
context.functionsSourceV2 = await prepareFunctionsUpload_1.prepareFunctionsUpload(undefined, options);
|
|
92
103
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
await Promise.all(Object.
|
|
97
|
-
ensureApiEnabled.ensure(projectId,
|
|
104
|
+
for (const endpoint of backend.allEndpoints(wantBackend)) {
|
|
105
|
+
endpoint.environmentVariables = wantBackend.environmentVariables;
|
|
106
|
+
}
|
|
107
|
+
await Promise.all(Object.values(wantBackend.requiredAPIs).map((api) => {
|
|
108
|
+
return ensureApiEnabled.ensure(projectId, api, "functions", false);
|
|
98
109
|
}));
|
|
99
|
-
validate.functionIdsAreValid(wantBackend
|
|
110
|
+
validate.functionIdsAreValid(backend.allEndpoints(wantBackend));
|
|
100
111
|
context.filters = functionsDeployHelper_1.getFilterGroups(options);
|
|
101
|
-
const
|
|
102
|
-
return functionsDeployHelper_1.functionMatchesAnyGroup(
|
|
112
|
+
const matchingBackend = backend.matchingBackend(wantBackend, (endpoint) => {
|
|
113
|
+
return functionsDeployHelper_1.functionMatchesAnyGroup(endpoint, context.filters);
|
|
103
114
|
});
|
|
104
|
-
const
|
|
105
|
-
await
|
|
106
|
-
|
|
107
|
-
await
|
|
115
|
+
const haveBackend = await backend.existingBackend(context);
|
|
116
|
+
await checkIam_1.ensureServiceAgentRoles(projectId, wantBackend, haveBackend);
|
|
117
|
+
inferDetailsFromExisting(wantBackend, haveBackend, usedDotenv);
|
|
118
|
+
await triggerRegionHelper_1.ensureTriggerRegions(wantBackend);
|
|
119
|
+
await prompts_1.promptForFailurePolicies(options, matchingBackend, haveBackend);
|
|
120
|
+
await prompts_1.promptForMinInstances(options, matchingBackend, haveBackend);
|
|
108
121
|
await backend.checkAvailability(context, wantBackend);
|
|
109
122
|
}
|
|
110
123
|
exports.prepare = prepare;
|
|
124
|
+
function inferDetailsFromExisting(want, have, usedDotenv) {
|
|
125
|
+
var _a;
|
|
126
|
+
for (const wantE of backend.allEndpoints(want)) {
|
|
127
|
+
const haveE = (_a = have.endpoints[wantE.region]) === null || _a === void 0 ? void 0 : _a[wantE.id];
|
|
128
|
+
if (!haveE) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!usedDotenv) {
|
|
132
|
+
wantE.environmentVariables = Object.assign(Object.assign({}, haveE.environmentVariables), wantE.environmentVariables);
|
|
133
|
+
}
|
|
134
|
+
if (!wantE.availableMemoryMb && haveE.availableMemoryMb) {
|
|
135
|
+
wantE.availableMemoryMb = haveE.availableMemoryMb;
|
|
136
|
+
}
|
|
137
|
+
maybeCopyTriggerRegion(wantE, haveE);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.inferDetailsFromExisting = inferDetailsFromExisting;
|
|
141
|
+
function maybeCopyTriggerRegion(wantE, haveE) {
|
|
142
|
+
if (!backend.isEventTriggered(wantE) || !backend.isEventTriggered(haveE)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (wantE.eventTrigger.region || !haveE.eventTrigger.region) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (JSON.stringify(haveE.eventTrigger.eventFilters) !==
|
|
149
|
+
JSON.stringify(wantE.eventTrigger.eventFilters)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
wantE.eventTrigger.region = haveE.eventTrigger.region;
|
|
153
|
+
}
|
|
@@ -102,52 +102,52 @@ const MB_TO_GHZ = {
|
|
|
102
102
|
4096: 4.8,
|
|
103
103
|
8192: 4.8,
|
|
104
104
|
};
|
|
105
|
-
function canCalculateMinInstanceCost(
|
|
106
|
-
if (!
|
|
105
|
+
function canCalculateMinInstanceCost(endpoint) {
|
|
106
|
+
if (!endpoint.minInstances) {
|
|
107
107
|
return true;
|
|
108
108
|
}
|
|
109
|
-
if (
|
|
110
|
-
if (!MB_TO_GHZ[
|
|
109
|
+
if (endpoint.platform == "gcfv1") {
|
|
110
|
+
if (!MB_TO_GHZ[endpoint.availableMemoryMb || 256]) {
|
|
111
111
|
return false;
|
|
112
112
|
}
|
|
113
|
-
if (!V1_REGION_TO_TIER[
|
|
113
|
+
if (!V1_REGION_TO_TIER[endpoint.region]) {
|
|
114
114
|
return false;
|
|
115
115
|
}
|
|
116
116
|
return true;
|
|
117
117
|
}
|
|
118
|
-
if (!V2_REGION_TO_TIER[
|
|
118
|
+
if (!V2_REGION_TO_TIER[endpoint.region]) {
|
|
119
119
|
return false;
|
|
120
120
|
}
|
|
121
121
|
return true;
|
|
122
122
|
}
|
|
123
123
|
exports.canCalculateMinInstanceCost = canCalculateMinInstanceCost;
|
|
124
124
|
const SECONDS_PER_MONTH = 30 * 24 * 60 * 60;
|
|
125
|
-
function monthlyMinInstanceCost(
|
|
125
|
+
function monthlyMinInstanceCost(endpoints) {
|
|
126
126
|
const usage = {
|
|
127
127
|
gcfv1: { 1: { ram: 0, cpu: 0 }, 2: { ram: 0, cpu: 0 } },
|
|
128
128
|
gcfv2: { 1: { ram: 0, cpu: 0 }, 2: { ram: 0, cpu: 0 } },
|
|
129
129
|
};
|
|
130
|
-
for (const
|
|
131
|
-
if (!
|
|
130
|
+
for (const endpoint of endpoints) {
|
|
131
|
+
if (!endpoint.minInstances) {
|
|
132
132
|
continue;
|
|
133
133
|
}
|
|
134
|
-
const ramMb =
|
|
134
|
+
const ramMb = endpoint.availableMemoryMb || 256;
|
|
135
135
|
const ramGb = ramMb / 1024;
|
|
136
|
-
if (
|
|
136
|
+
if (endpoint.platform === "gcfv1") {
|
|
137
137
|
const cpu = MB_TO_GHZ[ramMb];
|
|
138
|
-
const tier = V1_REGION_TO_TIER[
|
|
138
|
+
const tier = V1_REGION_TO_TIER[endpoint.region];
|
|
139
139
|
usage["gcfv1"][tier].ram =
|
|
140
|
-
usage["gcfv1"][tier].ram + ramGb * SECONDS_PER_MONTH *
|
|
140
|
+
usage["gcfv1"][tier].ram + ramGb * SECONDS_PER_MONTH * endpoint.minInstances;
|
|
141
141
|
usage["gcfv1"][tier].cpu =
|
|
142
|
-
usage["gcfv1"][tier].cpu +
|
|
142
|
+
usage["gcfv1"][tier].cpu + cpu * SECONDS_PER_MONTH * endpoint.minInstances;
|
|
143
143
|
}
|
|
144
144
|
else {
|
|
145
145
|
const cpu = 1;
|
|
146
|
-
const tier = V2_REGION_TO_TIER[
|
|
146
|
+
const tier = V2_REGION_TO_TIER[endpoint.region];
|
|
147
147
|
usage["gcfv2"][tier].ram =
|
|
148
|
-
usage["gcfv2"][tier].ram + ramGb * SECONDS_PER_MONTH *
|
|
148
|
+
usage["gcfv2"][tier].ram + ramGb * SECONDS_PER_MONTH * endpoint.minInstances;
|
|
149
149
|
usage["gcfv2"][tier].cpu =
|
|
150
|
-
usage["gcfv2"][tier].cpu + cpu * SECONDS_PER_MONTH *
|
|
150
|
+
usage["gcfv2"][tier].cpu + cpu * SECONDS_PER_MONTH * endpoint.minInstances;
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
let v1MemoryBill = usage["gcfv1"][1].ram * exports.V1_RATES.memoryGb[1] + usage["gcfv1"][2].ram * exports.V1_RATES.memoryGb[2];
|
|
@@ -10,23 +10,22 @@ const backend = require("./backend");
|
|
|
10
10
|
const pricing = require("./pricing");
|
|
11
11
|
const utils = require("../../utils");
|
|
12
12
|
async function promptForFailurePolicies(options, want, have) {
|
|
13
|
-
const
|
|
14
|
-
return backend.
|
|
13
|
+
const retryEndpoints = backend.allEndpoints(want).filter((e) => {
|
|
14
|
+
return backend.isEventTriggered(e) && e.eventTrigger.retry;
|
|
15
15
|
});
|
|
16
|
-
if (
|
|
16
|
+
if (retryEndpoints.length === 0) {
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
-
const
|
|
20
|
-
|
|
19
|
+
const newRetryEndpoints = retryEndpoints.filter((endpoint) => {
|
|
20
|
+
var _a;
|
|
21
|
+
const existing = (_a = have.endpoints[endpoint.region]) === null || _a === void 0 ? void 0 : _a[endpoint.id];
|
|
22
|
+
return !(existing && backend.isEventTriggered(existing) && existing.eventTrigger.retry);
|
|
21
23
|
});
|
|
22
|
-
|
|
23
|
-
return !existingRetryFunctions.some(backend.sameFunctionName(fn));
|
|
24
|
-
});
|
|
25
|
-
if (newRetryFunctions.length == 0) {
|
|
24
|
+
if (newRetryEndpoints.length == 0) {
|
|
26
25
|
return;
|
|
27
26
|
}
|
|
28
27
|
const warnMessage = "The following functions will newly be retried in case of failure: " +
|
|
29
|
-
clc.bold(
|
|
28
|
+
clc.bold(newRetryEndpoints.sort(backend.compareFunctions).map(functionsDeployHelper_1.getFunctionLabel).join(", ")) +
|
|
30
29
|
". " +
|
|
31
30
|
"Retried executions are billed as any other execution, and functions are retried repeatedly until they either successfully execute or the maximum retry period has elapsed, which can be up to 7 days. " +
|
|
32
31
|
"For safety, you might want to ensure that your functions are idempotent; see https://firebase.google.com/docs/functions/retries to learn more.";
|
|
@@ -90,21 +89,22 @@ async function promptForMinInstances(options, want, have) {
|
|
|
90
89
|
if (options.force) {
|
|
91
90
|
return;
|
|
92
91
|
}
|
|
93
|
-
const increasesCost =
|
|
94
|
-
|
|
92
|
+
const increasesCost = backend.someEndpoint(want, (wantE) => {
|
|
93
|
+
var _a;
|
|
94
|
+
if (!pricing.canCalculateMinInstanceCost(wantE)) {
|
|
95
95
|
return true;
|
|
96
96
|
}
|
|
97
|
-
const wantCost = pricing.monthlyMinInstanceCost([
|
|
98
|
-
const
|
|
97
|
+
const wantCost = pricing.monthlyMinInstanceCost([wantE]);
|
|
98
|
+
const haveE = (_a = have.endpoints[wantE.region]) === null || _a === void 0 ? void 0 : _a[wantE.id];
|
|
99
99
|
let haveCost;
|
|
100
|
-
if (!
|
|
100
|
+
if (!haveE) {
|
|
101
101
|
haveCost = 0;
|
|
102
102
|
}
|
|
103
|
-
else if (!pricing.canCalculateMinInstanceCost(
|
|
103
|
+
else if (!pricing.canCalculateMinInstanceCost(wantE)) {
|
|
104
104
|
return true;
|
|
105
105
|
}
|
|
106
106
|
else {
|
|
107
|
-
haveCost = pricing.monthlyMinInstanceCost([
|
|
107
|
+
haveCost = pricing.monthlyMinInstanceCost([haveE]);
|
|
108
108
|
}
|
|
109
109
|
return wantCost > haveCost;
|
|
110
110
|
});
|
|
@@ -116,7 +116,8 @@ async function promptForMinInstances(options, want, have) {
|
|
|
116
116
|
exit: 1,
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
|
-
const functionLines =
|
|
119
|
+
const functionLines = backend
|
|
120
|
+
.allEndpoints(want)
|
|
120
121
|
.filter((fn) => fn.minInstances)
|
|
121
122
|
.sort(backend.compareFunctions)
|
|
122
123
|
.map((fn) => {
|
|
@@ -126,17 +127,17 @@ async function promptForMinInstances(options, want, have) {
|
|
|
126
127
|
})
|
|
127
128
|
.join("\n");
|
|
128
129
|
let costLine;
|
|
129
|
-
if (
|
|
130
|
+
if (backend.someEndpoint(want, (fn) => !pricing.canCalculateMinInstanceCost(fn))) {
|
|
130
131
|
costLine =
|
|
131
132
|
"Cannot calculate the minimum monthly bill for this configuration. Consider running " +
|
|
132
133
|
clc.bold("npm install -g firebase-tools");
|
|
133
134
|
}
|
|
134
135
|
else {
|
|
135
|
-
const cost = pricing.monthlyMinInstanceCost(want).toFixed(2);
|
|
136
|
+
const cost = pricing.monthlyMinInstanceCost(backend.allEndpoints(want)).toFixed(2);
|
|
136
137
|
costLine = `With these options, your minimum bill will be $${cost} in a 30-day month`;
|
|
137
138
|
}
|
|
138
139
|
let cudAnnotation = "";
|
|
139
|
-
if (
|
|
140
|
+
if (backend.someEndpoint(want, (fn) => fn.platform == "gcfv2" && !!fn.minInstances)) {
|
|
140
141
|
cudAnnotation =
|
|
141
142
|
"\nThis bill can be lowered with a one year commitment. See https://cloud.google.com/run/cud for more";
|
|
142
143
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InlineExecutor = exports.QueueExecutor = void 0;
|
|
4
|
+
const queue_1 = require("../../../throttler/queue");
|
|
5
|
+
async function handler(op) {
|
|
6
|
+
var _a, _b, _c, _d, _e, _f;
|
|
7
|
+
try {
|
|
8
|
+
op.result = await op.func();
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
const code = err.status ||
|
|
12
|
+
err.code || ((_b = (_a = err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) || ((_c = err.original) === null || _c === void 0 ? void 0 : _c.code) || ((_f = (_e = (_d = err.original) === null || _d === void 0 ? void 0 : _d.context) === null || _e === void 0 ? void 0 : _e.response) === null || _f === void 0 ? void 0 : _f.statusCode);
|
|
13
|
+
if (code === 429 || code === 409) {
|
|
14
|
+
throw err;
|
|
15
|
+
}
|
|
16
|
+
op.error = err;
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
class QueueExecutor {
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.queue = new queue_1.Queue(Object.assign(Object.assign({}, options), { handler }));
|
|
23
|
+
}
|
|
24
|
+
async run(func) {
|
|
25
|
+
const op = { func };
|
|
26
|
+
await this.queue.run(op);
|
|
27
|
+
if (op.error) {
|
|
28
|
+
throw op.error;
|
|
29
|
+
}
|
|
30
|
+
return op.result;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.QueueExecutor = QueueExecutor;
|
|
34
|
+
class InlineExecutor {
|
|
35
|
+
run(func) {
|
|
36
|
+
return func();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.InlineExecutor = InlineExecutor;
|