Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cdk/lib/app-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,12 @@ export class AppStack extends Stack {
OPENSEARCH_NODE: `https://${Fn.importValue(
`Sps-Data-${env}-OpenSearchDomainEndpoint`,
)}`,
// #353 -- CloudFront distribution id for the synchronous post-commit
// edge purge (lib/edit/revalidation.ts invalidateCloudFront). Populated
// in both envs; before this the var was unset so the purge no-opped.
// The web task role carries the matching cloudfront:CreateInvalidation
// grant (TaskRoleCloudFrontPolicy). Enable rides the #502 edge cutover.
SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID: envConfig.cloudFrontDistributionId,
// SAML SP non-secret config (#466). Without these, getSamlEnv()'s
// requireEnv throws on the first missing var and every SAML route
// 503s ("SAML SP is not configured"); SP-initiated sign-in is dead.
Expand Down
19 changes: 11 additions & 8 deletions cdk/lib/etl-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1295,11 +1295,12 @@ export class EtlStack extends Stack {
// outage / SDK error; the ~5 min SLA is that recovery floor, not everyday
// edge-cache purge latency.
//
// Dormant-safe: the task injects NO SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID, so
// the worker no-ops without touching the DB (empty-queue-safe) until the
// operator supplies the distribution id at enable time -- exactly as the
// synchronous invalidation path is dormant pre-launch. This keeps the
// reconciler decoupled from the #502-frozen EdgeStack distribution.
// #353 enabled: the task injects SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID from
// envConfig.cloudFrontDistributionId so the reconciler drains the
// cdn_invalidation outbox against the EdgeStack distribution. Empty-queue-
// safe, so it stays a no-op whenever the outbox is empty. Enable lands with
// the #502 prod edge cutover (CloudFront stays as CDN); deploy via
// `cdk deploy Sps-Etl-<env>` (the env var needs CDK, not just an image roll).
// ------------------------------------------------------------------
const cdnReconcileLogGroup = new logs.LogGroup(
this,
Expand Down Expand Up @@ -1402,9 +1403,11 @@ export class EtlStack extends Stack {
}),
environment: {
NODE_ENV: "production",
// NO SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID: dormant-safe -- the worker
// no-ops without touching the DB until the operator supplies it at
// enable time. No OPENSEARCH_* either (this worker never reads it).
// #353 enable -- wire the EdgeStack distribution id so the reconciler
// drains the cdn_invalidation outbox. envConfig.cloudFrontDistributionId
// is populated in both envs; this was omitted while the path was
// dormant. No OPENSEARCH_* (this worker never reads it).
SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID: envConfig.cloudFrontDistributionId,
},
secrets: {
// db.read + db.write collapse onto this single DSN (no
Expand Down
8 changes: 8 additions & 0 deletions cdk/test/__snapshots__/app-stack.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ exports[`AppStack prod matches the snapshot 1`] = `
],
},
},
{
"Name": "SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID",
"Value": "E28NKDFXC7K2ZL",
},
{
"Name": "SAML_IDP_ENTITY_ID",
"Value": "https://login-proxy.weill.cornell.edu/idp",
Expand Down Expand Up @@ -3295,6 +3299,10 @@ exports[`AppStack staging matches the snapshot 1`] = `
],
},
},
{
"Name": "SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID",
"Value": "E17NRWINXLP3B3",
},
{
"Name": "SAML_IDP_ENTITY_ID",
"Value": "https://login-proxy.weill.cornell.edu/idp",
Expand Down
8 changes: 8 additions & 0 deletions cdk/test/__snapshots__/etl-stack.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,10 @@ exports[`EtlStack prod matches the snapshot 1`] = `
"Name": "NODE_ENV",
"Value": "production",
},
{
"Name": "SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID",
"Value": "E28NKDFXC7K2ZL",
},
],
"Essential": true,
"Image": {
Expand Down Expand Up @@ -5914,6 +5918,10 @@ exports[`EtlStack staging matches the snapshot 1`] = `
"Name": "NODE_ENV",
"Value": "production",
},
{
"Name": "SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID",
"Value": "E17NRWINXLP3B3",
},
],
"Essential": true,
"Image": {
Expand Down
25 changes: 15 additions & 10 deletions cdk/test/etl-stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ describe("EtlStack", () => {
expect(td.Properties?.Memory).toBe("512");
});

it("injects ONLY the DATABASE_URL secret (no OPENSEARCH_*, no SCHOLARS_*) and omits SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID (dormant-safe)", () => {
it("injects ONLY the DATABASE_URL secret (no OPENSEARCH_*/SCHOLARS_* secrets) and wires the SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID env to the distribution id (#353 enabled)", () => {
const td = cdnReconcileTaskDef();
const container = (
td.Properties?.ContainerDefinitions as
Expand All @@ -703,16 +703,21 @@ describe("EtlStack", () => {
/^OPENSEARCH_/.test(n ?? ""),
);
expect(leaked).toEqual([]);
// Dormant-safe: no distribution id is hardcoded onto the task; the
// worker no-ops until the operator supplies it at enable time.
const envNames = (
container?.Environment as Array<{ Name?: string }> | undefined
)?.map((e) => e.Name);
expect(envNames ?? []).not.toContain(
"SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID",
// #353 enabled: the distribution id is wired (plaintext env, not a
// secret) so the reconciler drains the outbox; the prod template gets
// the prod EdgeStack distribution. Was omitted while the path was dormant.
const envEntries =
(container?.Environment as
| Array<{ Name?: string; Value?: string }>
| undefined) ?? [];
const distEntry = envEntries.find(
(e) => e.Name === "SCHOLARS_CLOUDFRONT_DISTRIBUTION_ID",
);
// No OpenSearch endpoint either.
expect(envNames ?? []).not.toContain("OPENSEARCH_NODE");
// Prod EdgeStack distribution (config.ts prod.cloudFrontDistributionId);
// staging's is E17NRWINXLP3B3.
expect(distEntry?.Value).toBe("E28NKDFXC7K2ZL");
// No OpenSearch endpoint either -- this worker never reads it.
expect(envEntries.map((e) => e.Name)).not.toContain("OPENSEARCH_NODE");
});

it("the cdn reconcile exec role lists EXACTLY ONE secret ARN (db/etl; no opensearch, no *)", () => {
Expand Down
Loading