No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

functionsEmulatorShared.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.toBackendInfo = exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.prepareEndpoints = exports.eventServiceImplemented = exports.EmulatedTrigger = exports.HttpConstants = exports.EVENTARC_SOURCE_ENV = void 0;
  4. const os = require("os");
  5. const path = require("path");
  6. const fs = require("fs");
  7. const crypto_1 = require("crypto");
  8. const _ = require("lodash");
  9. const backend = require("../deploy/functions/backend");
  10. const constants_1 = require("./constants");
  11. const manifest_1 = require("../extensions/manifest");
  12. const extensionsHelper_1 = require("../extensions/extensionsHelper");
  13. const postinstall_1 = require("./extensions/postinstall");
  14. const services_1 = require("../deploy/functions/services");
  15. const prepare_1 = require("../deploy/functions/prepare");
  16. const events = require("../functions/events");
  17. const utils_1 = require("../utils");
  18. const V2_EVENTS = [
  19. events.v2.PUBSUB_PUBLISH_EVENT,
  20. ...events.v2.STORAGE_EVENTS,
  21. ...events.v2.DATABASE_EVENTS,
  22. ];
  23. exports.EVENTARC_SOURCE_ENV = "EVENTARC_CLOUD_EVENT_SOURCE";
  24. class HttpConstants {
  25. }
  26. exports.HttpConstants = HttpConstants;
  27. HttpConstants.CALLABLE_AUTH_HEADER = "x-callable-context-auth";
  28. HttpConstants.ORIGINAL_AUTH_HEADER = "x-original-auth";
  29. class EmulatedTrigger {
  30. constructor(definition, module) {
  31. this.definition = definition;
  32. this.module = module;
  33. }
  34. get memoryLimitBytes() {
  35. return (this.definition.availableMemoryMb || 128) * 1024 * 1024;
  36. }
  37. get timeoutMs() {
  38. return (this.definition.timeoutSeconds || 60) * 1000;
  39. }
  40. getRawFunction() {
  41. if (!this.module) {
  42. throw new Error("EmulatedTrigger has not been provided a module.");
  43. }
  44. const func = _.get(this.module, this.definition.entryPoint);
  45. return func.__emulator_func || func;
  46. }
  47. }
  48. exports.EmulatedTrigger = EmulatedTrigger;
  49. function eventServiceImplemented(eventType) {
  50. return V2_EVENTS.includes(eventType);
  51. }
  52. exports.eventServiceImplemented = eventServiceImplemented;
  53. function prepareEndpoints(endpoints) {
  54. const bkend = backend.of(...endpoints);
  55. for (const ep of endpoints) {
  56. (0, services_1.serviceForEndpoint)(ep).validateTrigger(ep, bkend);
  57. }
  58. (0, prepare_1.inferBlockingDetails)(bkend);
  59. }
  60. exports.prepareEndpoints = prepareEndpoints;
  61. function emulatedFunctionsFromEndpoints(endpoints) {
  62. const regionDefinitions = [];
  63. for (const endpoint of endpoints) {
  64. if (!endpoint.region) {
  65. endpoint.region = "us-central1";
  66. }
  67. const def = {
  68. entryPoint: endpoint.entryPoint,
  69. platform: endpoint.platform,
  70. region: endpoint.region,
  71. name: endpoint.id,
  72. id: `${endpoint.region}-${endpoint.id}`,
  73. codebase: endpoint.codebase,
  74. };
  75. def.availableMemoryMb = endpoint.availableMemoryMb || 256;
  76. def.labels = endpoint.labels || {};
  77. if (endpoint.platform === "gcfv1") {
  78. def.labels[exports.EVENTARC_SOURCE_ENV] =
  79. "cloudfunctions-emulated.googleapis.com" +
  80. `/projects/${endpoint.project || "project"}/locations/${endpoint.region}/functions/${endpoint.id}`;
  81. }
  82. else if (endpoint.platform === "gcfv2") {
  83. def.labels[exports.EVENTARC_SOURCE_ENV] =
  84. "run-emulated.googleapis.com" +
  85. `/projects/${endpoint.project || "project"}/locations/${endpoint.region}/services/${endpoint.id}`;
  86. }
  87. def.timeoutSeconds = endpoint.timeoutSeconds || 60;
  88. def.secretEnvironmentVariables = endpoint.secretEnvironmentVariables || [];
  89. def.platform = endpoint.platform;
  90. if (backend.isHttpsTriggered(endpoint)) {
  91. def.httpsTrigger = endpoint.httpsTrigger;
  92. }
  93. else if (backend.isCallableTriggered(endpoint)) {
  94. def.httpsTrigger = {};
  95. def.labels = Object.assign(Object.assign({}, def.labels), { "deployment-callable": "true" });
  96. }
  97. else if (backend.isEventTriggered(endpoint)) {
  98. const eventTrigger = endpoint.eventTrigger;
  99. if (endpoint.platform === "gcfv1") {
  100. def.eventTrigger = {
  101. eventType: eventTrigger.eventType,
  102. resource: eventTrigger.eventFilters.resource,
  103. };
  104. }
  105. else {
  106. if (!eventServiceImplemented(eventTrigger.eventType) && !eventTrigger.channel) {
  107. continue;
  108. }
  109. const { resource, topic, bucket } = endpoint.eventTrigger.eventFilters;
  110. const eventResource = resource || topic || bucket;
  111. def.eventTrigger = {
  112. eventType: eventTrigger.eventType,
  113. resource: eventResource,
  114. channel: eventTrigger.channel,
  115. eventFilters: eventTrigger.eventFilters,
  116. eventFilterPathPatterns: eventTrigger.eventFilterPathPatterns,
  117. };
  118. }
  119. }
  120. else if (backend.isScheduleTriggered(endpoint)) {
  121. def.eventTrigger = { eventType: "pubsub", resource: "" };
  122. def.schedule = endpoint.scheduleTrigger;
  123. }
  124. else if (backend.isBlockingTriggered(endpoint)) {
  125. def.blockingTrigger = {
  126. eventType: endpoint.blockingTrigger.eventType,
  127. options: endpoint.blockingTrigger.options || {},
  128. };
  129. }
  130. else if (backend.isTaskQueueTriggered(endpoint)) {
  131. def.httpsTrigger = {};
  132. }
  133. else {
  134. }
  135. regionDefinitions.push(def);
  136. }
  137. return regionDefinitions;
  138. }
  139. exports.emulatedFunctionsFromEndpoints = emulatedFunctionsFromEndpoints;
  140. function emulatedFunctionsByRegion(definitions, secretEnvVariables = []) {
  141. const regionDefinitions = [];
  142. for (const def of definitions) {
  143. if (!def.regions) {
  144. def.regions = ["us-central1"];
  145. }
  146. for (const region of def.regions) {
  147. const defDeepCopy = JSON.parse(JSON.stringify(def));
  148. defDeepCopy.regions = [region];
  149. defDeepCopy.region = region;
  150. defDeepCopy.id = `${region}-${defDeepCopy.name}`;
  151. defDeepCopy.platform = defDeepCopy.platform || "gcfv1";
  152. defDeepCopy.secretEnvironmentVariables = secretEnvVariables;
  153. regionDefinitions.push(defDeepCopy);
  154. }
  155. }
  156. return regionDefinitions;
  157. }
  158. exports.emulatedFunctionsByRegion = emulatedFunctionsByRegion;
  159. function getEmulatedTriggersFromDefinitions(definitions, module) {
  160. return definitions.reduce((obj, definition) => {
  161. obj[definition.id] = new EmulatedTrigger(definition, module);
  162. return obj;
  163. }, {});
  164. }
  165. exports.getEmulatedTriggersFromDefinitions = getEmulatedTriggersFromDefinitions;
  166. function getTemporarySocketPath() {
  167. const rand = (0, crypto_1.randomBytes)(8).toString("hex");
  168. if (process.platform === "win32") {
  169. return path.join("\\\\?\\pipe", `fire_emu_${rand}`);
  170. }
  171. else {
  172. return path.join(os.tmpdir(), `fire_emu_${rand}.sock`);
  173. }
  174. }
  175. exports.getTemporarySocketPath = getTemporarySocketPath;
  176. function getFunctionService(def) {
  177. var _a;
  178. if (def.eventTrigger) {
  179. if (def.eventTrigger.channel) {
  180. return constants_1.Constants.SERVICE_EVENTARC;
  181. }
  182. return (_a = def.eventTrigger.service) !== null && _a !== void 0 ? _a : getServiceFromEventType(def.eventTrigger.eventType);
  183. }
  184. if (def.blockingTrigger) {
  185. return def.blockingTrigger.eventType;
  186. }
  187. if (def.httpsTrigger) {
  188. return "https";
  189. }
  190. return "unknown";
  191. }
  192. exports.getFunctionService = getFunctionService;
  193. function getServiceFromEventType(eventType) {
  194. if (eventType.includes("firestore")) {
  195. return constants_1.Constants.SERVICE_FIRESTORE;
  196. }
  197. if (eventType.includes("database")) {
  198. return constants_1.Constants.SERVICE_REALTIME_DATABASE;
  199. }
  200. if (eventType.includes("pubsub")) {
  201. return constants_1.Constants.SERVICE_PUBSUB;
  202. }
  203. if (eventType.includes("storage")) {
  204. return constants_1.Constants.SERVICE_STORAGE;
  205. }
  206. if (eventType.includes("analytics")) {
  207. return constants_1.Constants.SERVICE_ANALYTICS;
  208. }
  209. if (eventType.includes("auth")) {
  210. return constants_1.Constants.SERVICE_AUTH;
  211. }
  212. if (eventType.includes("crashlytics")) {
  213. return constants_1.Constants.SERVICE_CRASHLYTICS;
  214. }
  215. if (eventType.includes("remoteconfig")) {
  216. return constants_1.Constants.SERVICE_REMOTE_CONFIG;
  217. }
  218. if (eventType.includes("testing")) {
  219. return constants_1.Constants.SERVICE_TEST_LAB;
  220. }
  221. return "";
  222. }
  223. exports.getServiceFromEventType = getServiceFromEventType;
  224. function waitForBody(req) {
  225. let data = "";
  226. return new Promise((resolve) => {
  227. req.on("data", (chunk) => {
  228. data += chunk;
  229. });
  230. req.on("end", () => {
  231. resolve(data);
  232. });
  233. });
  234. }
  235. exports.waitForBody = waitForBody;
  236. function findModuleRoot(moduleName, filepath) {
  237. const hierarchy = filepath.split(path.sep);
  238. for (let i = 0; i < hierarchy.length; i++) {
  239. try {
  240. let chunks = [];
  241. if (i) {
  242. chunks = hierarchy.slice(0, -i);
  243. }
  244. else {
  245. chunks = hierarchy;
  246. }
  247. const packagePath = path.join(chunks.join(path.sep), "package.json");
  248. const serializedPackage = fs.readFileSync(packagePath, "utf8").toString();
  249. if (JSON.parse(serializedPackage).name === moduleName) {
  250. return chunks.join("/");
  251. }
  252. break;
  253. }
  254. catch (err) {
  255. }
  256. }
  257. return "";
  258. }
  259. exports.findModuleRoot = findModuleRoot;
  260. function formatHost(info) {
  261. const host = (0, utils_1.connectableHostname)(info.host);
  262. if (host.includes(":")) {
  263. return `[${host}]:${info.port}`;
  264. }
  265. else {
  266. return `${host}:${info.port}`;
  267. }
  268. }
  269. exports.formatHost = formatHost;
  270. function getSignatureType(def) {
  271. if (def.httpsTrigger || def.blockingTrigger) {
  272. return "http";
  273. }
  274. return def.platform === "gcfv2" ? "cloudevent" : "event";
  275. }
  276. exports.getSignatureType = getSignatureType;
  277. const LOCAL_SECRETS_FILE = ".secret.local";
  278. function getSecretLocalPath(backend, projectDir) {
  279. const secretsFile = backend.extensionInstanceId
  280. ? `${backend.extensionInstanceId}${LOCAL_SECRETS_FILE}`
  281. : LOCAL_SECRETS_FILE;
  282. const secretDirectory = backend.extensionInstanceId
  283. ? path.join(projectDir, manifest_1.ENV_DIRECTORY)
  284. : backend.functionsDir;
  285. return path.join(secretDirectory, secretsFile);
  286. }
  287. exports.getSecretLocalPath = getSecretLocalPath;
  288. function toBackendInfo(e, cf3Triggers) {
  289. var _a, _b;
  290. const envWithSecrets = Object.assign({}, e.env);
  291. for (const s of e.secretEnv) {
  292. envWithSecrets[s.key] = backend.secretVersionName(s);
  293. }
  294. let extensionVersion = e.extensionVersion;
  295. if (extensionVersion) {
  296. extensionVersion = (0, extensionsHelper_1.substituteParams)(extensionVersion, e.env);
  297. if ((_a = extensionVersion.spec) === null || _a === void 0 ? void 0 : _a.postinstallContent) {
  298. extensionVersion.spec.postinstallContent = (0, postinstall_1.replaceConsoleLinks)(extensionVersion.spec.postinstallContent);
  299. }
  300. }
  301. let extensionSpec = e.extensionSpec;
  302. if (extensionSpec) {
  303. extensionSpec = (0, extensionsHelper_1.substituteParams)(extensionSpec, e.env);
  304. if (extensionSpec === null || extensionSpec === void 0 ? void 0 : extensionSpec.postinstallContent) {
  305. extensionSpec.postinstallContent = (0, postinstall_1.replaceConsoleLinks)(extensionSpec.postinstallContent);
  306. }
  307. }
  308. return JSON.parse(JSON.stringify({
  309. directory: e.functionsDir,
  310. env: envWithSecrets,
  311. extensionInstanceId: e.extensionInstanceId,
  312. extension: e.extension,
  313. extensionVersion: extensionVersion,
  314. extensionSpec: extensionSpec,
  315. functionTriggers: (_b = e.predefinedTriggers) !== null && _b !== void 0 ? _b : cf3Triggers.filter((t) => t.codebase === e.codebase),
  316. }));
  317. }
  318. exports.toBackendInfo = toBackendInfo;