"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.merge = exports.of = exports.empty = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.AllFunctionsPlatforms = exports.secretVersionName = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_CPU_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.DEFAULT_CONCURRENCY = exports.memoryToGen2Cpu = exports.memoryToGen1Cpu = exports.memoryOptionDisplayName = exports.isValidMemoryOption = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.endpointTriggerType = void 0; const gcf = require("../../gcp/cloudfunctions"); const gcfV2 = require("../../gcp/cloudfunctionsv2"); const run = require("../../gcp/run"); const utils = require("../../utils"); const error_1 = require("../../error"); const functional_1 = require("../../functional"); function endpointTriggerType(endpoint) { if (isScheduleTriggered(endpoint)) { return "scheduled"; } else if (isHttpsTriggered(endpoint)) { return "https"; } else if (isCallableTriggered(endpoint)) { return "callable"; } else if (isEventTriggered(endpoint)) { return endpoint.eventTrigger.eventType; } else if (isTaskQueueTriggered(endpoint)) { return "taskQueue"; } else if (isBlockingTriggered(endpoint)) { return endpoint.blockingTrigger.eventType; } else { throw new Error("Unexpected trigger type for endpoint " + JSON.stringify(endpoint)); } } exports.endpointTriggerType = endpointTriggerType; exports.AllVpcEgressSettings = ["PRIVATE_RANGES_ONLY", "ALL_TRAFFIC"]; exports.AllIngressSettings = [ "ALLOW_ALL", "ALLOW_INTERNAL_ONLY", "ALLOW_INTERNAL_AND_GCLB", ]; const allMemoryOptions = [128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768]; function isValidMemoryOption(mem) { return allMemoryOptions.includes(mem); } exports.isValidMemoryOption = isValidMemoryOption; function memoryOptionDisplayName(option) { return { 128: "128MB", 256: "256MB", 512: "512MB", 1024: "1GB", 2048: "2GB", 4096: "4GB", 8192: "8GB", 16384: "16GB", 32768: "32GB", }[option]; } exports.memoryOptionDisplayName = memoryOptionDisplayName; function memoryToGen1Cpu(memory) { return { 128: 0.0833, 256: 0.1666, 512: 0.3333, 1024: 0.5833, 2048: 1, 4096: 2, 8192: 2, 16384: 4, 32768: 8, }[memory]; } exports.memoryToGen1Cpu = memoryToGen1Cpu; function memoryToGen2Cpu(memory) { return { 128: 1, 256: 1, 512: 1, 1024: 1, 2048: 1, 4096: 2, 8192: 2, 16384: 4, 32768: 8, }[memory]; } exports.memoryToGen2Cpu = memoryToGen2Cpu; exports.DEFAULT_CONCURRENCY = 80; exports.DEFAULT_MEMORY = 256; exports.MIN_CPU_FOR_CONCURRENCY = 1; exports.SCHEDULED_FUNCTION_LABEL = Object.freeze({ deployment: "firebase-schedule" }); function secretVersionName(s) { var _a; return `projects/${s.projectId}/secrets/${s.secret}/versions/${(_a = s.version) !== null && _a !== void 0 ? _a : "latest"}`; } exports.secretVersionName = secretVersionName; exports.AllFunctionsPlatforms = ["gcfv1", "gcfv2"]; function isHttpsTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "httpsTrigger"); } exports.isHttpsTriggered = isHttpsTriggered; function isCallableTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "callableTrigger"); } exports.isCallableTriggered = isCallableTriggered; function isEventTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "eventTrigger"); } exports.isEventTriggered = isEventTriggered; function isScheduleTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "scheduleTrigger"); } exports.isScheduleTriggered = isScheduleTriggered; function isTaskQueueTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "taskQueueTrigger"); } exports.isTaskQueueTriggered = isTaskQueueTriggered; function isBlockingTriggered(triggered) { return {}.hasOwnProperty.call(triggered, "blockingTrigger"); } exports.isBlockingTriggered = isBlockingTriggered; function empty() { return { requiredAPIs: [], endpoints: {}, environmentVariables: {}, }; } exports.empty = empty; function of(...endpoints) { const bkend = Object.assign({}, empty()); for (const endpoint of endpoints) { bkend.endpoints[endpoint.region] = bkend.endpoints[endpoint.region] || {}; if (bkend.endpoints[endpoint.region][endpoint.id]) { throw new Error("Trying to create a backend with the same endpiont twice"); } bkend.endpoints[endpoint.region][endpoint.id] = endpoint; } return bkend; } exports.of = of; function merge(...backends) { const merged = of(...(0, functional_1.flattenArray)(backends.map((b) => allEndpoints(b)))); const apiToReasons = {}; for (const b of backends) { for (const { api, reason } of b.requiredAPIs) { const reasons = apiToReasons[api] || new Set(); if (reason) { reasons.add(reason); } apiToReasons[api] = reasons; } merged.environmentVariables = Object.assign(Object.assign({}, merged.environmentVariables), b.environmentVariables); } for (const [api, reasons] of Object.entries(apiToReasons)) { merged.requiredAPIs.push({ api, reason: Array.from(reasons).join(" ") }); } return merged; } exports.merge = merge; function isEmptyBackend(backend) { return (Object.keys(backend.requiredAPIs).length === 0 && Object.keys(backend.endpoints).length === 0); } exports.isEmptyBackend = isEmptyBackend; function functionName(cloudFunction) { return `projects/${cloudFunction.project}/locations/${cloudFunction.region}/functions/${cloudFunction.id}`; } exports.functionName = functionName; function scheduleIdForFunction(cloudFunction) { return `firebase-schedule-${cloudFunction.id}-${cloudFunction.region}`; } exports.scheduleIdForFunction = scheduleIdForFunction; async function existingBackend(context, forceRefresh) { if (!context.loadedExistingBackend || forceRefresh) { await loadExistingBackend(context); } return context.existingBackend; } exports.existingBackend = existingBackend; async function loadExistingBackend(ctx) { var _a; ctx.loadedExistingBackend = true; ctx.existingBackend = Object.assign({}, empty()); ctx.unreachableRegions = { gcfV1: [], gcfV2: [], }; const gcfV1Results = await gcf.listAllFunctions(ctx.projectId); for (const apiFunction of gcfV1Results.functions) { const endpoint = gcf.endpointFromFunction(apiFunction); ctx.existingBackend.endpoints[endpoint.region] = ctx.existingBackend.endpoints[endpoint.region] || {}; ctx.existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint; } ctx.unreachableRegions.gcfV1 = gcfV1Results.unreachable; let gcfV2Results; try { gcfV2Results = await gcfV2.listAllFunctions(ctx.projectId); const runResults = await Promise.all(gcfV2Results.functions.map((fn) => run.getService(fn.serviceConfig.service))); for (const [apiFunction, runService] of (0, functional_1.zip)(gcfV2Results.functions, runResults)) { const endpoint = gcfV2.endpointFromFunction(apiFunction); endpoint.concurrency = runService.spec.template.spec.containerConcurrency || 1; endpoint.cpu = +runService.spec.template.spec.containers[0].resources.limits.cpu; ctx.existingBackend.endpoints[endpoint.region] = ctx.existingBackend.endpoints[endpoint.region] || {}; ctx.existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint; } ctx.unreachableRegions.gcfV2 = gcfV2Results.unreachable; } catch (err) { if (err.status === 404 && ((_a = err.message) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes("method not found"))) { return; } throw err; } } async function checkAvailability(context, want) { var _a, _b, _c, _d; if (!context.loadedExistingBackend) { await loadExistingBackend(context); } const gcfV1Regions = new Set(); const gcfV2Regions = new Set(); for (const ep of allEndpoints(want)) { if (ep.platform === "gcfv1") { gcfV1Regions.add(ep.region); } else { gcfV2Regions.add(ep.region); } } const neededUnreachableV1 = (_a = context.unreachableRegions) === null || _a === void 0 ? void 0 : _a.gcfV1.filter((region) => gcfV1Regions.has(region)); const neededUnreachableV2 = (_b = context.unreachableRegions) === null || _b === void 0 ? void 0 : _b.gcfV2.filter((region) => gcfV2Regions.has(region)); if (neededUnreachableV1 === null || neededUnreachableV1 === void 0 ? void 0 : neededUnreachableV1.length) { throw new error_1.FirebaseError("The following Cloud Functions regions are currently unreachable:\n\t" + neededUnreachableV1.join("\n\t") + "\nThis deployment contains functions in those regions. Please try again in a few minutes, or exclude these regions from your deployment."); } if (neededUnreachableV2 === null || neededUnreachableV2 === void 0 ? void 0 : neededUnreachableV2.length) { throw new error_1.FirebaseError("The following Cloud Functions V2 regions are currently unreachable:\n\t" + neededUnreachableV2.join("\n\t") + "\nThis deployment contains functions in those regions. Please try again in a few minutes, or exclude these regions from your deployment."); } if ((_c = context.unreachableRegions) === null || _c === void 0 ? void 0 : _c.gcfV1.length) { utils.logLabeledWarning("functions", "The following Cloud Functions regions are currently unreachable:\n" + context.unreachableRegions.gcfV1.join("\n") + "\nCloud Functions in these regions won't be deleted."); } if ((_d = context.unreachableRegions) === null || _d === void 0 ? void 0 : _d.gcfV2.length) { utils.logLabeledWarning("functions", "The following Cloud Functions V2 regions are currently unreachable:\n" + context.unreachableRegions.gcfV2.join("\n") + "\nCloud Functions in these regions won't be deleted."); } } exports.checkAvailability = checkAvailability; function allEndpoints(backend) { return Object.values(backend.endpoints).reduce((accum, perRegion) => { return [...accum, ...Object.values(perRegion)]; }, []); } exports.allEndpoints = allEndpoints; function someEndpoint(backend, predicate) { for (const endpoints of Object.values(backend.endpoints)) { if (Object.values(endpoints).some(predicate)) { return true; } } return false; } exports.someEndpoint = someEndpoint; function findEndpoint(backend, predicate) { for (const endpoints of Object.values(backend.endpoints)) { const endpoint = Object.values(endpoints).find(predicate); if (endpoint) return endpoint; } } exports.findEndpoint = findEndpoint; function matchingBackend(backend, predicate) { const filtered = Object.assign(Object.assign({}, backend), { endpoints: {} }); for (const endpoint of allEndpoints(backend)) { if (!predicate(endpoint)) { continue; } filtered.endpoints[endpoint.region] = filtered.endpoints[endpoint.region] || {}; filtered.endpoints[endpoint.region][endpoint.id] = endpoint; } return filtered; } exports.matchingBackend = matchingBackend; function regionalEndpoints(backend, region) { return backend.endpoints[region] ? Object.values(backend.endpoints[region]) : []; } exports.regionalEndpoints = regionalEndpoints; const hasEndpoint = (backend) => (endpoint) => { return (!!backend.endpoints[endpoint.region] && !!backend.endpoints[endpoint.region][endpoint.id]); }; exports.hasEndpoint = hasEndpoint; const missingEndpoint = (backend) => (endpoint) => { return !(0, exports.hasEndpoint)(backend)(endpoint); }; exports.missingEndpoint = missingEndpoint; function compareFunctions(left, right) { if (left.platform !== right.platform) { return right.platform < left.platform ? -1 : 1; } if (left.region < right.region) { return -1; } if (left.region > right.region) { return 1; } if (left.id < right.id) { return -1; } if (left.id > right.id) { return 1; } return 0; } exports.compareFunctions = compareFunctions;