firebase-tools 9.21.0 → 9.23.2
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 +7 -3
- package/lib/api.js +2 -0
- package/lib/apiv2.js +3 -2
- package/lib/commands/crashlytics-symbols-upload.js +1 -1
- 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-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 +8 -0
- package/lib/commands/index.js +6 -5
- package/lib/commands/init.js +3 -0
- package/lib/commands/remoteconfig-get.js +6 -5
- 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 +8 -1
- package/lib/deploy/functions/checkIam.js +65 -4
- package/lib/deploy/functions/containerCleaner.js +97 -50
- package/lib/deploy/functions/eventTypes.js +10 -0
- package/lib/deploy/functions/prepare.js +12 -1
- package/lib/deploy/functions/release/fabricator.js +75 -10
- package/lib/deploy/functions/release/index.js +9 -1
- package/lib/deploy/functions/release/planner.js +3 -0
- package/lib/deploy/functions/release/reporter.js +8 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +28 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +10 -11
- package/lib/deploy/functions/services/index.js +38 -0
- package/lib/deploy/functions/services/storage.js +43 -0
- package/lib/deploy/functions/triggerRegionHelper.js +6 -30
- package/lib/deploy/index.js +10 -1
- package/lib/emulator/auth/handlers.js +1 -1
- package/lib/emulator/auth/operations.js +27 -9
- package/lib/emulator/auth/widget_ui.js +17 -3
- package/lib/emulator/functionsEmulator.js +18 -2
- package/lib/emulator/functionsEmulatorShared.js +1 -0
- package/lib/emulator/pubsubEmulator.js +58 -45
- package/lib/emulator/storage/cloudFunctions.js +13 -6
- package/lib/ensureApiEnabled.js +11 -14
- package/lib/extensions/askUserForParam.js +42 -10
- package/lib/extensions/checkProjectBilling.js +7 -7
- package/lib/extensions/emulator/triggerHelper.js +1 -0
- package/lib/extensions/export.js +107 -0
- package/lib/extensions/extensionsApi.js +103 -21
- package/lib/extensions/extensionsHelper.js +4 -1
- 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/functions/env.js +2 -2
- package/lib/gcp/artifactregistry.js +16 -0
- package/lib/gcp/cloudfunctions.js +27 -7
- package/lib/gcp/cloudfunctionsv2.js +45 -5
- 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/secretManager.js +27 -6
- package/lib/gcp/storage.js +48 -32
- package/lib/init/features/functions/index.js +3 -3
- package/lib/init/features/hosting/github.js +3 -0
- package/lib/init/features/project.js +2 -1
- package/lib/previews.js +1 -1
- package/lib/projectUtils.js +10 -1
- package/package.json +5 -4
- package/schema/firebase-config.json +9 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.prettySecretName = exports.getSecretLabels = exports.getManagedSecrets = exports.grantFirexServiceAgentSecretAdminRole = exports.usesSecrets = exports.ensureSecretManagerApiEnabled = void 0;
|
|
3
|
+
exports.prettySecretName = exports.getSecretLabels = exports.getActiveSecrets = exports.getManagedSecrets = exports.grantFirexServiceAgentSecretAdminRole = exports.usesSecrets = exports.ensureSecretManagerApiEnabled = exports.SECRET_LABEL = void 0;
|
|
4
4
|
const getProjectNumber_1 = require("../getProjectNumber");
|
|
5
5
|
const utils = require("../utils");
|
|
6
6
|
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
@@ -8,7 +8,7 @@ const projectUtils_1 = require("../projectUtils");
|
|
|
8
8
|
const extensionsApi = require("./extensionsApi");
|
|
9
9
|
const secretManagerApi = require("../gcp/secretManager");
|
|
10
10
|
const logger_1 = require("../logger");
|
|
11
|
-
|
|
11
|
+
exports.SECRET_LABEL = "firebase-extensions-managed";
|
|
12
12
|
async function ensureSecretManagerApiEnabled(options) {
|
|
13
13
|
const projectId = projectUtils_1.needProjectId(options);
|
|
14
14
|
return await ensureApiEnabled_1.ensure(projectId, "secretmanager.googleapis.com", "extensions", options.markdown);
|
|
@@ -26,24 +26,25 @@ async function grantFirexServiceAgentSecretAdminRole(secret) {
|
|
|
26
26
|
}
|
|
27
27
|
exports.grantFirexServiceAgentSecretAdminRole = grantFirexServiceAgentSecretAdminRole;
|
|
28
28
|
async function getManagedSecrets(instance) {
|
|
29
|
-
return (await Promise.all(getActiveSecrets(instance).map(async (secretResourceName) => {
|
|
29
|
+
return (await Promise.all(getActiveSecrets(instance.config.source.spec, instance.config.params).map(async (secretResourceName) => {
|
|
30
30
|
const secret = secretManagerApi.parseSecretResourceName(secretResourceName);
|
|
31
|
-
const labels = await secretManagerApi.
|
|
32
|
-
if (labels && labels[SECRET_LABEL]) {
|
|
31
|
+
const labels = (await secretManagerApi.getSecret(secret.projectId, secret.name)).labels;
|
|
32
|
+
if (labels && labels[exports.SECRET_LABEL]) {
|
|
33
33
|
return secretResourceName;
|
|
34
34
|
}
|
|
35
35
|
return Promise.resolve("");
|
|
36
36
|
}))).filter((secretId) => !!secretId);
|
|
37
37
|
}
|
|
38
38
|
exports.getManagedSecrets = getManagedSecrets;
|
|
39
|
-
function getActiveSecrets(
|
|
40
|
-
return
|
|
41
|
-
.map((p) => p.type == extensionsApi.ParamType.SECRET
|
|
39
|
+
function getActiveSecrets(spec, params) {
|
|
40
|
+
return spec.params
|
|
41
|
+
.map((p) => (p.type == extensionsApi.ParamType.SECRET ? params[p.param] : ""))
|
|
42
42
|
.filter((pv) => !!pv);
|
|
43
43
|
}
|
|
44
|
+
exports.getActiveSecrets = getActiveSecrets;
|
|
44
45
|
function getSecretLabels(instanceId) {
|
|
45
46
|
const labels = {};
|
|
46
|
-
labels[SECRET_LABEL] = instanceId;
|
|
47
|
+
labels[exports.SECRET_LABEL] = instanceId;
|
|
47
48
|
return labels;
|
|
48
49
|
}
|
|
49
50
|
exports.getSecretLabels = getSecretLabels;
|
|
@@ -62,10 +62,20 @@ exports.displayChanges = displayChanges;
|
|
|
62
62
|
async function update(updateOptions) {
|
|
63
63
|
const { projectId, instanceId, source, extRef, params } = updateOptions;
|
|
64
64
|
if (extRef) {
|
|
65
|
-
return await extensionsApi.updateInstanceFromRegistry(
|
|
65
|
+
return await extensionsApi.updateInstanceFromRegistry({
|
|
66
|
+
projectId,
|
|
67
|
+
instanceId,
|
|
68
|
+
extRef,
|
|
69
|
+
params,
|
|
70
|
+
});
|
|
66
71
|
}
|
|
67
72
|
else if (source) {
|
|
68
|
-
return await extensionsApi.updateInstance(
|
|
73
|
+
return await extensionsApi.updateInstance({
|
|
74
|
+
projectId,
|
|
75
|
+
instanceId,
|
|
76
|
+
extensionSource: source,
|
|
77
|
+
params,
|
|
78
|
+
});
|
|
69
79
|
}
|
|
70
80
|
throw new error_1.FirebaseError(`Neither a source nor a version of the extension was supplied for ${instanceId}. Please make sure this is a valid extension and try again.`);
|
|
71
81
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseVersionPredicate = void 0;
|
|
4
|
+
const error_1 = require("../error");
|
|
5
|
+
function parseVersionPredicate(versionPredicate) {
|
|
6
|
+
const versionPredicateRegex = "^(?<comparator>>=|<=|>|<)?(?<targetSemVer>.*)";
|
|
7
|
+
const matches = versionPredicate.match(versionPredicateRegex);
|
|
8
|
+
if (!matches || !matches.groups.targetSemVer) {
|
|
9
|
+
throw new error_1.FirebaseError("Invalid version predicate.");
|
|
10
|
+
}
|
|
11
|
+
const comparator = matches.groups.comparator || "=";
|
|
12
|
+
return { comparator, targetSemVer: matches.groups.targetSemVer };
|
|
13
|
+
}
|
|
14
|
+
exports.parseVersionPredicate = parseVersionPredicate;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.displayWarningPrompts = void 0;
|
|
3
|
+
exports.displayWarningsForDeploy = exports.displayWarningPrompts = void 0;
|
|
4
4
|
const marked = require("marked");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const extensionsApi_1 = require("./extensionsApi");
|
|
7
7
|
const displayExtensionInfo_1 = require("./displayExtensionInfo");
|
|
8
8
|
const extensionsHelper_1 = require("./extensionsHelper");
|
|
9
9
|
const resolveSource_1 = require("./resolveSource");
|
|
10
|
+
const deploymentSummary_1 = require("../deploy/extensions/deploymentSummary");
|
|
11
|
+
const planner_1 = require("../deploy/extensions/planner");
|
|
12
|
+
const functional_1 = require("../functional");
|
|
10
13
|
const utils = require("../utils");
|
|
11
14
|
function displayEAPWarning({ publisherId, sourceDownloadUri, githubLink, }) {
|
|
12
15
|
const publisherNameLink = githubLink ? `[${publisherId}](${githubLink})` : publisherId;
|
|
@@ -35,3 +38,32 @@ async function displayWarningPrompts(publisherId, launchStage, extensionVersion)
|
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
exports.displayWarningPrompts = displayWarningPrompts;
|
|
41
|
+
const toListEntry = (i) => {
|
|
42
|
+
var _a, _b, _c, _d;
|
|
43
|
+
const idAndRef = deploymentSummary_1.humanReadable(i);
|
|
44
|
+
const sourceCodeLink = `\n\t[Source Code](${(_a = i.extensionVersion) === null || _a === void 0 ? void 0 : _a.sourceDownloadUri})`;
|
|
45
|
+
const githubLink = ((_c = (_b = i.extensionVersion) === null || _b === void 0 ? void 0 : _b.spec) === null || _c === void 0 ? void 0 : _c.sourceUrl) ? `\n\t[Publisher Contact](${(_d = i.extensionVersion) === null || _d === void 0 ? void 0 : _d.spec.sourceUrl})`
|
|
46
|
+
: "";
|
|
47
|
+
return `${idAndRef}${sourceCodeLink}${githubLink}`;
|
|
48
|
+
};
|
|
49
|
+
async function displayWarningsForDeploy(instancesToCreate) {
|
|
50
|
+
const trustedPublishers = await resolveSource_1.getTrustedPublishers();
|
|
51
|
+
for (const i of instancesToCreate) {
|
|
52
|
+
await planner_1.getExtension(i);
|
|
53
|
+
await planner_1.getExtensionVersion(i);
|
|
54
|
+
}
|
|
55
|
+
const [eapExtensions, nonEapExtensions] = functional_1.partition(instancesToCreate, (i) => { var _a, _b; return !trustedPublishers.includes((_b = (_a = i.ref) === null || _a === void 0 ? void 0 : _a.publisherId) !== null && _b !== void 0 ? _b : ""); });
|
|
56
|
+
const experimental = nonEapExtensions.filter((i) => i.extension.registryLaunchStage === extensionsApi_1.RegistryLaunchStage.EXPERIMENTAL);
|
|
57
|
+
if (experimental.length) {
|
|
58
|
+
const humanReadableList = experimental.map((i) => `\t${deploymentSummary_1.humanReadable(i)}`).join("\n");
|
|
59
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`The following are instances of ${clc.bold("experimental")} extensions.They may not be production-ready. Their functionality may change in backward-incompatible ways before their official release, or they may be discontinued.\n${humanReadableList}\n`));
|
|
60
|
+
}
|
|
61
|
+
if (eapExtensions.length) {
|
|
62
|
+
const humanReadableList = eapExtensions.map(toListEntry).join("\n");
|
|
63
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`These extensions are in preview and are built by a developer in the Extensions Publisher Early Access Program (http://bit.ly/firex-provider. Their functionality might change in backwards-incompatible ways. Since these extensions aren't built by Firebase, reach out to their publisher with questions about them.` +
|
|
64
|
+
` They are provided “AS IS”, without any warranty, express or implied, from Google.` +
|
|
65
|
+
` Google disclaims all liability for any damages, direct or indirect, resulting from the use of these extensions\n${humanReadableList}`));
|
|
66
|
+
}
|
|
67
|
+
return experimental.length > 0 || eapExtensions.length > 0;
|
|
68
|
+
}
|
|
69
|
+
exports.displayWarningsForDeploy = displayWarningsForDeploy;
|
package/lib/functions/env.js
CHANGED
|
@@ -32,11 +32,11 @@ const RESERVED_KEYS = [
|
|
|
32
32
|
const LINE_RE = new RegExp("^" +
|
|
33
33
|
"\\s*" +
|
|
34
34
|
"(\\w+)" +
|
|
35
|
-
"\\s
|
|
35
|
+
"\\s*=[\\f\\t\\v]*" +
|
|
36
36
|
"(" +
|
|
37
37
|
"\\s*'(?:\\\\'|[^'])*'|" +
|
|
38
38
|
'\\s*"(?:\\\\"|[^"])*"|' +
|
|
39
|
-
"[^#\\r\\n]
|
|
39
|
+
"[^#\\r\\n]*" +
|
|
40
40
|
")?" +
|
|
41
41
|
"\\s*" +
|
|
42
42
|
"(?:#[^\\n]*)?" +
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deletePackage = exports.API_VERSION = void 0;
|
|
4
|
+
const apiv2_1 = require("../apiv2");
|
|
5
|
+
const api_1 = require("../api");
|
|
6
|
+
exports.API_VERSION = "v1beta2";
|
|
7
|
+
const client = new apiv2_1.Client({
|
|
8
|
+
urlPrefix: api_1.artifactRegistryDomain,
|
|
9
|
+
auth: true,
|
|
10
|
+
apiVersion: exports.API_VERSION,
|
|
11
|
+
});
|
|
12
|
+
async function deletePackage(name) {
|
|
13
|
+
const res = await client.delete(name);
|
|
14
|
+
return res.body;
|
|
15
|
+
}
|
|
16
|
+
exports.deletePackage = deletePackage;
|
|
@@ -4,6 +4,7 @@ exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFun
|
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const error_1 = require("../error");
|
|
6
6
|
const logger_1 = require("../logger");
|
|
7
|
+
const previews_1 = require("../previews");
|
|
7
8
|
const api = require("../api");
|
|
8
9
|
const backend = require("../deploy/functions/backend");
|
|
9
10
|
const utils = require("../utils");
|
|
@@ -50,7 +51,12 @@ async function createFunction(cloudFunction) {
|
|
|
50
51
|
const apiPath = cloudFunction.name.substring(0, cloudFunction.name.lastIndexOf("/"));
|
|
51
52
|
const endpoint = `/${exports.API_VERSION}/${apiPath}`;
|
|
52
53
|
try {
|
|
54
|
+
const headers = {};
|
|
55
|
+
if (previews_1.previews.artifactregistry) {
|
|
56
|
+
headers["X-Firebase-Artifact-Registry"] = "optin";
|
|
57
|
+
}
|
|
53
58
|
const res = await api.request("POST", endpoint, {
|
|
59
|
+
headers,
|
|
54
60
|
auth: true,
|
|
55
61
|
data: cloudFunction,
|
|
56
62
|
origin: api.functionsOrigin,
|
|
@@ -145,7 +151,12 @@ async function updateFunction(cloudFunction) {
|
|
|
145
151
|
const endpoint = `/${exports.API_VERSION}/${cloudFunction.name}`;
|
|
146
152
|
const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "environmentVariables");
|
|
147
153
|
try {
|
|
154
|
+
const headers = {};
|
|
155
|
+
if (previews_1.previews.artifactregistry) {
|
|
156
|
+
headers["X-Firebase-Artifact-Registry"] = "optin";
|
|
157
|
+
}
|
|
148
158
|
const res = await api.request("PATCH", endpoint, {
|
|
159
|
+
headers,
|
|
149
160
|
qs: {
|
|
150
161
|
updateMask: fieldMasks.join(","),
|
|
151
162
|
},
|
|
@@ -215,19 +226,24 @@ async function listAllFunctions(projectId) {
|
|
|
215
226
|
}
|
|
216
227
|
exports.listAllFunctions = listAllFunctions;
|
|
217
228
|
function endpointFromFunction(gcfFunction) {
|
|
218
|
-
var _a, _b;
|
|
229
|
+
var _a, _b, _c;
|
|
219
230
|
const [, project, , region, , id] = gcfFunction.name.split("/");
|
|
220
231
|
let trigger;
|
|
221
232
|
let uri;
|
|
222
|
-
if (gcfFunction.
|
|
223
|
-
trigger = { httpsTrigger: {} };
|
|
224
|
-
uri = gcfFunction.httpsTrigger.url;
|
|
225
|
-
}
|
|
226
|
-
else if ((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) {
|
|
233
|
+
if ((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) {
|
|
227
234
|
trigger = {
|
|
228
235
|
scheduleTrigger: {},
|
|
229
236
|
};
|
|
230
237
|
}
|
|
238
|
+
else if ((_b = gcfFunction.labels) === null || _b === void 0 ? void 0 : _b["deployment-taskqueue"]) {
|
|
239
|
+
trigger = {
|
|
240
|
+
taskQueueTrigger: {},
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
else if (gcfFunction.httpsTrigger) {
|
|
244
|
+
trigger = { httpsTrigger: {} };
|
|
245
|
+
uri = gcfFunction.httpsTrigger.url;
|
|
246
|
+
}
|
|
231
247
|
else {
|
|
232
248
|
trigger = {
|
|
233
249
|
eventTrigger: {
|
|
@@ -235,7 +251,7 @@ function endpointFromFunction(gcfFunction) {
|
|
|
235
251
|
eventFilters: {
|
|
236
252
|
resource: gcfFunction.eventTrigger.resource,
|
|
237
253
|
},
|
|
238
|
-
retry: !!((
|
|
254
|
+
retry: !!((_c = gcfFunction.eventTrigger.failurePolicy) === null || _c === void 0 ? void 0 : _c.retry),
|
|
239
255
|
},
|
|
240
256
|
};
|
|
241
257
|
}
|
|
@@ -284,6 +300,10 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
|
|
|
284
300
|
};
|
|
285
301
|
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-scheduled": "true" });
|
|
286
302
|
}
|
|
303
|
+
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
304
|
+
gcfFunction.httpsTrigger = {};
|
|
305
|
+
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
|
|
306
|
+
}
|
|
287
307
|
else {
|
|
288
308
|
gcfFunction.httpsTrigger = {};
|
|
289
309
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.endpointFromFunction = exports.functionFromEndpoint = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.PUBSUB_PUBLISH_EVENT = exports.API_VERSION = void 0;
|
|
3
|
+
exports.endpointFromFunction = exports.functionFromEndpoint = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.megabytes = exports.PUBSUB_PUBLISH_EVENT = exports.API_VERSION = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const apiv2_1 = require("../apiv2");
|
|
6
6
|
const error_1 = require("../error");
|
|
@@ -17,6 +17,35 @@ const client = new apiv2_1.Client({
|
|
|
17
17
|
apiVersion: exports.API_VERSION,
|
|
18
18
|
});
|
|
19
19
|
exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
|
|
20
|
+
const BYTES_PER_UNIT = {
|
|
21
|
+
"": 1,
|
|
22
|
+
k: 1e3,
|
|
23
|
+
M: 1e6,
|
|
24
|
+
G: 1e9,
|
|
25
|
+
T: 1e12,
|
|
26
|
+
Ki: 1 << 10,
|
|
27
|
+
Mi: 1 << 20,
|
|
28
|
+
Gi: 1 << 30,
|
|
29
|
+
Ti: 1 << 40,
|
|
30
|
+
};
|
|
31
|
+
function megabytes(memory) {
|
|
32
|
+
const re = /^([0-9]+(\.[0-9]*)?)(Ki|Mi|Gi|Ti|k|M|G|T|([eE]([0-9]+)))?$/;
|
|
33
|
+
const matches = re.exec(memory);
|
|
34
|
+
if (!matches) {
|
|
35
|
+
throw new Error(`Invalid memory quantity "${memory}""`);
|
|
36
|
+
}
|
|
37
|
+
const quantity = Number.parseFloat(matches[1]);
|
|
38
|
+
let bytes;
|
|
39
|
+
if (matches[5]) {
|
|
40
|
+
bytes = quantity * Math.pow(10, Number.parseFloat(matches[5]));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const suffix = matches[3] || "";
|
|
44
|
+
bytes = quantity * BYTES_PER_UNIT[suffix];
|
|
45
|
+
}
|
|
46
|
+
return bytes / 1e6;
|
|
47
|
+
}
|
|
48
|
+
exports.megabytes = megabytes;
|
|
20
49
|
function functionsOpLogReject(funcName, type, err) {
|
|
21
50
|
var _a, _b;
|
|
22
51
|
if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 429) {
|
|
@@ -136,7 +165,8 @@ function functionFromEndpoint(endpoint, source) {
|
|
|
136
165
|
serviceConfig: {},
|
|
137
166
|
};
|
|
138
167
|
proto.copyIfPresent(gcfFunction, endpoint, "labels");
|
|
139
|
-
proto.copyIfPresent(gcfFunction.serviceConfig, endpoint, "
|
|
168
|
+
proto.copyIfPresent(gcfFunction.serviceConfig, endpoint, "environmentVariables", "vpcConnector", "vpcConnectorEgressSettings", "serviceAccountEmail", "ingressSettings");
|
|
169
|
+
proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "availableMemory", "availableMemoryMb", (mb) => `${mb}M`);
|
|
140
170
|
proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
141
171
|
proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "minInstanceCount", "minInstances");
|
|
142
172
|
proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "maxInstanceCount", "maxInstances");
|
|
@@ -153,18 +183,22 @@ function functionFromEndpoint(endpoint, source) {
|
|
|
153
183
|
gcfFunction.eventTrigger.eventFilters.push({ attribute, value });
|
|
154
184
|
}
|
|
155
185
|
}
|
|
186
|
+
proto.renameIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "triggerRegion", "region");
|
|
156
187
|
if (endpoint.eventTrigger.retry) {
|
|
157
188
|
logger_1.logger.warn("Cannot set a retry policy on Cloud Function", endpoint.id);
|
|
158
189
|
}
|
|
159
190
|
}
|
|
160
191
|
else if (backend.isScheduleTriggered(endpoint)) {
|
|
161
|
-
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), {
|
|
192
|
+
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-scheduled": "true" });
|
|
193
|
+
}
|
|
194
|
+
else if (backend.isTaskQueueTriggered(endpoint)) {
|
|
195
|
+
gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
|
|
162
196
|
}
|
|
163
197
|
return gcfFunction;
|
|
164
198
|
}
|
|
165
199
|
exports.functionFromEndpoint = functionFromEndpoint;
|
|
166
200
|
function endpointFromFunction(gcfFunction) {
|
|
167
|
-
var _a;
|
|
201
|
+
var _a, _b;
|
|
168
202
|
const [, project, , region, , id] = gcfFunction.name.split("/");
|
|
169
203
|
let trigger;
|
|
170
204
|
if (((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) === "true") {
|
|
@@ -172,6 +206,11 @@ function endpointFromFunction(gcfFunction) {
|
|
|
172
206
|
scheduleTrigger: {},
|
|
173
207
|
};
|
|
174
208
|
}
|
|
209
|
+
else if (((_b = gcfFunction.labels) === null || _b === void 0 ? void 0 : _b["deployment-taskqueue"]) === "true") {
|
|
210
|
+
trigger = {
|
|
211
|
+
taskQueueTrigger: {},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
175
214
|
else if (gcfFunction.eventTrigger) {
|
|
176
215
|
trigger = {
|
|
177
216
|
eventTrigger: {
|
|
@@ -199,7 +238,8 @@ function endpointFromFunction(gcfFunction) {
|
|
|
199
238
|
const endpoint = Object.assign(Object.assign({ platform: "gcfv2", id,
|
|
200
239
|
project,
|
|
201
240
|
region }, trigger), { entryPoint: gcfFunction.buildConfig.entryPoint, runtime: gcfFunction.buildConfig.runtime, uri: gcfFunction.serviceConfig.uri });
|
|
202
|
-
proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccountEmail", "
|
|
241
|
+
proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccountEmail", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "environmentVariables");
|
|
242
|
+
proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "availableMemoryMb", "availableMemory", megabytes);
|
|
203
243
|
proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "timeout", "timeoutSeconds", proto.durationFromSeconds);
|
|
204
244
|
proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "minInstances", "minInstanceCount");
|
|
205
245
|
proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "maxInstances", "maxInstanceCount");
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.queueFromEndpoint = exports.queueNameForEndpoint = exports.setEnqueuer = exports.getIamPolicy = exports.setIamPolicy = exports.deleteQueue = exports.purgeQueue = exports.upsertQueue = exports.updateQueue = exports.getQueue = exports.createQueue = exports.DEFAULT_SETTINGS = void 0;
|
|
4
|
+
const proto = require("./proto");
|
|
5
|
+
const apiv2_1 = require("../apiv2");
|
|
6
|
+
const api_1 = require("../api");
|
|
7
|
+
const API_VERSION = "v2";
|
|
8
|
+
const client = new apiv2_1.Client({
|
|
9
|
+
urlPrefix: api_1.cloudTasksOrigin,
|
|
10
|
+
auth: true,
|
|
11
|
+
apiVersion: API_VERSION,
|
|
12
|
+
});
|
|
13
|
+
exports.DEFAULT_SETTINGS = {
|
|
14
|
+
rateLimits: {
|
|
15
|
+
maxConcurrentDispatches: 1000,
|
|
16
|
+
maxBurstSize: 100,
|
|
17
|
+
maxDispatchesPerSecond: 500,
|
|
18
|
+
},
|
|
19
|
+
state: "RUNNING",
|
|
20
|
+
retryConfig: {
|
|
21
|
+
maxDoublings: 16,
|
|
22
|
+
maxAttempts: 3,
|
|
23
|
+
maxBackoff: "3600s",
|
|
24
|
+
minBackoff: "0.100s",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
async function createQueue(queue) {
|
|
28
|
+
const path = queue.name.substring(0, queue.name.lastIndexOf("/"));
|
|
29
|
+
const res = await client.post(path, queue);
|
|
30
|
+
return res.body;
|
|
31
|
+
}
|
|
32
|
+
exports.createQueue = createQueue;
|
|
33
|
+
async function getQueue(name) {
|
|
34
|
+
const res = await client.get(name);
|
|
35
|
+
return res.body;
|
|
36
|
+
}
|
|
37
|
+
exports.getQueue = getQueue;
|
|
38
|
+
async function updateQueue(queue) {
|
|
39
|
+
const res = await client.patch(queue.name, queue, {
|
|
40
|
+
queryParams: { updateMask: proto.fieldMasks(queue).join(",") },
|
|
41
|
+
});
|
|
42
|
+
return res.body;
|
|
43
|
+
}
|
|
44
|
+
exports.updateQueue = updateQueue;
|
|
45
|
+
async function upsertQueue(queue) {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
try {
|
|
48
|
+
const existing = await module.exports.getQueue(queue.name);
|
|
49
|
+
if (JSON.stringify(queue) === JSON.stringify(existing)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (existing.state === "DISABLED") {
|
|
53
|
+
await module.exports.purgeQueue(queue.name);
|
|
54
|
+
}
|
|
55
|
+
await module.exports.updateQueue(queue);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 404) {
|
|
60
|
+
await module.exports.createQueue(queue);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.upsertQueue = upsertQueue;
|
|
67
|
+
async function purgeQueue(name) {
|
|
68
|
+
await client.post(`${name}:purge`);
|
|
69
|
+
}
|
|
70
|
+
exports.purgeQueue = purgeQueue;
|
|
71
|
+
async function deleteQueue(name) {
|
|
72
|
+
await client.delete(name);
|
|
73
|
+
}
|
|
74
|
+
exports.deleteQueue = deleteQueue;
|
|
75
|
+
async function setIamPolicy(name, policy) {
|
|
76
|
+
const res = await client.post(`${name}:setIamPolicy`, {
|
|
77
|
+
policy,
|
|
78
|
+
});
|
|
79
|
+
return res.body;
|
|
80
|
+
}
|
|
81
|
+
exports.setIamPolicy = setIamPolicy;
|
|
82
|
+
async function getIamPolicy(name) {
|
|
83
|
+
const res = await client.post(`${name}:getIamPolicy`);
|
|
84
|
+
return res.body;
|
|
85
|
+
}
|
|
86
|
+
exports.getIamPolicy = getIamPolicy;
|
|
87
|
+
const ENQUEUER_ROLE = "roles/cloudtasks.enqueuer";
|
|
88
|
+
async function setEnqueuer(name, invoker, assumeEmpty = false) {
|
|
89
|
+
var _a, _b;
|
|
90
|
+
let existing;
|
|
91
|
+
if (assumeEmpty) {
|
|
92
|
+
existing = {
|
|
93
|
+
bindings: [],
|
|
94
|
+
etag: "",
|
|
95
|
+
version: 3,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
existing = await module.exports.getIamPolicy(name);
|
|
100
|
+
}
|
|
101
|
+
const [, project] = name.split("/");
|
|
102
|
+
const invokerMembers = proto.getInvokerMembers(invoker, project);
|
|
103
|
+
while (true) {
|
|
104
|
+
const policy = {
|
|
105
|
+
bindings: existing.bindings.filter((binding) => binding.role != ENQUEUER_ROLE),
|
|
106
|
+
etag: existing.etag,
|
|
107
|
+
version: existing.version,
|
|
108
|
+
};
|
|
109
|
+
if (invokerMembers.length) {
|
|
110
|
+
policy.bindings.push({ role: ENQUEUER_ROLE, members: invokerMembers });
|
|
111
|
+
}
|
|
112
|
+
if (JSON.stringify(policy) === JSON.stringify(existing)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
await module.exports.setIamPolicy(name, policy);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 429) {
|
|
121
|
+
existing = await module.exports.getIamPolicy(name);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
throw err;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.setEnqueuer = setEnqueuer;
|
|
129
|
+
function queueNameForEndpoint(endpoint) {
|
|
130
|
+
return `projects/${endpoint.project}/locations/${endpoint.region}/queues/${endpoint.id}`;
|
|
131
|
+
}
|
|
132
|
+
exports.queueNameForEndpoint = queueNameForEndpoint;
|
|
133
|
+
function queueFromEndpoint(endpoint) {
|
|
134
|
+
const queue = Object.assign(Object.assign({}, JSON.parse(JSON.stringify(exports.DEFAULT_SETTINGS))), { name: queueNameForEndpoint(endpoint) });
|
|
135
|
+
if (endpoint.taskQueueTrigger.rateLimits) {
|
|
136
|
+
proto.copyIfPresent(queue.rateLimits, endpoint.taskQueueTrigger.rateLimits, "maxBurstSize", "maxConcurrentDispatches", "maxDispatchesPerSecond");
|
|
137
|
+
}
|
|
138
|
+
if (endpoint.taskQueueTrigger.retryConfig) {
|
|
139
|
+
proto.copyIfPresent(queue.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxBackoff", "maxDoublings", "maxRetryDuration", "minBackoff");
|
|
140
|
+
}
|
|
141
|
+
return queue;
|
|
142
|
+
}
|
|
143
|
+
exports.queueFromEndpoint = queueFromEndpoint;
|
package/lib/gcp/docker.js
CHANGED
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Client = void 0;
|
|
3
|
+
exports.Client = exports.GCR_SUBDOMAIN_MAPPING = void 0;
|
|
4
4
|
const error_1 = require("../error");
|
|
5
5
|
const api = require("../apiv2");
|
|
6
|
+
exports.GCR_SUBDOMAIN_MAPPING = {
|
|
7
|
+
"us-west1": "us",
|
|
8
|
+
"us-west2": "us",
|
|
9
|
+
"us-west3": "us",
|
|
10
|
+
"us-west4": "us",
|
|
11
|
+
"us-central1": "us",
|
|
12
|
+
"us-central2": "us",
|
|
13
|
+
"us-east1": "us",
|
|
14
|
+
"us-east4": "us",
|
|
15
|
+
"northamerica-northeast1": "us",
|
|
16
|
+
"southamerica-east1": "us",
|
|
17
|
+
"europe-west1": "eu",
|
|
18
|
+
"europe-west2": "eu",
|
|
19
|
+
"europe-west3": "eu",
|
|
20
|
+
"europe-west4": "eu",
|
|
21
|
+
"europe-west5": "eu",
|
|
22
|
+
"europe-west6": "eu",
|
|
23
|
+
"europe-central2": "eu",
|
|
24
|
+
"europe-north1": "eu",
|
|
25
|
+
"asia-east1": "asia",
|
|
26
|
+
"asia-east2": "asia",
|
|
27
|
+
"asia-northeast1": "asia",
|
|
28
|
+
"asia-northeast2": "asia",
|
|
29
|
+
"asia-northeast3": "asia",
|
|
30
|
+
"asia-south1": "asia",
|
|
31
|
+
"asia-southeast2": "asia",
|
|
32
|
+
"australia-southeast1": "asia",
|
|
33
|
+
};
|
|
6
34
|
function isErrors(response) {
|
|
7
|
-
return Object.prototype.hasOwnProperty.call(response, "errors");
|
|
35
|
+
return !!response && Object.prototype.hasOwnProperty.call(response, "errors");
|
|
8
36
|
}
|
|
9
37
|
const API_VERSION = "v2";
|
|
10
38
|
class Client {
|
|
@@ -27,6 +55,9 @@ class Client {
|
|
|
27
55
|
async deleteTag(path, tag) {
|
|
28
56
|
var _a;
|
|
29
57
|
const response = await this.client.delete(`${path}/manifests/${tag}`);
|
|
58
|
+
if (!response.body) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
30
61
|
if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) != 0) {
|
|
31
62
|
throw new error_1.FirebaseError(`Failed to delete tag ${tag} at path ${path}`, {
|
|
32
63
|
children: response.body.errors,
|
|
@@ -36,6 +67,9 @@ class Client {
|
|
|
36
67
|
async deleteImage(path, digest) {
|
|
37
68
|
var _a;
|
|
38
69
|
const response = await this.client.delete(`${path}/manifests/${digest}`);
|
|
70
|
+
if (!response.body) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
39
73
|
if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) != 0) {
|
|
40
74
|
throw new error_1.FirebaseError(`Failed to delete image ${digest} at path ${path}`, {
|
|
41
75
|
children: response.body.errors,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.regionInLocation = exports.DUAL_REGION_MAPPING = exports.MULTI_REGION_MAPPING = void 0;
|
|
4
|
+
exports.MULTI_REGION_MAPPING = {
|
|
5
|
+
"us-central1": "us",
|
|
6
|
+
"us-east1": "us",
|
|
7
|
+
"us-east4": "us",
|
|
8
|
+
"us-west1": "us",
|
|
9
|
+
"us-west2": "us",
|
|
10
|
+
"us-west3": "us",
|
|
11
|
+
"us-west4": "us",
|
|
12
|
+
"europe-central2": "eu",
|
|
13
|
+
"europe-north1": "eu",
|
|
14
|
+
"europe-west1": "eu",
|
|
15
|
+
"europe-west3": "eu",
|
|
16
|
+
"europe-west4": "eu",
|
|
17
|
+
"europe-west5": "eu",
|
|
18
|
+
"asia-east1": "asia",
|
|
19
|
+
"asia-east2": "asia",
|
|
20
|
+
"asia-northeast1": "asia",
|
|
21
|
+
"asia-northeast2": "asia",
|
|
22
|
+
"asia-northeast3": "asia",
|
|
23
|
+
"asia-south1": "asia",
|
|
24
|
+
"asia-south2": "asia",
|
|
25
|
+
"asia-southeast1": "asia",
|
|
26
|
+
"asia-southeast2": "asia",
|
|
27
|
+
};
|
|
28
|
+
exports.DUAL_REGION_MAPPING = {
|
|
29
|
+
"asia-northeast1": "asia1",
|
|
30
|
+
"asia-northeast2": "asia1",
|
|
31
|
+
"europe-north1": "eur4",
|
|
32
|
+
"europe-west4": "eur4",
|
|
33
|
+
"us-central1": "nam4",
|
|
34
|
+
"us-east1": "nam4",
|
|
35
|
+
};
|
|
36
|
+
function regionInLocation(region, location) {
|
|
37
|
+
region = region.toLowerCase();
|
|
38
|
+
location = location.toLowerCase();
|
|
39
|
+
if (exports.MULTI_REGION_MAPPING[region] === location || exports.DUAL_REGION_MAPPING[region] === location) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
exports.regionInLocation = regionInLocation;
|
package/lib/gcp/proto.js
CHANGED
|
@@ -67,10 +67,10 @@ function fieldMasksHelper(prefixes, cursor, doNotRecurseIn, masks) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
function getInvokerMembers(invoker, projectId) {
|
|
70
|
-
if (invoker
|
|
70
|
+
if (invoker.includes("private")) {
|
|
71
71
|
return [];
|
|
72
72
|
}
|
|
73
|
-
if (invoker
|
|
73
|
+
if (invoker.includes("public")) {
|
|
74
74
|
return ["allUsers"];
|
|
75
75
|
}
|
|
76
76
|
return invoker.map((inv) => formatServiceAccount(inv, projectId));
|