From 37fceb546405f9ebe91689a9112e269b4cd788b3 Mon Sep 17 00:00:00 2001 From: Alexander Amiri Date: Wed, 18 Mar 2026 00:09:10 +0100 Subject: [PATCH] Enforce team name constraints and fix TG name overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add team name validation: lowercase letters only (a-z), max 12 chars - Rename excluded team: platform-owners → platform - Rename registered team: platform-test-team → testteam - Add TG name truncation+hash for names exceeding 32-char AWS limit - Update docs with {team}-{name} naming convention --- .github/CODEOWNERS | 8 +++--- docs/app-yaml-reference.md | 25 +++++++++++-------- scripts/ensure-tf-boilerplate.sh | 2 +- scripts/resolve-team.sh | 2 +- scripts/sync-registered-teams.py | 2 +- terraform/lambda-src/ci_broker/handler.py | 2 +- .../lambda-src/team_provisioner/handler.py | 24 ++++++++++++++++++ terraform/modules/service-routing/main.tf | 14 ++++++++++- .../platform/registered-teams.auto.tfvars | 2 +- 9 files changed, 61 insertions(+), 20 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ca55169..f1f0412 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -* @javaBin/platform-owners -terraform/org/ @javaBin/platform-owners -terraform/platform/iam/ @javaBin/platform-owners -terraform/platform/monitoring/ @javaBin/platform-owners +* @javaBin/platform +terraform/org/ @javaBin/platform +terraform/platform/iam/ @javaBin/platform +terraform/platform/monitoring/ @javaBin/platform diff --git a/docs/app-yaml-reference.md b/docs/app-yaml-reference.md index 17550fd..45b1b3a 100644 --- a/docs/app-yaml-reference.md +++ b/docs/app-yaml-reference.md @@ -65,11 +65,13 @@ Used for: ECS service name, ECR repo name, IAM role names, CloudWatch log groups Team that owns this service. Must match a file in `javaBin/registry/teams/`. +Team names must be **lowercase letters only** (a-z), no hyphens, digits, or symbols. Max 12 characters. This constraint ensures resource names fit within AWS limits (e.g. ALB target groups: 32 chars). + ```yaml team: core ``` -Used for: resource tagging, budget allocation, access control. +Used for: resource name prefix (`{team}-{service}`), ABAC tagging, budget allocation, access control. ### compute @@ -361,17 +363,20 @@ Generated files have a `# GENERATED FROM app.yaml` marker. The script only overw ## Naming Conventions +All app resources are prefixed with the team name for ABAC enforcement. + | Resource | Name Pattern | |----------|-------------| -| ECS service | `{name}` | -| ECR repo | `{name}` | -| S3 bucket | `javabin-{bucket_name}-{account_id}` | -| DynamoDB table | `javabin-{table_name}` | -| SQS queue | `javabin-{queue_name}` | -| RDS instance | `{db_name}` (identifier) | -| SSM (secrets) | `/javabin/apps/{service}/{secret_name}` | -| IAM task role | `javabin-{name}` | -| CloudWatch logs | `/ecs/javabin/{name}` | +| ECS service | `{team}-{name}` | +| ECR repo | `{team}-{name}` | +| ALB target group | `{team}-{name}` (max 32 chars, truncated+hashed if over) | +| S3 bucket | `{team}-{name}-{account_id}` | +| DynamoDB table | `{team}-{name}` | +| SQS queue | `{team}-{name}` | +| RDS instance | `{team}-{name}` | +| SSM (secrets) | `/javabin/apps/{team}/{service}/{secret_name}` | +| IAM task role | `{team}-{name}` | +| CloudWatch logs | `/ecs/{team}/{name}` | | DNS record | `{routing.host}` | | SSM (Cognito) | `/javabin/platform-apps/{name}/cognito-*` | diff --git a/scripts/ensure-tf-boilerplate.sh b/scripts/ensure-tf-boilerplate.sh index 3a0bff1..1636911 100755 --- a/scripts/ensure-tf-boilerplate.sh +++ b/scripts/ensure-tf-boilerplate.sh @@ -26,7 +26,7 @@ PROJECT="javabin" TEAM="" if command -v gh >/dev/null 2>&1; then TEAM=$(gh api "/repos/${GITHUB_REPOSITORY}/teams" \ - --jq '[.[] | select(.slug != "platform-owners")] | .[0].slug // ""' 2>/dev/null || true) + --jq '[.[] | select(.slug != "platform")] | .[0].slug // ""' 2>/dev/null || true) fi [ -z "$TEAM" ] && TEAM="unknown" diff --git a/scripts/resolve-team.sh b/scripts/resolve-team.sh index a780121..1f37a27 100644 --- a/scripts/resolve-team.sh +++ b/scripts/resolve-team.sh @@ -14,7 +14,7 @@ set -e REPO="${GITHUB_REPOSITORY:?GITHUB_REPOSITORY required}" TEAM=$(gh api "/repos/${REPO}/teams" \ - --jq '[.[] | select(.slug != "platform-owners")] | .[0].slug // empty' 2>/dev/null || true) + --jq '[.[] | select(.slug != "platform")] | .[0].slug // empty' 2>/dev/null || true) if [ -z "$TEAM" ]; then echo "ERROR: repo ${REPO} does not belong to any GitHub team." diff --git a/scripts/sync-registered-teams.py b/scripts/sync-registered-teams.py index 5b603d1..d1c608f 100644 --- a/scripts/sync-registered-teams.py +++ b/scripts/sync-registered-teams.py @@ -24,7 +24,7 @@ GITHUB_API = "https://api.github.com" # Platform-internal teams that don't get app CI roles -EXCLUDED_TEAMS = {"platform-owners"} +EXCLUDED_TEAMS = {"platform"} def github_get(path, token): diff --git a/terraform/lambda-src/ci_broker/handler.py b/terraform/lambda-src/ci_broker/handler.py index e8a4a2d..428bcd8 100644 --- a/terraform/lambda-src/ci_broker/handler.py +++ b/terraform/lambda-src/ci_broker/handler.py @@ -42,7 +42,7 @@ "deploy": f"{PROJECT}-ci-deploy-", } -EXCLUDED_TEAMS = {"platform-owners"} +EXCLUDED_TEAMS = {"platform"} def _get_ssm(param_name): diff --git a/terraform/lambda-src/team_provisioner/handler.py b/terraform/lambda-src/team_provisioner/handler.py index f8fccdc..a672e3a 100644 --- a/terraform/lambda-src/team_provisioner/handler.py +++ b/terraform/lambda-src/team_provisioner/handler.py @@ -65,6 +65,23 @@ ) BUDGET_ENFORCEMENT_TOPIC_ARN = os.environ.get("BUDGET_ENFORCEMENT_TOPIC_ARN", "") +# Team name constraints: lowercase letters only (a-z), max 12 characters. +# This keeps resource names within AWS limits (ALB TG: 32 chars for {team}-{service}). +import re +_TEAM_NAME_RE = re.compile(r"^[a-z]{1,12}$") + + +def _validate_team_name(name): + """Return True if the team name meets naming constraints.""" + if not _TEAM_NAME_RE.match(name): + logger.warning( + "Invalid team name '%s' — must be 1-12 lowercase letters (a-z), no hyphens/digits/symbols", + name, + ) + return False + return True + + # javaBin horizontal white logo PNG — loaded from the Lambda zip at cold start. # CID-embedded in welcome emails to avoid "trust this email" prompts in Outlook. # Source file: terraform/lambda-src/team_provisioner/javabin-logo-white.png @@ -1892,6 +1909,13 @@ def handler(event, context): ) continue + if not _validate_team_name(team_name): + results[team_name] = { + "error": "invalid team name — must be 1-12 lowercase letters only (a-z), " + "no hyphens, digits, or special characters" + } + continue + if not team.get("description"): logger.warning( "Team %s missing required 'description' — skipping", diff --git a/terraform/modules/service-routing/main.tf b/terraform/modules/service-routing/main.tf index 39dd2a5..4b56c87 100644 --- a/terraform/modules/service-routing/main.tf +++ b/terraform/modules/service-routing/main.tf @@ -2,8 +2,20 @@ # ALB Target Group ################################################################################ +locals { + # ALB target group names are limited to 32 characters. + # Use full name if it fits, otherwise truncate and append a 6-char hash suffix + # to avoid collisions. Other resources don't have this limit. + full_name = "${var.team}-${var.name}" + tg_name = ( + length(local.full_name) <= 32 + ? local.full_name + : "${substr(local.full_name, 0, 25)}-${substr(sha256(local.full_name), 0, 6)}" + ) +} + resource "aws_lb_target_group" "this" { - name = "${var.team}-${var.name}" + name = local.tg_name port = var.port protocol = "HTTP" vpc_id = var.vpc_id diff --git a/terraform/platform/registered-teams.auto.tfvars b/terraform/platform/registered-teams.auto.tfvars index 875054a..bcc0487 100644 --- a/terraform/platform/registered-teams.auto.tfvars +++ b/terraform/platform/registered-teams.auto.tfvars @@ -2,5 +2,5 @@ # Source of truth: GitHub teams in javaBin org registered_teams = [ - "platform-test-team", + "testteam", ]