Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -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
25 changes: 15 additions & 10 deletions docs/app-yaml-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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-*` |

Expand Down
2 changes: 1 addition & 1 deletion scripts/ensure-tf-boilerplate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion scripts/resolve-team.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
2 changes: 1 addition & 1 deletion scripts/sync-registered-teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion terraform/lambda-src/ci_broker/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"deploy": f"{PROJECT}-ci-deploy-",
}

EXCLUDED_TEAMS = {"platform-owners"}
EXCLUDED_TEAMS = {"platform"}


def _get_ssm(param_name):
Expand Down
24 changes: 24 additions & 0 deletions terraform/lambda-src/team_provisioner/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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",
Expand Down
14 changes: 13 additions & 1 deletion terraform/modules/service-routing/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion terraform/platform/registered-teams.auto.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# Source of truth: GitHub teams in javaBin org

registered_teams = [
"platform-test-team",
"testteam",
]