Coverage for gco/stacks/constants.py: 92.45%
129 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-30 21:22 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-30 21:22 +0000
1"""Pinned version constants for GCO infrastructure.
3Single source of truth for all version-pinned infrastructure components.
4Centralising these makes it easy to:
61. See every pinned version at a glance
72. Update versions in one place
83. Let the dependency scanner (`.github/scripts/dependency-scan.sh`)
9 find them with a simple import instead of regex scraping
104. Write tests that assert versions haven't drifted
12When updating a version here, also check:
13- ``lambda/helm-installer/charts.yaml`` for Helm chart versions
14- ``requirements-lock.txt`` for Python dependency versions
15- ``cdk.json`` context for ``kubernetes_version``
17The dependency scanner runs monthly and opens an issue when any of
18these fall behind the latest available release.
19"""
21from __future__ import annotations
23from collections.abc import Collection, Mapping
24from functools import lru_cache
25from types import MappingProxyType
27# ---------------------------------------------------------------------------
28# Lambda Runtimes
29# ---------------------------------------------------------------------------
30# Keep every Lambda language runtime here rather than spelling enum members in
31# individual stacks. The monthly dependency scan compares these constants with
32# the newest managed runtimes exposed by aws-cdk-lib and checks the Node major
33# against .nvmrc, package.json, and Dockerfile.dev.
34LAMBDA_PYTHON_RUNTIME = "PYTHON_3_14"
35"""CDK enum name for Python Lambdas (``lambda_.Runtime.PYTHON_3_14``)."""
37LAMBDA_NODEJS_RUNTIME = "NODEJS_24_X"
38"""CDK enum name for Node.js Lambdas (``lambda_.Runtime.NODEJS_24_X``)."""
41# ---------------------------------------------------------------------------
42# Deployment Region Contract
43# ---------------------------------------------------------------------------
44@lru_cache(maxsize=1)
45def cloudformation_region_partitions() -> Mapping[str, str]:
46 """Return immutable SDK-known CloudFormation Region-to-partition metadata.
48 Botocore endpoint metadata covers every AWS partition and requires neither
49 credentials nor a network request. Keeping this dynamic avoids a project
50 allowlist that would reject opt-in, sovereign, or newly supported Regions
51 already known to the installed SDK.
52 """
53 import boto3
55 session = boto3.Session()
56 region_partitions: dict[str, str] = {}
57 for partition in session.get_available_partitions():
58 for region in session.get_available_regions(
59 "cloudformation",
60 partition_name=partition,
61 ):
62 recorded_partition = region_partitions.setdefault(region, partition)
63 if recorded_partition != partition: 63 ↛ 64line 63 didn't jump to line 64 because the condition on line 63 was never true
64 raise RuntimeError(
65 "AWS SDK endpoint metadata assigns CloudFormation region "
66 f"{region!r} to both {recorded_partition!r} and {partition!r}"
67 )
68 if not region_partitions: 68 ↛ 69line 68 didn't jump to line 69 because the condition on line 68 was never true
69 raise RuntimeError("AWS SDK endpoint metadata contains no CloudFormation regions")
70 return MappingProxyType(region_partitions)
73@lru_cache(maxsize=1)
74def known_cloudformation_regions() -> frozenset[str]:
75 """Return every AWS SDK-known Region that exposes CloudFormation."""
76 return frozenset(cloudformation_region_partitions())
79def validated_deployment_partition(
80 regions: Collection[object],
81 *,
82 region_partitions: Mapping[str, str] | None = None,
83) -> str:
84 """Require a deployment topology to resolve to exactly one AWS partition.
86 A single credentials/account context and this application's cross-stack
87 references cannot span commercial, China, GovCloud, or ISO partitions.
88 Region count remains deliberately unlimited within the selected partition.
89 """
90 if not regions: 90 ↛ 91line 90 didn't jump to line 91 because the condition on line 90 was never true
91 raise ValueError("At least one deployment region must be specified")
93 metadata = (
94 cloudformation_region_partitions() if region_partitions is None else region_partitions
95 )
96 if not metadata: 96 ↛ 97line 96 didn't jump to line 97 because the condition on line 96 was never true
97 raise RuntimeError("AWS SDK endpoint metadata contains no CloudFormation regions")
99 regions_by_partition: dict[str, list[str]] = {}
100 for region in regions:
101 if not isinstance(region, str) or region not in metadata: 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true
102 raise ValueError(
103 f"Invalid region {region!r}; expected an AWS region with a "
104 "CloudFormation endpoint known to the installed SDK"
105 )
106 partition = metadata[region]
107 regions_by_partition.setdefault(partition, []).append(region)
109 if len(regions_by_partition) != 1:
110 details = "; ".join(
111 f"{partition}: {', '.join(sorted(partition_regions))}"
112 for partition, partition_regions in sorted(regions_by_partition.items())
113 )
114 raise ValueError(
115 f"Deployment regions must all belong to a single AWS partition; found {details}"
116 )
117 return next(iter(regions_by_partition))
120def validated_regional_deployment_regions(
121 value: object,
122 *,
123 known_regions: Collection[str] | None = None,
124) -> tuple[str, ...]:
125 """Return a non-empty, unique list of SDK-known workload Regions.
127 There is deliberately no project-specific allowlist or maximum count. The
128 optional ``known_regions`` argument lets callers reuse endpoint metadata
129 they have already loaded while preserving this one validation contract.
130 """
131 if not isinstance(value, list) or not value:
132 raise ValueError("At least one region must be specified")
134 regions: list[str] = []
135 valid_regions = (
136 known_cloudformation_regions() if known_regions is None else frozenset(known_regions)
137 )
138 if not valid_regions: 138 ↛ 139line 138 didn't jump to line 139 because the condition on line 138 was never true
139 raise RuntimeError("AWS SDK endpoint metadata contains no CloudFormation regions")
141 for region in value:
142 if not isinstance(region, str) or region not in valid_regions:
143 raise ValueError(
144 f"Invalid region {region!r}; expected an AWS region with a "
145 "CloudFormation endpoint known to the installed SDK"
146 )
147 regions.append(region)
148 if len(regions) != len(set(regions)):
149 raise ValueError("Duplicate regions found in configuration")
150 return tuple(regions)
153# ---------------------------------------------------------------------------
154# HTTP Request Body Limits
155# ---------------------------------------------------------------------------
156DEFAULT_MAX_REQUEST_BODY_BYTES = 1_048_576
157"""Default hard cap shared by API ingress and in-cluster request middleware."""
159MAX_CONFIGURABLE_REQUEST_BODY_BYTES = 10 * 1024 * 1024
160"""Largest supported cap; matches API Gateway's request payload ceiling."""
162# The cross-region aggregator has a deliberately read-mostly regional contract.
163# Keep both its identity policy and each regional API resource policy generated
164# from this exact allowlist so a compromised aggregator cannot reach unrelated
165# control-plane mutations exposed by the regional greedy route.
166AGGREGATOR_REGIONAL_API_ROUTES = (
167 ("GET", "api/v1/jobs"),
168 ("DELETE", "api/v1/jobs"),
169 ("GET", "api/v1/health"),
170 ("GET", "api/v1/status"),
171)
174def validated_request_body_limit(value: object) -> int:
175 """Return a safe request-body limit or reject an inconsistent deployment."""
176 if type(value) is not int or value < 1 or value > MAX_CONFIGURABLE_REQUEST_BODY_BYTES:
177 raise ValueError(
178 "max_request_body_bytes must be an integer between 1 and "
179 f"{MAX_CONFIGURABLE_REQUEST_BODY_BYTES}"
180 )
181 return value
184# ---------------------------------------------------------------------------
185# API Gateway Auth Secret
186# ---------------------------------------------------------------------------
187# Physical name of the Secrets Manager secret that holds the rotating HMAC
188# signing key used by trusted API Gateway proxy Lambdas. It is created by
189# ``GCOApiGatewayGlobalStack`` (in the ``api_gateway`` region) and read by the
190# regional service-account role and regional API proxy Lambda. The historical
191# ``api-gateway-auth-token`` suffix is retained to avoid replacing deployments.
194def api_gateway_auth_secret_name(project_name: str) -> str:
195 """Secrets Manager name for the proxy-to-backend HMAC signing key.
197 Derived from ``project_name`` (``<project_name>/api-gateway-auth-token``)
198 so two deployments in the same account+region do not collide on the secret
199 name. For the default ``project_name="gco"`` this renders
200 ``gco/api-gateway-auth-token`` — byte-for-byte identical to the pre-#139
201 literal, so existing deployments see no resource replacement.
203 Single source of truth shared by three call sites that must agree exactly:
205 1. ``GCOApiGatewayGlobalStack._create_secret`` — the ``secret_name`` the
206 secret is actually created with.
207 2. ``GCORegionalStack`` — the deterministic IAM ``Resource`` ARN granting
208 the service-account role read access to the secret. Built from this
209 name plus the API Gateway region and account so it renders identically
210 whether the API Gateway stack is cross-region or co-located with the
211 regional stack (see issue #125 — a synthesis-time cross-stack export
212 token used to leak into the ARN and dodge the cdk-nag suppression in
213 single-region topologies).
214 3. ``gco.stacks.nag_suppressions.add_iam_suppressions`` — the
215 ``AwsSolutions-IAM5`` acknowledgment scoped to this exact ARN.
217 Keep the three call sites in lockstep by calling this helper with the
218 stack's ``project_name`` rather than re-typing the name.
219 """
220 return f"{project_name}/api-gateway-auth-token" # nosec B105 — secret path/name, not a credential
223def cross_region_aggregator_role_name(project_name: str) -> str:
224 """IAM role name used by regional API resource-policy principals.
226 IAM roles are global within an account, and the role ARN is embedded in
227 API Gateway resource policies synthesized in other regions. A deterministic
228 project-scoped physical name avoids an unsupported cross-region
229 CloudFormation export. ``project_name`` is validated at 31 characters, so
230 this 24-character suffix keeps the result below IAM's 64-character limit.
231 """
232 return f"{project_name}-cross-region-aggregator"
235# ---------------------------------------------------------------------------
236# Backend TLS private PKI
237# ---------------------------------------------------------------------------
240def backend_tls_server_name(project_name: str) -> str:
241 """Private certificate identity asserted by every backend TLS client.
243 The name deliberately does not need public DNS. Proxy clients connect to
244 Global Accelerator or an internal ALB's real DNS name while sending this
245 value as SNI and verifying it against the deployment-local root CA.
246 """
247 return f"backend.{project_name}.gco.internal"
250def backend_tls_root_secret_name(project_name: str) -> str:
251 """Secrets Manager name containing the deployment-local root private key."""
252 return f"{project_name}/backend-tls/root-ca"
255def backend_tls_root_ca_parameter_name(project_name: str) -> str:
256 """SSM parameter containing only the public root trust bundle."""
257 return f"/{project_name}/backend-tls/root-ca.pem"
260def backend_tls_certificate_parameter_prefix(project_name: str) -> str:
261 """SSM prefix under which regional imported-certificate ARNs are stored."""
262 return f"/{project_name}/backend-tls/certificate-arn/"
265def backend_tls_certificate_arn_parameter_name(project_name: str, region: str) -> str:
266 """SSM parameter containing one region's stable imported ACM ARN."""
267 return f"{backend_tls_certificate_parameter_prefix(project_name)}{region}"
270# ---------------------------------------------------------------------------
271# EKS Add-on Versions
272# ---------------------------------------------------------------------------
273# Pinned to specific eksbuild versions for reproducible deployments.
274# The dependency scanner checks ``aws eks describe-addon-versions`` monthly
275# and opens an issue when newer builds are available.
277EKS_ADDON_POD_IDENTITY_AGENT = "v1.3.10-eksbuild.3"
278"""EKS Pod Identity Agent — enables IRSA and Pod Identity for service accounts."""
280EKS_ADDON_METRICS_SERVER = "v0.9.0-eksbuild.4"
281"""Kubernetes Metrics Server — provides CPU/memory metrics for HPA and ``kubectl top``."""
283EKS_ADDON_EFS_CSI_DRIVER = "v3.4.1-eksbuild.1"
284"""Amazon EFS CSI Driver — mounts EFS file systems as Kubernetes persistent volumes."""
286EKS_ADDON_CLOUDWATCH_OBSERVABILITY = "v6.4.0-eksbuild.1"
287"""Amazon CloudWatch Observability — Container Insights, Prometheus metrics, FluentBit logs."""
289EKS_ADDON_FSX_CSI_DRIVER = "v1.9.0-eksbuild.1"
290"""Amazon FSx CSI Driver — mounts FSx for Lustre file systems as Kubernetes persistent volumes."""
292# ---------------------------------------------------------------------------
293# EKS Cluster Subnet Constraints
294# ---------------------------------------------------------------------------
295# A few Availability Zones cannot host the subnets you pass when creating an
296# EKS cluster (the control-plane elastic network interfaces). EKS rejects
297# cluster creation if any supplied subnet is in one of these zones. The
298# constraint is published by *Availability Zone ID* (e.g. ``use1-az3``), which
299# is stable across accounts — unlike the AZ *name* (``us-east-1e``), which AWS
300# randomizes per account. Match by ID, then resolve to this account's names.
301# Source: https://docs.aws.amazon.com/eks/latest/userguide/network-reqs.html
302# ("Subnet requirements for clusters" — disallowed Availability Zone IDs).
304EKS_UNSUPPORTED_AZ_IDS: dict[str, tuple[str, ...]] = {
305 "us-east-1": ("use1-az3",),
306 "us-west-1": ("usw1-az2",),
307 "ca-central-1": ("cac1-az3",),
308}
309"""AWS-region → Availability Zone IDs that cannot hold EKS cluster subnets.
311The regional VPC deliberately spans every AZ in the region (one public + one
312private subnet each), but the EKS cluster's control-plane subnet selection must
313exclude any subnet in these zones or ``CreateCluster`` fails with
314``InvalidParameterException``. Regions absent from this map have no such
315restriction. Keep in sync with the AWS EKS networking requirements doc.
316"""
318# ---------------------------------------------------------------------------
319# Aurora PostgreSQL Engine Version
320# ---------------------------------------------------------------------------
321# Pinned to a specific minor version. The dependency scanner checks
322# ``aws rds describe-db-engine-versions`` monthly for newer releases
323# within the same major line.
325AURORA_POSTGRES_VERSION = "VER_17_9"
326"""CDK enum name for the Aurora PostgreSQL engine version (e.g. ``rds.AuroraPostgresEngineVersion.VER_17_9``)."""
328AURORA_POSTGRES_VERSION_DISPLAY = "17.9"
329"""Human-readable version string for documentation and logging."""
330# ---------------------------------------------------------------------------
331# Analytics Environment Constants
332# ---------------------------------------------------------------------------
333# Pinned values consumed by the optional analytics environment (SageMaker
334# Studio, EMR Serverless, Cognito hosted UI, and the always-on
335# Cluster_Shared_Bucket in ``GCOGlobalStack``). Keeping them here lets the
336# analytics stack, the regional stack, the global stack, and the tests import
337# from a single source of truth.
339EMR_SERVERLESS_RELEASE_LABEL = "emr-7.13.0"
340"""EMR Serverless Spark release label used for ``emrserverless.CfnApplication``.
342Pinned to a stable Spark release so analytics workloads get a reproducible
343runtime across deployments. Update alongside the EKS add-ons above when a
344newer EMR release is validated against the studio notebooks.
345"""
347SAGEMAKER_ROLE_NAME_PREFIX = "AmazonSageMaker"
348"""Required prefix for the SageMaker Studio execution role name.
350Amazon SageMaker requires execution roles used by Studio domains to have a
351name that starts with ``AmazonSageMaker`` so that AWS-managed policies and
352service-linked trust relationships resolve correctly. Any role name generated
353for ``SageMaker_Execution_Role`` must begin with this prefix.
354"""
357def cognito_domain_prefix_default(project_name: str) -> str:
358 """Default prefix for the Cognito hosted-UI domain.
360 Derived from ``project_name`` (``<project_name>-studio``). The full domain
361 prefix is assembled at synth time by appending the account id (e.g.
362 ``gco-studio-123456789012``) so it stays globally unique within
363 ``cognito.UserPoolDomain``. Operators may override the prefix through the
364 ``analytics_environment.cognito.domain_prefix`` field in ``cdk.json``.
366 For ``project_name="gco"`` this renders ``gco-studio`` — identical to the
367 pre-#139 literal.
368 """
369 return f"{project_name}-studio"
372STUDIO_PRESIGNED_URL_EXPIRY_SECONDS = 300
373"""Default expiry (in seconds) for SageMaker Studio presigned domain URLs.
375Five minutes matches the shortest window accepted by
376``CreatePresignedDomainUrl`` while still giving a user enough time to click
377the link after the ``/studio/login`` Lambda returns it. The presigned-URL
378Lambda reads this through the ``URL_EXPIRES_SECONDS`` environment variable
379and callers may override it per-request.
380"""
383def cluster_shared_bucket_name_prefix(project_name: str) -> str:
384 """Name prefix for the always-on ``Cluster_Shared_Bucket`` in ``GCOGlobalStack``.
386 Derived from ``project_name``. The full bucket name is
387 ``<project_name>-cluster-shared-<account>-<global-region>``. The prefix is
388 what IAM policies and cdk-nag allow-list assertions scope against, so both
389 the bucket and the assertions must be built from the same ``project_name``.
390 For ``project_name="gco"`` this renders ``gco-cluster-shared`` — identical
391 to the pre-#139 literal.
392 """
393 return f"{project_name}-cluster-shared"
396def cluster_shared_ssm_parameter_prefix(project_name: str) -> str:
397 """SSM parameter namespace for the cluster-shared bucket metadata.
399 Derived from ``project_name`` (``/<project_name>/cluster-shared-bucket``).
400 ``GCOGlobalStack`` writes ``<prefix>/name``, ``<prefix>/arn``, and
401 ``<prefix>/region`` under this path; ``GCORegionalStack`` (always) and
402 ``GCOAnalyticsStack`` (when enabled) read them back via
403 ``cr.AwsCustomResource`` against the global region. Treat the full paths as
404 the contract. For ``project_name="gco"`` this renders
405 ``/gco/cluster-shared-bucket``.
406 """
407 return f"/{project_name}/cluster-shared-bucket"
410def regional_shared_bucket_name_prefix(project_name: str) -> str:
411 """Name prefix for the always-on general-purpose regional bucket.
413 Derived from ``project_name``. The full bucket name is
414 ``<project_name>-regional-shared-<account>-<region>``. Each
415 ``GCORegionalStack`` provisions exactly one such bucket per region,
416 unconditionally — there is no ``cdk.json`` toggle and no feature flag
417 gating its existence. It is general purpose (usable by any in-region
418 workload) and is in addition to the always-on central buckets owned by
419 ``GCOGlobalStack`` (the model bucket and the cluster-shared bucket). The
420 prefix is what IAM policies and cdk-nag allow-list assertions scope
421 against. For ``project_name="gco"`` this renders ``gco-regional-shared`` —
422 identical to the pre-#139 literal.
423 """
424 return f"{project_name}-regional-shared"
427def regional_shared_ssm_parameter_prefix(project_name: str) -> str:
428 """SSM parameter namespace for the regional general-purpose bucket metadata.
430 Derived from ``project_name`` (``/<project_name>/regional-shared-bucket``).
431 Each ``GCORegionalStack`` writes ``<prefix>/name``, ``<prefix>/arn``, and
432 ``<prefix>/region`` under this path **in its own region's** parameter
433 store, exactly as the model bucket and cluster-shared bucket publish
434 theirs. In-region workloads (and the regional upload surface) read them
435 back to resolve the always-on regional bucket without hardcoding
436 account/region into the name.
438 The per-region inference monitor builds the same path at runtime from its
439 injected ``PROJECT_NAME`` environment variable rather than importing this
440 helper (it needs no CDK imports at runtime), so keep the two in lockstep.
441 For ``project_name="gco"`` this renders ``/gco/regional-shared-bucket``.
442 """
443 return f"/{project_name}/regional-shared-bucket"
446MOONCAKE_COLD_TIER_KEY_PREFIX = "mooncake-kv"
447"""Object-key prefix for Mooncake cold-tier KV objects in the regional bucket.
449The per-region inference monitor resolves an endpoint's cold-tier object-store
450URI to ``s3://gco-regional-shared-<account>-<region>/mooncake-kv/<endpoint>/``,
451and the ``gco inference populate-kv`` upload surface writes under the same
452prefix, so operator-supplied warm-up objects land exactly where an endpoint's
453pods read them. This is the shared contract between the two sides; the monitor
454keeps a local copy of this value so it needs no CDK imports at runtime, so keep
455the two in lockstep if the prefix ever changes.
456"""
458# ---------------------------------------------------------------------------
459# Cost Monitoring Constants
460# ---------------------------------------------------------------------------
461# Shared contract between the monitoring stack (which owns the cost report
462# bucket, Glue database/table, and Athena workgroup), the regional stacks
463# (which grant the cost-monitor service write access by deterministic ARN),
464# the cost-monitor service (which writes Parquet reports), and the CLI (which
465# queries Athena). Everything below is derived from ``project_name`` so two
466# deployments in one account never collide.
468COST_REPORT_SCHEDULED_PREFIX = "reports"
469"""Object-key prefix for scheduled cost allocation reports.
471The cost-monitor service writes Hive-partitioned Parquet objects under
472``reports/region=<region>/date=<YYYY-MM-DD>/...`` and the Glue table's
473partition projection reads the same layout — keep the two in lockstep.
474"""
476COST_REPORT_ADHOC_PREFIX = "adhoc"
477"""Object-key prefix for ad-hoc (user-requested) cost reports.
479Kept out of the scheduled ``reports/`` prefix so an ad-hoc report whose
480window overlaps a scheduled window can never double-count in Athena
481aggregations over the scheduled table.
482"""
484COST_ATHENA_RESULTS_PREFIX = "athena-results"
485"""Object-key prefix for Athena query results inside the cost report bucket."""
488def cost_report_bucket_name_prefix(project_name: str) -> str:
489 """Name prefix for the cost report bucket in ``GCOMonitoringStack``.
491 Derived from ``project_name``. The full bucket name is
492 ``<project_name>-cost-reports-<account>-<monitoring-region>`` — fully
493 deterministic at synth time, which lets every regional stack grant its
494 cost-monitor role write access by literal ARN without a cross-region
495 SSM read (and without inverting the regional-before-monitoring deploy
496 order). The prefix is what IAM policies and cdk-nag allow-list
497 assertions scope against.
498 """
499 return f"{project_name}-cost-reports"
502def cost_report_bucket_name(project_name: str, account: str, monitoring_region: str) -> str:
503 """Deterministic physical name of the cost report bucket.
505 Single source of truth shared by the monitoring stack (which creates the
506 bucket), the regional stacks (which inject the name into the cost-monitor
507 service environment and grant S3 access by literal ARN), and the CLI
508 (which resolves the bucket for Athena result downloads and report
509 listings).
510 """
511 return f"{cost_report_bucket_name_prefix(project_name)}-{account}-{monitoring_region}"
514def cost_glue_database_name(project_name: str) -> str:
515 """Glue database name for cost analytics.
517 Glue database names must not contain hyphens, so the project name's
518 hyphens are folded to underscores (``gco`` renders ``gco_cost``).
519 """
520 return f"{project_name.replace('-', '_')}_cost"
523COST_GLUE_ALLOCATION_TABLE = "allocation_reports"
524"""Glue table over the scheduled cost allocation reports."""
527def cost_athena_workgroup_name(project_name: str) -> str:
528 """Athena workgroup name for cost analytics queries."""
529 return f"{project_name}-cost"
532MOONCAKE_MASTER_DEFAULT_IMAGE = "vllm/vllm-openai:v0.26.0"
533"""Default container image for the shared per-region Mooncake master.
535The master StatefulSet runs the ``mooncake_master`` daemon (RPC + built-in HTTP
536metadata server). That binary ships in the ``mooncake-transfer-engine`` package
537that the upstream vLLM OpenAI server image already bundles, so the same pinned
538image used for disaggregated prefill/decode pods also serves the master without
539a separate build. The inference monitor reads this through the
540``MOONCAKE_MASTER_IMAGE`` environment variable and a per-endpoint
541``spec.mooncake.store.master_image`` overrides it.
543Keep this tag in lockstep with ``cli/images.py:_DISAGGREGATED_DEFAULT_IMAGE``
544(the disaggregated role-pod default); bump both together when validating a new
545vLLM release and never use a mutable/rolling tag such as ``latest``.
546"""