123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.API_VERSION = void 0;
- const clc = require("colorette");
- const error_1 = require("../error");
- const logger_1 = require("../logger");
- const backend = require("../deploy/functions/backend");
- const utils = require("../utils");
- const proto = require("./proto");
- const runtimes = require("../deploy/functions/runtimes");
- const projectConfig = require("../functions/projectConfig");
- const apiv2_1 = require("../apiv2");
- const api_1 = require("../api");
- const constants_1 = require("../functions/constants");
- exports.API_VERSION = "v1";
- const client = new apiv2_1.Client({ urlPrefix: api_1.functionsOrigin, apiVersion: exports.API_VERSION });
- function functionsOpLogReject(funcName, type, err) {
- var _a, _b;
- 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) {
- utils.logWarning(`${clc.bold(clc.yellow("functions:"))} got "Quota Exceeded" error while trying to ${type} ${funcName}. Waiting to retry...`);
- }
- else {
- utils.logWarning(clc.bold(clc.yellow("functions:")) + " failed to " + type + " function " + funcName);
- }
- throw new error_1.FirebaseError(`Failed to ${type} function ${funcName}`, {
- original: err,
- context: { function: funcName },
- });
- }
- async function generateUploadUrl(projectId, location) {
- const parent = "projects/" + projectId + "/locations/" + location;
- const endpoint = `/${parent}/functions:generateUploadUrl`;
- try {
- const res = await client.post(endpoint, {}, { retryCodes: [503] });
- return res.body.uploadUrl;
- }
- catch (err) {
- logger_1.logger.info("\n\nThere was an issue deploying your functions. Verify that your project has a Google App Engine instance setup at https://console.cloud.google.com/appengine and try again. If this issue persists, please contact support.");
- throw err;
- }
- }
- exports.generateUploadUrl = generateUploadUrl;
- async function createFunction(cloudFunction) {
- const apiPath = cloudFunction.name.substring(0, cloudFunction.name.lastIndexOf("/"));
- const endpoint = `/${apiPath}`;
- try {
- const res = await client.post(endpoint, cloudFunction);
- return {
- name: res.body.name,
- type: "create",
- done: false,
- };
- }
- catch (err) {
- throw functionsOpLogReject(cloudFunction.name, "create", err);
- }
- }
- exports.createFunction = createFunction;
- async function setIamPolicy(options) {
- const endpoint = `/${options.name}:setIamPolicy`;
- try {
- await client.post(endpoint, {
- policy: options.policy,
- updateMask: Object.keys(options.policy).join(","),
- });
- }
- catch (err) {
- throw new error_1.FirebaseError(`Failed to set the IAM Policy on the function ${options.name}`, {
- original: err,
- });
- }
- }
- exports.setIamPolicy = setIamPolicy;
- async function getIamPolicy(fnName) {
- const endpoint = `/${fnName}:getIamPolicy`;
- try {
- const res = await client.get(endpoint);
- return res.body;
- }
- catch (err) {
- throw new error_1.FirebaseError(`Failed to get the IAM Policy on the function ${fnName}`, {
- original: err,
- });
- }
- }
- exports.getIamPolicy = getIamPolicy;
- async function setInvokerCreate(projectId, fnName, invoker) {
- if (invoker.length === 0) {
- throw new error_1.FirebaseError("Invoker cannot be an empty array");
- }
- const invokerMembers = proto.getInvokerMembers(invoker, projectId);
- const invokerRole = "roles/cloudfunctions.invoker";
- const bindings = [{ role: invokerRole, members: invokerMembers }];
- const policy = {
- bindings: bindings,
- etag: "",
- version: 3,
- };
- await setIamPolicy({ name: fnName, policy: policy });
- }
- exports.setInvokerCreate = setInvokerCreate;
- async function setInvokerUpdate(projectId, fnName, invoker) {
- var _a;
- if (invoker.length === 0) {
- throw new error_1.FirebaseError("Invoker cannot be an empty array");
- }
- const invokerMembers = proto.getInvokerMembers(invoker, projectId);
- const invokerRole = "roles/cloudfunctions.invoker";
- const currentPolicy = await getIamPolicy(fnName);
- const currentInvokerBinding = (_a = currentPolicy.bindings) === null || _a === void 0 ? void 0 : _a.find((binding) => binding.role === invokerRole);
- if (currentInvokerBinding &&
- JSON.stringify(currentInvokerBinding.members.sort()) === JSON.stringify(invokerMembers.sort())) {
- return;
- }
- const bindings = (currentPolicy.bindings || []).filter((binding) => binding.role !== invokerRole);
- bindings.push({
- role: invokerRole,
- members: invokerMembers,
- });
- const policy = {
- bindings: bindings,
- etag: currentPolicy.etag || "",
- version: 3,
- };
- await setIamPolicy({ name: fnName, policy: policy });
- }
- exports.setInvokerUpdate = setInvokerUpdate;
- async function updateFunction(cloudFunction) {
- const endpoint = `/${cloudFunction.name}`;
- const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "environmentVariables", "secretEnvironmentVariables");
- try {
- const res = await client.patch(endpoint, cloudFunction, {
- queryParams: {
- updateMask: fieldMasks.join(","),
- },
- });
- return {
- done: false,
- name: res.body.name,
- type: "update",
- };
- }
- catch (err) {
- throw functionsOpLogReject(cloudFunction.name, "update", err);
- }
- }
- exports.updateFunction = updateFunction;
- async function deleteFunction(name) {
- const endpoint = `/${name}`;
- try {
- const res = await client.delete(endpoint);
- return {
- done: false,
- name: res.body.name,
- type: "delete",
- };
- }
- catch (err) {
- throw functionsOpLogReject(name, "delete", err);
- }
- }
- exports.deleteFunction = deleteFunction;
- async function list(projectId, region) {
- const endpoint = "/projects/" + projectId + "/locations/" + region + "/functions";
- try {
- const res = await client.get(endpoint);
- if (res.body.unreachable && res.body.unreachable.length > 0) {
- logger_1.logger.debug(`[functions] unable to reach the following regions: ${res.body.unreachable.join(", ")}`);
- }
- return {
- functions: res.body.functions || [],
- unreachable: res.body.unreachable || [],
- };
- }
- catch (err) {
- logger_1.logger.debug(`[functions] failed to list functions for ${projectId}`);
- logger_1.logger.debug(`[functions] ${err === null || err === void 0 ? void 0 : err.message}`);
- throw new error_1.FirebaseError(`Failed to list functions for ${projectId}`, {
- original: err,
- status: err instanceof error_1.FirebaseError ? err.status : undefined,
- });
- }
- }
- async function listFunctions(projectId, region) {
- const res = await list(projectId, region);
- return res.functions;
- }
- exports.listFunctions = listFunctions;
- async function listAllFunctions(projectId) {
- return list(projectId, "-");
- }
- exports.listAllFunctions = listAllFunctions;
- function endpointFromFunction(gcfFunction) {
- var _a, _b, _c, _d, _e, _f, _g, _h;
- const [, project, , region, , id] = gcfFunction.name.split("/");
- let trigger;
- let uri;
- let securityLevel;
- if ((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) {
- trigger = {
- scheduleTrigger: {},
- };
- }
- else if ((_b = gcfFunction.labels) === null || _b === void 0 ? void 0 : _b["deployment-taskqueue"]) {
- trigger = {
- taskQueueTrigger: {},
- };
- }
- else if (((_c = gcfFunction.labels) === null || _c === void 0 ? void 0 : _c["deployment-callable"]) ||
- ((_d = gcfFunction.labels) === null || _d === void 0 ? void 0 : _d["deployment-callabled"])) {
- trigger = {
- callableTrigger: {},
- };
- }
- else if ((_e = gcfFunction.labels) === null || _e === void 0 ? void 0 : _e[constants_1.BLOCKING_LABEL]) {
- trigger = {
- blockingTrigger: {
- eventType: constants_1.BLOCKING_LABEL_KEY_TO_EVENT[gcfFunction.labels[constants_1.BLOCKING_LABEL]],
- },
- };
- }
- else if (gcfFunction.httpsTrigger) {
- trigger = { httpsTrigger: {} };
- }
- else {
- trigger = {
- eventTrigger: {
- eventType: gcfFunction.eventTrigger.eventType,
- eventFilters: { resource: gcfFunction.eventTrigger.resource },
- retry: !!((_f = gcfFunction.eventTrigger.failurePolicy) === null || _f === void 0 ? void 0 : _f.retry),
- },
- };
- }
- if (gcfFunction.httpsTrigger) {
- uri = gcfFunction.httpsTrigger.url;
- securityLevel = gcfFunction.httpsTrigger.securityLevel;
- }
- if (!runtimes.isValidRuntime(gcfFunction.runtime)) {
- logger_1.logger.debug("GCFv1 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
- }
- const endpoint = Object.assign(Object.assign({ platform: "gcfv1", id,
- project,
- region }, trigger), { entryPoint: gcfFunction.entryPoint, runtime: gcfFunction.runtime });
- if (uri) {
- endpoint.uri = uri;
- }
- if (securityLevel) {
- endpoint.securityLevel = securityLevel;
- }
- proto.copyIfPresent(endpoint, gcfFunction, "minInstances", "maxInstances", "ingressSettings", "labels", "environmentVariables", "secretEnvironmentVariables", "sourceUploadUrl");
- proto.renameIfPresent(endpoint, gcfFunction, "serviceAccount", "serviceAccountEmail");
- proto.convertIfPresent(endpoint, gcfFunction, "availableMemoryMb", (raw) => raw);
- proto.convertIfPresent(endpoint, gcfFunction, "timeoutSeconds", "timeout", (dur) => dur === null ? null : proto.secondsFromDuration(dur));
- if (gcfFunction.vpcConnector) {
- endpoint.vpc = { connector: gcfFunction.vpcConnector };
- proto.convertIfPresent(endpoint.vpc, gcfFunction, "egressSettings", "vpcConnectorEgressSettings", (raw) => raw);
- }
- endpoint.codebase = ((_g = gcfFunction.labels) === null || _g === void 0 ? void 0 : _g[constants_1.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
- if ((_h = gcfFunction.labels) === null || _h === void 0 ? void 0 : _h[constants_1.HASH_LABEL]) {
- endpoint.hash = gcfFunction.labels[constants_1.HASH_LABEL];
- }
- return endpoint;
- }
- exports.endpointFromFunction = endpointFromFunction;
- function functionFromEndpoint(endpoint, sourceUploadUrl) {
- var _a, _b;
- if (endpoint.platform !== "gcfv1") {
- throw new error_1.FirebaseError("Trying to create a v1 CloudFunction with v2 API. This should never happen");
- }
- if (!runtimes.isValidRuntime(endpoint.runtime)) {
- throw new error_1.FirebaseError("Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
- " This should never happen");
- }
- const gcfFunction = {
- name: backend.functionName(endpoint),
- sourceUploadUrl: sourceUploadUrl,
- entryPoint: endpoint.entryPoint,
- runtime: endpoint.runtime,
- dockerRegistry: "ARTIFACT_REGISTRY",
- };
- if (typeof endpoint.labels !== "undefined") {
- gcfFunction.labels = Object.assign({}, endpoint.labels);
- }
- if (backend.isEventTriggered(endpoint)) {
- if (!((_a = endpoint.eventTrigger.eventFilters) === null || _a === void 0 ? void 0 : _a.resource)) {
- throw new error_1.FirebaseError("Cannot create v1 function from an eventTrigger without a resource");
- }
- gcfFunction.eventTrigger = {
- eventType: endpoint.eventTrigger.eventType,
- resource: endpoint.eventTrigger.eventFilters.resource,
- };
- gcfFunction.eventTrigger.failurePolicy = endpoint.eventTrigger.retry
- ? { retry: {} }
- : undefined;
- }
- else if (backend.isScheduleTriggered(endpoint)) {
- const id = backend.scheduleIdForFunction(endpoint);
- gcfFunction.eventTrigger = {
- eventType: "google.pubsub.topic.publish",
- resource: `projects/${endpoint.project}/topics/${id}`,
- };
- gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-scheduled": "true" });
- }
- else if (backend.isTaskQueueTriggered(endpoint)) {
- gcfFunction.httpsTrigger = {};
- gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
- }
- else if (backend.isBlockingTriggered(endpoint)) {
- gcfFunction.httpsTrigger = {};
- gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [constants_1.BLOCKING_LABEL]: constants_1.BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
- }
- else {
- gcfFunction.httpsTrigger = {};
- if (backend.isCallableTriggered(endpoint)) {
- gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-callable": "true" });
- }
- if (endpoint.securityLevel) {
- gcfFunction.httpsTrigger.securityLevel = endpoint.securityLevel;
- }
- }
- proto.copyIfPresent(gcfFunction, endpoint, "minInstances", "maxInstances", "ingressSettings", "environmentVariables", "secretEnvironmentVariables");
- proto.renameIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "serviceAccount");
- proto.convertIfPresent(gcfFunction, endpoint, "availableMemoryMb", (mem) => mem);
- proto.convertIfPresent(gcfFunction, endpoint, "timeout", "timeoutSeconds", (sec) => sec ? proto.durationFromSeconds(sec) : null);
- if (endpoint.vpc) {
- proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnector", "connector");
- proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnectorEgressSettings", "egressSettings");
- }
- else if (endpoint.vpc === null) {
- gcfFunction.vpcConnector = null;
- gcfFunction.vpcConnectorEgressSettings = null;
- }
- const codebase = endpoint.codebase || projectConfig.DEFAULT_CODEBASE;
- if (codebase !== projectConfig.DEFAULT_CODEBASE) {
- gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [constants_1.CODEBASE_LABEL]: codebase });
- }
- else {
- (_b = gcfFunction.labels) === null || _b === void 0 ? true : delete _b[constants_1.CODEBASE_LABEL];
- }
- if (endpoint.hash) {
- gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [constants_1.HASH_LABEL]: endpoint.hash });
- }
- return gcfFunction;
- }
- exports.functionFromEndpoint = functionFromEndpoint;
|