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.

ensure.js 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.secretAccess = exports.cloudBuildEnabled = exports.defaultServiceAccount = void 0;
  4. const clc = require("colorette");
  5. const ensureApiEnabled_1 = require("../../ensureApiEnabled");
  6. const error_1 = require("../../error");
  7. const utils_1 = require("../../utils");
  8. const secretManager_1 = require("../../gcp/secretManager");
  9. const projects_1 = require("../../management/projects");
  10. const functional_1 = require("../../functional");
  11. const track_1 = require("../../track");
  12. const backend = require("./backend");
  13. const FAQ_URL = "https://firebase.google.com/support/faq#functions-runtime";
  14. const CLOUD_BUILD_API = "cloudbuild.googleapis.com";
  15. async function defaultServiceAccount(e) {
  16. const metadata = await (0, projects_1.getFirebaseProject)(e.project);
  17. if (e.platform === "gcfv1") {
  18. return `${metadata.projectId}@appspot.gserviceaccount.com`;
  19. }
  20. else if (e.platform === "gcfv2") {
  21. return `${metadata.projectNumber}-compute@developer.gserviceaccount.com`;
  22. }
  23. (0, functional_1.assertExhaustive)(e.platform);
  24. }
  25. exports.defaultServiceAccount = defaultServiceAccount;
  26. function nodeBillingError(projectId) {
  27. void (0, track_1.track)("functions_runtime_notices", "nodejs10_billing_error");
  28. return new error_1.FirebaseError(`Cloud Functions deployment requires the pay-as-you-go (Blaze) billing plan. To upgrade your project, visit the following URL:
  29. https://console.firebase.google.com/project/${projectId}/usage/details
  30. For additional information about this requirement, see Firebase FAQs:
  31. ${FAQ_URL}`, { exit: 1 });
  32. }
  33. function nodePermissionError(projectId) {
  34. void (0, track_1.track)("functions_runtime_notices", "nodejs10_permission_error");
  35. return new error_1.FirebaseError(`Cloud Functions deployment requires the Cloud Build API to be enabled. The current credentials do not have permission to enable APIs for project ${clc.bold(projectId)}.
  36. Please ask a project owner to visit the following URL to enable Cloud Build:
  37. https://console.cloud.google.com/apis/library/cloudbuild.googleapis.com?project=${projectId}
  38. For additional information about this requirement, see Firebase FAQs:
  39. ${FAQ_URL}
  40. `);
  41. }
  42. function isPermissionError(e) {
  43. var _a, _b, _c;
  44. return ((_c = (_b = (_a = e.context) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.status) === "PERMISSION_DENIED";
  45. }
  46. async function cloudBuildEnabled(projectId) {
  47. try {
  48. await (0, ensureApiEnabled_1.ensure)(projectId, CLOUD_BUILD_API, "functions");
  49. }
  50. catch (e) {
  51. if ((0, error_1.isBillingError)(e)) {
  52. throw nodeBillingError(projectId);
  53. }
  54. else if (isPermissionError(e)) {
  55. throw nodePermissionError(projectId);
  56. }
  57. throw e;
  58. }
  59. }
  60. exports.cloudBuildEnabled = cloudBuildEnabled;
  61. async function secretsToServiceAccounts(b) {
  62. const secretsToSa = {};
  63. for (const e of backend.allEndpoints(b)) {
  64. const sa = e.serviceAccount || (await module.exports.defaultServiceAccount(e));
  65. for (const s of e.secretEnvironmentVariables || []) {
  66. const serviceAccounts = secretsToSa[s.secret] || new Set();
  67. serviceAccounts.add(sa);
  68. secretsToSa[s.secret] = serviceAccounts;
  69. }
  70. }
  71. return secretsToSa;
  72. }
  73. async function secretAccess(projectId, wantBackend, haveBackend) {
  74. var _a, _b;
  75. const ensureAccess = async (secret, serviceAccounts) => {
  76. (0, utils_1.logLabeledBullet)("functions", `ensuring ${clc.bold(serviceAccounts.join(", "))} access to secret ${clc.bold(secret)}.`);
  77. await (0, secretManager_1.ensureServiceAgentRole)({ name: secret, projectId }, serviceAccounts, "roles/secretmanager.secretAccessor");
  78. (0, utils_1.logLabeledSuccess)("functions", `ensured ${clc.bold(serviceAccounts.join(", "))} access to ${clc.bold(secret)}.`);
  79. };
  80. const wantSecrets = await secretsToServiceAccounts(wantBackend);
  81. const haveSecrets = await secretsToServiceAccounts(haveBackend);
  82. for (const [secret, serviceAccounts] of Object.entries(haveSecrets)) {
  83. for (const serviceAccount of serviceAccounts) {
  84. (_a = wantSecrets[secret]) === null || _a === void 0 ? void 0 : _a.delete(serviceAccount);
  85. }
  86. if (((_b = wantSecrets[secret]) === null || _b === void 0 ? void 0 : _b.size) === 0) {
  87. delete wantSecrets[secret];
  88. }
  89. }
  90. const ensure = [];
  91. for (const [secret, serviceAccounts] of Object.entries(wantSecrets)) {
  92. ensure.push(ensureAccess(secret, Array.from(serviceAccounts)));
  93. }
  94. await Promise.all(ensure);
  95. }
  96. exports.secretAccess = secretAccess;