feat(llmo): CloudFront CDN log delivery with assume-role setup (LLMO-5566)#2680
feat(llmo): CloudFront CDN log delivery with assume-role setup (LLMO-5566)#2680claudiaboldis wants to merge 15 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
This PR will trigger a minor release when merged. |
…d, CF templates, setupType, rescan endpoint
- gateEdgeOptimizeWizard now derives externalId = toSafeAwsName(imsOrgId) server-side; FE no longer sends externalId
- getEdgeOptimizeBootstrapUrl accepts setupType ('log-only'|'log-and-oae') and selects the matching CF template key
- Add customer-bootstrap-role.yaml and customer-bootstrap-role-log-only.yaml CloudFormation templates
- Add POST /sites/:siteId/llmo/cdn-log-rescan (rescanCdnLogDelivery) for idempotent re-enabling of log delivery
- Remove 14 stale 'externalId missing' tests; add 9 tests for rescanCdnLogDelivery
Introduced by: #2680
…MO-5566) - Remove dead !Organization early-return from gateEdgeOptimizeWizard - Add test for custom EDGE_OPTIMIZE_ROLE_NAME env var path - Add test for rejection with no .message (covers 'unknown error' fallback) Introduced by: #2680
…strapUrl (LLMO-5566) - Add test for log-only setupType without EDGE_OPTIMIZE_TEMPLATE_KEY_LOG_ONLY (default fallback) - Add test for missing IMS org ID in the bootstrap URL inline org check Introduced by: #2680
main #2682 shipped the CloudFront onboarding wizard (LlmoCloudFrontController, cdn-onboard/cloudfront/*) while this branch was open, making the branch's parallel edge-optimize/* wizard redundant. Reset onto main and rebuilt around the only net-new slice: CloudWatch log delivery. - Add enableCdnLogDelivery + rescanCdnLogDelivery on LlmoCloudFrontController under cdn-onboard/cloudfront/log-delivery and .../log-rescan - Assume-role externalId uses main's client-supplied per-session UUID (validateCloudfrontCredentials); imsOrgId for the org-scoped delivery destination is resolved server-side from the site's organization - Reuse tokowaka CloudFrontEdgeClient.listDistributions for the rescan (no local edge-optimize support module needed) - Add src/support/cdn-log-delivery.js (CloudWatch Logs delivery control plane) and @aws-sdk/client-cloudwatch-logs; OpenAPI + capability/route entries; tests Introduced by: #2680
f3bbb27 to
8cc66b0
Compare
MysticatBot
left a comment
There was a problem hiding this comment.
Hey @claudiaboldis,
Verdict: Request changes - two issues to address before merge (concurrency cap and HTTP status correctness).
Complexity: HIGH - large diff; API surface + new dependency.
Changes: Adds CDN log delivery endpoints (single-distribution and bulk rescan) using STS AssumeRole to create CloudWatch Logs delivery sources in customer accounts (13 files).
Note: Recommend a human read before merge - this change modifies the OpenAPI contract (docs/openapi/). The bot review is a complement to, not a replacement for, a human read here.
Must fix before merge
- [Important] Unbounded concurrency in
rescanCdnLogDeliverywill throttle on large accounts -src/controllers/llmo/llmo-cloudfront.js:1228.Promise.allSettledfirescreateCdnLogDeliveryfor ALL distributions in parallel with no concurrency cap. Each call issues 1-3 CloudWatch Logs API calls. A customer with 50+ distributions will hit CloudWatch Logs per-account throttling (10-25 TPS), causing most calls to fail withThrottlingException. Fix: add a concurrency limiter (e.g.p-limit(5)) or batch distributions in small groups. - [Important] Missing server config returns 400 (client error) instead of 500 -
src/controllers/llmo/llmo-cloudfront.js:1164. WhenCDN_LOG_DELIVERY_DEST_ACCOUNT_IDis not set,badRequest(...)tells the caller their input is wrong when the server is misconfigured. Fix: returninternalServerError(...)instead (same applies inrescanCdnLogDeliveryat line ~1218).
Non-blocking (3): minor issues and suggestions
- nit:
docs/LLMO-5566/plan.mdreferencessrc/controllers/llmo/llmo.jsand 16 endpoints, but the actual controller isllmo-cloudfront.jsand this PR delivers 2 endpoints - the plan is misleading from day one - suggestion: Both new handlers diverge from the existing
mutationErrorResponsepattern used by sibling endpoints in this controller (which surfaces actionable AWS errors likeAccessDeniedExceptionandThrottlingExceptionas 400s instead of generic 500) - consider usingmutationErrorResponsein the outer catch for consistency -src/controllers/llmo/llmo-cloudfront.js:1270 - suggestion:
rescanCdnLogDeliveryresponse includes rawoutcome.reason?.messageper distribution, which can expose internal AWS error details (ARNs, role names) to the caller - consider sanitizing to error category only -src/controllers/llmo/llmo-cloudfront.js:1244
|
Mysticat review failed: Claude CLI crashed (exit 1): stderr= stdout={"type":"result","subtype":"error_max_budget_usd","duration_ms":628407,"duration_api_ms":1527224,"is_error":true,"num_turns":38,"stop_reason":"end_turn","session_id":"7e869476-18cc-497e-b372-17f8889bac58","total_cost_usd":10.07213945,"usage":{"input_tokens":19,"cache_creation_input_tokens":224486,"c |
- rescanCdnLogDelivery: cap parallel createCdnLogDelivery calls in batches of 5 to avoid CloudWatch Logs per-account throttling on large accounts - missing CDN_LOG_DELIVERY_DEST_ACCOUNT_ID now returns 500 (server misconfig), not 400 - outer catch uses mutationErrorResponse so AWS AccessDenied/Throttling surface as 4xx, consistent with sibling CloudFront endpoints - per-distribution rescan errors report the AWS error category (name) only, never the raw message (avoids leaking ARNs/role names) - rewrite docs/LLMO-5566/plan.md to reflect the re-homed log-delivery design Introduced by: #2680
…-5566) Address MysticatBot non-blocking suggestions on PR #2680: - buildDeliverySourceName now hash-truncates names over 60 chars (CloudWatch Logs limit) using a deterministic sha256 suffix, so long IMS org / resource ids stay valid and the name remains stable across calls (idempotency depends on it) - document why deliveryId may be undefined after a ConflictException race (eventual consistency; delivery is enabled regardless, so no retry loop) Introduced by: #2680
|
Reviewed the two new CDN log-delivery endpoints (
Nits (optional, non-blocking):
Please confirm:
|
main #2682 shipped the CloudFront onboarding wizard (LlmoCloudFrontController, cdn-onboard/cloudfront/*) while this branch was open, making the branch's parallel edge-optimize/* wizard redundant. Reset onto main and rebuilt around the only net-new slice: CloudWatch log delivery. - Add enableCdnLogDelivery + rescanCdnLogDelivery on LlmoCloudFrontController under cdn-onboard/cloudfront/log-delivery and .../log-rescan - Assume-role externalId uses main's client-supplied per-session UUID (validateCloudfrontCredentials); imsOrgId for the org-scoped delivery destination is resolved server-side from the site's organization - Reuse tokowaka CloudFrontEdgeClient.listDistributions for the rescan (no local edge-optimize support module needed) - Add src/support/cdn-log-delivery.js (CloudWatch Logs delivery control plane) and @aws-sdk/client-cloudwatch-logs; OpenAPI + capability/route entries; tests Introduced by: #2680
- rescanCdnLogDelivery: cap parallel createCdnLogDelivery calls in batches of 5 to avoid CloudWatch Logs per-account throttling on large accounts - missing CDN_LOG_DELIVERY_DEST_ACCOUNT_ID now returns 500 (server misconfig), not 400 - outer catch uses mutationErrorResponse so AWS AccessDenied/Throttling surface as 4xx, consistent with sibling CloudFront endpoints - per-distribution rescan errors report the AWS error category (name) only, never the raw message (avoids leaking ARNs/role names) - rewrite docs/LLMO-5566/plan.md to reflect the re-homed log-delivery design Introduced by: #2680
Addresses MysticatBot review on PR #2680: - cdn-log-delivery: catch ConflictException/ResourceAlreadyExistsException from CreateDelivery (TOCTOU between concurrent enable/rescan for the same distribution) and return { created:false, alreadyExisted:true } with the winning delivery id, preserving idempotency instead of surfacing a 500 - add integration tests (test/it/postgres + shared) for the log-delivery and log-rescan endpoints covering the LLMO-admin access gate (404/403) and request-body validation (400), mirroring the llmo-cloudflare-onboarding IT pattern Introduced by: #2680
533a216 to
9669a50
Compare
…LMO-5566) Close the codecov/patch gap from the TOCTOU fix: add tests for - CreateDelivery rejecting with a non-conflict error (rethrow path) - conflict where the raced delivery can't be found (findExistingDelivery returns undefined) Introduced by: #2680
…-5566) Address MysticatBot non-blocking suggestions on PR #2680: - buildDeliverySourceName now hash-truncates names over 60 chars (CloudWatch Logs limit) using a deterministic sha256 suffix, so long IMS org / resource ids stay valid and the name remains stable across calls (idempotency depends on it) - document why deliveryId may be undefined after a ConflictException race (eventual consistency; delivery is enabled regardless, so no retry loop) Introduced by: #2680
Security, correctness and test-fidelity fixes: - assertDistributionServesSite guard in enableCdnLogDelivery (#4/#3) - findExistingDelivery validates destination ARN, not just source name (#1) - PutDeliverySource ConflictException surfaces as DeliverySourceConflict 4xx (#2) - Remove invalid deliverySourceName filter from DescribeDeliveries SDK call (#9) - Add CW Logs errors to CATEGORIZED_AWS_ERRORS (#5) - mutationErrorResponse returns error name only, not full message (#10) - rescanCdnLogDelivery: all-ResourceNotFoundException short-circuit (#12) - CDN_LOG_DELIVERY_DEST_ACCOUNT_ID documented in .env.example (#6) - AccessDenied test: properly named error, split 400/500 cases (#11) - findExistingDelivery stubs include deliveryDestinationArn - New test: PutDeliverySource ConflictException -> DeliverySourceConflict Also fix pre-existing TS errors from main (ts-ignore DrsClient missing methods in brands.js; null->undefined coercion in rest-transport.js). Introduced by: #2680
- Strict 12-digit accountId validation (reject non-digit chars outright) - toSafeAwsName guard: throw on IMS org IDs that produce invalid AWS names - Remove dead ResourceAlreadyExistsException from CreateDelivery catch - Add 50-page guard to findExistingDelivery paginator - Audit log on ResourceNotFoundException path in enableCdnLogDelivery - CDN_LOG_RESCAN_MAX_DISTRIBUTIONS cap in rescanCdnLogDelivery (truncated+totalFound) - Assert AWS command inputs (put/create) in cdn-log-delivery tests - Partial-state recovery test (source exists, no delivery -> created:true) - empty-distributions rescan test + max-distributions cap test Introduced by: #2680 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main #2682 shipped the CloudFront onboarding wizard (LlmoCloudFrontController, cdn-onboard/cloudfront/*) while this branch was open, making the branch's parallel edge-optimize/* wizard redundant. Reset onto main and rebuilt around the only net-new slice: CloudWatch log delivery. - Add enableCdnLogDelivery + rescanCdnLogDelivery on LlmoCloudFrontController under cdn-onboard/cloudfront/log-delivery and .../log-rescan - Assume-role externalId uses main's client-supplied per-session UUID (validateCloudfrontCredentials); imsOrgId for the org-scoped delivery destination is resolved server-side from the site's organization - Reuse tokowaka CloudFrontEdgeClient.listDistributions for the rescan (no local edge-optimize support module needed) - Add src/support/cdn-log-delivery.js (CloudWatch Logs delivery control plane) and @aws-sdk/client-cloudwatch-logs; OpenAPI + capability/route entries; tests Introduced by: #2680
- rescanCdnLogDelivery: cap parallel createCdnLogDelivery calls in batches of 5 to avoid CloudWatch Logs per-account throttling on large accounts - missing CDN_LOG_DELIVERY_DEST_ACCOUNT_ID now returns 500 (server misconfig), not 400 - outer catch uses mutationErrorResponse so AWS AccessDenied/Throttling surface as 4xx, consistent with sibling CloudFront endpoints - per-distribution rescan errors report the AWS error category (name) only, never the raw message (avoids leaking ARNs/role names) - rewrite docs/LLMO-5566/plan.md to reflect the re-homed log-delivery design Introduced by: #2680
Addresses MysticatBot review on PR #2680: - cdn-log-delivery: catch ConflictException/ResourceAlreadyExistsException from CreateDelivery (TOCTOU between concurrent enable/rescan for the same distribution) and return { created:false, alreadyExisted:true } with the winning delivery id, preserving idempotency instead of surfacing a 500 - add integration tests (test/it/postgres + shared) for the log-delivery and log-rescan endpoints covering the LLMO-admin access gate (404/403) and request-body validation (400), mirroring the llmo-cloudflare-onboarding IT pattern Introduced by: #2680
…LMO-5566) Close the codecov/patch gap from the TOCTOU fix: add tests for - CreateDelivery rejecting with a non-conflict error (rethrow path) - conflict where the raced delivery can't be found (findExistingDelivery returns undefined) Introduced by: #2680
…-5566) Address MysticatBot non-blocking suggestions on PR #2680: - buildDeliverySourceName now hash-truncates names over 60 chars (CloudWatch Logs limit) using a deterministic sha256 suffix, so long IMS org / resource ids stay valid and the name remains stable across calls (idempotency depends on it) - document why deliveryId may be undefined after a ConflictException race (eventual consistency; delivery is enabled regardless, so no retry loop) Introduced by: #2680
Security, correctness and test-fidelity fixes: - assertDistributionServesSite guard in enableCdnLogDelivery (#4/#3) - findExistingDelivery validates destination ARN, not just source name (#1) - PutDeliverySource ConflictException surfaces as DeliverySourceConflict 4xx (#2) - Remove invalid deliverySourceName filter from DescribeDeliveries SDK call (#9) - Add CW Logs errors to CATEGORIZED_AWS_ERRORS (#5) - mutationErrorResponse returns error name only, not full message (#10) - rescanCdnLogDelivery: all-ResourceNotFoundException short-circuit (#12) - CDN_LOG_DELIVERY_DEST_ACCOUNT_ID documented in .env.example (#6) - AccessDenied test: properly named error, split 400/500 cases (#11) - findExistingDelivery stubs include deliveryDestinationArn - New test: PutDeliverySource ConflictException -> DeliverySourceConflict Also fix pre-existing TS errors from main (ts-ignore DrsClient missing methods in brands.js; null->undefined coercion in rest-transport.js). Introduced by: #2680
- Strict 12-digit accountId validation (reject non-digit chars outright) - toSafeAwsName guard: throw on IMS org IDs that produce invalid AWS names - Remove dead ResourceAlreadyExistsException from CreateDelivery catch - Add 50-page guard to findExistingDelivery paginator - Audit log on ResourceNotFoundException path in enableCdnLogDelivery - CDN_LOG_RESCAN_MAX_DISTRIBUTIONS cap in rescanCdnLogDelivery (truncated+totalFound) - Assert AWS command inputs (put/create) in cdn-log-delivery tests - Partial-state recovery test (source exists, no delivery -> created:true) - empty-distributions rescan test + max-distributions cap test Introduced by: #2680 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er-side derivation externalId is now derived server-side from the site's IMS org id (resolveConnectorExternalId), so the client no longer supplies it. Remove the two tests that expected a 400 when externalId was absent from the request body, and align the imsOrgId assertion with TEST_IMS_ORG_ID. Introduced by: N/A Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9669a50 to
b6d1aca
Compare
|
IAM permissions — confirmed. Both Shared-client placement — deferred, tracked. Agreed that |
The previous fix used `?? undefined` to satisfy TypeScript, but `null ?? undefined` evaluates to `undefined`, which JSON-serializes as a missing key — breaking the null-promotion contract. Cast to `any` instead so the null is preserved on the wire while TypeScript is satisfied. Introduced by: N/A Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…elivery (LLMO-5566) Adds two customer-facing CloudFormation templates that provision the cross-account IAM role required for CDN log delivery: - customer-bootstrap-role.yaml: full edge-optimize + log delivery permissions - customer-bootstrap-role-log-only.yaml: log delivery permissions only Both grant logs:PutDeliverySource, logs:CreateDelivery, logs:GetDeliverySource, and logs:DescribeDeliveries — all four operations used by enableCdnLogDelivery and rescanCdnLogDelivery. Introduced by: #2680 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d (LLMO-5566) Mirrors the unit test fix: externalId is no longer accepted from the client (it is derived server-side from the site's IMS org id), so the IT tests that expected a 400 for a missing externalId are removed for both log-delivery and log-rescan. Introduced by: N/A Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- buildDeliverySourceName: throws when org id produces empty/invalid AWS name segment - createCdnLogDelivery: rethrows non-conflict error from PutDeliverySource - enableCdnLogDelivery: returns 400 when distribution does not serve the site (guard.error path) - rescanCdnLogDelivery: returns 400 when all distributions fail with ResourceNotFoundException Introduced by: #2680 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…udfront-log-delivery-assume-role
Summary
AssumeRole(connector role) so the LLMO UI CloudFront wizard never needs AWS credentials in the browser — all CloudFront and CloudWatch Logs calls are performed by the Lambdaconnect,distributions,prerequisites,origins,behaviors) that power the step-by-step CloudFront BYOCDN setup flowPOST /sites/:siteId/llmo/cdn-log-delivery(diagram step 8) — idempotently creates a CloudWatch Logs delivery source in the customer's account and links it to Adobe's cross-account delivery destination, enabling CloudFront standard log push to the cdn-logs S3 bucketNew files
src/support/edge-optimize.jsassumeConnectorRole(STS),listCloudFrontDistributions,getDistributionConfig, idempotent step-on-poll deploy orchestratorsrc/support/cdn-log-delivery.jslogs:PutDeliverySource+logs:CreateDeliveryvia the assumed roleEndpoints (all
POST, allINTERNAL_ROUTES— admin/IMS-only)POST /sites/:siteId/llmo/edge-optimize/connectPOST /sites/:siteId/llmo/edge-optimize/distributionsPOST /sites/:siteId/llmo/edge-optimize/prerequisitesPOST /sites/:siteId/llmo/edge-optimize/originsPOST /sites/:siteId/llmo/edge-optimize/behaviorsPOST /sites/:siteId/llmo/cdn-log-deliverycustomer-bootstrap-role.yamlinEDGE_OPTIMIZE_TEMPLATE_BUCKET) must addlogs:PutDeliverySource,logs:CreateDelivery,logs:GetDeliverySource,logs:DescribeDeliveries— today the role only grants CloudFront permscdn-logs-<org>delivery destination + cross-account policy for the BYOCDN CloudFront path (the handler surfaces a clear "destination not provisioned" 400 if it's missing)Test plan
Related
🤖 Generated with Claude Code