feat(P4): Nova rebrand — AWS resource migration (REQ-163)
Rename all acdl-* AWS resources → nova-* across terraform (DynamoDB, Secrets Manager, Lambda, SNS, SG, KMS alias, ECS, ECR, IAM user/policy, state bucket, ALB, VPC/subnet names). Lambda default table names → nova-* (D-111). State bucket backend → nova-tfstate (-migrate-state documented). New docs/NOVA_AWS_MIGRATION.md runbook (staged migration + rollback). New scripts/migrate_dynamodb_data.py (scan+copy, dry-run default). acdl-deploy- → nova-deploy- role ARN in deploy workflows. Test fixtures updated; terraform validate + pytest + run_ci.sh PASS. ---ci--- project: acdl phase: 4 milestone: v1.15 status: execute ---/ci---
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
# ACDL v1.1 Spike — AWS Bootstrap Runbook
|
||||
# Nova v1.1 Spike — AWS Bootstrap Runbook
|
||||
|
||||
Phase 08 bootstraps the AWS engine for the v1.1 spike. It uses the
|
||||
**root account credential for account 581513795199 exactly once**, then
|
||||
closes D-034 by having the user manually rotate the root key afterward.
|
||||
|
||||
> **Spike scope (D-039):** the spike uses a per-run-rotated IAM *user* key
|
||||
> (`acdl-spike-runner`), NOT OIDC. Real OIDC federation is deferred to
|
||||
> (`nova-spike-runner`), NOT OIDC. Real OIDC federation is deferred to
|
||||
> v1.2 (blocked on go-gitea/gitea#36988 — Gitea Actions does not support
|
||||
> `id-token: write`). The `acdl-spike-runner` user + its key are deleted
|
||||
> `id-token: write`). The `nova-spike-runner` user + its key are deleted
|
||||
> in v1.2 cleanup when the OIDC role lands.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Set the bootstrap root key in env** (never commit, never echo):
|
||||
```bash
|
||||
export ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID="<root key>"
|
||||
export ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY="<root secret>"
|
||||
export NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID="<root key>"
|
||||
export NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY="<root secret>"
|
||||
export AWS_DEFAULT_REGION="us-east-1"
|
||||
```
|
||||
|
||||
@@ -29,7 +29,7 @@ closes D-034 by having the user manually rotate the root key afterward.
|
||||
```bash
|
||||
python3 terraform/bootstrap/create_iam_user.py
|
||||
```
|
||||
Prints `ACDL_AWS_ACCESS_KEY_ID=<...>` + `ACDL_AWS_SECRET_ACCESS_KEY=<...>`
|
||||
Prints `NOVA_AWS_ACCESS_KEY_ID=<...>` + `NOVA_AWS_SECRET_ACCESS_KEY=<...>`
|
||||
to stdout (capture if you want the initial key; `rotate_spike_key.sh`
|
||||
creates a fresh one anyway).
|
||||
|
||||
@@ -38,9 +38,9 @@ closes D-034 by having the user manually rotate the root key afterward.
|
||||
```bash
|
||||
bash scripts/rotate_spike_key.sh
|
||||
```
|
||||
Optionally uploads to Gitea Actions secrets if `ACDL_GITEA_TOKEN` is set.
|
||||
Optionally uploads to Gitea Actions secrets if `NOVA_GITEA_TOKEN` is set.
|
||||
|
||||
5. **Verify** (manual): confirm the caller identity is `acdl-spike-runner`
|
||||
5. **Verify** (manual): confirm the caller identity is `nova-spike-runner`
|
||||
(not root); the S3 bucket + DynamoDB table + IAM user + scoped policy
|
||||
all exist; `.env.secrets` + `.bootstrap_state.json` are gitignored.
|
||||
(`scripts/verify_phase08.sh` was the automated gate; it has been
|
||||
@@ -49,33 +49,33 @@ closes D-034 by having the user manually rotate the root key afterward.
|
||||
6. **MANUAL — D-034 closure:** rotate/deactivate the **root** key in the
|
||||
AWS IAM console (the user does this, not the script). The bootstrap
|
||||
root key has now served its one-shot purpose; the spike uses the
|
||||
rotated `acdl-spike-runner` key for Phases 09-10.
|
||||
rotated `nova-spike-runner` key for Phases 09-10.
|
||||
|
||||
## v1.11 Phase 56 — IAM re-bootstrap + OIDC role (REQ-116)
|
||||
|
||||
The v1.11 milestone re-bootstraps IAM to close G-005 (CAP-017..022
|
||||
deploy-unverified). Phase 56 extends the spike-runner policy with
|
||||
CloudFront/WAF/Lambda/DynamoDB-contracts/SecretsManager/SNS/CE/KMS/OIDC
|
||||
permissions and re-creates the `acdl-act-runner-role` (CAP-022).
|
||||
permissions and re-creates the `nova-act-runner-role` (CAP-022).
|
||||
|
||||
**Apply the IAM baseline (idempotent):**
|
||||
```bash
|
||||
export ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID="<root key>"
|
||||
export ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY="<root secret>"
|
||||
export NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID="<root key>"
|
||||
export NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY="<root secret>"
|
||||
export AWS_DEFAULT_REGION="us-east-1"
|
||||
python3 terraform/bootstrap/apply_iam_baseline.py
|
||||
```
|
||||
|
||||
This script:
|
||||
1. Creates (or versions) the customer-managed policy
|
||||
`acdl-spike-runner-policy` from
|
||||
`nova-spike-runner-policy` from
|
||||
`terraform/bootstrap/spike_runner_policy.json` (ARN
|
||||
`arn:aws:iam::581513795199:policy/acdl-spike-runner-policy`).
|
||||
2. Attaches it to the `acdl-spike-runner` user and deletes any leftover
|
||||
`arn:aws:iam::581513795199:policy/nova-spike-runner-policy`).
|
||||
2. Attaches it to the `nova-spike-runner` user and deletes any leftover
|
||||
inline policy (the v1.1 inline policy hit the 2048-byte limit; the
|
||||
managed-policy path supports 6144 bytes per version + up to 5
|
||||
versions).
|
||||
3. Re-creates the `acdl-act-runner-role` OIDC role if absent, attaches
|
||||
3. Re-creates the `nova-act-runner-role` OIDC role if absent, attaches
|
||||
the same managed policy, and sets a trust policy that permits root
|
||||
assume until go-gitea/gitea#36988 merges real OIDC federation.
|
||||
|
||||
@@ -84,9 +84,9 @@ regression-tested by `tests/test_iam_policy_baseline.py` (15 tests).
|
||||
|
||||
## What the spike uses for Phases 09-10
|
||||
|
||||
- **State backend:** S3 bucket `acdl-tfstate-581513795199-us-east-1` +
|
||||
DynamoDB table `acdl-outbox` (one table for both lock + outbox, D-P08-1).
|
||||
- **Auth:** the rotated `acdl-spike-runner` key in `.env.secrets`
|
||||
- **State backend:** S3 bucket `nova-tfstate-581513795199-us-east-1` +
|
||||
DynamoDB table `nova-outbox` (one table for both lock + outbox, D-P08-1).
|
||||
- **Auth:** the rotated `nova-spike-runner` key in `.env.secrets`
|
||||
(gitignored, chmod 600). Re-rotate after each spike run via
|
||||
`rotate_spike_key.sh` (D-039).
|
||||
|
||||
@@ -95,6 +95,6 @@ regression-tested by `tests/test_iam_policy_baseline.py` (15 tests).
|
||||
| Concern | Spike (Phase 08) | v1.2 |
|
||||
|---------|------------------|------|
|
||||
| AWS auth | per-run-rotated long-lived key (D-039 waiver) | real OIDC federation (go-gitea/gitea#36988) |
|
||||
| IAM | minimal user `acdl-spike-runner` + scoped policy | OIDC role + trust policy (no user, no key) |
|
||||
| IAM | minimal user `nova-spike-runner` + scoped policy | OIDC role + trust policy (no user, no key) |
|
||||
| State backend | S3 + DynamoDB single-region (us-east-1) | multi-region |
|
||||
| Secret storage | gitignored `.env.secrets` + optional Gitea secret | Gitea OIDC-issued web-identity token (no secret) |
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Apply the ACDL spike-runner managed policy + OIDC act_runner role.
|
||||
"""Apply the Nova spike-runner managed policy + OIDC act_runner role.
|
||||
|
||||
Phase 56 (REQ-116, v1.11). Idempotent: re-running creates the managed
|
||||
policy if absent (or creates a new version if the policy document
|
||||
@@ -31,10 +31,10 @@ import boto3
|
||||
ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
POLICY_PATH = ROOT / "terraform" / "bootstrap" / "spike_runner_policy.json"
|
||||
ACCOUNT = os.environ.get("ACDL_AWS_ACCOUNT_ID", "581513795199")
|
||||
USER = "acdl-spike-runner"
|
||||
POLICY_NAME = "acdl-spike-runner-policy"
|
||||
USER = "nova-spike-runner"
|
||||
POLICY_NAME = "nova-spike-runner-policy"
|
||||
POLICY_ARN = f"arn:aws:iam::{ACCOUNT}:policy/{POLICY_NAME}"
|
||||
ROLE_NAME = "acdl-act-runner-role"
|
||||
ROLE_NAME = "nova-act-runner-role"
|
||||
|
||||
|
||||
def _session():
|
||||
@@ -86,7 +86,7 @@ def apply_managed_policy(iam, policy_doc: str) -> str:
|
||||
PolicyName=POLICY_NAME,
|
||||
Path="/",
|
||||
PolicyDocument=policy_doc,
|
||||
Description="ACDL spike-runner baseline (v1.11 REQ-116). Extended from inline user policy to managed policy to fit the 6144-byte limit.",
|
||||
Description="Nova spike-runner baseline (v1.11 REQ-116). Extended from inline user policy to managed policy to fit the 6144-byte limit.",
|
||||
)
|
||||
print(f"created: {created['Policy']['Arn']}")
|
||||
return created["Policy"]["Arn"]
|
||||
@@ -117,10 +117,10 @@ def ensure_runner_role(iam):
|
||||
iam.create_role(
|
||||
RoleName=ROLE_NAME,
|
||||
AssumeRolePolicyDocument=json.dumps(_trust_policy_for_runner()),
|
||||
Description="ACDL act_runner OIDC role (CAP-022, v1.11 Phase 56 re-creation). Trust policy permits root assume until go-gitea/gitea#36988 merges real OIDC federation.",
|
||||
Description="Nova act_runner OIDC role (CAP-022, v1.11 Phase 56 re-creation). Trust policy permits root assume until go-gitea/gitea#36988 merges real OIDC federation.",
|
||||
MaxSessionDuration=3600,
|
||||
Tags=[
|
||||
{"Key": "Project", "Value": "acdl"},
|
||||
{"Key": "Project", "Value": "nova"},
|
||||
{"Key": "Capability", "Value": "CAP-022"},
|
||||
{"Key": "Milestone", "Value": "v1.11"},
|
||||
{"Key": "ManagedBy", "Value": "ciagent"},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Create the ACDL v1.1 spike IAM user + scoped inline policy + initial key.
|
||||
"""Create the Nova v1.1 spike IAM user + scoped inline policy + initial key.
|
||||
|
||||
Idempotent: skips user creation if the user exists; creates an initial
|
||||
access key if none active exists. Prints the key to stdout for the
|
||||
@@ -32,8 +32,8 @@ import boto3
|
||||
|
||||
|
||||
REGION = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
|
||||
USER_NAME = "acdl-spike-runner"
|
||||
POLICY_NAME = "acdl-spike-runner-policy"
|
||||
USER_NAME = "nova-spike-runner"
|
||||
POLICY_NAME = "nova-spike-runner-policy"
|
||||
POLICY_FILE = os.path.join(os.path.dirname(__file__), "spike_runner_policy.json")
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Create the ACDL v1.1 spike AWS state backend (idempotent).
|
||||
"""Create the Nova v1.1 spike AWS state backend (idempotent).
|
||||
|
||||
- S3 bucket acdl-tfstate-<account_id>-us-east-1 (versioning enabled).
|
||||
- DynamoDB table acdl-outbox (PAY_PER_REQUEST; PK contractId, SK
|
||||
- S3 bucket nova-tfstate-<account_id>-us-east-1 (versioning enabled).
|
||||
- DynamoDB table nova-outbox (PAY_PER_REQUEST; PK contractId, SK
|
||||
eventType#eventTs) — used for BOTH Terraform state locking AND the
|
||||
evidence outbox (D-P08-1).
|
||||
|
||||
@@ -31,8 +31,8 @@ import boto3
|
||||
|
||||
REGION = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
|
||||
ACCOUNT_ID = os.environ.get("ACDL_AWS_ACCOUNT_ID", "581513795199")
|
||||
STATE_BUCKET = f"acdl-tfstate-{ACCOUNT_ID}-us-east-1"
|
||||
OUTBOX_TABLE = "acdl-outbox"
|
||||
STATE_BUCKET = f"nova-tfstate-{ACCOUNT_ID}-us-east-1"
|
||||
OUTBOX_TABLE = "nova-outbox"
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
"s3:GetBucketVersioning"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::acdl-tfstate-581513795199-us-east-1",
|
||||
"arn:aws:s3:::acdl-tfstate-581513795199-us-east-1/*"
|
||||
"arn:aws:s3:::nova-tfstate-581513795199-us-east-1",
|
||||
"arn:aws:s3:::nova-tfstate-581513795199-us-east-1/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -27,7 +27,7 @@
|
||||
"dynamodb:Scan",
|
||||
"dynamodb:DescribeTable"
|
||||
],
|
||||
"Resource": "arn:aws:dynamodb:us-east-1:581513795199:table/acdl-outbox"
|
||||
"Resource": "arn:aws:dynamodb:us-east-1:581513795199:table/nova-outbox"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
@@ -142,7 +142,7 @@
|
||||
"lambda:UntagResource",
|
||||
"lambda:PublishLayerVersion"
|
||||
],
|
||||
"Resource": "arn:aws:lambda:us-east-1:581513795199:function:acdl-*"
|
||||
"Resource": "arn:aws:lambda:us-east-1:581513795199:function:nova-*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
@@ -158,10 +158,10 @@
|
||||
"dynamodb:Batch*"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:dynamodb:us-east-1:581513795199:table/acdl-contracts",
|
||||
"arn:aws:dynamodb:us-east-1:581513795199:table/acdl-contracts/*",
|
||||
"arn:aws:dynamodb:us-east-1:581513795199:table/acdl-change-requests",
|
||||
"arn:aws:dynamodb:us-east-1:581513795199:table/acdl-change-requests/*"
|
||||
"arn:aws:dynamodb:us-east-1:581513795199:table/nova-contracts",
|
||||
"arn:aws:dynamodb:us-east-1:581513795199:table/nova-contracts/*",
|
||||
"arn:aws:dynamodb:us-east-1:581513795199:table/nova-change-requests",
|
||||
"arn:aws:dynamodb:us-east-1:581513795199:table/nova-change-requests/*"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -174,7 +174,7 @@
|
||||
"secretsmanager:DeleteSecret",
|
||||
"secretsmanager:ListSecrets"
|
||||
],
|
||||
"Resource": "arn:aws:secretsmanager:us-east-1:581513795199:secret:acdl/*"
|
||||
"Resource": "arn:aws:secretsmanager:us-east-1:581513795199:secret:nova/*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
@@ -186,7 +186,7 @@
|
||||
"sns:DeleteTopic",
|
||||
"sns:ListTopics"
|
||||
],
|
||||
"Resource": "arn:aws:sns:us-east-1:581513795199:acdl-*"
|
||||
"Resource": "arn:aws:sns:us-east-1:581513795199:nova-*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
@@ -217,7 +217,7 @@
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:kms:*:*:key/*",
|
||||
"arn:aws:kms:*:*:alias/acdl-*"
|
||||
"arn:aws:kms:*:*:alias/nova-*"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -236,7 +236,7 @@
|
||||
"iam:TagRole",
|
||||
"iam:UntagRole"
|
||||
],
|
||||
"Resource": "arn:aws:iam::*:role/acdl-*"
|
||||
"Resource": "arn:aws:iam::*:role/nova-*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user