Fable r3
Vote¶
APPROVE WITH NOTES — r3 addresses both round-2 P1s with gates that only ever act on known literals (2a/2b, 8a/8b/8c), every round-2 note is folded in, and the new steps I checked against the provider source and AWS docs are safe; the one mechanical gap I found (the 7a forget can never appear in a refreshed plan) stalls the cutover in a safe state rather than endangering traffic, so it is a P2 build fix, not a blocker.
Round-2 items status¶
| Item | Status | Note |
|---|---|---|
| Astra P1-1 (cert collision detected before cert exists) | addressed | §4.3 two-stage: read-only predictor, then gated cert-request (2a) creating only aws_acm_certificate, cert-requested.json, generate --stage rebuild emits literals; allow_overwrite = false gated (provider sends CREATE unless allow_overwrite or !IsNewResource(), https://raw.githubusercontent.com/hashicorp/terraform-provider-aws/main/internal/service/route53/record.go); ACM reuse per FQDN confirmed ("request additional ACM certificates for your FQDN for as long as the CNAME record remains in place", https://docs.aws.amazon.com/acm/latest/userguide/dns-validation.html) |
| Astra P1-2 (worker-on vs unknown task_definition) | addressed | §4.8 #8a–8c: on revision is its own address created and verified first; 8c updates task_definition literal→literal; skip_destroy = true keeps both revisions ACTIVE for --rollback; 8a before 8b so App Runner is untouched on failure |
| Astra P2 (connector carries both SGs) / Fable P2 | addressed | §3.1, §3.2, §4.3, §4.4, §4.5; live DescribeVpcConnector.SecurityGroups, env_sg: join | omit; fixture lines 377–379 confirm both SGs |
| Astra P2 (IGW + public IP path) | addressed | §4.5 second route path |
| Astra P2 (no-custom-domain branch) | addressed | §4.1 check 3, §4.8 #1, §4.9 "No custom domain", §4.11; see P2 note on new_host |
| Astra P2 (rollback after hand-over) | addressed | §4.11 row: re-freeze, refresh, restore literal task_definition, off-twin via 8a gate |
| Astra P3 (pin provider for UPSERT/INSYNC) | addressed | §4.9, §7, §8 offline assertion at pinned version |
| Astra/Opus/Fable P3 (ADR-0016) | addressed | §14 |
| Opus P2 (validation compare too early) | addressed | as Astra P1-1 |
Opus P2 / Fable P2 (forget in import phase) |
addressed | §4.9 step 5, §7: import phase extended; split rejected with reason. plan.py:74,103 confirm today's restriction. See P2 note on refresh |
| Opus P2 (rollback waypoint) | addressed | §4.9 manifest lists rollback path per pair; direct (0,100)→(100,0) fails |
| Opus P2 (drop UNCONFIRMED UPSERT/INSYNC) | addressed | §4.9, §15 |
| Opus P3 (CloudTrail limits) | addressed | §5.2 item 1 |
| Opus P3 (build ordering) | addressed | §4.3 depends_on, grace period, circuit breaker |
| Opus P3 (log group on rollback) | addressed | §4.11 infra-rebuild-rollback/ with removed { destroy = false } |
Opus P3 (which root for idle_timeout) |
addressed | §4.2, §4.7 prepare-alb |
| Fable P3 (health-check defaults) | addressed | §3.3; provider docs confirm TCP/5/2/1/5 |
Fable P3 (MaxCapacity = 0) |
addressed | §4.3 UNCONFIRMED, §8 stand-in; API doc still only documents MinCapacity 0 for ECS (https://docs.aws.amazon.com/autoscaling/application/APIReference/API_RegisterScalableTarget.html) |
| Fable P3 (sync deploy vs state==live) | addressed | §4.1 check 4 |
| Fable P3 (execution role SSM/KMS) | addressed | §4.3, §4.4 |
Fable P3 (ROLLBACK_SUCCEEDED) |
addressed | §4.1, §4.8 #8b, §4.10 |
Blocking issues (only if REJECT)¶
None.
Notes (non-blocking)¶
- P2 — §4.9 step 5 and §4.11 "Weighted pair at (100, 0)": the
removedblock will produce noforgetaction, so a gate that requires one cannot pass. After the batch, the simple record's refresh callsfindResourceRecordSetByFourPartKeywhose filter requiresrecordSetID == SetIdentifier;""≠"apprunner", so the record is not found and the Read doesd.SetId("")(https://raw.githubusercontent.com/hashicorp/terraform-provider-aws/main/internal/service/route53/record.go). Terraform'splanForgetthen returnsNoOpwhen the prior object is null (https://raw.githubusercontent.com/hashicorp/terraform/main/internal/terraform/node_resource_abstract_instance.go). The same holds for the two weighted records in the reverse. Consequence:check --phase import --forgotten …fails with "does not forget it", the runbook forbids any M5a apply until it passes, and 7b (an M5a-root plan) is stuck at (100, 0): safe, all traffic on App Runner, DNS revert batch still available. Edit: the extended import gate accepts, for exactly the manifest-named addresses, eitherforgetor ano-opwith null before/after together with aresource_driftdelete entry, and asserts the address is absent from state after apply (check --state); keep theremovedblock for the-refresh=falsecase. Add it to the §8 must-fail/must-pass list. - P2 — §4.9 "No custom domain" /
new_host: the record fornew_hostis never generated. §4.3 and §4.8 #3 create a certificate and listener fornew_hostbut noaws_route53_recordpointing it at the ALB, so "give clients the new URL" has no URL. Edit:infra-rebuildcreates an alias A (or CNAME) fornew_hostin step 3 withallow_overwrite = false, gated on name/type/target;pre-createrequires no record of any type at that name; and reword "a zone that passes the §3.2 zone rules" since the §3.2 rule "holding a CNAME toDNSTarget" cannot apply here (exact-name, public, delegated, same account is the applicable subset). - P3 — §4.3 step 2b (adopt an unowned matching record). "In no state" means in neither ecsodus state; the record may be owned by another IaC in the account (the App Runner case is already in the M5a state via
CertificateValidationRecords). The import is harmless (prevent_destroy, M5c forgets it) but the report should say ownership is unknown and that the alternative is referencing the literal FQDN without importing. - P3 — §4.3 "Certificate", step 3.
aws_acm_certificate_validationtakes literalvalidation_record_fqdns, so there is no implicit edge to the created validation record; adddepends_onon it (the resource otherwise polls in parallel until ISSUED, create timeout 75 min, https://raw.githubusercontent.com/hashicorp/terraform-provider-aws/main/website/docs/r/acm_certificate_validation.html.markdown). - P3 — §4.8 #3 wording. After 2a the certificate (and after 2b, the M5a record) appear in the
rebuildplan asno-op; say the gate allows no-ops of exactly those addresses, since the preamble says "exactly its addresses". - P3 — §4.8 #8a. Compare
container_definitionsafter parsing the JSON (the provider normalizes it); a string compare ofoffvsoncan fail spuriously. - P3 — §3.2 drop an UNCONFIRMED.
aws_apprunner_vpc_connectorhasForceNew: trueonsecurity_groups,subnets,vpc_connector_nameand a tags-only Update (https://raw.githubusercontent.com/hashicorp/terraform-provider-aws/main/internal/service/apprunner/vpc_connector.go), so any mismatch plans a replace;checkrejects it as stated. - P3 — §3.5
ignore_changespath.image_identifiersits directly underimage_repositoryin the provider schema, sosource_configuration[0].image_repository[0].image_identifieris correct;auto_deployments_enableddefaults totrueas §3.3 says; onlyservice_nameandencryption_configurationare ForceNew, soworker-offis in place (https://raw.githubusercontent.com/hashicorp/terraform-provider-aws/main/website/docs/r/apprunner_service.html.markdown). - P3 — §4.9 recovery vs §4.11. "the old simple-record block is never applied again" is contradicted by the rollback, which re-adds and imports it; say "never applied again unless rolling back".
- P3 — checked, no change:
TERRAFORM_CONSTRAINT = "~> 1.10"(src/ecsodus/emit/terraform.py:25) supportsremovedblocks;handoff_stacksexists in the manifest (terraform.py:208,cli.py:228);docs/adr/on this checkout ends at 0015 andbuild-hatchlingexists as a branch;aws_ecs_service.task_definitionaccepts a full revision ARN and the provider keeps the ARN form in state when configured as one.