Coverage for gco / stacks / constants.py: 100.00%
124 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +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``)."""
56# ---------------------------------------------------------------------------
57# Deployment Region Contract
58# ---------------------------------------------------------------------------
59@lru_cache(maxsize=1)
60def cloudformation_region_partitions() -> Mapping[str, str]:
61 """Return immutable SDK-known CloudFormation Region-to-partition metadata.
63 Botocore endpoint metadata covers every AWS partition and requires neither
64 credentials nor a network request. Keeping this dynamic avoids a project
65 allowlist that would reject opt-in, sovereign, or newly supported Regions
66 already known to the installed SDK.
67 """
68 import boto3
70 session = boto3.Session()
71 region_partitions: dict[str, str] = {}
72 for partition in session.get_available_partitions():
73 for region in session.get_available_regions(
74 "cloudformation",
75 partition_name=partition,
76 ):
77 recorded_partition = region_partitions.setdefault(region, partition)
78 if recorded_partition != partition:
79 raise RuntimeError(
80 "AWS SDK endpoint metadata assigns CloudFormation region "
81 f"{region!r} to both {recorded_partition!r} and {partition!r}"
82 )
83 if not region_partitions:
84 raise RuntimeError("AWS SDK endpoint metadata contains no CloudFormation regions")
85 return MappingProxyType(region_partitions)
88@lru_cache(maxsize=1)
89def known_cloudformation_regions() -> frozenset[str]:
90 """Return every AWS SDK-known Region that exposes CloudFormation."""
91 return frozenset(cloudformation_region_partitions())
94def validated_deployment_partition(
95 regions: Collection[object],
96 *,
97 region_partitions: Mapping[str, str] | None = None,
98) -> str:
99 """Require a deployment topology to resolve to exactly one AWS partition.
101 A single credentials/account context and this application's cross-stack
102 references cannot span commercial, China, GovCloud, or ISO partitions.
103 Region count remains deliberately unlimited within the selected partition.
104 """
105 if not regions:
106 raise ValueError("At least one deployment region must be specified")
108 metadata = (
109 cloudformation_region_partitions() if region_partitions is None else region_partitions
110 )
111 if not metadata:
112 raise RuntimeError("AWS SDK endpoint metadata contains no CloudFormation regions")
114 regions_by_partition: dict[str, list[str]] = {}
115 for region in regions:
116 if not isinstance(region, str) or region not in metadata:
117 raise ValueError(
118 f"Invalid region {region!r}; expected an AWS region with a "
119 "CloudFormation endpoint known to the installed SDK"
120 )
121 partition = metadata[region]
122 regions_by_partition.setdefault(partition, []).append(region)
124 if len(regions_by_partition) != 1:
125 details = "; ".join(
126 f"{partition}: {', '.join(sorted(partition_regions))}"
127 for partition, partition_regions in sorted(regions_by_partition.items())
128 )
129 raise ValueError(
130 f"Deployment regions must all belong to a single AWS partition; found {details}"
131 )
132 return next(iter(regions_by_partition))
135def validated_regional_deployment_regions(
136 value: object,
137 *,
138 known_regions: Collection[str] | None = None,
139) -> tuple[str, ...]:
140 """Return a non-empty, unique list of SDK-known workload Regions.
142 There is deliberately no project-specific allowlist or maximum count. The
143 optional ``known_regions`` argument lets callers reuse endpoint metadata
144 they have already loaded while preserving this one validation contract.
145 """
146 if not isinstance(value, list) or not value:
147 raise ValueError("At least one region must be specified")
149 regions: list[str] = []
150 valid_regions = (
151 known_cloudformation_regions() if known_regions is None else frozenset(known_regions)
152 )
153 if not valid_regions:
154 raise RuntimeError("AWS SDK endpoint metadata contains no CloudFormation regions")
156 for region in value:
157 if not isinstance(region, str) or region not in valid_regions:
158 raise ValueError(
159 f"Invalid region {region!r}; expected an AWS region with a "
160 "CloudFormation endpoint known to the installed SDK"
161 )
162 regions.append(region)
163 if len(regions) != len(set(regions)):
164 raise ValueError("Duplicate regions found in configuration")
165 return tuple(regions)
168# ---------------------------------------------------------------------------
169# HTTP Request Body Limits
170# ---------------------------------------------------------------------------
171DEFAULT_MAX_REQUEST_BODY_BYTES = 1_048_576
172"""Default hard cap shared by API ingress and in-cluster request middleware."""
174MAX_CONFIGURABLE_REQUEST_BODY_BYTES = 10 * 1024 * 1024
175"""Largest supported cap; matches API Gateway's request payload ceiling."""
177# The cross-region aggregator has a deliberately read-mostly regional contract.
178# Keep both its identity policy and each regional API resource policy generated
179# from this exact allowlist so a compromised aggregator cannot reach unrelated
180# control-plane mutations exposed by the regional greedy route.
181AGGREGATOR_REGIONAL_API_ROUTES = (
182 ("GET", "api/v1/jobs"),
183 ("DELETE", "api/v1/jobs"),
184 ("GET", "api/v1/health"),
185 ("GET", "api/v1/status"),
186 # Read-only introspection of the region's deployed job validation policy.
187 # Callers need it before submission to know whether a manifest will be
188 # admitted; it exposes the same class of deployment metadata as
189 # api/v1/status (which already reports the caps and namespace allowlist)
190 # and mutates nothing.
191 ("GET", "api/v1/policy"),
192)
195def validated_request_body_limit(value: object) -> int:
196 """Return a safe request-body limit or reject an inconsistent deployment."""
197 if type(value) is not int or value < 1 or value > MAX_CONFIGURABLE_REQUEST_BODY_BYTES:
198 raise ValueError(
199 "max_request_body_bytes must be an integer between 1 and "
200 f"{MAX_CONFIGURABLE_REQUEST_BODY_BYTES}"
201 )
202 return value
205# ---------------------------------------------------------------------------
206# API Gateway Auth Secret
207# ---------------------------------------------------------------------------
208# Physical name of the Secrets Manager secret that holds the rotating HMAC
209# signing key used by trusted API Gateway proxy Lambdas. It is created by
210# ``GCOApiGatewayGlobalStack`` (in the ``api_gateway`` region) and read by the
211# regional service-account role and regional API proxy Lambda. The historical
212# ``api-gateway-auth-token`` suffix is retained to avoid replacing deployments.
215def api_gateway_auth_secret_name(project_name: str) -> str:
216 """Secrets Manager name for the proxy-to-backend HMAC signing key.
218 Derived from ``project_name`` (``<project_name>/api-gateway-auth-token``)
219 so two deployments in the same account+region do not collide on the secret
220 name. For the default ``project_name="gco"`` this renders
221 ``gco/api-gateway-auth-token`` — byte-for-byte identical to the pre-#139
222 literal, so existing deployments see no resource replacement.
224 Single source of truth shared by three call sites that must agree exactly:
226 1. ``GCOApiGatewayGlobalStack._create_secret`` — the ``secret_name`` the
227 secret is actually created with.
228 2. ``GCORegionalStack`` — the deterministic IAM ``Resource`` ARN granting
229 the service-account role read access to the secret. Built from this
230 name plus the API Gateway region and account so it renders identically
231 whether the API Gateway stack is cross-region or co-located with the
232 regional stack (see issue #125 — a synthesis-time cross-stack export
233 token used to leak into the ARN and dodge the cdk-nag suppression in
234 single-region topologies).
235 3. ``gco.stacks.nag_suppressions.add_iam_suppressions`` — the
236 ``AwsSolutions-IAM5`` acknowledgment scoped to this exact ARN.
238 Keep the three call sites in lockstep by calling this helper with the
239 stack's ``project_name`` rather than re-typing the name.
240 """
241 return f"{project_name}/api-gateway-auth-token" # nosec B105 — secret path/name, not a credential
244def cross_region_aggregator_role_name(project_name: str) -> str:
245 """IAM role name used by regional API resource-policy principals.
247 IAM roles are global within an account, and the role ARN is embedded in
248 API Gateway resource policies synthesized in other regions. A deterministic
249 project-scoped physical name avoids an unsupported cross-region
250 CloudFormation export. ``project_name`` is validated at 31 characters, so
251 this 24-character suffix keeps the result below IAM's 64-character limit.
252 """
253 return f"{project_name}-cross-region-aggregator"
256# ---------------------------------------------------------------------------
257# Backend TLS private PKI
258# ---------------------------------------------------------------------------
261def backend_tls_server_name(project_name: str) -> str:
262 """Private certificate identity asserted by every backend TLS client.
264 The name deliberately does not need public DNS. Proxy clients connect to
265 Global Accelerator or an internal ALB's real DNS name while sending this
266 value as SNI and verifying it against the deployment-local root CA.
267 """
268 return f"backend.{project_name}.gco.internal"
271def backend_tls_root_secret_name(project_name: str) -> str:
272 """Secrets Manager name containing the deployment-local root private key."""
273 return f"{project_name}/backend-tls/root-ca"
276def backend_tls_root_ca_parameter_name(project_name: str) -> str:
277 """SSM parameter containing only the public root trust bundle."""
278 return f"/{project_name}/backend-tls/root-ca.pem"
281def backend_tls_certificate_parameter_prefix(project_name: str) -> str:
282 """SSM prefix under which regional imported-certificate ARNs are stored."""
283 return f"/{project_name}/backend-tls/certificate-arn/"
286def backend_tls_certificate_arn_parameter_name(project_name: str, region: str) -> str:
287 """SSM parameter containing one region's stable imported ACM ARN."""
288 return f"{backend_tls_certificate_parameter_prefix(project_name)}{region}"
291# ---------------------------------------------------------------------------
292# EKS Add-on Versions
293# ---------------------------------------------------------------------------
294# Pinned to specific eksbuild versions for reproducible deployments.
295# The dependency scanner checks ``aws eks describe-addon-versions`` monthly
296# and opens an issue when newer builds are available.
298EKS_ADDON_POD_IDENTITY_AGENT = "v1.4.0-eksbuild.2"
299"""EKS Pod Identity Agent — enables IRSA and Pod Identity for service accounts."""
301EKS_ADDON_METRICS_SERVER = "v0.9.0-eksbuild.10"
302"""Kubernetes Metrics Server — provides CPU/memory metrics for HPA and ``kubectl top``."""
304EKS_ADDON_EFS_CSI_DRIVER = "v3.4.2-eksbuild.1"
305"""Amazon EFS CSI Driver — mounts EFS file systems as Kubernetes persistent volumes."""
307EKS_ADDON_CLOUDWATCH_OBSERVABILITY = "v6.6.0-eksbuild.1"
308"""Amazon CloudWatch Observability — Container Insights, Prometheus metrics, FluentBit logs."""
310EKS_ADDON_FSX_CSI_DRIVER = "v1.10.0-eksbuild.2"
311"""Amazon FSx CSI Driver — mounts FSx for Lustre file systems as Kubernetes persistent volumes."""
313# ---------------------------------------------------------------------------
314# EKS Cluster Subnet Constraints
315# ---------------------------------------------------------------------------
316# A few Availability Zones cannot host the subnets you pass when creating an
317# EKS cluster (the control-plane elastic network interfaces). EKS rejects
318# cluster creation if any supplied subnet is in one of these zones. The
319# constraint is published by *Availability Zone ID* (e.g. ``use1-az3``), which
320# is stable across accounts — unlike the AZ *name* (``us-east-1e``), which AWS
321# randomizes per account. Match by ID, then resolve to this account's names.
322# Source: https://docs.aws.amazon.com/eks/latest/userguide/network-reqs.html
323# ("Subnet requirements for clusters" — disallowed Availability Zone IDs).
325EKS_UNSUPPORTED_AZ_IDS: dict[str, tuple[str, ...]] = {
326 "us-east-1": ("use1-az3",),
327 "us-west-1": ("usw1-az2",),
328 "ca-central-1": ("cac1-az3",),
329}
330"""AWS-region → Availability Zone IDs that cannot hold EKS cluster subnets.
332The regional VPC deliberately spans every AZ in the region (one public + one
333private subnet each), but the EKS cluster's control-plane subnet selection must
334exclude any subnet in these zones or ``CreateCluster`` fails with
335``InvalidParameterException``. Regions absent from this map have no such
336restriction. Keep in sync with the AWS EKS networking requirements doc.
337"""
339# ---------------------------------------------------------------------------
340# Aurora PostgreSQL Engine Version
341# ---------------------------------------------------------------------------
342# Pinned to a specific minor version. The dependency scanner checks
343# ``aws rds describe-db-engine-versions`` monthly for newer releases
344# within the same major line.
346AURORA_POSTGRES_VERSION = "17.10"
347"""Aurora PostgreSQL engine version, applied via ``rds.AuroraPostgresEngineVersion.of()``.
349A plain ``major.minor`` string rather than a CDK enum name: enum members lag
350new RDS minor releases, which needlessly coupled an Aurora engine bump to an
351aws-cdk-lib release. The dependency scanner validates this pin directly
352against ``rds describe-db-engine-versions`` — the authoritative source — so
353a newer minor is reported the day RDS ships it, independent of the CDK
354library's enum catalog.
355"""
356# ---------------------------------------------------------------------------
357# Analytics Environment Constants
358# ---------------------------------------------------------------------------
359# Pinned values consumed by the optional analytics environment (SageMaker
360# Studio, EMR Serverless, Cognito hosted UI, and the always-on
361# Cluster_Shared_Bucket in ``GCOGlobalStack``). Keeping them here lets the
362# analytics stack, the regional stack, the global stack, and the tests import
363# from a single source of truth.
365EMR_SERVERLESS_RELEASE_LABEL = "emr-7.14.0"
366"""EMR Serverless Spark release label used for ``emrserverless.CfnApplication``.
368Pinned to a stable Spark release so analytics workloads get a reproducible
369runtime across deployments. Update alongside the EKS add-ons above when a
370newer EMR release is validated against the studio notebooks.
371"""
373SAGEMAKER_ROLE_NAME_PREFIX = "AmazonSageMaker"
374"""Required prefix for the SageMaker Studio execution role name.
376Amazon SageMaker requires execution roles used by Studio domains to have a
377name that starts with ``AmazonSageMaker`` so that AWS-managed policies and
378service-linked trust relationships resolve correctly. Any role name generated
379for ``SageMaker_Execution_Role`` must begin with this prefix.
380"""
383def cognito_domain_prefix_default(project_name: str) -> str:
384 """Default prefix for the Cognito hosted-UI domain.
386 Derived from ``project_name`` (``<project_name>-studio``). The full domain
387 prefix is assembled at synth time by appending the account id (e.g.
388 ``gco-studio-123456789012``) so it stays globally unique within
389 ``cognito.UserPoolDomain``. Operators may override the prefix through the
390 ``analytics_environment.cognito.domain_prefix`` field in ``cdk.json``.
392 For ``project_name="gco"`` this renders ``gco-studio`` — identical to the
393 pre-#139 literal.
394 """
395 return f"{project_name}-studio"
398STUDIO_PRESIGNED_URL_EXPIRY_SECONDS = 300
399"""Default expiry (in seconds) for SageMaker Studio presigned domain URLs.
401Five minutes matches the shortest window accepted by
402``CreatePresignedDomainUrl`` while still giving a user enough time to click
403the link after the ``/studio/login`` Lambda returns it. The presigned-URL
404Lambda reads this through the ``URL_EXPIRES_SECONDS`` environment variable
405and callers may override it per-request.
406"""
409# ---------------------------------------------------------------------------
410# S3 bucket naming policy
411# ---------------------------------------------------------------------------
412# No GCO stack sets an explicit ``bucket_name``. S3 bucket names live in one
413# global namespace and S3 does not guarantee a deleted name becomes reusable
414# promptly — a validation cycle hit ``BucketAlreadyExists`` on a
415# project/account/region name three days after its previous incarnation was
416# deleted. CloudFormation-generated names (``<stack>-<construct>-<random>``)
417# are unique per stack instance, so destroy-and-redeploy, retained buckets
418# from earlier deployments, and parallel deployments in one account can never
419# collide. The owning stack publishes each bucket's identity (``name``,
420# ``arn``, ``region``) as SSM parameters under a ``project_name``-derived
421# prefix; every consumer resolves the bucket from that contract and nothing
422# reconstructs a bucket name. ``tests/test_bucket_naming_contract.py``
423# enforces the policy on the stack sources.
426def cluster_shared_ssm_parameter_prefix(project_name: str) -> str:
427 """SSM parameter namespace for the cluster-shared bucket metadata.
429 Derived from ``project_name`` (``/<project_name>/cluster-shared-bucket``).
430 ``GCOGlobalStack`` writes ``<prefix>/name``, ``<prefix>/arn``, and
431 ``<prefix>/region`` under this path; ``GCORegionalStack`` (always) and
432 ``GCOAnalyticsStack`` (when enabled) read them back via
433 ``cr.AwsCustomResource`` against the global region. Treat the full paths as
434 the contract — the bucket's physical name is CloudFormation-generated and
435 is only knowable through them. For ``project_name="gco"`` this renders
436 ``/gco/cluster-shared-bucket``.
437 """
438 return f"/{project_name}/cluster-shared-bucket"
441def regional_shared_ssm_parameter_prefix(project_name: str) -> str:
442 """SSM parameter namespace for the regional general-purpose bucket metadata.
444 Derived from ``project_name`` (``/<project_name>/regional-shared-bucket``).
445 Each ``GCORegionalStack`` provisions exactly one general-purpose bucket per
446 region, unconditionally (no ``cdk.json`` toggle gates its existence), and
447 writes ``<prefix>/name``, ``<prefix>/arn``, and ``<prefix>/region`` under
448 this path **in its own region's** parameter store, exactly as the model
449 bucket and cluster-shared bucket publish theirs. In-region workloads (and
450 the regional upload surface) read them back to resolve the bucket — its
451 physical name is CloudFormation-generated and never reconstructed.
453 The per-region inference monitor builds the same path at runtime from its
454 injected ``PROJECT_NAME`` environment variable rather than importing this
455 helper (it needs no CDK imports at runtime), so keep the two in lockstep.
456 For ``project_name="gco"`` this renders ``/gco/regional-shared-bucket``.
457 """
458 return f"/{project_name}/regional-shared-bucket"
461MOONCAKE_COLD_TIER_KEY_PREFIX = "mooncake-kv"
462"""Object-key prefix for Mooncake cold-tier KV objects in the regional bucket.
464The per-region inference monitor resolves an endpoint's cold-tier object-store
465URI to ``s3://<regional-shared-bucket>/mooncake-kv/<endpoint>/`` (the bucket
466name comes from that region's ``/<project>/regional-shared-bucket/name``
467parameter), and the ``gco inference populate-kv`` upload surface writes under
468the same prefix, so operator-supplied warm-up objects land exactly where an
469endpoint's pods read them. This is the shared contract between the two sides;
470the monitor keeps a local copy of this value so it needs no CDK imports at
471runtime, so keep the two in lockstep if the prefix ever changes.
472"""
474# ---------------------------------------------------------------------------
475# Cost Monitoring Constants
476# ---------------------------------------------------------------------------
477# Shared contract between the monitoring stack (which owns the cost report
478# bucket, Glue database/table, and Athena workgroup and grants the regional
479# cost-monitor roles through the bucket and key policies), the regional stacks
480# (which let the cost-monitor role read the published bucket identity), the
481# cost-monitor service (which resolves the bucket from SSM and writes Parquet
482# reports), and the CLI (which queries Athena). Everything below is derived
483# from ``project_name`` so two deployments in one account never collide.
485COST_REPORT_SCHEDULED_PREFIX = "reports"
486"""Object-key prefix for scheduled cost allocation reports.
488The cost-monitor service writes Hive-partitioned Parquet objects under
489``reports/region=<region>/date=<YYYY-MM-DD>/...`` and the Glue table's
490partition projection reads the same layout — keep the two in lockstep.
491"""
493COST_REPORT_ADHOC_PREFIX = "adhoc"
494"""Object-key prefix for ad-hoc (user-requested) cost reports.
496Kept out of the scheduled ``reports/`` prefix so an ad-hoc report whose
497window overlaps a scheduled window can never double-count in Athena
498aggregations over the scheduled table.
499"""
501COST_ATHENA_RESULTS_PREFIX = "athena-results"
502"""Object-key prefix for Athena query results inside the cost report bucket."""
505def cost_report_ssm_parameter_prefix(project_name: str) -> str:
506 """SSM parameter namespace for the cost report bucket identity.
508 Derived from ``project_name`` (``/<project_name>/cost-report-bucket``).
509 ``GCOMonitoringStack`` writes ``<prefix>/name``, ``<prefix>/arn``, and
510 ``<prefix>/region`` under this path **in the monitoring region's**
511 parameter store — the same publish-then-resolve contract the model,
512 cluster-shared, and regional-shared buckets use. The bucket's physical
513 name is CloudFormation-generated (see the S3 bucket naming policy above),
514 so these parameters are the only way to learn it:
516 * the per-region cost-monitor service reads ``<prefix>/name`` at runtime
517 (``COST_REPORT_BUCKET_PARAMETER`` / ``COST_REPORT_BUCKET_PARAMETER_REGION``
518 injected by ``GCORegionalStack``). The monitoring stack deploys *after*
519 the regional stacks, so on a fresh deploy-all the parameter does not
520 exist yet when the service first boots; it retries on every scheduled
521 pass until it does;
522 * the regional cost-monitor role is granted ``ssm:GetParameter`` on the
523 ``/name`` parameter by literal ARN, while S3 and KMS access come from
524 the monitoring stack's bucket and key policies (principal based);
525 * ``gco storage`` and release validation read ``<prefix>/name`` and
526 ``<prefix>/arn`` instead of reconstructing a name.
528 For ``project_name="gco"`` this renders ``/gco/cost-report-bucket``.
529 """
530 return f"/{project_name}/cost-report-bucket"
533def cost_glue_database_name(project_name: str) -> str:
534 """Glue database name for cost analytics.
536 Glue database names must not contain hyphens, so the project name's
537 hyphens are folded to underscores (``gco`` renders ``gco_cost``).
538 """
539 return f"{project_name.replace('-', '_')}_cost"
542COST_GLUE_ALLOCATION_TABLE = "allocation_reports"
543"""Glue table over the scheduled cost allocation reports."""
546def cost_athena_workgroup_name(project_name: str) -> str:
547 """Athena workgroup name for cost analytics queries."""
548 return f"{project_name}-cost"
551MOONCAKE_MASTER_DEFAULT_IMAGE = "vllm/vllm-openai:v0.29.0"
552"""Default container image for the shared per-region Mooncake master.
554The master StatefulSet runs the ``mooncake_master`` daemon (RPC + built-in HTTP
555metadata server). That binary ships in the ``mooncake-transfer-engine`` package
556that the upstream vLLM OpenAI server image already bundles, so the same pinned
557image used for disaggregated prefill/decode pods also serves the master without
558a separate build. The inference monitor reads this through the
559``MOONCAKE_MASTER_IMAGE`` environment variable and a per-endpoint
560``spec.mooncake.store.master_image`` overrides it.
562Keep this tag in lockstep with ``cli/images.py:_DISAGGREGATED_DEFAULT_IMAGE``
563(the disaggregated role-pod default); bump both together when validating a new
564vLLM release and never use a mutable/rolling tag such as ``latest``.
565"""