diff --git a/lambdas/functions/webhook/jest.config.ts b/lambdas/functions/webhook/jest.config.ts index 6e1368d818..b7a4cac980 100644 --- a/lambdas/functions/webhook/jest.config.ts +++ b/lambdas/functions/webhook/jest.config.ts @@ -6,10 +6,10 @@ const config: Config = { ...defaultConfig, coverageThreshold: { global: { - statements: 99, - branches: 86, + statements: 99.07, + branches: 93.33, functions: 100, - lines: 99, + lines: 99.02, }, }, }; diff --git a/lambdas/functions/webhook/src/webhook/handler.test.ts b/lambdas/functions/webhook/src/webhook/handler.test.ts index d56755d552..e5aec02391 100644 --- a/lambdas/functions/webhook/src/webhook/handler.test.ts +++ b/lambdas/functions/webhook/src/webhook/handler.test.ts @@ -7,7 +7,7 @@ import checkrun_event from '../../test/resources/github_check_run_event.json'; import workflowjob_event from '../../test/resources/github_workflowjob_event.json'; import queuesConfig from '../../test/resources/multi_runner_configurations.json'; import { sendActionRequest } from '../sqs'; -import { handle } from './handler'; +import { canRunJob, handle } from './handler'; jest.mock('../sqs'); jest.mock('@terraform-aws-github-runner/aws-ssm-util'); @@ -261,7 +261,7 @@ describe('handler', () => { event, ); expect(resp.statusCode).toBe(202); - expect(sendActionRequest).not.toBeCalled; + expect(sendActionRequest).not.toBeCalled(); }); it('Check webhook does not accept jobs where the job labels are spread across label matchers.', async () => { @@ -289,7 +289,7 @@ describe('handler', () => { event, ); expect(resp.statusCode).toBe(202); - expect(sendActionRequest).not.toBeCalled; + expect(sendActionRequest).not.toBeCalled(); }); it('Check webhook does not accept jobs where not all labels are supported by the runner.', async () => { @@ -321,7 +321,7 @@ describe('handler', () => { event, ); expect(resp.statusCode).toBe(202); - expect(sendActionRequest).not.toBeCalled; + expect(sendActionRequest).not.toBeCalled(); }); it('Check webhook will accept jobs with a single acceptable label.', async () => { @@ -385,7 +385,7 @@ describe('handler', () => { event, ); expect(resp.statusCode).toBe(202); - expect(sendActionRequest).not.toBeCalled; + expect(sendActionRequest).not.toBeCalled(); }); it('Check webhook will accept jobs for specific labels if workflow labels are specific', async () => { process.env.RUNNER_CONFIG = JSON.stringify([ @@ -520,3 +520,54 @@ describe('handler', () => { }); }); }); + +describe('canRunJob', () => { + it('should accept job with an exact match and identical labels.', () => { + const workflowLabels = ['self-hosted', 'linux', 'x64', 'ubuntu-latest']; + const runnerLabels = [['self-hosted', 'linux', 'x64', 'ubuntu-latest']]; + const exactMatch = true; + expect(canRunJob(workflowLabels, runnerLabels, exactMatch)).toBe(true); + }); + + it('should accept job with an exact match and runner supports requested capabilites.', () => { + const workflowLabels = ['self-hosted', 'linux', 'x64']; + const runnerLabels = [['self-hosted', 'linux', 'x64', 'ubuntu-latest']]; + const exactMatch = true; + expect(canRunJob(workflowLabels, runnerLabels, exactMatch)).toBe(true); + }); + + it('should NOT accept job with an exact match and runner not matching requested capabilites.', () => { + const workflowLabels = ['self-hosted', 'linux', 'x64', 'ubuntu-latest']; + const runnerLabels = [['self-hosted', 'linux', 'x64']]; + const exactMatch = true; + expect(canRunJob(workflowLabels, runnerLabels, exactMatch)).toBe(false); + }); + + it('should accept job with for a non exact match. Any label that matches will accept the job.', () => { + const workflowLabels = ['self-hosted', 'linux', 'x64', 'ubuntu-latest', 'gpu']; + const runnerLabels = [['gpu']]; + const exactMatch = false; + expect(canRunJob(workflowLabels, runnerLabels, exactMatch)).toBe(true); + }); + + it('should NOT accept job with for an exact match. Not all requested capabilites are supported.', () => { + const workflowLabels = ['self-hosted', 'linux', 'x64', 'ubuntu-latest', 'gpu']; + const runnerLabels = [['gpu']]; + const exactMatch = true; + expect(canRunJob(workflowLabels, runnerLabels, exactMatch)).toBe(false); + }); + + it('Should not accecpt jobs not providing labels if exact match is.', () => { + const workflowLabels: string[] = []; + const runnerLabels = [['self-hosted', 'linux', 'x64']]; + const exactMatch = true; + expect(canRunJob(workflowLabels, runnerLabels, exactMatch)).toBe(false); + }); + + it('Should accept jobs not providing labels and exact match is set to false.', () => { + const workflowLabels: string[] = []; + const runnerLabels = [['self-hosted', 'linux', 'x64']]; + const exactMatch = false; + expect(canRunJob(workflowLabels, runnerLabels, exactMatch)).toBe(true); + }); +}); diff --git a/lambdas/functions/webhook/src/webhook/handler.ts b/lambdas/functions/webhook/src/webhook/handler.ts index 7b37b14b12..b5ad65f54e 100644 --- a/lambdas/functions/webhook/src/webhook/handler.ts +++ b/lambdas/functions/webhook/src/webhook/handler.ts @@ -158,7 +158,7 @@ function isRepoNotAllowed(repoFullName: string, repositoryWhiteList: string[]): return repositoryWhiteList.length > 0 && !repositoryWhiteList.includes(repoFullName); } -function canRunJob( +export function canRunJob( workflowJobLabels: string[], runnerLabelsMatchers: string[][], workflowLabelCheckAll: boolean, @@ -166,9 +166,10 @@ function canRunJob( runnerLabelsMatchers = runnerLabelsMatchers.map((runnerLabel) => { return runnerLabel.map((label) => label.toLowerCase()); }); - const match = workflowLabelCheckAll + const matchLabels = workflowLabelCheckAll ? runnerLabelsMatchers.some((rl) => workflowJobLabels.every((wl) => rl.includes(wl.toLowerCase()))) : runnerLabelsMatchers.some((rl) => workflowJobLabels.some((wl) => rl.includes(wl.toLowerCase()))); + const match = workflowJobLabels.length === 0 ? !matchLabels : matchLabels; logger.debug( `Received workflow job event with labels: '${JSON.stringify(workflowJobLabels)}'. The event does ${ diff --git a/modules/webhook/variables.tf b/modules/webhook/variables.tf index a79f311314..7c36faf1b9 100644 --- a/modules/webhook/variables.tf +++ b/modules/webhook/variables.tf @@ -34,7 +34,7 @@ variable "tags" { } variable "runner_config" { - description = "SQS queue to publish accepted build events based on the runner type." + description = "SQS queue to publish accepted build events based on the runner type. When exact match is disabled the webhook accecpts the event if one of the workflow job labels is part of the matcher." type = map(object({ arn = string id = string