feat(webhook): enforce per-runner dynamic labels policy in dispatcher#5172
Conversation
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.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
631c506 to
f77cb8a
Compare
|
@copilot resolve the merge conflicts in this pull request |
Merge conflicts resolved in commit Two files had conflicts:
All 277 tests pass. |
|
@Brend-Smits I tested in my environment and it is working |
Brend-Smits
left a comment
There was a problem hiding this comment.
Great work! Thanks a lot 🚀
🤖 I have created a release *beep* *boop* --- ## [7.8.0](v7.7.1...v7.8.0) (2026-06-24) ### Features * **runners:** add volume_initialization_rate to block_device_mappings ([#5165](#5165)) ([449df46](449df46)), closes [#5163](#5163) * support dynamic EC2 placement and block device overrides ([#5178](#5178)) ([58fd1c2](58fd1c2)) * **webhook:** enforce per-runner dynamic labels policy in dispatcher ([#5172](#5172)) ([d624d96](d624d96)) ### Bug Fixes * **ci:** restore persist-credentials for gh-pages deploy ([#5162](#5162)) ([9955d73](9955d73)) * **codeowners:** require admin review for nested .github paths ([#5164](#5164)) ([83ea30c](83ea30c)) * **mac:** handle RunInstances scale errors ([#5183](#5183)) ([7d8c576](7d8c576)) * **scale-up:** prevent runnerLabels accumulation across dynamic label groups ([#5176](#5176)) ([246949e](246949e)), closes [#5175](#5175) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: runners-releaser[bot] <194412594+runners-releaser[bot]@users.noreply.github.com>
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_jobarrives, it picks thefirst matching runner queue that both opts into dynamic labels
(
matcherConfig.enableDynamicLabels) and accepts everyghr-ec2-*label on thejob per
matcherConfig.ec2DynamicLabelsPolicy. If no queue qualifies, therequest is returned with
202and a warning is logged instead of falling back toa queue with the dynamic labels stripped.
Highlights:
dispatch.ts.{ blocked_keys?, restricted_keys? }.blocked_keysrejects EC2 override keys outright.restricted_keysapplies per-key value rules:{ allowed?, denied?, max? }.blocked_keysorrestricted_keysareallowed by default.
runner-matcher-configSSMparameter via
MatcherConfig. No new SSM parameter or env var is added.ec2_dynamic_labels_policy; matcher configexposes
ec2DynamicLabelsPolicy.Example
A job with
ghr-ec2-instance-type:r5.largewill be rejected with202and awarning for the
m5queue rather than being silently dispatched without thelabel.
Test Plan
yarn exec vitest run functions/webhook/src/runners/dynamic-labels-policy.test.ts functions/webhook/src/runners/dispatch.test.tsterraform fmt -check -diff main.tf variables.tf modules/webhook/variables.tf modules/multi-runner/variables.tfRelated Issues
Fixes #5161
Fixes #5160