Coverage for gco / stacks / constants.py: 100.00%
132 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-10-28 16:19 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-10-28 16:19 +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# Resource-governance defaults live in gco.resource_governance (a
28# runtime-shippable module) because the manifest/queue processors need the
29# same values inside their container images, whose build context deliberately
30# excludes gco/stacks/** (see _SERVICE_IMAGE_COMMON_EXCLUDES in the regional
31# stack). Re-exported here so synth-side code keeps one import home.
32from gco.resource_governance import (
33 DEFAULT_MANIFEST_RESOURCE_CAPS as DEFAULT_MANIFEST_RESOURCE_CAPS,
34)
35from gco.resource_governance import (
36 DEFAULT_RESOURCE_QUOTA as DEFAULT_RESOURCE_QUOTA,
37)
38from gco.resource_governance import (
39 parse_k8s_quantity as parse_k8s_quantity,
40)
42# ---------------------------------------------------------------------------
43# Lambda Runtimes
44# ---------------------------------------------------------------------------
45# Keep every Lambda language runtime here rather than spelling enum members in
46# individual stacks. The monthly dependency scan compares these constants with
47# the newest managed runtimes exposed by aws-cdk-lib and checks the Node major
48# against .nvmrc, package.json, and Dockerfile.dev.
49LAMBDA_PYTHON_RUNTIME = "PYTHON_3_14"
50"""CDK enum name for Python Lambdas (``lambda_.Runtime.PYTHON_3_14``)."""
52LAMBDA_NODEJS_RUNTIME = "NODEJS_24_X"
53"""CDK enum name for Node.js Lambdas (``lambda_.Runtime.NODEJS_24_X``)."""
55LAMBDA_SOURCE_ASSET_EXCLUDE = (
56 "__pycache__",
57 "*.pyc",
58 "*.pyo",
59 ".mypy_cache",
60 ".pytest_cache",
61 ".DS_Store",
62)
63"""Local caches never packaged from a ``lambda/`` source directory.
65A test run that imports a handler writes its bytecode next to it, and these
66files would otherwise ship in the function's zip and enter its asset hash, so
67a working checkout would publish different code than a clean clone of the
68same commit. CDK matches a pattern without a slash against every name below
69the asset directory. ``gco.stacks.lambda_assets.lambda_source_code`` applies
70them to the zipped source directories, and the CLI leaves the same caches out
71of its own Lambda builds (``cli/stacks.py``).
72"""
75# ---------------------------------------------------------------------------
76# Deployment Region Contract
77# ---------------------------------------------------------------------------
78@lru_cache(maxsize=1)
79def cloudformation_region_partitions() -> Mapping[str, str]:
80 """Return immutable SDK-known CloudFormation Region-to-partition metadata.
82 Botocore endpoint metadata covers every AWS partition and requires neither
83 credentials nor a network request. Keeping this dynamic avoids a project
84 allowlist that would reject opt-in, sovereign, or newly supported Regions
85 already known to the installed SDK.
86 """
87 import boto3
89 session = boto3.Session()
90 region_partitions: dict[str, str] = {}
91 for partition in session.get_available_partitions():
92 for region in session.get_available_regions(
93 "cloudformation",
94 partition_name=partition,
95 ):
96 recorded_partition = region_partitions.setdefault(region, partition)
97 if recorded_partition != partition:
98 raise RuntimeError(
99 "AWS SDK endpoint metadata assigns CloudFormation region "
100 f"{region!r} to both {recorded_partition!r} and {partition!r}"
101 )
102 if not region_partitions:
103 raise RuntimeError("AWS SDK endpoint metadata contains no CloudFormation regions")
104 return MappingProxyType(region_partitions)
107@lru_cache(maxsize=1)
108def known_cloudformation_regions() -> frozenset[str]:
109 """Return every AWS SDK-known Region that exposes CloudFormation."""
110 return frozenset(cloudformation_region_partitions())
113def validated_deployment_partition(
114 regions: Collection[object],
115 *,
116 region_partitions: Mapping[str, str] | None = None,
117) -> str:
118 """Require a deployment topology to resolve to exactly one AWS partition.
120 A single credentials/account context and this application's cross-stack
121 references cannot span commercial, China, GovCloud, or ISO partitions.
122 Region count remains deliberately unlimited within the selected partition.
123 """
124 if not regions:
125 raise ValueError("At least one deployment region must be specified")
127 metadata = (
128 cloudformation_region_partitions() if region_partitions is None else region_partitions
129 )
130 if not metadata:
131 raise RuntimeError("AWS SDK endpoint metadata contains no CloudFormation regions")
133 regions_by_partition: dict[str, list[str]] = {}
134 for region in regions:
135 if not isinstance(region, str) or region not in metadata:
136 raise ValueError(
137 f"Invalid region {region!r}; expected an AWS region with a "
138 "CloudFormation endpoint known to the installed SDK"
139 )
140 partition = metadata[region]
141 regions_by_partition.setdefault(partition, []).append(region)
143 if len(regions_by_partition) != 1:
144 details = "; ".join(
145 f"{partition}: {', '.join(sorted(partition_regions))}"
146 for partition, partition_regions in sorted(regions_by_partition.items())
147 )
148 raise ValueError(
149 f"Deployment regions must all belong to a single AWS partition; found {details}"
150 )
151 return next(iter(regions_by_partition))
154CONTROL_PLANE_ONLY_CONTEXT_KEY = "gco:control-plane-only"
155"""Run-scoped CDK context key that synthesizes the app with zero workload Regions.
157Passing ``--context gco:control-plane-only=true`` makes ``ConfigLoader`` report an
158empty ``deployment_regions.regional`` without editing ``cdk.json``, so the
159synthesized app contains only the global, API Gateway, and monitoring stacks.
160``gco upgrade`` uses it to update the monitoring stack in place so it stops
161referencing the regional stacks before those are destroyed and recreated. Any
162value other than the string ``"true"`` (case-insensitive) leaves the configured
163Region list alone.
164"""
167def control_plane_only_requested(value: object) -> bool:
168 """Return whether a ``gco:control-plane-only`` context value opts in."""
169 if value is True:
170 return True
171 return isinstance(value, str) and value.strip().lower() == "true"
174def validated_regional_deployment_regions(
175 value: object,
176 *,
177 known_regions: Collection[str] | None = None,
178) -> tuple[str, ...]:
179 """Return a unique, possibly empty list of SDK-known workload Regions.
181 There is deliberately no project-specific allowlist, minimum, or maximum
182 count. An empty list is a valid *control-plane-only* topology: the global,
183 API Gateway, and monitoring stacks stand with no regional cluster behind
184 them, which is how a deployment is scaled to zero workload Regions (and
185 what ``gco upgrade`` passes through while it recreates them). The optional
186 ``known_regions`` argument lets callers reuse endpoint metadata they have
187 already loaded while preserving this one validation contract.
188 """
189 if not isinstance(value, list):
190 raise ValueError("deployment_regions.regional must be a list of AWS region names")
192 regions: list[str] = []
193 valid_regions = (
194 known_cloudformation_regions() if known_regions is None else frozenset(known_regions)
195 )
196 if not valid_regions:
197 raise RuntimeError("AWS SDK endpoint metadata contains no CloudFormation regions")
199 for region in value:
200 if not isinstance(region, str) or region not in valid_regions:
201 raise ValueError(
202 f"Invalid region {region!r}; expected an AWS region with a "
203 "CloudFormation endpoint known to the installed SDK"
204 )
205 regions.append(region)
206 if len(regions) != len(set(regions)):
207 raise ValueError("Duplicate regions found in configuration")
208 return tuple(regions)
211# ---------------------------------------------------------------------------
212# HTTP Request Body Limits
213# ---------------------------------------------------------------------------
214DEFAULT_MAX_REQUEST_BODY_BYTES = 1_048_576
215"""Default hard cap shared by API ingress and in-cluster request middleware."""
217MAX_CONFIGURABLE_REQUEST_BODY_BYTES = 10 * 1024 * 1024
218"""Largest supported cap; matches API Gateway's request payload ceiling."""
220# The cross-region aggregator has a deliberately read-mostly regional contract.
221# Keep both its identity policy and each regional API resource policy generated
222# from this exact allowlist so a compromised aggregator cannot reach unrelated
223# control-plane mutations exposed by the regional greedy route.
224AGGREGATOR_REGIONAL_API_ROUTES = (
225 ("GET", "api/v1/jobs"),
226 ("DELETE", "api/v1/jobs"),
227 ("GET", "api/v1/health"),
228 ("GET", "api/v1/status"),
229 # Read-only introspection of the region's deployed job validation policy.
230 # Callers need it before submission to know whether a manifest will be
231 # admitted; it exposes the same class of deployment metadata as
232 # api/v1/status (which already reports the caps and namespace allowlist)
233 # and mutates nothing.
234 ("GET", "api/v1/policy"),
235)
238def validated_request_body_limit(value: object) -> int:
239 """Return a safe request-body limit or reject an inconsistent deployment."""
240 if type(value) is not int or value < 1 or value > MAX_CONFIGURABLE_REQUEST_BODY_BYTES:
241 raise ValueError(
242 "max_request_body_bytes must be an integer between 1 and "
243 f"{MAX_CONFIGURABLE_REQUEST_BODY_BYTES}"
244 )
245 return value
248# ---------------------------------------------------------------------------
249# API Gateway Auth Secret
250# ---------------------------------------------------------------------------
251# Physical name of the Secrets Manager secret that holds the rotating HMAC
252# signing key used by trusted API Gateway proxy Lambdas. It is created by
253# ``GCOApiGatewayGlobalStack`` (in the ``api_gateway`` region) and read by the
254# regional service-account role and regional API proxy Lambda. The historical
255# ``api-gateway-auth-token`` suffix is retained to avoid replacing deployments.
258def api_gateway_auth_secret_name(project_name: str) -> str:
259 """Secrets Manager name for the proxy-to-backend HMAC signing key.
261 Derived from ``project_name`` (``<project_name>/api-gateway-auth-token``)
262 so two deployments in the same account+region do not collide on the secret
263 name. For the default ``project_name="gco"`` this renders
264 ``gco/api-gateway-auth-token`` — byte-for-byte identical to the pre-#139
265 literal, so existing deployments see no resource replacement.
267 Single source of truth shared by the call sites that must agree exactly:
269 1. ``GCOApiGatewayGlobalStack._create_secret`` — the ``secret_name`` the
270 secret is actually created with.
271 2. ``GCORegionalStack`` (the manifest-processor, inference-proxy and
272 health-monitor roles) and ``GCORegionalApiGatewayStack`` (both proxy
273 Lambda roles) — the IAM ``Resource`` granting read access to the
274 secret: ``arn:<partition>:secretsmanager:<API Gateway Region>:<account>:
275 secret:<this name>-??????``. Building it from the name renders the same
276 grant in every topology, whereas the passed-in cross-stack reference
277 becomes a native export when the stacks share a Region (issue #125,
278 PR #461). ``-??????`` matches the six random characters Secrets Manager
279 appends to the name in the ARN and nothing else, so the grant needs no
280 cdk-nag acknowledgment.
282 Call this helper with the stack's ``project_name`` rather than re-typing
283 the name. ``tests/test_cdk_synthesis_matrix.py`` checks every rendered
284 grant on the secret against the API Gateway Region.
285 """
286 return f"{project_name}/api-gateway-auth-token" # nosec B105 # secret path/name, not a credential
289def cross_region_aggregator_role_name(project_name: str) -> str:
290 """IAM role name used by regional API resource-policy principals.
292 IAM roles are global within an account, and the role ARN is embedded in
293 API Gateway resource policies synthesized in other regions. A deterministic
294 project-scoped physical name avoids an unsupported cross-region
295 CloudFormation export. ``project_name`` is validated at 31 characters, so
296 this 24-character suffix keeps the result below IAM's 64-character limit.
297 """
298 return f"{project_name}-cross-region-aggregator"
301# ---------------------------------------------------------------------------
302# Backend TLS private PKI
303# ---------------------------------------------------------------------------
306def backend_tls_server_name(project_name: str) -> str:
307 """Private certificate identity asserted by every backend TLS client.
309 The name deliberately does not need public DNS. Proxy clients connect to
310 Global Accelerator or an internal ALB's real DNS name while sending this
311 value as SNI and verifying it against the deployment-local root CA.
312 """
313 return f"backend.{project_name}.gco.internal"
316def backend_tls_root_secret_name(project_name: str) -> str:
317 """Secrets Manager name containing the deployment-local root private key."""
318 return f"{project_name}/backend-tls/root-ca"
321def backend_tls_root_ca_parameter_name(project_name: str) -> str:
322 """SSM parameter containing only the public root trust bundle."""
323 return f"/{project_name}/backend-tls/root-ca.pem"
326def backend_tls_certificate_parameter_prefix(project_name: str) -> str:
327 """SSM prefix under which regional imported-certificate ARNs are stored."""
328 return f"/{project_name}/backend-tls/certificate-arn/"
331def backend_tls_certificate_arn_parameter_name(project_name: str, region: str) -> str:
332 """SSM parameter containing one region's stable imported ACM ARN."""
333 return f"{backend_tls_certificate_parameter_prefix(project_name)}{region}"
336# ---------------------------------------------------------------------------
337# EKS Add-on Versions
338# ---------------------------------------------------------------------------
339# Pinned to specific eksbuild versions for reproducible deployments.
340# The dependency scanner checks ``aws eks describe-addon-versions`` monthly
341# and opens an issue when newer builds are available.
343EKS_ADDON_POD_IDENTITY_AGENT = "v1.4.0-eksbuild.3"
344"""EKS Pod Identity Agent — enables IRSA and Pod Identity for service accounts."""
346EKS_ADDON_METRICS_SERVER = "v0.9.0-eksbuild.11"
347"""Kubernetes Metrics Server — provides CPU/memory metrics for HPA and ``kubectl top``.
349The ``v0.9.x`` line registers only ``metrics.k8s.io/v1beta1``; the ``v1`` API
350that went GA in Kubernetes 1.37 arrives with a later metrics-server release.
351The two in-cluster callers (``gco.services.health_monitor`` and the jobs API
352route) read the version through ``METRICS_API_VERSION`` in
353``gco/k8s_api_versions.py``. When this pin moves to a release that also
354registers ``v1`` (upstream ``master`` does; expected from ``v0.10.x``), flip
355that constant.
356"""
358EKS_ADDON_EFS_CSI_DRIVER = "v3.4.2-eksbuild.1"
359"""Amazon EFS CSI Driver — mounts EFS file systems as Kubernetes persistent volumes."""
361EKS_ADDON_CLOUDWATCH_OBSERVABILITY = "v6.7.0-eksbuild.1"
362"""Amazon CloudWatch Observability — Container Insights, Prometheus metrics, FluentBit logs."""
364EKS_ADDON_FSX_CSI_DRIVER = "v1.10.0-eksbuild.2"
365"""Amazon FSx CSI Driver — mounts FSx for Lustre file systems as Kubernetes persistent volumes."""
367# ---------------------------------------------------------------------------
368# EKS Cluster Subnet Constraints
369# ---------------------------------------------------------------------------
370# A few Availability Zones cannot host the subnets you pass when creating an
371# EKS cluster (the control-plane elastic network interfaces). EKS rejects
372# cluster creation if any supplied subnet is in one of these zones. The
373# constraint is published by *Availability Zone ID* (e.g. ``use1-az3``), which
374# is stable across accounts — unlike the AZ *name* (``us-east-1e``), which AWS
375# randomizes per account. Match by ID, then resolve to this account's names.
376# Source: https://docs.aws.amazon.com/eks/latest/userguide/network-reqs.html
377# ("Subnet requirements for clusters" — disallowed Availability Zone IDs).
379EKS_UNSUPPORTED_AZ_IDS: dict[str, tuple[str, ...]] = {
380 "us-east-1": ("use1-az3",),
381 "us-west-1": ("usw1-az2",),
382 "ca-central-1": ("cac1-az3",),
383}
384"""AWS-region → Availability Zone IDs that cannot hold EKS cluster subnets.
386The regional VPC deliberately spans every AZ in the region (one public + one
387private subnet each), but the EKS cluster's control-plane subnet selection must
388exclude any subnet in these zones or ``CreateCluster`` fails with
389``InvalidParameterException``. Regions absent from this map have no such
390restriction. Keep in sync with the AWS EKS networking requirements doc.
391"""
393# ---------------------------------------------------------------------------
394# Aurora PostgreSQL Engine Version
395# ---------------------------------------------------------------------------
396# Pinned to a specific minor version. The dependency scanner checks
397# ``aws rds describe-db-engine-versions`` monthly for newer releases
398# within the same major line.
400AURORA_POSTGRES_VERSION = "17.11"
401"""Aurora PostgreSQL engine version, applied via ``rds.AuroraPostgresEngineVersion.of()``.
403A plain ``major.minor`` string rather than a CDK enum name: enum members lag
404new RDS minor releases, which needlessly coupled an Aurora engine bump to an
405aws-cdk-lib release. The dependency scanner validates this pin directly
406against ``rds describe-db-engine-versions`` — the authoritative source — so
407a newer minor is reported the day RDS ships it, independent of the CDK
408library's enum catalog.
409"""
410# ---------------------------------------------------------------------------
411# Analytics Environment Constants
412# ---------------------------------------------------------------------------
413# Pinned values consumed by the optional analytics environment (SageMaker
414# Studio, EMR Serverless, Cognito hosted UI, and the always-on
415# Cluster_Shared_Bucket in ``GCOGlobalStack``). Keeping them here lets the
416# analytics stack, the regional stack, the global stack, and the tests import
417# from a single source of truth.
419EMR_SERVERLESS_RELEASE_LABEL = "emr-spark-8.1.0"
420"""EMR Serverless Spark release label used for ``emrserverless.CfnApplication``.
422Pinned to a stable Spark release so analytics workloads get a reproducible
423runtime across deployments. Update alongside the EKS add-ons above when a
424newer EMR release is validated against the studio notebooks.
426Two label families are valid for the ``SPARK`` application this pins: the
427classic ``emr-N.x.y`` line (Spark 3; ``emr-7.14.0`` was the last pin on it)
428and the AWS runtime for Apache Spark line, ``emr-spark-N.x.y`` (Spark 4;
429``emr-spark-8.0.0`` GA, then ``8.1.0``), which is the current major and the
430one pinned here. The monthly dependency scan ranks both families by version
431and reports the newer one, so a future major shows up as "new major
432available". Moving between families is a Spark major-version change for
433every notebook that runs on the application; the repository ships no Spark
434job of its own, so the pin carried no code change of ours.
435"""
437SAGEMAKER_ROLE_NAME_PREFIX = "AmazonSageMaker"
438"""Required prefix for the SageMaker Studio execution role name.
440Amazon SageMaker requires execution roles used by Studio domains to have a
441name that starts with ``AmazonSageMaker`` so that AWS-managed policies and
442service-linked trust relationships resolve correctly. Any role name generated
443for ``SageMaker_Execution_Role`` must begin with this prefix.
444"""
447def cognito_domain_prefix_default(project_name: str) -> str:
448 """Default prefix for the Cognito hosted-UI domain.
450 Derived from ``project_name`` (``<project_name>-studio``). The full domain
451 prefix is assembled at synth time by appending the account id (e.g.
452 ``gco-studio-123456789012``) so it stays globally unique within
453 ``cognito.UserPoolDomain``. Operators may override the prefix through the
454 ``analytics_environment.cognito.domain_prefix`` field in ``cdk.json``.
456 For ``project_name="gco"`` this renders ``gco-studio`` — identical to the
457 pre-#139 literal.
458 """
459 return f"{project_name}-studio"
462STUDIO_PRESIGNED_URL_EXPIRY_SECONDS = 300
463"""Default expiry (in seconds) for SageMaker Studio presigned domain URLs.
465Five minutes matches the shortest window accepted by
466``CreatePresignedDomainUrl`` while still giving a user enough time to click
467the link after the ``/studio/login`` Lambda returns it. The presigned-URL
468Lambda reads this through the ``URL_EXPIRES_SECONDS`` environment variable
469and callers may override it per-request.
470"""
473# ---------------------------------------------------------------------------
474# S3 bucket naming policy
475# ---------------------------------------------------------------------------
476# No GCO stack sets an explicit ``bucket_name``. S3 bucket names live in one
477# global namespace and S3 does not guarantee a deleted name becomes reusable
478# promptly — a validation cycle hit ``BucketAlreadyExists`` on a
479# project/account/region name three days after its previous incarnation was
480# deleted. CloudFormation-generated names (``<stack>-<construct>-<random>``)
481# are unique per stack instance, so destroy-and-redeploy, retained buckets
482# from earlier deployments, and parallel deployments in one account can never
483# collide. The owning stack publishes each bucket's identity (``name``,
484# ``arn``, ``region``) as SSM parameters under a ``project_name``-derived
485# prefix; every consumer resolves the bucket from that contract and nothing
486# reconstructs a bucket name. ``tests/test_bucket_naming_contract.py``
487# enforces the policy on the stack sources.
490def cluster_shared_ssm_parameter_prefix(project_name: str) -> str:
491 """SSM parameter namespace for the cluster-shared bucket metadata.
493 Derived from ``project_name`` (``/<project_name>/cluster-shared-bucket``).
494 ``GCOGlobalStack`` writes ``<prefix>/name``, ``<prefix>/arn``, and
495 ``<prefix>/region`` under this path; ``GCORegionalStack`` (always) and
496 ``GCOAnalyticsStack`` (when enabled) read them back via
497 ``cr.AwsCustomResource`` against the global region. Treat the full paths as
498 the contract — the bucket's physical name is CloudFormation-generated and
499 is only knowable through them. For ``project_name="gco"`` this renders
500 ``/gco/cluster-shared-bucket``.
501 """
502 return f"/{project_name}/cluster-shared-bucket"
505def regional_shared_ssm_parameter_prefix(project_name: str) -> str:
506 """SSM parameter namespace for the regional general-purpose bucket metadata.
508 Derived from ``project_name`` (``/<project_name>/regional-shared-bucket``).
509 Each ``GCORegionalStack`` provisions exactly one general-purpose bucket per
510 region, unconditionally (no ``cdk.json`` toggle gates its existence), and
511 writes ``<prefix>/name``, ``<prefix>/arn``, and ``<prefix>/region`` under
512 this path **in its own region's** parameter store, exactly as the model
513 bucket and cluster-shared bucket publish theirs. In-region workloads (and
514 the regional upload surface) read them back to resolve the bucket — its
515 physical name is CloudFormation-generated and never reconstructed.
517 The per-region inference monitor builds the same path at runtime from its
518 injected ``PROJECT_NAME`` environment variable rather than importing this
519 helper (it needs no CDK imports at runtime), so keep the two in lockstep.
520 For ``project_name="gco"`` this renders ``/gco/regional-shared-bucket``.
521 """
522 return f"/{project_name}/regional-shared-bucket"
525MOONCAKE_COLD_TIER_KEY_PREFIX = "mooncake-kv"
526"""Object-key prefix for Mooncake cold-tier KV objects in the regional bucket.
528The per-region inference monitor resolves an endpoint's cold-tier object-store
529URI to ``s3://<regional-shared-bucket>/mooncake-kv/<endpoint>/`` (the bucket
530name comes from that region's ``/<project>/regional-shared-bucket/name``
531parameter), and the ``gco inference populate-kv`` upload surface writes under
532the same prefix, so operator-supplied warm-up objects land exactly where an
533endpoint's pods read them. This is the shared contract between the two sides;
534the monitor keeps a local copy of this value so it needs no CDK imports at
535runtime, so keep the two in lockstep if the prefix ever changes.
536"""
538# ---------------------------------------------------------------------------
539# Cost Monitoring Constants
540# ---------------------------------------------------------------------------
541# Shared contract between the monitoring stack (which owns the cost report
542# bucket, Glue database/table, and Athena workgroup and grants the regional
543# cost-monitor roles through the bucket and key policies), the regional stacks
544# (which let the cost-monitor role read the published bucket identity), the
545# cost-monitor service (which resolves the bucket from SSM and writes Parquet
546# reports), and the CLI (which queries Athena). Everything below is derived
547# from ``project_name`` so two deployments in one account never collide.
549COST_REPORT_SCHEDULED_PREFIX = "reports"
550"""Object-key prefix for scheduled cost allocation reports.
552The cost-monitor service writes Hive-partitioned Parquet objects under
553``reports/region=<region>/date=<YYYY-MM-DD>/...`` and the Glue table's
554partition projection reads the same layout — keep the two in lockstep.
555"""
557COST_REPORT_ADHOC_PREFIX = "adhoc"
558"""Object-key prefix for ad-hoc (user-requested) cost reports.
560Kept out of the scheduled ``reports/`` prefix so an ad-hoc report whose
561window overlaps a scheduled window can never double-count in Athena
562aggregations over the scheduled table.
563"""
565COST_ATHENA_RESULTS_PREFIX = "athena-results"
566"""Object-key prefix for Athena query results inside the cost report bucket."""
569def cost_report_ssm_parameter_prefix(project_name: str) -> str:
570 """SSM parameter namespace for the cost report bucket identity.
572 Derived from ``project_name`` (``/<project_name>/cost-report-bucket``).
573 ``GCOMonitoringStack`` writes ``<prefix>/name``, ``<prefix>/arn``, and
574 ``<prefix>/region`` under this path **in the monitoring region's**
575 parameter store — the same publish-then-resolve contract the model,
576 cluster-shared, and regional-shared buckets use. The bucket's physical
577 name is CloudFormation-generated (see the S3 bucket naming policy above),
578 so these parameters are the only way to learn it:
580 * the per-region cost-monitor service reads ``<prefix>/name`` at runtime
581 (``COST_REPORT_BUCKET_PARAMETER`` / ``COST_REPORT_BUCKET_PARAMETER_REGION``
582 injected by ``GCORegionalStack``). The monitoring stack deploys *after*
583 the regional stacks, so on a fresh deploy-all the parameter does not
584 exist yet when the service first boots; it retries on every scheduled
585 pass until it does;
586 * the regional cost-monitor role is granted ``ssm:GetParameter`` on the
587 ``/name`` parameter by literal ARN, while S3 and KMS access come from
588 the monitoring stack's bucket and key policies (principal based);
589 * ``gco storage`` and release validation read ``<prefix>/name`` and
590 ``<prefix>/arn`` instead of reconstructing a name.
592 For ``project_name="gco"`` this renders ``/gco/cost-report-bucket``.
593 """
594 return f"/{project_name}/cost-report-bucket"
597def cost_glue_database_name(project_name: str) -> str:
598 """Glue database name for cost analytics.
600 Glue database names must not contain hyphens, so the project name's
601 hyphens are folded to underscores (``gco`` renders ``gco_cost``).
602 """
603 return f"{project_name.replace('-', '_')}_cost"
606COST_GLUE_ALLOCATION_TABLE = "allocation_reports"
607"""Glue table over the scheduled cost allocation reports."""
610def cost_athena_workgroup_name(project_name: str) -> str:
611 """Athena workgroup name for cost analytics queries."""
612 return f"{project_name}-cost"
615MOONCAKE_MASTER_DEFAULT_IMAGE = "vllm/vllm-openai:v0.32.0"
616"""Default container image for the shared per-region Mooncake master.
618The master StatefulSet runs the ``mooncake_master`` daemon (RPC + built-in HTTP
619metadata server). That binary ships in the ``mooncake-transfer-engine`` package
620that the upstream vLLM OpenAI server image already bundles, so the same pinned
621image used for disaggregated prefill/decode pods also serves the master without
622a separate build. The inference monitor reads this through the
623``MOONCAKE_MASTER_IMAGE`` environment variable and a per-endpoint
624``spec.mooncake.store.master_image`` overrides it.
626Keep this tag in lockstep with ``cli/images.py:_DISAGGREGATED_DEFAULT_IMAGE``
627(the disaggregated role-pod default); bump both together when validating a new
628vLLM release and never use a mutable/rolling tag such as ``latest``.
629"""