@caelo-cms/provisioning 0.3.8 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/stacks/gcp-firebase/index.ts +68 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caelo-cms/provisioning",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"description": "Pulumi-driven provisioning for Caelo CMS — `bunx @caelo-cms/provisioning --provider <self-hosted|gcp|aws|azure>` brings up a TLS-served install with Postgres + storage + secrets manager + the admin + the API gateway.",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"prepublishOnly": "tsc -b --force && tsc --project stacks/gcp/tsconfig.json && tsc --project stacks/gcp-firebase/tsconfig.json"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@caelo-cms/shared": "^0.3.
|
|
41
|
+
"@caelo-cms/shared": "^0.3.10",
|
|
42
42
|
"@clack/prompts": "1.3.0",
|
|
43
43
|
"@pulumi/aws": "^7",
|
|
44
44
|
"@pulumi/awsx": "^3",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* Shared code lives in `packages/provisioning/src/` (imported here).
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
+
import * as command from "@pulumi/command";
|
|
28
29
|
import * as gcp from "@pulumi/gcp";
|
|
29
30
|
import * as pulumi from "@pulumi/pulumi";
|
|
30
31
|
import * as random from "@pulumi/random";
|
|
@@ -464,10 +465,24 @@ const adminSvc = cloudRunService({
|
|
|
464
465
|
// v0.3.1 — Firebase Hosting uses a Google-managed service identity
|
|
465
466
|
// to proxy traffic via `rewrites` to Cloud Run. Grant it
|
|
466
467
|
// `roles/run.invoker` on the gateway so /api/** requests succeed.
|
|
467
|
-
//
|
|
468
|
-
// gcp
|
|
469
|
-
//
|
|
470
|
-
//
|
|
468
|
+
//
|
|
469
|
+
// v0.3.10 — `gcp.projects.ServiceIdentity` is NOT supported for
|
|
470
|
+
// `firebasehosting.googleapis.com` (the GenerateServiceIdentity API
|
|
471
|
+
// returns IAM_SERVICE_NOT_CONFIGURED_FOR_IDENTITIES for it). The
|
|
472
|
+
// `gcloud beta services identity create` CLI command works where
|
|
473
|
+
// the GenerateServiceIdentity API does not — it falls back to a
|
|
474
|
+
// different bootstrap path. We shell out via @pulumi/command,
|
|
475
|
+
// which is idempotent (gcloud returns existing if the SA is
|
|
476
|
+
// already provisioned). The SA email is deterministic from the
|
|
477
|
+
// project number, so we construct it independently.
|
|
478
|
+
const firebaseHostingIdentityCmd = new command.local.Command(
|
|
479
|
+
`${namePrefix}-firebasehosting-identity`,
|
|
480
|
+
{
|
|
481
|
+
create: pulumi.interpolate`gcloud beta services identity create --service=firebasehosting.googleapis.com --project=${project} --quiet`,
|
|
482
|
+
},
|
|
483
|
+
opts,
|
|
484
|
+
);
|
|
485
|
+
|
|
471
486
|
const projectInfo = gcp.organizations.getProjectOutput({ projectId: project }, opts);
|
|
472
487
|
const firebaseHostingSa = pulumi.interpolate`serviceAccount:service-${projectInfo.number}@gcp-sa-firebasehosting.iam.gserviceaccount.com`;
|
|
473
488
|
|
|
@@ -480,25 +495,41 @@ new gcp.cloudrun.IamMember(
|
|
|
480
495
|
role: "roles/run.invoker",
|
|
481
496
|
member: firebaseHostingSa,
|
|
482
497
|
},
|
|
483
|
-
opts,
|
|
498
|
+
{ ...opts, dependsOn: [firebaseHostingIdentityCmd] },
|
|
484
499
|
);
|
|
485
500
|
|
|
486
501
|
// =========================================================================
|
|
487
502
|
// Admin custom domain — Cloud Run domain mapping
|
|
488
503
|
// =========================================================================
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
504
|
+
//
|
|
505
|
+
// v0.3.10 — Cloud Run DomainMapping requires the operator to verify
|
|
506
|
+
// domain ownership in Google Search Console FIRST. Without that
|
|
507
|
+
// verification the create fails with "Caller is not authorized to
|
|
508
|
+
// administer the domain ..." even when the provisioner SA has full
|
|
509
|
+
// admin rights on the project. This is a one-time manual step we
|
|
510
|
+
// cannot automate.
|
|
511
|
+
//
|
|
512
|
+
// To keep the install working out of the box, the DomainMapping is
|
|
513
|
+
// opt-in via `provisionAdminDomain=true`. When disabled (default),
|
|
514
|
+
// the operator reaches the admin via the Cloud Run-generated URL
|
|
515
|
+
// (`https://<admin-svc>-<hash>-<region>.a.run.app`). They can flip
|
|
516
|
+
// the knob + verify the domain at https://search.google.com/search-console
|
|
517
|
+
// later and run `pulumi up` to bind admin.<domain> → Cloud Run.
|
|
518
|
+
const provisionAdminDomain = cfg.getBoolean("provisionAdminDomain") ?? false;
|
|
519
|
+
const adminDomainMapping = provisionAdminDomain
|
|
520
|
+
? new gcp.cloudrun.DomainMapping(
|
|
521
|
+
`${namePrefix}-admin-domain`,
|
|
522
|
+
{
|
|
523
|
+
location: region,
|
|
524
|
+
name: adminDomain,
|
|
525
|
+
metadata: { namespace: project },
|
|
526
|
+
spec: {
|
|
527
|
+
routeName: adminSvc.name,
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
{ ...opts, dependsOn: [adminSvc] },
|
|
531
|
+
)
|
|
532
|
+
: undefined;
|
|
502
533
|
|
|
503
534
|
// =========================================================================
|
|
504
535
|
// IAP on the admin Cloud Run service (no LB)
|
|
@@ -515,13 +546,19 @@ const adminDomainMapping = new gcp.cloudrun.DomainMapping(
|
|
|
515
546
|
// shipped. If your provider build is older than v8.x, run
|
|
516
547
|
// `gcloud beta run services update <admin-service> --iap` as a
|
|
517
548
|
// post-up step. The stack provisions the IAM bindings either way.
|
|
549
|
+
// v0.3.10 — Cloud Run v1 IAM API (`gcp.cloudrun.IamMember`) rejects
|
|
550
|
+
// `roles/iap.httpsResourceAccessor` with "Role is not supported for
|
|
551
|
+
// this resource". The v2 IAM API (`gcp.cloudrunv2.ServiceIamMember`)
|
|
552
|
+
// is the right binding for native-IAP-on-Cloud-Run, matching
|
|
553
|
+
// Google's documented setup: `gcloud run services
|
|
554
|
+
// add-iam-policy-binding ... --role=roles/iap.httpsResourceAccessor`.
|
|
518
555
|
for (const principal of iapAllowlist) {
|
|
519
|
-
new gcp.
|
|
556
|
+
new gcp.cloudrunv2.ServiceIamMember(
|
|
520
557
|
`${namePrefix}-admin-iap-${principal.replace(/[^a-z0-9]/gi, "-").slice(0, 40)}`,
|
|
521
558
|
{
|
|
522
559
|
location: region,
|
|
523
560
|
project,
|
|
524
|
-
|
|
561
|
+
name: adminSvc.name,
|
|
525
562
|
role: "roles/iap.httpsResourceAccessor",
|
|
526
563
|
member: principal,
|
|
527
564
|
},
|
|
@@ -533,12 +570,22 @@ for (const principal of iapAllowlist) {
|
|
|
533
570
|
// Outputs — DNS records the operator wires manually
|
|
534
571
|
// =========================================================================
|
|
535
572
|
|
|
573
|
+
const adminMappingStatuses: pulumi.Output<
|
|
574
|
+
gcp.types.output.cloudrun.DomainMappingStatus[] | undefined
|
|
575
|
+
> = adminDomainMapping
|
|
576
|
+
? adminDomainMapping.statuses
|
|
577
|
+
: (pulumi.output(undefined) as pulumi.Output<
|
|
578
|
+
gcp.types.output.cloudrun.DomainMappingStatus[] | undefined
|
|
579
|
+
>);
|
|
580
|
+
|
|
536
581
|
const dnsRecords: pulumi.Output<DnsRecord[]> = pulumi
|
|
537
|
-
.all([
|
|
582
|
+
.all([adminMappingStatuses, firebaseCustomDomain.requiredDnsUpdates])
|
|
538
583
|
.apply(([adminStatuses, firebaseUpdates]) => {
|
|
539
584
|
const out: DnsRecord[] = [];
|
|
540
585
|
|
|
541
586
|
// Admin subdomain — Cloud Run domain mapping prints CNAMEs/As.
|
|
587
|
+
// When `provisionAdminDomain=false`, no mapping exists and the
|
|
588
|
+
// operator reaches admin via the *.run.app URL printed separately.
|
|
542
589
|
const adminTargets =
|
|
543
590
|
adminStatuses
|
|
544
591
|
?.flatMap((s) => s.resourceRecords ?? [])
|