Skip to content

feat(webhook): enforce per-runner dynamic labels policy in dispatcher#5172

Open
edersonbrilhante wants to merge 12 commits into
mainfrom
feat/per-matcher-dynamic-labels-policy
Open

feat(webhook): enforce per-runner dynamic labels policy in dispatcher#5172
edersonbrilhante wants to merge 12 commits into
mainfrom
feat/per-matcher-dynamic-labels-policy

Conversation

@edersonbrilhante

@edersonbrilhante edersonbrilhante commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Description

Adds an opt-in, per-matcher policy for dynamic EC2 override labels so operators
can declare which ghr-ec2-* keys/values each runner queue is allowed to honor.

The webhook lambda is the gatekeeper: when a workflow_job arrives, it picks the
first matching runner queue that both opts into dynamic labels
(matcherConfig.enableDynamicLabels) and accepts every ghr-ec2-* label on the
job per matcherConfig.ec2DynamicLabelsPolicy. If no queue qualifies, the
request is returned with 202 and a warning is logged instead of falling back to
a queue with the dynamic labels stripped.

Highlights:

  • Adds a small EC2 dynamic labels policy evaluator co-located with dispatch.ts.
  • Replaces the flat reserved-key schema with:
    { blocked_keys?, restricted_keys? }.
  • blocked_keys rejects EC2 override keys outright.
  • restricted_keys applies per-key value rules:
    { allowed?, denied?, max? }.
  • EC2 override keys not listed in either blocked_keys or restricted_keys are
    allowed by default.
  • The flag and policy travel inside the existing runner-matcher-config SSM
    parameter via MatcherConfig. No new SSM parameter or env var is added.
  • Root single-runner config exposes ec2_dynamic_labels_policy; matcher config
    exposes ec2DynamicLabelsPolicy.

Example

multi_runner_config = {
  m5 = {
    matcherConfig = {
      labelMatchers           = [["self-hosted", "linux"]]
      exactMatch              = true
      enableDynamicLabels     = true
      ec2DynamicLabelsPolicy = {
        blocked_keys = ["image-id", "subnet-id"]

        restricted_keys = {
          "instance-type" = {
            allowed = ["m5.*"]
            denied  = ["m5.metal"]
          }

          "ebs-volume-size" = {
            max = 200
          }
        }
      }
    }

    runner_config = { ... }
  }
}

A job with ghr-ec2-instance-type:r5.large will be rejected with 202 and a
warning for the m5 queue rather than being silently dispatched without the
label.

Test Plan

  • yarn exec vitest run functions/webhook/src/runners/dynamic-labels-policy.test.ts functions/webhook/src/runners/dispatch.test.ts
  • terraform fmt -check -diff main.tf variables.tf modules/webhook/variables.tf modules/multi-runner/variables.tf

Related Issues

Fixes #5161
Fixes #5160

Pure module that, given a list of GitHub Actions labels and a policy, returns the ghr-ec2-* labels that violate the policy with a human-readable reason. Supports allowed_keys/denied_keys meta filters and per-key value rules (allowed/denied globs, numeric max). Keys use the same hyphenated form as the labels (e.g. instance-type). Not wired into dispatch yet.
Extend the MatcherConfig type so each runner-matcher entry can opt in to dynamic labels and ship its own per-matcher policy. The fields are optional so existing matcher configs keep working unchanged.
The flag now travels per-matcher inside the runner-matcher-config SSM blob (see MatcherConfig.enableDynamicLabels), so the global env-var version is no longer needed in either ConfigWebhook or ConfigDispatcher.
handleWorkflowJob now picks the first matching runner queue that both opts into dynamic labels (matcherConfig.enableDynamicLabels) and accepts every ghr-ec2-* label on the job per its dynamicLabelsPolicy. If no such queue exists, the request is returned with status 202 and a warning is logged instead of falling back to a queue with the dynamic labels stripped.
The dispatcher is now the sole gatekeeper for dynamic labels, so scale-up no longer reads ENABLE_DYNAMIC_LABELS or applies a policy. It simply forwards every ghr-ec2-* label that survived dispatch into the EC2 override config.
Move the dynamic-labels opt-in and the per-runner policy from the runner_config block into matcherConfig, so they ride with the rest of the matcher metadata into the webhook lambda. Stop passing enable_dynamic_labels and dynamic_labels_policy to the runners module (the dispatcher is the gatekeeper) and stop OR-ing the flag at the webhook module boundary.
Drop the top-level enable_dynamic_labels variable and the ENABLE_DYNAMIC_LABELS env on the webhook and dispatcher lambdas. The flag and policy now travel as fields inside runner_matcher_config[*].matcherConfig and are serialized into the existing runner-matcher-config SSM parameter. Also remove the matching variables and lambda env vars from the runners module since scale-up no longer evaluates the policy.
Single-runner deployments can now declare a dynamic_labels_policy alongside enable_dynamic_labels. The root module nests both fields inside runner_matcher_config[0].matcherConfig so the webhook lambda can enforce the policy per-matcher. Document the flat policy schema (allowed_keys/denied_keys plus per-key allowed/denied/max rules with hyphenated keys) and clarify that violating jobs are returned with a 202.
@github-actions

github-actions Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@edersonbrilhante edersonbrilhante force-pushed the feat/per-matcher-dynamic-labels-policy branch from 631c506 to f77cb8a Compare June 16, 2026 11:07
@edersonbrilhante edersonbrilhante marked this pull request as ready for review June 16, 2026 13:30
@edersonbrilhante edersonbrilhante requested review from a team as code owners June 16, 2026 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: allow/deny list for dynamic label values Feature: allow enable_dynamic_labels per multi_runner_config entry

1 participant