Skip to content
This repository was archived by the owner on Jan 16, 2025. It is now read-only.

Commit c5e3c21

Browse files
authored
feat: Add option to match some of the labes instead of all #2122 (#2123)
* feat: add some behavior force pushed to make conventionalcommits happy * run prettier * add WORKFLOW_JOB_LABELS_CHECK_ALL to tests
1 parent ca11f6b commit c5e3c21

File tree

8 files changed

+76
-6
lines changed

8 files changed

+76
-6
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ In case the setup does not work as intended follow the trace of events:
458458
| <a name="input_runner_ec2_tags"></a> [runner\_ec2\_tags](#input\_runner\_ec2\_tags) | Map of tags that will be added to the launch template instance tag specificatons. | `map(string)` | `{}` | no |
459459
| <a name="input_runner_egress_rules"></a> [runner\_egress\_rules](#input\_runner\_egress\_rules) | List of egress rules for the GitHub runner instances. | <pre>list(object({<br> cidr_blocks = list(string)<br> ipv6_cidr_blocks = list(string)<br> prefix_list_ids = list(string)<br> from_port = number<br> protocol = string<br> security_groups = list(string)<br> self = bool<br> to_port = number<br> description = string<br> }))</pre> | <pre>[<br> {<br> "cidr_blocks": [<br> "0.0.0.0/0"<br> ],<br> "description": null,<br> "from_port": 0,<br> "ipv6_cidr_blocks": [<br> "::/0"<br> ],<br> "prefix_list_ids": null,<br> "protocol": "-1",<br> "security_groups": null,<br> "self": null,<br> "to_port": 0<br> }<br>]</pre> | no |
460460
| <a name="input_runner_enable_workflow_job_labels_check"></a> [runner\_enable\_workflow\_job\_labels\_check](#input\_runner\_enable\_workflow\_job\_labels\_check) | If set to true all labels in the workflow job even are matched agaist the custom labels and GitHub labels (os, architecture and `self-hosted`). When the labels are not matching the event is dropped at the webhook. | `bool` | `false` | no |
461+
| <a name="input_runner_enable_workflow_job_labels_check_all"></a> [runner\_enable\_workflow\_job\_labels\_check\_all](#input\_runner\_enable\_workflow\_job\_labels\_check\_all) | If set to true all labels in the workflow job must match the GitHub labels (os, architecture and `self-hosted`). When false if __any__ label matches it will trigger the webhook. `runner_enable_workflow_job_labels_check` must be true for this to take effect. | `bool` | `true` | no |
461462
| <a name="input_runner_extra_labels"></a> [runner\_extra\_labels](#input\_runner\_extra\_labels) | Extra (custom) labels for the runners (GitHub). Separate each label by a comma. Labels checks on the webhook can be enforced by setting `enable_workflow_job_labels_check`. GitHub read-only labels should not be provided. | `string` | `""` | no |
462463
| <a name="input_runner_group_name"></a> [runner\_group\_name](#input\_runner\_group\_name) | Name of the runner group. | `string` | `"Default"` | no |
463464
| <a name="input_runner_iam_role_managed_policy_arns"></a> [runner\_iam\_role\_managed\_policy\_arns](#input\_runner\_iam\_role\_managed\_policy\_arns) | Attach AWS or customer-managed IAM policies (by ARN) to the runner IAM role | `list(string)` | `[]` | no |

main.tf

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ module "webhook" {
111111

112112
# labels
113113
enable_workflow_job_labels_check = var.runner_enable_workflow_job_labels_check
114+
workflow_job_labels_check_all = var.runner_enable_workflow_job_labels_check_all
114115
runner_labels = "self-hosted,${var.runner_os},${var.runner_architecture},${var.runner_extra_labels}"
115116

116117
role_path = var.role_path

modules/webhook/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ No modules.
9797
| <a name="input_tags"></a> [tags](#input\_tags) | Map of tags that will be added to created resources. By default resources will be tagged with name and environment. | `map(string)` | `{}` | no |
9898
| <a name="input_webhook_lambda_s3_key"></a> [webhook\_lambda\_s3\_key](#input\_webhook\_lambda\_s3\_key) | S3 key for webhook lambda function. Required if using S3 bucket to specify lambdas. | `any` | `null` | no |
9999
| <a name="input_webhook_lambda_s3_object_version"></a> [webhook\_lambda\_s3\_object\_version](#input\_webhook\_lambda\_s3\_object\_version) | S3 object version for webhook lambda function. Useful if S3 versioning is enabled on source bucket. | `any` | `null` | no |
100+
| <a name="input_workflow_job_labels_check_all"></a> [workflow\_job\_labels\_check\_all](#input\_workflow\_job\_labels\_check\_all) | If set to true all labels in the workflow job must match the GitHub labels (os, architecture and `self-hosted`). When false if __any__ label matches it will trigger the webhook. `enable_workflow_job_labels_check` must be true for this to take effect. | `bool` | `true` | no |
100101

101102
## Outputs
102103

modules/webhook/lambdas/webhook/src/webhook/handler.test.ts

+42
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ describe('handler', () => {
124124
it('Check runner labels accept test job', async () => {
125125
process.env.RUNNER_LABELS = '["self-hosted", "test"]';
126126
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
127+
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'true';
127128
const event = JSON.stringify({
128129
...workflowjob_event,
129130
workflow_job: {
@@ -142,6 +143,7 @@ describe('handler', () => {
142143
it('Check runner labels accept job with mixed order.', async () => {
143144
process.env.RUNNER_LABELS = '["linux", "test", "self-hosted"]';
144145
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
146+
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'true';
145147
const event = JSON.stringify({
146148
...workflowjob_event,
147149
workflow_job: {
@@ -160,6 +162,7 @@ describe('handler', () => {
160162
it('Check webhook does not accept jobs where not all labels are provided in job.', async () => {
161163
process.env.RUNNER_LABELS = '["self-hosted", "test", "test2"]';
162164
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
165+
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'true';
163166
const event = JSON.stringify({
164167
...workflowjob_event,
165168
workflow_job: {
@@ -178,6 +181,7 @@ describe('handler', () => {
178181
it('Check webhook does not accept jobs where not all labels are supported by the runner.', async () => {
179182
process.env.RUNNER_LABELS = '["self-hosted", "x64", "linux", "test"]';
180183
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
184+
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'true';
181185
const event = JSON.stringify({
182186
...workflowjob_event,
183187
workflow_job: {
@@ -192,6 +196,44 @@ describe('handler', () => {
192196
expect(resp.statusCode).toBe(202);
193197
expect(sendActionRequest).not.toBeCalled;
194198
});
199+
200+
it('Check webhook will accept jobs with a single acceptable label.', async () => {
201+
process.env.RUNNER_LABELS = '["self-hosted", "x64", "linux", "test"]';
202+
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
203+
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'false';
204+
const event = JSON.stringify({
205+
...workflowjob_event,
206+
workflow_job: {
207+
...workflowjob_event.workflow_job,
208+
labels: ['x64'],
209+
},
210+
});
211+
const resp = await handle(
212+
{ 'X-Hub-Signature': await webhooks.sign(event), 'X-GitHub-Event': 'workflow_job' },
213+
event,
214+
);
215+
expect(resp.statusCode).toBe(201);
216+
expect(sendActionRequest).toBeCalled();
217+
});
218+
219+
it('Check webhook will not accept jobs without correct label when job label check all is false.', async () => {
220+
process.env.RUNNER_LABELS = '["self-hosted", "x64", "linux", "test"]';
221+
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
222+
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'false';
223+
const event = JSON.stringify({
224+
...workflowjob_event,
225+
workflow_job: {
226+
...workflowjob_event.workflow_job,
227+
labels: ['ubuntu-latest'],
228+
},
229+
});
230+
const resp = await handle(
231+
{ 'X-Hub-Signature': await webhooks.sign(event), 'X-GitHub-Event': 'workflow_job' },
232+
event,
233+
);
234+
expect(resp.statusCode).toBe(202);
235+
expect(sendActionRequest).not.toBeCalled;
236+
});
195237
});
196238

197239
describe('Test for check_run event (legacy): ', () => {

modules/webhook/lambdas/webhook/src/webhook/handler.ts

+18-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ const supportedEvents = ['check_run', 'workflow_job'];
1111
const logger = rootLogger.getChildLogger();
1212

1313
export async function handle(headers: IncomingHttpHeaders, body: string): Promise<Response> {
14-
const { environment, repositoryWhiteList, enableWorkflowLabelCheck, runnerLabels } = readEnvironmentVariables();
14+
const { environment, repositoryWhiteList, enableWorkflowLabelCheck, workflowLabelCheckAll, runnerLabels } =
15+
readEnvironmentVariables();
1516

1617
// ensure header keys lower case since github headers can contain capitals.
1718
for (const key in headers) {
@@ -66,6 +67,7 @@ export async function handle(headers: IncomingHttpHeaders, body: string): Promis
6667
payload as WorkflowJobEvent,
6768
githubEvent,
6869
enableWorkflowLabelCheck,
70+
workflowLabelCheckAll,
6971
runnerLabels,
7072
);
7173
} else if (githubEvent == 'check_run') {
@@ -79,11 +81,13 @@ function readEnvironmentVariables() {
7981
const environment = process.env.ENVIRONMENT;
8082
const enableWorkflowLabelCheckEnv = process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK || 'false';
8183
const enableWorkflowLabelCheck = JSON.parse(enableWorkflowLabelCheckEnv) as boolean;
84+
const workflowLabelCheckAllEnv = process.env.WORKFLOW_JOB_LABELS_CHECK_ALL || 'false';
85+
const workflowLabelCheckAll = JSON.parse(workflowLabelCheckAllEnv) as boolean;
8286
const repositoryWhiteListEnv = process.env.REPOSITORY_WHITE_LIST || '[]';
8387
const repositoryWhiteList = JSON.parse(repositoryWhiteListEnv) as Array<string>;
8488
const runnerLabelsEnv = process.env.RUNNER_LABELS || '[]';
8589
const runnerLabels = JSON.parse(runnerLabelsEnv) as Array<string>;
86-
return { environment, repositoryWhiteList, enableWorkflowLabelCheck, runnerLabels };
90+
return { environment, repositoryWhiteList, enableWorkflowLabelCheck, workflowLabelCheckAll, runnerLabels };
8791
}
8892

8993
async function verifySignature(
@@ -117,9 +121,10 @@ async function handleWorkflowJob(
117121
body: WorkflowJobEvent,
118122
githubEvent: string,
119123
enableWorkflowLabelCheck: boolean,
124+
workflowLabelCheckAll: boolean,
120125
runnerLabels: string[],
121126
): Promise<Response> {
122-
if (enableWorkflowLabelCheck && !canRunJob(body, runnerLabels)) {
127+
if (enableWorkflowLabelCheck && !canRunJob(body, runnerLabels, workflowLabelCheckAll)) {
123128
logger.warn(
124129
`Received event contains runner labels '${body.workflow_job.labels}' that are not accepted.`,
125130
LogFields.print(),
@@ -171,10 +176,17 @@ function isRepoNotAllowed(repoFullName: string, repositoryWhiteList: string[]):
171176
return repositoryWhiteList.length > 0 && !repositoryWhiteList.includes(repoFullName);
172177
}
173178

174-
function canRunJob(job: WorkflowJobEvent, runnerLabels: string[]): boolean {
179+
function canRunJob(job: WorkflowJobEvent, runnerLabels: string[], workflowLabelCheckAll: boolean): boolean {
175180
const workflowJobLabels = job.workflow_job.labels;
176-
const runnerMatch = runnerLabels.every((l) => workflowJobLabels.includes(l));
177-
const jobMatch = workflowJobLabels.every((l) => runnerLabels.includes(l));
181+
let runnerMatch;
182+
let jobMatch;
183+
if (workflowLabelCheckAll) {
184+
runnerMatch = runnerLabels.every((l) => workflowJobLabels.includes(l));
185+
jobMatch = workflowJobLabels.every((l) => runnerLabels.includes(l));
186+
} else {
187+
runnerMatch = runnerLabels.some((l) => workflowJobLabels.includes(l));
188+
jobMatch = workflowJobLabels.some((l) => runnerLabels.includes(l));
189+
}
178190
const match = jobMatch && runnerMatch;
179191

180192
logger.debug(

modules/webhook/variables.tf

+6
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ variable "enable_workflow_job_labels_check" {
113113
default = false
114114
}
115115

116+
variable "workflow_job_labels_check_all" {
117+
description = "If set to true all labels in the workflow job must match the GitHub labels (os, architecture and `self-hosted`). When false if __any__ label matches it will trigger the webhook. `enable_workflow_job_labels_check` must be true for this to take effect."
118+
type = bool
119+
default = true
120+
}
121+
116122
variable "log_type" {
117123
description = "Logging format for lambda logging. Valid values are 'json', 'pretty', 'hidden'. "
118124
type = string

modules/webhook/webhook.tf

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ resource "aws_lambda_function" "webhook" {
1414
environment {
1515
variables = {
1616
ENABLE_WORKFLOW_JOB_LABELS_CHECK = var.enable_workflow_job_labels_check
17+
WORKFLOW_JOB_LABELS_CHECK_ALL = var.workflow_job_labels_check_all
1718
ENVIRONMENT = var.prefix
1819
LOG_LEVEL = var.log_level
1920
LOG_TYPE = var.log_type

variables.tf

+6
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,12 @@ variable "runner_enable_workflow_job_labels_check" {
511511
default = false
512512
}
513513

514+
variable "runner_enable_workflow_job_labels_check_all" {
515+
description = "If set to true all labels in the workflow job must match the GitHub labels (os, architecture and `self-hosted`). When false if __any__ label matches it will trigger the webhook. `runner_enable_workflow_job_labels_check` must be true for this to take effect."
516+
type = bool
517+
default = true
518+
}
519+
514520
variable "runner_ec2_tags" {
515521
description = "Map of tags that will be added to the launch template instance tag specificatons."
516522
type = map(string)

0 commit comments

Comments
 (0)