#!/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())