UNPKG

firebase-tools

Version:
245 lines (244 loc) • 9.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parameterizeProject = parameterizeProject; exports.setSecretParamsToLatest = setSecretParamsToLatest; exports.displayExportInfo = displayExportInfo; exports.memoryToMb = memoryToMb; exports.parseMemory = parseMemory; exports.resolveMigratedMemory = resolveMigratedMemory; exports.functionsEnvFromInstance = functionsEnvFromInstance; exports.secretsNeedingEjection = secretsNeedingEjection; exports.ejectSecretsFromInstance = ejectSecretsFromInstance; const planner_1 = require("../deploy/extensions/planner"); const deploymentSummary_1 = require("../deploy/extensions/deploymentSummary"); const logger_1 = require("../logger"); const secretManager_1 = require("../gcp/secretManager"); const secretsUtils_1 = require("./secretsUtils"); const secrets_1 = require("../deploy/extensions/secrets"); const error_1 = require("../error"); const utils_1 = require("../utils"); function parameterizeProject(projectId, projectNumber, spec) { const newParams = {}; for (const [key, val] of Object.entries(spec.params)) { const p1 = val.replace(projectId, "${param:PROJECT_ID}"); const p2 = p1.replace(projectNumber, "${param:PROJECT_NUMBER}"); newParams[key] = p2; } const newSpec = { ...spec }; newSpec.params = newParams; return newSpec; } async function setSecretParamsToLatest(spec) { const newParams = { ...spec.params }; const extensionVersion = await (0, planner_1.getExtensionVersion)(spec); const activeSecrets = (0, secretsUtils_1.getActiveSecrets)(extensionVersion.spec, newParams); for (const [key, val] of Object.entries(newParams)) { if (activeSecrets.includes(val)) { const parsed = (0, secretManager_1.parseSecretVersionResourceName)(val); parsed.versionId = "latest"; newParams[key] = (0, secretManager_1.toSecretVersionResourceName)(parsed); } } return { ...spec, params: newParams }; } function displayExportInfo(withRef, withoutRef) { logger_1.logger.info("The following Extension instances will be saved locally:"); logger_1.logger.info(""); displaySpecs(withRef); if (withoutRef.length) { logger_1.logger.info(`Your project also has the following instances installed from local sources. These will not be saved to firebase.json:`); for (const spec of withoutRef) { logger_1.logger.info(spec.instanceId); } } } function displaySpecs(specs) { for (let i = 0; i < specs.length; i++) { const spec = specs[i]; logger_1.logger.info(`${i + 1}. ${(0, deploymentSummary_1.humanReadable)(spec)}`); logger_1.logger.info(`Configuration will be written to 'extensions/${spec.instanceId}.env'`); for (const p of Object.entries(spec.params)) { logger_1.logger.info(`\t${p[0]}=${p[1]}`); } if (spec.allowedEventTypes?.length) { logger_1.logger.info(`\tALLOWED_EVENTS=${spec.allowedEventTypes}`); } if (spec.eventarcChannel) { logger_1.logger.info(`\tEVENTARC_CHANNEL=${spec.eventarcChannel}`); } logger_1.logger.info(""); } } const GIB_REGEX = /^\d+(?:\.\d+)?(?:Gi|GiB|G|GB)$/i; const MIB_REGEX = /^\d+(?:\.\d+)?(?:Mi|MiB|M|MB)?$/i; function memoryToMb(memory) { if (!memory) { return 0; } const trimmed = memory.trim(); if (GIB_REGEX.test(trimmed)) { return Math.round(parseFloat(trimmed) * 1024); } if (MIB_REGEX.test(trimmed)) { const parsed = parseFloat(trimmed); return isNaN(parsed) || parsed < 0 ? 0 : parsed; } return 0; } const MB_TO_MEMORY_OPTION = { 128: "128MiB", 256: "256MiB", 512: "512MiB", 1024: "1GiB", 2048: "2GiB", 4096: "4GiB", 8192: "8GiB", 16384: "16GiB", 32768: "32GiB", }; function parseMemory(raw) { if (!raw) { return undefined; } const mb = memoryToMb(raw); return MB_TO_MEMORY_OPTION[mb]; } const V1_MEMORY_PARAM = "firebaseextensions.v1beta.function/memory"; const V2_MEMORY_PARAM = "firebaseextensions.v1beta.v2function/memory"; const MEMORY_PARAMS = new Set([V1_MEMORY_PARAM, V2_MEMORY_PARAM]); function resolveMigratedMemory(liveSystemParams = {}, specSystemParams = []) { const getParam = (paramName) => { if (paramName in liveSystemParams) { return liveSystemParams[paramName]; } const defaultVal = specSystemParams.find((p) => p.param === paramName)?.default; return defaultVal !== undefined ? String(defaultVal) : undefined; }; const v1 = getParam(V1_MEMORY_PARAM); const v2 = getParam(V2_MEMORY_PARAM); if (v1 && v2) { return memoryToMb(v1) > memoryToMb(v2) ? v1 : v2; } return v2 ?? v1; } function resolveLiveSecret(specParam, liveParams) { const val = liveParams[specParam.param]; if (!val && !specParam.required) { return undefined; } if (!val) { throw new error_1.FirebaseError(`Secret ${specParam.param} was defined in the extension spec, but is missing in live deployed secrets.`, { exit: 1 }); } return val; } function functionsEnvFromInstance(instance) { const liveParams = instance.config?.params || {}; const liveSystemParams = instance.config?.systemParams || {}; const specParams = instance.config?.source?.spec?.params || []; const specSystemParams = instance.config?.source?.spec?.systemParams || []; const envs = {}; for (const specParam of specParams) { if (specParam.type === "SECRET") { const val = resolveLiveSecret(specParam, liveParams); if (!val) { continue; } const renamed = "FIREBASE_SECRET_REF_" + specParam.param; envs[renamed] = val; } else if (specParam.param in liveParams) { envs[specParam.param] = liveParams[specParam.param]; } else { envs[specParam.param] = specParam.default ?? ""; } } for (const [sysParamName, sysParamValue] of Object.entries(liveSystemParams)) { if (MEMORY_PARAMS.has(sysParamName)) { continue; } let renamed = sysParamName .replace(/^firebaseextensions\.v1beta\.(v2)?function\//, "EXT_MIGRATED_SYSTEM_") .toUpperCase(); if (renamed === "EXT_MIGRATED_SYSTEM_LOCATION") { renamed = "FUNCTION_DEFAULT_REGION"; } envs[renamed] = sysParamValue; } for (const specSystemParam of specSystemParams) { if (specSystemParam.param in liveSystemParams || MEMORY_PARAMS.has(specSystemParam.param)) { continue; } if ("default" in specSystemParam) { let renamed = specSystemParam.param .replace(/^firebaseextensions\.v1beta\.(v2)?function\//, "EXT_MIGRATED_SYSTEM_") .toUpperCase(); if (renamed === "EXT_MIGRATED_SYSTEM_LOCATION") { renamed = "FUNCTION_DEFAULT_REGION"; } envs[renamed] = String(specSystemParam.default ?? ""); } } const memory = resolveMigratedMemory(liveSystemParams, specSystemParams); if (memory) { envs["EXT_MIGRATED_SYSTEM_MEMORY"] = parseMemory(memory) ?? memory; } if (typeof instance.config.allowedEventTypes !== "undefined") { envs["EXT_SELECTED_EVENTS"] = instance.config.allowedEventTypes.toString(); } if (typeof instance.config.eventarcChannel !== "undefined") { envs["EVENTARC_CHANNEL"] = instance.config.eventarcChannel; } return envs; } async function secretsNeedingEjection(instance) { const liveParams = instance.config?.params || {}; const secretParams = (instance.config?.source?.spec?.params ?? []).filter((p) => p.type === "SECRET"); const checks = secretParams.map(async (specParam) => { const resourceName = resolveLiveSecret(specParam, liveParams); if (!resourceName) { return undefined; } const match = resourceName.match(secretManager_1.SECRET_VERSION_NAME_REGEX); if (!match?.groups) { throw new error_1.FirebaseError("Invalid secret version resource name [" + resourceName + "]."); } const projectId = match.groups.project; const secretId = match.groups.secret; const hasLabel = await (0, secrets_1.secretHasExtensionsLabel)(projectId, secretId); return hasLabel ? `${projectId}/${secretId}` : undefined; }); const results = await Promise.all(checks); return results.filter((r) => r !== undefined); } async function ejectSecretsFromInstance(instance) { const success = []; const fail = []; const liveParams = instance.config?.params || {}; for (const specParam of instance.config?.source?.spec?.params ?? []) { if (specParam.type !== "SECRET") { continue; } const resourceName = resolveLiveSecret(specParam, liveParams); if (!resourceName) { continue; } const match = resourceName.match(secretManager_1.SECRET_VERSION_NAME_REGEX); if (!match?.groups) { throw new error_1.FirebaseError(`Invalid secret version resource name [${resourceName}].`); } const projectId = match.groups.project; const secretId = match.groups.secret; const combinedId = `${projectId}/${secretId}`; try { await (0, secrets_1.transferSecretToKits)(projectId, secretId); success.push(combinedId); } catch (err) { fail.push(combinedId); const message = err instanceof Error ? err.message : String(err); (0, utils_1.logLabeledError)("extensions", "failed to change labels on " + combinedId + ": " + message); } } return { success: success, fail: fail }; }