This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch runs-on-template-wildcard
in repository https://gitbox.apache.org/repos/asf/airflow-ci-infra.git

commit e5603738c37eba04abeb5c6dce76a1028f1f3089
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue Oct 27 23:01:11 2026 +0100

    Let RunsOn upgrades deploy without an admin step
    
    Generated-by: Claude Opus 5
---
 terraform/runs-on-access/README.rst   |  7 +++--
 terraform/runs-on-access/main.tf      | 13 ++++-----
 terraform/runs-on/README.rst          | 27 ++++++++++--------
 terraform/runs-on/runs-on.yml.example | 53 ++++++++++++++++++++++++++---------
 terraform/runs-on/variables.tf        |  6 ++++
 5 files changed, 70 insertions(+), 36 deletions(-)

diff --git a/terraform/runs-on-access/README.rst 
b/terraform/runs-on-access/README.rst
index 04979cd..f4a6ac5 100644
--- a/terraform/runs-on-access/README.rst
+++ b/terraform/runs-on-access/README.rst
@@ -36,9 +36,10 @@ Roles
     Assumed only by jobs in the ``runs-on-prod`` environment, which 
``.asf.yaml`` limits to
     ``main`` and to deployments approved by a required reviewer other than the 
person who
     triggered them. It can call ``cloudformation:UpdateStack`` on the 
``runs-on`` stack only with
-    a template from RunsOn's published bucket, only for the versions listed in
-    ``runs_on_versions`` (exact template URLs), so a RunsOn upgrade means 
adding the version there
-    and applying this module before bumping ``runs_on_version`` in 
``terraform/runs-on``. It can
+    one of the versioned templates RunsOn publishes
+    
(``https://runs-on.s3.eu-west-1.amazonaws.com/cloudformation/template-v*.yaml``),
 so a RunsOn
+    upgrade or rollback needs only the reviewed ``runs_on_version`` change in
+    ``terraform/runs-on``. It can
     also pass the service role below and write the state and its lock file. It 
holds no
     infrastructure permissions.
 ``runs-on-cloudformation``
diff --git a/terraform/runs-on-access/main.tf b/terraform/runs-on-access/main.tf
index 771c71a..bfc50bf 100644
--- a/terraform/runs-on-access/main.tf
+++ b/terraform/runs-on-access/main.tf
@@ -20,9 +20,9 @@ locals {
   stack_arn  = 
"arn:aws:cloudformation:us-east-2:${local.account_id}:stack/runs-on/*"
   state      = "arn:aws:s3:::airflow-ci-tfstate/tf-state/runs-on.tfstate"
 
-  # RunsOn releases the apply role may deploy; older published templates stay 
reachable otherwise.
-  # Upgrading RunsOn means adding the new version here and applying this 
module first.
-  runs_on_versions = ["v3.3.2", "v3.5.1"]
+  # Any versioned template RunsOn publishes, so an upgrade (or a rollback) is 
a reviewed change of
+  # runs_on_version in terraform/runs-on alone. Templates from anywhere else 
stay out of reach.
+  runs_on_template_urls = 
"https://runs-on.s3.eu-west-1.amazonaws.com/cloudformation/template-v*.yaml";
 
   # The repository is pinned by its immutable IDs (repository_owner_id and 
repository_id claims),
   # so a renamed or re-created repository cannot match. Fork pull requests get 
no OIDC token at all,
@@ -163,12 +163,9 @@ data "aws_iam_policy_document" "apply" {
     actions   = ["cloudformation:UpdateStack"]
     resources = [local.stack_arn]
     condition {
-      test     = "StringEquals"
+      test     = "StringLike"
       variable = "cloudformation:TemplateUrl"
-      values = [
-        for v in local.runs_on_versions :
-        
"https://runs-on.s3.eu-west-1.amazonaws.com/cloudformation/template-${v}.yaml";
-      ]
+      values   = [local.runs_on_template_urls]
     }
   }
   # The provider sends the default tags on every UpdateStack.
diff --git a/terraform/runs-on/README.rst b/terraform/runs-on/README.rst
index e59290a..b869704 100644
--- a/terraform/runs-on/README.rst
+++ b/terraform/runs-on/README.rst
@@ -149,15 +149,16 @@ Bump ``runs_on_version`` (or any parameter) and 
``terraform plan`` / ``apply``:
 always resubmits the template URL, which is what RunsOn's `upgrade guide
 <https://runs-on.com/docs/maintenance/upgrades/>`_ requires (a reused 
processed template can
 silently skip parameter changes). Minor and patch releases have no downtime; 
major releases may.
-Rollback is the previous ``runs_on_version``. The apply role may only deploy 
the templates listed
-in ``runs_on_versions`` in `terraform/runs-on-access 
<../runs-on-access/README.rst>`_, so a
-``runs_on_version`` bump first needs the new version added there and applied 
by an admin. For the
-same reason, rollback works only while the previous version is still listed: 
keep it during an
-upgrade (for example ``["v3.3.2", "v3.4.0"]``) and drop it later. Template 
SHA-256s:
-``v3.3.2`` 
``3017805d77c1d717fdd229a1e1242db12c985944699d3d8dca01f6f28b35c16d``, ``v3.5.1``
-``9acd6fd154e2bb921f95466c45380d2e3d7bd718553a12af4e5c76d15915bd57``. Both 
templates create the
-same IAM roles, so the service role's guardrails in 
``terraform/runs-on-access`` need no change
-for this upgrade.
+Rollback is the previous ``runs_on_version``. The apply role may deploy any 
versioned template from
+RunsOn's bucket (``template-v*.yaml``, `terraform/runs-on-access 
<../runs-on-access/README.rst>`_)
+and ``runs_on_version`` must look like ``v3.5.1``, so an upgrade or rollback 
needs no admin step.
+What still needs an admin is IAM: the CloudFormation service role may only 
manage the IAM roles
+named in ``stack_role_prefixes`` and attach the two managed policies listed in
+``terraform/runs-on-access``. Before a bump compare the new template's 
``AWS::IAM::Role``,
+``AWS::IAM::InstanceProfile`` and ``ManagedPolicyArns`` with those lists; 
anything new fails the
+stack update until an admin adds it there and applies that module.
+Template SHA-256s: ``v3.3.2`` 
``3017805d77c1d717fdd229a1e1242db12c985944699d3d8dca01f6f28b35c16d``,
+``v3.5.1`` 
``9acd6fd154e2bb921f95466c45380d2e3d7bd718553a12af4e5c76d15915bd57``.
 
 Every plan shows ``"LicenseKey" = "****" -> 
"arn:...:parameter/runs-on/license-key"``:
 CloudFormation masks ``NoEcho`` parameters when Terraform reads the stack, and 
the AWS provider
@@ -244,8 +245,12 @@ of it was the Spot quota (peak 2332 of 3000 vCPU); the 
on-demand quota filled up
 Of 2156 Spot capacity errors in a day, 1959 were x64 (``m7a+m7i+m6a+m6i``) and 
197 arm64.
 More instance families give the Spot allocator more pools, so the x64 presets 
in
 ``runs-on.yml.example`` also allow ``m8a``, ``m8i``, ``m5a`` and ``m5`` (Spot 
``2xlarge`` on
-2026-10-27: 0.08-0.16 USD/hour against 0.12-0.15 for the existing families). 
Jobs that spell
-the family out in their label (``apache/airflow`` workflows) need the same 
change there.
+2026-10-27: 0.08-0.16 USD/hour against 0.12-0.15 for the existing families). 
Since
+``apache/airflow#75822`` the jobs (which spell their labels out instead of 
using these presets)
+also take Spot from capacity-optimized pools (``spot=co``) and accept up to 
twice their size
+(``cpu=8+16/ram=32+64``). Capacity-optimized ignores price, so a job can land 
on the larger size
+at up to twice the hourly price; the weekly report and the daily cost report 
show whether the net
+cost goes down.
 
 The CloudFormation template places runners in two Availability Zones 
(us-east-2a and 2b,
 fixed by the template). RunsOn's Spot spread across zones 
(``RUNS_ON_SPOT_FORCE_ZONE_SPREAD``,
diff --git a/terraform/runs-on/runs-on.yml.example 
b/terraform/runs-on/runs-on.yml.example
index 4f8b422..ee45cbd 100644
--- a/terraform/runs-on/runs-on.yml.example
+++ b/terraform/runs-on/runs-on.yml.example
@@ -25,42 +25,67 @@
 #
 # Classes are the EC2 equivalents of the measured EKS classes (AIP-118 
appendix):
 # small 4 vCPU / 16 GB, medium 8 / 32, xl 32 / 128. The xl class gets 128 GB 
because xdist
-# parallelism follows the host's 32 vCPUs. Spot is RunsOn's default (automatic 
rerun on
-# interruption); `volume` replaces the GitHub-hosted runners' root + /mnt 
disks.
+# parallelism follows the host's vCPUs. Each class also accepts up to twice 
its size, and Spot comes
+# from capacity-optimized pools (`spot: co`), as in the job labels of 
apache/airflow#75822; RunsOn
+# reruns an interrupted job on on-demand. `volume` replaces the GitHub-hosted 
runners' root + /mnt
+# disks.
 runners:
   airflow-amd-small:
-    cpu: 4
-    ram: 16
+    cpu: [4, 8]
+    ram: [16, 32]
     family: ["m8a", "m8i", "m7a", "m7i", "m6a", "m6i", "m5a", "m5"]
     image: ubuntu22-full-x64
     volume: 150gb
+    spot: co
   airflow-amd-medium:
-    cpu: 8
-    ram: 32
+    cpu: [8, 16]
+    ram: [32, 64]
     family: ["m8a", "m8i", "m7a", "m7i", "m6a", "m6i", "m5a", "m5"]
     image: ubuntu22-full-x64
     volume: 150gb
+    spot: co
   airflow-amd-xl:
-    cpu: 32
-    ram: 128
+    cpu: [32, 64]
+    ram: [128, 256]
     family: ["m8a", "m8i", "m7a", "m7i", "m6a", "m6i", "m5a", "m5"]
     image: ubuntu22-full-x64
     volume: 150gb
+    spot: co
   airflow-arm-small:
-    cpu: 4
-    ram: 16
+    cpu: [4, 8]
+    ram: [16, 32]
     family: ["m8g", "m7g", "m6g"]
     image: ubuntu22-full-arm64
     volume: 150gb
+    spot: co
   airflow-arm-medium:
-    cpu: 8
-    ram: 32
+    cpu: [8, 16]
+    ram: [32, 64]
     family: ["m8g", "m7g", "m6g"]
     image: ubuntu22-full-arm64
     volume: 150gb
+    spot: co
   airflow-arm-xl:
-    cpu: 32
-    ram: 128
+    cpu: [32, 64]
+    ram: [128, 256]
     family: ["m8g", "m7g", "m6g"]
     image: ubuntu22-full-arm64
     volume: 150gb
+    spot: co
+  airflow-amd-in-memory:
+    cpu: [8, 16]
+    ram: [32, 128]
+    family: ["m8a", "m8i", "m7a", "m7i", "m6a", "m6i", "m5a", "m5",
+             "r8i", "r7a", "r7i", "r6a", "r6i"]
+    image: ubuntu22-full-x64
+    volume: 150gb
+    spot: co
+    extras: ["tmpfs"]
+  airflow-arm-in-memory:
+    cpu: [8, 16]
+    ram: [32, 128]
+    family: ["m8g", "m7g", "m6g", "r8g", "r7g", "r6g"]
+    image: ubuntu22-full-arm64
+    volume: 150gb
+    spot: co
+    extras: ["tmpfs"]
diff --git a/terraform/runs-on/variables.tf b/terraform/runs-on/variables.tf
index 05d9fff..047ac17 100644
--- a/terraform/runs-on/variables.tf
+++ b/terraform/runs-on/variables.tf
@@ -28,6 +28,12 @@ variable "runs_on_version" {
   description = "RunsOn release to deploy; selects the version-pinned 
CloudFormation template."
   type        = string
   default     = "v3.5.1"
+
+  # The apply role may deploy only template-v*.yaml from RunsOn's bucket 
(terraform/runs-on-access).
+  validation {
+    condition     = can(regex("^v[0-9]+\\.[0-9]+\\.[0-9]+$", 
var.runs_on_version))
+    error_message = "runs_on_version must be a RunsOn release tag such as 
v3.5.1."
+  }
 }
 
 variable "github_organization" {

Reply via email to