d5bae868a4
core/env.py dual-read helper (D-108); 21 ACDL_*→NOVA_* env vars migrated across core/scripts/adapters/tests/workflows + .env/.env.secrets (key rename, values stay). G-106 binding: run_platform.sh:288-289 + regression_verify.py:309-312 dual-read (NOVA first, ACDL fallback). G-108 binding: Gitea NOVA_* secrets created via API + workflow secrets: refs updated (deploy.yml + modules-lifecycle.yml, .gitea + .github). acdl_tagging.py→nova_tagging.py (D-109 warn mode, nova:* enforced). .acdl/→.nova/ consumer path (resolver + deploy workflow + schema + tests + docs). Test fixtures updated; pytest + run_ci.sh PASS. ---ci--- project: acdl phase: 2 milestone: v1.15 status: execute ---/ci---
142 lines
5.1 KiB
Python
142 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""ACDL Phase 15 — push the consumer microservice Docker image to ECR.
|
|
|
|
Steps performed by this script:
|
|
1. Load AWS creds from /root/acdl/.env.secrets
|
|
(NOVA_AWS_ACCESS_KEY_ID, NOVA_AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION;
|
|
dual-read ACDL_* fallback until P5).
|
|
2. Create the ECR repo `acdl-microservice` if it doesn't exist
|
|
(ecr:DescribeRepositories / ecr:CreateRepository). Region: us-east-1.
|
|
3. Get the ECR login password (ecr:GetAuthorizationToken) and run
|
|
`docker login` with it.
|
|
|
|
After this script runs, it prints the docker `tag` and `push` commands
|
|
for the caller to run in the shell (steps 4-5 of T-15.1).
|
|
|
|
Usage:
|
|
python3 scripts/push_consumer_image.py
|
|
|
|
Constraints (T-15.1): the `aws` CLI is NOT installed — boto3 is used for
|
|
every AWS API call. `docker` is invoked via subprocess for the login
|
|
(since docker is the only thing that can use the auth token meaningfully).
|
|
"""
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import pathlib
|
|
|
|
import boto3
|
|
|
|
# Repo root on sys.path so `from core import env` resolves to THIS package
|
|
# (avoids editable-installed third-party `core` shadow).
|
|
_REPO_ROOT = str(pathlib.Path(__file__).resolve().parent.parent)
|
|
if _REPO_ROOT not in sys.path:
|
|
sys.path.insert(0, _REPO_ROOT)
|
|
|
|
from core import env
|
|
|
|
|
|
REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
ENV_FILE = REPO_ROOT / ".env.secrets"
|
|
AWS_ACCOUNT_ID = env.get_env("AWS_ACCOUNT_ID", "581513795199")
|
|
AWS_REGION = "us-east-1"
|
|
ECR_REPO_NAME = "acdl-microservice"
|
|
IMAGE_TAG = "latest"
|
|
|
|
|
|
def _load_env(path):
|
|
"""Load NOVA_AWS_* (preferred) / ACDL_AWS_* (fallback) + AWS_DEFAULT_REGION
|
|
from a flat KEY=VALUE file (dual-read per G-106, until P5)."""
|
|
creds = {}
|
|
with open(path, "r") as fh:
|
|
for line in fh:
|
|
line = line.strip()
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
k, v = line.split("=", 1)
|
|
creds[k.strip()] = v.strip()
|
|
return creds
|
|
|
|
|
|
def main():
|
|
if not ENV_FILE.exists():
|
|
print(f"FAIL: {ENV_FILE} not found", file=sys.stderr)
|
|
return 2
|
|
|
|
creds = _load_env(ENV_FILE)
|
|
# Dual-read: NOVA_* preferred, ACDL_* fallback (G-106, removed in P5).
|
|
access_key = creds.get("NOVA_AWS_ACCESS_KEY_ID") or creds.get("ACDL_AWS_ACCESS_KEY_ID")
|
|
secret_key = creds.get("NOVA_AWS_SECRET_ACCESS_KEY") or creds.get("ACDL_AWS_SECRET_ACCESS_KEY")
|
|
region = creds.get("AWS_DEFAULT_REGION", AWS_REGION)
|
|
if not access_key or not secret_key:
|
|
print("FAIL: NOVA_AWS_ACCESS_KEY_ID / NOVA_AWS_SECRET_ACCESS_KEY missing",
|
|
file=sys.stderr)
|
|
return 2
|
|
|
|
# Export the creds for the docker subprocess (it doesn't need them, but
|
|
# keeps parity with the terraform step that runs after this).
|
|
os.environ["AWS_ACCESS_KEY_ID"] = access_key
|
|
os.environ["AWS_SECRET_ACCESS_KEY"] = secret_key
|
|
os.environ["AWS_DEFAULT_REGION"] = region
|
|
|
|
session = boto3.Session(
|
|
aws_access_key_id=access_key,
|
|
aws_secret_access_key=secret_key,
|
|
region_name=region,
|
|
)
|
|
ecr = session.client("ecr")
|
|
|
|
# Step 2: create the ECR repo if it doesn't exist.
|
|
repo_uri = None
|
|
try:
|
|
resp = ecr.describe_repositories(repositoryNames=[ECR_REPO_NAME])
|
|
repo = resp["repositories"][0]
|
|
repo_uri = repo["repositoryUri"]
|
|
print(f"ecr: repository {ECR_REPO_NAME!r} already exists -> {repo_uri}")
|
|
except ecr.exceptions.RepositoryNotFoundException:
|
|
print(f"ecr: repository {ECR_REPO_NAME!r} not found, creating...")
|
|
resp = ecr.create_repository(repositoryName=ECR_REPO_NAME)
|
|
repo = resp["repository"]
|
|
repo_uri = repo["repositoryUri"]
|
|
print(f"ecr: created repository {ECR_REPO_NAME!r} -> {repo_uri}")
|
|
except Exception as exc:
|
|
print(f"FAIL: ecr describe/create failed: {exc}", file=sys.stderr)
|
|
return 1
|
|
|
|
# Step 3: get login password + run `docker login`.
|
|
auth = ecr.get_authorization_token()
|
|
token = auth["authorizationData"][0]["authorizationToken"]
|
|
# The token is base64(USERNAME:PASSWORD); docker login wants them split.
|
|
import base64
|
|
user_pw = base64.b64decode(token).decode("utf-8")
|
|
username, password = user_pw.split(":", 1)
|
|
registry = f"{AWS_ACCOUNT_ID}.dkr.ecr.{region}.amazonaws.com"
|
|
|
|
print(f"docker: logging in to {registry} ...")
|
|
login_cmd = [
|
|
"docker", "login",
|
|
"--username", username,
|
|
"--password-stdin",
|
|
registry,
|
|
]
|
|
proc = subprocess.run(login_cmd, input=password.encode("utf-8"),
|
|
capture_output=True)
|
|
if proc.returncode != 0:
|
|
print("FAIL: docker login failed:", file=sys.stderr)
|
|
sys.stderr.write(proc.stderr.decode("utf-8", "replace"))
|
|
return 1
|
|
print("docker: login OK")
|
|
|
|
# Steps 4-5: print the tag + push commands for the caller to run.
|
|
full_tag = f"{repo_uri}:{IMAGE_TAG}"
|
|
print("")
|
|
print("=== NEXT: run these commands in the shell to tag + push ===")
|
|
print(f"docker tag acdl-microservice:latest {full_tag}")
|
|
print(f"docker push {full_tag}")
|
|
print("")
|
|
print(f"ECR_IMAGE={full_tag}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |